gcb-host 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/daemon.js CHANGED
@@ -1,7 +1,18 @@
1
+ /**
2
+ * Daemon entry point logic: load configuration, build providers, serve the socket.
3
+ *
4
+ * Startup is fail-fast on purpose. A broker that cannot read its private key, or whose
5
+ * configuration is over-broad or malformed, must not come up and quietly serve nothing — an
6
+ * operator has to see the failure. `--check` performs the same validation and provider
7
+ * construction without binding the socket, which is what a deployment script should run
8
+ * before restarting the service.
9
+ */
10
+ import { createHash } from 'node:crypto';
1
11
  import { createAudit } from './audit.js';
2
12
  import { startBroker } from './broker.js';
3
- import { assertKeysReadable, assertUserTokensReadable, loadConfig, resolveConfigPath } from './config.js';
13
+ import { assertKeysReadable, assertUserTokensReadable, loadConfigText, readConfigText, resolveConfigPath } from './config.js';
4
14
  import { createProvider } from './providers/index.js';
15
+ import { ConfigWatcher } from './reload.js';
5
16
  /** Usage text. */
6
17
  const USAGE = 'usage: gcb daemon [--config <path>] [--check]\n' +
7
18
  ' --config defaults to $GIT_BROKER_CONFIG, then ./broker.config.yml\n' +
