commit | author | age
|
5cb5f7
|
1 |
This is ghub.info, produced by makeinfo version 6.5 from ghub.texi. |
C |
2 |
|
|
3 |
Copyright (C) 2017-2018 Jonas Bernoulli <jonas@bernoul.li> |
|
4 |
|
|
5 |
You can redistribute this document and/or modify it under the terms |
|
6 |
of the GNU General Public License as published by the Free Software |
|
7 |
Foundation, either version 3 of the License, or (at your option) |
|
8 |
any later version. |
|
9 |
|
|
10 |
This document is distributed in the hope that it will be useful, |
|
11 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 |
General Public License for more details. |
|
14 |
|
|
15 |
INFO-DIR-SECTION Emacs |
|
16 |
START-INFO-DIR-ENTRY |
|
17 |
* Ghub: (ghub). Minuscule client library for the Github API. |
|
18 |
END-INFO-DIR-ENTRY |
|
19 |
|
|
20 |
|
|
21 |
File: ghub.info, Node: Top, Next: Introduction, Up: (dir) |
|
22 |
|
|
23 |
Ghub User and Developer Manual |
|
24 |
****************************** |
|
25 |
|
|
26 |
Ghub provides basic support for using the APIs of various Git forges |
|
27 |
from Emacs packages. Originally it only supported the Github REST API, |
|
28 |
but now it also supports the Github GraphQL API as well as the REST APIs |
|
29 |
of Gitlab, Gitea, Gogs and Bitbucket. |
|
30 |
|
|
31 |
Ghub abstracts access to API resources using only a handful of basic |
|
32 |
functions such as ‘ghub-get‘. These are convenience wrappers around |
|
33 |
‘ghub-request‘. Additional forge-specific wrappers like ‘glab-put‘, |
|
34 |
‘gtea-put‘, ‘gogs-post‘ and ‘buck-delete‘ are also available. Ghub does |
|
35 |
not provide any resource-specific functions, with the exception of |
|
36 |
‘FORGE-repository-id‘. |
|
37 |
|
|
38 |
When accessing Github, then Ghub handles the creation and storage of |
|
39 |
access tokens using a setup wizard to make it easier for users to get |
|
40 |
started. The tokens for other forges have to be created manually. |
|
41 |
|
|
42 |
Ghub is intentionally limited to only provide these two essential |
|
43 |
features — basic request functions and guided setup — to avoid being too |
|
44 |
opinionated, which would hinder wide adoption. It is assumed that wide |
|
45 |
adoption would make life easier for users and maintainers alike, because |
|
46 |
then all packages that talk to forge APIs could be configured the same |
|
47 |
way. |
|
48 |
|
|
49 |
This manual is for Ghub version 3.0.0 (v3.0.0-5-g86a17df+1). |
|
50 |
|
|
51 |
Copyright (C) 2017-2018 Jonas Bernoulli <jonas@bernoul.li> |
|
52 |
|
|
53 |
You can redistribute this document and/or modify it under the terms |
|
54 |
of the GNU General Public License as published by the Free Software |
|
55 |
Foundation, either version 3 of the License, or (at your option) |
|
56 |
any later version. |
|
57 |
|
|
58 |
This document is distributed in the hope that it will be useful, |
|
59 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
60 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
61 |
General Public License for more details. |
|
62 |
|
|
63 |
* Menu: |
|
64 |
|
|
65 |
* Introduction:: |
|
66 |
* Getting Started:: |
|
67 |
* Using Ghub in Personal Scripts:: |
|
68 |
* Using Ghub in a Package:: |
|
69 |
* API:: |
|
70 |
* GraphQL Support:: |
|
71 |
* Support for Other Forges:: |
|
72 |
|
|
73 |
— The Detailed Node Listing — |
|
74 |
|
|
75 |
Getting Started |
|
76 |
|
|
77 |
* Setting the Username:: |
|
78 |
* Interactively Creating and Storing a Token:: |
|
79 |
* Manually Creating and Storing a Token:: |
|
80 |
* How Ghub uses Auth-Source:: |
|
81 |
|
|
82 |
API |
|
83 |
|
|
84 |
* Making Requests:: |
|
85 |
* Authentication:: |
|
86 |
* Configuration Variables:: |
|
87 |
|
|
88 |
Support for Other Forges |
|
89 |
|
|
90 |
* Forge Functions and Variables:: |
|
91 |
* Forge Limitations and Notes:: |
|
92 |
|
|
93 |
|
|
94 |
|
|
95 |
File: ghub.info, Node: Introduction, Next: Getting Started, Prev: Top, Up: Top |
|
96 |
|
|
97 |
1 Introduction |
|
98 |
************** |
|
99 |
|
|
100 |
Ghub provides basic support for using the APIs of various Git forges |
|
101 |
from Emacs packages. Originally it only supported the Github REST API, |
|
102 |
but now it also supports the Github GraphQL API as well as the REST APIs |
|
103 |
of Gitlab, Gitea, Gogs and Bitbucket. |
|
104 |
|
|
105 |
Ghub abstracts access to API resources using only a handful of basic |
|
106 |
functions such as ‘ghub-get‘. These are convenience wrappers around |
|
107 |
‘ghub-request‘. Additional forge-specific wrappers like ‘glab-put‘, |
|
108 |
‘gtea-put‘, ‘gogs-post‘ and ‘buck-delete‘ are also available. Ghub does |
|
109 |
not provide any resource-specific functions, with the exception of |
|
110 |
‘FORGE-repository-id‘. |
|
111 |
|
|
112 |
When accessing Github, then Ghub handles the creation and storage of |
|
113 |
access tokens using a setup wizard to make it easier for users to get |
|
114 |
started. The tokens for other forges have to be created manually. |
|
115 |
|
|
116 |
Ghub is intentionally limited to only provide these two essential |
|
117 |
features — basic request functions and guided setup — to avoid being too |
|
118 |
opinionated, which would hinder wide adoption. It is assumed that wide |
|
119 |
adoption would make life easier for users and maintainers alike, because |
|
120 |
then all packages that talk to forge APIs could be configured the same |
|
121 |
way. |
|
122 |
|
|
123 |
Fancier interfaces can be implemented on top of Ghub, and one such |
|
124 |
wrapper — named simply Ghub+ — has already been implemented. The |
|
125 |
benefit of basing various opinionated interfaces on top of a single |
|
126 |
library that provides only the core functionality is that choosing the |
|
127 |
programming interface no longer dictates how access tokens are handled. |
|
128 |
Users can then use multiple packages that access the Github API without |
|
129 |
having to learn the various incompatible ways packages expect the |
|
130 |
appropriate token to be made available to them. |
|
131 |
|
|
132 |
Ghub uses the built-in ‘auth-source’ library to store access tokens. |
|
133 |
That library is very flexible and supports multiple backends, which |
|
134 |
means that it is up to the user how secrets are stored. They can, among |
|
135 |
other things, choose between storing secrets in plain text for ease of |
|
136 |
use, or encrypted for better security. |
|
137 |
|
|
138 |
Previously (as in until this library is widely adopted) it was up to |
|
139 |
package authors to decide if things should be easy or secure. (Note |
|
140 |
that ‘auth-source’ defaults to "easy" — you have been warned.) |
|
141 |
|
|
142 |
Ghub expects package authors to use a dedicated access token instead |
|
143 |
of sharing a single token between all packages that rely on it. That |
|
144 |
means that users cannot configure Ghub once and later start using a new |
|
145 |
package without any additional setup. But Ghub helps with that. |
|
146 |
|
|
147 |
When the user invokes some command that ultimately results in |
|
148 |
‘ghub-request’ being called and the appropriate token is not available |
|
149 |
yet, then the user is guided through the process of creating and storing |
|
150 |
a new token, and at the end of that process the request is carried out |
|
151 |
as if the token had been available to begin with. |
|
152 |
|
|
153 |
|
|
154 |
File: ghub.info, Node: Getting Started, Next: Using Ghub in Personal Scripts, Prev: Introduction, Up: Top |
|
155 |
|
|
156 |
2 Getting Started |
|
157 |
***************** |
|
158 |
|
|
159 |
Each package that uses Ghub uses its own token. Despite that, chances |
|
160 |
are good that after successfully configuring one package you can just |
|
161 |
start using another package pretty much instantly. |
|
162 |
|
|
163 |
If the necessary token to access a Github instance is not available |
|
164 |
when a package makes an API request, then a setup wizard pops up, and |
|
165 |
after answering a few questions you are good to go. Even the request |
|
166 |
that caused the wizard to be summoned should succeed and for most users |
|
167 |
this should be true even when configuring the very first token. |
|
168 |
|
|
169 |
However, in some situations some manual configuration is necessary |
|
170 |
*before* using the wizard, or the wizard cannot be used at all: |
|
171 |
|
|
172 |
• If you don’t want to use the wizard then you don’t have to and can |
|
173 |
create tokens manually as described in *note Manually Creating and |
|
174 |
Storing a Token::. |
|
175 |
|
|
176 |
• Unfortunately only Github supports the creation of tokens by using |
|
177 |
the API. If you want to access another forge, then you have to |
|
178 |
create the token manually as describe in *note Manually Creating |
|
179 |
and Storing a Token::. Also see *note Support for Other Forges::. |
|
180 |
|
|
181 |
• If you want to access a Github Enterprise instance, then you have |
|
182 |
to tell Ghub about that before the wizard makes its appearance by |
|
183 |
setting the Git variable ‘github.host’. You also have to tell Ghub |
|
184 |
your username for that instance using the variable |
|
185 |
‘github.HOST.user’ even if it is the same as on Github.com. |
|
186 |
|
|
187 |
These variables are documented in *note Configuration Variables::. |
|
188 |
Also see *note Setting the Username::. TL;DR: If your Github |
|
189 |
Enterprise instance is hosted at ‘git.example.com’ and your |
|
190 |
username on that instance is ‘jtribbiani’, then set ‘github.host’ |
|
191 |
to ‘git.example.com/api/v3’ in every repository cloned from that |
|
192 |
instance (i.e. _do not_ set it globally) and _globally_ set |
|
193 |
‘github.git.example.com/api/v3.user’ to ‘jtribbiani’. The latter |
|
194 |
is necessary even if your username on Github.com is the same. |
|
195 |
|
|
196 |
• If the variable ‘github.user’ (or ‘github.HOST.user’ for an |
|
197 |
Enterprise instance) is unset when the wizard is first summoned, |
|
198 |
then you are asked to provide your username. That value is then |
|
199 |
stored *globally* to avoid having to ask you that question once per |
|
200 |
repository. If you have multiple accounts on Github.com (or a |
|
201 |
Github Enterprise instance), then you have to explicitly tell Ghub |
|
202 |
about that. This can be done by setting the repository-local |
|
203 |
values of the appropriate variable *before* the wizard is invoked. |
|
204 |
|
|
205 |
• You might forget to do the above, which is why it is important to |
|
206 |
carefully read the output of the wizard. If it turns out that you |
|
207 |
forgot to set a variable, then you must abort, set the variable, |
|
208 |
and repeat the request to trigger the wizard again. |
|
209 |
|
|
210 |
• The setup wizard should work even if you have enabled two-factor |
|
211 |
authentication. However if your Github Enterprise instance |
|
212 |
enforces Single Sign-On as an additional security measure, then you |
|
213 |
are out of luck and have to create the token manually as described |
|
214 |
in *note Manually Creating and Storing a Token::. |
|
215 |
|
|
216 |
The variables mentioned above — and others — are documented in *note |
|
217 |
Configuration Variables:: and the setup wizard is documented in *note |
|
218 |
Interactively Creating and Storing a Token::. |
|
219 |
|
|
220 |
* Menu: |
|
221 |
|
|
222 |
* Setting the Username:: |
|
223 |
* Interactively Creating and Storing a Token:: |
|
224 |
* Manually Creating and Storing a Token:: |
|
225 |
* How Ghub uses Auth-Source:: |
|
226 |
|
|
227 |
|
|
228 |
File: ghub.info, Node: Setting the Username, Next: Interactively Creating and Storing a Token, Up: Getting Started |
|
229 |
|
|
230 |
2.1 Setting the Username |
|
231 |
======================== |
|
232 |
|
|
233 |
If you haven’t set the Git variable ‘github.user’ yet when making a |
|
234 |
request, then you will be asked: |
|
235 |
|
|
236 |
Git variable `github.user' is unset. Set to: |
|
237 |
|
|
238 |
You are expected to provide your Github username here. The provided |
|
239 |
value will be saved globally (using ‘git config --global github.user |
|
240 |
USERNAME’). |
|
241 |
|
|
242 |
If you need to identify as another user in a particular repository, |
|
243 |
then you have to set that variable locally, *before* making a request: |
|
244 |
|
|
245 |
cd /path/to/repo |
|
246 |
git config github.user USERNAME |
|
247 |
|
|
248 |
For Github Enterprise instances you have to specify where the API can |
|
249 |
be accessed *before* you try to access it and a different variable has |
|
250 |
to be used to set the username. For example if the API is available at |
|
251 |
‘https://example.com/api/v3’, then you should do this: |
|
252 |
|
|
253 |
# Do this once |
|
254 |
git config --global github.example.com/api/v3.user EMPLOYEE |
|
255 |
|
|
256 |
# Do this for every corporate repository |
|
257 |
cd /path/to/repo |
|
258 |
git config github.host example.com/api/v3 |
|
259 |
|
|
260 |
If you do not set ‘github.example.com/api/v3.user’, then you will be |
|
261 |
asked to provide the value when trying to make a request, but you do |
|
262 |
have to manually set ‘github.host’, or Ghub assumes that you are trying |
|
263 |
to access ‘api.github.com’. |
|
264 |
|
|
265 |
|
|
266 |
File: ghub.info, Node: Interactively Creating and Storing a Token, Next: Manually Creating and Storing a Token, Prev: Setting the Username, Up: Getting Started |
|
267 |
|
|
268 |
2.2 Interactively Creating and Storing a Token |
|
269 |
============================================== |
|
270 |
|
|
271 |
Ghub uses a different token for every package as well as for every |
|
272 |
machine from which you access the Github API (and obviously also for |
|
273 |
every Github instance and user). This allows packages to only request |
|
274 |
the scopes that they actually need and also gives users the opportunity |
|
275 |
to refuse access to certain scopes if they expect to not use the |
|
276 |
features that need them. |
|
277 |
|
|
278 |
Usually you don’t have to worry about creating and storing a token |
|
279 |
yourself and can just make a request. Note however that you don’t have |
|
280 |
to use the setup wizard described below. Alternatively you can perform |
|
281 |
the setup manually as described in the next section. |
|
282 |
|
|
283 |
If you make a request and the required token is not available yet, |
|
284 |
then the setup wizard will first ask you something like this: |
|
285 |
|
|
286 |
Such a Github API token is not available: |
|
287 |
|
|
288 |
Host: api.github.com |
|
289 |
User: USERNAME |
|
290 |
Package: PACKAGE |
|
291 |
|
|
292 |
Scopes requested in `PACKAGE-github-token-scopes': |
|
293 |
repo |
|
294 |
Store on Github as: |
|
295 |
"Emacs package PACKAGE @ LOCAL-MACHINE" |
|
296 |
Store locally according to option `auth-sources': |
|
297 |
("~/.authinfo" "~/.authinfo.gpg" "~/.netrc") |
|
298 |
|
|
299 |
If in doubt, then abort and first view the section of |
|
300 |
the Ghub documentation called "Interactively Creating |
|
301 |
and Storing a Token". |
|
302 |
|
|
303 |
Create and store such a token? (yes or no) |
|
304 |
|
|
305 |
If you don’t have any doubts, then answer "yes". Lets address some |
|
306 |
of the doubts that you might have: |
|
307 |
|
|
308 |
• ‘Host’ usually is "api.github.com" and that is usually what you |
|
309 |
want. If you are trying to access a Github Enterprise instance, |
|
310 |
then it should be something else and you have to set the value |
|
311 |
manually before the setup wizard is summoned, as described in the |
|
312 |
parent section. |
|
313 |
|
|
314 |
• ‘User’ should be your Github.com (or Github Enterprise instance) |
|
315 |
username. If it is something else and it doesn’t look like a |
|
316 |
simple typo, then you should read the parent section again. In |
|
317 |
either case you have to abort. |
|
318 |
|
|
319 |
• ‘Package’ should be the name of the package you are using to access |
|
320 |
the Github API. |
|
321 |
|
|
322 |
If it is ‘ghub’, then the package author disregarded that |
|
323 |
convention and you should probably report a bug in the issue |
|
324 |
tracker of that package. |
|
325 |
|
|
326 |
Or you yourself are using ‘ghub-request’ or one of its wrappers |
|
327 |
directly, in which case this is expected and perfectly fine. In |
|
328 |
that case you might however want to abort and change the value of |
|
329 |
the variable ‘ghub-github-token-scopes’ before triggering the |
|
330 |
wizard again. |
|
331 |
|
|
332 |
• Each ‘PACKAGE’ has to specify the tokens that it needs using a |
|
333 |
variable named ‘PACKAGE-github-token-scopes’. The doc-string of |
|
334 |
that variable should document why the various scopes are needed. |
|
335 |
|
|
336 |
The meaning of the various scopes are documented at |
|
337 |
<https://magit.vc/goto/f63aeb0a>. |
|
338 |
|
|
339 |
• The value of ‘auth-sources’ is shown. The default value causes |
|
340 |
secrets to be stored in plain text. Because this might be |
|
341 |
unexpected, Ghub additionally displays a warning when appropriate. |
|
342 |
|
|
343 |
WARNING: The token will be stored unencrypted in "~/.authinfo". |
|
344 |
If you don't want that, you have to abort and customize |
|
345 |
the `auth-sources' option. |
|
346 |
|
|
347 |
Whether that is something that needs fixing, is up to you. If your |
|
348 |
answer is yes, then you should abort and see *note How Ghub uses |
|
349 |
Auth-Source:: for instructions on how to save the token more |
|
350 |
securely. |
|
351 |
|
|
352 |
• When creating a token it is necessary to provide a token |
|
353 |
description. Ghub uses descriptions that have the form "Emacs |
|
354 |
package PACKAGE @ LOCAL-MACHINE". |
|
355 |
|
|
356 |
Github uses the token description to identify the token, not merely |
|
357 |
as something useful to humans. Token descriptions therefore have |
|
358 |
to be unique and in rare cases you get an additional prompt, asking |
|
359 |
you something like: |
|
360 |
|
|
361 |
A token named "Emacs package PACKAGE @ LOCAL-MACHINE" |
|
362 |
already exists on Github. Replace it? |
|
363 |
|
|
364 |
You might see this message when you have lost the old token and |
|
365 |
want to replace it with a new one, in which case you should |
|
366 |
obviously just proceed. |
|
367 |
|
|
368 |
Or two of your computers have the same hostname, which is bad |
|
369 |
practice because it gains you nothing but leads to issues such as |
|
370 |
this. Or you are dual-booting on this machine and use the same |
|
371 |
hostname in all operating systems, which is a somewhat reasonable |
|
372 |
thing to do, but never-the-less leads to issues like this. |
|
373 |
|
|
374 |
In either case you will have to use something other than the value |
|
375 |
returned by ‘system-name’ to identify the current machine or |
|
376 |
operating system. Or you can continue to identify different things |
|
377 |
using the same identifier, in which case you have to manually |
|
378 |
distribute the token. |
|
379 |
|
|
380 |
The former is recommended and also easier to do, using the variable |
|
381 |
‘ghub-override-system-name’. See *note Configuration Variables:: |
|
382 |
for details. |
|
383 |
|
|
384 |
After the above prompt you are also asked for your username and |
|
385 |
password. If you have enabled two-factor authentication, then you also |
|
386 |
have to provide the authentication code at least twice. If you make |
|
387 |
sure the code is still good for a while when asked for it first, then |
|
388 |
you can just press ‘RET’ at the later prompt(s). |
|
389 |
|
|
390 |
|
|
391 |
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 |
|
392 |
|
|
393 |
2.3 Manually Creating and Storing a Token |
|
394 |
========================================= |
|
395 |
|
|
396 |
If you cannot or don’t want to use the wizard then you have to (1) |
|
397 |
figure out what scopes a package wants, (2) create such a token using |
|
398 |
the web interface and (3) store the token where Ghub expects to find it. |
|
399 |
|
|
400 |
A package named ‘PACKAGE’ has to specify the scopes that it wants in |
|
401 |
the variable named ‘PACKAGE-ghub-token-scopes’. The doc-string of such |
|
402 |
variables should document what the various scopes are needed for. |
|
403 |
|
|
404 |
To create or edit a token go to <https://github.com/settings/tokens>. |
|
405 |
For Gitlab.com use <https://gitlab.com/profile/personal_access_tokens>. |
|
406 |
|
|
407 |
Finally store the token in a place where Ghub looks for it, as |
|
408 |
described in *note How Ghub uses Auth-Source::. |
|
409 |
|
|
410 |
If you store the token in a file like ‘~/.authinfo’, then note that |
|
411 |
‘auth-source’’s parsing of that file is brittle. Make sure the file |
|
412 |
ends with a newline character, that there are no empty or invalid lines, |
|
413 |
and that all comments are prefixed with ‘#’. |
|
414 |
|
|
415 |
|
|
416 |
File: ghub.info, Node: How Ghub uses Auth-Source, Prev: Manually Creating and Storing a Token, Up: Getting Started |
|
417 |
|
|
418 |
2.4 How Ghub uses Auth-Source |
|
419 |
============================= |
|
420 |
|
|
421 |
Please see *note (auth)Top:: for all the gory details about Auth-Source. |
|
422 |
Some Ghub-specific information and important notes follow. |
|
423 |
|
|
424 |
The variable ‘auth-sources’ controls how and where Auth-Source stores |
|
425 |
new secrets and where it looks for known secrets. The default value is |
|
426 |
‘("~/.authinfo" "~/.authinfo.gpg" "~/.netrc")’, which means that it |
|
427 |
looks in all of these files in order to find secrets and that it stores |
|
428 |
new secrets in ‘~/.authinfo’ because that is the first element of the |
|
429 |
list. It doesn’t matter which files already do or don’t exist when |
|
430 |
storing a new secret, the first file is always used. |
|
431 |
|
|
432 |
Secrets are stored in ‘~/.authinfo’ in plain text. If you don’t want |
|
433 |
that (good choice), then you have to customize ‘auth-sources’, e.g. by |
|
434 |
flipping the positions of the first two elements. |
|
435 |
|
|
436 |
Auth-Source also supports storing secrets in various key-chains. |
|
437 |
Refer to its documentation for more information. |
|
438 |
|
|
439 |
Some Auth-Source backends only support storing three values per |
|
440 |
entry, the "machine", the "login" and the "password". Because Ghub uses |
|
441 |
separate tokens for each package, it has to squeeze four values into |
|
442 |
those three slots, and it does that by using "USERNAME^PACKAGE" as the |
|
443 |
"login". |
|
444 |
|
|
445 |
Assuming your username is "ziggy",the package is named "stardust", |
|
446 |
and you want to access *Github.com* an entry in one of the three |
|
447 |
mentioned files would then look like this: |
|
448 |
|
|
449 |
machine api.github.com login ziggy^stardust password 012345abcdef... |
|
450 |
|
|
451 |
Assuming your username is "ziggy",the package is named "stardust", |
|
452 |
and you want to access *Gitlab.com* an entry in one of the three |
|
453 |
mentioned files would then look like this: |
|
454 |
|
|
455 |
machine gitlab.com/api/v4 login ziggy^stardust password 012345abcdef... |
|
456 |
|
|
457 |
|
|
458 |
File: ghub.info, Node: Using Ghub in Personal Scripts, Next: Using Ghub in a Package, Prev: Getting Started, Up: Top |
|
459 |
|
|
460 |
3 Using Ghub in Personal Scripts |
|
461 |
******************************** |
|
462 |
|
|
463 |
You can use ‘ghub-request’ and its wrapper functions in your personal |
|
464 |
scripts, of course. Unlike when you use Ghub from a package that you |
|
465 |
distribute for others to use, you don’t have to specify a package in |
|
466 |
personal scripts. |
|
467 |
|
|
468 |
;; This is perfectly acceptable in personal scripts ... |
|
469 |
(ghub-get "/user") |
|
470 |
|
|
471 |
;; ... and actually equal to |
|
472 |
(ghub-get "/user" nil :auth 'ghub) |
|
473 |
|
|
474 |
;; In packages you have to specify the package using AUTH. |
|
475 |
(ghub-get "/user" nil :auth 'foobar) |
|
476 |
|
|
477 |
When you do not specify the ‘AUTH’ argument, then a request is made |
|
478 |
on behalf of the ‘ghub’ package itself. Like for any package that uses |
|
479 |
Ghub, ‘ghub’ has to declare what scopes it needs, using, in this case, |
|
480 |
the variable ‘ghub-github-token-scopes’. |
|
481 |
|
|
482 |
The default value of that variable is ‘(repo)’ and you might want to |
|
483 |
add additional scopes. You can later add additional scopes to an |
|
484 |
existing token, using the web interface at |
|
485 |
<https://github.com/settings/tokens>. |
|
486 |
|
|
487 |
If you do that, then you might want to also set the variable |
|
488 |
accordingly, but note that Ghub only consults that when *creating* a new |
|
489 |
token. If you want to know a token’s effective scopes use the command |
|
490 |
‘ghub-token-scopes’, described in the next section. |
|
491 |
|
|
492 |
|
|
493 |
File: ghub.info, Node: Using Ghub in a Package, Next: API, Prev: Using Ghub in Personal Scripts, Up: Top |
|
494 |
|
|
495 |
4 Using Ghub in a Package |
|
496 |
************************* |
|
497 |
|
|
498 |
Every package should use its own token. This allows you as the author |
|
499 |
of some package to only request access to API scopes that are actually |
|
500 |
needed, which in turn might make it easier for users to trust your |
|
501 |
package not to do unwanted things. |
|
502 |
|
|
503 |
The scopes used by ‘PACKAGE’ have to be defined using the variable |
|
504 |
‘PACKAGE-github-token-scopes’, and you have to tell ‘ghub-request’ on |
|
505 |
behalf of which package a request is being made by passing the symbol |
|
506 |
‘PACKAGE’ as the value of its ‘AUTH’ argument. |
|
507 |
|
|
508 |
(ghub-request "GET" "/user" nil :auth 'PACKAGE) |
|
509 |
|
|
510 |
-- Variable: PACKAGE-github-token-scopes |
|
511 |
|
|
512 |
This variable defines the token scopes requested by the package |
|
513 |
named ‘PACKAGE’. The doc-string should explain what the various |
|
514 |
scopes are needed for to prevent users from giving ‘PACKAGE’ fewer |
|
515 |
permissions than it absolutely needs and also to give them greater |
|
516 |
confidence that ‘PACKAGE’ is only requesting the permissions that |
|
517 |
it actually needs. |
|
518 |
|
|
519 |
The value of this variable does not necessarily correspond to the |
|
520 |
scopes that the respective token actually gives access to. There |
|
521 |
is nothing that prevents users from changing the value *after* |
|
522 |
creating the token or from editing the token’s scopes later on. |
|
523 |
|
|
524 |
So it is pointless to check the value of this variable before |
|
525 |
making a request. You also should not query the API to reliably |
|
526 |
determine the supported tokens before making a query. Doing the |
|
527 |
latter would mean that every request becomes two requests and that |
|
528 |
the first request would have to be done using the user’s password |
|
529 |
instead of a token. |
|
530 |
|
|
531 |
-- Command: ghub-token-scopes |
|
532 |
|
|
533 |
Because we cannot be certain that the user hasn’t messed up the |
|
534 |
scopes, Ghub provides this command to make it easy to debug such |
|
535 |
issues without having to rely on users being thoughtful enough to |
|
536 |
correctly determine the used scopes manually. |
|
537 |
|
|
538 |
Just tell users to run ‘M-x ghub-token-scopes’ and to provide the |
|
539 |
correct values for the ‘HOST’, ‘USERNAME’ and ‘PACKAGE’ when |
|
540 |
prompted, and to then post the output. |
|
541 |
|
|
542 |
It is to be expected that users will occasionally mess that up so |
|
543 |
this command outputs not only the scopes but also the user input so |
|
544 |
that you can have greater confidence in the validity of the user’s |
|
545 |
answer. |
|
546 |
|
|
547 |
Scopes for USERNAME^PACKAGE@HOST: (SCOPE...) |
|
548 |
|
|
549 |
|
|
550 |
File: ghub.info, Node: API, Next: GraphQL Support, Prev: Using Ghub in a Package, Up: Top |
|
551 |
|
|
552 |
5 API |
|
553 |
***** |
|
554 |
|
|
555 |
This section describes the Ghub API. In other words it describes the |
|
556 |
public functions and variables provided by the Ghub package and not the |
|
557 |
APIs of the supported forges, which can be accessed by using those |
|
558 |
functions. The forge APIs are documented at: |
|
559 |
|
|
560 |
• Github: <https://developer.github.com/v3> |
|
561 |
|
|
562 |
• Gitlab: <https://docs.gitlab.com/ee/api/README.html> |
|
563 |
|
|
564 |
• Gitea: <https://docs.gitea.io/en-us/api-usage> and |
|
565 |
<https://try.gitea.io/api/swagger> |
|
566 |
|
|
567 |
• Gogs: <https://github.com/gogs/go-gogs-client/wiki> |
|
568 |
|
|
569 |
• Bitbucket: |
|
570 |
<https://developer.atlassian.com/bitbucket/api/2/reference> |
|
571 |
|
|
572 |
* Menu: |
|
573 |
|
|
574 |
* Making Requests:: |
|
575 |
* Authentication:: |
|
576 |
* Configuration Variables:: |
|
577 |
|
|
578 |
|
|
579 |
File: ghub.info, Node: Making Requests, Next: Authentication, Up: API |
|
580 |
|
|
581 |
5.1 Making Requests |
|
582 |
=================== |
|
583 |
|
|
584 |
-- Function: ghub-request method resource &optional params &key query |
|
585 |
payload headers unpaginate noerror reader username auth host |
|
586 |
callback errorback url value error extra method* |
|
587 |
|
|
588 |
This function makes a request for ‘RESOURCE’ using ‘METHOD’. |
|
589 |
‘PARAMS’, ‘QUERY’, ‘PAYLOAD’ and/or ‘HEADERS’ are alists holding |
|
590 |
additional request data. The response body is returned and the |
|
591 |
response header is stored in the variable ‘ghub-response-headers’. |
|
592 |
|
|
593 |
• ‘METHOD’ is the HTTP method, given as a string. |
|
594 |
|
|
595 |
• ‘RESOURCE’ is the resource to access, given as a string |
|
596 |
beginning with a slash. |
|
597 |
|
|
598 |
• ‘PARAMS’, ‘QUERY’, ‘PAYLOAD’ and ‘HEADERS’ are alists and are |
|
599 |
used to specify request data. All these arguments are alists |
|
600 |
that resemble the JSON expected and returned by the Github |
|
601 |
API. The keys are symbols and the values stored in the ‘cdr’ |
|
602 |
(not the ‘cadr’) can be strings, integers, or lists of strings |
|
603 |
and integers. |
|
604 |
|
|
605 |
The Github API documentation is vague on how data has to be |
|
606 |
transmitted and for a particular resource usually just talks |
|
607 |
about "parameters". Generally speaking when the ‘METHOD’ is |
|
608 |
"HEAD" or "GET", then they have to be transmitted as a query, |
|
609 |
otherwise as a payload. |
|
610 |
|
|
611 |
• Use ‘PARAMS’ to automatically transmit like ‘QUERY’ or |
|
612 |
‘PAYLOAD’ would depending on ‘METHOD’. |
|
613 |
|
|
614 |
• Use ‘QUERY’ to explicitly transmit data as a query. |
|
615 |
|
|
616 |
• Use ‘PAYLOAD’ to explicitly transmit data as a payload. |
|
617 |
Instead of an alist, ‘PAYLOAD’ may also be a string, in |
|
618 |
which case it gets encoded as UTF-8 but is otherwise |
|
619 |
transmitted as-is. |
|
620 |
|
|
621 |
• Use ‘HEADERS’ for those rare resources that require that |
|
622 |
the data is transmitted as headers instead of as a query |
|
623 |
or payload. When that is the case, then the Github API |
|
624 |
documentation usually mentions it explicitly. |
|
625 |
|
|
626 |
• If ‘SILENT’ is non-nil, then progress reports and the like are |
|
627 |
not messaged. |
|
628 |
|
|
629 |
• If ‘UNPAGINATE’ is t, then this function make as many requests |
|
630 |
as necessary to get all values. If ‘UNPAGINATE’ is a natural |
|
631 |
number, then it gets at most that many pages. For any other |
|
632 |
non-nil value it raises an error. |
|
633 |
|
|
634 |
• If ‘NOERROR’ is non-nil, then no error is raised if the |
|
635 |
request fails and ‘nil’ is returned instead. If ‘NOERROR’ is |
|
636 |
‘return’, then the error payload is returned instead of ‘nil’. |
|
637 |
|
|
638 |
• If ‘READER’ is non-nil, then it is used to read and return |
|
639 |
from the response buffer. The default is |
|
640 |
‘ghub--read-json-payload’. For the very few resources that do |
|
641 |
not return JSON, you might want to use ‘ghub--decode-payload’. |
|
642 |
|
|
643 |
• If ‘USERNAME’ is non-nil, then the request is made on behalf |
|
644 |
of that user. It is better to specify the user using the Git |
|
645 |
variable ‘github.user’ for "api.github.com", or |
|
646 |
‘github.HOST.user’ if connecting to a Github Enterprise |
|
647 |
instance. |
|
648 |
|
|
649 |
• Each package that uses Ghub should use its own token. If |
|
650 |
‘AUTH’ is ‘nil’ or unspecified, then the generic ‘ghub’ token |
|
651 |
is used instead. This is only acceptable for personal |
|
652 |
utilities. A packages that is distributed to other users |
|
653 |
should always use this argument to identify itself, using a |
|
654 |
symbol matching its name. |
|
655 |
|
|
656 |
Package authors who find this inconvenient should write a |
|
657 |
wrapper around this function and possibly for the |
|
658 |
method-specific functions as well. |
|
659 |
|
|
660 |
Beside ‘nil’, some other symbols have a special meaning too. |
|
661 |
‘none’ means to make an unauthorized request. ‘basic’ means |
|
662 |
to make a password based request. If the value is a string, |
|
663 |
then it is assumed to be a valid token. ‘basic’ and an |
|
664 |
explicit token string are only intended for internal and |
|
665 |
debugging uses. |
|
666 |
|
|
667 |
If ‘AUTH’ is a package symbol, then the scopes are specified |
|
668 |
using the variable ‘AUTH-github-token-scopes’. It is an error |
|
669 |
if that is not specified. See ‘ghub-github-token-scopes’ for |
|
670 |
an example. |
|
671 |
|
|
672 |
• If ‘HOST’ is non-nil, then connect to that Github instance. |
|
673 |
This defaults to "api.github.com". When a repository is |
|
674 |
connected to a Github Enterprise instance, then it is better |
|
675 |
to specify that using the Git variable ‘github.host’ instead |
|
676 |
of using this argument. |
|
677 |
|
|
678 |
• If ‘FORGE’ is ‘gitlab’, then connect to Gitlab.com or, |
|
679 |
depending on ‘HOST’, to another Gitlab instance. This is only |
|
680 |
intended for internal use. Instead of using this argument you |
|
681 |
should use function ‘glab-request’ and other ‘glab-*’ |
|
682 |
functions. |
|
683 |
|
|
684 |
• If ‘CALLBACK’ and/or ‘ERRORBACK’ is non-nil, then this |
|
685 |
function makes one or more asynchronous requests and calls |
|
686 |
‘CALLBACK’ or ‘ERRORBACK’ when finished. If an error |
|
687 |
occurred, then it calls ‘ERRORBACK’, or if that is ‘nil’, then |
|
688 |
‘CALLBACK’. When no error occurred then it calls ‘CALLBACK’. |
|
689 |
When making asynchronous requests, then no errors are |
|
690 |
signaled, regardless of the value of ‘NOERROR’. |
|
691 |
|
|
692 |
Both callbacks are called with four arguments. |
|
693 |
|
|
694 |
• For ‘CALLBACK’, the combined value of the retrieved |
|
695 |
pages. For ‘ERRORBACK’, the error that occured when |
|
696 |
retrieving the last page. |
|
697 |
|
|
698 |
• The headers of the last page as an alist. |
|
699 |
|
|
700 |
• Status information provided by ‘url-retrieve’. Its |
|
701 |
‘:error’ property holds the same information as the first |
|
702 |
argument to ‘ERRORBACK’. |
|
703 |
|
|
704 |
• A ‘ghub--req’ struct, which can be passed to |
|
705 |
‘ghub-continue’ (which see) to retrieve the next page, if |
|
706 |
any. |
|
707 |
|
|
708 |
-- Function: ghub-continue args |
|
709 |
|
|
710 |
If there is a next page, then this function retrieves that. |
|
711 |
|
|
712 |
This function is only intended to be called from callbacks. If |
|
713 |
there is a next page, then that is retrieve and the buffer that the |
|
714 |
result will be loaded into is returned, or t if the process has |
|
715 |
already completed. If there is no next page, then return nil. |
|
716 |
|
|
717 |
Callbacks are called with four arguments (see ‘ghub-request’). The |
|
718 |
forth argument is a ‘ghub--req’ struct, intended to be passed to |
|
719 |
this function. A callback may use the struct’s ‘extra’ slot to |
|
720 |
pass additional information to the callback that will be called |
|
721 |
after the next request. Use the function ‘ghub-req-extra’ to get |
|
722 |
and set the value of that slot. |
|
723 |
|
|
724 |
As an example, using ‘ghub-continue’ in a callback like so: |
|
725 |
|
|
726 |
(ghub-get "/users/tarsius/repos" nil |
|
727 |
:callback (lambda (value _headers _status req) |
|
728 |
(unless (ghub-continue req) |
|
729 |
(setq my-value value)))) |
|
730 |
|
|
731 |
is equivalent to: |
|
732 |
|
|
733 |
(ghub-get "/users/tarsius/repos" nil |
|
734 |
:unpaginate t |
|
735 |
:callback (lambda (value _headers _status _req) |
|
736 |
(setq my-value value))) |
|
737 |
|
|
738 |
To demonstrate how to pass information from one callback to the |
|
739 |
next, here we record when we start fetching each page: |
|
740 |
|
|
741 |
(ghub-get "/users/tarsius/repos" nil |
|
742 |
:extra (list (current-time)) |
|
743 |
:callback (lambda (value _headers _status req) |
|
744 |
(push (current-time) (ghub-req-extra req)) |
|
745 |
(unless (ghub-continue req) |
|
746 |
(setq my-times (ghub-req-extra req)) |
|
747 |
(setq my-value value)))) |
|
748 |
|
|
749 |
-- Variable: ghub-response-headers |
|
750 |
|
|
751 |
A select few Github API resources respond by transmitting data in |
|
752 |
the response header instead of in the response body. Because there |
|
753 |
are so few of these inconsistencies, ‘ghub-request’ always returns |
|
754 |
the response body. |
|
755 |
|
|
756 |
To access the response headers use this variable after |
|
757 |
‘ghub-request’ has returned. |
|
758 |
|
|
759 |
-- Function: ghub-response-link-relations req headers payload |
|
760 |
|
|
761 |
This function returns an alist of the link relations in ‘HEADERS’, |
|
762 |
or if optional ‘HEADERS’ is nil, then those in |
|
763 |
‘ghub-response-headers’. |
|
764 |
|
|
765 |
When accessing a Bitbucket instance then the link relations are in |
|
766 |
‘PAYLOAD’ instead of ‘HEADERS’, making their API merely RESTish and |
|
767 |
forcing this function to append those relations to the value of |
|
768 |
‘ghub-response-headers’, for later use when this function is called |
|
769 |
with ‘nil’ for ‘PAYLOAD’. |
|
770 |
|
|
771 |
-- Variable: ghub-override-system-name |
|
772 |
|
|
773 |
If non-nil, the value of this variable is used to override the |
|
774 |
value returned by ‘system-name’ for the purpose of identifying the |
|
775 |
local machine, which is necessary because Ghub uses separate tokens |
|
776 |
for each machine. Also see *note Configuration Variables::. |
|
777 |
|
|
778 |
-- Variable: ghub-github-token-scopes |
|
779 |
-- Variable: PACKAGE-github-token-scopes |
|
780 |
|
|
781 |
Such a variable defines the token scopes requested by the |
|
782 |
respective package ‘PACKAGE’ given by the first word in the |
|
783 |
variable name. ‘ghub’ itself is treated like any other package. |
|
784 |
Also see *note Using Ghub in a Package::. |
|
785 |
|
|
786 |
-- Function: ghub-head resource &optional params &key query payload |
|
787 |
headers unpaginate noerror reader username auth host callback |
|
788 |
errorback |
|
789 |
-- Function: ghub-get resource &optional params &key query payload |
|
790 |
headers unpaginate noerror reader username auth host callback |
|
791 |
errorback |
|
792 |
|
|
793 |
These functions are simple wrappers around ‘ghub-request’. Their |
|
794 |
signature is identical to that of the latter, except that they do |
|
795 |
not have an argument named ‘METHOD’. The HTTP method is instead |
|
796 |
given by the second word in the function name. |
|
797 |
|
|
798 |
As described in the documentation for ‘ghub-request’, it depends on |
|
799 |
the used method whether the value of the ‘PARAMS’ argument is used |
|
800 |
as the query or the payload. For the "HEAD" and "GET" methods it |
|
801 |
is used as the query. |
|
802 |
|
|
803 |
-- Function: ghub-put resource &optional params &key query payload |
|
804 |
headers unpaginate noerror reader username auth host callback |
|
805 |
errorback |
|
806 |
-- Function: ghub-post resource &optional params &key query payload |
|
807 |
headers unpaginate noerror reader username auth host callback |
|
808 |
errorback |
|
809 |
-- Function: ghub-patch resource &optional params &key query payload |
|
810 |
headers unpaginate noerror reader username auth host callback |
|
811 |
errorback |
|
812 |
-- Function: ghub-delete resource &optional params &key query payload |
|
813 |
headers unpaginate noerror reader username auth host callback |
|
814 |
errorback |
|
815 |
|
|
816 |
These functions are simple wrappers around ‘ghub-request’. Their |
|
817 |
signature is identical to that of the latter, except that they do |
|
818 |
not have an argument named ‘METHOD’. The HTTP method is instead |
|
819 |
given by the second word in the function name. |
|
820 |
|
|
821 |
As described in the documentation for ‘ghub-request’, it depends on |
|
822 |
the used method whether the value of the ‘PARAMS’ argument is used |
|
823 |
as the query or the payload. For the "PUT", "POST", "PATCH" and |
|
824 |
"DELETE" methods it is used as the payload. |
|
825 |
|
|
826 |
-- Function: ghub-wait resource &optional duration &key username auth |
|
827 |
host |
|
828 |
|
|
829 |
Some API requests result in an immediate successful response even |
|
830 |
when the requested action has not actually been carried out yet. |
|
831 |
An example is the request for the creation of a new repository, |
|
832 |
which doesn’t cause the repository to immediately become available. |
|
833 |
The Github API documentation usually mentions this when describing |
|
834 |
an affected resource. |
|
835 |
|
|
836 |
If you want to do something with some resource right after making a |
|
837 |
request for its creation, then you might have to wait for it to |
|
838 |
actually be created. This function can be used to do so. It |
|
839 |
repeatedly tries to access the resource until it becomes available |
|
840 |
or until the timeout exceeds. In the latter case it signals |
|
841 |
‘ghub-error’. |
|
842 |
|
|
843 |
‘RESOURCE’ specifies the resource that this function waits for. |
|
844 |
|
|
845 |
‘DURATION’ specifies the maximum number of seconds to wait for, |
|
846 |
defaulting to 64 seconds. Emacs will block during that time, but |
|
847 |
the user can abort using ‘C-g’. |
|
848 |
|
|
849 |
The first attempt is made immediately and will often succeed. If |
|
850 |
not, then another attempt is made after two seconds, and each |
|
851 |
subsequent attempt is made after waiting as long as we already |
|
852 |
waited between all preceding attempts combined. |
|
853 |
|
|
854 |
See ‘ghub-request’’s documentation above for information about the |
|
855 |
other arguments. |
|
856 |
|
|
857 |
-- Function: ghub-graphql graphql &optional variables &key username |
|
858 |
auth host callback |
|
859 |
|
|
860 |
This function makes a GraphQL request using ‘GRAPHQL’ and |
|
861 |
‘VARIABLES’ as inputs. ‘GRAPHQL’ is a GraphQL string. ‘VARIABLES’ |
|
862 |
is a JSON-like alist. The other arguments behave as for |
|
863 |
‘ghub-request’ (which see). |
|
864 |
|
|
865 |
The response is returned as a JSON-like alist. Even if the |
|
866 |
response contains ‘errors’, this function does not raise an error. |
|
867 |
Cursor-handling is likewise left to the caller. |
|
868 |
|
|
869 |
|
|
870 |
File: ghub.info, Node: Authentication, Next: Configuration Variables, Prev: Making Requests, Up: API |
|
871 |
|
|
872 |
5.2 Authentication |
|
873 |
================== |
|
874 |
|
|
875 |
-- Command: ghub-create-token |
|
876 |
|
|
877 |
This command creates a new token using the values it reads from the |
|
878 |
user and then stores it according to the variable ‘auth-sources’. |
|
879 |
It can also be called non-interactively, but you shouldn’t do that |
|
880 |
yourself. |
|
881 |
|
|
882 |
This is useful if you want to fully setup things before attempting |
|
883 |
to make the initial request, if you want to provide fewer than the |
|
884 |
requested scopes or customize ‘auth-sources’ first, or if something |
|
885 |
has gone wrong when using the wizard that is used when making a |
|
886 |
request without doing this first. (Note that instead of using this |
|
887 |
command you can also just repeat the initial request after making |
|
888 |
the desired adjustments — that is easier.) |
|
889 |
|
|
890 |
This command reads, in order, the ‘HOST’ (Github instance), the |
|
891 |
‘USERNAME’, the ‘PACKAGE’, and the ‘SCOPES’ in the minibuffer, |
|
892 |
providing reasonable default choices. ‘SCOPES’ defaults to the |
|
893 |
scopes that ‘PACKAGE’ requests using the variable |
|
894 |
‘PACKAGE-github-token-scopes’. |
|
895 |
|
|
896 |
-- Command: ghub-token-scopes |
|
897 |
|
|
898 |
Users are free to give a token access to fewer scopes than what the |
|
899 |
respective package requested. That can, of course, lead to issues, |
|
900 |
and package maintainers have to be able to quickly determine if |
|
901 |
such a (mis-)configuration is the root cause when users report |
|
902 |
issues. |
|
903 |
|
|
904 |
This command reads the required values in the minibuffer and then |
|
905 |
shows a message containing these values along with the scopes of |
|
906 |
the respective token. It also returns the scopes (only) when |
|
907 |
called non-interactively. Also see *note Using Ghub in a |
|
908 |
Package::. |
|
909 |
|
|
910 |
|
|
911 |
File: ghub.info, Node: Configuration Variables, Prev: Authentication, Up: API |
|
912 |
|
|
913 |
5.3 Configuration Variables |
|
914 |
=========================== |
|
915 |
|
|
916 |
The username and, unless you only use Github.com itself, the Github |
|
917 |
Enterprise instance have to be configured using Git variables. In rare |
|
918 |
cases it might also be necessary to specify the identity of the local |
|
919 |
machine, which is done using a lisp variable. |
|
920 |
|
|
921 |
-- Variable: github.user |
|
922 |
|
|
923 |
The Github.com username. This should be set globally and if you |
|
924 |
have multiple Github.com user accounts, then you should set this |
|
925 |
locally only for those repositories that you want to access using |
|
926 |
the secondary identity. |
|
927 |
|
|
928 |
-- Variable: github.HOST.user |
|
929 |
|
|
930 |
This variable serves the same purpose as ‘github.user’ but for the |
|
931 |
Github Enterprise instance identified by ‘HOST’. |
|
932 |
|
|
933 |
The reason why separate variables are used is that this makes it |
|
934 |
possible to set both values globally instead of having to set one |
|
935 |
of the values locally in each and every repository that is |
|
936 |
connected to the Github Enterprise instance, not Github.com. |
|
937 |
|
|
938 |
-- Variable: github.host |
|
939 |
|
|
940 |
This variable should only be set locally for a repository and |
|
941 |
specifies the Github Enterprise edition that that repository is |
|
942 |
connected to. You should not set this globally because then each |
|
943 |
and every repository becomes connected to the specified Github |
|
944 |
Enterprise instance, including those that should actually be |
|
945 |
connected to Github.com. |
|
946 |
|
|
947 |
When this is undefined, then "api.github.com" is used (defined in |
|
948 |
the constant ‘ghub-default-host’, which you should never attempt to |
|
949 |
change.) |
|
950 |
|
|
951 |
-- Variable: ghub-override-system-name |
|
952 |
|
|
953 |
Ghub uses a different token for each quadruple ‘(USERNAME PACKAGE |
|
954 |
HOST LOCAL-MACHINE)’. Theoretically it could reuse tokens to some |
|
955 |
extent but that would be more difficult to implement, less |
|
956 |
flexible, and less secure (though slightly more convenient). |
|
957 |
|
|
958 |
A token is identified on the respective Github instance (Github.com |
|
959 |
or a Github Enterprise instance) using the pair ‘(PACKAGE . |
|
960 |
LOCAL-MACHINE)’, or more precisely the string "Emacs package |
|
961 |
PACKAGE @ LOCAL-MACHINE". ‘USERNAME’ and ‘HOST’ do not have to be |
|
962 |
encoded because the token is stored for ‘USERNAME’ on ‘HOST’ and |
|
963 |
cannot be used by another user and/or on another instance. |
|
964 |
|
|
965 |
There is one potential problem though; for any given ‘(PACKAGE . |
|
966 |
LOCAL-MACHINE)’ there can only be one token identified by "Emacs |
|
967 |
package PACKAGE @ LOCAL-MACHINE"; Github does not allow multiple |
|
968 |
tokens with the same description because it uses the description as |
|
969 |
the identifier (it could use some hash instead, but alas it does |
|
970 |
not). |
|
971 |
|
|
972 |
If you have multiple machines and some of them have the same name, |
|
973 |
then you should probably change that as this is not how things |
|
974 |
ought to be. However if you dual-boot, then it might make sense to |
|
975 |
give that machine the same name regardless of what operating system |
|
976 |
you have booted into. |
|
977 |
|
|
978 |
You could use the same token on both operating systems, but setting |
|
979 |
that up might be somewhat difficult because it is not possible to |
|
980 |
download an existing token from Github. You could, of course, |
|
981 |
locally copy the token, but that is inconvenient and would make it |
|
982 |
harder to only revoke the token used on your infected Windows |
|
983 |
installation without also revoking it for your totally safe *BSD |
|
984 |
installation. |
|
985 |
|
|
986 |
Alternatively you can set this variable to a unique value, that |
|
987 |
will then be used to identify the local machine instead of the |
|
988 |
value returned by ‘system-name’. |
|
989 |
|
|
990 |
|
|
991 |
File: ghub.info, Node: GraphQL Support, Next: Support for Other Forges, Prev: API, Up: Top |
|
992 |
|
|
993 |
6 GraphQL Support |
|
994 |
***************** |
|
995 |
|
|
996 |
-- Function: ghub-graphql graphql &optional variables &key username |
|
997 |
auth host callback silent callback errorback value extra |
|
998 |
|
|
999 |
This function makes a GraphQL request using ‘GRAPHQL’ and |
|
1000 |
‘VARIABLES’ as inputs. ‘GRAPHQL’ is a GraphQL string. ‘VARIABLES’ |
|
1001 |
is a JSON-like alist. The other arguments behave as for |
|
1002 |
‘ghub-request’ (which see). |
|
1003 |
|
|
1004 |
The response is returned as a JSON-like alist. Even if the |
|
1005 |
response contains ‘errors’, this function does not raise an error. |
|
1006 |
Cursor-handling is likewise left to the caller. |
|
1007 |
|
|
1008 |
‘ghub-graphql’ is a thin convenience wrapper around ‘ghub-request’, |
|
1009 |
similar to ‘ghub-post’ and friends. While the latter only hard-code the |
|
1010 |
value of the ‘METHOD’ argument, the former also hard-codes ‘RESOURCE’ |
|
1011 |
and constructs ‘PAYLOAD’ from ‘GRAPHEQL’ and ‘VARIABLES’. It also drops |
|
1012 |
‘UNPAGINATE’, ‘NOERROR’, ‘READER’ (internal functions expect alist-ified |
|
1013 |
JSON) and ‘FORGE’ (only Github currently supports GraphQL). |
|
1014 |
|
|
1015 |
‘ghub-graphql’ does not account for the fact that pagination works |
|
1016 |
differently in GraphQL than it does in REST, so users of this function |
|
1017 |
have to deal with that themselves. Likewise error handling works |
|
1018 |
differently and has to be done by the caller too. |
|
1019 |
|
|
1020 |
An early attempt at implementing automatic unpaginating for GraphQL |
|
1021 |
can be found in the ‘faithful-graphql’ branch, provided I haven’t |
|
1022 |
deleted that by now. On that branch I try to do things as intended by |
|
1023 |
the designers of GraphQL, using variables and fragments, and drowning in |
|
1024 |
a sea of boilerplate. |
|
1025 |
|
|
1026 |
The problem with that approach is that it only works for applications |
|
1027 |
that fetch specific information on demand and actually want things to be |
|
1028 |
paginated. I am convinced that GraphQL is very nice for web apps. |
|
1029 |
|
|
1030 |
However the Forge package for which I am implementing all of this has |
|
1031 |
very different needs. It wants to fetch "all the data" and "cache" it |
|
1032 |
locally, so that it is available even when there is no internet |
|
1033 |
connection. GraphQL was designed around the idea that you should be |
|
1034 |
able to "ask for what you need and get exactly that". But when that |
|
1035 |
boils down to "look, if I persist, then you are going to hand me over |
|
1036 |
all the data anyway, so just caught it up already", then things start to |
|
1037 |
fall apart. If Github’s GraphQL allowed pagination to be turned off |
|
1038 |
completely, then teaching ‘ghub-graphql’ about error handling would be |
|
1039 |
enough. |
|
1040 |
|
|
1041 |
But it doesn’t and when doing things as intended, then that leads to |
|
1042 |
huge amounts of repetitive boilerplate, which is so boring to write that |
|
1043 |
doing it without introducing bugs left and right is near impossible; so |
|
1044 |
I decided to give up on GraphQL variables, fragments and conditions, and |
|
1045 |
instead implement something more powerful, though also more opinionated. |
|
1046 |
|
|
1047 |
-- Function: ghub--graphql-vacuum query variables callback &optional |
|
1048 |
until &key narrow username auth host forge |
|
1049 |
|
|
1050 |
This function is an opinionated alternative to ‘ghub-graphql’. It |
|
1051 |
relies and dark magic to get the job done. |
|
1052 |
|
|
1053 |
It makes an initial request using ‘QUERY’. It then looks for |
|
1054 |
paginated edges in the returned data and makes more requests to |
|
1055 |
resolve them. In order to do so it automatically transforms the |
|
1056 |
initial ‘QUERY’ into another query suitable for that particular |
|
1057 |
edge. The data retrieved by subsequent requests is then injected |
|
1058 |
into the data of the original request before that is returned or |
|
1059 |
passed to the callback. If subsequently retrieved data features |
|
1060 |
new paginated edges, then those are followed recursively. |
|
1061 |
|
|
1062 |
The end result is essentially the same as using ‘ghub-graphql’, if |
|
1063 |
only it were possible to say "do not paginate anything". The |
|
1064 |
implementation is much more complicated because it is not possible |
|
1065 |
to do that. |
|
1066 |
|
|
1067 |
‘QUERY’ is a GraphQL query expressed as an s-expression. The |
|
1068 |
‘graphql’ package is used to turn that into a GraphQL query string, |
|
1069 |
but the format is somewhat different than as documented for that |
|
1070 |
package. Also only a subset of the GraphQL features are supported; |
|
1071 |
fragments for example are not, and magical stuff happens to |
|
1072 |
variables. This is not documented yet, I am afraid. Look at |
|
1073 |
existing callers. |
|
1074 |
|
|
1075 |
‘VARIABLES’ is a JSON-like alist as for ‘ghub-graphql’. |
|
1076 |
|
|
1077 |
‘UNTIL’ is an alist ‘((EDGE-until . VALUE)...)’. When unpaginating |
|
1078 |
‘EDGE’ try not to fetch beyond the element whose first field has |
|
1079 |
the value ‘VALUE’ and remove that element as well as all "lesser" |
|
1080 |
elements from the retrieved data if necessary. Look at |
|
1081 |
‘forge--pull-repository’ for an example. This is only useful if |
|
1082 |
you "cache" the response locally and want to avoid fetching data |
|
1083 |
again that you already have. |
|
1084 |
|
|
1085 |
Other arguments behave as for ‘ghub-graphql’ and ‘ghub-request’, |
|
1086 |
more or less. |
|
1087 |
|
|
1088 |
Using ‘ghub--graphql-vacuum’, the following resource specific |
|
1089 |
functions are implemented. These functions are not part of the public |
|
1090 |
API yet and are very much subject to change. |
|
1091 |
|
|
1092 |
-- Function: ghub-fetch-repository owner name callback &optional until |
|
1093 |
&key username auth host forge |
|
1094 |
|
|
1095 |
This function asynchronously fetches forge data about the specified |
|
1096 |
repository. Once all data has been collected, ‘CALLBACK’ is called |
|
1097 |
with the data as the only argument. |
|
1098 |
|
|
1099 |
-- Function: ghub-fetch-issue owner name callback &optional until &key |
|
1100 |
username auth host forge |
|
1101 |
|
|
1102 |
This function asynchronously fetches forge data about the specified |
|
1103 |
issue. Once all data has been collected, ‘CALLBACK’ is called with |
|
1104 |
the data as the only argument. |
|
1105 |
|
|
1106 |
-- Function: ghub-fetch-pullreq owner name callback &optional until |
|
1107 |
&key username auth host forge |
|
1108 |
|
|
1109 |
This function asynchronously fetches forge data about the specified |
|
1110 |
pull-request. Once all data has been collected, ‘CALLBACK’ is |
|
1111 |
called with the data as the only argument. |
|
1112 |
|
|
1113 |
Note that in order to avoid duplication all of these functions base |
|
1114 |
their initial query on the query stored in ‘ghub-fetch-repository’. The |
|
1115 |
latter two pass that query through ‘ghub--graphql-prepare-query’, which |
|
1116 |
then used ‘ghub--graphql-narrow-query’ to remove parts the caller is not |
|
1117 |
interested in. These two functions are also used internally, when |
|
1118 |
unpaginating, but as demonstrated here they can be useful even before |
|
1119 |
making an initial request. |
|
1120 |
|
|
1121 |
|
|
1122 |
File: ghub.info, Node: Support for Other Forges, Prev: GraphQL Support, Up: Top |
|
1123 |
|
|
1124 |
7 Support for Other Forges |
|
1125 |
************************** |
|
1126 |
|
|
1127 |
* Menu: |
|
1128 |
|
|
1129 |
* Forge Functions and Variables:: |
|
1130 |
* Forge Limitations and Notes:: |
|
1131 |
|
|
1132 |
|
|
1133 |
File: ghub.info, Node: Forge Functions and Variables, Next: Forge Limitations and Notes, Up: Support for Other Forges |
|
1134 |
|
|
1135 |
7.1 Forge Functions and Variables |
|
1136 |
================================= |
|
1137 |
|
|
1138 |
Originally Ghub supported only Github but now it also supports Gitlab, |
|
1139 |
Gitea, Gogs and Bitbucket. The function ‘ghub-request’ and all the |
|
1140 |
‘ghub-METHOD’ convenience wrappers default to acting on a Github forge |
|
1141 |
but can be told to act on another forge using their FORGE argument. |
|
1142 |
|
|
1143 |
The FORGE argument only specifies what kind of forge to act on, not |
|
1144 |
which instance. The HOST argument can be used to select the instance. |
|
1145 |
For some forges a default instance is defined: |
|
1146 |
|
|
1147 |
• Forge ‘github’ defaults to host ‘api.github.com’. |
|
1148 |
|
|
1149 |
• Forge ‘gitlab’ defaults to host ‘gitlab.com/api/v4’. |
|
1150 |
|
|
1151 |
• Forge ‘bitbucket’ defaults to host ‘api.bitbucket.org/2.0’. |
|
1152 |
|
|
1153 |
• No canonical host exists for the ‘gitea’ and ‘gogs’ forges and |
|
1154 |
‘localhost:3000/api/v1’ is used as the default host in both cases. |
|
1155 |
|
|
1156 |
Together the FORGE and HOST arguments specify the forge type and |
|
1157 |
instance. In addition to that, it is also necessary to specify on whose |
|
1158 |
behalf the request is being made, which can be done using the USERNAME |
|
1159 |
and AUTH arguments. |
|
1160 |
|
|
1161 |
Having to specify these arguments for every request is inconvenient. |
|
1162 |
Additional variables and convenience functions can be used to make that |
|
1163 |
unnecessary in most cases. |
|
1164 |
|
|
1165 |
These variables can be set globally and/or for a specific repository |
|
1166 |
as explained in *note Configuration Variables:: with a focus on Github |
|
1167 |
instances. To summarize: |
|
1168 |
|
|
1169 |
• For <https://github.com> the Git variable ‘github.user’ specifies |
|
1170 |
the user. |
|
1171 |
|
|
1172 |
• For another ‘github’ instance the Git variable ‘github.HOST.user’ |
|
1173 |
specifies the user. The HOST in that variable name is the same as |
|
1174 |
the value of the HOST argument of the called function. |
|
1175 |
|
|
1176 |
• Instead of specifying the HOST in every function call, the Git |
|
1177 |
variable ‘github.host’ can be used. This should only be set |
|
1178 |
locally. |
|
1179 |
|
|
1180 |
For ‘gitlab’ and ‘bitbucket’ forges similar variables are available: |
|
1181 |
|
|
1182 |
• ‘gitlab.user’ specifies the <https://gitlab.com> user. |
|
1183 |
|
|
1184 |
• ‘gitlab.HOST.user’ specifies the user for the HOST ‘gitlab’ |
|
1185 |
instance. |
|
1186 |
|
|
1187 |
• ‘gitlab.host’ specifies the ‘gitlab’ host, unless the HOST argument |
|
1188 |
is non-nil |
|
1189 |
|
|
1190 |
• ‘bitbucket.user’ specifies the <https://bitbucket.org> user. |
|
1191 |
|
|
1192 |
• ‘bitbucket.HOST.user’ specifies the user for the HOST ‘bitbucket’ |
|
1193 |
instance. |
|
1194 |
|
|
1195 |
• ‘bitbucket.host’ specifies the ‘bitbucket’ host, unless the HOST |
|
1196 |
argument is non-nil. |
|
1197 |
|
|
1198 |
For ‘gitea’ and ‘gogs’ forges some similar variables are available, |
|
1199 |
however for some of the ‘ghub.*’ variables no equivalent variable exist |
|
1200 |
for these two forges: |
|
1201 |
|
|
1202 |
• ‘gitea.user’ is *not* used because no canonical ‘gitea’ instance |
|
1203 |
exists. |
|
1204 |
|
|
1205 |
• ‘gitea.HOST.user’ specifies the user for the HOST ‘gitea’ instance. |
|
1206 |
|
|
1207 |
• ‘gitea.host’ specifies the ‘gitea’ host, unless the HOST argument |
|
1208 |
is non-nil |
|
1209 |
|
|
1210 |
• ‘gogs.user’ is *not* used because no canonical ‘gitea’ instance |
|
1211 |
exists. |
|
1212 |
|
|
1213 |
• ‘gogs.HOST.user’ specifies the user for the HOST ‘gogs’ instance. |
|
1214 |
|
|
1215 |
• ‘gogs.host’ specifies the ‘gogs’ host, unless the HOST argument is |
|
1216 |
non-nil |
|
1217 |
|
|
1218 |
‘ghub-request’ and ‘ghub-METHOD’ can be used to make a request for |
|
1219 |
any of the supported forge types, but except when making a request for a |
|
1220 |
‘github’ instance, then that requires the use of the FORGE argument. |
|
1221 |
|
|
1222 |
To avoid that, functions named ‘FORGE-request’ and ‘FORGE-METHOD’ are |
|
1223 |
also available. The following forms are equivalent, for example: |
|
1224 |
|
|
1225 |
(ghub-get ... :auth 'PACKAGE :forge 'gitlab) |
|
1226 |
(glab-get ... :auth 'PACKAGE) |
|
1227 |
|
|
1228 |
These forms would remain equivalent even if you did not specify a |
|
1229 |
value for the AUTH arguments — but you should not do that if you plan to |
|
1230 |
share your code with others (see *note Using Ghub in a Package::). If |
|
1231 |
you do omit AUTH, then the request is made on behalf of the ‘ghub’ |
|
1232 |
package, *regardless* of the symbol prefix of the function you use to do |
|
1233 |
so. |
|
1234 |
|
|
1235 |
All ‘FORGE-request’ and ‘FORGE-METHOD’ functions, including but not |
|
1236 |
limited to ‘ghub-METHOD’, are very simple wrappers around |
|
1237 |
‘ghub-request’. They take fewer arguments than ‘ghub-request’ and |
|
1238 |
instead pass constant values for the arguments METHOD and/or FORGE. |
|
1239 |
|
|
1240 |
|
|
1241 |
File: ghub.info, Node: Forge Limitations and Notes, Prev: Forge Functions and Variables, Up: Support for Other Forges |
|
1242 |
|
|
1243 |
7.2 Forge Limitations and Notes |
|
1244 |
=============================== |
|
1245 |
|
|
1246 |
• The token creation wizard is only available for ‘github’ forges, |
|
1247 |
because all other forges do not support using the API to create an |
|
1248 |
API token. As a consequence, if the user makes a request and the |
|
1249 |
necessary token cannot be found, then that results in an error. |
|
1250 |
Tokens can be created at: |
|
1251 |
|
|
1252 |
• Gitlab: <https://gitlab.com/profile/personal_access_tokens> |
|
1253 |
|
|
1254 |
• Bitbucket: |
|
1255 |
<https://bitbucket.org/account/user/tarsius/app-passwords> |
|
1256 |
|
|
1257 |
• Gitea: <https://localhost:3000/user/settings/applications> |
|
1258 |
|
|
1259 |
• Gogs: <https://localhost:3000/user/settings/applications> |
|
1260 |
|
|
1261 |
Also see *note Manually Creating and Storing a Token:: and *note |
|
1262 |
How Ghub uses Auth-Source::. |
|
1263 |
|
|
1264 |
• As mentioned in the previous node, the variables ‘gitea.host’ and |
|
1265 |
‘gogs.host’ are not taken into account. |
|
1266 |
|
|
1267 |
• Gitea and Gogs do not support limiting a token to certain scopes. |
|
1268 |
|
|
1269 |
• The Bitbucket API is fairly broken. Some resources only work if a |
|
1270 |
slash is appended while others only work if no slash is appended. |
|
1271 |
I am unable to access any private repositories and some resources |
|
1272 |
don’t work for me at all. Also the API is only RESTish; pagination |
|
1273 |
information is part of the response body instead of the header. |
|
1274 |
Due to such issues it is possible that I will eventually have to |
|
1275 |
remove support for Bitbucket altogether. |
|
1276 |
|
|
1277 |
• The Gitlab API documentation is not always accurate, though I don’t |
|
1278 |
have an example at hand. It also isn’t structured well, making it |
|
1279 |
occationally difficult to find the information one is looking for. |
|
1280 |
|
|
1281 |
• Where one would use ‘user/repo’ when accessing another forge, one |
|
1282 |
has to use ‘user%2Frepo’ when accessing Gitlab, e.g.: |
|
1283 |
|
|
1284 |
(glab-get "/projects/python-mode-devs%2Fpython-mode") |
|
1285 |
|
|
1286 |
|
|
1287 |
|
|
1288 |
Tag Table: |
|
1289 |
Node: Top764 |
|
1290 |
Node: Introduction3279 |
|
1291 |
Node: Getting Started6321 |
|
1292 |
Node: Setting the Username10055 |
|
1293 |
Node: Interactively Creating and Storing a Token11480 |
|
1294 |
Node: Manually Creating and Storing a Token17131 |
|
1295 |
Node: How Ghub uses Auth-Source18354 |
|
1296 |
Node: Using Ghub in Personal Scripts20287 |
|
1297 |
Node: Using Ghub in a Package21743 |
|
1298 |
Node: API24361 |
|
1299 |
Node: Making Requests25158 |
|
1300 |
Node: Authentication39197 |
|
1301 |
Node: Configuration Variables41042 |
|
1302 |
Node: GraphQL Support44762 |
|
1303 |
Node: Support for Other Forges51427 |
|
1304 |
Node: Forge Functions and Variables51644 |
|
1305 |
Node: Forge Limitations and Notes56163 |
|
1306 |
|
|
1307 |
End Tag Table |
|
1308 |
|
|
1309 |
|
|
1310 |
Local Variables: |
|
1311 |
coding: utf-8 |
|
1312 |
End: |