From: Andreas Gerler Date: Sun, 26 Apr 2026 17:51:52 +0000 (+0200) Subject: update ghub X-Git-Url: http://git.bundesbrandschatzamt.de/?a=commitdiff_plain;h=d0d868f2add068074bd3e2fcf06729979b9d2222;p=emacsconfig.git update ghub --- diff --git a/elpa/ghub-20251130.1842/.dir-locals.el b/elpa/ghub-20251130.1842/.dir-locals.el deleted file mode 100644 index aff6808e..00000000 --- a/elpa/ghub-20251130.1842/.dir-locals.el +++ /dev/null @@ -1,9 +0,0 @@ -((nil - (indent-tabs-mode . nil)) - (makefile-mode - (indent-tabs-mode . t)) - (git-commit-mode - (git-commit-major-mode . git-commit-elisp-text-mode)) - ("CHANGELOG" - (nil (fill-column . 70) - (mode . display-fill-column-indicator)))) diff --git a/elpa/ghub-20251130.1842/dir b/elpa/ghub-20251130.1842/dir deleted file mode 100644 index b703bcac..00000000 --- a/elpa/ghub-20251130.1842/dir +++ /dev/null @@ -1,18 +0,0 @@ -This is the file .../info/dir, which contains the -topmost node of the Info hierarchy, called (dir)Top. -The first time you invoke Info you start off looking at this node. - -File: dir, Node: Top This is the top of the INFO tree - - This (the Directory node) gives a menu of major topics. - Typing "q" exits, "H" lists all Info commands, "d" returns here, - "h" gives a primer for first-timers, - "mEmacs" visits the Emacs manual, etc. - - In Emacs, you can click mouse button 2 on a menu item or cross reference - to select it. - -* Menu: - -Emacs -* Ghub: (ghub). Client library for the Github API. diff --git a/elpa/ghub-20251130.1842/ghub-autoloads.el b/elpa/ghub-20251130.1842/ghub-autoloads.el deleted file mode 100644 index 3e17afff..00000000 --- a/elpa/ghub-20251130.1842/ghub-autoloads.el +++ /dev/null @@ -1,52 +0,0 @@ -;;; ghub-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from ghub.el - -(autoload 'ghub-clear-caches "ghub" "\ -Clear all caches that might negatively affect Ghub. - -If a library that is used by Ghub caches incorrect information -such as a mistyped password, then that can prevent Ghub from -asking the user for the correct information again. - -Set `url-http-real-basic-auth-storage' to nil -and call `auth-source-forget+'." t) -(register-definition-prefixes "ghub" '("auth-source-netrc-parse" "ghub-")) - - -;;; Generated autoloads from ghub-graphql.el - -(register-definition-prefixes "ghub-graphql" '("ghub-")) - - -;;; Generated autoloads from ghub-legacy.el - -(register-definition-prefixes "ghub-legacy" '("ghub-fetch-repository")) - - -;;; Generated autoloads from gsexp.el - -(register-definition-prefixes "gsexp" '("gsexp-")) - -;;; End of scraped data - -(provide 'ghub-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; ghub-autoloads.el ends here diff --git a/elpa/ghub-20251130.1842/ghub-graphql.el b/elpa/ghub-20251130.1842/ghub-graphql.el deleted file mode 100644 index adaaa24e..00000000 --- a/elpa/ghub-20251130.1842/ghub-graphql.el +++ /dev/null @@ -1,352 +0,0 @@ -;;; ghub-graphql.el --- Access Github API using GraphQL -*- lexical-binding:t -*- - -;; Copyright (C) 2016-2025 Jonas Bernoulli - -;; Author: Jonas Bernoulli -;; Homepage: https://github.com/magit/ghub -;; Keywords: tools - -;; SPDX-License-Identifier: GPL-3.0-or-later - -;; This file is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published -;; by the Free Software Foundation, either version 3 of the License, -;; or (at your option) any later version. -;; -;; This file 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 General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with this file. If not, see . - -;;; Commentary: - -;; This library implements GraphQL queries for Github. - -;;; Code: - -(require 'ghub) -(require 'gsexp) -(require 'treepy) - -(eval-when-compile (require 'subr-x)) - -(define-error 'ghub-graphql-error "GraphQL Error" 'ghub-error) - -(defvar ghub-graphql-message-progress nil - "Whether to show \"Fetching page N...\" in echo area during requests. -By default this information is only shown in the mode-line of the buffer -from which the request was initiated, and if you kill that buffer, then -nowhere. That may make it desirable to display the same message in the -echo area as well.") - -(defvar ghub-graphql-items-per-request 50 - "Number of GraphQL items to query for entities that return a collection. - -Adjust this value if you're hitting query timeouts against larger -repositories.") - -(cl-defun ghub-graphql-rate-limit (&key username auth host) - "Return rate limit information." - (let-alist (ghub-query - '(query (rateLimit limit cost remaining resetAt)) nil - :synchronous t :username username :auth auth :host host) - .data.rateLimit)) - -(cl-defstruct (ghub--graphql-req - (:include ghub--req) - (:constructor ghub--make-graphql-req) - (:copier nil)) - (query nil :read-only t) - (query-str nil :read-only nil) - (variables nil :read-only t) - (until nil :read-only t) - (pages 0 :read-only nil) - (paginate nil :read-only nil) - (narrow nil :read-only t)) - -(cl-defun ghub-query - (query &optional variables - &key until narrow headers paginate - callback errorback noerror synchronous - username auth host forge) - (declare (indent defun)) - (unless forge - (setq forge 'github)) - (unless host - (setq host (ghub--host forge))) - (unless (or username (stringp auth) (eq auth 'none)) - (setq username (ghub--username host forge))) - (when (eq callback 'pp) - (setq callback #'ghub--graphql-pp-response) - (setq noerror t)) - (ghub--graphql-retrieve - (ghub--make-graphql-req - :url (ghub--encode-url - host (if (eq forge 'gitlab) "/api/graphql" "/graphql")) - :method "POST" - :headers (ghub--headers headers host auth username forge) - :handler #'ghub--graphql-handle-response - :query query - :variables variables - :until until - :buffer (current-buffer) - :narrow narrow - :paginate (or paginate - (and-let* ((p (and (eq auth 'forge) - (fboundp 'magit-get) - (magit-get "forge.graphqlItemLimit")))) - (string-to-number p))) - :noerror noerror - :synchronous synchronous - :callback callback - :errorback errorback))) - -(cl-defun ghub--graphql-retrieve (req &optional lineage cursor) - (let ((p (cl-incf (ghub--graphql-req-pages req)))) - (when (> p 1) - (when ghub-graphql-message-progress - (let ((message-log-max nil)) - (message "Fetching page %s..." p))) - (ghub--graphql-set-mode-line req "Fetching page %s" p))) - (setf (ghub--graphql-req-query-str req) - (gsexp-encode - (ghub--graphql-prepare-query - (ghub--graphql-req-query req) - lineage cursor))) - (when ghub-debug - (with-current-buffer (get-buffer-create " *gsexp-encode*") - (erase-buffer) - (insert (ghub--graphql-req-query-str req) "\n\n") - (let ((pos (point))) - (insert (ghub--encode-payload (ghub--graphql-req-variables req)) "\n") - (ignore-errors (json-pretty-print pos (point)))))) - (ghub--retrieve - (ghub--encode-payload - `((query . ,(ghub--graphql-req-query-str req)) - ,@(and-let* ((variables (ghub--graphql-req-variables req))) - `((variables . ,variables))))) - req) - (ghub--req-value req)) - -(defun ghub--graphql-prepare-query (query &optional lineage cursor paginate) - (when lineage - (setq query (ghub--graphql-narrow-query query lineage cursor))) - (let ((loc (ghub--alist-zip query)) - variables) - (catch :done - (while t - (let ((node (treepy-node loc))) - (when (and (vectorp node) - (listp (aref node 0))) - (let ((alist (append node ())) - (vars nil)) - (when-let ((edges (cadr (assq :edges alist)))) - (push (list 'first - (apply - #'min - (delq nil (list (and (numberp edges) edges) - paginate - ghub-graphql-items-per-request)))) - vars) - (setq loc (treepy-up loc)) - (setq node (treepy-node loc)) - (setq loc (treepy-replace - loc `(,(car node) - ,(cadr node) - (pageInfo endCursor hasNextPage) - (edges (node ,@(cddr node)))))) - (setq loc (treepy-down loc)) - (setq loc (treepy-next loc))) - (dolist (elt alist) - (cond ((keywordp (car elt))) - ((length= elt 3) - (push (list (nth 0 elt) (nth 1 elt)) vars) - (push (list (nth 1 elt) (nth 2 elt)) variables)) - ((length= elt 2) - (push elt vars)))) - (setq loc (treepy-replace loc (vconcat (nreverse vars))))))) - (if (treepy-end-p loc) - (let ((node (copy-sequence (treepy-node loc)))) - (when variables - (push (vconcat (nreverse variables)) - (cdr node))) - (throw :done node)) - (setq loc (treepy-next loc))))))) - -(defun ghub--graphql-handle-response (status req) - (let ((buf (current-buffer))) - (unwind-protect - (progn - (set-buffer-multibyte t) - (let* ((headers (ghub--handle-response-headers status req)) - (payload (ghub--handle-response-payload req)) - (err (plist-get status :error)) - (errors (and (not (and (ghub--req-noerror req) - (assq 'data payload))) - (assq 'errors payload)))) - (cond ((or err errors) - (when (and (not err) ghub-debug) - (ignore-errors (json-pretty-print (point) (point-max))) - (pop-to-buffer buf)) - (ghub--graphql-handle-failure - req (or err errors) headers status)) - ((ghub--graphql-walk-response req (assq 'data payload)))))) - (when (and (buffer-live-p buf) - (not (buffer-local-value 'ghub-debug buf))) - (kill-buffer buf))))) - -(defun ghub--graphql-handle-failure (req errors headers status) - (ghub--graphql-set-mode-line req) - (setf (ghub--req-value req) errors) - (cond-let - ([errorback (ghub--req-errorback req)] - (ghub--graphql-run-callback req errorback errors headers status req)) - ((ghub--req-noerror req) - (when-let ((callback (ghub--req-callback req))) - (ghub--graphql-run-callback req callback errors))) - ((ghub--signal-error (if (eq (car errors) 'errors) - (cons 'ghub-graphql-error (cdr errors)) - errors))))) - -(defun ghub--graphql-handle-success (req data) - (ghub--graphql-set-mode-line req) - (when-let ((narrow (ghub--graphql-req-narrow req))) - (while-let ((key (pop narrow))) - (setq data (cdr (assq key data))))) - (setf (ghub--req-value req) data) - (when-let ((callback (ghub--req-callback req))) - (ghub--graphql-run-callback req callback data))) - -(defun ghub--graphql-run-callback (req callback &rest args) - (let ((buffer (ghub--req-buffer req))) - (if (buffer-live-p buffer) - (with-current-buffer buffer - (apply callback args)) - (apply callback args)))) - -(defun ghub--graphql-set-mode-line (req &optional format &rest args) - (let ((buffer (ghub--graphql-req-buffer req))) - (when (buffer-live-p buffer) - (with-current-buffer buffer - (setq mode-line-process - (and format (concat " " (apply #'format format args)))) - (force-mode-line-update t))))) - -(defun ghub--graphql-pp-response (data) - (pp-display-expression data "*Pp Eval Output*")) - -(defun ghub--graphql-walk-response (req data) - (let* ((loc (ghub--req-value req)) - (loc (if (not loc) - (ghub--alist-zip data) - (setq data (ghub--graphql-narrow-data - data (ghub--graphql-lineage loc))) - (setf (alist-get 'edges data) - (append (alist-get 'edges (treepy-node loc)) - (or (alist-get 'edges data) - (error "BUG: Expected new nodes")))) - (treepy-replace loc data)))) - (catch :done - (while t - (when (eq (car-safe (treepy-node loc)) 'edges) - (setq loc (treepy-up loc)) - (pcase-let ((`(,key . ,val) (treepy-node loc))) - (let-alist val - (let* ((cursor (and .pageInfo.hasNextPage - .pageInfo.endCursor)) - (until (cdr (assq (intern (format "%s-until" key)) - (ghub--graphql-req-until req)))) - (nodes (mapcar #'cdar .edges)) - (nodes (if until - (seq-take-while - (lambda (node) - (or (string> (cdr (assq 'updatedAt node)) - until) - (setq cursor nil))) - nodes) - nodes))) - (cond (cursor - (setf (ghub--req-value req) loc) - (ghub--graphql-retrieve req - (ghub--graphql-lineage loc) - cursor) - (throw :done nil)) - ((setq loc (treepy-replace loc (cons key nodes))))))))) - (if (treepy-end-p loc) - (progn (ghub--graphql-handle-success req (treepy-root loc)) - (throw :done nil)) - (setq loc (treepy-next loc))))))) - -(defun ghub--graphql-lineage (loc) - (let (lineage) - (while (treepy-up loc) - (push (car (treepy-node loc)) lineage) - (setq loc (treepy-up loc))) - lineage)) - -(defun ghub--graphql-narrow-data (data lineage) - (while-let ((key (pop lineage))) - (if (consp (car lineage)) - (progn (pop lineage) - (setf data (cadr data))) - (setq data (assq key (cdr data))))) - data) - -(defun ghub--graphql-narrow-query (query lineage &optional cursor) - (if (consp (car lineage)) - (let* ((child (cddr query)) - (alist (append (cadr query) ())) - (single (cdr (assq :singular alist)))) - `(,(car single) - ,(vector (list (cadr single) (cdr (car lineage)))) - ,@(if (cdr lineage) - (ghub--graphql-narrow-query child (cdr lineage) cursor) - child))) - (let* ((child (or (assq (car lineage) (cdr query)) - ;; Alias - (cl-find-if (lambda (c) - (eq (car-safe (car-safe c)) - (car lineage))) - query) - ;; Edges - (cl-find-if (lambda (c) - (and (listp c) - (vectorp (cadr c)) - (eq (cadr (assq :singular - (append (cadr c) ()))) - (car lineage)))) - (cdr query)) - (error "BUG: Failed to narrow query"))) - (object (car query)) - (args (and (vectorp (cadr query)) - (cadr query)))) - `(,object - ,@(and args (list args)) - ,(cond ((cdr lineage) - (ghub--graphql-narrow-query child (cdr lineage) cursor)) - (cursor - `(,(car child) - ,(vconcat `((after ,cursor)) - (cadr child)) - ,@(cddr child))) - (t - child)))))) - -(defun ghub--alist-zip (root) - (let ((branchp (##and (listp %) (listp (cdr %)))) - (make-node (lambda (_ children) children))) - (treepy-zipper branchp #'identity make-node root))) - -;;; _ -;; Local Variables: -;; read-symbol-shorthands: ( -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") -;; ("when-let" . "cond-let--when-let") -;; ("while-let" . "cond-let--while-let")) -;; End: -(provide 'ghub-graphql) -;;; ghub-graphql.el ends here diff --git a/elpa/ghub-20251130.1842/ghub-legacy.el b/elpa/ghub-20251130.1842/ghub-legacy.el deleted file mode 100644 index 4c46b3c0..00000000 --- a/elpa/ghub-20251130.1842/ghub-legacy.el +++ /dev/null @@ -1,377 +0,0 @@ -;;; ghub-legacy.el --- Deprecated Ghub functions -*- lexical-binding:t -*- - -;; Copyright (C) 2016-2025 Jonas Bernoulli - -;; Author: Jonas Bernoulli -;; Homepage: https://github.com/magit/ghub -;; Keywords: tools - -;; SPDX-License-Identifier: GPL-3.0-or-later - -;; This file is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published -;; by the Free Software Foundation, either version 3 of the License, -;; or (at your option) any later version. -;; -;; This file 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 General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with this file. If not, see . - -;;; Commentary: - -;; Deprecated Ghub functions. - -;;; Code: - -(require 'ghub-graphql) - -(cl-defun ghub-graphql (graphql - &optional variables - &key username auth host forge - headers silent - callback errorback value extra) - (cl-assert (not (stringp variables))) - (cl-assert (or (stringp graphql) - (memq (car-safe graphql) '(query mutation)))) - (unless (stringp graphql) - (setq graphql (gsexp-encode (ghub--graphql-prepare-query graphql)))) - (ghub-request "POST" - (if (eq forge 'gitlab) "/api/graphql" "/graphql") - nil - :payload `((query . ,graphql) - ,@(and variables `((variables ,@variables)))) - :headers headers :silent silent - :username username :auth auth :host host :forge forge - :callback callback :errorback errorback - :extra extra :value value)) - -(cl-defun ghub--graphql (graphql - &optional variables - &key username auth host forge - headers - callback errorback) - (ghub--graphql-vacuum graphql variables callback nil - :username username - :auth auth - :host host - :forge forge - :headers headers - :errorback errorback)) - -(cl-defun ghub--graphql-vacuum - ( query variables callback - &optional until - &key narrow username auth host forge - headers paginate errorback noerror synchronous) - (ghub-query query variables - :until until :narrow narrow :headers headers :paginate paginate - :callback callback - :errorback errorback :noerror noerror :synchronous synchronous - :username username :auth auth :host host :forge forge)) - -(defconst ghub-fetch-repository-sparse - '(query - (repository - [(owner $owner String!) - (name $name String!)] - name - id - createdAt - updatedAt - nameWithOwner - (parent nameWithOwner) - description - homepageUrl - (defaultBranchRef name) - isArchived - isFork - isLocked - isMirror - isPrivate - hasDiscussionsEnabled - hasIssuesEnabled - hasWikiEnabled - (licenseInfo name) - (stargazers totalCount) - (watchers totalCount)))) - -(defconst ghub-fetch-repository - `(query - (repository - ,@(cdr (cadr ghub-fetch-repository-sparse)) - (assignableUsers [(:edges t)] - id - login - name) - (discussionCategories [(:edges t)] - id - name - emoji - isAnswerable - description) - (discussions [(:edges t) - (:singular discussion number) - (orderBy ((field UPDATED_AT) (direction DESC)))] - id - databaseId - number - url - stateReason - ;; Discussions lack isReadByViewer. - (answer id) - (author login) - title - createdAt - updatedAt - closedAt - locked - (category id) - body - (comments [(:edges t)] - id - databaseId - (author login) - createdAt - updatedAt - body - (replies [(:edges 20)] - id - databaseId - (author login) - createdAt - updatedAt - body)) - (labels [(:edges t)] - id)) - (issues [(:edges t) - (:singular issue number) - (orderBy ((field UPDATED_AT) (direction DESC)))] - number - id - state - stateReason - isReadByViewer - (author login) - title - createdAt - updatedAt - closedAt - locked - (milestone id) - body - (assignees [(:edges t)] - id) - (comments [(:edges t)] - id - databaseId - (author login) - createdAt - updatedAt - body) - (labels [(:edges t)] - id)) - (labels [(:edges t) - (:singular label id)] - id - name - color - description) - (milestones [(:edges t) - (:singular milestone id)] - id - number - title - createdAt - updatedAt - dueOn - closedAt - description) - (pullRequests [(:edges t) - (:singular pullRequest number) - (orderBy ((field UPDATED_AT) (direction DESC)))] - number - id - state - isReadByViewer - (author login) - title - createdAt - updatedAt - closedAt - mergedAt - isDraft - locked - maintainerCanModify - isCrossRepository - (milestone id) - body - (baseRef name - (repository nameWithOwner)) - baseRefOid - (headRef name - (repository (owner login) - nameWithOwner)) - headRefOid - (assignees [(:edges t)] - id) - (reviewRequests [(:edges t)] - (requestedReviewer "... on User { id }\n")) - (comments [(:edges t)] - id - databaseId - (author login) - createdAt - updatedAt - body) - (labels [(:edges t)] - id))))) - -(defconst ghub-fetch-repository-review-threads - '(query - (repository - [(owner $owner String!) - (name $name String!)] - (pullRequests [(:edges t) - (:singular pullRequest number) - (orderBy ((field UPDATED_AT) (direction DESC)))] - number - baseRefOid - headRefOid - (reviewThreads [(:edges t)] - id - line - originalLine - diffSide - (resolvedBy login) - (comments [(:edges t)] - id - databaseId - (author login) - createdAt - updatedAt - body - (replyTo databaseId) - (originalCommit oid) - path)))))) - -(cl-defun ghub-fetch-repository ( owner name callback - &optional until - &key username auth host forge - headers paginate errorback sparse) - "Asynchronously fetch forge data about the specified repository. -Once all data has been collected, CALLBACK is called with the -data as the only argument." - (ghub-query (if sparse - ghub-fetch-repository-sparse - ghub-fetch-repository) - `((owner . ,owner) - (name . ,name)) - :until until - :narrow '(repository) - :username username - :auth auth - :host host - :forge forge - :headers headers - :paginate paginate - :callback callback - :errorback errorback)) - -(cl-defun ghub-fetch-discussion ( owner name number callback - &optional until - &key username auth host forge - headers errorback) - "Asynchronously fetch forge data about the specified discussion. -Once all data has been collected, CALLBACK is called with the -data as the only argument." - (ghub-query (ghub--graphql-prepare-query - ghub-fetch-repository - `(repository discussions (discussion . ,number))) - `((owner . ,owner) - (name . ,name)) - :until until - :narrow '(repository discussion) - :username username - :auth auth - :host host - :forge forge - :headers headers - :callback callback - :errorback errorback)) - -(cl-defun ghub-fetch-issue ( owner name number callback - &optional until - &key username auth host forge - headers paginate errorback) - "Asynchronously fetch forge data about the specified issue. -Once all data has been collected, CALLBACK is called with the -data as the only argument." - (ghub-query (ghub--graphql-narrow-query - ghub-fetch-repository - `(repository issues (issue . ,number))) - `((owner . ,owner) - (name . ,name)) - :until until - :narrow '(repository issue) - :username username - :auth auth - :host host - :forge forge - :headers headers - :paginate paginate - :callback callback - :errorback errorback)) - -(cl-defun ghub-fetch-pullreq ( owner name number callback - &optional until - &key username auth host forge - headers paginate errorback) - "Asynchronously fetch forge data about the specified pull-request. -Once all data has been collected, CALLBACK is called with the -data as the only argument." - (ghub-query (ghub--graphql-narrow-query - ghub-fetch-repository - `(repository pullRequests (pullRequest . ,number))) - `((owner . ,owner) - (name . ,name)) - :until until - :narrow '(repository pullRequest) - :username username - :auth auth - :host host - :forge forge - :headers headers - :paginate paginate - :callback callback - :errorback errorback)) - -(cl-defun ghub-fetch-review-threads ( owner name number callback - &optional until - &key username auth host forge - headers paginate errorback) - "Asynchronously fetch forge data about the review threads from a pull-request. -Once all data has been collected, CALLBACK is called with the -data as the only argument." - (ghub-query (ghub--graphql-narrow-query - ghub-fetch-repository-review-threads - `(repository pullRequests (pullRequest . ,number))) - `((owner . ,owner) - (name . ,name)) - :until until - :narrow '(repository pullRequest) - :username username - :auth auth - :host host - :forge forge - :headers headers - :paginate paginate - :callback callback - :errorback errorback)) - -;;; _ -(provide 'ghub-legacy) -;;; ghub-legacy.el ends here diff --git a/elpa/ghub-20251130.1842/ghub-pkg.el b/elpa/ghub-20251130.1842/ghub-pkg.el deleted file mode 100644 index 432944d3..00000000 --- a/elpa/ghub-20251130.1842/ghub-pkg.el +++ /dev/null @@ -1,14 +0,0 @@ -;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "ghub" "20251130.1842" - "Client libraries for Git forge APIs." - '((emacs "29.1") - (compat "30.1") - (cond-let "0.2") - (llama "1.0") - (treepy "0.1.2")) - :url "https://github.com/magit/ghub" - :commit "9f416605d560ed2a6b62a87d1f624549901b1102" - :revdesc "9f416605d560" - :keywords '("tools") - :authors '(("Jonas Bernoulli" . "emacs.ghub@jonas.bernoulli.dev")) - :maintainers '(("Jonas Bernoulli" . "emacs.ghub@jonas.bernoulli.dev"))) diff --git a/elpa/ghub-20251130.1842/ghub.el b/elpa/ghub-20251130.1842/ghub.el deleted file mode 100644 index 317fe329..00000000 --- a/elpa/ghub-20251130.1842/ghub.el +++ /dev/null @@ -1,826 +0,0 @@ -;;; ghub.el --- Client libraries for Git forge APIs -*- lexical-binding:t -*- - -;; Copyright (C) 2016-2025 Jonas Bernoulli - -;; Author: Jonas Bernoulli -;; Homepage: https://github.com/magit/ghub -;; Keywords: tools - -;; Package-Version: 20251130.1842 -;; Package-Revision: 9f416605d560 -;; Package-Requires: ( -;; (emacs "29.1") -;; (compat "30.1") -;; (cond-let "0.2") -;; (llama "1.0") -;; (treepy "0.1.2")) - -;; SPDX-License-Identifier: GPL-3.0-or-later - -;; This file is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published -;; by the Free Software Foundation, either version 3 of the License, -;; or (at your option) any later version. -;; -;; This file 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 General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with this file. If not, see . - -;;; Commentary: - -;; Ghub provides basic support for using the APIs of various Git forges -;; from Emacs packages. Originally it only supported the Github REST -;; API, but now it also supports the Github GraphQL API as well as the -;; REST APIs of Gitlab, Gitea, Gogs and Bitbucket. - -;; Ghub abstracts access to API resources using only a handful of basic -;; functions such as `ghub-get'. These are convenience wrappers around -;; `ghub-request'. - -;; When accessing Github, then Ghub handles the creation and storage of -;; access tokens using a setup wizard to make it easier for users to get -;; started. The tokens for other forges have to be created manually. - -;; Ghub is intentionally limited to only provide these two essential -;; features — basic request functions and guided setup — to avoid being -;; too opinionated, which would hinder wide adoption. It is assumed that -;; wide adoption would make life easier for users and maintainers alike, -;; because then all packages that talk to forge APIs could be configured -;; the same way. - -;; Please consult the manual (info "ghub") for more information. - -;;; Code: - -(require 'auth-source) -(require 'cl-lib) -(require 'compat) -(require 'cond-let) -(require 'gnutls) -(require 'let-alist) -(require 'llama) -(require 'url) -(require 'url-auth) -(require 'url-http) - -(eval-when-compile (require 'subr-x)) - -(defvar url-callback-arguments) -(defvar url-http-end-of-headers) -(defvar url-http-extra-headers) -(defvar url-http-response-status) - -;;; Settings - -(defvar ghub-default-host-alist - '((github . "api.github.com") - (gitlab . "gitlab.com/api/v4") - (gitea . "localhost:3000/api/v1") - (gogs . "localhost:3000/api/v1") - (bitbucket . "api.bitbucket.org/2.0")) - "Alist of default hosts used when the respective `FORGE.host' is not set.") - -(defvar ghub-github-token-scopes '(repo) - "The Github API scopes that your private tools need. - -You have to manually create or update the token at -https://github.com/settings/tokens. This variable -only serves as documentation.") - -(defvar ghub-insecure-hosts nil - "List of hosts that use http instead of https.") - -(defvar ghub-debug nil - "Record additional debug information.") - -;;; Request -;;;; Object - -(cl-defstruct (ghub--req - (:constructor ghub--make-req) - (:copier nil)) - (url nil :read-only nil) - (forge nil :read-only t) - (silent nil :read-only t) - (method nil :read-only t) - (headers nil :read-only t) - (handler nil :read-only t) - (unpaginate nil :read-only nil) - (noerror nil :read-only t) - (reader nil :read-only t) - (buffer nil :read-only t) - (synchronous nil :read-only t) - (callback nil :read-only t) - (errorback nil :read-only t) - (value nil :read-only nil) - (extra nil :read-only nil)) - -(defalias 'ghub-req-extra #'ghub--req-extra) - -;;;; API - -(define-error 'ghub-error "Ghub/Url Error" 'error) -(define-error 'ghub-http-error "HTTP Error" 'ghub-error) - -(defvar ghub-response-headers nil - "The headers returned in response to the last request. -`ghub-request' returns the response body and stores the -response headers in this variable.") - -(cl-defun ghub-head (resource - &optional params - &key query payload headers - silent unpaginate noerror reader - username auth host - callback errorback extra) - "Make a `HEAD' request for RESOURCE, with optional query PARAMS. -Like calling `ghub-request' (which see) with \"HEAD\" as METHOD." - (ghub-request "HEAD" resource params - :query query :payload payload :headers headers - :silent silent :unpaginate unpaginate - :noerror noerror :reader reader - :username username :auth auth :host host - :callback callback :errorback errorback :extra extra)) - -(cl-defun ghub-get (resource - &optional params - &key query payload headers - silent unpaginate noerror reader - username auth host forge - callback errorback extra) - "Make a `GET' request for RESOURCE, with optional query PARAMS. -Like calling `ghub-request' (which see) with \"GET\" as METHOD." - (ghub-request "GET" resource params - :query query :payload payload :headers headers - :silent silent :unpaginate unpaginate - :noerror noerror :reader reader - :username username :auth auth :host host :forge forge - :callback callback :errorback errorback :extra extra)) - -(cl-defun ghub-put (resource - &optional params - &key query payload headers - silent unpaginate noerror reader - username auth host forge - callback errorback extra) - "Make a `PUT' request for RESOURCE, with optional payload PARAMS. -Like calling `ghub-request' (which see) with \"PUT\" as METHOD." - (ghub-request "PUT" resource params - :query query :payload payload :headers headers - :silent silent :unpaginate unpaginate - :noerror noerror :reader reader - :username username :auth auth :host host :forge forge - :callback callback :errorback errorback :extra extra)) - -(cl-defun ghub-post (resource - &optional params - &key query payload headers - silent unpaginate noerror reader - username auth host forge - callback errorback extra) - "Make a `POST' request for RESOURCE, with optional payload PARAMS. -Like calling `ghub-request' (which see) with \"POST\" as METHOD." - (ghub-request "POST" resource params - :query query :payload payload :headers headers - :silent silent :unpaginate unpaginate - :noerror noerror :reader reader - :username username :auth auth :host host :forge forge - :callback callback :errorback errorback :extra extra)) - -(cl-defun ghub-patch (resource - &optional params - &key query payload headers - silent unpaginate noerror reader - username auth host forge - callback errorback extra) - "Make a `PATCH' request for RESOURCE, with optional payload PARAMS. -Like calling `ghub-request' (which see) with \"PATCH\" as METHOD." - (ghub-request "PATCH" resource params - :query query :payload payload :headers headers - :silent silent :unpaginate unpaginate - :noerror noerror :reader reader - :username username :auth auth :host host :forge forge - :callback callback :errorback errorback :extra extra)) - -(cl-defun ghub-delete (resource - &optional params - &key query payload headers - silent unpaginate noerror reader - username auth host forge - callback errorback extra) - "Make a `DELETE' request for RESOURCE, with optional payload PARAMS. -Like calling `ghub-request' (which see) with \"DELETE\" as METHOD." - (ghub-request "DELETE" resource params - :query query :payload payload :headers headers - :silent silent :unpaginate unpaginate - :noerror noerror :reader reader - :username username :auth auth :host host :forge forge - :callback callback :errorback errorback :extra extra)) - -(cl-defun ghub-request ( method resource - &optional params - &key query payload headers - silent unpaginate noerror reader - username auth host forge - callback errorback value extra) - "Make a request for RESOURCE and return the response body. - -Also place the response headers in `ghub-response-headers'. - -METHOD is the HTTP method, given as a string. -RESOURCE is the resource to access, given as a string beginning - with a slash. - -PARAMS, QUERY, PAYLOAD and HEADERS are alists used to specify - data. The Github API documentation is vague on how data has - to be transmitted and for a particular resource usually just - talks about \"parameters\". Generally speaking when the METHOD - is \"HEAD\" or \"GET\", then they have to be transmitted as a - query, otherwise as a payload. -Use PARAMS to automatically transmit like QUERY or PAYLOAD would - depending on METHOD. -Use QUERY to explicitly transmit data as a query. -Use PAYLOAD to explicitly transmit data as a payload. - Instead of an alist, PAYLOAD may also be a string, in which - case it gets encoded as UTF-8 but is otherwise transmitted as-is. -Use HEADERS for those rare resources that require that the data - is transmitted as headers instead of as a query or payload. - When that is the case, then the API documentation usually - mentions it explicitly. - -If SILENT is non-nil, then don't message progress reports and - the like. - -If UNPAGINATE is t, then make as many requests as necessary to - get all values. If UNPAGINATE is a natural number, then get - at most that many pages. For any other non-nil value raise - an error. -If NOERROR is non-nil, then do not raise an error if the request - fails and return nil instead. If NOERROR is `return', then - return the error payload instead of nil. -If READER is non-nil, then it is used to read and return from the - response buffer. The default is `ghub--read-json-payload'. - For the very few resources that do not return JSON, you might - want to use `ghub--decode-payload'. - -If USERNAME is non-nil, then make a request on behalf of that - user. It is better to specify the user using the Git variable - `github.user' for \"api.github.com\", or `github.HOST.user' if - connecting to a Github Enterprise instance. - -Each package that uses `ghub' should use its own token. If AUTH - is nil, then the generic `ghub' token is used instead. This - is only acceptable for personal utilities. A packages that - is distributed to other users should always use this argument - to identify itself, using a symbol matching its name. - - Package authors who find this inconvenient should write a - wrapper around this function and possibly for the - method-specific functions as well. - - Some symbols have a special meaning. `none' means to make an - unauthorized request. `basic' means to make a password based - request. If the value is a string, then it is assumed to be - a valid token. `basic' and an explicit token string are only - intended for internal and debugging uses. - -If HOST is non-nil, then connect to that Github instance. This - defaults to \"api.github.com\". When a repository is connected - to a Github Enterprise instance, then it is better to specify - that using the Git variable `github.host' instead of using this - argument. - -If optional FORGE is nil, then it is assumed that HOST is a - Github host. When connecting to another forge type, then - FORGE must be one of `gitlab', `gitea', `gogs' or `bitbucket'. - -If CALLBACK and/or ERRORBACK is non-nil, then make one or more - asynchronous requests and call CALLBACK or ERRORBACK when - finished. If no error occurred, then call CALLBACK, unless - that is nil. - - If an error occurred, then call ERRORBACK, or if that is nil, - then CALLBACK. ERRORBACK can also be t, in which case an error - is signaled instead. NOERROR is ignored for all asynchronous - requests. - -Both callbacks are called with four arguments. - 1. For CALLBACK, the combined value of the retrieved pages. - For ERRORBACK, the error that occurred when retrieving the - last page. - 2. The headers of the last page as an alist. - 3. Status information provided by `url-retrieve'. Its `:error' - property holds the same information as ERRORBACK's first - argument. - 4. A `ghub--req' struct, which can be passed to `ghub-continue' - (which see) to retrieve the next page, if any." - (declare (indent defun)) - (cl-assert (or (booleanp unpaginate) (natnump unpaginate))) - (unless (string-prefix-p "/" resource) - (setq resource (concat "/" resource))) - (unless host - (setq host (ghub--host forge))) - (unless (or username (stringp auth) (eq auth 'none)) - (setq username (ghub--username host forge))) - (cond ((not params)) - ((member method '("GET" "HEAD")) - (when query - (error "PARAMS and QUERY are mutually exclusive for METHOD %S" - method)) - (setq query params)) - (t - (when payload - (error "PARAMS and PAYLOAD are mutually exclusive for METHOD %S" - method)) - (setq payload params))) - (when (or callback errorback) - (setq noerror t)) - (ghub--retrieve - (ghub--encode-payload payload) - (ghub--make-req - :url (ghub--encode-url host resource query) - :forge forge - :silent silent - :method (encode-coding-string method 'utf-8) ;#35 - :headers (ghub--headers headers host auth username forge) - :handler #'ghub--handle-response - :unpaginate unpaginate - :noerror noerror - :reader reader - :buffer (current-buffer) - :synchronous (not (or callback errorback)) - :callback callback - :errorback errorback - :value value - :extra extra))) - -(defun ghub-continue (req) - "If there is a next page, then retrieve that. - -This function is only intended to be called from callbacks. If -there is a next page, then retrieve that and return the buffer -that the result will be loaded into, or t if the process has -already completed. If there is no next page, then return nil. - -Callbacks are called with four arguments (see `ghub-request'). -The forth argument is a `ghub--req' struct, intended to be passed -to this function. A callback may use the struct's `extra' slot -to pass additional information to the callback that will be -called after the next request has finished. Use the function -`ghub-req-extra' to get and set the value of this slot." - (and (assq 'next (ghub-response-link-relations req)) - (or (ghub--retrieve nil req) t))) - -(cl-defun ghub-wait (resource - &optional duration - &key username auth host forge) - "Busy-wait up to DURATION seconds for RESOURCE to become available. - -DURATION specifies how many seconds to wait at most. It defaults -to 64 seconds. The first attempt is made immediately, the second -after two seconds, and each subsequent attempt is made after -waiting as long again as we already waited between all preceding -attempts combined. - -See `ghub-request' for information about the other arguments." - (unless duration - (setq duration 64)) - (with-local-quit - (let ((total 0)) - (while (not (ghub-request "GET" resource nil - :noerror t - :username username - :auth auth - :host host - :forge forge)) - (message "Waited (%3ss of %ss) for %s..." total duration resource) - (if (= total duration) - (error "%s is taking too long to create %s" - (if forge (capitalize (symbol-name forge)) "Github") - resource) - (if (> total 0) - (let ((wait (min total (- duration total)))) - (sit-for wait) - (cl-incf total wait)) - (sit-for (setq total 2)))))))) - -(defun ghub-response-link-relations (req &optional headers payload) - "Return an alist of link relations in HEADERS. -If optional HEADERS is nil, then return those that were -previously stored in the variable `ghub-response-headers'. - -When accessing a Bitbucket instance then the link relations -are in PAYLOAD instead of HEADERS, making their API merely -RESTish and forcing this function to append those relations -to the value of `ghub-response-headers', for later use when -this function is called with nil for PAYLOAD." - (if (eq (ghub--req-forge req) 'bitbucket) - (if payload - (let* ((page (seq-keep (##assq % payload) - '(size page pagelen next previous))) - (headers (cons (cons 'link-alist page) headers))) - (if (and req (or (ghub--req-callback req) - (ghub--req-errorback req))) - (setq-local ghub-response-headers headers) - (setq-default ghub-response-headers headers)) - page) - (cdr (assq 'link-alist ghub-response-headers))) - (and-let* ((headers (or headers ghub-response-headers)) - (rels (cdr (or (assoc "Link" headers) - (assoc "link" headers))))) - (mapcar (lambda (elt) - (pcase-let ((`(,url ,rel) (split-string elt "; "))) - (cons (intern (substring rel 5 -1)) - (substring url 1 -1)))) - (split-string rels ", ?"))))) - -(cl-defun ghub-repository-id (owner name &key username auth host forge noerror) - "Return the id of the specified repository. -Signal an error if the id cannot be determined." - (or (pcase forge - ((or 'nil 'github) - (let-alist (ghub-graphql - '(query (repository [(owner $owner String!) - (name $name String!)] - id)) - `((owner . ,owner) - (name . ,name)) - :username username :auth auth :host host) - .data.repository.id)) - ('gitlab - (number-to-string - (alist-get - 'id (ghub-get (format "/projects/%s%%2F%s" - (string-replace "/" "%2F" owner) - name) - nil :forge 'gitlab - :username username :auth auth :host host)))) - ((or 'forgejo 'gitea 'gogs) - (number-to-string - (alist-get - 'id (ghub-get (format "/repos/%s/%s" owner name) - nil :forge forge - :username username :auth auth :host host)))) - ('bitbucket - (substring - (alist-get 'uuid - (ghub-get (format "/repositories/%s/%s" owner name) - nil :forge 'bitbucket - :username username :auth auth :host host)) - 1 -1)) - (_ (error "ghub-repository-id: Forge type `%s' is unknown" forge))) - (and (not noerror) - (error "Repository %S does not exist on %S.\n%s%S?" - (concat owner "/" name) - (or host (ghub--host forge)) - "Maybe it was renamed and you have to update " - "remote..url")))) - -;;;; Internal - -(cl-defun ghub--retrieve (payload req) - (pcase-let* - (((cl-struct ghub--req headers method url handler silent synchronous) req) - (url-request-extra-headers - (if (functionp headers) (funcall headers) headers)) - (url-request-method method) - (url-request-data payload) - (url-show-status nil)) - (if synchronous - (if-let ((buf (url-retrieve-synchronously url silent))) - (with-current-buffer buf - (funcall handler (car url-callback-arguments) req)) - (error "ghub--retrieve: No buffer returned")) - (url-retrieve url handler (list req) silent)))) - -(defun ghub--handle-response (status req) - (let ((buf (current-buffer))) - (unwind-protect - (progn - (set-buffer-multibyte t) - (let* ((unpaginate (ghub--req-unpaginate req)) - (headers (ghub--handle-response-headers status req)) - (payload (ghub--handle-response-payload req)) - (payload (ghub--handle-response-error status payload req)) - (value (ghub--handle-response-value payload req)) - (prev (ghub--req-url req)) - (next (cdr (assq 'next (ghub-response-link-relations - req headers payload))))) - (when (numberp unpaginate) - (cl-decf unpaginate)) - (setf (ghub--req-url req) - (url-generic-parse-url next)) - (setf (ghub--req-unpaginate req) unpaginate) - (or (and next - unpaginate - (or (eq unpaginate t) - (> unpaginate 0)) - (ghub-continue req)) - (let ((req-buf (ghub--req-buffer req)) - (callback (ghub--req-callback req)) - (errorback (ghub--req-errorback req)) - (err (plist-get status :error))) - (cond ((and err errorback) - (setf (ghub--req-url req) prev) - (funcall (if (eq errorback t) - 'ghub--errorback - errorback) - err headers status req)) - (callback - (save-current-buffer - (when (buffer-live-p req-buf) - (set-buffer req-buf)) - (funcall callback value headers status req))) - (t value)))))) - (when (and (buffer-live-p buf) - (not (buffer-local-value 'ghub-debug buf))) - (kill-buffer buf))))) - -(defun ghub--handle-response-headers (_status req) - (let (headers) - (when (memq url-http-end-of-headers '(nil 0)) - (unless url-debug (setq url-debug t)) - (error "BUG: Missing headers in response buffer %s" (current-buffer))) - (goto-char (point-min)) - (forward-line 1) - (while (re-search-forward "^\\([^:]*\\): \\(.+\\)" - url-http-end-of-headers t) - (push (cons (match-string 1) - (match-string 2)) - headers)) - (setq headers (nreverse headers)) - (goto-char (1+ url-http-end-of-headers)) - (if (and req (or (ghub--req-callback req) - (ghub--req-errorback req))) - (setq-local ghub-response-headers headers) - (setq-default ghub-response-headers headers)) - headers)) - -(defun ghub--handle-response-error (status payload req) - (if-let ((err (plist-get status :error))) - (if-let ((noerror (ghub--req-noerror req))) - (if (eq noerror 'return) - payload - (setcdr (last err) (list payload)) - nil) - (ghub--signal-error err payload req)) - payload)) - -(defun ghub--signal-error (err &optional payload req) - (pcase-let ((`(,symb . ,data) err)) - (if (eq symb 'error) - (if (eq (car-safe data) 'http) - (signal 'ghub-http-error - (let ((code (car (cdr-safe data)))) - (list code - (nth 2 (assq code url-http-codes)) - (and req (url-recreate-url (ghub--req-url req))) - payload))) - (signal 'ghub-error data)) - (signal symb data)))) - -(defun ghub--errorback (err _headers _status req) - (ghub--signal-error err (nth 3 err) req)) - -(defun ghub--handle-response-value (payload req) - (setf (ghub--req-value req) - (nconc (ghub--req-value req) - (if-let ((nested (and (eq (ghub--req-forge req) 'bitbucket) - (assq 'values payload)))) - (cdr nested) - payload)))) - -(defun ghub--handle-response-payload (req) - (funcall (or (ghub--req-reader req) - 'ghub--read-json-payload) - url-http-response-status)) - -(defun ghub--read-json-payload (_status &optional json-type-args) - (and-let* ((payload (ghub--decode-payload))) - (progn - (ghub--assert-json-available) - (condition-case nil - (apply #'json-parse-string payload - (or json-type-args - '( :object-type alist - :array-type list - :null-object nil - :false-object nil))) - (json-parse-error - (when ghub-debug - (pop-to-buffer (current-buffer))) - (setq-local ghub-debug t) - `((message . ,(if (looking-at "") - (if (re-search-forward - "

