;;; my-github.el -- Github client -*- lexical-binding: t -*- ;; Copyright (C) 2023 Free Software Foundation. ;; Author: Yuchen Pei ;; Package-Requires: ((emacs "28.2")) ;; This file is part of dotfiles. ;; dotfiles is free software: you can redistribute it and/or modify it under ;; the terms of the GNU Affero General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; dotfiles is distributed in the hope that it will be useful, but WITHOUT ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General ;; Public License for more details. ;; You should have received a copy of the GNU Affero General Public ;; License along with dotfiles. If not, see . ;;; Commentary: ;; Github client. ;;; Code: (defun my-grok-github (url) "get github info of a project. url is the url of the project License; name; description; homepage; created at" (when (string-match "github.com\\(/[^/]+/[^/]+\\)/?.*$" url) (with-current-buffer (url-retrieve-synchronously (concat "https://api.github.com/repos" (replace-regexp-in-string "\\.git$" "" (match-string 1 url)))) (set-buffer-multibyte t) (my-delete-http-header) (my-grok-github-make-info (json-read))))) (defun my-grok-github-make-info (raw) (list (cons "Title" (alist-get 'name raw)) (cons "Description" (alist-get 'description raw)) (cons "Source" (alist-get 'html_url raw)) (cons "Website" (alist-get 'homepage raw)) (cons "Released" (substring (alist-get 'created_at raw) 0 10)) (cons "Pushed" (substring (alist-get 'pushed_at raw) 0 10)) (cons "Subject" (string-join (alist-get 'topics raw) ", ")) ;; FIXME: why did we comment this out? ;; (cons "License" (my-post-process-licensing-name ;; (alist-get 'spdx_id (alist-get 'license raw)))) (cons "Developers" (my-grok-github-get-developer-name (alist-get 'url (alist-get 'owner raw)))))) (defun my-grok-github-get-developer-name (url) (with-current-buffer (url-retrieve-synchronously url) (set-buffer-multibyte t) (my-delete-http-header) (alist-get 'name (json-read)))) ;;; urls with github ;; TODO: generalise the following to common forges, including ;; cgit, gitlab etc. (defun my-github-revision-url (revision file remote) "Returns the REMOTE github instance url of REVISION for FILE." (let ((repo-url (vc-git-repository-url file remote))) (format "%s/commit/%s" repo-url revision))) (defun my-github-revision-url-default (&optional revision file remote) "Returns the REMOTE github instance url of REVISION for FILE. Same as `my-github-revision-url', but all params can be nil, and sensible defaults are set first." (setq file (or file (buffer-file-name) default-directory) revision (or revision (vc-working-revision file))) (my-github-revision-url revision file remote)) (defun my-github-branch-url (branch file remote) "Returns the REMOTE github instance url of BRANCH containing FILE. https://github.com/MariaDB/server/commits/10.4" (let ((repo-url (vc-git-repository-url file remote))) (format "%s/commits/%s" repo-url branch))) (defun my-github-branch-url-default (&optional branch file remote) "Returns the REMOTE github instance url of BRANCH containing FILE. Same as `my-github-branch-url', but all params can be nil, and sensible defaults are set first." (setq file (or file (buffer-file-name) default-directory) branch (or branch (car (vc-git-branches)))) (my-github-branch-url branch file remote)) (defun my-github-file-url (file line-no revision remote) "Returns the REMOTE github instance url of the FILE at LINE-NO at REVISION. https://github.com/MariaDB/server/blob/0254eb9307f58409f856517a90109d37ef9e10c4/storage/spider/mysql-test/spider/bugfix/r/mdev_31463.result" (let* ((repo-root (vc-git-root file)) (path (file-relative-name file repo-root)) (repo-url (vc-git-repository-url file remote))) (format "%s/blob/%s/%s" repo-url revision path (if line-no (format "#L%d" line-no) "")))) (defun my-github-file-url-default (&optional file line-no revision remote) "Returns the REMOTE github instance url of the FILE at LINE-NO at REVISION. Same as `my-github-file-url', but all params can be nil, and sensible defaults are set first." (unless (setq file (or file (buffer-file-name))) (error "Please supply a file")) (setq revision (or revision (vc-working-revision file))) (my-github-file-url file line-no revision remote)) (defun my-github-raw-file-url (file revision remote) "Returns the REMOTE github instance url of the raw FILE at REVISION. https://g.ypei.me/librejs.git/plain/main_background.js?id=e2230c23e4aa7a74ea34825856acf7edd8a61e04" (let* ((repo-root (vc-git-root file)) (path (file-relative-name file repo-root)) (repo-url (vc-git-repository-url file remote))) (format "%s/raw/%s/%s" repo-url path revision))) (defun my-github-raw-file-url-default (&optional file revision remote) "Returns the REMOTE github instance url of the raw FILE at REVISION. Same as `my-github-raw-file-url', but all params can be nil, and sensible defaults are set first." (unless (setq file (or file (buffer-file-name))) (error "Please supply a file")) (setq (revision (or revision (vc-working-revision file)))) (my-github-raw-file-url file revision remote)) (defun my-github-revision-to-url (revision) "Returns the remote github instance url of REVISION. Uses default remote." (let ((file (or (buffer-file-name) default-directory))) (my-github-revision-url revision file nil))) (defun my-github-file-to-url (file) "Returns the remote github instance url of FILE. Uses default remote and currently checked out revision." (let ((revision (vc-working-revision file))) (my-github-file-url file nil revision nil))) (defun my-github-current-file-to-url () "Returns the remote github instance url of the current buffer file-loc. Uses default remote and currently checked out revision." (let ((file (buffer-file-name)) (line-no (1+ (current-line))) (revision (vc-working-revision file))) (unless file (error "Current buffer is not associated with any file")) (my-github-file-url file line-no revision nil))) (defun my-github-file-to-raw-url (file) "Returns the remote github instance url of FILE. Uses default remote and currently checked out revision." (let ((revision (vc-working-revision file))) (my-github-raw-file-url file revision nil))) (defun my-github-current-file-to-raw-url () "Returns the remote github instance url of the current buffer file. Uses default remote and currently checked out revision." (let ((file (buffer-file-name)) (revision (vc-working-revision file))) (unless file (error "Current buffer is not associated with any file")) (my-github-file-url file revision nil))) ;; FIXME: remove the following redundant functions (defun my-github-revision-url (file revision) "Returns the github url of the upstream repo containing FILE at REVISION." (let ((repo-url (vc-git-repository-url file)) (revision (or revision (vc-working-revision default-directory)))) (format "%s/commit/%s" repo-url revision))) (defun my-github-kill-revision-url (revision) "Kill the github revision url for REVISION." (kill-new (my-github-revision-url revision))) (defun my-github-file-loc-url (file-loc &optional revision) "Convert a file location to a github url." (pcase-let* ((`(,file ,line-no) (split-string file-loc ":")) (revision (or revision (vc-working-revision file)))) (my-github-file-loc-url-internal file line-no revision))) (defun my-github-file-loc-url-internal (file line-no revision) "Convert a FILE location at LINE-NO to a github url. REVISION is the commit hash." (let* ((repo-url (vc-git-repository-url file)) (repo-root (vc-git-root file)) (path (file-relative-name file repo-root))) (format "%s/blob/%s/%s#L%s" repo-url revision path line-no))) (defun my-github-kill-current-file-loc-url () "Kill the github url from where the point is at." (interactive) (kill-new (my-github-file-loc-url-internal (buffer-file-name) (1+ (current-line)) (vc-working-revision (buffer-file-name))))) (defun my-org-backtrace-to-github (bt &optional revision) (string-join (mapcar (lambda (link) (string-match "\\[\\[\\(.*\\)\\]\\[\\(.*\\)\\]\\]" link) (let ((target (match-string 1 link)) (label (match-string 2 link))) (format "[[%s][%s]]" (my-github-file-loc-url target revision) label))) (split-string bt " > ")) " > ")) (defun my-org-backtrace-to-github-region (beg end) (interactive "r") (kill-new (my-org-backtrace-to-github (buffer-substring-no-properties beg end)))) (defun my-org-backtrace-to-github-slack (beg end) (interactive "r") (let ((bt (buffer-substring-no-properties beg end)) (revision (when current-prefix-arg (read-string "Rrevision: "))) ) (with-temp-buffer (insert "#+options: ^:nil ") (goto-char (point-max)) (insert (my-org-backtrace-to-github bt revision)) (org-md-export-as-markdown)))) (provide 'my-github) ;;; my-github.el ends here