From 70eaeae3a324e197b292c5ceff6b922c86484e39 Mon Sep 17 00:00:00 2001 From: Andreas Gerler Date: Thu, 18 Jul 2024 15:10:10 +0200 Subject: [PATCH] add ghub Signed-off-by: Andreas Gerler --- elpa/ghub-20240507.1647/buck.el | 129 +++ elpa/ghub-20240507.1647/dir | 18 + elpa/ghub-20240507.1647/ghub-autoloads.el | 67 ++ elpa/ghub-20240507.1647/ghub-graphql.el | 615 +++++++++++ elpa/ghub-20240507.1647/ghub-pkg.el | 15 + elpa/ghub-20240507.1647/ghub.el | 998 ++++++++++++++++++ elpa/ghub-20240507.1647/ghub.info | 1172 +++++++++++++++++++++ elpa/ghub-20240507.1647/glab.el | 168 +++ elpa/ghub-20240507.1647/gogs.el | 141 +++ elpa/ghub-20240507.1647/gsexp.el | 102 ++ elpa/ghub-20240507.1647/gtea.el | 141 +++ 11 files changed, 3566 insertions(+) create mode 100644 elpa/ghub-20240507.1647/buck.el create mode 100644 elpa/ghub-20240507.1647/dir create mode 100644 elpa/ghub-20240507.1647/ghub-autoloads.el create mode 100644 elpa/ghub-20240507.1647/ghub-graphql.el create mode 100644 elpa/ghub-20240507.1647/ghub-pkg.el create mode 100644 elpa/ghub-20240507.1647/ghub.el create mode 100644 elpa/ghub-20240507.1647/ghub.info create mode 100644 elpa/ghub-20240507.1647/glab.el create mode 100644 elpa/ghub-20240507.1647/gogs.el create mode 100644 elpa/ghub-20240507.1647/gsexp.el create mode 100644 elpa/ghub-20240507.1647/gtea.el diff --git a/elpa/ghub-20240507.1647/buck.el b/elpa/ghub-20240507.1647/buck.el new file mode 100644 index 00000000..fff45968 --- /dev/null +++ b/elpa/ghub-20240507.1647/buck.el @@ -0,0 +1,129 @@ +;;; buck.el --- Client library for the Bitbucket API -*- lexical-binding:t -*- + +;; Copyright (C) 2016-2024 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: + +;; Buck is a library that provides basic support for using the Bitbucket API +;; from Emacs packages. It abstracts access to API resources using only +;; a handful of functions that are not resource-specific. + +;; This library is implemented on top of Ghub. Unlike Ghub, Buck does +;; not support the guided creation of tokens because Bitbucket lacks the +;; features that would be necessary to implement that. Users have to +;; create tokens through the web interface. + +;;; Code: + +(require 'ghub) + +(defconst buck-default-host "api.bitbucket.org/2.0" + "The default host that is used if `buck.host' is not set.") + +;; HEAD and PATCH are not supported according to +;; https://developer.atlassian.com/bitbucket/api/2/reference/meta/uri-uuid + +(cl-defun buck-get (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `GET' request for RESOURCE, with optional query PARAMS. +Like calling `ghub-request' (which see) with \"GET\" as METHOD +and `bitbucket' as FORGE." + (ghub-request "GET" resource params :forge 'bitbucket + :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 buck-put (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `PUT' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"PUT\" as METHOD +and `bitbucket' as FORGE." + (ghub-request "PUT" resource params :forge 'bitbucket + :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 buck-post (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `POST' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"POST\" as METHOD +and `bitbucket' as FORGE." + (ghub-request "POST" resource params :forge 'bitbucket + :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 buck-delete (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `DELETE' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"DELETE\" as METHOD +and `bitbucket' as FORGE." + (ghub-request "DELETE" resource params :forge 'bitbucket + :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 buck-request (method resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a request for RESOURCE and return the response body. +Like calling `ghub-request' (which see) with `bitbucket' as FORGE." + (ghub-request method resource params :forge 'bitbucket + :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 buck-repository-id (owner name &key username auth host) + "Return the id of the repository specified by OWNER, NAME and HOST." + (substring (cdr (assq 'uuid + (buck-get (format "/repositories/%s/%s" owner name) + nil + :username username :auth auth :host host))) + 1 -1)) + +;;; _ +(provide 'buck) +;;; buck.el ends here diff --git a/elpa/ghub-20240507.1647/dir b/elpa/ghub-20240507.1647/dir new file mode 100644 index 00000000..b703bcac --- /dev/null +++ b/elpa/ghub-20240507.1647/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-20240507.1647/ghub-autoloads.el b/elpa/ghub-20240507.1647/ghub-autoloads.el new file mode 100644 index 00000000..68d71a6a --- /dev/null +++ b/elpa/ghub-20240507.1647/ghub-autoloads.el @@ -0,0 +1,67 @@ +;;; 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 buck.el + +(register-definition-prefixes "buck" '("buck-default-host")) + + +;;; 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" '("ghub-")) + + +;;; Generated autoloads from ghub-graphql.el + +(register-definition-prefixes "ghub-graphql" '("ghub-")) + + +;;; Generated autoloads from glab.el + +(register-definition-prefixes "glab" '("glab-default-host")) + + +;;; Generated autoloads from gogs.el + +(register-definition-prefixes "gogs" '("gogs-default-host")) + + +;;; Generated autoloads from gsexp.el + +(register-definition-prefixes "gsexp" '("gsexp-")) + + +;;; Generated autoloads from gtea.el + +(register-definition-prefixes "gtea" '("gtea-default-host")) + +;;; 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-20240507.1647/ghub-graphql.el b/elpa/ghub-20240507.1647/ghub-graphql.el new file mode 100644 index 00000000..088c8198 --- /dev/null +++ b/elpa/ghub-20240507.1647/ghub-graphql.el @@ -0,0 +1,615 @@ +;;; ghub-graphql.el --- Access Github API using GrapthQL -*- lexical-binding:t -*- + +;; Copyright (C) 2016-2024 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 . + +;;; Code: + +(require 'ghub) +(require 'gsexp) +(require 'treepy) + +;; Needed for Emacs < 27. +(eval-when-compile (require 'json)) +(declare-function json-read-from-string "json" (string)) +(declare-function json-encode "json" (object)) + +(eval-when-compile (require 'pp)) ; Needed for Emacs < 29. +(eval-when-compile (require 'subr-x)) + +;;; Api + +(define-error 'ghub-graphql-error "GraphQL Error" 'ghub-error) + +(defvar ghub-graphql-items-per-request 100 + "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 (graphql + &optional variables + &key username auth host forge + headers silent + callback errorback value extra) + "Make a GraphQL request using GRAPHQL and VARIABLES. +Return the response as a JSON-like alist. Even if the response +contains `errors', do not raise an error. GRAPHQL is a GraphQL +string. VARIABLES is a JSON-like alist. The other arguments +behave as for `ghub-request' (which see)." + (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-rate-limit (&key username auth host) + "Return rate limit information." + (let-alist (ghub-graphql + '(query (rateLimit limit cost remaining resetAt)) + nil :username username :auth auth :host host) + .data.rateLimit)) + +(cl-defun ghub--repository-id (owner name &key username auth host) + "Return the id of the repository specified by OWNER, NAME and HOST." + (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)) + +;;; Api (drafts) + +(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 + 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) + (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--graphql-vacuum (if sparse + ghub-fetch-repository-sparse + ghub-fetch-repository) + `((owner . ,owner) + (name . ,name)) + callback until + :narrow '(repository) + :username username + :auth auth + :host host + :forge forge + :headers headers + :paginate paginate + :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--graphql-vacuum (ghub--graphql-narrow-query + ghub-fetch-repository + `(repository issues (issue . ,number))) + `((owner . ,owner) + (name . ,name)) + callback until + :narrow '(repository issue) + :username username + :auth auth + :host host + :forge forge + :headers headers + :paginate paginate + :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--graphql-vacuum (ghub--graphql-narrow-query + ghub-fetch-repository + `(repository pullRequests (pullRequest . ,number))) + `((owner . ,owner) + (name . ,name)) + callback until + :narrow '(repository pullRequest) + :username username + :auth auth + :host host + :forge forge + :headers headers + :paginate paginate + :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--graphql-vacuum (ghub--graphql-narrow-query + ghub-fetch-repository-review-threads + `(repository pullRequests (pullRequest . ,number))) + `((owner . ,owner) + (name . ,name)) + callback until + :narrow '(repository pullRequest) + :username username + :auth auth + :host host + :forge forge + :headers headers + :paginate paginate + :errorback errorback)) + +;;; Internal + +(defvar ghub--graphql-debug nil + "Whether `ghub--graphql-retrieve' updates the \" *gsexp-encode*\" buffer.") + +(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--graphql-vacuum ( query variables callback + &optional until + &key narrow username auth host forge + headers paginate errorback) + "Make a GraphQL request using QUERY and VARIABLES. +See Info node `(ghub)GraphQL Support'." + (unless host + (setq host (ghub--host forge))) + (unless (or username (stringp auth) (eq auth 'none)) + (setq username (ghub--username host forge))) + (ghub--graphql-retrieve + (ghub--make-graphql-req + :url (url-generic-parse-url + (format "https://%s/graphql" + (if (string-suffix-p "/v3" host) + (substring host 0 -3) + host))) + :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))) + :callback (and (not (eq callback 'synchronous)) callback) + :errorback (and (not (eq callback 'synchronous)) errorback)))) + +(defvar ghub--graphql-synchronous-value nil) + +(cl-defun ghub--graphql-synchronous ( query variables + &optional until + &key narrow username auth host forge + headers paginate) + "Make a synchronous GraphQL request using QUERY and VARIABLES. +See Info node `(ghub)GraphQL Support'." + (unwind-protect + (progn (ghub--graphql-vacuum query variables 'synchronous until + :narrow narrow :username username + :auth auth :host host :forge forge + :headers headers :paginate paginate) + ghub--graphql-synchronous-value) + (setq ghub--graphql-synchronous-value nil))) + +(cl-defun ghub--graphql-retrieve (req &optional lineage cursor) + (let ((p (cl-incf (ghub--graphql-req-pages req)))) + (when (> p 1) + (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--graphql-debug + (with-current-buffer (get-buffer-create " *gsexp-encode*") + (erase-buffer) + (insert (ghub--graphql-req-query-str req)))) + (ghub--retrieve + (let ((json-false nil)) + (ghub--encode-payload + `((query . ,(ghub--graphql-req-query-str req)) + (variables . ,(ghub--graphql-req-variables req))))) + 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) + (cl-block nil + (while t + (let ((node (treepy-node loc))) + (when (and (vectorp node) + (listp (aref node 0))) + (let ((alist (cl-coerce node 'list)) + vars) + (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 (cl-coerce variables 'vector) + (cdr node))) + (cl-return node)) + (setq loc (treepy-next loc))))))) + +(defun ghub--graphql-handle-response (status req) + (let ((buffer (current-buffer))) + (unwind-protect + (progn + (set-buffer-multibyte t) + (let* ((headers (ghub--handle-response-headers status req)) + (payload (ghub--handle-response-payload req)) + (payload (ghub--handle-response-error status payload req)) + (err (plist-get status :error)) + (errors (cdr (assq 'errors payload))) + (errors (and errors (cons 'ghub-graphql-error errors)))) + (if (or err errors) + (ghub--graphql-handle-failure + req (or err errors) headers status) + (ghub--graphql-walk-response req (assq 'data payload))))) + (when (buffer-live-p buffer) + (kill-buffer buffer))))) + +(defun ghub--graphql-handle-failure (req errors headers status) + (if-let ((errorback (ghub--req-errorback req))) + (let ((buffer (ghub--req-buffer req))) + (with-current-buffer + (if (buffer-live-p buffer) buffer (current-buffer)) + (funcall errorback errors headers status req))) + (ghub--signal-error errors))) + +(defun ghub--graphql-handle-success (req data) + (let ((callback (ghub--req-callback req)) + (buffer (ghub--req-buffer req)) + (narrow (ghub--graphql-req-narrow req))) + (while-let ((key (pop narrow))) + (setq data (cdr (assq key data)))) + (with-current-buffer + (if (buffer-live-p buffer) buffer (current-buffer)) + (funcall (or callback #'ghub--graphql-pp-response) + data)))) + +(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)))) + (cl-block nil + (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) + (cl-return)) + ((setq loc (treepy-replace loc (cons key nodes))))))))) + (cond ((not (treepy-end-p loc)) + (setq loc (treepy-next loc))) + ((ghub--req-callback req) + (ghub--graphql-handle-success req (treepy-root loc)) + (ghub--graphql-set-mode-line req nil) + (cl-return)) + (t + (setq ghub--graphql-synchronous-value (treepy-root loc)) + (cl-return))))))) + +(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 (cl-coerce (cadr query) 'list)) + (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 + (cl-coerce (cadr c) + 'list))) + (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 (lambda (elt) (and (listp elt) (listp (cdr elt))))) + (make-node (lambda (_ children) children))) + (treepy-zipper branchp #'identity make-node root))) + +(defun ghub--graphql-set-mode-line (req string &rest args) + (let ((buffer (ghub--graphql-req-buffer req))) + (when (buffer-live-p buffer) + (with-current-buffer buffer + (setq mode-line-process + (and string (concat " " (apply #'format string args)))) + (force-mode-line-update t))))) + +(defun ghub--graphql-pp-response (data) + (require 'pp) ; needed for Emacs < 29. + (pp-display-expression data "*Pp Eval Output*")) + +;;; _ +(provide 'ghub-graphql) +;;; ghub-graphql.el ends here diff --git a/elpa/ghub-20240507.1647/ghub-pkg.el b/elpa/ghub-20240507.1647/ghub-pkg.el new file mode 100644 index 00000000..c792cfcf --- /dev/null +++ b/elpa/ghub-20240507.1647/ghub-pkg.el @@ -0,0 +1,15 @@ +(define-package "ghub" "20240507.1647" "Client libraries for Git forge APIs." + '((emacs "26.3") + (compat "29.1.4.5") + (let-alist "1.0.6") + (treepy "0.1.2")) + :commit "1df5298c3c4d26ec3720dc6c31f8bfd07858f8a9" :authors + '(("Jonas Bernoulli" . "emacs.ghub@jonas.bernoulli.dev")) + :maintainer + '("Jonas Bernoulli" . "emacs.ghub@jonas.bernoulli.dev") + :keywords + '("tools") + :url "https://github.com/magit/ghub") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/ghub-20240507.1647/ghub.el b/elpa/ghub-20240507.1647/ghub.el new file mode 100644 index 00000000..2d89ea39 --- /dev/null +++ b/elpa/ghub-20240507.1647/ghub.el @@ -0,0 +1,998 @@ +;;; ghub.el --- Client libraries for Git forge APIs -*- lexical-binding:t -*- + +;; Copyright (C) 2016-2024 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: + +;; 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'. Additional forge-specific wrappers like `glab-put', +;; `gtea-put', `gogs-post' and `buck-delete' are also available. Ghub +;; does not provide any resource-specific functions, with the exception +;; of `FORGE-repository-id'. + +;; 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 'gnutls) +(require 'let-alist) +(require 'url) +(require 'url-auth) +(require 'url-http) + +(eval-when-compile (require 'subr-x)) + +;; Needed for Emacs < 27. +(eval-when-compile (require 'json)) +(declare-function json-read-from-string "json" (string)) +(declare-function json-encode "json" (object)) + +(declare-function glab-repository-id "glab" (owner name &key username auth host)) +(declare-function gtea-repository-id "gtea" (owner name &key username auth host)) +(declare-function gogs-repository-id "gogs" (owner name &key username auth host)) +(declare-function buck-repository-id "buck" (owner name &key username auth host)) + +(defvar url-callback-arguments) +(defvar url-http-end-of-headers) +(defvar url-http-extra-headers) +(defvar url-http-response-status) + +;;; Settings + +(defconst ghub-default-host "api.github.com" + "The default host that is used if `ghub.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-json-use-jansson nil + "Whether to use the Jansson library, if available. +This is experimental. Only let-bind this but do not enable it +globally because doing that is likely to break other packages +that use `ghub'. As a user also do not enable this yet. +See https://github.com/magit/ghub/pull/149.") + +(defvar ghub-json-object-type 'alist + "The object type that is used for json payload decoding.") + +(defvar ghub-json-array-type 'list + "The array type that is used for json payload decoding.") + +;;; 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) + (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 + 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 + :callback callback :errorback errorback :extra extra)) + +(cl-defun ghub-put (resource + &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + 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 + :callback callback :errorback errorback :extra extra)) + +(cl-defun ghub-post (resource + &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + 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 + :callback callback :errorback errorback :extra extra)) + +(cl-defun ghub-patch (resource + &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + 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 + :callback callback :errorback errorback :extra extra)) + +(cl-defun ghub-delete (resource + &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + 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 + :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 FORGE is `gitlab', then connect to Gitlab.com or, depending + on HOST, to another Gitlab instance. This is only intended for + internal use. Instead of using this argument you should use + function `glab-request' and other `glab-*' functions. + +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." + (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 (url-generic-parse-url + (concat (if (member host ghub-insecure-hosts) "http://" "https://") + (cond ((and (equal resource "/graphql") + (string-suffix-p "/v3" host)) + ;; Needed for some Github Enterprise instances. + (substring host 0 -3)) + ((and (equal resource "/api/graphql") + (string-suffix-p "/api/v4" host)) + ;; Needed for all Gitlab instances. + (substring host 0 -7)) + (host)) + resource + (and query (concat "?" (ghub--url-encode-params query))))) + :forge forge + :silent silent + ;; Encode in case caller used (symbol-name 'GET). #35 + :method (encode-coding-string method 'utf-8) + :headers (ghub--headers headers host auth username forge) + :handler #'ghub--handle-response + :unpaginate unpaginate + :noerror noerror + :reader reader + :buffer (current-buffer) + :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 (cl-mapcan (lambda (key) + (and-let* ((elt (assq key payload))) + (list elt))) + '(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." + (let ((fn (cl-case forge + ((nil ghub github) #'ghub--repository-id) + (gitlab #'glab-repository-id) + (gitea #'gtea-repository-id) + (gogs #'gogs-repository-id) + (bitbucket #'buck-repository-id) + (t (intern (format "%s-repository-id" forge)))))) + (unless (fboundp fn) + (error "ghub-repository-id: Forge type/abbreviation `%s' is unknown" + forge)) + (or (funcall fn owner name :username username :auth auth :host host) + (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 + +(defvar ghub-use-workaround-for-emacs-bug-54989 t + "Whether to work around Emacs bug debbugs#54989. + +If t (the default), work around the bug if necessary (i.e., if not +using Emacs 29 or later). If nil, then don't work around the bug. +Setting this variable only has an effect if it is done before +`ghub' is loaded. + +Also see https://github.com/magit/ghub/wiki/Known-Issues.") + +(cl-defun ghub--retrieve (payload req) + (let ((url-request-extra-headers + (let ((headers (ghub--req-headers req))) + (if (functionp headers) (funcall headers) headers))) + (url-request-method (ghub--req-method req)) + (url-request-data payload) + (url-show-status nil) + (url (ghub--req-url req)) + (handler (ghub--req-handler req)) + (silent (ghub--req-silent req))) + (if (or (ghub--req-callback req) + (ghub--req-errorback req)) + (url-retrieve url handler (list req) silent) + (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"))))) + +(defun ghub--handle-response (status req) + (let ((buffer (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 ((buffer (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 + (with-current-buffer + (if (buffer-live-p buffer) buffer (current-buffer)) + (funcall callback value headers status req))) + (t value)))))) + (when (buffer-live-p buffer) + (kill-buffer buffer))))) + +(defun ghub--handle-response-headers (_status req) + (goto-char (point-min)) + (forward-line 1) + (let (headers) + (when (memq url-http-end-of-headers '(nil 0)) + (setq url-debug t) + (error "BUG: missing headers; but there's a patch for that \ +see https://github.com/magit/ghub/wiki/Known-Issues")) + (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) + (let ((noerror (ghub--req-noerror req)) + (err (plist-get status :error))) + (if err + (if noerror + (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-filename (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) + (let ((raw (ghub--decode-payload))) + (and raw + (condition-case nil + (if (and ghub-json-use-jansson + (fboundp 'json-parse-string)) + (json-parse-string + raw + :object-type ghub-json-object-type + :array-type ghub-json-array-type + :false-object nil + :null-object nil) + (require 'json) + (let ((json-object-type ghub-json-object-type) + (json-array-type ghub-json-array-type) + (json-false nil) + (json-null nil)) + (json-read-from-string raw))) + ((json-parse-error json-readtable-error) + `((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) + (and payload + (progn + (unless (stringp payload) + (setq payload + (if (and ghub-json-use-jansson + (fboundp 'json-serialize)) + (json-serialize payload + ;; :object-type and :array-type + ;; are not supported here. + :false-object nil + :null-object :null) + (require 'json) + (let ((json-object-type ghub-json-object-type) + (json-array-type ghub-json-array-type) + (json-false nil) + (json-null :null)) + ;; Unfortunately `json-encode' may modify the input. + ;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=40693. + ;; and https://github.com/magit/forge/issues/267 + (json-encode (copy-tree payload)))))) + (encode-coding-string payload 'utf-8)))) + +(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 "&")) + +;;; 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) + (cl-ecase forge + ((nil gitea gogs bitbucket) + (cons "Authorization" (ghub--basic-auth host username))) + ((github gitlab) + (error "%s does not support basic authentication" + (capitalize (symbol-name forge))))) + (cons (cl-ecase forge + ((nil 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 (car (ghub--auth-source-get (list :secret) + :host host :user user)) + (progn + ;; 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. + ;; The (:max 1) is needed and has to be placed at the + ;; end for Emacs releases before 26.1. #24 #64 #72 + (auth-source-forget (list :host host :user user :max 1)) + (and (not nocreate) + (error "\ +Required %s token (\"%s\" for \"%s\") does not exist. +See https://magit.vc/manual/ghub/Getting-Started.html +or (info \"(ghub)Getting Started\") for instructions. +\(The setup wizard no longer exists.)" + (capitalize (symbol-name (or forge 'github))) + user host)))))) + (if (functionp token) (funcall token) token))) + +(cl-defgeneric ghub--host (&optional forge) + (cl-ecase forge + ((nil github) + (or (ignore-errors (car (process-lines "git" "config" "github.host"))) + ghub-default-host)) + (gitlab + (or (ignore-errors (car (process-lines "git" "config" "gitlab.host"))) + (bound-and-true-p glab-default-host))) + (gitea + (or (ignore-errors (car (process-lines "git" "config" "gitea.host"))) + (bound-and-true-p gtea-default-host))) + (gogs + (or (ignore-errors (car (process-lines "git" "config" "gogs.host"))) + (bound-and-true-p gogs-default-host))) + (bitbucket + (or (ignore-errors (car (process-lines "git" "config" "bitbucket.host"))) + (bound-and-true-p buck-default-host))))) + +(cl-defgeneric ghub--username (host &optional forge) + (let ((var + (cl-ecase forge + ((nil github) + (if (equal host ghub-default-host) + "github.user" + (format "github.%s.user" host))) + (gitlab + (if (equal host "gitlab.com/api/v4") + "gitlab.user" + (format "gitlab.%s.user" host))) + (bitbucket + (if (equal host "api.bitbucket.org/2.0") + "bitbucket.user" + (format "bitbucket.%s.user" host))) + (gitea + (when (zerop (call-process "git" nil nil nil "config" "gitea.host")) + (message "WARNING: gitea.host is set but always ignored")) + (format "gitea.%s.user" host)) + (gogs + (when (zerop (call-process "git" nil nil nil "config" "gogs.host")) + (message "WARNING: gogs.host is set but always ignored")) + (format "gogs.%s.user" host))))) + (condition-case nil + (car (process-lines "git" "config" var)) + (error + (let ((user (read-string + (format "Git variable `%s' is unset. Set to: " var)))) + (if (equal user "") + (user-error "The empty string is not a valid username") + (call-process + "git" nil nil nil "config" + (if (eq (read-char-choice + (format "Set %s=%s [g]lobally (recommended) or [l]ocally? " + var user) + (list ?g ?l)) + ?g) + "--global" + "--local") + var user) + user)))))) + +(defun ghub--ident (username package) + (format "%s^%s" username package)) + +(defun ghub--auth-source-get (keys &rest spec) + (declare (indent 1)) + (let ((plist (car (apply #'auth-source-search + (append spec (list :max 1)))))) + (mapcar (lambda (k) + (plist-get plist k)) + keys))) + +(when (< emacs-major-version 28) + ;; Fixed by Emacs commit 0b98ea5fbe276c67206896dca111c000f984ee0f. + (advice-add 'url-http-handle-authentication :around + 'url-http-handle-authentication@unauthorized-bugfix) + (defun url-http-handle-authentication@unauthorized-bugfix (fn proxy) + "If authorization failed then don't try again but fail properly. +For Emacs 27.1 prevent a useful `http' error from being replaced +by a generic one that omits all useful information. For earlier +releases prevent a new request from being made, which would +either result in an infinite loop or (e.g., in the case of `ghub') +the user being asked for their name." + (if (assoc "Authorization" url-http-extra-headers) + t ; Return "success", here also known as "successfully failed". + (funcall fn proxy)))) + +(when (and (< emacs-major-version 29) + ghub-use-workaround-for-emacs-bug-54989) + ;; Fixed in Emacs commit 0829c6836eff14dda0cf8b3047376967f7b000f4. + ;; Discussed in https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54989 + ;; and https://github.com/magit/ghub/issues/81#issuecomment-1100700165. + ;; + ;; Cleanup from 26faa2b943675107e1664b2fea7174137c473475 is not + ;; included in this copy because doing that would require changes + ;; to more functions. + ;; + ;; This function has seen a few other changes since Emacs 25.1. + ;; Of these only 4f1df40db36b221e7842bd75d6281922dcb268ee makes + ;; a functional change, fixing debbug#35658. The first release to + ;; contain that commit is 27.1. That commit either fixes a related + ;; bug or it deals with the same bug but only partially fixes it. + (advice-add 'url-http-chunked-encoding-after-change-function :override + 'url-http-chunked-encoding-after-change-function@54989-backport) + (defvar url-http-chunked-last-crlf-missing nil) + (defvar url-http-content-type) + (defvar url-http-chunked-start) + (defvar url-http-chunked-length) + (defvar url-http-chunked-counter) + (defun url-http-chunked-encoding-after-change-function@54989-backport + (st nd length) + "Backport bugfix from https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54989." + (if url-http-chunked-last-crlf-missing + (progn + (goto-char url-http-chunked-last-crlf-missing) + (if (not (looking-at "\r\n")) + (url-http-debug + "Still spinning for the terminator of last chunk...") + (url-http-debug "Saw the last CRLF.") + (delete-region (match-beginning 0) (match-end 0)) + (when (url-http-parse-headers) + (url-http-activate-callback)))) + (save-excursion + (goto-char st) + (let ((read-next-chunk t) + (case-fold-search t) + (regexp nil) + (no-initial-crlf nil)) + ;; We need to loop thru looking for more chunks even within + ;; one after-change-function call. + (while read-next-chunk + (setq no-initial-crlf (= 0 url-http-chunked-counter)) + (with-no-warnings + (if url-http-content-type + (url-display-percentage nil + "Reading [%s]... chunk #%d" + url-http-content-type url-http-chunked-counter) + (url-display-percentage nil + "Reading... chunk #%d" + url-http-chunked-counter))) + (url-http-debug "Reading chunk %d (%d %d %d)" + url-http-chunked-counter st nd length) + (setq regexp (if no-initial-crlf + "\\([0-9a-z]+\\).*\r?\n" + "\r?\n\\([0-9a-z]+\\).*\r?\n")) + + (if url-http-chunked-start + ;; We know how long the chunk is supposed to be, skip over + ;; leading crap if possible. + (if (> nd (+ url-http-chunked-start url-http-chunked-length)) + (progn + (url-http-debug "Got to the end of chunk #%d!" + url-http-chunked-counter) + (goto-char (+ url-http-chunked-start + url-http-chunked-length))) + (url-http-debug "Still need %d bytes to hit end of chunk" + (- (+ url-http-chunked-start + url-http-chunked-length) + nd)) + (setq read-next-chunk nil))) + (if (not read-next-chunk) + (url-http-debug "Still spinning for next chunk...") + (if no-initial-crlf (skip-chars-forward "\r\n")) + (if (not (looking-at regexp)) + (progn + ;; Must not have received the entirety of the chunk header, + ;; need to spin some more. + (url-http-debug "Did not see start of chunk @ %d!" (point)) + (setq read-next-chunk nil)) + ;; The data we got may have started in the middle of the + ;; initial chunk header, so move back to the start of the + ;; line and re-compute. + (when (= url-http-chunked-counter 0) + (beginning-of-line) + (looking-at regexp)) + (add-text-properties (match-beginning 0) (match-end 0) + (list 'chunked-encoding t + 'face 'cursor + 'invisible t)) + (setq url-http-chunked-length + (string-to-number (buffer-substring (match-beginning 1) + (match-end 1)) + 16) + url-http-chunked-counter (1+ url-http-chunked-counter) + url-http-chunked-start (set-marker + (or url-http-chunked-start + (make-marker)) + (match-end 0))) + (delete-region (match-beginning 0) (match-end 0)) + (url-http-debug "Saw start of chunk %d (length=%d, start=%d" + url-http-chunked-counter url-http-chunked-length + (marker-position url-http-chunked-start)) + (if (= 0 url-http-chunked-length) + (progn + ;; Found the end of the document! Wheee! + (url-http-debug "Saw end of stream chunk!") + (setq read-next-chunk nil) + (with-no-warnings + (url-display-percentage nil nil)) + ;; Every chunk, even the last 0-length one, is + ;; terminated by CRLF. Skip it. + (if (not (looking-at "\r?\n")) + (progn + (url-http-debug + "Spinning for the terminator of last chunk...") + (setq-local url-http-chunked-last-crlf-missing + (point))) + (url-http-debug "Removing terminator of last chunk") + (delete-region (match-beginning 0) (match-end 0)) + (when (re-search-forward "^\r?\n" nil t) + (url-http-debug "Saw end of trailers...")) + (when (url-http-parse-headers) + (url-http-activate-callback))))))))))))) + +;;; _ +(provide 'ghub) +(require 'ghub-graphql) +;;; ghub.el ends here diff --git a/elpa/ghub-20240507.1647/ghub.info b/elpa/ghub-20240507.1647/ghub.info new file mode 100644 index 00000000..a57ff1bf --- /dev/null +++ b/elpa/ghub-20240507.1647/ghub.info @@ -0,0 +1,1172 @@ +This is ghub.info, produced by makeinfo version 6.7 from ghub.texi. + + Copyright (C) 2017-2024 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 3.6.0. + + Copyright (C) 2017-2024 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:: + +Creating and Storing a Token + +* Creating a Token:: +* Storing a Token:: + + +API + +* Their APIs:: +* Making REST Requests:: +* Making GraphQL Requests:: +* Github Convenience Wrappers:: +* Non-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". + + This package used to attempt to get the answers to these questions +using a setup wizard. Unfortunately that had to be removed because (a) +it only ever supported Github, (b) by now Github has removed support for +that on their end, (c) it did not always work, and (d) when it couldn’t +be used, or failed, then it made things _more_ complicated. + + So now it is necessary for users to read some documentation and +because many things can go wrong, those instructions have to be fairly +detailed. You can of course skip over most of this, but if things go +wrong, then I would like to kindly request that you take another look +before asking me for help. + +* 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 +=========================================== + +Originally Ghub supported only Github but now it also supports Gitlab, +Gitea, Gogs and Bitbucket. For the historic reason just given, the +function ‘ghub-request’ defaults to acting on a ‘github’ forge, but can +be told to act on another forge using the FORGE argument. + + The FORGE argument only specifies what kind of forge to act on, not +which instance. 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 that 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 +================================ + +* Menu: + +* Creating a Token:: +* Storing a Token:: + + +File: ghub.info, Node: Creating a Token, Next: Storing a Token, Up: Creating and Storing a Token + +2.3.1 Creating a Token +---------------------- + +To create a token, use the web interface of the forge/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::. + + +File: ghub.info, Node: Storing a Token, Prev: Creating a Token, Up: Creating and Storing a Token + +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:: +* Non-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. This is only + intended for internal use. Instead of using this argument you + should use function ‘glab-request’ and other ‘glab-*’ + functions. + + • 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, Next: Non-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: Non-Github Convenience Wrappers, Prev: Github Convenience Wrappers, Up: API + +3.5 Non-Github Convenience Wrappers +=================================== + +‘ghub-request’ and ‘ghub-METHOD’ can be used to make a request for any +of the supported forge types, but except when making a request for a +‘github’ instance, then that requires the use of the FORGE argument. + + To avoid that, functions named ‘FORGE-request’ and ‘FORGE-METHOD’ are +also available. The following forms are equivalent, for example: + + (ghub-get ... :auth 'PACKAGE :forge 'gitlab) + (glab-get ... :auth 'PACKAGE) + + These forms would remain equivalent even if you did not specify a +value for the AUTH arguments — but you should not do that if you plan to +share your code with others (see *note Using Ghub in Your Own +Package::). If you do omit AUTH, then the request is made on behalf of +the ‘ghub’ package, *regardless* of the symbol prefix of the function +you use to do so. + + All ‘FORGE-request’ and ‘FORGE-METHOD’ functions, including but not +limited to ‘ghub-METHOD’, are very simple wrappers around +‘ghub-request’. They take fewer arguments than ‘ghub-request’ and +instead pass constant values for the arguments METHOD and/or FORGE. + + -- Function: buck-request resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: glab-request resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: gogs-request resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + Wrappers around ‘ghub-request’ which hardcode the FORGE to either + ‘bitbucket’, ‘gitlab’, ‘gogs’ or ‘gitea’. + + -- Function: buck-get resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: buck-put resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: buck-post resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: buck-delete resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: glab-head resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: glab-get resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: glab-put resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: glab-post resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: glab-patch resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: glab-delete resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: gogs-get resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: gogs-put resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: gogs-post resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: gogs-patch resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: gogs-delete resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: gtea-get resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: gtea-put resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: gtea-post resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: gtea-patch resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + -- Function: gtea-delete resource &optional params &key query payload + headers unpaginate noerror reader username auth host callback + errorback + Wrappers around ‘FORGE-METHOD’ which hardcode the FORGE to either + ‘bitbucket’, ‘gitlab’, ‘gogs’ or ‘gitea’, and the METHOD to the + implied method. + + Note that ‘buck-head’, ‘buck-patch’, ‘gogs-head’ and ‘gtea-head’ do + not exist because the respective APIs do not appear to support + these methods. + + +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.: + + (glab-get "/projects/python-mode-devs%2Fpython-mode") + + +File: ghub.info, Node: Function Index, Next: Variable Index, Prev: Notes, Up: Top + +Appendix A Function Index +************************* + +[index] +* Menu: + +* buck-delete: Non-Github Convenience Wrappers. + (line 49) +* buck-get: Non-Github Convenience Wrappers. + (line 40) +* buck-post: Non-Github Convenience Wrappers. + (line 46) +* buck-put: Non-Github Convenience Wrappers. + (line 43) +* buck-request: Non-Github Convenience Wrappers. + (line 28) +* ghub--graphql-vacuum: Making GraphQL Requests. + (line 56) +* ghub-continue: Making REST Requests. + (line 125) +* 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 174) +* ghub-wait: Github Convenience Wrappers. + (line 44) +* glab-delete: Non-Github Convenience Wrappers. + (line 67) +* glab-get: Non-Github Convenience Wrappers. + (line 55) +* glab-head: Non-Github Convenience Wrappers. + (line 52) +* glab-patch: Non-Github Convenience Wrappers. + (line 64) +* glab-post: Non-Github Convenience Wrappers. + (line 61) +* glab-put: Non-Github Convenience Wrappers. + (line 58) +* glab-request: Non-Github Convenience Wrappers. + (line 31) +* gogs-delete: Non-Github Convenience Wrappers. + (line 82) +* gogs-get: Non-Github Convenience Wrappers. + (line 70) +* gogs-patch: Non-Github Convenience Wrappers. + (line 79) +* gogs-post: Non-Github Convenience Wrappers. + (line 76) +* gogs-put: Non-Github Convenience Wrappers. + (line 73) +* gogs-request: Non-Github Convenience Wrappers. + (line 34) +* gtea-delete: Non-Github Convenience Wrappers. + (line 97) +* gtea-get: Non-Github Convenience Wrappers. + (line 85) +* gtea-patch: Non-Github Convenience Wrappers. + (line 94) +* gtea-post: Non-Github Convenience Wrappers. + (line 91) +* gtea-put: Non-Github Convenience Wrappers. + (line 88) + + +File: ghub.info, Node: Variable Index, Prev: Function Index, Up: Top + +Appendix B Variable Index +************************* + +[index] +* Menu: + +* ghub-response-headers: Making REST Requests. + (line 165) +* 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: Introduction2316 +Node: Getting Started2721 +Node: Basic Concepts Arguments and Variables4403 +Node: Setting the Username8257 +Ref: Setting your Githubcom Username8741 +Ref: Setting your Gitlabcom Username9097 +Ref: Setting your Github Enterprise Username9563 +Ref: Setting your Username for Other Hosts and/or Forges10296 +Node: Creating and Storing a Token11102 +Node: Creating a Token11366 +Node: Storing a Token12386 +Node: Github Configuration Variables14231 +Node: API15942 +Node: Their APIs16180 +Node: Making REST Requests16905 +Node: Making GraphQL Requests26318 +Node: Github Convenience Wrappers33001 +Node: Non-Github Convenience Wrappers36705 +Node: Notes42209 +Node: Using Ghub in Personal Scripts42416 +Node: Using Ghub in Your Own Package43307 +Node: Forge Limitations and Notes44242 +Node: Function Index45215 +Node: Variable Index50864 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/elpa/ghub-20240507.1647/glab.el b/elpa/ghub-20240507.1647/glab.el new file mode 100644 index 00000000..04e6ea1b --- /dev/null +++ b/elpa/ghub-20240507.1647/glab.el @@ -0,0 +1,168 @@ +;;; glab.el --- Client library for the Gitlab API -*- lexical-binding:t -*- + +;; Copyright (C) 2016-2024 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: + +;; Glab is a library that provides basic support for using the Gitlab API +;; from Emacs packages. It abstracts access to API resources using only +;; a handful of functions that are not resource-specific. + +;; This library is implemented on top of Ghub. Unlike Ghub, Glab does +;; not support the guided creation of tokens because Gitlab lacks the +;; features that would be necessary to implement that. Users have to +;; create tokens through the web interface. + +;;; Code: + +(require 'ghub) + +(defconst glab-default-host "gitlab.com/api/v4" + "The default host that is used if `glab.host' is not set.") + +(cl-defun glab-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 +and `gitlab' as FORGE." + (ghub-request "HEAD" resource params :forge 'gitlab + :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 glab-get (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `GET' request for RESOURCE, with optional query PARAMS. +Like calling `ghub-request' (which see) with \"GET\" as METHOD +and `gitlab' as FORGE." + (ghub-request "GET" resource params :forge 'gitlab + :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 glab-put (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `PUT' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"PUT\" as METHOD +and `gitlab' as FORGE." + (ghub-request "PUT" resource params :forge 'gitlab + :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 glab-post (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `POST' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"POST\" as METHOD +and `gitlab' as FORGE." + (ghub-request "POST" resource params :forge 'gitlab + :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 glab-patch (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `PATCH' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"PATCH\" as METHOD +and `gitlab' as FORGE." + (ghub-request "PATCH" resource params :forge 'gitlab + :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 glab-delete (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `DELETE' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"DELETE\" as METHOD +and `gitlab' as FORGE." + (ghub-request "DELETE" resource params :forge 'gitlab + :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 glab-request (method resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a request for RESOURCE and return the response body. +Like calling `ghub-request' (which see) with `gitlab' as FORGE." + (ghub-request method resource params :forge 'gitlab + :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 glab-graphql (graphql &optional variables + &key username auth host + headers silent + callback errorback value extra) + "Make a GraphQL request using GRAPHQL and VARIABLES. +Like calling `ghub-graphql' (which see) with `gitlab' as FORGE." + (ghub-graphql graphql variables :forge 'gitlab + :username username :auth auth :host host + :headers headers :silent silent + :callback callback :errorback errorback + :value value :extra extra)) + +(cl-defun glab-repository-id (owner name &key username auth host) + "Return the id of the repository specified by OWNER, NAME and HOST." + (number-to-string + (cdr (assq 'id (glab-get (format "/projects/%s%%2F%s" + (string-replace "/" "%2F" owner) + name) + nil :username username :auth auth :host host))))) + +;;; _ +(provide 'glab) +;;; glab.el ends here diff --git a/elpa/ghub-20240507.1647/gogs.el b/elpa/ghub-20240507.1647/gogs.el new file mode 100644 index 00000000..c34f938a --- /dev/null +++ b/elpa/ghub-20240507.1647/gogs.el @@ -0,0 +1,141 @@ +;;; gogs.el --- Client library for the Gogs API -*- lexical-binding:t -*- + +;; Copyright (C) 2016-2024 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: + +;; Gogs is a library that provides basic support for using the Gogs API +;; from Emacs packages. It abstracts access to API resources using only +;; a handful of functions that are not resource-specific. + +;; This library is implemented on top of Ghub. Unlike Ghub, Gogs does +;; not support the guided creation of tokens because Gogs lacks the +;; features that would be necessary to implement that. Users have to +;; create tokens through the web interface. + +;;; Code: + +(require 'ghub) + +(defconst gogs-default-host "localhost:3000/api/v1" + "The default Gogs host.") + +;; HEAD does not appear to be supported. + +(cl-defun gogs-get (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `GET' request for RESOURCE, with optional query PARAMS. +Like calling `ghub-request' (which see) with \"GET\" as METHOD +and `gogs' as FORGE." + (ghub-request "GET" resource params :forge 'gogs + :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 gogs-put (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `PUT' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"PUT\" as METHOD +and `gogs' as FORGE." + (ghub-request "PUT" resource params :forge 'gogs + :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 gogs-post (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `POST' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"POST\" as METHOD +and `gogs' as FORGE." + (ghub-request "POST" resource params :forge 'gogs + :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 gogs-patch (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `PATCH' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"PATCH\" as METHOD +and `gogs' as FORGE." + (ghub-request "PATCH" resource params :forge 'gogs + :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 gogs-delete (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `DELETE' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"DELETE\" as METHOD +and `gogs' as FORGE." + (ghub-request "DELETE" resource params :forge 'gogs + :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 gogs-request (method resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a request for RESOURCE and return the response body. +Like calling `ghub-request' (which see) with `gogs' as FORGE." + (ghub-request method resource params :forge 'gogs + :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 gogs-repository-id (owner name &key username auth host) + "Return the id of the repository specified by OWNER, NAME and HOST." + (number-to-string + (cdr (assq 'id (gogs-get (format "/repos/%s/%s" owner name) + nil :username username :auth auth :host host))))) + +;;; _ +(provide 'gogs) +;;; gogs.el ends here diff --git a/elpa/ghub-20240507.1647/gsexp.el b/elpa/ghub-20240507.1647/gsexp.el new file mode 100644 index 00000000..0ede22a6 --- /dev/null +++ b/elpa/ghub-20240507.1647/gsexp.el @@ -0,0 +1,102 @@ +;;; gsexp.el --- GraphQl as S-expressions -*- lexical-binding:t -*- + +;; Copyright (C) 2016-2024 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-20240507.1647/gtea.el b/elpa/ghub-20240507.1647/gtea.el new file mode 100644 index 00000000..e7c0292f --- /dev/null +++ b/elpa/ghub-20240507.1647/gtea.el @@ -0,0 +1,141 @@ +;;; gtea.el --- Client library for the Gitea API -*- lexical-binding:t -*- + +;; Copyright (C) 2016-2024 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: + +;; Gtea is a library that provides basic support for using the Gitea API +;; from Emacs packages. It abstracts access to API resources using only +;; a handful of functions that are not resource-specific. + +;; This library is implemented on top of Ghub. Unlike Ghub, Gtea does +;; not support the guided creation of tokens because Gitea lacks the +;; features that would be necessary to implement that. Users have to +;; create tokens through the web interface. + +;;; Code: + +(require 'ghub) + +(defconst gtea-default-host "localhost:3000/api/v1" + "The default Gitea host.") + +;; HEAD does not appear to be supported. + +(cl-defun gtea-get (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `GET' request for RESOURCE, with optional query PARAMS. +Like calling `ghub-request' (which see) with \"GET\" as METHOD +and `gitea' as FORGE." + (ghub-request "GET" resource params :forge 'gitea + :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 gtea-put (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `PUT' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"PUT\" as METHOD +and `gitea' as FORGE." + (ghub-request "PUT" resource params :forge 'gitea + :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 gtea-post (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `POST' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"POST\" as METHOD +and `gitea' as FORGE." + (ghub-request "POST" resource params :forge 'gitea + :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 gtea-patch (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `PATCH' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"PATCH\" as METHOD +and `gitea' as FORGE." + (ghub-request "PATCH" resource params :forge 'gitea + :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 gtea-delete (resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a `DELETE' request for RESOURCE, with optional payload PARAMS. +Like calling `ghub-request' (which see) with \"DELETE\" as METHOD +and `gitea' as FORGE." + (ghub-request "DELETE" resource params :forge 'gitea + :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 gtea-request (method resource &optional params + &key query payload headers + silent unpaginate noerror reader + username auth host + callback errorback extra) + "Make a request for RESOURCE and return the response body. +Like calling `ghub-request' (which see) with `gitea' as FORGE." + (ghub-request method resource params :forge 'gitea + :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 gtea-repository-id (owner name &key username auth host) + "Return the id of the repository specified by OWNER, NAME and HOST." + (number-to-string + (cdr (assq 'id (gtea-get (format "/repos/%s/%s" owner name) + nil :username username :auth auth :host host))))) + +;;; _ +(provide 'gtea) +;;; gtea.el ends here -- 2.47.3