\\(?:\\)?\\([^<]+\\)" nil t) - (match-string 1) - "error description missing") - (string-trim (buffer-substring (point) (point-max))))) - (documentation_url - . "https://github.com/magit/ghub/wiki/Github-Errors"))))))) - -(defun ghub--decode-payload (&optional _status) - (and (not (eobp)) - (decode-coding-string - (buffer-substring-no-properties (point) (point-max)) - 'utf-8))) - -(defun ghub--encode-payload (payload) - (cl-typecase payload - (null nil) - (string (encode-coding-string payload 'utf-8)) - (t (ghub--assert-json-available) - (encode-coding-string - (json-serialize payload - :null-object :null - :false-object nil) - 'utf-8)))) - -(defun ghub--encode-url (host resource &optional query) - (url-generic-parse-url - (concat (if (member host ghub-insecure-hosts) "http://" "https://") - ;; Needed for some Github Enterprise instances. - (cond - ((and (equal resource "/graphql") - (string-suffix-p "/v3" host)) - (substring host 0 -3)) - ;; Needed for all Gitlab instances. - ((and (equal resource "/api/graphql") - (string-suffix-p "/api/v4" host)) - (substring host 0 -7)) - (host)) - resource - (and query (concat "?" (ghub--url-encode-params query)))))) - -(defun ghub--url-encode-params (params) - (mapconcat (lambda (param) - (pcase-let ((`(,key . ,val) param)) - (concat (url-hexify-string (symbol-name key)) "=" - (cl-typecase val - (integer (number-to-string val)) - (boolean (if val "true" "false")) - (t (url-hexify-string val)))))) - params "&")) - -(defun ghub--assert-json-available () - (unless (and (fboundp 'json-available-p) - (json-available-p)) - (error "Ghub requires Emacs 29 --with-json or Emacs >= 30"))) - -;;; Authentication -;;;; API - -;;;###autoload -(defun ghub-clear-caches () - "Clear all caches that might negatively affect Ghub. - -If a library that is used by Ghub caches incorrect information -such as a mistyped password, then that can prevent Ghub from -asking the user for the correct information again. - -Set `url-http-real-basic-auth-storage' to nil -and call `auth-source-forget+'." - (interactive) - (setq url-http-real-basic-auth-storage nil) - (auth-source-forget+)) - -;;;; Internal - -(defun ghub--headers (headers host auth username forge) - (push (cons "Content-Type" "application/json") headers) - (if (eq auth 'none) - headers - (unless (or username (stringp auth)) - (setq username (ghub--username host forge))) - (lambda () - (if (eq auth 'basic) - (cons (cons "Authorization" (ghub--basic-auth host username)) - headers) - (cons (ghub--auth host auth username forge) headers))))) - -(cl-defgeneric ghub--auth (host auth &optional username forge) - (unless username - (setq username (ghub--username host forge))) - (if (eq auth 'basic) - (pcase-exhaustive forge - ((or 'nil 'forgejo 'gitea 'gogs 'bitbucket) - (cons "Authorization" (ghub--basic-auth host username))) - ((or 'github 'gitlab) - (error "%s does not support basic authentication" - (capitalize (symbol-name forge))))) - (cons (pcase-exhaustive forge - ((or 'nil 'forgejo 'github 'gitea 'gogs 'bitbucket) "Authorization") - ('gitlab "Private-Token")) - (if (eq forge 'bitbucket) - ;; For some undocumented reason Bitbucket supports - ;; values of the form "token " only for GET - ;; requests. For PUT requests we have to use basic - ;; authentication. Note that the secret is a token - ;; (aka "app password"), not the actual password. - ;; The documentation fails to mention this little - ;; detail. See #97. - (concat "Basic " - (base64-encode-string - (concat username ":" - (ghub--token host username auth nil forge)) - t)) - (concat - (and (not (eq forge 'gitlab)) "token ") - (encode-coding-string - (cl-typecase auth - (string auth) - (null (ghub--token host username 'ghub nil forge)) - (symbol (ghub--token host username auth nil forge)) - (t (signal 'wrong-type-argument - `((or stringp symbolp) ,auth)))) - 'utf-8)))))) - -(defun ghub--basic-auth (host username) - (let ((url (url-generic-parse-url - (if (member host ghub-insecure-hosts) "http://" "https://")))) - (setf (url-user url) username) - (url-basic-auth url t))) - -(defun ghub--token (host username package &optional nocreate forge) - (let* ((user (ghub--ident username package)) - (token (or (ghub--auth-source-get :secret :host host :user user) - (and (string-match "\\`\\([^/]+\\)" host) - (ghub--auth-source-get :secret - :host (match-string 1 host) - :user user))))) - (unless (or token nocreate) - (error "\ -Required %s token (%S for %s%S) does not exist. -See https://docs.magit.vc/ghub/Getting-Started.html -or (info \"(ghub)Getting Started\") for instructions." - (capitalize (symbol-name (or forge 'github))) - user - (if (string-match "\\`\\([^/]+\\)" host) - (format "either %S or " (match-string 1 host)) - "") - host)) - (if (functionp token) (funcall token) token))) - -(cl-defmethod ghub--host (&optional forge) - (let ((forge (or forge 'github))) - (or (ghub--git-get (format "%s.host" forge)) - (alist-get forge ghub-default-host-alist)))) - -(cl-defmethod ghub--username (host &optional forge) - (let* ((forge (or forge 'github)) - (host (or host (ghub--host forge))) - (var (format "%s.%s.user" forge host)) - (default-var (format "%s.user" forge))) - (cond ((ghub--git-get var)) - ((not (equal host (alist-get forge ghub-default-host-alist))) - (user-error "Cannot determine username; `%s' is unset" var)) - ((ghub--git-get default-var)) - ((user-error "%s; `%s' and `%s' are both unset" - "Cannot determine username" var default-var))))) - -(defun ghub--ident (username package) - (format "%s^%s" username package)) - -(defun ghub--auth-source-get (keys &rest spec) - (declare (indent 1)) - (if-let ((plist (car (apply #'auth-source-search spec)))) - (if (keywordp keys) - (plist-get plist keys) - (mapcar (##plist-get plist %) keys)) - ;; Auth-Source caches the information that there is no value, but in - ;; our case that is a situation that needs fixing, so we want to keep - ;; trying, by invalidating that information. - (auth-source-forget spec) - nil)) - -(define-advice auth-source-netrc-parse (:around (fn &rest args) handle-symlink) - "Follow symlinks for FILE before comparing modification times." - (let ((file (plist-get args :file))) - (when (and (stringp file) - (file-exists-p file)) - (setq args (plist-put args :file (file-chase-links file))))) - (apply fn args)) - -(defun ghub--git-get (var) - (catch 'non-zero - (car (process-lines-handling-status - "git" (##unless (zerop %) (throw 'non-zero nil)) - ;; Beginning with Git v2.46 we could use "get". - "config" "--get" var)))) - -;;; _ -;; Local Variables: -;; read-symbol-shorthands: ( -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") -;; ("when-let" . "cond-let--when-let") -;; ("while-let" . "cond-let--while-let")) -;; End: -(provide 'ghub) -(require 'ghub-graphql) -(require 'ghub-legacy) -;;; ghub.el ends here diff --git a/elpa/ghub-20251130.1842/ghub.info b/elpa/ghub-20251130.1842/ghub.info deleted file mode 100644 index 8cfce139..00000000 --- a/elpa/ghub-20251130.1842/ghub.info +++ /dev/null @@ -1,987 +0,0 @@ -This is ghub.info, produced by makeinfo version 6.8 from ghub.texi. - - Copyright (C) 2017-2025 Jonas Bernoulli - - - You can redistribute this document and/or modify it under the terms - of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) - any later version. - - This document 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 - General Public License for more details. - -INFO-DIR-SECTION Emacs -START-INFO-DIR-ENTRY -* Ghub: (ghub). Client library for the Github API. -END-INFO-DIR-ENTRY - - -File: ghub.info, Node: Top, Next: Introduction, Up: (dir) - -Ghub User and Developer Manual -****************************** - -Ghub is an Emacs library that is used by various packages to access the -APIs of various instances of various Git forge implementations. - -This manual is for Ghub version 5.0.2. - - Copyright (C) 2017-2025 Jonas Bernoulli - - - You can redistribute this document and/or modify it under the terms - of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) - any later version. - - This document 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 - General Public License for more details. - -* Menu: - -* Introduction:: -* Getting Started:: -* API:: -* Notes:: -* Function Index:: -* Variable Index:: - -— The Detailed Node Listing — - -Getting Started - -* Basic Concepts, Arguments and Variables: Basic Concepts Arguments and Variables. -* Setting the Username:: -* Creating and Storing a Token:: -* Github Configuration Variables:: - -API - -* Their APIs:: -* Making REST Requests:: -* Making GraphQL Requests:: -* Github Convenience Wrappers:: - -Notes - -* Using Ghub in Personal Scripts:: -* Using Ghub in Your Own Package:: -* Forge Limitations and Notes:: - - - -File: ghub.info, Node: Introduction, Next: Getting Started, Prev: Top, Up: Top - -1 Introduction -************** - -Ghub is an Emacs library that is used by various packages to access the -APIs of various instances of various Git forge implementations. - - A forge is a web-based collaborative software platform for developing -and distributing computer applications. Examples include Github and -Gitlab. - - -File: ghub.info, Node: Getting Started, Next: API, Prev: Introduction, Up: Top - -2 Getting Started -***************** - -This manual guides you through the steps that are necessary to use the -Forge package and/or to make a request using just Ghub itself, such as -this: - - (ghub-request "GET" "/user") - - To be able to do that, Ghub needs to know who you want to talk to, -who you are, and how you are going to prove the latter to the former. - - Additionally Ghub wants to know on behalf of which Emacs package it -is making a request. So the question of "who is making the request" has -to be rephrased as "which human (or bot) is using what (Emacs) package -to make the request". If, for example, the human known as "tarsius" is -using the ‘forge’ package, then that is represented in some places using -the string "tarsius^forge". - -* Menu: - -* Basic Concepts, Arguments and Variables: Basic Concepts Arguments and Variables. -* Setting the Username:: -* Creating and Storing a Token:: -* Github Configuration Variables:: - - -File: ghub.info, Node: Basic Concepts Arguments and Variables, Next: Setting the Username, Up: Getting Started - -2.1 Basic Concepts, Arguments and Variables -=========================================== - -(If you are trying to configure Forge for and/or -, then you can skip this section.) - - Originally Ghub supported only Github but now it also supports -Gitlab, Gitea, Gogs and Bitbucket. Because of that, the function -‘ghub-request’ defaults to acting on a ‘github’ forge, but can be told -to act on other forges using the FORGE argument. - - The FORGE argument only specifies what kind of forge we are -accessing, not which host specifically. The HOST argument can be used -to select the instance. For some forges a default instance is defined: - - • Forge ‘github’ defaults to host ‘api.github.com’. - • Forge ‘gitlab’ defaults to host ‘gitlab.com/api/v4’. - • Forge ‘bitbucket’ defaults to host ‘api.bitbucket.org/2.0’. - • No canonical host exists for the ‘gitea’ and ‘gogs’ forges and - ‘localhost:3000/api/v1’ is used as the default host in both cases. - - Together the FORGE and HOST arguments specify the forge type and -instance. In addition to that, it is also necessary to specify on whose -behalf the request is being made, which can be done using the USERNAME -and AUTH arguments. For example: - - (ghub-request "GET" "/user" nil - :forge 'github - :host "api.github.com" - :username "tarsius" - :auth 'forge) - - Having to specify these arguments for every request is inconvenient. -Additional variables and convenience functions can be used to make that -unnecessary in most cases. - - (But for debugging purposes the above explicit form is very useful. -You will obviously have to change the value of USERNAME and you should -use ‘ghub’ as AUTH when first trying this at home.) - - These variables can be set globally and/or for a specific repository. - - • For "api.github.com" (aka the API of ) the Git - variable ‘github.user’ specifies the user. - • For another ‘github’ instance the Git variable ‘github.HOST.user’ - specifies the user. The HOST in that variable name is the same as - the value of the HOST argument of the called function. - • Instead of specifying the HOST in every function call, the Git - variable ‘github.host’ can be used. This should only be set - locally. - - These ‘github’ specific variables are discussed in more detail in -*note Github Configuration Variables::. - -For ‘gitlab’ and ‘bitbucket’ forges similar variables are available: - - • ‘gitlab.user’ specifies the user. - • ‘gitlab.HOST.user’ specifies the user for the HOST ‘gitlab’ - instance. - • ‘gitlab.host’ specifies the ‘gitlab’ host, unless the HOST argument - is non-‘nil’ - • ‘bitbucket.user’ specifies the user. - • ‘bitbucket.HOST.user’ specifies the user for the HOST ‘bitbucket’ - instance. - • ‘bitbucket.host’ specifies the ‘bitbucket’ host, unless the HOST - argument is non-‘nil’. - - For the ‘gitea’ and ‘gogs’ forges some similar variables are -available, however for some of the ‘ghub.*’ variables no equivalent -variable exist for these two forges: - - • ‘gitea.user’ is *not* used because no canonical ‘gitea’ instance - exists. - • ‘gitea.HOST.user’ specifies the user for the HOST ‘gitea’ instance. - • ‘gitea.host’ specifies the ‘gitea’ host, unless the HOST argument - is non-‘nil’ - • ‘gogs.user’ is *not* used because no canonical ‘gogs’ instance - exists. - • ‘gogs.HOST.user’ specifies the user for the HOST ‘gogs’ instance. - • ‘gogs.host’ specifies the ‘gogs’ host, unless the HOST argument is - non-‘nil’ - - -File: ghub.info, Node: Setting the Username, Next: Creating and Storing a Token, Prev: Basic Concepts Arguments and Variables, Up: Getting Started - -2.2 Setting the Username -======================== - -Ghub needs to know your username, which you use on the host that you -want it to connect to. For each host a different Git variable has to be -set, to specify the username on that host. More than one variable is -needed, because you might use different usernames on different hosts. - -Setting your Github.com Username --------------------------------- - -To inform Ghub about your "github.com" username do this: - - git config --global github.user USERNAME - - If you need to identify as another user in a particular repository, -then you have to set that variable locally: - - cd /path/to/repo - git config --local github.user USERNAME - -Setting your Gitlab.com Username --------------------------------- - -To inform Ghub about your "gitlab.com" username do this: - - git config --global gitlab.user USERNAME - - If you need to identify as another user in a particular repository, -then you have to set that variable locally: - - cd /path/to/repo - git config --local gitlab.user USERNAME - - Make sure you use the correct USERNAME for this forge/host. It might -not be the same as on "github.com"! - -Setting your Github Enterprise Username ---------------------------------------- - -For Github Enterprise instances you have to specify where the API can be -accessed and a different variable has to be used to set the username. - - For example if the API is available at ‘https://example.com/api/v3’, -then you should do this: - - git config --global github.example.com/api/v3.user USERNAME - - Make sure you use the correct USERNAME for this instance. It might -not be the same as on "github.com"! - - Doing this only tells Ghub who you are on this host, additionally you -have to tell Ghub which repositories are connected to that forge/host, -like so: - - cd /path/to/repo - git config --local github.host example.com/api/v3 - -Setting your Username for Other Hosts and/or Forges ---------------------------------------------------- - -To inform Ghub about your username on HOST (a FORGE instance) do this: - - git config --global FORGE.HOST.user USERNAME - - FORGE can be one of ‘bitbucket’, ‘gitea’ or ‘gogs’. It can also be -‘github’ or ‘gitlab’; but if that is the case, then you should look at -the preceding sections instead, which discuss these cases specifically. - - HOST identifies the instance. This actually points at the top-level -endpoint of the API and may contain path components, e.g.: -‘example.com/api’. - - If you need to identify as another user in a particular repository, -then you have to set that variable locally: - - cd /path/to/repo - git config --local FORGE.HOST.user USERNAME - - -File: ghub.info, Node: Creating and Storing a Token, Next: Github Configuration Variables, Prev: Setting the Username, Up: Getting Started - -2.3 Creating and Storing a Token -================================ - -2.3.1 Creating a Token ----------------------- - -To create a token, use the web interface of the host you want to connect -to. Here is a list of pages to do this for certain popular hosts: - - • - • - - For other forges we cannot provide a functioning URL because they -contain unknown values such as your name. Just go to the general -settings page of the respective host and then go from there. - - Except on ‘gitea’ and ‘gogs’ each token can be limited to certain -"scopes", i.e., it is possible to limit for which purposes any given -token can be used. - - Before you create a token to be used for a certain package, you -should consult the documentation of that package, which in turn should -tell you which scopes are needed and why. The Forge package for example -does so in *note (forge)Token Creation::. - -2.3.2 Storing a Token ---------------------- - -Please also see *note (auth)Top:: for all the gory details about -Auth-Source. - - The variable ‘auth-sources’ controls how and where Auth-Source keeps -its secrets. The default value is a list of three files: -‘("~/.authinfo" "~/.authinfo.gpg" "~/.netrc")’, but to avoid confusion -you should make sure that only one of these files exists, and then you -should also adjust the value of the variable to only ever use that file, -for example: - - (setq auth-sources '("~/.authinfo")) - - In ‘~/.authinfo’ secrets are stored in plain text. If you don’t want -that, then you should use the encrypted ‘~/.authinfo.gpg’ instead: - - (setq auth-sources '("~/.authinfo.gpg")) - - Auth-Source also supports storing secrets in various external -key-chains. See *note (auth)Top:: for more information. - - The default Auth-Source backends only support storing three values -per entry; the "machine", the "login" and the "password". Because Ghub -uses separate tokens for each package, it has to squeeze four values -into those three slots, and it does that by using "USERNAME^PACKAGE" as -the "login". - - Assuming your *Github* username is "ziggy", the package is named -"forge", and you want to access *Github.com* with the *token* -"012345abcdef...", an entry in one of the three mentioned files would -then look like this: - - machine api.github.com login ziggy^forge password 012345abcdef... - - Assuming your *Gitlab* username is "ziggy", the package is named -"forge", and you want to access *Gitlab.com* with the *token* -"012345abcdef...", an entry in one of the three mentioned files would -then look like this: - - machine gitlab.com/api/v4 login ziggy^forge password 012345abcdef... - - -File: ghub.info, Node: Github Configuration Variables, Prev: Creating and Storing a Token, Up: Getting Started - -2.4 Github Configuration Variables -================================== - -The username and, unless you only use Github.com itself, the Github -Enterprise instance have to be configured using Git variables. In rare -cases it might also be necessary to specify the identity of the local -machine, which is done using a lisp variable. - - -- Variable: github.user - The Github.com username. This should be set globally and if you - have multiple Github.com user accounts, then you should set this - locally only for those repositories that you want to access using - the secondary identity. - - -- Variable: github.HOST.user - This variable serves the same purpose as ‘github.user’ but for the - Github Enterprise instance identified by ‘HOST’. - - The reason why separate variables are used is that this makes it - possible to set both values globally instead of having to set one - of the values locally in each and every repository that is - connected to the Github Enterprise instance, not Github.com. - - -- Variable: github.host - This variable should only be set locally for a repository and - specifies the Github Enterprise edition that that repository is - connected to. You should not set this globally because then each - and every repository becomes connected to the specified Github - Enterprise instance, including those that should actually be - connected to Github.com. - - When this is undefined, then "api.github.com" is used (defined in - the constant ‘ghub-default-host’, which you should never attempt to - change.) - - -File: ghub.info, Node: API, Next: Notes, Prev: Getting Started, Up: Top - -3 API -***** - -* Menu: - -* Their APIs:: -* Making REST Requests:: -* Making GraphQL Requests:: -* Github Convenience Wrappers:: - - -File: ghub.info, Node: Their APIs, Next: Making REST Requests, Up: API - -3.1 Their APIs -============== - -Of course this manual does not cover the APIs of all forges that it -supports, but for your convenience, here are the links to their API -manuals: - - • Github: - • (GraphQl) - • (REST) - • Gitlab: - • - • Gitea: - • - • - • Gogs: - • - • Bitbucket: - • - - -File: ghub.info, Node: Making REST Requests, Next: Making GraphQL Requests, Prev: Their APIs, Up: API - -3.2 Making REST Requests -======================== - - -- Function: ghub-request method resource &optional params &key query - payload headers unpaginate noerror reader username auth host - callback errorback url value error extra method* - This function makes a request for ‘RESOURCE’ using ‘METHOD’. - ‘PARAMS’, ‘QUERY’, ‘PAYLOAD’ and/or ‘HEADERS’ are alists holding - additional request data. The response body is returned and the - response headers are stored in the variable - ‘ghub-response-headers’. - - • ‘METHOD’ is the HTTP method, given as a string. - • ‘RESOURCE’ is the resource to access, given as a string - beginning with a slash. - - • ‘PARAMS’, ‘QUERY’, ‘PAYLOAD’ and ‘HEADERS’ are alists and are - used to specify request data. All these arguments are alists - that resemble the JSON expected and returned by the Github - API. The keys are symbols and the values stored in the ‘cdr’ - (not the ‘cadr’) can be strings, integers, or lists of strings - and integers. - - The Github API documentation is vague on how data has to be - transmitted and for a particular resource usually just talks - about "parameters". Generally speaking when the ‘METHOD’ is - "HEAD" or "GET", then they have to be transmitted as a query, - otherwise as a payload. - - • Use ‘PARAMS’ to automatically transmit like ‘QUERY’ or - ‘PAYLOAD’ would depending on ‘METHOD’. - • Use ‘QUERY’ to explicitly transmit data as a query. - • Use ‘PAYLOAD’ to explicitly transmit data as a payload. - Instead of an alist, ‘PAYLOAD’ may also be a string, in - which case it gets encoded as UTF-8 but is otherwise - transmitted as-is. - • Use ‘HEADERS’ for those rare resources that require that - the data is transmitted as headers instead of as a query - or payload. When that is the case, then the Github API - documentation usually mentions it explicitly. - - • If ‘SILENT’ is non-‘nil’, then progress reports and the like - are not messaged. - - • If ‘UNPAGINATE’ is ‘t’, then this function makes as many - requests as necessary to get all values. If ‘UNPAGINATE’ is a - natural number, then it gets at most that many pages. For any - other non-‘nil’ value it raises an error. - - • If ‘NOERROR’ is non-‘nil’, then no error is raised if the - request fails and ‘nil’ is returned instead. If ‘NOERROR’ is - ‘return’, then the error payload is returned instead of ‘nil’. - - • If ‘READER’ is non-‘nil’, then it is used to read and return - from the response buffer. The default is - ‘ghub--read-json-payload’. For the very few resources that do - not return JSON, you might want to use ‘ghub--decode-payload’. - - • If ‘USERNAME’ is non-‘nil’, then the request is made on behalf - of that user. It is better to specify the user using the Git - variable ‘github.user’ for "api.github.com", or - ‘github.HOST.user’ if connecting to a Github Enterprise - instance. - - • Each package that uses Ghub should use its own token. If - ‘AUTH’ is ‘nil’ or unspecified, then the generic ‘ghub’ token - is used instead. This is only acceptable for personal - utilities. A package that is distributed to other users - should always use this argument to identify itself, using a - symbol matching its name. - - Package authors who find this inconvenient should write a - wrapper around this function and possibly for the - method-specific functions as well. - - Beside ‘nil’, some other symbols have a special meaning too. - ‘none’ means to make an unauthorized request. ‘basic’ means - to make a password based request. If the value is a string, - then it is assumed to be a valid token. ‘basic’ and an - explicit token string are only intended for internal and - debugging uses. - - If ‘AUTH’ is a package symbol, then the scopes are specified - using the variable ‘AUTH-github-token-scopes’. It is an error - if that is not specified. See ‘ghub-github-token-scopes’ for - an example. - - • If ‘HOST’ is non-‘nil’, then connect to that Github instance. - This defaults to "api.github.com". When a repository is - connected to a Github Enterprise instance, then it is better - to specify that using the Git variable ‘github.host’ instead - of using this argument. - - • If ‘FORGE’ is ‘gitlab’, then connect to Gitlab.com or, - depending on ‘HOST’, to another Gitlab instance. - - • If ‘CALLBACK’ and/or ‘ERRORBACK’ is non-‘nil’, then this - function makes one or more asynchronous requests and calls - ‘CALLBACK’ or ‘ERRORBACK’ when finished. If no error - occurred, then it calls ‘CALLBACK’, unless that is ‘nil’. - - If an error occurred, then it calls ‘ERRORBACK’, or if that is - ‘nil’, then ‘CALLBACK’. ‘ERRORBACK’ can also be ‘t’, in which - case it signals instead. ‘NOERROR’ is ignored for all - asynchronous requests. - - Both callbacks are called with four arguments. - - 1. For ‘CALLBACK’, the combined value of the retrieved - pages. For ‘ERRORBACK’, the error that occurred when - retrieving the last page. - 2. The headers of the last page as an alist. - 3. Status information provided by ‘url-retrieve’. Its - ‘:error’ property holds the same information as the first - argument to ‘ERRORBACK’. - 4. A ‘ghub--req’ struct, which can be passed to - ‘ghub-continue’ (which see) to retrieve the next page, if - any. - - -- Function: ghub-continue args - If there is a next page, then this function retrieves that. - - This function is only intended to be called from callbacks. If - there is a next page, then that is retrieved and the buffer that - the result will be loaded into is returned, or ‘t’ if the process - has already completed. If there is no next page, then return - ‘nil’. - - Callbacks are called with four arguments (see ‘ghub-request’). The - forth argument is a ‘ghub--req’ struct, intended to be passed to - this function. A callback may use the struct’s ‘extra’ slot to - pass additional information to the callback that will be called - after the next request. Use the function ‘ghub-req-extra’ to get - and set the value of that slot. - - As an example, using ‘ghub-continue’ in a callback like so: - - (ghub-get "/users/tarsius/repos" nil - :callback (lambda (value _headers _status req) - (unless (ghub-continue req) - (setq my-value value)))) - - is equivalent to: - - (ghub-get "/users/tarsius/repos" nil - :unpaginate t - :callback (lambda (value _headers _status _req) - (setq my-value value))) - - To demonstrate how to pass information from one callback to the - next, here we record when we start fetching each page: - - (ghub-get "/users/tarsius/repos" nil - :extra (list (current-time)) - :callback (lambda (value _headers _status req) - (push (current-time) (ghub-req-extra req)) - (unless (ghub-continue req) - (setq my-times (ghub-req-extra req)) - (setq my-value value)))) - - -- Variable: ghub-response-headers - A select few Github API resources respond by transmitting data in - the response header instead of in the response body. Because there - are so few of these inconsistencies, ‘ghub-request’ always returns - the response body. - - To access the response headers use this variable after - ‘ghub-request’ has returned. - - -- Function: ghub-response-link-relations req headers payload - This function returns an alist of the link relations in ‘HEADERS’, - or if optional ‘HEADERS’ is ‘nil’, then those in - ‘ghub-response-headers’. - - When accessing a Bitbucket instance then the link relations are in - ‘PAYLOAD’ instead of ‘HEADERS’, making their API merely RESTish and - forcing this function to append those relations to the value of - ‘ghub-response-headers’, for later use when this function is called - with ‘nil’ for ‘PAYLOAD’. - - -File: ghub.info, Node: Making GraphQL Requests, Next: Github Convenience Wrappers, Prev: Making REST Requests, Up: API - -3.3 Making GraphQL Requests -=========================== - - -- Function: ghub-graphql graphql &optional variables &key username - auth host callback silent callback errorback value extra - This function makes a GraphQL request using ‘GRAPHQL’ and - ‘VARIABLES’ as inputs. ‘GRAPHQL’ is a GraphQL string. ‘VARIABLES’ - is a JSON-like alist. The other arguments behave as for - ‘ghub-request’ (which see). - - The response is returned as a JSON-like alist. Even if the - response contains ‘errors’, this function does not raise an error. - Cursor-handling is likewise left to the caller. - - ‘ghub-graphql’ is a thin convenience wrapper around ‘ghub-request’, -similar to ‘ghub-post’ and friends. While the latter only hard-code the -value of the ‘METHOD’ argument, the former also hard-codes ‘RESOURCE’ -and constructs ‘PAYLOAD’ from ‘GRAPHQL’ and ‘VARIABLES’. It also drops -‘UNPAGINATE’, ‘NOERROR’, ‘READER’ (internal functions expect alist-ified -JSON) and ‘FORGE’ (only Github currently supports GraphQL). - - ‘ghub-graphql’ does not account for the fact that pagination works -differently in GraphQL than it does in REST, so users of this function -have to deal with that themselves. Likewise error handling works -differently and has to be done by the caller too. - - An early attempt at implementing automatic unpaginating for GraphQL -can be found in the ‘faithful-graphql’ branch, provided I haven’t -deleted that by now. On that branch I try to do things as intended by -the designers of GraphQL, using variables and fragments, and drowning in -a sea of boilerplate. - - The problem with that approach is that it only works for applications -that fetch specific information on demand and actually want things to be -paginated. I am convinced that GraphQL is very nice for web apps. - - However the Forge package for which I have implemented all of this -has very different needs. It wants to fetch "all the data" and "cache" -it locally, so that it is available even when there is no internet -connection. GraphQL was designed around the idea that you should be -able to "ask for what you need and get exactly that". But when that -boils down to "look, if I persist, then you are going to hand me over -all the data anyway, so just caught it up already", then things start to -fall apart. If Github’s GraphQL allowed pagination to be turned off -completely, then teaching ‘ghub-graphql’ about error handling would be -enough. - - But it doesn’t and when doing things as intended, then that leads to -huge amounts of repetitive boilerplate, which is so boring to write that -doing it without introducing bugs left and right is near impossible; so -I decided to give up on GraphQL variables, fragments and conditions, and -instead implement something more powerful, though also more opinionated. - - -- Function: ghub--graphql-vacuum query variables callback &optional - until &key narrow username auth host forge - This function is an opinionated alternative to ‘ghub-graphql’. It - relies on dark magic to get the job done. - - It makes an initial request using ‘QUERY’. It then looks for - paginated edges in the returned data and makes more requests to - resolve them. In order to do so it automatically transforms the - initial ‘QUERY’ into another query suitable for that particular - edge. The data retrieved by subsequent requests is then injected - into the data of the original request before that is returned or - passed to the callback. If subsequently retrieved data features - new paginated edges, then those are followed recursively. - - The end result is essentially the same as using ‘ghub-graphql’, if - only it were possible to say "do not paginate anything". The - implementation is much more complicated because it is not possible - to do that. - - ‘QUERY’ is a GraphQL query expressed as an s-expression. The - bundled ‘gsexp’ library is used to turn that into a GraphQL query - string. Only a subset of the GraphQL features are supported; - fragments for example are not, and magical stuff happens to - variables. This is not documented yet, I am afraid. Look at - existing callers. - - ‘VARIABLES’ is a JSON-like alist as for ‘ghub-graphql’. - - ‘UNTIL’ is an alist ‘((EDGE-until . VALUE)...)’. When unpaginating - ‘EDGE’ try not to fetch beyond the element whose first field has - the value ‘VALUE’ and remove that element as well as all "lesser" - elements from the retrieved data if necessary. Look at - ‘forge--pull-repository’ for an example. This is only useful if - you "cache" the response locally and want to avoid fetching data - again that you already have. - - Other arguments behave as for ‘ghub-graphql’ and ‘ghub-request’, - more or less. If ‘CALLBACK’ is ‘nil’, pretty-print the response. - - Using ‘ghub--graphql-vacuum’, the following resource specific -functions are implemented. These functions are not part of the public -API yet and are very much subject to change. - - -- Function: ghub-fetch-repository owner name callback &optional until - &key username auth host forge - This function asynchronously fetches forge data about the specified - repository. Once all data has been collected, ‘CALLBACK’ is called - with the data as the only argument. - - -- Function: ghub-fetch-issue owner name callback &optional until &key - username auth host forge - This function asynchronously fetches forge data about the specified - issue. Once all data has been collected, ‘CALLBACK’ is called with - the data as the only argument. - - -- Function: ghub-fetch-pullreq owner name callback &optional until - &key username auth host forge - This function asynchronously fetches forge data about the specified - pull-request. Once all data has been collected, ‘CALLBACK’ is - called with the data as the only argument. - - Note that in order to avoid duplication all of these functions base -their initial query on the query stored in ‘ghub-fetch-repository’. The -latter two pass that query through ‘ghub--graphql-prepare-query’, which -then uses ‘ghub--graphql-narrow-query’ to remove parts the caller is not -interested in. These two functions are also used internally, when -unpaginating, but as demonstrated here they can be useful even before -making an initial request. - - -File: ghub.info, Node: Github Convenience Wrappers, Prev: Making GraphQL Requests, Up: API - -3.4 Github Convenience Wrappers -=============================== - - -- Function: ghub-head resource &optional params &key query payload - headers unpaginate noerror reader username auth host callback - errorback - -- Function: ghub-get resource &optional params &key query payload - headers unpaginate noerror reader username auth host callback - errorback - These functions are simple wrappers around ‘ghub-request’. Their - signature is identical to that of the latter, except that they do - not have an argument named ‘METHOD’. The HTTP method is instead - given by the second word in the function name. - - As described in the documentation for ‘ghub-request’, it depends on - the used method whether the value of the ‘PARAMS’ argument is used - as the query or the payload. For the "HEAD" and "GET" methods it - is used as the query. - - -- Function: ghub-put resource &optional params &key query payload - headers unpaginate noerror reader username auth host callback - errorback - -- Function: ghub-post resource &optional params &key query payload - headers unpaginate noerror reader username auth host callback - errorback - -- Function: ghub-patch resource &optional params &key query payload - headers unpaginate noerror reader username auth host callback - errorback - -- Function: ghub-delete resource &optional params &key query payload - headers unpaginate noerror reader username auth host callback - errorback - These functions are simple wrappers around ‘ghub-request’. Their - signature is identical to that of the latter, except that they do - not have an argument named ‘METHOD’. The HTTP method is instead - given by the second word in the function name. - - As described in the documentation for ‘ghub-request’, it depends on - the used method whether the value of the ‘PARAMS’ argument is used - as the query or the payload. For the "PUT", "POST", "PATCH" and - "DELETE" methods it is used as the payload. - - -- Function: ghub-wait resource &optional duration &key username auth - host - Some API requests result in an immediate successful response even - when the requested action has not actually been carried out yet. - An example is the request for the creation of a new repository, - which doesn’t cause the repository to immediately become available. - The Github API documentation usually mentions this when describing - an affected resource. - - If you want to do something with some resource right after making a - request for its creation, then you might have to wait for it to - actually be created. This function can be used to do so. It - repeatedly tries to access the resource until it becomes available - or until a timeout is reached. In the latter case it signals - ‘ghub-error’. - - ‘RESOURCE’ specifies the resource that this function waits for. - - ‘DURATION’ specifies the maximum number of seconds to wait for, - defaulting to 64 seconds. Emacs will block during that time, but - the user can abort using ‘C-g’. - - The first attempt is made immediately and will often succeed. If - not, then another attempt is made after two seconds, and each - subsequent attempt is made after waiting as long as we already - waited between all preceding attempts combined. - - See ‘ghub-request’’s documentation above for information about the - other arguments. - - -File: ghub.info, Node: Notes, Next: Function Index, Prev: API, Up: Top - -4 Notes -******* - -* Menu: - -* Using Ghub in Personal Scripts:: -* Using Ghub in Your Own Package:: -* Forge Limitations and Notes:: - - -File: ghub.info, Node: Using Ghub in Personal Scripts, Next: Using Ghub in Your Own Package, Up: Notes - -4.1 Using Ghub in Personal Scripts -================================== - -You can of course use ‘ghub-request’ and its wrapper functions in your -personal scripts. Unlike when you use Ghub in a package that you -distribute for others to use, you don’t have to explicitly specify a -package in personal scripts. - - ;; This is perfectly acceptable in personal scripts ... - (ghub-get "/user") - - ;; ... and actually equals to - (ghub-get "/user" nil :auth 'ghub) - - ;; In packages you have to specify the package using AUTH. - (ghub-get "/user" nil :auth 'some-package) - - When the ‘AUTH’ argument is not specified, then a request is made on -behalf of the ‘ghub’ package itself. Like for any other package you -have to create a dedicated token of course. - - -File: ghub.info, Node: Using Ghub in Your Own Package, Next: Forge Limitations and Notes, Prev: Using Ghub in Personal Scripts, Up: Notes - -4.2 Using Ghub in Your Own Package -================================== - -Every package should use its own token. This allows you as the author -of some package to only request access to API scopes that are actually -needed, which in turn might make it easier for users to trust your -package not to do unwanted things. - - You have to tell ‘ghub-request’ on behalf of which package a request -is being made by passing the symbol ‘PACKAGE’ as the value of its ‘AUTH’ -argument. - - (ghub-request "GET" "/user" nil :auth 'PACKAGE) - - Keep in mind that the users of your package will have to manually -create a suitable token. To make that easier, you should not only link -to this manual but also prominently mention the scopes the token needs; -and explain what they are needed for. - - -File: ghub.info, Node: Forge Limitations and Notes, Prev: Using Ghub in Your Own Package, Up: Notes - -4.3 Forge Limitations and Notes -=============================== - - • There are no default Gitea and Gogs instances so the variables - ‘gitea.host’ and ‘gogs.host’ are not taken into account. - - • Gitea and Gogs do not support limiting a token to certain scopes. - - • The Bitbucket API is fairly broken and my willingness to deal with - that is extremely limited unless someone pays me vast amounts of - money. - - • The Gitlab API documentation is not always accurate, though I don’t - have an example at hand. It also isn’t structured well, making it - occasionally difficult to find the information one is looking for. - - • Where one would use ‘user/repo’ when accessing another forge, one - has to use ‘user%2Frepo’ when accessing Gitlab, e.g.: - - (ghub-get "/projects/python-mode-devs%2Fpython-mode" - nil :forge 'gitlab) - - -File: ghub.info, Node: Function Index, Next: Variable Index, Prev: Notes, Up: Top - -Appendix A Function Index -************************* - -[index] -* Menu: - -* ghub--graphql-vacuum: Making GraphQL Requests. - (line 56) -* ghub-continue: Making REST Requests. - (line 122) -* ghub-delete: Github Convenience Wrappers. - (line 31) -* ghub-fetch-issue: Making GraphQL Requests. - (line 105) -* ghub-fetch-pullreq: Making GraphQL Requests. - (line 111) -* ghub-fetch-repository: Making GraphQL Requests. - (line 99) -* ghub-get: Github Convenience Wrappers. - (line 9) -* ghub-graphql: Making GraphQL Requests. - (line 6) -* ghub-head: Github Convenience Wrappers. - (line 6) -* ghub-patch: Github Convenience Wrappers. - (line 28) -* ghub-post: Github Convenience Wrappers. - (line 25) -* ghub-put: Github Convenience Wrappers. - (line 22) -* ghub-request: Making REST Requests. - (line 6) -* ghub-response-link-relations: Making REST Requests. - (line 172) -* ghub-wait: Github Convenience Wrappers. - (line 44) - - -File: ghub.info, Node: Variable Index, Prev: Function Index, Up: Top - -Appendix B Variable Index -************************* - -[index] -* Menu: - -* ghub-response-headers: Making REST Requests. - (line 163) -* github.host: Github Configuration Variables. - (line 26) -* github.HOST.user: Github Configuration Variables. - (line 17) -* github.user: Github Configuration Variables. - (line 11) - - - -Tag Table: -Node: Top773 -Node: Introduction2207 -Node: Getting Started2612 -Node: Basic Concepts Arguments and Variables3639 -Node: Setting the Username7643 -Ref: Setting your Githubcom Username8131 -Ref: Setting your Gitlabcom Username8487 -Ref: Setting your Github Enterprise Username8953 -Ref: Setting your Username for Other Hosts and/or Forges9686 -Node: Creating and Storing a Token10492 -Ref: Creating a Token10705 -Ref: Storing a Token11622 -Node: Github Configuration Variables13365 -Node: API15076 -Node: Their APIs15278 -Node: Making REST Requests15960 -Node: Making GraphQL Requests25270 -Node: Github Convenience Wrappers31959 -Node: Notes35623 -Node: Using Ghub in Personal Scripts35830 -Node: Using Ghub in Your Own Package36721 -Node: Forge Limitations and Notes37656 -Node: Function Index38668 -Node: Variable Index40936 - -End Tag Table - - -Local Variables: -coding: utf-8 -End: diff --git a/elpa/ghub-20251130.1842/gsexp.el b/elpa/ghub-20251130.1842/gsexp.el deleted file mode 100644 index a1fac790..00000000 --- a/elpa/ghub-20251130.1842/gsexp.el +++ /dev/null @@ -1,102 +0,0 @@ -;;; gsexp.el --- GraphQl as S-expressions -*- lexical-binding:t -*- - -;; Copyright (C) 2016-2025 Jonas Bernoulli - -;; Author: Jonas Bernoulli -;; Homepage: https://github.com/magit/ghub -;; Keywords: tools - -;; SPDX-License-Identifier: GPL-3.0-or-later - -;; This file is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published -;; by the Free Software Foundation, either version 3 of the License, -;; or (at your option) any later version. -;; -;; This file 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 General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with this file. If not, see . - -;;; Commentary: - -;; This library encodes S-expressions as GraphQl documents. Currently -;; it is rather incomplete and only intended for use by `ghub-graphql' -;; but I might fill the gaps and add documentation eventually. I am -;; not in a hurry though. - -;;; Code: - -(require 'cl-lib) - -(defvar gsexp-one-variable-per-line nil) - -(defun gsexp-encode (document) - (gsexp--pp (gsexp--encode-field document))) - -(defun gsexp--encode-field (field) - (cond - ((stringp field) - field) - ((symbolp field) - (symbol-name field)) - ((listp (car field)) - (concat (format "%s: " (caar field)) - (gsexp--encode-field (cons (car (cdar field)) - (cdr field))))) - ((concat - (pcase (pop field) - (`[] "") - ((and (pred symbolp) op) (symbol-name op)) - (`[,op] (symbol-name op)) - (`[,op ,name] (format "%s %s" op name))) - (and (vectorp (car field)) - (format " (%s%s)" - (if gsexp-one-variable-per-line "\n" "") - (mapconcat #'gsexp--encode-argument - (append (pop field) nil) - (if gsexp-one-variable-per-line ",\n" ",")))) - (and field - (format " {\n%s\n}" - (mapconcat #'gsexp--encode-field field "\n"))))))) - -(cl-defun gsexp--encode-argument ((argument value)) - (format "%s: %s" argument (gsexp--encode-value value))) - -(defun gsexp--encode-value (value) - (cond - ((numberp value) - (number-to-string value)) - ((symbolp value) ; including variables, enums, booleans and null - (symbol-name value)) - ((stringp value) - (prin1-to-string value)) - ((vectorp value) - (format "(%s)" (mapconcat #'gsexp--encode-value value ""))) - ((listp value) - (format "{%s}" (mapconcat - (pcase-lambda (`(,name ,value)) - (format "%s: %s" name (gsexp--encode-value value))) - value ", "))) - ((error "Invalid field value: %S" value)))) - -(defun gsexp--pp (string) - (with-temp-buffer - (save-excursion - (insert string)) - (while (< (point) (point-max)) - (unless (and (bolp) (eolp)) - (save-excursion - (let ((level (car (syntax-ppss (line-beginning-position))))) - (when (looking-at "\\s-*\\s)") - (cl-decf level)) - (indent-line-to (* 2 level))))) - (forward-line 1)) - (buffer-string))) - -;;; _ -(provide 'gsexp) -;;; gsexp.el ends here diff --git a/elpa/ghub-20260423.1634/.dir-locals.el b/elpa/ghub-20260423.1634/.dir-locals.el new file mode 100644 index 00000000..b6f86eae --- /dev/null +++ b/elpa/ghub-20260423.1634/.dir-locals.el @@ -0,0 +1,11 @@ +((nil + (indent-tabs-mode . nil)) + (emacs-lisp-mode + (lisp-indent-local-overrides . ((cond . 0) (interactive . 0)))) + (makefile-mode + (indent-tabs-mode . t)) + (git-commit-mode + (git-commit-major-mode . git-commit-elisp-text-mode)) + ("CHANGELOG" + (nil (fill-column . 70) + (mode . display-fill-column-indicator)))) diff --git a/elpa/ghub-20260423.1634/README-elpa b/elpa/ghub-20260423.1634/README-elpa new file mode 100644 index 00000000..a1ac7252 --- /dev/null +++ b/elpa/ghub-20260423.1634/README-elpa @@ -0,0 +1,21 @@ +Ghub provides basic support for using the APIs of various Git forges +from Emacs packages. Originally it only supported the Github REST +API, but now it also supports the Github GraphQL API as well as the +REST APIs of Gitlab, Gitea, Gogs and Bitbucket. + +Ghub abstracts access to API resources using only a handful of basic +functions such as `ghub-get'. These are convenience wrappers around +`ghub-request'. + +When accessing Github, then Ghub handles the creation and storage of +access tokens using a setup wizard to make it easier for users to get +started. The tokens for other forges have to be created manually. + +Ghub is intentionally limited to only provide these two essential +features — basic request functions and guided setup — to avoid being +too opinionated, which would hinder wide adoption. It is assumed that +wide adoption would make life easier for users and maintainers alike, +because then all packages that talk to forge APIs could be configured +the same way. + +Please consult the manual (info "ghub") for more information. diff --git a/elpa/ghub-20260423.1634/dir b/elpa/ghub-20260423.1634/dir new file mode 100644 index 00000000..b703bcac --- /dev/null +++ b/elpa/ghub-20260423.1634/dir @@ -0,0 +1,18 @@ +This is the file .../info/dir, which contains the +topmost node of the Info hierarchy, called (dir)Top. +The first time you invoke Info you start off looking at this node. + +File: dir, Node: Top This is the top of the INFO tree + + This (the Directory node) gives a menu of major topics. + Typing "q" exits, "H" lists all Info commands, "d" returns here, + "h" gives a primer for first-timers, + "mEmacs" visits the Emacs manual, etc. + + In Emacs, you can click mouse button 2 on a menu item or cross reference + to select it. + +* Menu: + +Emacs +* Ghub: (ghub). Client library for the Github API. diff --git a/elpa/ghub-20260423.1634/ghub-autoloads.el b/elpa/ghub-20260423.1634/ghub-autoloads.el new file mode 100644 index 00000000..45d57880 --- /dev/null +++ b/elpa/ghub-20260423.1634/ghub-autoloads.el @@ -0,0 +1,52 @@ +;;; ghub-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- +;; Generated by the `loaddefs-generate' function. + +;; This file is part of GNU Emacs. + +;;; Code: + +(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) + + + +;;; Generated autoloads from ghub.el + +(autoload 'ghub-clear-caches "ghub" +"Clear all caches that might negatively affect Ghub. + +If a library that is used by Ghub caches incorrect information +such as a mistyped password, then that can prevent Ghub from +asking the user for the correct information again. + +Set `url-http-real-basic-auth-storage' to nil +and call `auth-source-forget+'." t) +(register-definition-prefixes "ghub" '("auth-source-netrc-parse" "ghub-")) + + +;;; Generated autoloads from ghub-graphql.el + +(register-definition-prefixes "ghub-graphql" '("ghub-")) + + +;;; Generated autoloads from ghub-legacy.el + +(register-definition-prefixes "ghub-legacy" '("ghub-fetch-repository")) + + +;;; Generated autoloads from gsexp.el + +(register-definition-prefixes "gsexp" '("gsexp-")) + +;;; End of scraped data + +(provide 'ghub-autoloads) + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; no-native-compile: t +;; coding: utf-8-emacs-unix +;; End: + +;;; ghub-autoloads.el ends here diff --git a/elpa/ghub-20260423.1634/ghub-graphql.el b/elpa/ghub-20260423.1634/ghub-graphql.el new file mode 100644 index 00000000..c0e02c0c --- /dev/null +++ b/elpa/ghub-20260423.1634/ghub-graphql.el @@ -0,0 +1,356 @@ +;;; ghub-graphql.el --- Access Github API using GraphQL -*- lexical-binding:t -*- + +;; Copyright (C) 2016-2026 Jonas Bernoulli + +;; Author: Jonas Bernoulli +;; Homepage: https://github.com/magit/ghub +;; Keywords: tools + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. +;; +;; This file 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this file. If not, see . + +;;; Commentary: + +;; This library implements GraphQL queries for Github. + +;;; Code: + +(require 'ghub) +(require 'gsexp) +(require 'treepy) + +(eval-when-compile (require 'subr-x)) + +(define-error 'ghub-graphql-error "GraphQL Error" 'ghub-error) + +(defvar ghub-graphql-message-progress nil + "Whether to show \"Fetching page N...\" in echo area during requests. +By default this information is only shown in the mode-line of the buffer +from which the request was initiated, and if you kill that buffer, then +nowhere. That may make it desirable to display the same message in the +echo area as well.") + +(defvar ghub-graphql-items-per-request 50 + "Number of GraphQL items to query for entities that return a collection. + +Adjust this value if you're hitting query timeouts against larger +repositories.") + +(cl-defun ghub-graphql-rate-limit (&key username auth host) + "Return rate limit information." + (let-alist (ghub-query + '(query (rateLimit limit cost remaining resetAt)) nil + :synchronous t :username username :auth auth :host host) + .data.rateLimit)) + +(cl-defstruct (ghub--graphql-req + (:include ghub--req) + (:constructor ghub--make-graphql-req) + (:copier nil)) + (query nil :read-only t) + (query-str nil :read-only nil) + (variables nil :read-only t) + (until nil :read-only t) + (pages 0 :read-only nil) + (paginate nil :read-only nil) + (narrow nil :read-only t)) + +(cl-defun ghub-query + (query &optional variables + &key until narrow headers paginate + callback errorback noerror synchronous + username auth host forge) + (declare (indent defun)) + (unless forge + (setq forge 'github)) + (unless host + (setq host (ghub--host forge))) + (unless (or username (stringp auth) (eq auth 'none)) + (setq username (ghub--username host forge))) + (when (eq callback 'pp) + (setq callback #'ghub--graphql-pp-response) + (setq noerror t)) + (ghub--graphql-retrieve + (ghub--make-graphql-req + :url (ghub--encode-url + host (if (eq forge 'gitlab) "/api/graphql" "/graphql")) + :method "POST" + :headers (ghub--headers headers host auth username forge) + :handler #'ghub--graphql-handle-response + :query query + :variables variables + :until until + :buffer (current-buffer) + :narrow narrow + :paginate (or paginate + (and-let* ((p (and (eq auth 'forge) + (fboundp 'magit-get) + (magit-get "forge.graphqlItemLimit")))) + (string-to-number p))) + :noerror noerror + :synchronous synchronous + :callback callback + :errorback errorback))) + +(cl-defun ghub--graphql-retrieve (req &optional lineage cursor) + (let ((p (cl-incf (ghub--graphql-req-pages req)))) + (when (> p 1) + (when ghub-graphql-message-progress + (let ((message-log-max nil)) + (message "Fetching page %s..." p))) + (ghub--graphql-set-mode-line req "Fetching page %s" p))) + (setf (ghub--graphql-req-query-str req) + (gsexp-encode + (ghub--graphql-prepare-query + (ghub--graphql-req-query req) + lineage cursor))) + (when ghub-debug + (with-current-buffer (get-buffer-create " *gsexp-encode*") + (erase-buffer) + (insert (ghub--graphql-req-query-str req) "\n\n") + (when-let ((payload (ghub--graphql-req-variables req))) + (let ((pos (point))) + (insert (ghub--encode-payload payload) "\n") + (ignore-errors (json-pretty-print pos (point))))))) + (ghub--retrieve + (ghub--encode-payload + `((query . ,(ghub--graphql-req-query-str req)) + ,@(and-let* ((variables (ghub--graphql-req-variables req))) + `((variables . ,variables))))) + req) + (ghub--req-value req)) + +(defun ghub--graphql-prepare-query (query &optional lineage cursor paginate) + (when lineage + (setq query (ghub--graphql-narrow-query query lineage cursor))) + (let ((loc (ghub--alist-zip query)) + variables) + (catch :done + (while t + (let ((node (treepy-node loc))) + (when (and (vectorp node) + (listp (aref node 0))) + (let ((alist (append node ())) + (vars nil)) + (when-let ((edges (cadr (assq :edges alist)))) + (push (list 'first + (apply + #'min + (delq nil (list (and (numberp edges) edges) + paginate + ghub-graphql-items-per-request)))) + vars) + (setq loc (treepy-up loc)) + (setq node (treepy-node loc)) + (setq loc (treepy-replace + loc `(,(car node) + ,(cadr node) + (pageInfo endCursor hasNextPage) + (edges (node ,@(cddr node)))))) + (setq loc (treepy-down loc)) + (setq loc (treepy-next loc))) + (dolist (elt alist) + (cond ((keywordp (car elt))) + ((length= elt 3) + (push (list (nth 0 elt) (nth 1 elt)) vars) + (push (list (nth 1 elt) (nth 2 elt)) variables)) + ((length= elt 2) + (push elt vars)))) + (setq loc (treepy-replace loc (vconcat (nreverse vars))))))) + (if (treepy-end-p loc) + (let ((node (copy-sequence (treepy-node loc)))) + (when variables + (push (vconcat (nreverse variables)) + (cdr node))) + (throw :done node)) + (setq loc (treepy-next loc))))))) + +(defun ghub--graphql-handle-response (status req) + (let ((buf (current-buffer))) + (unwind-protect + (progn + (set-buffer-multibyte t) + (let* ((headers (ghub--handle-response-headers status req)) + (payload (ghub--handle-response-payload req)) + (data (assq 'data payload)) + (err (plist-get status :error)) + (errors (and (not (and (ghub--req-noerror req) + (assq 'data payload))) + (assq 'errors payload)))) + (cond ((or err errors) + (when (and (not err) ghub-debug) + (ignore-errors (json-pretty-print (point) (point-max))) + (pop-to-buffer buf)) + (if (ghub--req-noerror req) + (ghub--graphql-walk-response req data) + (ghub--graphql-handle-failure + req (or err errors) headers status))) + ((ghub--graphql-walk-response req data))))) + (when (and (buffer-live-p buf) + (not (buffer-local-value 'ghub-debug buf))) + (kill-buffer buf))))) + +(defun ghub--graphql-handle-failure (req errors headers status) + (ghub--graphql-set-mode-line req) + (setf (ghub--req-value req) errors) + (cond-let + ([errorback (ghub--req-errorback req)] + (ghub--graphql-run-callback req errorback errors headers status req)) + ((ghub--req-noerror req) + (when-let ((callback (ghub--req-callback req))) + (ghub--graphql-run-callback req callback errors))) + ((ghub--signal-error (if (eq (car errors) 'errors) + (cons 'ghub-graphql-error (cdr errors)) + errors))))) + +(defun ghub--graphql-handle-success (req data) + (ghub--graphql-set-mode-line req) + (when-let ((narrow (ghub--graphql-req-narrow req))) + (while-let ((key (pop narrow))) + (setq data (cdr (assq key data))))) + (setf (ghub--req-value req) data) + (when-let ((callback (ghub--req-callback req))) + (ghub--graphql-run-callback req callback data))) + +(defun ghub--graphql-run-callback (req callback &rest args) + (let ((buffer (ghub--req-buffer req))) + (if (buffer-live-p buffer) + (with-current-buffer buffer + (apply callback args)) + (apply callback args)))) + +(defun ghub--graphql-set-mode-line (req &optional format &rest args) + (let ((buffer (ghub--graphql-req-buffer req))) + (when (buffer-live-p buffer) + (with-current-buffer buffer + (setq mode-line-process + (and format (concat " " (apply #'format format args)))) + (force-mode-line-update t))))) + +(defun ghub--graphql-pp-response (data) + (pp-display-expression data "*Pp Eval Output*")) + +(defun ghub--graphql-walk-response (req data) + (let* ((loc (ghub--req-value req)) + (loc (if (not loc) + (ghub--alist-zip data) + (setq data (ghub--graphql-narrow-data + data (ghub--graphql-lineage loc))) + (setf (alist-get 'edges data) + (append (alist-get 'edges (treepy-node loc)) + (or (alist-get 'edges data) + (error "BUG: Expected new nodes")))) + (treepy-replace loc data)))) + (catch :done + (while t + (when (eq (car-safe (treepy-node loc)) 'edges) + (setq loc (treepy-up loc)) + (pcase-let ((`(,key . ,val) (treepy-node loc))) + (let-alist val + (let* ((cursor (and .pageInfo.hasNextPage + .pageInfo.endCursor)) + (until (cdr (assq (intern (format "%s-until" key)) + (ghub--graphql-req-until req)))) + (nodes (mapcar #'cdar .edges)) + (nodes (if until + (seq-take-while + (lambda (node) + (or (string> (cdr (assq 'updatedAt node)) + until) + (setq cursor nil))) + nodes) + nodes))) + (cond (cursor + (setf (ghub--req-value req) loc) + (ghub--graphql-retrieve req + (ghub--graphql-lineage loc) + cursor) + (throw :done nil)) + ((setq loc (treepy-replace loc (cons key nodes))))))))) + (if (treepy-end-p loc) + (progn (ghub--graphql-handle-success req (treepy-root loc)) + (throw :done nil)) + (setq loc (treepy-next loc))))))) + +(defun ghub--graphql-lineage (loc) + (let (lineage) + (while (treepy-up loc) + (push (car (treepy-node loc)) lineage) + (setq loc (treepy-up loc))) + lineage)) + +(defun ghub--graphql-narrow-data (data lineage) + (while-let ((key (pop lineage))) + (if (consp (car lineage)) + (progn (pop lineage) + (setf data (cadr data))) + (setq data (assq key (cdr data))))) + data) + +(defun ghub--graphql-narrow-query (query lineage &optional cursor) + (if (consp (car lineage)) + (let* ((child (cddr query)) + (alist (append (cadr query) ())) + (single (cdr (assq :singular alist)))) + `(,(car single) + ,(vector (list (cadr single) (cdr (car lineage)))) + ,@(if (cdr lineage) + (ghub--graphql-narrow-query child (cdr lineage) cursor) + child))) + (let* ((child (or (assq (car lineage) (cdr query)) + ;; Alias + (cl-find-if (lambda (c) + (eq (car-safe (car-safe c)) + (car lineage))) + query) + ;; Edges + (cl-find-if (lambda (c) + (and (listp c) + (vectorp (cadr c)) + (eq (cadr (assq :singular + (append (cadr c) ()))) + (car lineage)))) + (cdr query)) + (error "BUG: Failed to narrow query"))) + (object (car query)) + (args (and (vectorp (cadr query)) + (cadr query)))) + `(,object + ,@(and args (list args)) + ,(cond ((cdr lineage) + (ghub--graphql-narrow-query child (cdr lineage) cursor)) + (cursor + `(,(car child) + ,(vconcat `((after ,cursor)) + (cadr child)) + ,@(cddr child))) + (t + child)))))) + +(defun ghub--alist-zip (root) + (let ((branchp (##and (listp %) (listp (cdr %)))) + (make-node (lambda (_ children) children))) + (treepy-zipper branchp #'identity make-node root))) + +;;; _ +(provide 'ghub-graphql) +;; Local Variables: +;; read-symbol-shorthands: ( +;; ("and-let" . "cond-let--and-let") +;; ("if-let" . "cond-let--if-let") +;; ("when-let" . "cond-let--when-let") +;; ("while-let" . "cond-let--while-let")) +;; End: +;;; ghub-graphql.el ends here diff --git a/elpa/ghub-20260423.1634/ghub-legacy.el b/elpa/ghub-20260423.1634/ghub-legacy.el new file mode 100644 index 00000000..1fa2ff7a --- /dev/null +++ b/elpa/ghub-20260423.1634/ghub-legacy.el @@ -0,0 +1,384 @@ +;;; ghub-legacy.el --- Deprecated Ghub functions -*- lexical-binding:t -*- + +;; Copyright (C) 2016-2026 Jonas Bernoulli + +;; Author: Jonas Bernoulli +;; Homepage: https://github.com/magit/ghub +;; Keywords: tools + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. +;; +;; This file 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this file. If not, see . + +;;; Commentary: + +;; Deprecated Ghub functions. + +;;; Code: + +(require 'ghub-graphql) + +(cl-defun ghub-graphql (graphql + &optional variables + &key username auth host forge + headers silent + callback errorback value extra) + (declare (obsolete ghub-query "Ghub 5.1.0")) + (cl-assert (not (stringp variables))) + (cl-assert (or (stringp graphql) + (memq (car-safe graphql) '(query mutation)))) + (unless (stringp graphql) + (setq graphql (gsexp-encode (ghub--graphql-prepare-query graphql)))) + (ghub-request "POST" + (if (eq forge 'gitlab) "/api/graphql" "/graphql") + nil + :payload `((query . ,graphql) + ,@(and variables `((variables ,@variables)))) + :headers headers :silent silent + :username username :auth auth :host host :forge forge + :callback callback :errorback errorback + :extra extra :value value)) + +(cl-defun ghub--graphql (graphql + &optional variables + &key username auth host forge + headers + callback errorback) + (declare (obsolete ghub-query "Ghub 5.1.0")) + (ghub--graphql-vacuum graphql variables callback nil + :username username + :auth auth + :host host + :forge forge + :headers headers + :errorback errorback)) + +(cl-defun ghub--graphql-vacuum + ( query variables callback + &optional until + &key narrow username auth host forge + headers paginate errorback noerror synchronous) + (declare (obsolete ghub-query "Ghub 5.1.0")) + (ghub-query query variables + :until until :narrow narrow :headers headers :paginate paginate + :callback callback + :errorback errorback :noerror noerror :synchronous synchronous + :username username :auth auth :host host :forge forge)) + +(defconst ghub-fetch-repository-sparse + '(query + (repository + [(owner $owner String!) + (name $name String!)] + name + id + createdAt + updatedAt + nameWithOwner + (parent nameWithOwner) + description + homepageUrl + (defaultBranchRef name) + isArchived + isFork + isLocked + isMirror + isPrivate + hasDiscussionsEnabled + hasIssuesEnabled + hasWikiEnabled + (licenseInfo name) + (stargazers totalCount) + (watchers totalCount)))) + +(defconst ghub-fetch-repository + `(query + (repository + ,@(cdr (cadr ghub-fetch-repository-sparse)) + (assignableUsers [(:edges t)] + id + login + name) + (discussionCategories [(:edges t)] + id + name + emoji + isAnswerable + description) + (discussions [(:edges t) + (:singular discussion number) + (orderBy ((field UPDATED_AT) (direction DESC)))] + id + databaseId + number + url + stateReason + ;; Discussions lack isReadByViewer. + (answer id) + (author login) + title + createdAt + updatedAt + closedAt + locked + (category id) + body + (comments [(:edges t)] + id + databaseId + (author login) + createdAt + updatedAt + body + (replies [(:edges 20)] + id + databaseId + (author login) + createdAt + updatedAt + body)) + (labels [(:edges t)] + id)) + (issues [(:edges t) + (:singular issue number) + (orderBy ((field UPDATED_AT) (direction DESC)))] + number + id + state + stateReason + isReadByViewer + (author login) + title + createdAt + updatedAt + closedAt + locked + (milestone id) + body + (assignees [(:edges t)] + id) + (comments [(:edges t)] + id + databaseId + (author login) + createdAt + updatedAt + body) + (labels [(:edges t)] + id)) + (labels [(:edges t) + (:singular label id)] + id + name + color + description) + (milestones [(:edges t) + (:singular milestone id)] + id + number + title + createdAt + updatedAt + dueOn + closedAt + description) + (pullRequests [(:edges t) + (:singular pullRequest number) + (orderBy ((field UPDATED_AT) (direction DESC)))] + number + id + state + isReadByViewer + (author login) + title + createdAt + updatedAt + closedAt + mergedAt + isDraft + locked + maintainerCanModify + isCrossRepository + (milestone id) + body + (baseRef name + (repository nameWithOwner)) + baseRefOid + (headRef name + (repository (owner login) + nameWithOwner)) + headRefOid + (assignees [(:edges t)] + id) + (reviewRequests [(:edges t)] + (requestedReviewer "... on User { id }\n")) + (comments [(:edges t)] + id + databaseId + (author login) + createdAt + updatedAt + body) + (labels [(:edges t)] + id))))) + +(defconst ghub-fetch-repository-review-threads + '(query + (repository + [(owner $owner String!) + (name $name String!)] + (pullRequests [(:edges t) + (:singular pullRequest number) + (orderBy ((field UPDATED_AT) (direction DESC)))] + number + baseRefOid + headRefOid + (reviewThreads [(:edges t)] + id + line + originalLine + diffSide + (resolvedBy login) + (comments [(:edges t)] + id + databaseId + (author login) + createdAt + updatedAt + body + (replyTo databaseId) + (originalCommit oid) + path)))))) + +(cl-defun ghub-fetch-repository ( owner name callback + &optional until + &key username auth host forge + headers paginate errorback sparse) + "Asynchronously fetch forge data about the specified repository. +Once all data has been collected, CALLBACK is called with the +data as the only argument." + (declare (obsolete nil "Ghub 5.1.0")) + (ghub-query (if sparse + ghub-fetch-repository-sparse + ghub-fetch-repository) + `((owner . ,owner) + (name . ,name)) + :until until + :narrow '(repository) + :username username + :auth auth + :host host + :forge forge + :headers headers + :paginate paginate + :callback callback + :errorback errorback)) + +(cl-defun ghub-fetch-discussion ( owner name number callback + &optional until + &key username auth host forge + headers errorback) + "Asynchronously fetch forge data about the specified discussion. +Once all data has been collected, CALLBACK is called with the +data as the only argument." + (declare (obsolete nil "Ghub 5.1.0")) + (ghub-query (ghub--graphql-prepare-query + ghub-fetch-repository + `(repository discussions (discussion . ,number))) + `((owner . ,owner) + (name . ,name)) + :until until + :narrow '(repository discussion) + :username username + :auth auth + :host host + :forge forge + :headers headers + :callback callback + :errorback errorback)) + +(cl-defun ghub-fetch-issue ( owner name number callback + &optional until + &key username auth host forge + headers paginate errorback) + "Asynchronously fetch forge data about the specified issue. +Once all data has been collected, CALLBACK is called with the +data as the only argument." + (declare (obsolete nil "Ghub 5.1.0")) + (ghub-query (ghub--graphql-narrow-query + ghub-fetch-repository + `(repository issues (issue . ,number))) + `((owner . ,owner) + (name . ,name)) + :until until + :narrow '(repository issue) + :username username + :auth auth + :host host + :forge forge + :headers headers + :paginate paginate + :callback callback + :errorback errorback)) + +(cl-defun ghub-fetch-pullreq ( owner name number callback + &optional until + &key username auth host forge + headers paginate errorback) + "Asynchronously fetch forge data about the specified pull-request. +Once all data has been collected, CALLBACK is called with the +data as the only argument." + (ghub-query (ghub--graphql-narrow-query + ghub-fetch-repository + `(repository pullRequests (pullRequest . ,number))) + `((owner . ,owner) + (name . ,name)) + :until until + :narrow '(repository pullRequest) + :username username + :auth auth + :host host + :forge forge + :headers headers + :paginate paginate + :callback callback + :errorback errorback)) + +(cl-defun ghub-fetch-review-threads ( owner name number callback + &optional until + &key username auth host forge + headers paginate errorback) + "Asynchronously fetch forge data about the review threads from a pull-request. +Once all data has been collected, CALLBACK is called with the +data as the only argument." + (declare (obsolete nil "Ghub 5.1.0")) + (ghub-query (ghub--graphql-narrow-query + ghub-fetch-repository-review-threads + `(repository pullRequests (pullRequest . ,number))) + `((owner . ,owner) + (name . ,name)) + :until until + :narrow '(repository pullRequest) + :username username + :auth auth + :host host + :forge forge + :headers headers + :paginate paginate + :callback callback + :errorback errorback)) + +;;; _ +(provide 'ghub-legacy) +;;; ghub-legacy.el ends here diff --git a/elpa/ghub-20260423.1634/ghub-pkg.el b/elpa/ghub-20260423.1634/ghub-pkg.el new file mode 100644 index 00000000..3859d79d --- /dev/null +++ b/elpa/ghub-20260423.1634/ghub-pkg.el @@ -0,0 +1,14 @@ +;; -*- no-byte-compile: t; lexical-binding: nil -*- +(define-package "ghub" "20260423.1634" + "Client libraries for Git forge APIs." + '((emacs "29.1") + (compat "30.1") + (cond-let "0.2") + (llama "1.0") + (treepy "0.1.3")) + :url "https://github.com/magit/ghub" + :commit "2b6df7c3f958e64c47151d7d6ef45de38e614936" + :revdesc "2b6df7c3f958" + :keywords '("tools") + :authors '(("Jonas Bernoulli" . "emacs.ghub@jonas.bernoulli.dev")) + :maintainers '(("Jonas Bernoulli" . "emacs.ghub@jonas.bernoulli.dev"))) diff --git a/elpa/ghub-20260423.1634/ghub.el b/elpa/ghub-20260423.1634/ghub.el new file mode 100644 index 00000000..a455c39a --- /dev/null +++ b/elpa/ghub-20260423.1634/ghub.el @@ -0,0 +1,831 @@ +;;; ghub.el --- Client libraries for Git forge APIs -*- lexical-binding:t -*- + +;; Copyright (C) 2016-2026 Jonas Bernoulli + +;; Author: Jonas Bernoulli +;; Homepage: https://github.com/magit/ghub +;; Keywords: tools + +;; Package-Version: 20260423.1634 +;; Package-Revision: 2b6df7c3f958 +;; Package-Requires: ( +;; (emacs "29.1") +;; (compat "30.1") +;; (cond-let "0.2") +;; (llama "1.0") +;; (treepy "0.1.3")) + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. +;; +;; This file 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this file. If not, see . + +;;; Commentary: + +;; Ghub provides basic support for using the APIs of various Git forges +;; from Emacs packages. Originally it only supported the Github REST +;; API, but now it also supports the Github GraphQL API as well as the +;; REST APIs of Gitlab, Gitea, Gogs and Bitbucket. + +;; Ghub abstracts access to API resources using only a handful of basic +;; functions such as `ghub-get'. These are convenience wrappers around +;; `ghub-request'. + +;; When accessing Github, then Ghub handles the creation and storage of +;; access tokens using a setup wizard to make it easier for users to get +;; started. The tokens for other forges have to be created manually. + +;; Ghub is intentionally limited to only provide these two essential +;; features — basic request functions and guided setup — to avoid being +;; too opinionated, which would hinder wide adoption. It is assumed that +;; wide adoption would make life easier for users and maintainers alike, +;; because then all packages that talk to forge APIs could be configured +;; the same way. + +;; Please consult the manual (info "ghub") for more information. + +;;; Code: + +(require 'auth-source) +(require 'cl-lib) +(require 'compat) +(require 'cond-let) +(require 'gnutls) +(require 'let-alist) +(require 'llama) +(require 'url) +(require 'url-auth) +(require 'url-http) + +(eval-when-compile (require 'subr-x)) + +(defvar url-callback-arguments) +(defvar url-http-end-of-headers) +(defvar url-http-extra-headers) +(defvar url-http-response-status) + +;;; Settings + +(defvar ghub-default-host-alist + '((github . "api.github.com") + (gitlab . "gitlab.com") + (forgejo . "codeberg.org") + (bitbucket . "api.bitbucket.org")) + "Alist of default hosts used when the respective `FORGE.host' is not set.") + +(defvar ghub-github-token-scopes '(repo) + "The Github API scopes that your private tools need. + +You have to manually create or update the token at +https://github.com/settings/tokens. This variable +only serves as documentation.") + +(defvar ghub-insecure-hosts nil + "List of hosts that use http instead of https.") + +(defvar ghub-debug nil + "Record additional debug information.") + +;;; Request +;;;; Object + +(cl-defstruct (ghub--req + (:constructor ghub--make-req) + (:copier nil)) + (url nil :read-only nil) + (forge nil :read-only t) + (silent nil :read-only t) + (method nil :read-only t) + (headers nil :read-only t) + (handler nil :read-only t) + (unpaginate nil :read-only nil) + (noerror nil :read-only t) + (reader nil :read-only t) + (buffer nil :read-only t) + (synchronous nil :read-only t) + (callback nil :read-only t) + (errorback nil :read-only t) + (value nil :read-only nil) + (extra nil :read-only nil)) + +(defalias 'ghub-req-extra #'ghub--req-extra) + +;;;; API + +(define-error 'ghub-error "Ghub/Url Error" 'error) +(define-error 'ghub-http-error "HTTP Error" 'ghub-error) + +(defvar ghub-response-headers nil + "The headers returned in response to the last request. +`ghub-request' returns the response body and stores the +response headers in this variable.") + +(cl-defun ghub-head (resource + &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `HEAD' request for RESOURCE, with optional query PARAMS. +Like calling `ghub-request' (which see) with \"HEAD\" as METHOD." + (ghub-request "HEAD" resource params + :query query :payload payload :headers headers + :silent silent :unpaginate unpaginate + :noerror noerror :reader reader + :username username :auth auth :host host + :callback callback :errorback errorback :extra extra)) + +(cl-defun ghub-get (resource + &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host forge + callback errorback extra) + "Make a `GET' request for RESOURCE, with optional query PARAMS. +Like calling `ghub-request' (which see) with \"GET\" as METHOD." + (ghub-request "GET" resource params + :query query :payload payload :headers headers + :silent silent :unpaginate unpaginate + :noerror noerror :reader reader + :username username :auth auth :host host :forge forge + :callback callback :errorback errorback :extra extra)) + +(cl-defun ghub-put (resource + &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host forge + callback errorback extra) + "Make a `PUT' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"PUT\" as METHOD." + (ghub-request "PUT" resource params + :query query :payload payload :headers headers + :silent silent :unpaginate unpaginate + :noerror noerror :reader reader + :username username :auth auth :host host :forge forge + :callback callback :errorback errorback :extra extra)) + +(cl-defun ghub-post (resource + &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host forge + callback errorback extra) + "Make a `POST' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"POST\" as METHOD." + (ghub-request "POST" resource params + :query query :payload payload :headers headers + :silent silent :unpaginate unpaginate + :noerror noerror :reader reader + :username username :auth auth :host host :forge forge + :callback callback :errorback errorback :extra extra)) + +(cl-defun ghub-patch (resource + &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host forge + callback errorback extra) + "Make a `PATCH' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"PATCH\" as METHOD." + (ghub-request "PATCH" resource params + :query query :payload payload :headers headers + :silent silent :unpaginate unpaginate + :noerror noerror :reader reader + :username username :auth auth :host host :forge forge + :callback callback :errorback errorback :extra extra)) + +(cl-defun ghub-delete (resource + &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host forge + callback errorback extra) + "Make a `DELETE' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"DELETE\" as METHOD." + (ghub-request "DELETE" resource params + :query query :payload payload :headers headers + :silent silent :unpaginate unpaginate + :noerror noerror :reader reader + :username username :auth auth :host host :forge forge + :callback callback :errorback errorback :extra extra)) + +(cl-defun ghub-request ( method resource + &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host forge + callback errorback value extra) + "Make a request for RESOURCE and return the response body. + +Also place the response headers in `ghub-response-headers'. + +METHOD is the HTTP method, given as a string. +RESOURCE is the resource to access, given as a string beginning + with a slash. + +PARAMS, QUERY, PAYLOAD and HEADERS are alists used to specify + data. The Github API documentation is vague on how data has + to be transmitted and for a particular resource usually just + talks about \"parameters\". Generally speaking when the METHOD + is \"HEAD\" or \"GET\", then they have to be transmitted as a + query, otherwise as a payload. +Use PARAMS to automatically transmit like QUERY or PAYLOAD would + depending on METHOD. +Use QUERY to explicitly transmit data as a query. +Use PAYLOAD to explicitly transmit data as a payload. + Instead of an alist, PAYLOAD may also be a string, in which + case it gets encoded as UTF-8 but is otherwise transmitted as-is. +Use HEADERS for those rare resources that require that the data + is transmitted as headers instead of as a query or payload. + When that is the case, then the API documentation usually + mentions it explicitly. + +If SILENT is non-nil, then don't message progress reports and + the like. + +If UNPAGINATE is t, then make as many requests as necessary to + get all values. If UNPAGINATE is a natural number, then get + at most that many pages. For any other non-nil value raise + an error. +If NOERROR is non-nil, then do not raise an error if the request + fails and return nil instead. If NOERROR is `return', then + return the error payload instead of nil. +If READER is non-nil, then it is used to read and return from the + response buffer. The default is `ghub--read-json-payload'. + For the very few resources that do not return JSON, you might + want to use `ghub--decode-payload'. + +If USERNAME is non-nil, then make a request on behalf of that + user. It is better to specify the user using the Git variable + `github.user' for \"api.github.com\", or `github.HOST.user' if + connecting to a Github Enterprise instance. + +Each package that uses `ghub' should use its own token. If AUTH + is nil, then the generic `ghub' token is used instead. This + is only acceptable for personal utilities. A packages that + is distributed to other users should always use this argument + to identify itself, using a symbol matching its name. + + Package authors who find this inconvenient should write a + wrapper around this function and possibly for the + method-specific functions as well. + + Some symbols have a special meaning. `none' means to make an + unauthorized request. `basic' means to make a password based + request. If the value is a string, then it is assumed to be + a valid token. `basic' and an explicit token string are only + intended for internal and debugging uses. + +If HOST is non-nil, then connect to that Github instance. This + defaults to \"api.github.com\". When a repository is connected + to a Github Enterprise instance, then it is better to specify + that using the Git variable `github.host' instead of using this + argument. + +If optional FORGE is nil, then it is assumed that HOST is a + Github host. When connecting to another forge type, then + FORGE must be one of `gitlab', `gitea', `gogs' or `bitbucket'. + +If CALLBACK and/or ERRORBACK is non-nil, then make one or more + asynchronous requests and call CALLBACK or ERRORBACK when + finished. If no error occurred, then call CALLBACK, unless + that is nil. + + If an error occurred, then call ERRORBACK, or if that is nil, + then CALLBACK. ERRORBACK can also be t, in which case an error + is signaled instead. NOERROR is ignored for all asynchronous + requests. + +Both callbacks are called with four arguments. + 1. For CALLBACK, the combined value of the retrieved pages. + For ERRORBACK, the error that occurred when retrieving the + last page. + 2. The headers of the last page as an alist. + 3. Status information provided by `url-retrieve'. Its `:error' + property holds the same information as ERRORBACK's first + argument. + 4. A `ghub--req' struct, which can be passed to `ghub-continue' + (which see) to retrieve the next page, if any." + (declare (indent defun)) + (cl-assert (or (booleanp unpaginate) (natnump unpaginate))) + (unless (string-prefix-p "/" resource) + (setq resource (concat "/" resource))) + (unless host + (setq host (ghub--host forge))) + (unless (or username (stringp auth) (eq auth 'none)) + (setq username (ghub--username host forge))) + (cond ((not params)) + ((member method '("GET" "HEAD")) + (when query + (error "PARAMS and QUERY are mutually exclusive for METHOD %S" + method)) + (setq query params)) + (t + (when payload + (error "PARAMS and PAYLOAD are mutually exclusive for METHOD %S" + method)) + (setq payload params))) + (when (or callback errorback) + (setq noerror t)) + (ghub--retrieve + (ghub--encode-payload payload) + (ghub--make-req + :url (ghub--encode-url host resource query) + :forge forge + :silent silent + :method (encode-coding-string method 'utf-8) ;#35 + :headers (ghub--headers headers host auth username forge) + :handler #'ghub--handle-response + :unpaginate unpaginate + :noerror noerror + :reader reader + :buffer (current-buffer) + :synchronous (not (or callback errorback)) + :callback callback + :errorback errorback + :value value + :extra extra))) + +(defun ghub-continue (req) + "If there is a next page, then retrieve that. + +This function is only intended to be called from callbacks. If +there is a next page, then retrieve that and return the buffer +that the result will be loaded into, or t if the process has +already completed. If there is no next page, then return nil. + +Callbacks are called with four arguments (see `ghub-request'). +The forth argument is a `ghub--req' struct, intended to be passed +to this function. A callback may use the struct's `extra' slot +to pass additional information to the callback that will be +called after the next request has finished. Use the function +`ghub-req-extra' to get and set the value of this slot." + (and (assq 'next (ghub-response-link-relations req)) + (or (ghub--retrieve nil req) t))) + +(cl-defun ghub-wait (resource + &optional duration + &key username auth host forge) + "Busy-wait up to DURATION seconds for RESOURCE to become available. + +DURATION specifies how many seconds to wait at most. It defaults +to 64 seconds. The first attempt is made immediately, the second +after two seconds, and each subsequent attempt is made after +waiting as long again as we already waited between all preceding +attempts combined. + +See `ghub-request' for information about the other arguments." + (unless duration + (setq duration 64)) + (with-local-quit + (let ((total 0)) + (while (not (ghub-request "GET" resource nil + :noerror t + :username username + :auth auth + :host host + :forge forge)) + (message "Waited (%3ss of %ss) for %s..." total duration resource) + (if (= total duration) + (error "%s is taking too long to create %s" + (if forge (capitalize (symbol-name forge)) "Github") + resource) + (if (> total 0) + (let ((wait (min total (- duration total)))) + (sit-for wait) + (cl-incf total wait)) + (sit-for (setq total 2)))))))) + +(defun ghub-response-link-relations (req &optional headers payload) + "Return an alist of link relations in HEADERS. +If optional HEADERS is nil, then return those that were +previously stored in the variable `ghub-response-headers'. + +When accessing a Bitbucket instance then the link relations +are in PAYLOAD instead of HEADERS, making their API merely +RESTish and forcing this function to append those relations +to the value of `ghub-response-headers', for later use when +this function is called with nil for PAYLOAD." + (if (eq (ghub--req-forge req) 'bitbucket) + (if payload + (let* ((page (seq-keep (##assq % payload) + '(size page pagelen next previous))) + (headers (cons (cons 'link-alist page) headers))) + (if (and req (or (ghub--req-callback req) + (ghub--req-errorback req))) + (setq-local ghub-response-headers headers) + (setq-default ghub-response-headers headers)) + page) + (cdr (assq 'link-alist ghub-response-headers))) + (and-let* ((headers (or headers ghub-response-headers)) + (rels (cdr (or (assoc "Link" headers) + (assoc "link" headers))))) + (mapcar (lambda (elt) + (pcase-let ((`(,url ,rel) (split-string elt "; "))) + (cons (intern (substring rel 5 -1)) + (substring url 1 -1)))) + (split-string rels ", ?"))))) + +(cl-defun ghub-repository-id (owner name &key username auth host forge noerror) + "Return the id of the specified repository. +Signal an error if the id cannot be determined." + (or (pcase forge + ((or 'nil 'github) + (let-alist (ghub-query + '(query (repository [(owner $owner String!) + (name $name String!)] + id)) + `((owner . ,owner) + (name . ,name)) + :synchronous t + :username username :auth auth :host host) + .repository.id)) + ('gitlab + (number-to-string + (alist-get + 'id (ghub-get (format "/projects/%s%%2F%s" + (string-replace "/" "%2F" owner) + name) + nil :forge 'gitlab + :username username :auth auth :host host)))) + ((or 'forgejo 'gitea 'gogs) + (number-to-string + (alist-get + 'id (ghub-get (format "/repos/%s/%s" owner name) + nil :forge forge + :username username :auth auth :host host)))) + ('bitbucket + (substring + (alist-get 'uuid + (ghub-get (format "/repositories/%s/%s" owner name) + nil :forge 'bitbucket + :username username :auth auth :host host)) + 1 -1)) + (_ (error "ghub-repository-id: Forge type `%s' is unknown" forge))) + (and (not noerror) + (error "Repository %S does not exist on %S.\n%s%S?" + (concat owner "/" name) + (or host (ghub--host forge)) + "Maybe it was renamed and you have to update " + "remote..url")))) + +;;;; Internal + +(cl-defun ghub--retrieve (payload req) + (pcase-let* + (((cl-struct ghub--req headers method url handler silent synchronous) req) + (url-request-extra-headers + (if (functionp headers) (funcall headers) headers)) + (url-request-method method) + (url-request-data payload) + (url-show-status nil)) + (if synchronous + (if-let ((buf (url-retrieve-synchronously url silent))) + (with-current-buffer buf + (funcall handler (car url-callback-arguments) req)) + (error "ghub--retrieve: No buffer returned")) + (url-retrieve url handler (list req) silent)))) + +(defun ghub--handle-response (status req) + (let ((buf (current-buffer))) + (unwind-protect + (progn + (set-buffer-multibyte t) + (let* ((unpaginate (ghub--req-unpaginate req)) + (headers (ghub--handle-response-headers status req)) + (payload (ghub--handle-response-payload req)) + (payload (ghub--handle-response-error status payload req)) + (value (ghub--handle-response-value payload req)) + (prev (ghub--req-url req)) + (next (cdr (assq 'next (ghub-response-link-relations + req headers payload))))) + (when (numberp unpaginate) + (cl-decf unpaginate)) + (setf (ghub--req-url req) + (url-generic-parse-url next)) + (setf (ghub--req-unpaginate req) unpaginate) + (or (and next + unpaginate + (or (eq unpaginate t) + (> unpaginate 0)) + (ghub-continue req)) + (let ((req-buf (ghub--req-buffer req)) + (callback (ghub--req-callback req)) + (errorback (ghub--req-errorback req)) + (err (plist-get status :error))) + (cond ((and err errorback) + (setf (ghub--req-url req) prev) + (when (eq errorback t) + (setq errorback #'ghub--errorback)) + (condition-case nil + (funcall errorback err headers status req) + (wrong-number-of-arguments + (funcall errorback err)))) + (callback + (save-current-buffer + (when (buffer-live-p req-buf) + (set-buffer req-buf)) + (condition-case nil + (funcall callback value headers status req) + (wrong-number-of-arguments + (funcall callback value))))) + (t value)))))) + (when (and (buffer-live-p buf) + (not (buffer-local-value 'ghub-debug buf))) + (kill-buffer buf))))) + +(defun ghub--handle-response-headers (_status req) + (let (headers) + (when (memq url-http-end-of-headers '(nil 0)) + (unless url-debug (setq url-debug t)) + (error "BUG: Missing headers in response buffer %s" (current-buffer))) + (goto-char (point-min)) + (forward-line 1) + (while (re-search-forward "^\\([^:]*\\): \\(.+\\)" + url-http-end-of-headers t) + (push (cons (match-string 1) + (match-string 2)) + headers)) + (setq headers (nreverse headers)) + (goto-char (1+ url-http-end-of-headers)) + (if (and req (or (ghub--req-callback req) + (ghub--req-errorback req))) + (setq-local ghub-response-headers headers) + (setq-default ghub-response-headers headers)) + headers)) + +(defun ghub--handle-response-error (status payload req) + (if-let ((err (plist-get status :error))) + (if-let ((noerror (ghub--req-noerror req))) + (if (eq noerror 'return) + payload + (setcdr (last err) (list payload)) + nil) + (ghub--signal-error err payload req)) + payload)) + +(defun ghub--signal-error (err &optional payload req) + (pcase-let ((`(,symb . ,data) err)) + (if (eq symb 'error) + (if (eq (car-safe data) 'http) + (signal 'ghub-http-error + (let ((code (car (cdr-safe data)))) + (list code + (nth 2 (assq code url-http-codes)) + (and req (url-recreate-url (ghub--req-url req))) + payload))) + (signal 'ghub-error data)) + (signal symb data)))) + +(defun ghub--errorback (err _headers _status req) + (ghub--signal-error err (nth 3 err) req)) + +(defun ghub--handle-response-value (payload req) + (setf (ghub--req-value req) + (nconc (ghub--req-value req) + (if-let ((nested (and (eq (ghub--req-forge req) 'bitbucket) + (assq 'values payload)))) + (cdr nested) + payload)))) + +(defun ghub--handle-response-payload (req) + (funcall (or (ghub--req-reader req) + 'ghub--read-json-payload) + url-http-response-status)) + +(defun ghub--read-json-payload (_status &optional json-type-args) + (and-let* ((payload (ghub--decode-payload))) + (progn + (ghub--assert-json-available) + (condition-case nil + (apply #'json-parse-string payload + (or json-type-args + '( :object-type alist + :array-type list + :null-object nil + :false-object nil))) + (json-parse-error + (when ghub-debug + (pop-to-buffer (current-buffer))) + (setq-local ghub-debug t) + `((message . ,(if (looking-at "") + (if (re-search-forward + "