@@ -70,15 +81,22 @@ export async function runDaemon(argv, io = {}) {
70
81
  // Same convention as the management commands: --config, else GIT_BROKER_CONFIG, else
71
82
  // ./broker.config.yml. A wrong pick is loud, because it must load and validate to start at all.
72
83
  const resolved = resolveConfigPath(configPath);
84
+ const load = () => {
85
+ const text = readConfigText(resolved);
86
+ const next = loadConfigText(text, resolved);
87
+ assertKeysReadable(next);
88
+ assertUserTokensReadable(next);
89
+ return {
90
+ config: next,
91
+ providers: buildProviders(next, { log: (message) => stderr.write(`gcb daemon: ${message}\n`) }),
92
+ digest: createHash('sha256').update(text).digest('hex'),
93
+ };
94
+ };
73
95
  let config;
74
96
  let providers;
97
+ let initialDigest;
75
98
  try {
76
- config = loadConfig(resolved);
77
- assertKeysReadable(config);
78
- assertUserTokensReadable(config);
79
- providers = buildProviders(config, {
80
- log: (message) => stderr.write(`gcb daemon: ${message}\n`),
81
- });
99
+ ({ config, providers, digest: initialDigest } = load());
82
100
  }
83
101
  catch (error) {
84
102
  stderr.write(`gcb daemon: ${error.message}\n`);
@@ -107,16 +125,48 @@ export async function runDaemon(argv, io = {}) {
107
125
  stderr.write(`gcb daemon: cannot start: ${error.message}\n`);
108
126
  return 1;
109
127
  }
128
+ const active = { config, providers };
129
+ const watcher = new ConfigWatcher({
130
+ file: resolved,
131
+ initialDigest,
132
+ reload: () => {
133
+ const next = load();
134
+ if (next.config.socketPath !== active.config.socketPath) {
135
+ throw new Error('socketPath cannot change without restarting the broker');
136
+ }
137
+ if (next.config.socketMode !== active.config.socketMode) {
138
+ throw new Error('socketMode cannot change without restarting the broker');
139
+ }
140
+ if (next.config.auditPath !== active.config.auditPath) {
141
+ throw new Error('auditPath cannot change without restarting the broker');
142
+ }
143
+ broker.reload(next.config, next.providers);
144
+ active.config = next.config;
145
+ active.providers = next.providers;
146
+ stderr.write(`gcb daemon: config reloaded\n`);
147
+ audit.record({ event: 'config', decision: 'reload' });
148
+ return next.digest;
149
+ },
150
+ onError: (error) => {
151
+ stderr.write(`gcb daemon: config reload failed: ${error.message}\n`);
152
+ audit.record({ event: 'config', decision: 'reload-failed', reason: error.message });
153
+ },
154
+ });
155
+ watcher.start();
110
156
  await new Promise((resolve) => {
111
157
  const shutdown = (signal) => {
112
158
  stderr.write(`gcb daemon: ${signal} received, shutting down\n`);
159
+ watcher.stop();
160
+ process.off('SIGHUP', onHup);
113
161
  broker
114
162
  .close()
115
163
  .then(() => audit.close())
116
164
  .then(() => resolve());
117
165
  };
166
+ const onHup = () => watcher.check(true);
118
167
  process.once('SIGTERM', () => shutdown('SIGTERM'));
119
168
  process.once('SIGINT', () => shutdown('SIGINT'));
169
+ process.on('SIGHUP', onHup);
120
170
  });
121
171
  return 0;
122
172
  }
package/lib/daemon.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,7 @@ export type Fetcher = (url: string, init: {
15
15
  method: string;
16
16
  headers: Record<string, string>;
17
17
  body: string;
18
+ signal?: AbortSignal;
18
19
  }) => Promise<{
19
20
  ok: boolean;
20
21
  status: number;
@@ -1 +1 @@
1
- {"version":3,"file":"device-flow.d.ts","sourceRoot":"","sources":["../src/device-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,yDAAyD;AACzD,MAAM,MAAM,OAAO,GAAG,CACpB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KACpE,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAAC;AAEvE,sCAAsC;AACtC,MAAM,MAAM,KAAK,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAElD,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,oCAAoC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,0DAA0D;AAC1D,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8FAA8F;IAC9F,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,6EAA6E;AAC7E,eAAO,MAAM,eAAe,yCAAyC,CAAC;AACtE,eAAO,MAAM,SAAS,gDAAgD,CAAC;AACvE,sEAAsE;AACtE,eAAO,MAAM,YAAY,iDAAiD,CAAC;AAmB3E;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CA4B7F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,SAAS,CAAC,CAwCpB;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,SAAS,CAAC,CAyBpB"}
1
+ {"version":3,"file":"device-flow.d.ts","sourceRoot":"","sources":["../src/device-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,yDAAyD;AACzD,MAAM,MAAM,OAAO,GAAG,CACpB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,KAC1F,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAAC;AAEvE,sCAAsC;AACtC,MAAM,MAAM,KAAK,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAElD,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,oCAAoC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,0DAA0D;AAC1D,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8FAA8F;IAC9F,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,6EAA6E;AAC7E,eAAO,MAAM,eAAe,yCAAyC,CAAC;AACtE,eAAO,MAAM,SAAS,gDAAgD,CAAC;AACvE,sEAAsE;AACtE,eAAO,MAAM,YAAY,iDAAiD,CAAC;AAmB3E;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CA4B7F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,SAAS,CAAC,CAwCpB;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,SAAS,CAAC,CA0BpB"}
@@ -133,6 +133,7 @@ export async function refreshUserToken(clientId, refreshToken, fetcher) {
133
133
  grant_type: 'refresh_token',
134
134
  refresh_token: refreshToken,
135
135
  }).toString(),
136
+ signal: AbortSignal.timeout(30_000),
136
137
  });
137
138
  const text = await response.text();
138
139
  const parsed = parseJson(text);
@@ -1 +1 @@
1
- {"version":3,"file":"device-flow.js","sourceRoot":"","sources":["../src/device-flow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"device-flow.js","sourceRoot":"","sources":["../src/device-flow.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"github-app.d.ts","sourceRoot":"","sources":["../../src/providers/github-app.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAc,SAAS,EAAqB,mBAAmB,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAmBxG,mDAAmD;AACnD,MAAM,WAAW,wBAAwB;IACvC,qCAAqC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,mBAAmB,CAAC;IAClC,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,uCAAuC;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,mCAAmC;IACnC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,6BAA6B;IAC7B,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAED,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAuB7D;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAQjE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,QAAQ,CA+8BnF"}
1
+ {"version":3,"file":"github-app.d.ts","sourceRoot":"","sources":["../../src/providers/github-app.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAc,SAAS,EAAqB,mBAAmB,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAmBxG,mDAAmD;AACnD,MAAM,WAAW,wBAAwB;IACvC,qCAAqC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,mBAAmB,CAAC;IAClC,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,uCAAuC;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,mCAAmC;IACnC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,6BAA6B;IAC7B,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAED,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAuB7D;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAQjE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,QAAQ,CAo+BnF"}
@@ -390,6 +390,90 @@ export function createGithubAppProvider(options) {
390
390
  // the exchange even when the commands selected different proxy routes, then let the waiter re-read
391
391
  // the cache the winner wrote. Each exchange still uses its own command's fetch.
392
392
  let renewingPersonToken;
393
+ /**
394
+ * The token of the person this deployment authorized, when there is one.
395
+ *
396
+ * Only person-relevant actions may use it; GitHub will not author a pull request as a person for
397
+ * an app, refuses to resolve a review thread for one, and a reply must be attributable to the
398
+ * person answering.
399
+ * The refresh token lives beside the private key, and GitHub rotates it on every use, so the new
400
+ * one is written back — keeping the old would work once and then stop.
401
+ *
402
+ * @returns the user token, or `undefined` when nobody has authorized one.
403
+ */
404
+ async function personalToken() {
405
+ while (renewingPersonToken !== undefined) {
406
+ try {
407
+ await renewingPersonToken;
408
+ }
409
+ catch {
410
+ // The winner failed; this command retries after the lock is released.
411
+ }
412
+ }
413
+ const pending = renewPersonToken();
414
+ renewingPersonToken = pending;
415
+ try {
416
+ return await pending;
417
+ }
418
+ finally {
419
+ if (renewingPersonToken === pending)
420
+ renewingPersonToken = undefined;
421
+ }
422
+ }
423
+ /** The authorized person's token, falling back to the per-owner token for this owner. */
424
+ async function personTokenFor(owner) {
425
+ const personal = await personalToken();
426
+ if (personal !== undefined)
427
+ return personal;
428
+ const file = cfg.userTokens?.[owner];
429
+ if (file === undefined)
430
+ return undefined;
431
+ const token = readFileSync(file, 'utf8').trim();
432
+ return token === '' ? undefined : token;
433
+ }
434
+ async function renewPersonToken() {
435
+ if (cfg.clientId === undefined || cfg.privateKeyPath === undefined)
436
+ return undefined;
437
+ const path = userTokenPath(cfg.privateKeyPath);
438
+ if (!existsSync(path))
439
+ return undefined;
440
+ const now = Date.now();
441
+ // The file is the only thing that says who the broker may act as, so it is read before the cache is
442
+ // trusted: running `authorize` again — the documented way to change that person, and the only one —
443
+ // has to take effect now rather than when the cached token happens to expire.
444
+ const held = readFileSync(path, 'utf8').trim();
445
+ if (cachedPersonToken !== undefined &&
446
+ cachedPersonToken.until > now + 60_000 &&
447
+ cachedPersonToken.source === held) {
448
+ return cachedPersonToken.token;
449
+ }
450
+ let next;
451
+ try {
452
+ next = await refreshUserToken(cfg.clientId, held, (url, init) => fetchImpl(url, init));
453
+ }
454
+ catch (error) {
455
+ // This is the one place a renewal failure is explained: the caller is an action that failed,
456
+ // and "fetch failed" on its own names neither the host nor the reason.
457
+ throw new Error(`could not renew the authorized token from ${path}: ${error.message}${describeCause(error)}`);
458
+ }
459
+ if (next.refreshToken !== undefined) {
460
+ try {
461
+ writeFileSync(path, `${next.refreshToken}\n`, { mode: 0o600 });
462
+ }
463
+ catch (error) {
464
+ // GitHub invalidated the token on disk the moment it answered, so this is not transient and not
465
+ // recoverable: the next renewal reads a dead token. Nothing else in the log would say so.
466
+ throw new Error(`could not store the new refresh token in ${path}: ${error.message}; the previous ` +
467
+ 'one is no longer valid, so run authorize again');
468
+ }
469
+ }
470
+ cachedPersonToken = {
471
+ token: next.token,
472
+ until: now + (next.expiresInSeconds ?? 28_800) * 1_000,
473
+ source: next.refreshToken ?? held,
474
+ };
475
+ return cachedPersonToken.token;
476
+ }
393
477
  /**
394
478
  * One more sentence about a failure, when there is one worth having.
395
479
  *
@@ -426,16 +510,30 @@ export function createGithubAppProvider(options) {
426
510
  name: 'github-app',
427
511
  async runAction(request) {
428
512
  const spec = getGhAction(request.action);
429
- const wanted = { ...spec.permissions };
430
- await ensurePermissions(request.owner, wanted);
431
- const installationId = await resolveInstallationId(request.owner);
432
- const { json } = await call('POST', `${api}/app/installations/${installationId}/access_tokens`, {
433
- repositories: [request.repo],
434
- permissions: wanted,
435
- });
436
- const token = json?.token;
437
- if (typeof token !== 'string' || !token) {
438
- throw new ProviderConfigError('GitHub returned no installation token for the action');
513
+ const identity = request.identity ?? 'app';
514
+ let token;
515
+ let minted = false;
516
+ if (identity === 'user') {
517
+ const person = await personTokenFor(request.owner);
518
+ if (person === undefined) {
519
+ throw new ProviderConfigError('no authorized user token for this action; run gcb authorize or configure userTokens');
520
+ }
521
+ token = person;
522
+ }
523
+ else {
524
+ const wanted = { ...spec.permissions };
525
+ await ensurePermissions(request.owner, wanted);
526
+ const installationId = await resolveInstallationId(request.owner);
527
+ const { json } = await call('POST', `${api}/app/installations/${installationId}/access_tokens`, {
528
+ repositories: [request.repo],
529
+ permissions: wanted,
530
+ });
531
+ const mintedToken = json?.token;
532
+ if (typeof mintedToken !== 'string' || !mintedToken) {
533
+ throw new ProviderConfigError('GitHub returned no installation token for the action');
534
+ }
535
+ token = mintedToken;
536
+ minted = true;
439
537
  }
440
538
  let result;
441
539
  let warning;
@@ -455,32 +553,34 @@ export function createGithubAppProvider(options) {
455
553
  result = { ...NO_RUN, error: error instanceof Error ? error.message : String(error) };
456
554
  }
457
555
  finally {
458
- try {
459
- const response = await fetchImpl(`${api}/installation/token`, {
460
- method: 'DELETE',
461
- headers: {
462
- accept: 'application/vnd.github+json',
463
- authorization: `Bearer ${token}`,
464
- 'x-github-api-version': apiVersion,
465
- 'user-agent': 'gcb',
466
- },
467
- });
468
- if (!response.ok) {
469
- let detail = '';
470
- try {
471
- detail = (await response.text()).replace(/\s+/g, ' ').trim().slice(0, 200);
472
- }
473
- catch {
474
- // The status is enough; a body that cannot be read is still not a successful revoke.
556
+ if (minted) {
557
+ try {
558
+ const response = await fetchImpl(`${api}/installation/token`, {
559
+ method: 'DELETE',
560
+ headers: {
561
+ accept: 'application/vnd.github+json',
562
+ authorization: `Bearer ${token}`,
563
+ 'x-github-api-version': apiVersion,
564
+ 'user-agent': 'gcb',
565
+ },
566
+ });
567
+ if (!response.ok) {
568
+ let detail = '';
569
+ try {
570
+ detail = (await response.text()).replace(/\s+/g, ' ').trim().slice(0, 200);
571
+ }
572
+ catch {
573
+ // The status is enough; a body that cannot be read is still not a successful revoke.
574
+ }
575
+ log(`could not revoke the action token: GitHub answered ${response.status}${detail ? ` ${detail}` : ''}`);
576
+ warning = REVOKE_WARNING;
475
577
  }
476
- log(`could not revoke the action token: GitHub answered ${response.status}${detail ? ` ${detail}` : ''}`);
578
+ }
579
+ catch (error) {
580
+ log(`could not revoke the action token: ${String(error.message ?? error)}`);
477
581
  warning = REVOKE_WARNING;
478
582
  }
479
583
  }
480
- catch (error) {
481
- log(`could not revoke the action token: ${String(error.message ?? error)}`);
482
- warning = REVOKE_WARNING;
483
- }
484
584
  }
485
585
  return { ...result, tokenFingerprint: tokenFingerprint(token), ...(warning === undefined ? {} : { warning }) };
486
586
  },
@@ -559,11 +659,7 @@ export function createGithubAppProvider(options) {
559
659
  */
560
660
  async pullRequest(request) {
561
661
  if (request.action === 'fork') {
562
- let token = await personalToken();
563
- const userToken = cfg.userTokens?.[request.owner];
564
- if (token === undefined && userToken !== undefined) {
565
- token = readFileSync(userToken, 'utf8').trim();
566
- }
662
+ const token = await personTokenFor(request.owner);
567
663
  if (token === undefined) {
568
664
  throw new ProviderConfigError('no user token for fork; run gcb authorize or configure userTokens');
569
665
  }
@@ -581,28 +677,26 @@ export function createGithubAppProvider(options) {
581
677
  }
582
678
  return { number: 0, url: forked.html_url };
583
679
  }
584
- // The two actions a person may perform are decided first: when somebody has authorized this
585
- // deployment, their token is the whole authority for them, and requiring an installation as well
586
- // would refuse a repository the app was never installed on — the fork-to-upstream case the allowlist
587
- // exists to permit. Everything else still acts as the app, and only ever could.
680
+ // The person is selected explicitly, and by default for the two operations an installation token
681
+ // cannot perform (open/resolve): requiring an installation as well would refuse a repository the
682
+ // app was never installed on — the fork-to-upstream case the allowlist exists to permit. Reply,
683
+ // edit and comment stay on the app unless identity is explicitly 'user'.
588
684
  const wantsApp = request.identity === 'app';
589
- const wantsUser = request.identity === 'user';
590
- const personalForThis = !wantsApp && (request.action === 'open' || request.action === 'resolve');
591
- const personToken = personalForThis ? await personalToken() : undefined;
685
+ const personalForThis = request.identity === 'user' ||
686
+ (!wantsApp && (request.action === 'open' || request.action === 'resolve'));
687
+ const actingToken = personalForThis ? await personTokenFor(request.owner) : undefined;
688
+ // Resolving has no app route: the thread mutation is refused for an installation token, so a
689
+ // missing person token is a configuration error, not a reason to mint the wrong credential.
690
+ const personRequired = request.identity === 'user' || request.action === 'resolve';
691
+ if (personRequired && actingToken === undefined) {
692
+ throw new ProviderConfigError(`no authorized user token for ${request.action === 'resolve' ? 'resolving this thread' : 'this request'}; run gcb authorize or configure userTokens`);
693
+ }
592
694
  // `--status` also reads /actions/runs, which is checked against actions: read. Pre-flighting
593
695
  // pull_requests alone passed a grant that could not make that call, which is the opposite of what a
594
696
  // pre-flight is for.
595
697
  const wanted = request.action === 'status' ? { ...PULL_REQUEST_PERMISSIONS, actions: 'read' } : PULL_REQUEST_PERMISSIONS;
596
698
  // One variable, so every branch below holds a token without having to ask which kind it is.
597
699
  let token;
598
- // A token per owner is the other way a person acts. It counts exactly as the authorized token does:
599
- // whoever is acting, requiring an installation as well would refuse a repository the app was never
600
- // installed on — the fork-to-upstream case the allowlist exists to permit.
601
- const fileToken = personalForThis ? cfg.userTokens?.[request.owner] : undefined;
602
- const actingToken = personToken ?? (fileToken === undefined ? undefined : readFileSync(fileToken, 'utf8').trim());
603
- if (wantsUser && actingToken === undefined) {
604
- throw new ProviderConfigError('no authorized user token for this request; run gcb authorize or configure userTokens');
605
- }
606
700
  if (actingToken === undefined) {
607
701
  await ensurePermissions(request.owner, wanted);
608
702
  const installationId = await resolveInstallationId(request.owner);
@@ -620,84 +714,12 @@ export function createGithubAppProvider(options) {
620
714
  token = actingToken;
621
715
  }
622
716
  const collection = `${api}/repos/${request.owner}/${request.repo}/pulls`;
623
- /**
624
- * The token of the person this deployment authorized, when there is one.
625
-
626
- * Only two actions may use it, and both because an installation token cannot: GitHub will not
627
- * author a pull request as a person for an app, and refuses to resolve a review thread for one.
628
- * The refresh token lives beside the private key, and GitHub rotates it on every use, so the new
629
- * one is written back — keeping the old would work once and then stop.
630
- *
631
- * @returns the user token, or `undefined` when nobody has authorized one.
632
- */
633
- async function personalToken() {
634
- while (renewingPersonToken !== undefined) {
635
- try {
636
- await renewingPersonToken;
637
- }
638
- catch {
639
- // The winner failed; this command retries after the lock is released.
640
- }
641
- }
642
- const pending = renewPersonToken();
643
- renewingPersonToken = pending;
644
- try {
645
- return await pending;
646
- }
647
- finally {
648
- if (renewingPersonToken === pending)
649
- renewingPersonToken = undefined;
650
- }
651
- }
652
- async function renewPersonToken() {
653
- if (cfg.clientId === undefined || cfg.privateKeyPath === undefined)
654
- return undefined;
655
- const path = userTokenPath(cfg.privateKeyPath);
656
- if (!existsSync(path))
657
- return undefined;
658
- const now = Date.now();
659
- // The file is the only thing that says who the broker may act as, so it is read before the cache is
660
- // trusted: running `authorize` again — the documented way to change that person, and the only one —
661
- // has to take effect now rather than when the cached token happens to expire.
662
- const held = readFileSync(path, 'utf8').trim();
663
- if (cachedPersonToken !== undefined &&
664
- cachedPersonToken.until > now + 60_000 &&
665
- cachedPersonToken.source === held) {
666
- return cachedPersonToken.token;
667
- }
668
- let next;
669
- try {
670
- next = await refreshUserToken(cfg.clientId, held, (url, init) => fetchImpl(url, init));
671
- }
672
- catch (error) {
673
- // This is the one place a renewal failure is explained: the caller is an action that failed,
674
- // and "fetch failed" on its own names neither the host nor the reason.
675
- throw new Error(`could not renew the authorized token from ${path}: ${error.message}${describeCause(error)}`);
676
- }
677
- if (next.refreshToken !== undefined) {
678
- try {
679
- writeFileSync(path, `${next.refreshToken}\n`, { mode: 0o600 });
680
- }
681
- catch (error) {
682
- // GitHub invalidated the token on disk the moment it answered, so this is not transient and not
683
- // recoverable: the next renewal reads a dead token. Nothing else in the log would say so.
684
- throw new Error(`could not store the new refresh token in ${path}: ${error.message}; the previous ` +
685
- 'one is no longer valid, so run authorize again');
686
- }
687
- }
688
- cachedPersonToken = {
689
- token: next.token,
690
- until: now + (next.expiresInSeconds ?? 28_800) * 1_000,
691
- source: next.refreshToken ?? held,
692
- };
693
- return cachedPersonToken.token;
694
- }
695
717
  if (request.action === 'resolve') {
696
718
  // Resolving has no REST endpoint either. The thread id is the one `--threads` prints.
697
719
  // The thread id is a global node id, and the person token is not narrowed to a repository the way
698
720
  // an installation token is. Ask whose thread it is before changing it: the allowlist decided which
699
721
  // repositories this broker may touch, and a mutation is the wrong place to discover it did not.
700
- const { text: scoped } = await callWithToken('POST', `${api}/graphql`, personToken ?? token, {
722
+ const { text: scoped } = await callWithToken('POST', `${api}/graphql`, token, {
701
723
  query: 'query($threadId:ID!){node(id:$threadId){... on PullRequestReviewThread{pullRequest{repository{nameWithOwner}}}}}',
702
724
  variables: { threadId: request.threadId },
703
725
  });
@@ -715,7 +737,7 @@ export function createGithubAppProvider(options) {
715
737
  throw new Error(`refusing to resolve ${request.threadId}: it is not a review thread of ${request.owner}/${request.repo}` +
716
738
  (typeof belongsTo === 'string' ? ` (it belongs to ${belongsTo})` : ' (or it could not be read)'));
717
739
  }
718
- const { text: raw } = await callWithToken('POST', `${api}/graphql`, personToken ?? token, {
740
+ const { text: raw } = await callWithToken('POST', `${api}/graphql`, token, {
719
741
  query: 'mutation($threadId:ID!){resolveReviewThread(input:{threadId:$threadId}){thread{isResolved}}}',
720
742
  variables: { threadId: request.threadId ?? '' },
721
743
  });