diff options
author | William Carroll <wpcarro@gmail.com> | 2018-09-10T18·51-0400 |
---|---|---|
committer | William Carroll <wpcarro@gmail.com> | 2018-09-10T18·53-0400 |
commit | 17ee0e400bef47c371afcae76037f9ea6a44ad13 (patch) | |
tree | 0e5efee6f00e402890e91f3eceb4b29408a498b6 /configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159 | |
parent | 8b2fadf4776b7ddb4a67b4bc8ff6463770e56028 (diff) |
Support Vim, Tmux, Emacs with Stow
After moving off of Meta, Dotfiles has a greater responsibility to manage configs. Vim, Tmux, and Emacs are now within Stow's purview.
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159')
-rw-r--r-- | configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/dir | 18 | ||||
-rw-r--r-- | configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub-autoloads.el | 37 | ||||
-rw-r--r-- | configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub-pkg.el | 9 | ||||
-rw-r--r-- | configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub.el | 849 | ||||
-rw-r--r-- | configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub.elc | bin | 0 -> 43993 bytes | |||
-rw-r--r-- | configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub.info | 1002 |
6 files changed, 1915 insertions, 0 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/dir b/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/dir new file mode 100644 index 000000000000..6bd2b68c232b --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/dir @@ -0,0 +1,18 @@ +This is the file .../info/dir, which contains the +topmost node of the Info hierarchy, called (dir)Top. +The first time you invoke Info you start off looking at this node. + +File: dir, Node: Top This is the top of the INFO tree + + This (the Directory node) gives a menu of major topics. + Typing "q" exits, "?" lists all Info commands, "d" returns here, + "h" gives a primer for first-timers, + "mEmacs<Return>" visits the Emacs manual, etc. + + In Emacs, you can click mouse button 2 on a menu item or cross reference + to select it. + +* Menu: + +Emacs +* Ghub: (ghub). Minuscule client library for the Github API. diff --git a/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub-autoloads.el b/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub-autoloads.el new file mode 100644 index 000000000000..3aa6fde0e1ca --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub-autoloads.el @@ -0,0 +1,37 @@ +;;; 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" (23377 61608 155696 336000)) +;;; 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") (23377 61608 160174 126000)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; ghub-autoloads.el ends here diff --git a/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub-pkg.el b/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub-pkg.el new file mode 100644 index 000000000000..5394ab859458 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub-pkg.el @@ -0,0 +1,9 @@ +(define-package "ghub" "20180715.1159" "minuscule client library for the Github API" + '((emacs "24.4") + (let-alist "1.0.5")) + :keywords + '("tools") + :url "https://github.com/magit/ghub") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub.el b/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub.el new file mode 100644 index 000000000000..08ee86cabcb2 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub.el @@ -0,0 +1,849 @@ +;;; ghub.el --- minuscule client library for the Github API -*- lexical-binding: t -*- + +;; Copyright (C) 2016-2018 Jonas Bernoulli + +;; Author: Jonas Bernoulli <jonas@bernoul.li> +;; 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") + +(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) + (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-graphql (graphql &optional variables + &key username auth host + silent + callback errorback 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)) + +(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 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 payload + (unless (stringp payload) + (setq payload (json-encode-list payload))) + (setq payload (encode-coding-string payload 'utf-8))) + (when (or callback errorback) + (setq noerror t)) + (ghub--retrieve + 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) + :unpaginate unpaginate + :noerror noerror + :reader reader + :callback callback + :errorback errorback + :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)) + (silent (ghub--req-silent req))) + (if (or (ghub--req-callback req) + (ghub--req-errorback req)) + (url-retrieve url 'ghub--handle-response (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)) + (ghub--handle-response (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) + (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)))) + payload))) + +(defun ghub--handle-response-payload (req) + (funcall (or (ghub--req-reader req) + 'ghub--read-json-payload) + url-http-response-status)) + +(defun ghub--read-json-payload (_status) + (let ((raw (ghub--decode-payload))) + (and raw + (condition-case nil + (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 "<!DOCTYPE html>") + (if (re-search-forward + "<p>\\(?:<strong>\\)?\\([^<]+\\)" 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--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)) + +;;;; 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 for Emacs releases + ;; before 26.1. + (auth-source-forget (list :max 1 :host host :user user)) + (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 :max 1 spec)))) + (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) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; ghub.el ends here diff --git a/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub.elc b/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub.elc new file mode 100644 index 000000000000..9ffed416914f --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub.elc Binary files differdiff --git a/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub.info b/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub.info new file mode 100644 index 000000000000..311e5e331110 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/ghub-20180715.1159/ghub.info @@ -0,0 +1,1002 @@ +This is ghub.info, produced by makeinfo version 6.1 from ghub.texi. + + Copyright (C) 2017-2018 Jonas Bernoulli <jonas@bernoul.li> + + 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 <jonas@bernoul.li> + + 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 + <https://magit.vc/goto/f63aeb0a>. + + • 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 <https://github.com/settings/tokens>. +For Gitlab.com use <https://gitlab.com/profile/personal_access_tokens>. + + 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 +<https://github.com/settings/tokens>. + + 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 <https://developer.github.com/v3>. + +* 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 <https://gitlab.com/profile/personal_access_tokens>, +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: |