\\(?:\\)?\\([^<]+\\)" nil t) + (match-string 1) + "error description missing") + (string-trim (buffer-substring (point) (point-max))))) + (documentation_url + . "https://github.com/magit/ghub/wiki/Github-Errors"))))))) + +(defun ghub--decode-payload (&optional _status) + (and (not (eobp)) + (decode-coding-string + (buffer-substring-no-properties (point) (point-max)) + 'utf-8))) + +(defun ghub--encode-payload (payload) + (cl-typecase payload + (null nil) + (string (encode-coding-string payload 'utf-8)) + (t (ghub--assert-json-available) + (encode-coding-string + (json-serialize payload + :null-object :null + :false-object nil) + 'utf-8)))) + +(defun ghub--encode-url (host resource &optional query) + (url-generic-parse-url + (concat (if (member host ghub-insecure-hosts) "http://" "https://") + ;; Needed for some Github Enterprise instances. + (cond + ((and (equal resource "/graphql") + (string-suffix-p "/v3" host)) + (substring host 0 -3)) + ;; Needed for all Gitlab instances. + ((and (equal resource "/api/graphql") + (string-suffix-p "/api/v4" host)) + (substring host 0 -7)) + (host)) + resource + (and query (concat "?" (ghub--url-encode-params query)))))) + +(defun ghub--url-encode-params (params) + (mapconcat (lambda (param) + (pcase-let ((`(,key . ,val) param)) + (concat (url-hexify-string (symbol-name key)) "=" + (cl-typecase val + (integer (number-to-string val)) + (boolean (if val "true" "false")) + (t (url-hexify-string val)))))) + params "&")) + +(defun ghub--assert-json-available () + (unless (and (fboundp 'json-available-p) + (json-available-p)) + (error "Ghub requires Emacs 29 --with-json or Emacs >= 30"))) + +;;; Authentication +;;;; API + +;;;###autoload +(defun ghub-clear-caches () + "Clear all caches that might negatively affect Ghub. + +If a library that is used by Ghub caches incorrect information +such as a mistyped password, then that can prevent Ghub from +asking the user for the correct information again. + +Set `url-http-real-basic-auth-storage' to nil +and call `auth-source-forget+'." + (interactive) + (setq url-http-real-basic-auth-storage nil) + (auth-source-forget+)) + +;;;; Internal + +(defun ghub--headers (headers host auth username forge) + (push (cons "Content-Type" "application/json") headers) + (if (eq auth 'none) + headers + (unless (or username (stringp auth)) + (setq username (ghub--username host forge))) + (lambda () + (if (eq auth 'basic) + (cons (cons "Authorization" (ghub--basic-auth host username)) + headers) + (cons (ghub--auth host auth username forge) headers))))) + +(cl-defgeneric ghub--auth (host auth &optional username forge) + (unless username + (setq username (ghub--username host forge))) + (if (eq auth 'basic) + (pcase-exhaustive forge + ((or 'nil 'forgejo 'gitea 'gogs 'bitbucket) + (cons "Authorization" (ghub--basic-auth host username))) + ((or 'github 'gitlab) + (error "%s does not support basic authentication" + (capitalize (symbol-name forge))))) + (cons (pcase-exhaustive forge + ((or 'nil 'forgejo 'github 'gitea 'gogs 'bitbucket) "Authorization") + ('gitlab "Private-Token")) + (if (eq forge 'bitbucket) + ;; For some undocumented reason Bitbucket supports + ;; values of the form "token " only for GET + ;; requests. For PUT requests we have to use basic + ;; authentication. Note that the secret is a token + ;; (aka "app password"), not the actual password. + ;; The documentation fails to mention this little + ;; detail. See #97. + (concat "Basic " + (base64-encode-string + (concat username ":" + (ghub--token host username auth nil forge)) + t)) + (concat + (and (not (eq forge 'gitlab)) "token ") + (encode-coding-string + (cl-typecase auth + (string auth) + (null (ghub--token host username 'ghub nil forge)) + (symbol (ghub--token host username auth nil forge)) + (t (signal 'wrong-type-argument + `((or stringp symbolp) ,auth)))) + 'utf-8)))))) + +(defun ghub--basic-auth (host username) + (let ((url (url-generic-parse-url + (if (member host ghub-insecure-hosts) "http://" "https://")))) + (setf (url-user url) username) + (url-basic-auth url t))) + +(defun ghub--token (host username package &optional nocreate forge) + (let* ((user (ghub--ident username package)) + (token (or (ghub--auth-source-get :secret :host host :user user) + (and (string-match "\\`\\([^/]+\\)" host) + (ghub--auth-source-get :secret + :host (match-string 1 host) + :user user))))) + (unless (or token nocreate) + (error "\ +Required %s token (%S for %s%S) does not exist. +See https://docs.magit.vc/ghub/Getting-Started.html +or (info \"(ghub)Getting Started\") for instructions." + (capitalize (symbol-name (or forge 'github))) + user + (if (string-match "\\`\\([^/]+\\)" host) + (format "either %S or " (match-string 1 host)) + "") + host)) + (if (functionp token) (funcall token) token))) + +(cl-defmethod ghub--host (&optional forge) + (let ((forge (or forge 'github))) + (or (ghub--git-get (format "%s.host" forge)) + (alist-get forge ghub-default-host-alist)))) + +(cl-defmethod ghub--username (host &optional forge compat) + (let* ((forge (or forge 'github)) + (host (or host (ghub--host forge))) + (var (format "%s.%s.user" forge host)) + (default-var (format "%s.user" forge))) + (cond ((ghub--git-get var)) + ((and compat (ghub--git-get (format "%s.%s.user" forge compat)))) + ((not (equal host (alist-get forge ghub-default-host-alist))) + (user-error "Cannot determine username; `%s' is unset" var)) + ((ghub--git-get default-var)) + ((user-error "%s; `%s' and `%s' are both unset" + "Cannot determine username" var default-var))))) + +(defun ghub--ident (username package) + (format "%s^%s" username package)) + +(defun ghub--auth-source-get (keys &rest spec) + (declare (indent 1)) + (if-let ((plist (car (apply #'auth-source-search spec)))) + (if (keywordp keys) + (plist-get plist keys) + (mapcar (##plist-get plist %) keys)) + ;; Auth-Source caches the information that there is no value, but in + ;; our case that is a situation that needs fixing, so we want to keep + ;; trying, by invalidating that information. + (auth-source-forget spec) + nil)) + +(define-advice auth-source-netrc-parse (:around (fn &rest args) handle-symlink) + "Follow symlinks for FILE before comparing modification times." + (let ((file (plist-get args :file))) + (when (and (stringp file) + (file-exists-p file)) + (setq args (plist-put args :file (file-chase-links file))))) + (apply fn args)) + +(defun ghub--git-get (var) + (catch 'non-zero + (car (process-lines-handling-status + "git" (##unless (zerop %) (throw 'non-zero nil)) + ;; Beginning with Git v2.46 we could use "get". + "config" "--get" var)))) + +;;; _ +(provide 'ghub) +(require 'ghub-graphql) +;; Local Variables: +;; read-symbol-shorthands: ( +;; ("and-let" . "cond-let--and-let") +;; ("if-let" . "cond-let--if-let") +;; ("when-let" . "cond-let--when-let") +;; ("while-let" . "cond-let--while-let")) +;; End: +;;; ghub.el ends here diff --git a/elpa/ghub-20260423.1634/ghub.info b/elpa/ghub-20260423.1634/ghub.info new file mode 100644 index 00000000..94506c6b --- /dev/null +++ b/elpa/ghub-20260423.1634/ghub.info @@ -0,0 +1,988 @@ +This is ghub.info, produced by makeinfo version 6.8 from ghub.texi. + + Copyright (C) 2017-2026 Jonas Bernoulli + + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document 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 + General Public License for more details. + +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* Ghub: (ghub). Client library for the Github API. +END-INFO-DIR-ENTRY + + +File: ghub.info, Node: Top, Next: Introduction, Up: (dir) + +Ghub User and Developer Manual +****************************** + +Ghub is an Emacs library that is used by various packages to access the +APIs of various instances of various Git forge implementations. + +This manual is for Ghub version 5.1.0. + + Copyright (C) 2017-2026 Jonas Bernoulli + + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document 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 + General Public License for more details. + +* Menu: + +* Introduction:: +* Getting Started:: +* API:: +* Notes:: +* Function Index:: +* Variable Index:: + +— The Detailed Node Listing — + +Getting Started + +* Basic Concepts, Arguments and Variables: Basic Concepts Arguments and Variables. +* Setting the Username:: +* Creating and Storing a Token:: +* Github Configuration Variables:: + +API + +* Their APIs:: +* Making REST Requests:: +* Making GraphQL Requests:: +* Github Convenience Wrappers:: + +Notes + +* Using Ghub in Personal Scripts:: +* Using Ghub in Your Own Package:: +* Forge Limitations and Notes:: + + + +File: ghub.info, Node: Introduction, Next: Getting Started, Prev: Top, Up: Top + +1 Introduction +************** + +Ghub is an Emacs library that is used by various packages to access the +APIs of various instances of various Git forge implementations. + + A forge is a web-based collaborative software platform for developing +and distributing computer applications. Examples include Github and +Gitlab. + + +File: ghub.info, Node: Getting Started, Next: API, Prev: Introduction, Up: Top + +2 Getting Started +***************** + +This manual guides you through the steps that are necessary to use the +Forge package and/or to make a request using just Ghub itself, such as +this: + + (ghub-request "GET" "/user") + + To be able to do that, Ghub needs to know who you want to talk to, +who you are, and how you are going to prove the latter to the former. + + Additionally Ghub wants to know on behalf of which Emacs package it +is making a request. So the question of "who is making the request" has +to be rephrased as "which human (or bot) is using what (Emacs) package +to make the request". If, for example, the human known as "tarsius" is +using the ‘forge’ package, then that is represented in some places using +the string "tarsius^forge". + +* Menu: + +* Basic Concepts, Arguments and Variables: Basic Concepts Arguments and Variables. +* Setting the Username:: +* Creating and Storing a Token:: +* Github Configuration Variables:: + + +File: ghub.info, Node: Basic Concepts Arguments and Variables, Next: Setting the Username, Up: Getting Started + +2.1 Basic Concepts, Arguments and Variables +=========================================== + +(If you are trying to configure Forge for and/or +, then you can skip this section.) + + Originally Ghub supported only Github but now it also supports +Gitlab, Gitea, Gogs and Bitbucket. Because of that, the function +‘ghub-request’ defaults to acting on a ‘github’ forge, but can be told +to act on other forges using the FORGE argument. + + The FORGE argument only specifies what kind of forge we are +accessing, not which host specifically. The HOST argument can be used +to select the instance. For some forges a default instance is defined: + + • Forge ‘github’ defaults to host ‘api.github.com’. + • Forge ‘gitlab’ defaults to host ‘gitlab.com/api/v4’. + • Forge ‘bitbucket’ defaults to host ‘api.bitbucket.org/2.0’. + • No canonical host exists for the ‘gitea’ and ‘gogs’ forges and + ‘localhost:3000/api/v1’ is used as the default host in both cases. + + Together the FORGE and HOST arguments specify the forge type and +instance. In addition to that, it is also necessary to specify on whose +behalf the request is being made, which can be done using the USERNAME +and AUTH arguments. For example: + + (ghub-request "GET" "/user" nil + :forge 'github + :host "api.github.com" + :username "tarsius" + :auth 'forge) + + Having to specify these arguments for every request is inconvenient. +Additional variables and convenience functions can be used to make that +unnecessary in most cases. + + (But for debugging purposes the above explicit form is very useful. +You will obviously have to change the value of USERNAME and you should +use ‘ghub’ as AUTH when first trying this at home.) + + These variables can be set globally and/or for a specific repository. + + • For "api.github.com" (aka the API of ) the Git + variable ‘github.user’ specifies the user. + • For another ‘github’ instance the Git variable ‘github.HOST.user’ + specifies the user. The HOST in that variable name is the same as + the value of the HOST argument of the called function. + • Instead of specifying the HOST in every function call, the Git + variable ‘github.host’ can be used. This should only be set + locally. + + These ‘github’ specific variables are discussed in more detail in +*note Github Configuration Variables::. + +For ‘gitlab’ and ‘bitbucket’ forges similar variables are available: + + • ‘gitlab.user’ specifies the user. + • ‘gitlab.HOST.user’ specifies the user for the HOST ‘gitlab’ + instance. + • ‘gitlab.host’ specifies the ‘gitlab’ host, unless the HOST argument + is non-‘nil’ + • ‘bitbucket.user’ specifies the user. + • ‘bitbucket.HOST.user’ specifies the user for the HOST ‘bitbucket’ + instance. + • ‘bitbucket.host’ specifies the ‘bitbucket’ host, unless the HOST + argument is non-‘nil’. + + For the ‘gitea’ and ‘gogs’ forges some similar variables are +available, however for some of the ‘ghub.*’ variables no equivalent +variable exist for these two forges: + + • ‘gitea.user’ is *not* used because no canonical ‘gitea’ instance + exists. + • ‘gitea.HOST.user’ specifies the user for the HOST ‘gitea’ instance. + • ‘gitea.host’ specifies the ‘gitea’ host, unless the HOST argument + is non-‘nil’ + • ‘gogs.user’ is *not* used because no canonical ‘gogs’ instance + exists. + • ‘gogs.HOST.user’ specifies the user for the HOST ‘gogs’ instance. + • ‘gogs.host’ specifies the ‘gogs’ host, unless the HOST argument is + non-‘nil’ + + +File: ghub.info, Node: Setting the Username, Next: Creating and Storing a Token, Prev: Basic Concepts Arguments and Variables, Up: Getting Started + +2.2 Setting the Username +======================== + +Ghub needs to know your username, which you use on the host that you +want it to connect to. For each host a different Git variable has to be +set, to specify the username on that host. More than one variable is +needed, because you might use different usernames on different hosts. + +Setting your Github.com Username +-------------------------------- + +To inform Ghub about your "github.com" username do this: + + git config --global github.user USERNAME + + If you need to identify as another user in a particular repository, +then you have to set that variable locally: + + cd /path/to/repo + git config --local github.user USERNAME + +Setting your Gitlab.com Username +-------------------------------- + +To inform Ghub about your "gitlab.com" username do this: + + git config --global gitlab.user USERNAME + + If you need to identify as another user in a particular repository, +then you have to set that variable locally: + + cd /path/to/repo + git config --local gitlab.user USERNAME + + Make sure you use the correct USERNAME for this forge/host. It might +not be the same as on "github.com"! + +Setting your Github Enterprise Username +--------------------------------------- + +For Github Enterprise instances you have to specify where the API can be +accessed and a different variable has to be used to set the username. + + For example if the API is available at ‘https://example.com/api/v3’, +then you should do this: + + git config --global github.example.com/api/v3.user USERNAME + + Make sure you use the correct USERNAME for this instance. It might +not be the same as on "github.com"! + + Doing this only tells Ghub who you are on this host, additionally you +have to tell Ghub which repositories are connected to that forge/host, +like so: + + cd /path/to/repo + git config --local github.host example.com/api/v3 + +Setting your Username for Other Hosts and/or Forges +--------------------------------------------------- + +To inform Ghub about your username on HOST (a FORGE instance) do this: + + git config --global FORGE.HOST.user USERNAME + + FORGE can be one of ‘bitbucket’, ‘gitea’ or ‘gogs’. It can also be +‘github’ or ‘gitlab’; but if that is the case, then you should look at +the preceding sections instead, which discuss these cases specifically. + + HOST identifies the instance. This actually points at the top-level +endpoint of the API and may contain path components, e.g.: +‘example.com/api’. + + If you need to identify as another user in a particular repository, +then you have to set that variable locally: + + cd /path/to/repo + git config --local FORGE.HOST.user USERNAME + + +File: ghub.info, Node: Creating and Storing a Token, Next: Github Configuration Variables, Prev: Setting the Username, Up: Getting Started + +2.3 Creating and Storing a Token +================================ + +2.3.1 Creating a Token +---------------------- + +To create a token, use the web interface of the host you want to connect +to. Here is a list of pages to do this for certain popular hosts: + + • + • + + For other forges we cannot provide a functioning URL because they +contain unknown values such as your name. Just go to the general +settings page of the respective host and then go from there. + + Except on ‘gitea’ and ‘gogs’ each token can be limited to certain +"scopes", i.e., it is possible to limit for which purposes any given +token can be used. + + Before you create a token to be used for a certain package, you +should consult the documentation of that package, which in turn should +tell you which scopes are needed and why. The Forge package for example +does so in *note (forge)Setup for Githubcom:: and *note (forge)Setup for +Gitlabcom::. + +2.3.2 Storing a Token +--------------------- + +Please also see *note (auth)Top:: for all the gory details about +Auth-Source. + + The variable ‘auth-sources’ controls how and where Auth-Source keeps +its secrets. The default value is a list of three files: +‘("~/.authinfo" "~/.authinfo.gpg" "~/.netrc")’, but to avoid confusion +you should make sure that only one of these files exists, and then you +should also adjust the value of the variable to only ever use that file, +for example: + + (setq auth-sources '("~/.authinfo")) + + In ‘~/.authinfo’ secrets are stored in plain text. If you don’t want +that, then you should use the encrypted ‘~/.authinfo.gpg’ instead: + + (setq auth-sources '("~/.authinfo.gpg")) + + Auth-Source also supports storing secrets in various external +key-chains. See *note (auth)Top:: for more information. + + The default Auth-Source backends only support storing three values +per entry; the "machine", the "login" and the "password". Because Ghub +uses separate tokens for each package, it has to squeeze four values +into those three slots, and it does that by using "USERNAME^PACKAGE" as +the "login". + + Assuming your *Github* username is "ziggy", the package is named +"forge", and you want to access *Github.com* with the *token* +"012345abcdef...", an entry in one of the three mentioned files would +then look like this: + + machine api.github.com login ziggy^forge password 012345abcdef... + + Assuming your *Gitlab* username is "ziggy", the package is named +"forge", and you want to access *Gitlab.com* with the *token* +"012345abcdef...", an entry in one of the three mentioned files would +then look like this: + + machine gitlab.com/api/v4 login ziggy^forge password 012345abcdef... + + +File: ghub.info, Node: Github Configuration Variables, Prev: Creating and Storing a Token, Up: Getting Started + +2.4 Github Configuration Variables +================================== + +The username and, unless you only use Github.com itself, the Github +Enterprise instance have to be configured using Git variables. In rare +cases it might also be necessary to specify the identity of the local +machine, which is done using a lisp variable. + + -- Variable: github.user + The Github.com username. This should be set globally and if you + have multiple Github.com user accounts, then you should set this + locally only for those repositories that you want to access using + the secondary identity. + + -- Variable: github.HOST.user + This variable serves the same purpose as ‘github.user’ but for the + Github Enterprise instance identified by ‘HOST’. + + The reason why separate variables are used is that this makes it + possible to set both values globally instead of having to set one + of the values locally in each and every repository that is + connected to the Github Enterprise instance, not Github.com. + + -- Variable: github.host + This variable should only be set locally for a repository and + specifies the Github Enterprise edition that that repository is + connected to. You should not set this globally because then each + and every repository becomes connected to the specified Github + Enterprise instance, including those that should actually be + connected to Github.com. + + When this is undefined, then "api.github.com" is used (defined in + the constant ‘ghub-default-host’, which you should never attempt to + change.) + + +File: ghub.info, Node: API, Next: Notes, Prev: Getting Started, Up: Top + +3 API +***** + +* Menu: + +* Their APIs:: +* Making REST Requests:: +* Making GraphQL Requests:: +* Github Convenience Wrappers:: + + +File: ghub.info, Node: Their APIs, Next: Making REST Requests, Up: API + +3.1 Their APIs +============== + +Of course this manual does not cover the APIs of all forges that it +supports, but for your convenience, here are the links to their API +manuals: + + • Github: + • (GraphQl) + • (REST) + • Gitlab: + • + • Gitea: + • + • + • Gogs: + • + • Bitbucket: + • + + +File: ghub.info, Node: Making REST Requests, Next: Making GraphQL Requests, Prev: Their APIs, Up: API + +3.2 Making REST Requests +======================== + + -- Function: ghub-request method resource &optional params &key query + payload headers unpaginate noerror reader username auth host + callback errorback url value error extra method* + This function makes a request for ‘RESOURCE’ using ‘METHOD’. + ‘PARAMS’, ‘QUERY’, ‘PAYLOAD’ and/or ‘HEADERS’ are alists holding + additional request data. The response body is returned and the + response headers are stored in the variable + ‘ghub-response-headers’. + + • ‘METHOD’ is the HTTP method, given as a string. + • ‘RESOURCE’ is the resource to access, given as a string + beginning with a slash. + + • ‘PARAMS’, ‘QUERY’, ‘PAYLOAD’ and ‘HEADERS’ are alists and are + used to specify request data. All these arguments are alists + that resemble the JSON expected and returned by the Github + API. The keys are symbols and the values stored in the ‘cdr’ + (not the ‘cadr’) can be strings, integers, or lists of strings + and integers. + + The Github API documentation is vague on how data has to be + transmitted and for a particular resource usually just talks + about "parameters". Generally speaking when the ‘METHOD’ is + "HEAD" or "GET", then they have to be transmitted as a query, + otherwise as a payload. + + • Use ‘PARAMS’ to automatically transmit like ‘QUERY’ or + ‘PAYLOAD’ would depending on ‘METHOD’. + • Use ‘QUERY’ to explicitly transmit data as a query. + • Use ‘PAYLOAD’ to explicitly transmit data as a payload. + Instead of an alist, ‘PAYLOAD’ may also be a string, in + which case it gets encoded as UTF-8 but is otherwise + transmitted as-is. + • Use ‘HEADERS’ for those rare resources that require that + the data is transmitted as headers instead of as a query + or payload. When that is the case, then the Github API + documentation usually mentions it explicitly. + + • If ‘SILENT’ is non-‘nil’, then progress reports and the like + are not messaged. + + • If ‘UNPAGINATE’ is ‘t’, then this function makes as many + requests as necessary to get all values. If ‘UNPAGINATE’ is a + natural number, then it gets at most that many pages. For any + other non-‘nil’ value it raises an error. + + • If ‘NOERROR’ is non-‘nil’, then no error is raised if the + request fails and ‘nil’ is returned instead. If ‘NOERROR’ is + ‘return’, then the error payload is returned instead of ‘nil’. + + • If ‘READER’ is non-‘nil’, then it is used to read and return + from the response buffer. The default is + ‘ghub--read-json-payload’. For the very few resources that do + not return JSON, you might want to use ‘ghub--decode-payload’. + + • If ‘USERNAME’ is non-‘nil’, then the request is made on behalf + of that user. It is better to specify the user using the Git + variable ‘github.user’ for "api.github.com", or + ‘github.HOST.user’ if connecting to a Github Enterprise + instance. + + • Each package that uses Ghub should use its own token. If + ‘AUTH’ is ‘nil’ or unspecified, then the generic ‘ghub’ token + is used instead. This is only acceptable for personal + utilities. A package that is distributed to other users + should always use this argument to identify itself, using a + symbol matching its name. + + Package authors who find this inconvenient should write a + wrapper around this function and possibly for the + method-specific functions as well. + + Beside ‘nil’, some other symbols have a special meaning too. + ‘none’ means to make an unauthorized request. ‘basic’ means + to make a password based request. If the value is a string, + then it is assumed to be a valid token. ‘basic’ and an + explicit token string are only intended for internal and + debugging uses. + + If ‘AUTH’ is a package symbol, then the scopes are specified + using the variable ‘AUTH-github-token-scopes’. It is an error + if that is not specified. See ‘ghub-github-token-scopes’ for + an example. + + • If ‘HOST’ is non-‘nil’, then connect to that Github instance. + This defaults to "api.github.com". When a repository is + connected to a Github Enterprise instance, then it is better + to specify that using the Git variable ‘github.host’ instead + of using this argument. + + • If ‘FORGE’ is ‘gitlab’, then connect to Gitlab.com or, + depending on ‘HOST’, to another Gitlab instance. + + • If ‘CALLBACK’ and/or ‘ERRORBACK’ is non-‘nil’, then this + function makes one or more asynchronous requests and calls + ‘CALLBACK’ or ‘ERRORBACK’ when finished. If no error + occurred, then it calls ‘CALLBACK’, unless that is ‘nil’. + + If an error occurred, then it calls ‘ERRORBACK’, or if that is + ‘nil’, then ‘CALLBACK’. ‘ERRORBACK’ can also be ‘t’, in which + case it signals instead. ‘NOERROR’ is ignored for all + asynchronous requests. + + Both callbacks are called with four arguments. + + 1. For ‘CALLBACK’, the combined value of the retrieved + pages. For ‘ERRORBACK’, the error that occurred when + retrieving the last page. + 2. The headers of the last page as an alist. + 3. Status information provided by ‘url-retrieve’. Its + ‘:error’ property holds the same information as the first + argument to ‘ERRORBACK’. + 4. A ‘ghub--req’ struct, which can be passed to + ‘ghub-continue’ (which see) to retrieve the next page, if + any. + + -- Function: ghub-continue args + If there is a next page, then this function retrieves that. + + This function is only intended to be called from callbacks. If + there is a next page, then that is retrieved and the buffer that + the result will be loaded into is returned, or ‘t’ if the process + has already completed. If there is no next page, then return + ‘nil’. + + Callbacks are called with four arguments (see ‘ghub-request’). The + forth argument is a ‘ghub--req’ struct, intended to be passed to + this function. A callback may use the struct’s ‘extra’ slot to + pass additional information to the callback that will be called + after the next request. Use the function ‘ghub-req-extra’ to get + and set the value of that slot. + + As an example, using ‘ghub-continue’ in a callback like so: + + (ghub-get "/users/tarsius/repos" nil + :callback (lambda (value _headers _status req) + (unless (ghub-continue req) + (setq my-value value)))) + + is equivalent to: + + (ghub-get "/users/tarsius/repos" nil + :unpaginate t + :callback (lambda (value _headers _status _req) + (setq my-value value))) + + To demonstrate how to pass information from one callback to the + next, here we record when we start fetching each page: + + (ghub-get "/users/tarsius/repos" nil + :extra (list (current-time)) + :callback (lambda (value _headers _status req) + (push (current-time) (ghub-req-extra req)) + (unless (ghub-continue req) + (setq my-times (ghub-req-extra req)) + (setq my-value value)))) + + -- Variable: ghub-response-headers + A select few Github API resources respond by transmitting data in + the response header instead of in the response body. Because there + are so few of these inconsistencies, ‘ghub-request’ always returns + the response body. + + To access the response headers use this variable after + ‘ghub-request’ has returned. + + -- Function: ghub-response-link-relations req headers payload + This function returns an alist of the link relations in ‘HEADERS’, + or if optional ‘HEADERS’ is ‘nil’, then those in + ‘ghub-response-headers’. + + When accessing a Bitbucket instance then the link relations are in + ‘PAYLOAD’ instead of ‘HEADERS’, making their API merely RESTish and + forcing this function to append those relations to the value of + ‘ghub-response-headers’, for later use when this function is called + with ‘nil’ for ‘PAYLOAD’. + + +File: ghub.info, Node: Making GraphQL Requests, Next: Github Convenience Wrappers, Prev: Making REST Requests, Up: API + +3.3 Making GraphQL Requests +=========================== + + -- Function: ghub-graphql graphql &optional variables &key username + auth host callback silent callback errorback value extra + This function makes a GraphQL request using ‘GRAPHQL’ and + ‘VARIABLES’ as inputs. ‘GRAPHQL’ is a GraphQL string. ‘VARIABLES’ + is a JSON-like alist. The other arguments behave as for + ‘ghub-request’ (which see). + + The response is returned as a JSON-like alist. Even if the + response contains ‘errors’, this function does not raise an error. + Cursor-handling is likewise left to the caller. + + ‘ghub-graphql’ is a thin convenience wrapper around ‘ghub-request’, +similar to ‘ghub-post’ and friends. While the latter only hard-code the +value of the ‘METHOD’ argument, the former also hard-codes ‘RESOURCE’ +and constructs ‘PAYLOAD’ from ‘GRAPHQL’ and ‘VARIABLES’. It also drops +‘UNPAGINATE’, ‘NOERROR’, ‘READER’ (internal functions expect alist-ified +JSON) and ‘FORGE’ (only Github currently supports GraphQL). + + ‘ghub-graphql’ does not account for the fact that pagination works +differently in GraphQL than it does in REST, so users of this function +have to deal with that themselves. Likewise error handling works +differently and has to be done by the caller too. + + An early attempt at implementing automatic unpaginating for GraphQL +can be found in the ‘faithful-graphql’ branch, provided I haven’t +deleted that by now. On that branch I try to do things as intended by +the designers of GraphQL, using variables and fragments, and drowning in +a sea of boilerplate. + + The problem with that approach is that it only works for applications +that fetch specific information on demand and actually want things to be +paginated. I am convinced that GraphQL is very nice for web apps. + + However the Forge package for which I have implemented all of this +has very different needs. It wants to fetch "all the data" and "cache" +it locally, so that it is available even when there is no internet +connection. GraphQL was designed around the idea that you should be +able to "ask for what you need and get exactly that". But when that +boils down to "look, if I persist, then you are going to hand me over +all the data anyway, so just caught it up already", then things start to +fall apart. If Github’s GraphQL allowed pagination to be turned off +completely, then teaching ‘ghub-graphql’ about error handling would be +enough. + + But it doesn’t and when doing things as intended, then that leads to +huge amounts of repetitive boilerplate, which is so boring to write that +doing it without introducing bugs left and right is near impossible; so +I decided to give up on GraphQL variables, fragments and conditions, and +instead implement something more powerful, though also more opinionated. + + -- Function: ghub--graphql-vacuum query variables callback &optional + until &key narrow username auth host forge + This function is an opinionated alternative to ‘ghub-graphql’. It + relies on dark magic to get the job done. + + It makes an initial request using ‘QUERY’. It then looks for + paginated edges in the returned data and makes more requests to + resolve them. In order to do so it automatically transforms the + initial ‘QUERY’ into another query suitable for that particular + edge. The data retrieved by subsequent requests is then injected + into the data of the original request before that is returned or + passed to the callback. If subsequently retrieved data features + new paginated edges, then those are followed recursively. + + The end result is essentially the same as using ‘ghub-graphql’, if + only it were possible to say "do not paginate anything". The + implementation is much more complicated because it is not possible + to do that. + + ‘QUERY’ is a GraphQL query expressed as an s-expression. The + bundled ‘gsexp’ library is used to turn that into a GraphQL query + string. Only a subset of the GraphQL features are supported; + fragments for example are not, and magical stuff happens to + variables. This is not documented yet, I am afraid. Look at + existing callers. + + ‘VARIABLES’ is a JSON-like alist as for ‘ghub-graphql’. + + ‘UNTIL’ is an alist ‘((EDGE-until . VALUE)...)’. When unpaginating + ‘EDGE’ try not to fetch beyond the element whose first field has + the value ‘VALUE’ and remove that element as well as all "lesser" + elements from the retrieved data if necessary. Look at + ‘forge--pull-repository’ for an example. This is only useful if + you "cache" the response locally and want to avoid fetching data + again that you already have. + + Other arguments behave as for ‘ghub-graphql’ and ‘ghub-request’, + more or less. If ‘CALLBACK’ is ‘nil’, pretty-print the response. + + Using ‘ghub--graphql-vacuum’, the following resource specific +functions are implemented. These functions are not part of the public +API yet and are very much subject to change. + + -- Function: ghub-fetch-repository owner name callback &optional until + &key username auth host forge + This function asynchronously fetches forge data about the specified + repository. Once all data has been collected, ‘CALLBACK’ is called + with the data as the only argument. + + -- Function: ghub-fetch-issue owner name callback &optional until &key + username auth host forge + This function asynchronously fetches forge data about the specified + issue. Once all data has been collected, ‘CALLBACK’ is called with + the data as the only argument. + + -- Function: ghub-fetch-pullreq owner name callback &optional until + &key username auth host forge + This function asynchronously fetches forge data about the specified + pull-request. Once all data has been collected, ‘CALLBACK’ is + called with the data as the only argument. + + Note that in order to avoid duplication all of these functions base +their initial query on the query stored in ‘ghub-fetch-repository’. The +latter two pass that query through ‘ghub--graphql-prepare-query’, which +then uses ‘ghub--graphql-narrow-query’ to remove parts the caller is not +interested in. These two functions are also used internally, when +unpaginating, but as demonstrated here they can be useful even before +making an initial request. + + +File: ghub.info, Node: Github Convenience Wrappers, Prev: Making GraphQL Requests, Up: API + +3.4 Github Convenience Wrappers +=============================== + + -- Function: ghub-head resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: ghub-get resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + These functions are simple wrappers around ‘ghub-request’. Their + signature is identical to that of the latter, except that they do + not have an argument named ‘METHOD’. The HTTP method is instead + given by the second word in the function name. + + As described in the documentation for ‘ghub-request’, it depends on + the used method whether the value of the ‘PARAMS’ argument is used + as the query or the payload. For the "HEAD" and "GET" methods it + is used as the query. + + -- Function: ghub-put resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: ghub-post resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: ghub-patch resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: ghub-delete resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + These functions are simple wrappers around ‘ghub-request’. Their + signature is identical to that of the latter, except that they do + not have an argument named ‘METHOD’. The HTTP method is instead + given by the second word in the function name. + + As described in the documentation for ‘ghub-request’, it depends on + the used method whether the value of the ‘PARAMS’ argument is used + as the query or the payload. For the "PUT", "POST", "PATCH" and + "DELETE" methods it is used as the payload. + + -- Function: ghub-wait resource &optional duration &key username auth + host + Some API requests result in an immediate successful response even + when the requested action has not actually been carried out yet. + An example is the request for the creation of a new repository, + which doesn’t cause the repository to immediately become available. + The Github API documentation usually mentions this when describing + an affected resource. + + If you want to do something with some resource right after making a + request for its creation, then you might have to wait for it to + actually be created. This function can be used to do so. It + repeatedly tries to access the resource until it becomes available + or until a timeout is reached. In the latter case it signals + ‘ghub-error’. + + ‘RESOURCE’ specifies the resource that this function waits for. + + ‘DURATION’ specifies the maximum number of seconds to wait for, + defaulting to 64 seconds. Emacs will block during that time, but + the user can abort using ‘C-g’. + + The first attempt is made immediately and will often succeed. If + not, then another attempt is made after two seconds, and each + subsequent attempt is made after waiting as long as we already + waited between all preceding attempts combined. + + See ‘ghub-request’’s documentation above for information about the + other arguments. + + +File: ghub.info, Node: Notes, Next: Function Index, Prev: API, Up: Top + +4 Notes +******* + +* Menu: + +* Using Ghub in Personal Scripts:: +* Using Ghub in Your Own Package:: +* Forge Limitations and Notes:: + + +File: ghub.info, Node: Using Ghub in Personal Scripts, Next: Using Ghub in Your Own Package, Up: Notes + +4.1 Using Ghub in Personal Scripts +================================== + +You can of course use ‘ghub-request’ and its wrapper functions in your +personal scripts. Unlike when you use Ghub in a package that you +distribute for others to use, you don’t have to explicitly specify a +package in personal scripts. + + ;; This is perfectly acceptable in personal scripts ... + (ghub-get "/user") + + ;; ... and actually equals to + (ghub-get "/user" nil :auth 'ghub) + + ;; In packages you have to specify the package using AUTH. + (ghub-get "/user" nil :auth 'some-package) + + When the ‘AUTH’ argument is not specified, then a request is made on +behalf of the ‘ghub’ package itself. Like for any other package you +have to create a dedicated token of course. + + +File: ghub.info, Node: Using Ghub in Your Own Package, Next: Forge Limitations and Notes, Prev: Using Ghub in Personal Scripts, Up: Notes + +4.2 Using Ghub in Your Own Package +================================== + +Every package should use its own token. This allows you as the author +of some package to only request access to API scopes that are actually +needed, which in turn might make it easier for users to trust your +package not to do unwanted things. + + You have to tell ‘ghub-request’ on behalf of which package a request +is being made by passing the symbol ‘PACKAGE’ as the value of its ‘AUTH’ +argument. + + (ghub-request "GET" "/user" nil :auth 'PACKAGE) + + Keep in mind that the users of your package will have to manually +create a suitable token. To make that easier, you should not only link +to this manual but also prominently mention the scopes the token needs; +and explain what they are needed for. + + +File: ghub.info, Node: Forge Limitations and Notes, Prev: Using Ghub in Your Own Package, Up: Notes + +4.3 Forge Limitations and Notes +=============================== + + • There are no default Gitea and Gogs instances so the variables + ‘gitea.host’ and ‘gogs.host’ are not taken into account. + + • Gitea and Gogs do not support limiting a token to certain scopes. + + • The Bitbucket API is fairly broken and my willingness to deal with + that is extremely limited unless someone pays me vast amounts of + money. + + • The Gitlab API documentation is not always accurate, though I don’t + have an example at hand. It also isn’t structured well, making it + occasionally difficult to find the information one is looking for. + + • Where one would use ‘user/repo’ when accessing another forge, one + has to use ‘user%2Frepo’ when accessing Gitlab, e.g.: + + (ghub-get "/projects/python-mode-devs%2Fpython-mode" + nil :forge 'gitlab) + + +File: ghub.info, Node: Function Index, Next: Variable Index, Prev: Notes, Up: Top + +Appendix A Function Index +************************* + +[index] +* Menu: + +* ghub--graphql-vacuum: Making GraphQL Requests. + (line 56) +* ghub-continue: Making REST Requests. + (line 122) +* ghub-delete: Github Convenience Wrappers. + (line 31) +* ghub-fetch-issue: Making GraphQL Requests. + (line 105) +* ghub-fetch-pullreq: Making GraphQL Requests. + (line 111) +* ghub-fetch-repository: Making GraphQL Requests. + (line 99) +* ghub-get: Github Convenience Wrappers. + (line 9) +* ghub-graphql: Making GraphQL Requests. + (line 6) +* ghub-head: Github Convenience Wrappers. + (line 6) +* ghub-patch: Github Convenience Wrappers. + (line 28) +* ghub-post: Github Convenience Wrappers. + (line 25) +* ghub-put: Github Convenience Wrappers. + (line 22) +* ghub-request: Making REST Requests. + (line 6) +* ghub-response-link-relations: Making REST Requests. + (line 172) +* ghub-wait: Github Convenience Wrappers. + (line 44) + + +File: ghub.info, Node: Variable Index, Prev: Function Index, Up: Top + +Appendix B Variable Index +************************* + +[index] +* Menu: + +* ghub-response-headers: Making REST Requests. + (line 163) +* github.host: Github Configuration Variables. + (line 26) +* github.HOST.user: Github Configuration Variables. + (line 17) +* github.user: Github Configuration Variables. + (line 11) + + + +Tag Table: +Node: Top773 +Node: Introduction2207 +Node: Getting Started2612 +Node: Basic Concepts Arguments and Variables3639 +Node: Setting the Username7643 +Ref: Setting your Githubcom Username8131 +Ref: Setting your Gitlabcom Username8487 +Ref: Setting your Github Enterprise Username8953 +Ref: Setting your Username for Other Hosts and/or Forges9686 +Node: Creating and Storing a Token10492 +Ref: Creating a Token10705 +Ref: Storing a Token11666 +Node: Github Configuration Variables13409 +Node: API15120 +Node: Their APIs15322 +Node: Making REST Requests16004 +Node: Making GraphQL Requests25314 +Node: Github Convenience Wrappers32003 +Node: Notes35667 +Node: Using Ghub in Personal Scripts35874 +Node: Using Ghub in Your Own Package36765 +Node: Forge Limitations and Notes37700 +Node: Function Index38712 +Node: Variable Index40980 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/elpa/ghub-20260423.1634/gsexp.el b/elpa/ghub-20260423.1634/gsexp.el new file mode 100644 index 00000000..e869dafb --- /dev/null +++ b/elpa/ghub-20260423.1634/gsexp.el @@ -0,0 +1,102 @@ +;;; gsexp.el --- GraphQl as S-expressions -*- lexical-binding:t -*- + +;; Copyright (C) 2016-2026 Jonas Bernoulli + +;; Author: Jonas Bernoulli +;; Homepage: https://github.com/magit/ghub +;; Keywords: tools + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. +;; +;; This file 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this file. If not, see . + +;;; Commentary: + +;; This library encodes S-expressions as GraphQl documents. Currently +;; it is rather incomplete and only intended for use by `ghub-graphql' +;; but I might fill the gaps and add documentation eventually. I am +;; not in a hurry though. + +;;; Code: + +(require 'cl-lib) + +(defvar gsexp-one-variable-per-line nil) + +(defun gsexp-encode (document) + (gsexp--pp (gsexp--encode-field document))) + +(defun gsexp--encode-field (field) + (cond + ((stringp field) + field) + ((symbolp field) + (symbol-name field)) + ((listp (car field)) + (concat (format "%s: " (caar field)) + (gsexp--encode-field (cons (car (cdar field)) + (cdr field))))) + ((concat + (pcase (pop field) + (`[] "") + ((and (pred symbolp) op) (symbol-name op)) + (`[,op] (symbol-name op)) + (`[,op ,name] (format "%s %s" op name))) + (and (vectorp (car field)) + (format " (%s%s)" + (if gsexp-one-variable-per-line "\n" "") + (mapconcat #'gsexp--encode-argument + (append (pop field) nil) + (if gsexp-one-variable-per-line ",\n" ",")))) + (and field + (format " {\n%s\n}" + (mapconcat #'gsexp--encode-field field "\n"))))))) + +(cl-defun gsexp--encode-argument ((argument value)) + (format "%s: %s" argument (gsexp--encode-value value))) + +(defun gsexp--encode-value (value) + (cond + ((numberp value) + (number-to-string value)) + ((symbolp value) ; including variables, enums, booleans and null + (symbol-name value)) + ((stringp value) + (prin1-to-string value)) + ((vectorp value) + (format "(%s)" (mapconcat #'gsexp--encode-value value ""))) + ((listp value) + (format "{%s}" (mapconcat + (pcase-lambda (`(,name ,value)) + (format "%s: %s" name (gsexp--encode-value value))) + value ", "))) + ((error "Invalid field value: %S" value)))) + +(defun gsexp--pp (string) + (with-temp-buffer + (save-excursion + (insert string)) + (while (< (point) (point-max)) + (unless (and (bolp) (eolp)) + (save-excursion + (let ((level (car (syntax-ppss (line-beginning-position))))) + (when (looking-at "\\s-*\\s)") + (cl-decf level)) + (indent-line-to (* 2 level))))) + (forward-line 1)) + (buffer-string))) + +;;; _ +(provide 'gsexp) +;;; gsexp.el ends here