From e73ea29b20565efd0f95c405b537d530ea21e79c Mon Sep 17 00:00:00 2001 From: Andreas Gerler Date: Tue, 28 Aug 2018 20:10:37 +0200 Subject: [PATCH] Update ghub. Signed-off-by: Andreas Gerler --- elpa/ghub-20171207.1537/ghub-autoloads.el | 37 - elpa/ghub-20171207.1537/ghub-pkg.el | 8 - elpa/ghub-20171207.1537/ghub.el | 574 ---------- elpa/ghub-20171207.1537/ghub.info | 864 -------------- .../dir | 2 +- elpa/ghub-20180821.1430/ghub-autoloads.el | 63 ++ elpa/ghub-20180821.1430/ghub-graphql.el | 65 ++ elpa/ghub-20180821.1430/ghub-pkg.el | 13 + elpa/ghub-20180821.1430/ghub.el | 856 ++++++++++++++ elpa/ghub-20180821.1430/ghub.info | 1002 +++++++++++++++++ elpa/ghub-20180821.1430/glab.el | 153 +++ elpa/ghub-readme.txt | 16 + 12 files changed, 2169 insertions(+), 1484 deletions(-) delete mode 100644 elpa/ghub-20171207.1537/ghub-autoloads.el delete mode 100644 elpa/ghub-20171207.1537/ghub-pkg.el delete mode 100644 elpa/ghub-20171207.1537/ghub.el delete mode 100644 elpa/ghub-20171207.1537/ghub.info rename elpa/{ghub-20171207.1537 => ghub-20180821.1430}/dir (89%) create mode 100644 elpa/ghub-20180821.1430/ghub-autoloads.el create mode 100644 elpa/ghub-20180821.1430/ghub-graphql.el create mode 100644 elpa/ghub-20180821.1430/ghub-pkg.el create mode 100644 elpa/ghub-20180821.1430/ghub.el create mode 100644 elpa/ghub-20180821.1430/ghub.info create mode 100644 elpa/ghub-20180821.1430/glab.el create mode 100644 elpa/ghub-readme.txt diff --git a/elpa/ghub-20171207.1537/ghub-autoloads.el b/elpa/ghub-20171207.1537/ghub-autoloads.el deleted file mode 100644 index 729d8ebf..00000000 --- a/elpa/ghub-20171207.1537/ghub-autoloads.el +++ /dev/null @@ -1,37 +0,0 @@ -;;; ghub-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) - -;;;### (autoloads nil "ghub" "ghub.el" (23082 53689 16798 734000)) -;;; Generated autoloads from ghub.el - -(autoload 'ghub-create-token "ghub" "\ -Create, store and return a new token. - -HOST is the Github instance, usually \"api.github.com\". -USERNAME is the name of a user on that instance. -PACKAGE is the package that will use the token. -SCOPES are the scopes the token is given access to. - -\(fn HOST USERNAME PACKAGE SCOPES)" t nil) - -(autoload 'ghub-token-scopes "ghub" "\ -Return and echo the scopes of the specified token. -This is intended for debugging purposes only. The user -has to provide several values including their password. - -\(fn HOST USERNAME PACKAGE)" t nil) - -;;;*** - -;;;### (autoloads nil nil ("ghub-pkg.el") (23082 53689 19979 831000)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; ghub-autoloads.el ends here diff --git a/elpa/ghub-20171207.1537/ghub-pkg.el b/elpa/ghub-20171207.1537/ghub-pkg.el deleted file mode 100644 index 06193fca..00000000 --- a/elpa/ghub-20171207.1537/ghub-pkg.el +++ /dev/null @@ -1,8 +0,0 @@ -(define-package "ghub" "20171207.1537" "minuscule client library for the Github API" - '((emacs "24.4") - (let-alist "1.0.5")) - :url "https://github.com/magit/ghub" :keywords - '("tools")) -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/elpa/ghub-20171207.1537/ghub.el b/elpa/ghub-20171207.1537/ghub.el deleted file mode 100644 index b1ceb4fe..00000000 --- a/elpa/ghub-20171207.1537/ghub.el +++ /dev/null @@ -1,574 +0,0 @@ -;;; ghub.el --- minuscule client library for the Github API -*- lexical-binding: t -*- - -;; Copyright (C) 2016-2017 Jonas Bernoulli - -;; Author: Jonas Bernoulli -;; Homepage: https://github.com/magit/ghub -;; Keywords: tools -;; Package-Requires: ((emacs "24.4") (let-alist "1.0.5")) - -;; This file is not part of GNU Emacs. - -;; 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, 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. - -;; For a copy of the GPL see https://www.gnu.org/licenses/gpl.txt. - -;;; Commentary: - -;; Ghub is a library that provides basic support for using the Github API -;; from Emacs packages. It abstracts access to API resources using only -;; a handful of functions that are not resource-specific. - -;; It also handles the creation, storage and use of access tokens using a -;; setup wizard, to make it easier for users to get started and to reduce -;; the support burden imposed on package maintainers. It also comes with -;; a comprehensive manual to address the cases when things don't just -;; work as expected. - -;; 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 adaption. It is assumed that -;; wide adoption would make life easier for users and maintainers alike, -;; because then all packages that talk to the Github API could be -;; configured the same way. - -;;; Code: - -(require 'auth-source) -(require 'cl-lib) -(require 'json) -(require 'let-alist) -(require 'url) -(require 'url-auth) - -(eval-when-compile (require 'subr-x)) - -(defvar url-http-end-of-headers) -(defvar url-http-response-status) - -;;; Settings - -(defconst ghub-default-host "api.github.com") - -(defvar ghub-github-token-scopes '(repo) - "The Github API scopes that your private tools need. - -The token that is created based on the value of this variable -is used when `ghub-request' (or one of its wrappers) is called -without providing a value for AUTH. Packages should always -identify themselves using that argument, but when you use Ghub -directly in private tools, then that is not necessary and the -request is made on behalf of the `ghub' package itself, aka on -behalf of some private tool. - -By default the only requested scope is `repo' because that is -sufficient as well as required for most common uses. This and -other scopes are documented at https://magit.vc/goto/2e586d36. - -If your private tools need other scopes, then you have to add -them here *before* creating the token. Alternatively you can -edit the scopes of an existing token using the web interface -at https://github.com/settings/tokens.") - -(defvar ghub-override-system-name nil - "If non-nil, the string used to identify the local machine. -If this is nil, then the value returned by `system-name' is -used instead.") - -;;; Request -;;;; API - -(defvar ghub-response-headers nil - "The headers returned in response to the last request. -`ghub-request' returns the response body and stores the -response header in this variable.") - -(defvar ghub-raw-response-body nil) - -(cl-defun ghub-head (resource &optional params - &key query payload headers - unpaginate noerror reader - username auth host) - "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 - :unpaginate unpaginate :noerror noerror :reader reader - :username username :auth auth :host host)) - -(cl-defun ghub-get (resource &optional params - &key query payload headers - unpaginate noerror reader - username auth host) - "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 - :unpaginate unpaginate :noerror noerror :reader reader - :username username :auth auth :host host)) - -(cl-defun ghub-put (resource &optional params - &key query payload headers - unpaginate noerror reader - username auth host) - "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 - :unpaginate unpaginate :noerror noerror :reader reader - :username username :auth auth :host host)) - -(cl-defun ghub-post (resource &optional params - &key query payload headers - unpaginate noerror reader - username auth host) - "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 - :unpaginate unpaginate :noerror noerror :reader reader - :username username :auth auth :host host)) - -(cl-defun ghub-patch (resource &optional params - &key query payload headers - unpaginate noerror reader - username auth host) - "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 - :unpaginate unpaginate :noerror noerror :reader reader - :username username :auth auth :host host)) - -(cl-defun ghub-delete (resource &optional params - &key query payload headers - unpaginate noerror reader - username auth host) - "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 - :unpaginate unpaginate :noerror noerror :reader reader - :username username :auth auth :host host)) - -(define-error 'ghub-error "Ghub Error") -(define-error 'ghub-http-error "HTTP Error" 'ghub-error) -(define-error 'ghub-301 "Moved Permanently" 'ghub-http-error) -(define-error 'ghub-400 "Bad Request" 'ghub-http-error) -(define-error 'ghub-401 "Unauthorized" 'ghub-http-error) -(define-error 'ghub-403 "Forbidden" 'ghub-http-error) -(define-error 'ghub-404 "Not Found" 'ghub-http-error) -(define-error 'ghub-422 "Unprocessable Entity" 'ghub-http-error) - -(cl-defun ghub-request (method resource &optional params - &key query payload headers - unpaginate noerror reader - username auth host) - "Make a request for RESOURCE and return the response body. - -Also place the response header 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. -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 UNPAGINATE is non-nil, then make multiple requests if necessary - to get all items at RESOURCE. -If NOERROR is non-nil, then do not raise an error if the request - fails and return nil instead. -If READER is non-nil, then it is used to read and return from the - response buffer. The default is `ghub--read-json-response'. - For the very few resources that do not return json, you might - want to use `ghub--read-raw-response'. - -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 also. - - 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 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." - (unless (string-prefix-p "/" resource) - (setq resource (concat "/" resource))) - (unless host - (setq host (ghub--host))) - (cond - ((not params)) - ((memq 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 (and payload (not (stringp payload))) - (setq payload (encode-coding-string (json-encode-list payload) 'utf-8))) - (let* ((qry (and query (concat "?" (ghub--url-encode-params query)))) - (buf (let ((url-request-extra-headers - `(("Content-Type" . "application/json") - ,@(and (not (eq auth 'none)) - (list (cons "Authorization" - (ghub--auth host auth username)))) - ,@headers)) - ;; Encode in case caller used (symbol-name 'GET). #35 - (url-request-method (encode-coding-string method 'utf-8)) - (url-request-data payload)) - (url-retrieve-synchronously - (concat "https://" host resource qry))))) - (unwind-protect - (with-current-buffer buf - (set-buffer-multibyte t) - (let (link body) - (goto-char (point-min)) - (let (headers) - (while (re-search-forward "^\\([^:]*\\): \\(.+\\)" - url-http-end-of-headers t) - (push (cons (match-string 1) - (match-string 2)) - headers)) - (and (setq link (cdr (assoc "Link" headers))) - (setq link (car (rassoc - (list "rel=\"next\"") - (mapcar (lambda (elt) (split-string elt "; ")) - (split-string link ","))))) - (string-match "[?&]page=\\([^&>]+\\)" link) - (setq link (match-string 1 link))) - (setq ghub-response-headers (nreverse headers))) - (unless url-http-end-of-headers - (error "ghub: url-http-end-of-headers is nil when it shouldn't")) - (goto-char (1+ url-http-end-of-headers)) - (setq body (funcall (or reader 'ghub--read-json-response))) - (unless (or noerror (= (/ url-http-response-status 100) 2)) - (let ((data (list method resource qry payload body))) - (pcase url-http-response-status - (301 (signal 'ghub-301 data)) - (400 (signal 'ghub-400 data)) - (401 (signal 'ghub-401 data)) - (403 (signal 'ghub-403 data)) - (404 (signal 'ghub-404 data)) - (422 (signal 'ghub-422 data)) - (_ (signal 'ghub-http-error - (cons url-http-response-status data)))))) - (if (and link unpaginate) - (nconc body - (ghub-request - method resource nil - :query (cons (cons 'page link) - (cl-delete 'page query :key #'car)) - :payload payload - :headers headers - :unpaginate t :noerror noerror :reader reader - :username username :auth auth :host host)) - body))) - (kill-buffer buf)))) - -(defun ghub-wait (resource &optional username auth host duration) - "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 subequent attemts are made after -waiting as long 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 (ignore-errors (ghub-get resource nil - :username username - :auth auth - :host host))) - (message "Waited (%3ss of %ss) for %s..." total duration resource) - (if (= total duration) - (signal 'ghub-error - (list (format "Github is taking too long to create %s" - resource))) - (if (> total 0) - (let ((wait (min total (- duration total)))) - (sit-for wait) - (cl-incf total wait)) - (sit-for (setq total 2)))))))) - -;;;; Internal - -(defun ghub--read-json-response () - (and (not (eobp)) - (let ((json-object-type 'alist) - (json-array-type 'list) - (json-key-type 'symbol) - (json-false nil) - (json-null nil)) - (json-read-from-string (ghub--read-raw-response))))) - -(defun ghub--read-raw-response () - (and (not (eobp)) - (setq ghub-raw-response-body - (decode-coding-string - (buffer-substring-no-properties (point) (point-max)) - 'utf-8)))) - -(defun ghub--url-encode-params (params) - (mapconcat (lambda (param) - (concat (url-hexify-string (symbol-name (car param))) "=" - (url-hexify-string (cdr param)))) - params "&")) - -;;; Authentication -;;;; API - -;;;###autoload -(defun ghub-create-token (host username package scopes) - "Create, store and return a new token. - -HOST is the Github instance, usually \"api.github.com\". -USERNAME is the name of a user on that instance. -PACKAGE is the package that will use the token. -SCOPES are the scopes the token is given access to." - (interactive - (pcase-let ((`(,host ,username ,package) - (ghub--read-triplet))) - (list host username package - (split-string - (read-string - "Scopes (separated by commas): " - (mapconcat #'symbol-name - (symbol-value - (intern (format "%s-github-token-scopes" package))) - ",")) - "," t "[\s\t]+")))) - (let ((user (ghub--ident username package))) - (cl-destructuring-bind (_save token) - (ghub--auth-source-get (list :save-function :secret) - :create t :host host :user user - :secret - (cdr (assq 'token - (ghub-post - "/authorizations" - `((scopes . ,scopes) - (note . ,(ghub--ident-github package))) - :username username :auth 'basic :host host)))) - ;; If the Auth-Source cache contains the information that there - ;; is no value, then setting the value does not invalidate that - ;; now incorrect information. - (auth-source-forget (list :host host :user user)) - token))) - -;;;###autoload -(defun ghub-token-scopes (host username package) - "Return and echo the scopes of the specified token. -This is intended for debugging purposes only. The user -has to provide several values including their password." - (interactive (ghub--read-triplet)) - (let ((scopes - (cdr (assq 'scopes (ghub--get-token-plist host username package))))) - (when (called-interactively-p 'any) - ;; Also show the input values to make it easy for package - ;; authors to verify that the user has done it correctly. - (message "Scopes for %s@%s: %S" - (ghub--ident username package) - host scopes)) - scopes)) - -;;;; Internal - -(defun ghub--auth (host auth &optional username) - (unless username - (setq username (ghub--username host))) - (if (eq auth 'basic) - (ghub--basic-auth host username) - (concat "token " - (encode-coding-string - (cl-typecase auth - (string auth) - (null (ghub--token host username 'ghub)) - (symbol (ghub--token host username auth)) - (t (signal 'wrong-type-argument - `((or stringp symbolp) ,auth)))) - 'utf-8)))) - -(defun ghub--basic-auth (host username) - (let ((url (url-generic-parse-url (concat "https://" host)))) - (setf (url-user url) username) - (url-basic-auth url t))) - -(defun ghub--token (host username package &optional nocreate) - (let ((user (ghub--ident username package))) - (or (ghub--auth-source-get :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. - (auth-source-forget (list :host host :user user)) - (and (not nocreate) - (ghub--confirm-create-token host username package)))))) - -(defun ghub--host () - (or (ignore-errors (car (process-lines "git" "config" "github.host"))) - ghub-default-host)) - -(defun ghub--username (host) - (let ((var (if (string-prefix-p "api.github.com" host) - "github.user" - (format "github.%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)))) - (or (and user (progn (call-process "git" nil nil nil - "config" "--global" var user) - user)) - (user-error "Abort"))))))) - -(defun ghub--ident (username package) - (format "%s^%s" username package)) - -(defun ghub--ident-github (package) - (format "Emacs package %s @ %s" - package - (or ghub-override-system-name (system-name)))) - -(defun ghub--package-scopes (package) - (symbol-value (intern (format "%s-github-token-scopes" package)))) - -(defun ghub--confirm-create-token (host username package) - (let* ((ident (ghub--ident-github package)) - (scopes (ghub--package-scopes package)) - (max-mini-window-height 40)) - (if (yes-or-no-p - (format - "Such a Github API token is not available: - - Host: %s - User: %s - Package: %s - - Scopes requested in `%s-github-token-scopes':\n%s - Store locally according to `auth-sources':\n %S - Store on Github as:\n %S - -WARNING: If you have enabled two-factor authentication then - you have to abort and create the token manually. - -If in doubt, then abort and view the documentation first. - -Otherwise confirm and then provide your Github username and -password at the next two prompts. Depending on the backend -you might have to provide a passphrase and confirm that you -really want to save the token. - -Create and store such a token? " - host username package package - (mapconcat (lambda (scope) (format " %s" scope)) scopes "\n") - auth-sources ident)) - (progn - (when (ghub--get-token-id host username package) - (if (yes-or-no-p - (format - "A token named %S\nalready exists on Github. Replace it?" - ident)) - (ghub--delete-token host username package) - (user-error "Abort"))) - (ghub-create-token host username package scopes)) - (user-error "Abort")))) - -(defun ghub--get-token-id (host username package) - (let ((ident (ghub--ident-github package))) - (cl-some (lambda (x) - (let-alist x - (and (equal .app.name ident) .id))) - (ghub-get "/authorizations" nil - :unpaginate t - :username username :auth 'basic :host host)))) - -(defun ghub--get-token-plist (host username package) - (ghub-get (format "/authorizations/%s" - (ghub--get-token-id host username package)) - nil :username username :auth 'basic :host host)) - -(defun ghub--delete-token (host username package) - (ghub-delete (format "/authorizations/%s" - (ghub--get-token-id host username package)) - nil :username username :auth 'basic :host host)) - -(defun ghub--read-triplet () - (let ((host (read-string "Host: " (ghub--host)))) - (list host - (read-string "Username: " (ghub--username host)) - (intern (read-string "Package: " "ghub"))))) - -(defun ghub--auth-source-get (key:s &rest spec) - (declare (indent 1)) - (let ((plist (car (apply #'auth-source-search :max 1 spec)))) - (cl-flet ((value (k) (let ((v (plist-get plist k))) - (if (functionp v) (funcall v) v)))) - (if (listp key:s) - (mapcar #'value key:s) - (value key:s))))) - -(advice-add 'auth-source-netrc-parse-next-interesting :around - 'auth-source-netrc-parse-next-interesting@save-match-data) -(defun auth-source-netrc-parse-next-interesting@save-match-data (fn) - "Save match-data for the benefit of caller `auth-source-netrc-parse-one'. -Without wrapping this function in `save-match-data' the caller -won't see the secret from a line that is followed by a commented -line." - (save-match-data (funcall fn))) - -;;; ghub.el ends soon -(provide 'ghub) -;; Local Variables: -;; indent-tabs-mode: nil -;; End: -;;; ghub.el ends here diff --git a/elpa/ghub-20171207.1537/ghub.info b/elpa/ghub-20171207.1537/ghub.info deleted file mode 100644 index d7cae31c..00000000 --- a/elpa/ghub-20171207.1537/ghub.info +++ /dev/null @@ -1,864 +0,0 @@ -This is ghub.info, produced by makeinfo version 5.2 from ghub.texi. - - Copyright (C) 2017 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). Minuscule 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 a library that provides basic support for using the Github API -from Emacs packages. It abstracts access to API resources using only a -handful of functions that are not resource-specific. - - It also handles the creation, storage and use of access tokens using -a setup wizard, to make it easier for users to get started and to reduce -the support burden imposed on package maintainers. It also comes with a -comprehensive manual to address the cases when things don’t just work as -expected. - - 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 adaption. It is assumed that wide -adoption would make life easier for users and maintainers alike, because -then all packages that talk to the Github API could be configured the -same way. - - This manual is for Ghub version 1.3.0 (v1.3.0-22-g45cd8c8+1). - - Copyright (C) 2017 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:: -* Limitations:: -* Getting Started:: -* Using Ghub in Personal Scripts:: -* Using Ghub in a Package:: -* API:: - -— The Detailed Node Listing — - -Getting Started - -* Setting the Username:: -* Interactively Creating and Storing a Token:: -* Manually Creating and Storing a Token:: -* How Ghub uses Auth-Source:: - -API - -* Making Requests:: -* Authentication:: -* Configuration Variables:: - - - -File: ghub.info, Node: Introduction, Next: Limitations, Prev: Top, Up: Top - -1 Introduction -************** - -Ghub is a library that provides basic support for using the Github API -from Emacs packages. It abstracts access to API resources using only a -handful of functions that are not resource-specific. - - It also handles the creation, storage and use of access tokens using -a setup wizard, to make it easier for users to get started and to reduce -the support burden imposed on package maintainers. It also comes with a -comprehensive manual to address the cases when things don’t just work as -expected. - - 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 adaption. - - Fancier interfaces can implemented on top of Ghub, and one such -wrapper — named simply Ghub+ — has already been implemented. The -benefit of basing various opinionated interfaces on top of a single -library that provides only the core functionality is that choosing the -programming interface does no longer also dictate how access tokens are -handled. Users can then use multiple packages that access the Github -API without having to learn the various incompatible ways packages -expects the appropriate token to be made available to them. - - Ghub uses the built-in ‘auth-source’ library to store access tokens. -That library is very flexible and supports multiple backends, which -means that it is up to the user how secrets are stored. They can, among -other things, choose between storing secrets in plain text for ease of -use, or encrypted for better security. - - Previously (as in until this library is widely adapted) it was up to -package authors to decide if things should be easy or secure. (Note -that ‘auth-source’ defaults to "easy" — you have been warned.) - - Ghub expects package authors to use a dedicated access token instead -of sharing a single token between all packages that rely on it. That -means that users cannot configure Ghub once and later start using a new -package without any additional setup. But Ghub helps with that. - - When the user invokes some command defined in some package and that -ultimately results in ‘ghub-request’ being called and the appropriate -token is not available yet, then the user is guided through the process -of creating and storing a new token, and at the end of that process the -request is carried out as if the token had been available to begin with. - - -File: ghub.info, Node: Limitations, Next: Getting Started, Prev: Introduction, Up: Top - -2 Limitations -************* - -As mentioned, Ghub restricts itself to provide core functionality and -leaves it to other libraries to do opinionated things on top of that. -But currently there also are some limitations that concern the core -parts. Obviously these limitations have to be lifted to maximize -adaption and so they will. - - These secondary features already exist (just mentioning them, so you -don’t assume that they don’t): - - • Support for Github Enterprise instances. - - • Support for token scope validation. - - These features will likely never be implemented in Ghub: - - • Functions for particular resources. - - • Using something other than json-like alists to handle what the - Github API calls "parameters". - - • Retrieving an existing token. Github does not support it so we - cannot do it. If you lose your local copy of a token, then you - have to replace it with a new one. - - These features are not supported yet: - - • Asynchronous requests are not supported yet. - - • The Github GraphQL API is not supported yet. - - • If a request fails due to a missing scope, then that isn’t handled - by ‘ghub-request’ yet. For now package authors just have to hope - that users don’t mess with scopes or handle such errors themselves. - - Likewise, other errors too should be handled by Ghub. - - • Packages cannot request additional scopes. - - Once a token has been created a package cannot request new scopes - that it previously did not need. I am not quite sure yet whether - Ghub should support that. An alternative would be to let packages - at least invalidate their own tokens. - - • Other Git forges (Gitlab, Bitbucket…) are not supported. - - Support for other forges will likely be implemented using *other* - libraries that are very similar to this library. Whether common - code will be factored out into a library to be used by all these - libraries or whether some code duplication is to be preferred, has - not been decided yet. - - -File: ghub.info, Node: Getting Started, Next: Using Ghub in Personal Scripts, Prev: Limitations, Up: Top - -3 Getting Started -***************** - -If you would like to use a package that uses this library, then just do -so. If the necessary information isn’t available when it attempts to -make a request, then you will be asked to provide it. - -* Menu: - -* Setting the Username:: -* Interactively Creating and Storing a Token:: -* Manually Creating and Storing a Token:: -* How Ghub uses Auth-Source:: - - -File: ghub.info, Node: Setting the Username, Next: Interactively Creating and Storing a Token, Up: Getting Started - -3.1 Setting the Username -======================== - -If you haven’t set the Git variable ‘github.user’ yet when making a -request, then you will be asked: - - Git variable `github.user' is unset. Set to: - - You are expected to provide your Github username here. The provided -value will be saved globally (using ‘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, *before* making a request: - - cd /path/to/repo - git config github.user USERNAME - - For Github Enterprise instances you have to specify where the API can -be accessed *before* you try to access it 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: - - # Do this once - git config --global github.example.com/api/v3.user EMPLOYEE - - # Do this for every corporate repository - cd /path/to/repo - git config github.host example.com/api/v3 - - If you do not set ‘github.example.com/api/v3.user’, then you will be -asked to provide the value when trying to make a request, but you do -have to manually set ‘github.host’, or Ghub assumes that you are trying -to access ‘api.github.com’. - - -File: ghub.info, Node: Interactively Creating and Storing a Token, Next: Manually Creating and Storing a Token, Prev: Setting the Username, Up: Getting Started - -3.2 Interactively Creating and Storing a Token -============================================== - -Ghub uses a different token for every package as well as for every -machine from which you access the Github API (and obviously also for -every Github instance and user). This allows packages to only request -the scopes that they actually need and also gives users the opportunity -to refuse access to certain scopes if they expect to not use the -features that need them. - - Usually you don’t have to worry about creating and storing a token -yourself and can just make a request. Note however that you don’t have -to use the setup wizard described below. Alternatively you can perform -the setup manually as described in the next section. - - Unfortunately the wizard does not work (yet?) if you have enabled -two-factor authentication, in which case the token has to be created and -stored manually. - - If you make a request and the required token is not available yet, -then the setup wizard will first ask you something like this: - - Such a Github API token is not available: - - Host: api.github.com - User: USERNAME - Package: PACKAGE - - Scopes requested in `PACKAGE-github-token-scopes': - repo - Store locally according to `auth-sources': - ("~/.authinfo" "~/.authinfo.gpg" "~/.netrc") - Store on Github as: - "Emacs package FOO @ LOCAL-MACHINE" - - WARNING: If you have enabled two-factor authentication - then you have to create the token manually. - - If in doubt, then abort and view the documentation first. - - Create and store such a token? (yes or no) - - If you don’t have any doubts, then answer "yes". Lets address some -of the doubts that you might have: - - • Unfortunately Ghub cannot know whether you have enabled two-factor - authentication without making a request using basic (password) - authentication, which it cannot do if you have enabled two-factor - authentication. It also cannot determine whether you have enabled - two-factor authentication depending on whether a request fails or - not because there are other reasons why a request might fail. - - So this warning is always displayed and you are expected to know - whether you have enabled two-factor authentication or not. If you - have, then you must abort and see *note Manually Creating and - Storing a Token::. - - • ‘Host’ usually is "api.github.com" and that’s usually what you - want. If you are trying to access a Github Enterprise instance, - then it should be something else and you have to set the value - manually as described in the next section. - - • ‘User’ should be your Github.com (or Github Enterprise instance) - username. If it is something else, then you made a mistake at the - first prompt or during the step describe in the previous section - and have to refer to that in order to fix this issue. - - • ‘Package’ should be the name of the package you are using to access - the Github API. - - If it is ‘ghub’, then the package author disregarded that - convention and you should probably report a bug in the issue - tracker of that package. - - Or you yourself are using ‘ghub-request’ or one of its wrappers - directly, in which case this is expected and perfectly fine. In - that case you might however want to abort and change the value of - the variable ‘ghub-github-token-scopes’ before triggering the - wizard again. - - • Each PACKAGE has to specify the tokens that it needs using a - variable named ‘PACKAGE-github-token-scopes’. The doc-string of - that variable should document why the various scopes are needed. - - The meaning of the various scopes are documented at - . - - • The value of ‘auth-sources’ is shown. The default value causes - secrets to be stored in plain text. Because this might be - unexpected, Ghub additionally displays a warning when appropriate. - - WARNING: ... unencrypted ... TODO implement ... - - Whether that is something that needs fixing, is up to you. If your - answer is yes, then you should abort and see *note How Ghub uses - Auth-Source:: for instructions on how to save the token more - securely. - - • When creating a token it is necessary to provide a token - description. Ghub used descriptions that have the form "Emacs - package PACKAGE @ LOCAL-MACHINE". - - Github uses the token description to identify the token, not merely - as something useful to humans. Token descriptions therefore have - to be unique and in rare cases you get an additional prompt, asking - you something like: - - A token named "Emacs package FOO @ LOCAL-MACHINE" - already exists on Github. Replace it? - - You might see this message when you have lost the old token and - want to replace it with a new one, in which case you should - obviously just proceed. - - Or two of your computers have the same hostname, which is bad - practice because it gains you nothing but leads to issues such as - this. Or you are dual-booting on this machine and use the same - hostname in all operating systems, which is a somewhat reasonable - thing to do. - - In either case you will have to use something other than the value - returned by ‘system-name’ to identify the current machine or - operating system. Or you can continue to identify different things - using the same identifier, in which case you have to manually - distribute the token. - - The former is recommended and also easier to do, using the variable - ‘ghub-override-system-name’. See *note Configuration Variables:: - for details. - - -File: ghub.info, Node: Manually Creating and Storing a Token, Next: How Ghub uses Auth-Source, Prev: Interactively Creating and Storing a Token, Up: Getting Started - -3.3 Manually Creating and Storing a Token -========================================= - -If you have enabled two-factor authentication, then you have to create -and store access tokens yourself. You might also prefers to always do -it manually to have more control than when relying on the setup wizard. - - If you cannot or don’t want to use the wizard then you have to (1) -figure out what scopes a package wants, (2) create such a token using -the web interface and (3) store the token where Ghub expects to find it. - - A package named PACKAGE has to specify the scopes that it wants in -the variable named ‘PACKAGE-ghub-token-scopes’. The doc-string of such -variables should document what the various scopes are needed for. - - To create or edit a token go to . - - Finally store the token in a place where Ghub looks for it as -describe in *note How Ghub uses Auth-Source::. - - -File: ghub.info, Node: How Ghub uses Auth-Source, Prev: Manually Creating and Storing a Token, Up: Getting Started - -3.4 How Ghub uses Auth-Source -============================= - -Please see *note (auth)Top:: for all the gory details about Auth-Source. -Some Ghub-specific information and important notes follow. - - The variable ‘auth-sources’ controls how and where Auth-Source stores -new secrets and where it looks for known secrets. The default value is -‘("~/.authinfo" "~/.authinfo.gpg" "~/.netrc")’, which means that it -looks in all of these files in order to find secrets and that it stores -new secrets in ‘~/.authinfo’ because that is the first element of the -list. It doesn’t matter which files already do or don’t exist when -storing a new secret, the first file is always used. - - Secrets are stored in ‘~/.authinfo’ in plain text. If you don’t want -that (good choice), then you have to customize ‘auth-sources’ e.g. by -flipping the positions of the first two elements. - - Auth-Source also supports storing secrets in various key-chains, -refer to its documentation for more information. - - Some 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". - - An entry in one of the three mentioned files would then look like -this: - - machine api.github.com login tarsius^ghub password 012345abcdef... - - -File: ghub.info, Node: Using Ghub in Personal Scripts, Next: Using Ghub in a Package, Prev: Getting Started, Up: Top - -4 Using Ghub in Personal Scripts -******************************** - -You can use ‘ghub-request’ and its wrapper functions in your personal -scripts of course. Unlike when you use Ghub from a package that you -distribute for others to use, you don’t have to specify a package in -personal scripts. - - ;; This is perfectly acceptable in personal scripts ... - (ghub-get "/user") - - ;; ... and actually equal to - (ghub-get "/user" nil :auth 'ghub) - - ;; In packages you have to specify the package using AUTH. - (ghub-get "/user" nil :auth 'foobar) - - When you do not specify the AUTH argument, then a request is made on -behalf of the ‘ghub’ package itself. Like for any package that uses -Ghub, ‘ghub’ has to declare what scopes it needs, using, in this case, -the variable ‘ghub-github-token-scopes’. - - The default value of that variable is ‘(repo)’ and you might want to -add additional scopes. You can later add additional scopes to an -existing token, using the web interface at -. - - If you do that, then you might want to also set the variable -accordingly, but note that Ghub only consults that when *creating* a new -token. If you want to know a token’s effective scopes use the command -‘ghub-token-scopes’, described in the next section. - - -File: ghub.info, Node: Using Ghub in a Package, Next: API, Prev: Using Ghub in Personal Scripts, Up: Top - -5 Using Ghub in a 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. - - The scopes used by PACKAGE have to be defined using the variable -‘PACKAGE-github-token-scopes’, and you have to tell ‘ghub-request’ on -behalf of what 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) - - -- Variable: PACKAGE-github-token-scopes - - This variable defines the token scopes requested by the package - named PACKAGE. The doc-string should explain what the various - scopes are needed for to prevent users from giving PACKAGE fewer - permissions than it absolutely needs and also to give them greater - confidence that PACKAGE is only requesting the permissions that it - actually need. - - The value of this variable does not necessarily correspond to the - scopes that the respective token actually gives access to. There - is nothing that prevents users from changing the value *after* - creating the token or from editing the token’s scopes later on. - - So it is pointless to check the value of this variable before - making a request. You also should not query the API to reliably - determine the supported tokens before making a query. Doing the - latter would mean that every request becomes two requests and that - the first request would have to be done using the user’s password - instead of a token. - - -- Command: ghub-token-scopes - - Because we cannot be certain that the user hasn’t messed up the - scopes, Ghub provides this command to make it easy to debug such - issues without having to rely on users being thoughtful enough to - correctly determine the used scopes manually. - - Just tell users to run ‘M-x ghub-token-scopes’ and to provide the - correct values for the HOST, USERNAME and PACKAGE when prompted, - and to then post the output. - - It is to be expected that users will occasionally mess that up so - this command does not only output the scopes but also the user - input so that you can have greater confidence in the validity of - the user’s answer. - - Scopes for USERNAME^PACKAGE@HOST: (SCOPE...) - - -File: ghub.info, Node: API, Prev: Using Ghub in a Package, Up: Top - -6 API -***** - -This section describes the Ghub API. In other words it describes the -public functions and variables provided by the Ghub library and not the -Github API that can be accessed by using those functions. The latter is -documented at . - -* Menu: - -* Making Requests:: -* Authentication:: -* Configuration Variables:: - - -File: ghub.info, Node: Making Requests, Next: Authentication, Up: API - -6.1 Making Requests -=================== - - -- Function: ghub-request method resource &optional params &key query - payload headers unpaginate noerror reader username auth host - - This function make 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 header in - 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 are stored in the ‘cdr’ (not - the ‘cadr’) and can be strings, integers and 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. - - • 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 UNPAGINATE is non-nil, then this function makes multiple - requests if necessary to get all items at RESOURCE. - - • If NOERROR is non-nil, then no error is raised if the request - fails and ‘nil’ is returned instead. - - • If READER is non-nil, then it is used to read and return from - the response buffer. The default is - ‘ghub--read-json-response’. For the very few resources that - do not return json, you might want to use - ‘ghub--read-raw-response’. - - • 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, 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 also. - - 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 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. - - -- 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 header use this variable after - ‘ghub-request’ has returned. - - -- Variable: ghub-override-system-name - - If non-nil, the value of this variable is used to override the - value returned by ‘system-name’ for the purpose of identifying the - local machine, which is necessary because Ghub uses separate tokens - for each machine. Also see *note Configuration Variables::. - - -- Variable: ghub-github-token-scopes - -- Variable: PACKAGE-github-token-scopes - - Such a variable define the token scopes requested by the respective - package PACKAGE given by the first word in the variable name. - ‘ghub’ itself is treated like any other package. Also see *note - Using Ghub in a Package::. - - -- Function: ghub-head resource &optional params &key query payload - headers unpaginate noerror reader username auth host - -- Function: ghub-get resource &optional params &key query payload - headers unpaginate noerror reader username auth host - - 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 - -- Function: ghub-post resource &optional params &key query payload - headers unpaginate noerror reader username auth host - -- Function: ghub-patch resource &optional params &key query payload - headers unpaginate noerror reader username auth host - -- Function: ghub-delete resource &optional params &key query payload - headers unpaginate noerror reader username auth host - - 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 username auth host duration - - 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 mention this when describing - an affected resource. - - If you want to do something with some resource right after creating - 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 the timeout exceeds. In the latter case it - signals ‘ghub-error’. - - RESOURCE specifies the resource that this function waits for. - - DURATION specifies for how many seconds to wait at most, 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 often that will actually - 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: Authentication, Next: Configuration Variables, Prev: Making Requests, Up: API - -6.2 Authentication -================== - - -- Command: ghub-create-token - - This command creates a new token using the values it reads from the - user and then stores it according to variable ‘auth-sources’. It - can also be called non-interactively, but you shouldn’t do that - yourself. - - This is useful if you want fully setup things before attempting to - make the initial request, if you want to provide fewer than the - requested scopes or customize ‘auth-sources’ first, or if something - has gone wrong when using the wizard that is used when making a - request without doing this first. (Note that instead of using this - command you can also just repeat the initial request after making - the desired adjustments — that is easier.) - - This command reads, in that order, the HOST (Github instance), the - USERNAME, the PACKAGE and the SCOPES in the minibuffer, providing - reasonable default choices. SCOPES defaults to the scopes that - PACKAGE requests using the variable ‘PACKAGE-github-token-scopes’. - - -- Command: ghub-token-scopes - - Users are free to give a token access to fewer scopes than what the - respective package requested. That can of course lead to issues - and package maintainers have to be able to quickly determine if - such a (mis-)configuration is the root cause when users report - issues. - - This command reads the required values in the minibuffer and then - shows a message containing these values along with the scopes of - the respective token. It also returns the scopes (only) when - called non-interactively. Also see *note Using Ghub in a - Package::. - - -File: ghub.info, Node: Configuration Variables, Prev: Authentication, Up: API - -6.3 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 the 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 (define in - the constant ‘ghub-default-host-host’, which you should never - attempt to change.) - - -- Variable: ghub-override-system-name - - Ghub uses a different token for each quadruple ‘(USERNAME PACKAGE - HOST LOCAL-MACHINE)‘. Theoretically it could reuse tokens to some - extend but that would be more difficult to implement, less - flexible, and less secure (though slightly more convenient). - - A token is identified on the respective Github instance (Github.com - or a Github Enterprise instance) using the pair ‘(PACKAGE . - LOCAL-MACHINE)‘, or more precisely the string "Emacs package - PACKAGE @ LOCAL-MACHINE". USERNAME and HOST do not have to be - encoded because the token is stored for USERNAME on HOST and cannot - be used by another user and/or on another instance. - - There is one potential problem though; for any given ‘(PACKAGE . - LOCAL-MACHINE)‘ there can only be one token identified by "Emacs - package PACKAGE @ LOCAL-MACHINE", Github does not allow multiple - tokens with the same description because it uses the description as - the identifier. (It could use some hash instead, but alas it does - not.) - - If you have multiple machines and some of them have the same name, - then you should probably change that as this is not how thing ought - to be. However if you dual-boot, then it might make sense to give - that machine the same name regardless of what operating system you - have booted into. - - You could use the same token on both operating systems, but setting - that up might be somewhat difficult because it is not possible to - download an existing token from Github. You could of course - locally copy the token, but that is inconvenient and would make it - harder to only revoke the token used on your infected Windows - installation without also revoking it for your totally safe *BSD - installation. - - Alternatively you can set this variable to a unique value, that - will then be used to identify the local machine instead of the - value returned by ‘system-name’. - - - -Tag Table: -Node: Top757 -Node: Introduction2786 -Node: Limitations5322 -Node: Getting Started7463 -Node: Setting the Username7965 -Node: Interactively Creating and Storing a Token9390 -Node: Manually Creating and Storing a Token15345 -Node: How Ghub uses Auth-Source16433 -Node: Using Ghub in Personal Scripts18003 -Node: Using Ghub in a Package19452 -Node: API22017 -Node: Making Requests22444 -Node: Authentication31001 -Node: Configuration Variables32800 - -End Tag Table - - -Local Variables: -coding: utf-8 -End: diff --git a/elpa/ghub-20171207.1537/dir b/elpa/ghub-20180821.1430/dir similarity index 89% rename from elpa/ghub-20171207.1537/dir rename to elpa/ghub-20180821.1430/dir index 6bd2b68c..8392fa02 100644 --- a/elpa/ghub-20171207.1537/dir +++ b/elpa/ghub-20180821.1430/dir @@ -5,7 +5,7 @@ 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, "?" lists all Info commands, "d" returns here, + Typing "q" exits, "H" lists all Info commands, "d" returns here, "h" gives a primer for first-timers, "mEmacs" visits the Emacs manual, etc. diff --git a/elpa/ghub-20180821.1430/ghub-autoloads.el b/elpa/ghub-20180821.1430/ghub-autoloads.el new file mode 100644 index 00000000..d8d4fbcc --- /dev/null +++ b/elpa/ghub-20180821.1430/ghub-autoloads.el @@ -0,0 +1,63 @@ +;;; ghub-autoloads.el --- automatically extracted autoloads +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "ghub" "ghub.el" (0 0 0 0)) +;;; Generated autoloads from ghub.el + +(autoload 'ghub-create-token "ghub" "\ +Create, store and return a new token. + +HOST is the Github instance, usually \"api.github.com\". +USERNAME is the name of a user on that instance. +PACKAGE is the package that will use the token. +SCOPES are the scopes the token is given access to. + +\(fn HOST USERNAME PACKAGE SCOPES)" t nil) + +(autoload 'ghub-token-scopes "ghub" "\ +Return and echo the scopes of the specified token. +This is intended for debugging purposes only. The user +has to provide several values including their password. + +\(fn HOST USERNAME PACKAGE)" t nil) + +(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+'. + +\(fn)" t nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "ghub" '("auth-source-netrc-parse-next-interesting@save-match-data" "ghub-"))) + +;;;*** + +;;;### (autoloads nil "glab" "glab.el" (0 0 0 0)) +;;; Generated autoloads from glab.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "glab" '("glab-default-host"))) + +;;;*** + +;;;### (autoloads nil nil ("ghub-graphql.el" "ghub-pkg.el") (0 0 +;;;;;; 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; ghub-autoloads.el ends here diff --git a/elpa/ghub-20180821.1430/ghub-graphql.el b/elpa/ghub-20180821.1430/ghub-graphql.el new file mode 100644 index 00000000..8f24fb56 --- /dev/null +++ b/elpa/ghub-20180821.1430/ghub-graphql.el @@ -0,0 +1,65 @@ +;;; ghub-graphql.el --- access Github API using GrapthQL -*- lexical-binding: t -*- + +;; Copyright (C) 2016-2018 Jonas Bernoulli + +;; Author: Jonas Bernoulli +;; Homepage: https://github.com/magit/ghub + +;; This file is not part of GNU Emacs. + +;; 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, 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. + +;; For a copy of the GPL see https://www.gnu.org/licenses/gpl.txt. + +;;; Code: + +(require 'ghub) + +(cl-defun ghub-graphql (graphql &optional variables + &key username auth host + 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 (stringp graphql)) + (cl-assert (not (stringp variables))) + (ghub-request "POST" "/graphql" nil :payload + (json-encode `(("query" . ,graphql) + ,@(and variables `(("variables" ,@variables))))) + :silent silent + :username username :auth auth :host host + :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 ($owner:String!, $name:String!) { + repository(owner:$owner, name:$name) { id } + }" + `((owner . ,owner) + (name . ,name)) + :username username :auth auth :host host) + .data.repository.id)) + +;;; _ +(provide 'ghub-graphql) +;;; ghub-graphql.el ends here diff --git a/elpa/ghub-20180821.1430/ghub-pkg.el b/elpa/ghub-20180821.1430/ghub-pkg.el new file mode 100644 index 00000000..bdb8ea6c --- /dev/null +++ b/elpa/ghub-20180821.1430/ghub-pkg.el @@ -0,0 +1,13 @@ +(define-package "ghub" "20180821.1430" "minuscule client library for the Github API" + '((emacs "24.4") + (let-alist "1.0.5")) + :keywords + '("tools") + :authors + '(("Jonas Bernoulli" . "jonas@bernoul.li")) + :maintainer + '("Jonas Bernoulli" . "jonas@bernoul.li") + :url "https://github.com/magit/ghub") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/ghub-20180821.1430/ghub.el b/elpa/ghub-20180821.1430/ghub.el new file mode 100644 index 00000000..c1f861f6 --- /dev/null +++ b/elpa/ghub-20180821.1430/ghub.el @@ -0,0 +1,856 @@ +;;; ghub.el --- minuscule client library for the Github API -*- lexical-binding: t -*- + +;; Copyright (C) 2016-2018 Jonas Bernoulli + +;; Author: Jonas Bernoulli +;; Homepage: https://github.com/magit/ghub +;; Keywords: tools +;; Package-Requires: ((emacs "24.4") (let-alist "1.0.5")) + +;; This file is not part of GNU Emacs. + +;; 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, 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. + +;; For a copy of the GPL see https://www.gnu.org/licenses/gpl.txt. + +;;; Commentary: + +;; Ghub is a library that provides basic support for using the Github API +;; from Emacs packages. It abstracts access to API resources using only +;; a handful of functions that are not resource-specific. + +;; Ghub handles the creation, storage and use of access tokens using a +;; setup wizard to make it easier for users to get started and to reduce +;; the support burden imposed on package maintainers. It also comes with +;; a comprehensive manual to address the cases when things don't just +;; work as expected or in case you don't want to use the wizard. + +;; 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 the Github API could be +;; configured the same way. + +;;; Code: + +(require 'auth-source) +(require 'cl-lib) +(require 'json) +(require 'let-alist) +(require 'url) +(require 'url-auth) +(require 'url-http) + +(eval-when-compile (require 'subr-x)) + +(defvar url-callback-arguments) +(defvar url-http-end-of-headers) +(defvar url-http-extra-headers) +(defvar url-http-response-status) + +;;; Settings + +(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. + +The token that is created based on the value of this variable +is used when `ghub-request' (or one of its wrappers) is called +without providing a value for AUTH. Packages should always +identify themselves using that argument, but when you use Ghub +directly in private tools, then that is not necessary and the +request is made on behalf of the `ghub' package itself, aka on +behalf of some private tool. + +By default the only requested scope is `repo' because that is +sufficient as well as required for most common uses. This and +other scopes are documented at URL `https://magit.vc/goto/2e586d36'. + +If your private tools need other scopes, then you have to add +them here *before* creating the token. Alternatively you can +edit the scopes of an existing token using the web interface +at URL `https://github.com/settings/tokens'.") + +(defvar ghub-override-system-name nil + "If non-nil, the string used to identify the local machine. +If this is nil, then the value returned by `system-name' is +used instead.") + +;;; Request +;;;; Object + +(cl-defstruct (ghub--req + (:constructor ghub--make-req) + (:copier nil)) + (url nil :read-only nil) + (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) + (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 header 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 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 make one or more + asynchronous requests and call CALLBACK or ERRORBACK when + finished. If an error occurred, then call ERRORBACK, or if + that is nil, then CALLBACK. When no error occurred then call + CALLBACK. When making asynchronous requests, then no errors + are signaled, regardless of the value of NOERROR. + +Both callbacks are called with four arguments. + 1. For CALLBACK, the combined value of the retrieved pages. + For ERRORBACK, the error that occured 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 "https://" host resource + (and query (concat "?" (ghub--url-encode-params query))))) + :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 + :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 callbacks 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)) + (or (ghub--retrieve nil req) t))) + +(cl-defun ghub-wait (resource &optional duration &key username auth host) + "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-get resource nil + :noerror t + :username username + :auth auth + :host host)) + (message "Waited (%3ss of %ss) for %s..." total duration resource) + (if (= total duration) + (error "Github is taking too long to create %s" 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 (&optional headers) + "Return an alist of link relations in HEADERS. +If optional HEADERS is nil, then return those +in `ghub-response-headers'." + (let ((rels (cdr (assoc "Link" (or headers ghub-response-headers))))) + (and rels (mapcar (lambda (elt) + (pcase-let ((`(,url ,rel) (split-string elt "; "))) + (cons (intern (substring rel 5 -1)) + (substring url 1 -1)))) + (split-string rels ", "))))) + +;;;; Internal + +(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) + ;; When this function has already been called, then it is a + ;; no-op. Otherwise it sets `url-registered-auth-schemes' among + ;; other things. If we didn't ensure that it has been run, then + ;; `url-retrieve-synchronously' would do it, which would cause + ;; the value that we let-bind below to be overwritten, and the + ;; "default" value to be lost outside the let-binding. + (url-do-setup) + (with-current-buffer + (let ((url-registered-auth-schemes + '(("basic" ghub--basic-auth-errorback . 10)))) + (url-retrieve-synchronously url silent)) + (funcall handler (car url-callback-arguments) req))))) + +(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 (nconc (ghub--req-value req) payload)) + (next (cdr (assq 'next (ghub-response-link-relations + headers))))) + (when (numberp unpaginate) + (cl-decf unpaginate)) + (setf (ghub--req-url req) + (url-generic-parse-url next)) + (setf (ghub--req-value req) value) + (setf (ghub--req-unpaginate req) unpaginate) + (or (and next + unpaginate + (or (eq unpaginate t) + (> unpaginate 0)) + (ghub-continue req)) + (let ((callback (ghub--req-callback req)) + (errorback (ghub--req-errorback req)) + (err (plist-get status :error))) + (cond ((and err errorback) + (funcall errorback err headers status req)) + (callback + (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) + (while (re-search-forward "^\\([^:]*\\): \\(.+\\)" + url-http-end-of-headers t) + (push (cons (match-string 1) + (match-string 2)) + headers)) + (setq headers (nreverse headers)) + (unless url-http-end-of-headers + (error "BUG: missing headers %s" (plist-get status :error))) + (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)) + payload))) + +(defun ghub--signal-error (err &optional payload) + (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)) + payload))) + (signal 'ghub-error data)) + (signal symb data)))) + +(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 + (let ((json-object-type 'alist) + (json-array-type 'list) + (json-key-type 'symbol) + (json-false nil) + (json-null nil)) + (json-read-from-string raw)) + (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 (json-encode-list 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)) "=" + (if (integerp val) + (number-to-string val) + (url-hexify-string val))))) + params "&")) + +;;; Authentication +;;;; API + +;;;###autoload +(defun ghub-create-token (host username package scopes) + "Create, store and return a new token. + +HOST is the Github instance, usually \"api.github.com\". +USERNAME is the name of a user on that instance. +PACKAGE is the package that will use the token. +SCOPES are the scopes the token is given access to." + (interactive + (pcase-let ((`(,host ,username ,package) + (ghub--read-triplet))) + (list host username package + (split-string + (read-string + "Scopes (separated by commas): " + (mapconcat #'symbol-name + (symbol-value + (intern (format "%s-github-token-scopes" package))) + ",")) + "," t "[\s\t]+")))) + (let ((user (ghub--ident username package))) + (cl-destructuring-bind (save token) + (ghub--auth-source-get (list :save-function :secret) + :create t :host host :user user + :secret + (cdr (assq 'token + (ghub-post + "/authorizations" + `((scopes . ,scopes) + (note . ,(ghub--ident-github package))) + :username username :auth 'basic :host host)))) + ;; Build-in back-ends return a function that does the actual + ;; saving, while for some third-party back-ends ":create t" + ;; is enough. + (when (functionp save) + (funcall save)) + ;; If the Auth-Source cache contains the information that there + ;; is no value, then setting the value does not invalidate that + ;; now incorrect information. + (auth-source-forget (list :host host :user user)) + token))) + +;;;###autoload +(defun ghub-token-scopes (host username package) + "Return and echo the scopes of the specified token. +This is intended for debugging purposes only. The user +has to provide several values including their password." + (interactive (ghub--read-triplet)) + (let ((scopes + (cdr (assq 'scopes (ghub--get-token-plist host username package))))) + (when (called-interactively-p 'any) + ;; Also show the input values to make it easy for package + ;; authors to verify that the user has done it correctly. + (message "Scopes for %s@%s: %S" + (ghub--ident username package) + host scopes)) + scopes)) + +;;;###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) + (if (eq forge 'gitlab) + (error "Gitlab does not support basic authentication") + (cons (cons "Authorization" (ghub--basic-auth host username)) + headers)) + (cons (ghub--auth host auth username forge) headers))))) + +(defun ghub--auth (host auth &optional username forge) + (unless username + (setq username (ghub--username host))) + (if (eq auth 'basic) + (if (eq forge 'gitlab) + (error "Gitlab does not support basic authentication") + (cons "Authorization" (ghub--basic-auth host username))) + (cons (if (eq forge 'gitlab) + "Private-Token" + "Authorization") + (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 (concat "https://" host)))) + (setf (url-user url) username) + (url-basic-auth url t))) + +(defun ghub--basic-auth-errorback (url &optional prompt _overwrite _realm _args) + ;; This gets called twice. Do nothing the first time, + ;; when PROMPT is nil. See `url-get-authentication'. + (when prompt + (if (assoc "X-GitHub-OTP" (ghub--handle-response-headers nil nil)) + (progn + (setq url-http-extra-headers + `(("Content-Type" . "application/json") + ("X-GitHub-OTP" . ,(ghub--read-2fa-code)) + ;; Without "Content-Type" and "Authorization". + ;; The latter gets re-added from the return value. + ,@(cddr url-http-extra-headers))) + ;; Return the cached values, they are correct. + (url-basic-auth url nil nil nil)) + ;; Remove the invalid cached values and fail, which + ;; is better than the invalid values sticking around. + (setq url-http-real-basic-auth-storage + (cl-delete (format "%s:%d" (url-host url) (url-port url)) + url-http-real-basic-auth-storage + :test #'equal :key #'car)) + nil))) + +(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. See #24, #64. + (auth-source-forget (list :host host :user user :max 1)) + (and (not nocreate) + (if (eq forge 'gitlab) + (error + (concat + "Required Gitlab token does not exist. See " + "https://magit.vc/manual/ghub/Gitlab-Support.html " + "for instructions.")) + (ghub--confirm-create-token host username package))))))) + (if (functionp token) (funcall token) token))) + +(defun ghub--host (&optional forge) + (if (eq forge 'gitlab) + (or (ignore-errors (car (process-lines "git" "config" "gitlab.host"))) + (bound-and-true-p glab-default-host)) + (or (ignore-errors (car (process-lines "git" "config" "github.host"))) + ghub-default-host))) + +(defun ghub--username (host &optional forge) + (let ((var (cond ((string-prefix-p "api.github.com" host) "github.user") + ((string-prefix-p "gitlab.com/api" host) "gitlab.user") + ((eq forge 'gitlab) (format "gitlab.%s.user" host)) + (t (format "github.%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)))) + (or (and user (progn (call-process "git" nil nil nil + "config" "--global" var user) + user)) + (user-error "Abort"))))))) + +(defun ghub--ident (username package) + (format "%s^%s" username package)) + +(defun ghub--ident-github (package) + (format "Emacs package %s @ %s" + package + (or ghub-override-system-name (system-name)))) + +(defun ghub--package-scopes (package) + (let ((var (intern (format "%s-github-token-scopes" package)))) + (if (boundp var) + (symbol-value var) + (error "%s fails to define %s" package var)))) + +(defun ghub--confirm-create-token (host username package) + (let* ((ident (ghub--ident-github package)) + (scopes (ghub--package-scopes package)) + (max-mini-window-height 40)) + (if (let ((message-log-max nil)) + (yes-or-no-p + (format + "Such a Github API token is not available: + + Host: %s + User: %s + Package: %s + + Scopes requested in `%s-github-token-scopes':\n%s + Store on Github as:\n %S + Store locally according to option `auth-sources':\n %S +%s +If in doubt, then abort and first view the section of the Ghub +documentation called \"Manually Creating and Storing a Token\". + +Otherwise confirm and then provide your Github username and +password at the next two prompts. Depending on the backend +you might have to provide a passphrase and confirm that you +really want to save the token. + +Create and store such a token? " + host username package package + (mapconcat (lambda (scope) (format " %s" scope)) scopes "\n") + ident auth-sources + (if (and (stringp (car auth-sources)) + (not (string-suffix-p ".gpg" (car auth-sources)))) + (format " +WARNING: The token will be stored unencrypted in %S. + If you don't want that, you have to abort and customize + the `auth-sources' option.\n" (car auth-sources)) + "")))) + (progn + (when (ghub--get-token-id host username package) + (if (yes-or-no-p + (format + "A token named %S\nalready exists on Github. Replace it?" + ident)) + (ghub--delete-token host username package) + (user-error "Abort"))) + (ghub-create-token host username package scopes)) + (user-error "Abort")))) + +(defun ghub--get-token-id (host username package) + (let ((ident (ghub--ident-github package))) + (cl-some (lambda (x) + (let-alist x + (and (equal .app.name ident) .id))) + (ghub-get "/authorizations" + '((per_page . 100)) + :unpaginate t + :username username :auth 'basic :host host)))) + +(defun ghub--get-token-plist (host username package) + (ghub-get (format "/authorizations/%s" + (ghub--get-token-id host username package)) + nil :username username :auth 'basic :host host)) + +(defun ghub--delete-token (host username package) + (ghub-delete (format "/authorizations/%s" + (ghub--get-token-id host username package)) + nil :username username :auth 'basic :host host)) + +(defun ghub--read-triplet () + (let ((host (read-string "Host: " (ghub--host)))) + (list host + (read-string "Username: " (ghub--username host)) + (intern (read-string "Package: " "ghub"))))) + +(defvar ghub--2fa-cache nil) + +(defun ghub--read-2fa-code () + (let ((code (read-number "Two-factor authentication code: " + (and ghub--2fa-cache + (< (float-time (time-subtract + (current-time) + (cdr ghub--2fa-cache))) + 25) + (car ghub--2fa-cache))))) + (setq ghub--2fa-cache (cons code (current-time))) + (number-to-string code))) + +(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))) + +(advice-add 'auth-source-netrc-parse-next-interesting :around + 'auth-source-netrc-parse-next-interesting@save-match-data) +(defun auth-source-netrc-parse-next-interesting@save-match-data (fn) + "Save match-data for the benefit of caller `auth-source-netrc-parse-one'. +Without wrapping this function in `save-match-data' the caller +won't see the secret from a line that is followed by a commented +line." + (save-match-data (funcall fn))) + +;;; _ +(provide 'ghub) +(require 'ghub-graphql) +;;; ghub.el ends here diff --git a/elpa/ghub-20180821.1430/ghub.info b/elpa/ghub-20180821.1430/ghub.info new file mode 100644 index 00000000..dec230de --- /dev/null +++ b/elpa/ghub-20180821.1430/ghub.info @@ -0,0 +1,1002 @@ +This is ghub.info, produced by makeinfo version 6.5 from ghub.texi. + + Copyright (C) 2017-2018 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). Minuscule 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 a library that provides basic support for using the Github API +from Emacs packages. It abstracts access to API resources using only a +handful of functions that are not resource-specific. + + Ghub handles the creation, storage and use of access tokens using a +setup wizard to make it easier for users to get started and to reduce +the support burden imposed on package maintainers. It also comes with a +comprehensive manual to address the cases when things don’t just work as +expected or in case you don’t want to use the wizard. + + 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 the Github API could be configured the +same way. + +This manual is for Ghub version 2.0.1 (v2.0.1-10-g85b5ae3+1). + + Copyright (C) 2017-2018 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:: +* Using Ghub in Personal Scripts:: +* Using Ghub in a Package:: +* API:: +* Gitlab Support:: + +— The Detailed Node Listing — + +Getting Started + +* Setting the Username:: +* Interactively Creating and Storing a Token:: +* Manually Creating and Storing a Token:: +* How Ghub uses Auth-Source:: + +API + +* Making Requests:: +* Authentication:: +* Configuration Variables:: + + + +File: ghub.info, Node: Introduction, Next: Getting Started, Prev: Top, Up: Top + +1 Introduction +************** + +Ghub is a library that provides basic support for using the Github API +from Emacs packages. It abstracts access to API resources using only a +handful of functions that are not resource-specific. + + Ghub handles the creation, storage and use of access tokens using a +setup wizard to make it easier for users to get started and to reduce +the support burden imposed on package maintainers. It also comes with a +comprehensive manual to address the cases when things don’t just work as +expected or in case you don’t want to use the wizard. + + 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 the Github API could be configured the +same way. + + Fancier interfaces can be implemented on top of Ghub, and one such +wrapper — named simply Ghub+ — has already been implemented. The +benefit of basing various opinionated interfaces on top of a single +library that provides only the core functionality is that choosing the +programming interface no longer dictates how access tokens are handled. +Users can then use multiple packages that access the Github API without +having to learn the various incompatible ways packages expect the +appropriate token to be made available to them. + + Ghub uses the built-in ‘auth-source’ library to store access tokens. +That library is very flexible and supports multiple backends, which +means that it is up to the user how secrets are stored. They can, among +other things, choose between storing secrets in plain text for ease of +use, or encrypted for better security. + + Previously (as in until this library is widely adopted) it was up to +package authors to decide if things should be easy or secure. (Note +that ‘auth-source’ defaults to "easy" — you have been warned.) + + Ghub expects package authors to use a dedicated access token instead +of sharing a single token between all packages that rely on it. That +means that users cannot configure Ghub once and later start using a new +package without any additional setup. But Ghub helps with that. + + When the user invokes some command that ultimately results in +‘ghub-request’ being called and the appropriate token is not available +yet, then the user is guided through the process of creating and storing +a new token, and at the end of that process the request is carried out +as if the token had been available to begin with. + + +File: ghub.info, Node: Getting Started, Next: Using Ghub in Personal Scripts, Prev: Introduction, Up: Top + +2 Getting Started +***************** + +Each package that uses Ghub uses its own token. Despite that, chances +are good that after successfully configuring one package you can just +start using another package pretty much instantly. + + If the necessary token is not available when a package makes an API +request, then a setup wizard pops up, and after answering a few +questions you are good to go. Even the request that caused the wizard +to be summoned should succeed and for most users this should be true +even when configuring the very first token. + + However, in some situations some manual configuration is necessary +*before* using the wizard, or the wizard cannot be used at all: + + • If you don’t want to use the wizard then you don’t have to and can + create tokens manually as described in *note Manually Creating and + Storing a Token::. + + • If you want to access Gitlab.com or another Gitlab instance, then + you have to create the token manually as describe in *note Manually + Creating and Storing a Token::. Also see *note Gitlab Support::. + + • If you want to access a Github Enterprise instance, then you have + to tell Ghub about that before the wizard makes its appearance by + setting the Git variable ‘github.host’. You also have to tell Ghub + your username for that instance using the variable + ‘github.HOST.user’ even if it is the same as on Github.com. + + • If the variable ‘github.user’ (or ‘github.HOST.user’ for an + Enterprise instance) is unset when the wizard is first summoned, + then you are asked to provide your username. That value is then + stored *globally* to avoid having to ask you that question once per + repository. If you have multiple accounts on Github.com (or an + Enterprise instance), then you have to explicitly tell Ghub about + that. This can be done by setting the repository-local values of + the appropriate variable *before* the wizard is invoked. + + • You might forget to do the above, which is why it is important to + carefully read the output of the wizard. If it turns out that you + forgot to set a variable, then you must abort, set the variable, + and repeat the request to trigger the wizard again. + + • The setup wizard should work even if you have enabled two-factor + authentication. However if your Github Enterprise instance + enforces Single Sign-On as an additional security measure, then you + are out of luck and have to create the token manually as described + in *note Manually Creating and Storing a Token::. + + The variables mentioned above — and others — are documented in *note +Configuration Variables:: and the setup wizard is documented in *note +Interactively Creating and Storing a Token::. + +* Menu: + +* Setting the Username:: +* Interactively Creating and Storing a Token:: +* Manually Creating and Storing a Token:: +* How Ghub uses Auth-Source:: + + +File: ghub.info, Node: Setting the Username, Next: Interactively Creating and Storing a Token, Up: Getting Started + +2.1 Setting the Username +======================== + +If you haven’t set the Git variable ‘github.user’ yet when making a +request, then you will be asked: + + Git variable `github.user' is unset. Set to: + + You are expected to provide your Github username here. The provided +value will be saved globally (using ‘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, *before* making a request: + + cd /path/to/repo + git config github.user USERNAME + + For Github Enterprise instances you have to specify where the API can +be accessed *before* you try to access it 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: + + # Do this once + git config --global github.example.com/api/v3.user EMPLOYEE + + # Do this for every corporate repository + cd /path/to/repo + git config github.host example.com/api/v3 + + If you do not set ‘github.example.com/api/v3.user’, then you will be +asked to provide the value when trying to make a request, but you do +have to manually set ‘github.host’, or Ghub assumes that you are trying +to access ‘api.github.com’. + + +File: ghub.info, Node: Interactively Creating and Storing a Token, Next: Manually Creating and Storing a Token, Prev: Setting the Username, Up: Getting Started + +2.2 Interactively Creating and Storing a Token +============================================== + +Ghub uses a different token for every package as well as for every +machine from which you access the Github API (and obviously also for +every Github instance and user). This allows packages to only request +the scopes that they actually need and also gives users the opportunity +to refuse access to certain scopes if they expect to not use the +features that need them. + + Usually you don’t have to worry about creating and storing a token +yourself and can just make a request. Note however that you don’t have +to use the setup wizard described below. Alternatively you can perform +the setup manually as described in the next section. + + If you make a request and the required token is not available yet, +then the setup wizard will first ask you something like this: + + Such a Github API token is not available: + + Host: api.github.com + User: USERNAME + Package: PACKAGE + + Scopes requested in `PACKAGE-github-token-scopes': + repo + Store on Github as: + "Emacs package PACKAGE @ LOCAL-MACHINE" + Store locally according to option `auth-sources': + ("~/.authinfo" "~/.authinfo.gpg" "~/.netrc") + + If in doubt, then abort and first view the section of the Ghub + documentation called "Manually Creating and Storing a Token". + + Create and store such a token? (yes or no) + + If you don’t have any doubts, then answer "yes". Lets address some +of the doubts that you might have: + + • ‘Host’ usually is "api.github.com" and that is usually what you + want. If you are trying to access a Github Enterprise instance, + then it should be something else and you have to set the value + manually before the setup wizard is summoned, as described in the + parent section. + + • ‘User’ should be your Github.com (or Github Enterprise instance) + username. If it is something else and it doesn’t look like a + simple typo, then you should read the parent section again. In + either case you have to abort. + + • ‘Package’ should be the name of the package you are using to access + the Github API. + + If it is ‘ghub’, then the package author disregarded that + convention and you should probably report a bug in the issue + tracker of that package. + + Or you yourself are using ‘ghub-request’ or one of its wrappers + directly, in which case this is expected and perfectly fine. In + that case you might however want to abort and change the value of + the variable ‘ghub-github-token-scopes’ before triggering the + wizard again. + + • Each ‘PACKAGE’ has to specify the tokens that it needs using a + variable named ‘PACKAGE-github-token-scopes’. The doc-string of + that variable should document why the various scopes are needed. + + The meaning of the various scopes are documented at + . + + • The value of ‘auth-sources’ is shown. The default value causes + secrets to be stored in plain text. Because this might be + unexpected, Ghub additionally displays a warning when appropriate. + + WARNING: The token will be stored unencrypted in "~/.authinfo". + If you don't want that, you have to abort and customize + the `auth-sources' option.\n\n" (car auth-sources)) + + Whether that is something that needs fixing, is up to you. If your + answer is yes, then you should abort and see *note How Ghub uses + Auth-Source:: for instructions on how to save the token more + securely. + + • When creating a token it is necessary to provide a token + description. Ghub uses descriptions that have the form "Emacs + package PACKAGE @ LOCAL-MACHINE". + + Github uses the token description to identify the token, not merely + as something useful to humans. Token descriptions therefore have + to be unique and in rare cases you get an additional prompt, asking + you something like: + + A token named "Emacs package PACKAGE @ LOCAL-MACHINE" + already exists on Github. Replace it? + + You might see this message when you have lost the old token and + want to replace it with a new one, in which case you should + obviously just proceed. + + Or two of your computers have the same hostname, which is bad + practice because it gains you nothing but leads to issues such as + this. Or you are dual-booting on this machine and use the same + hostname in all operating systems, which is a somewhat reasonable + thing to do, but never-the-less leads to issues like this. + + In either case you will have to use something other than the value + returned by ‘system-name’ to identify the current machine or + operating system. Or you can continue to identify different things + using the same identifier, in which case you have to manually + distribute the token. + + The former is recommended and also easier to do, using the variable + ‘ghub-override-system-name’. See *note Configuration Variables:: + for details. + + After the above prompt you are also asked for you username and +password. If you have enabled two-factor authentication, then you also +have to provide the authentication code at least twice. If you make +sure the code is still good for a while when asked for it first, then +you can just press ‘RET’ at the later prompt(s). + + +File: ghub.info, Node: Manually Creating and Storing a Token, Next: How Ghub uses Auth-Source, Prev: Interactively Creating and Storing a Token, Up: Getting Started + +2.3 Manually Creating and Storing a Token +========================================= + +If you cannot or don’t want to use the wizard then you have to (1) +figure out what scopes a package wants, (2) create such a token using +the web interface and (3) store the token where Ghub expects to find it. + + A package named ‘PACKAGE’ has to specify the scopes that it wants in +the variable named ‘PACKAGE-ghub-token-scopes’. The doc-string of such +variables should document what the various scopes are needed for. + + To create or edit a token go to . +For Gitlab.com use . + + Finally store the token in a place where Ghub looks for it, as +described in *note How Ghub uses Auth-Source::. + + If you store the token in a file like ‘~/.authinfo’, then note that +‘auth-source’’s parsing of that file is brittle. Make sure the file +ends with a newline character, that there are no empty or invalid lines, +and that all comments are prefixed with ‘#’. + + +File: ghub.info, Node: How Ghub uses Auth-Source, Prev: Manually Creating and Storing a Token, Up: Getting Started + +2.4 How Ghub uses Auth-Source +============================= + +Please see *note (auth)Top:: for all the gory details about Auth-Source. +Some Ghub-specific information and important notes follow. + + The variable ‘auth-sources’ controls how and where Auth-Source stores +new secrets and where it looks for known secrets. The default value is +‘("~/.authinfo" "~/.authinfo.gpg" "~/.netrc")’, which means that it +looks in all of these files in order to find secrets and that it stores +new secrets in ‘~/.authinfo’ because that is the first element of the +list. It doesn’t matter which files already do or don’t exist when +storing a new secret, the first file is always used. + + Secrets are stored in ‘~/.authinfo’ in plain text. If you don’t want +that (good choice), then you have to customize ‘auth-sources’, e.g. by +flipping the positions of the first two elements. + + Auth-Source also supports storing secrets in various key-chains. +Refer to its documentation for more information. + + Some 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 username is "ziggy",the package is named "stardust", +and you want to access *Github.com* an entry in one of the three +mentioned files would then look like this: + + machine api.github.com login ziggy^stardust password 012345abcdef... + + Assuming your username is "ziggy",the package is named "stardust", +and you want to access *Gitlab.com* an entry in one of the three +mentioned files would then look like this: + + machine gitlab.com/api/v4 login ziggy^stardust password 012345abcdef... + + +File: ghub.info, Node: Using Ghub in Personal Scripts, Next: Using Ghub in a Package, Prev: Getting Started, Up: Top + +3 Using Ghub in Personal Scripts +******************************** + +You can use ‘ghub-request’ and its wrapper functions in your personal +scripts, of course. Unlike when you use Ghub from a package that you +distribute for others to use, you don’t have to specify a package in +personal scripts. + + ;; This is perfectly acceptable in personal scripts ... + (ghub-get "/user") + + ;; ... and actually equal to + (ghub-get "/user" nil :auth 'ghub) + + ;; In packages you have to specify the package using AUTH. + (ghub-get "/user" nil :auth 'foobar) + + When you do not specify the ‘AUTH’ argument, then a request is made +on behalf of the ‘ghub’ package itself. Like for any package that uses +Ghub, ‘ghub’ has to declare what scopes it needs, using, in this case, +the variable ‘ghub-github-token-scopes’. + + The default value of that variable is ‘(repo)’ and you might want to +add additional scopes. You can later add additional scopes to an +existing token, using the web interface at +. + + If you do that, then you might want to also set the variable +accordingly, but note that Ghub only consults that when *creating* a new +token. If you want to know a token’s effective scopes use the command +‘ghub-token-scopes’, described in the next section. + + +File: ghub.info, Node: Using Ghub in a Package, Next: API, Prev: Using Ghub in Personal Scripts, Up: Top + +4 Using Ghub in a 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. + + The scopes used by ‘PACKAGE’ have to be defined using the variable +‘PACKAGE-github-token-scopes’, and 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) + + -- Variable: PACKAGE-github-token-scopes + + This variable defines the token scopes requested by the package + named ‘PACKAGE’. The doc-string should explain what the various + scopes are needed for to prevent users from giving ‘PACKAGE’ fewer + permissions than it absolutely needs and also to give them greater + confidence that ‘PACKAGE’ is only requesting the permissions that + it actually needs. + + The value of this variable does not necessarily correspond to the + scopes that the respective token actually gives access to. There + is nothing that prevents users from changing the value *after* + creating the token or from editing the token’s scopes later on. + + So it is pointless to check the value of this variable before + making a request. You also should not query the API to reliably + determine the supported tokens before making a query. Doing the + latter would mean that every request becomes two requests and that + the first request would have to be done using the user’s password + instead of a token. + + -- Command: ghub-token-scopes + + Because we cannot be certain that the user hasn’t messed up the + scopes, Ghub provides this command to make it easy to debug such + issues without having to rely on users being thoughtful enough to + correctly determine the used scopes manually. + + Just tell users to run ‘M-x ghub-token-scopes’ and to provide the + correct values for the ‘HOST’, ‘USERNAME’ and ‘PACKAGE’ when + prompted, and to then post the output. + + It is to be expected that users will occasionally mess that up so + this command outputs not only the scopes but also the user input so + that you can have greater confidence in the validity of the user’s + answer. + + Scopes for USERNAME^PACKAGE@HOST: (SCOPE...) + + +File: ghub.info, Node: API, Next: Gitlab Support, Prev: Using Ghub in a Package, Up: Top + +5 API +***** + +This section describes the Ghub API. In other words it describes the +public functions and variables provided by the Ghub library and not the +Github API that can be accessed by using those functions. The latter is +documented at . + +* Menu: + +* Making Requests:: +* Authentication:: +* Configuration Variables:: + + +File: ghub.info, Node: Making Requests, Next: Authentication, Up: API + +5.1 Making 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 header is 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 make 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 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. + + 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 an error + occurred, then it calls ‘ERRORBACK’, or if that is ‘nil’, then + ‘CALLBACK’. When no error occurred then it calls ‘CALLBACK’. + When making asynchronous requests, then no errors are + signaled, regardless of the value of ‘NOERROR’. + + Both callbacks are called with four arguments. + + • For ‘CALLBACK’, the combined value of the retrieved + pages. For ‘ERRORBACK’, the error that occured when + retrieving the last page. + + • The headers of the last page as an alist. + + • Status information provided by ‘url-retrieve’. Its + ‘:error’ property holds the same information as the first + argument to ‘ERRORBACK’. + + • 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 retrieve 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 callbacks 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 headers + + This function returns an alist of the link relations in ‘HEADERS’, + or if optional ‘HEADERS’ is nil, then those in + ‘ghub-response-headers’. + + -- Variable: ghub-override-system-name + + If non-nil, the value of this variable is used to override the + value returned by ‘system-name’ for the purpose of identifying the + local machine, which is necessary because Ghub uses separate tokens + for each machine. Also see *note Configuration Variables::. + + -- Variable: ghub-github-token-scopes + -- Variable: PACKAGE-github-token-scopes + + Such a variable defines the token scopes requested by the + respective package ‘PACKAGE’ given by the first word in the + variable name. ‘ghub’ itself is treated like any other package. + Also see *note Using Ghub in a Package::. + + -- 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 the timeout exceeds. 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. + + -- Function: ghub-graphql graphql &optional variables &key username + auth host callback + + 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. + + +File: ghub.info, Node: Authentication, Next: Configuration Variables, Prev: Making Requests, Up: API + +5.2 Authentication +================== + + -- Command: ghub-create-token + + This command creates a new token using the values it reads from the + user and then stores it according to the variable ‘auth-sources’. + It can also be called non-interactively, but you shouldn’t do that + yourself. + + This is useful if you want to fully setup things before attempting + to make the initial request, if you want to provide fewer than the + requested scopes or customize ‘auth-sources’ first, or if something + has gone wrong when using the wizard that is used when making a + request without doing this first. (Note that instead of using this + command you can also just repeat the initial request after making + the desired adjustments — that is easier.) + + This command reads, in order, the ‘HOST’ (Github instance), the + ‘USERNAME’, the ‘PACKAGE’, and the ‘SCOPES’ in the minibuffer, + providing reasonable default choices. ‘SCOPES’ defaults to the + scopes that ‘PACKAGE’ requests using the variable + ‘PACKAGE-github-token-scopes’. + + -- Command: ghub-token-scopes + + Users are free to give a token access to fewer scopes than what the + respective package requested. That can, of course, lead to issues, + and package maintainers have to be able to quickly determine if + such a (mis-)configuration is the root cause when users report + issues. + + This command reads the required values in the minibuffer and then + shows a message containing these values along with the scopes of + the respective token. It also returns the scopes (only) when + called non-interactively. Also see *note Using Ghub in a + Package::. + + +File: ghub.info, Node: Configuration Variables, Prev: Authentication, Up: API + +5.3 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.) + + -- Variable: ghub-override-system-name + + Ghub uses a different token for each quadruple ‘(USERNAME PACKAGE + HOST LOCAL-MACHINE)’. Theoretically it could reuse tokens to some + extent but that would be more difficult to implement, less + flexible, and less secure (though slightly more convenient). + + A token is identified on the respective Github instance (Github.com + or a Github Enterprise instance) using the pair ‘(PACKAGE . + LOCAL-MACHINE)’, or more precisely the string "Emacs package + PACKAGE @ LOCAL-MACHINE". ‘USERNAME’ and ‘HOST’ do not have to be + encoded because the token is stored for ‘USERNAME’ on ‘HOST’ and + cannot be used by another user and/or on another instance. + + There is one potential problem though; for any given ‘(PACKAGE . + LOCAL-MACHINE)’ there can only be one token identified by "Emacs + package PACKAGE @ LOCAL-MACHINE"; Github does not allow multiple + tokens with the same description because it uses the description as + the identifier (it could use some hash instead, but alas it does + not). + + If you have multiple machines and some of them have the same name, + then you should probably change that as this is not how things + ought to be. However if you dual-boot, then it might make sense to + give that machine the same name regardless of what operating system + you have booted into. + + You could use the same token on both operating systems, but setting + that up might be somewhat difficult because it is not possible to + download an existing token from Github. You could, of course, + locally copy the token, but that is inconvenient and would make it + harder to only revoke the token used on your infected Windows + installation without also revoking it for your totally safe *BSD + installation. + + Alternatively you can set this variable to a unique value, that + will then be used to identify the local machine instead of the + value returned by ‘system-name’. + + +File: ghub.info, Node: Gitlab Support, Prev: API, Up: Top + +6 Gitlab Support +**************** + +Support for Gitlab.com and other Gitlab instances is implemented in the +library ‘glab.el’. This library is build on top of ‘ghub.el’ and is +maintained in the same repository, but it is distributed as a separate +package. + + When accessing Gitlab.com or another Gitlab instance, use +‘glab-request’ instead of ‘ghub-request’, ‘glab-get’ instead of +‘ghub-get’, etc. Likewise use the Git variables in the ‘gitlab’ group +instead of those in the ‘github’ group, i.e. ‘gitlab.user’, +‘gitlab.HOST.user’ and ‘gitlab.host’. + + The Gitlab API cannot be used to create tokens, so Glab cannot +provide a setup wizard like Ghub does. As a consequence, if the user +makes a request and the necessary token cannot be found, then that +results in an error. + + You have to manually create and store the necessary tokens. Tokens +can be created at , +or the equivalent URL for another Gitlab instance. To store the token +locally, follow the instructions in *note Manually Creating and Storing +a Token:: and *note How Ghub uses Auth-Source::. + + Packages that use Glab can define ‘PACKAGE-gitlab-token-scopes’ for +documentation purposes. But unlike ‘PACKAGE-github-token-scopes’, which +is used by the setup wizard, this is optional. + + And a random hint: where you would use ‘user/repo’ when accessing +Github, you have to use ‘user%2Frepo’ when accessing Gitlab, e.g.: + + (glab-get "/projects/python-mode-devs%2Fpython-mode") + + + +Tag Table: +Node: Top763 +Node: Introduction2838 +Node: Getting Started5562 +Node: Setting the Username8620 +Node: Interactively Creating and Storing a Token10045 +Node: Manually Creating and Storing a Token15710 +Node: How Ghub uses Auth-Source16933 +Node: Using Ghub in Personal Scripts18866 +Node: Using Ghub in a Package20322 +Node: API22940 +Node: Making Requests23391 +Node: Authentication37080 +Node: Configuration Variables38925 +Node: Gitlab Support42645 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/elpa/ghub-20180821.1430/glab.el b/elpa/ghub-20180821.1430/glab.el new file mode 100644 index 00000000..fa35b351 --- /dev/null +++ b/elpa/ghub-20180821.1430/glab.el @@ -0,0 +1,153 @@ +;;; glab.el --- minuscule client library for the Gitlab API -*- lexical-binding: t -*- + +;; Copyright (C) 2016-2018 Jonas Bernoulli + +;; Author: Jonas Bernoulli +;; Homepage: https://github.com/magit/ghub +;; Keywords: tools + +;; This file is not part of GNU Emacs. + +;; 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, 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. + +;; For a copy of the GPL see https://www.gnu.org/licenses/gpl.txt. + +;;; 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-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" owner name) + nil :username username :auth auth :host host))))) + +;;; _ +(provide 'glab) +;;; glab.el ends here diff --git a/elpa/ghub-readme.txt b/elpa/ghub-readme.txt new file mode 100644 index 00000000..2e6c5237 --- /dev/null +++ b/elpa/ghub-readme.txt @@ -0,0 +1,16 @@ +Ghub is a library that provides basic support for using the Github API +from Emacs packages. It abstracts access to API resources using only +a handful of functions that are not resource-specific. + +Ghub handles the creation, storage and use of access tokens using a +setup wizard to make it easier for users to get started and to reduce +the support burden imposed on package maintainers. It also comes with +a comprehensive manual to address the cases when things don't just +work as expected or in case you don't want to use the wizard. + +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 the Github API could be +configured the same way. -- 2.47.3