canopycms 0.0.68-int.111 → 0.0.68-int.113
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/dist/branch-health.js +14 -9
- package/dist/cli/cli.js +4 -0
- package/dist/cli/generate-ai-content.js +2 -0
- package/dist/editor/Editor.js +2 -2
- package/dist/editor/components/StagedChangesDrawer.d.ts +37 -0
- package/dist/editor/components/StagedChangesDrawer.js +75 -0
- package/dist/editor/group-manager/GroupCard.d.ts +2 -0
- package/dist/editor/group-manager/GroupCard.js +2 -2
- package/dist/editor/group-manager/GroupForm.js +1 -1
- package/dist/editor/group-manager/InternalGroupsTab.d.ts +1 -0
- package/dist/editor/group-manager/InternalGroupsTab.js +2 -2
- package/dist/editor/group-manager/hooks/useGroupState.d.ts +2 -0
- package/dist/editor/group-manager/hooks/useGroupState.js +18 -1
- package/dist/editor/group-manager/index.js +9 -4
- package/dist/editor/group-manager/types.d.ts +3 -1
- package/dist/editor/hooks/usePermissionManager.js +4 -3
- package/dist/editor/permission-manager/index.js +9 -3
- package/dist/editor/permission-manager/types.d.ts +3 -1
- package/dist/git-manager.d.ts +10 -0
- package/dist/git-manager.js +6 -5
- package/dist/utils/error.js +6 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/cms-worker.d.ts +39 -124
- package/dist/worker/cms-worker.js +232 -259
- package/dist/worker/git-sync.d.ts +7 -5
- package/dist/worker/git-sync.js +32 -29
- package/dist/worker/github-auth.d.ts +7 -7
- package/dist/worker/github-auth.js +12 -14
- package/dist/worker/github-gateway.d.ts +144 -0
- package/dist/worker/github-gateway.js +370 -0
- package/dist/worker/github-mirror.d.ts +84 -10
- package/dist/worker/github-mirror.js +237 -21
- package/dist/worker/rebase.d.ts +1 -1
- package/dist/worker/rebase.js +1 -6
- package/dist/worker/shared-repo-git.d.ts +6 -0
- package/dist/worker/shared-repo-git.js +8 -0
- package/dist/worker/task-runner.d.ts +2 -2
- package/dist/worker/task-runner.js +77 -127
- package/dist/worker/worker-context.d.ts +12 -40
- package/package.json +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import { completeTask, dequeueTask, failTask, recoverOrphanedTasks, releaseTask, retryTask, } from '../task-queue/cms-task-queue.js';
|
|
3
|
-
import {
|
|
3
|
+
import { isNoCommitsBetweenError, isRefAlreadyGoneError } from '../github-service.js';
|
|
4
4
|
import { BranchMetadataCorruptError, BranchMetadataFileManager, getBranchMetadataFileManager, } from '../branch-metadata.js';
|
|
5
5
|
import { sanitizeBranchName, RESERVED_SETTINGS_BRANCH_PREFIX } from '../paths/branch-name.js';
|
|
6
6
|
import { getErrorMessage, redactCredentials } from '../utils/error.js';
|
|
7
|
-
import { isNonFastForwardRejection,
|
|
7
|
+
import { isNonFastForwardRejection, workflowPushRefusalFile } from '../utils/git.js';
|
|
8
|
+
import { GitHubPushError, describeMove } from './github-gateway.js';
|
|
9
|
+
import { RefusedPushError, assertPlainBranchName } from './github-mirror.js';
|
|
8
10
|
import { clearHistoryRewrittenMarker, readPublishedSha } from './history-rewrite.js';
|
|
9
11
|
import { writeWorkerStatus } from '../task-queue/worker-status.js';
|
|
10
12
|
import { workerLog, workerLogError, workerLogWarn } from './log.js';
|
|
@@ -184,9 +186,9 @@ export async function processTaskQueue(ctx) {
|
|
|
184
186
|
const message = getErrorMessage(err);
|
|
185
187
|
workerLogError(`Task ${task.id} (${task.action}) failed:`, message);
|
|
186
188
|
// [REDACT] task.error is persisted (pending/failed task JSON) and served
|
|
187
|
-
// to the browser by the admin panel's Tasks tab
|
|
188
|
-
// message
|
|
189
|
-
//
|
|
189
|
+
// to the browser by the admin panel's Tasks tab, so a credential in any
|
|
190
|
+
// failure's message must not reach it. Console output above stays raw
|
|
191
|
+
// (journald/CloudWatch is trusted).
|
|
190
192
|
const persistedMessage = redactCredentials(message);
|
|
191
193
|
// DEP-L1: only transient failures are worth retrying; a permanent one
|
|
192
194
|
// would burn the retry budget on an identical doomed request.
|
|
@@ -206,16 +208,6 @@ export async function processTaskQueue(ctx) {
|
|
|
206
208
|
? ' Permanently failed (non-retryable error)'
|
|
207
209
|
: ` Permanently failed after ${maxRetries} retries`);
|
|
208
210
|
}
|
|
209
|
-
// The credential may have rotated, and the next retry resolves
|
|
210
|
-
// buildGitHubUrl() afresh to pick a new token up. Without this a push
|
|
211
|
-
// meeting a revoked token spends its whole retry budget (5s/10s/20s
|
|
212
|
-
// backoff) waiting on the git-sync loop's refresh up to 5 minutes away,
|
|
213
|
-
// and fails permanently while the working token sits in the secret store.
|
|
214
|
-
//
|
|
215
|
-
// Ungated, and AFTER the outcome is recorded rather than before: the task
|
|
216
|
-
// is safely in pending/ or failed/ while a network read runs, and that
|
|
217
|
-
// read is bounded and never throws. See CmsWorker.refreshGitHubCredential.
|
|
218
|
-
await ctx.refreshGitHubCredential();
|
|
219
211
|
}
|
|
220
212
|
processed++;
|
|
221
213
|
}
|
|
@@ -235,7 +227,7 @@ export async function processTaskQueue(ctx) {
|
|
|
235
227
|
* - an AbortSignal cancels Octokit HTTP calls and kills the push's git process
|
|
236
228
|
* (pushBranchToGitHub passes it to simple-git's `abort`);
|
|
237
229
|
* - a Promise.race rejects when either fires, so work that cannot observe the
|
|
238
|
-
* signal (a hung
|
|
230
|
+
* signal (a hung credential resolution in the gateway's push) still ends
|
|
239
231
|
* the attempt and the worker moves on instead of stalling forever.
|
|
240
232
|
*
|
|
241
233
|
* A raced-out credential resolution is not cancelled. On the App path the mint
|
|
@@ -278,30 +270,23 @@ export async function executeTask(ctx, task, signal) {
|
|
|
278
270
|
case 'push-and-create-pr': {
|
|
279
271
|
const branch = requireString(payload, 'branch');
|
|
280
272
|
await ctx.pushBranchToGitHub(branch, signal);
|
|
281
|
-
const pr = await ctx.
|
|
282
|
-
owner: ctx.githubOwner,
|
|
283
|
-
repo: ctx.githubRepo,
|
|
273
|
+
const pr = await ctx.github().createPullRequest({
|
|
284
274
|
head: branch,
|
|
285
275
|
base: optionalString(payload, 'baseBranch', ctx.baseBranch),
|
|
286
276
|
title: optionalString(payload, 'title', `Submit ${branch}`),
|
|
287
277
|
body: optionalString(payload, 'body', ''),
|
|
288
|
-
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
return { prUrl: pr.data.html_url, prNumber: pr.data.number };
|
|
278
|
+
}, signal);
|
|
279
|
+
workerLog(`Created PR #${pr.number} for ${branch}`);
|
|
280
|
+
return { prUrl: pr.url, prNumber: pr.number };
|
|
292
281
|
}
|
|
293
282
|
case 'push-and-update-pr': {
|
|
294
283
|
const branch = requireString(payload, 'branch');
|
|
295
284
|
const prNumber = requireNumber(payload, 'pullRequestNumber');
|
|
296
285
|
await ctx.pushBranchToGitHub(branch, signal);
|
|
297
|
-
await ctx.
|
|
298
|
-
owner: ctx.githubOwner,
|
|
299
|
-
repo: ctx.githubRepo,
|
|
300
|
-
pull_number: prNumber,
|
|
286
|
+
await ctx.github().updatePullRequest(prNumber, {
|
|
301
287
|
title: optionalString(payload, 'title', `Submit ${branch}`),
|
|
302
288
|
body: optionalString(payload, 'body', ''),
|
|
303
|
-
|
|
304
|
-
});
|
|
289
|
+
}, signal);
|
|
305
290
|
workerLog(`Updated PR #${prNumber} for ${branch}`);
|
|
306
291
|
return { prNumber };
|
|
307
292
|
}
|
|
@@ -326,16 +311,13 @@ export async function executeTask(ctx, task, signal) {
|
|
|
326
311
|
// base, so GitHub 422s a PR for it: push it and stop.
|
|
327
312
|
if (isSettingsBranch(branch)) {
|
|
328
313
|
await ctx.pushBranchToGitHub(branch, signal);
|
|
329
|
-
workerLog(`
|
|
314
|
+
workerLog(`No PR for ${branch}: settings branches never get one`);
|
|
330
315
|
return { pushed: true };
|
|
331
316
|
}
|
|
332
317
|
await ctx.pushBranchToGitHub(branch, signal);
|
|
333
318
|
let result;
|
|
334
319
|
try {
|
|
335
|
-
result = await createOrUpdatePullRequest({
|
|
336
|
-
octokit: ctx.octokit(),
|
|
337
|
-
owner: ctx.githubOwner,
|
|
338
|
-
repo: ctx.githubRepo,
|
|
320
|
+
result = await ctx.github().createOrUpdatePullRequest({
|
|
339
321
|
head: branch,
|
|
340
322
|
base,
|
|
341
323
|
title: optionalString(payload, 'title', `Submit ${branch}`),
|
|
@@ -359,29 +341,13 @@ export async function executeTask(ctx, task, signal) {
|
|
|
359
341
|
}
|
|
360
342
|
case 'convert-to-draft': {
|
|
361
343
|
const draftPrNumber = requireNumber(payload, 'pullRequestNumber');
|
|
362
|
-
|
|
363
|
-
// Use the GraphQL API via Octokit.
|
|
364
|
-
const { data: pr } = await ctx.octokit().pulls.get({
|
|
365
|
-
owner: ctx.githubOwner,
|
|
366
|
-
repo: ctx.githubRepo,
|
|
367
|
-
pull_number: draftPrNumber,
|
|
368
|
-
request: { signal },
|
|
369
|
-
});
|
|
370
|
-
await ctx
|
|
371
|
-
.octokit()
|
|
372
|
-
.graphql(`mutation($id: ID!) { convertPullRequestToDraft(input: { pullRequestId: $id }) { pullRequest { isDraft } } }`, { id: pr.node_id, request: { signal } });
|
|
344
|
+
await ctx.github().convertPullRequestToDraft(draftPrNumber, signal);
|
|
373
345
|
workerLog(`Converted PR #${draftPrNumber} to draft`);
|
|
374
346
|
return { prNumber: draftPrNumber, draft: true };
|
|
375
347
|
}
|
|
376
348
|
case 'close-pr': {
|
|
377
349
|
const closePrNumber = requireNumber(payload, 'pullRequestNumber');
|
|
378
|
-
await ctx.
|
|
379
|
-
owner: ctx.githubOwner,
|
|
380
|
-
repo: ctx.githubRepo,
|
|
381
|
-
pull_number: closePrNumber,
|
|
382
|
-
state: 'closed',
|
|
383
|
-
request: { signal },
|
|
384
|
-
});
|
|
350
|
+
await ctx.github().updatePullRequest(closePrNumber, { state: 'closed' }, signal);
|
|
385
351
|
return { closed: true };
|
|
386
352
|
}
|
|
387
353
|
case 'delete-remote-branch': {
|
|
@@ -421,12 +387,7 @@ export async function executeTask(ctx, task, signal) {
|
|
|
421
387
|
return { deleted: false, skipped: 'name-reused' };
|
|
422
388
|
}
|
|
423
389
|
try {
|
|
424
|
-
await ctx.
|
|
425
|
-
owner: ctx.githubOwner,
|
|
426
|
-
repo: ctx.githubRepo,
|
|
427
|
-
ref: `heads/${branch}`,
|
|
428
|
-
request: { signal },
|
|
429
|
-
});
|
|
390
|
+
await ctx.github().deleteBranch(branch, signal);
|
|
430
391
|
}
|
|
431
392
|
catch (err) {
|
|
432
393
|
// Already gone is the outcome this task wants; failing it would only park it in failed/.
|
|
@@ -565,15 +526,16 @@ export async function pushBranchToGitHub(ctx, branch, signal) {
|
|
|
565
526
|
// commit remote.git held when it was read, so what the marker logic at the end compares is
|
|
566
527
|
// what GitHub received.
|
|
567
528
|
await assertSharedRepoConfig(ctx.remoteGitPath, 'bare');
|
|
568
|
-
//
|
|
569
|
-
//
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
529
|
+
// Before `branch` reaches any git: `readPublishedSha` below would read a `<rev>:<path>` form as a
|
|
530
|
+
// tree lookup in remote.git. A refusal is permanent; a failure to run git is retried.
|
|
531
|
+
try {
|
|
532
|
+
await assertPlainBranchName(branch);
|
|
533
|
+
}
|
|
534
|
+
catch (err) {
|
|
535
|
+
if (err instanceof RefusedPushError)
|
|
536
|
+
throw new PermanentTaskError(err.message);
|
|
537
|
+
throw err;
|
|
538
|
+
}
|
|
577
539
|
// [SYNC-H1] If the rebase loop rewrote this branch's already-published
|
|
578
540
|
// history, GitHub still holds the commit it replaced, so an ordinary
|
|
579
541
|
// push is non-fast-forward forever. Push under a lease keyed to exactly
|
|
@@ -586,71 +548,49 @@ export async function pushBranchToGitHub(ctx, branch, signal) {
|
|
|
586
548
|
if (outgoingSha === null) {
|
|
587
549
|
throw new Error(`Branch "${branch}" is not in remote.git, so there is nothing to push`);
|
|
588
550
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
const message = getErrorMessage(err);
|
|
601
|
-
throwIfWorkflowRefusal(branch, message);
|
|
602
|
-
// A refused lease means GitHub is not at the commit we rewrote, so the
|
|
603
|
-
// marker is stale -- routine, not exceptional: tasks are re-run after a
|
|
604
|
-
// crash (recoverOrphanedTasks) and the marker survives any failure to
|
|
605
|
-
// clear it. The two benign shapes that reach here (GitHub already holds
|
|
606
|
-
// the rewritten history, or the branch moved past it) are ordinary
|
|
607
|
-
// fast-forwards a lease has no business blocking.
|
|
608
|
-
//
|
|
609
|
-
// So retry PLAIN and let git adjudicate: a non-forced push succeeds if and
|
|
610
|
-
// only if it fast-forwards, so it can never destroy anything, with no
|
|
611
|
-
// ancestry check to get wrong and no extra round trip to read GitHub's
|
|
612
|
-
// tip. Only if THAT is also rejected has the branch genuinely diverged.
|
|
613
|
-
//
|
|
614
|
-
// (git evaluates the lease only when it actually has an update to apply,
|
|
615
|
-
// so an up-to-date ref with a stale lease prints "Everything up-to-date",
|
|
616
|
-
// exits 0, and is absorbed above without reaching here.)
|
|
617
|
-
if (marker && isStaleLeaseRejection(message)) {
|
|
618
|
-
try {
|
|
619
|
-
await push();
|
|
620
|
-
}
|
|
621
|
-
catch (retryErr) {
|
|
622
|
-
const retryMessage = getErrorMessage(retryErr);
|
|
623
|
-
throwIfWorkflowRefusal(branch, retryMessage);
|
|
624
|
-
if (isNonFastForwardRejection(retryMessage)) {
|
|
625
|
-
throw new PermanentTaskError(`Push rejected for branch "${branch}": GitHub's tip is neither the commit this ` +
|
|
626
|
-
`deployment last published nor an ancestor of what it is pushing, so the branch ` +
|
|
627
|
-
`has genuinely diverged and nothing was overwritten. Something else moved it on ` +
|
|
628
|
-
`GitHub -- a direct push, or another CanopyCMS deployment sharing this repository.`);
|
|
629
|
-
}
|
|
630
|
-
throw retryErr;
|
|
631
|
-
}
|
|
632
|
-
return 'pushed-past-stale-lease';
|
|
633
|
-
}
|
|
634
|
-
// An ordinary non-fast-forward rejection: GitHub has commits this
|
|
635
|
-
// deployment never published, so retrying the identical push can never
|
|
636
|
-
// succeed (DEP-L1's git-failure-is-transient carve-out does NOT apply) and
|
|
637
|
-
// it fails fast instead of burning the retry budget. Deliberately does NOT
|
|
638
|
-
// advise renaming the branch: one reaching this point usually has an open
|
|
639
|
-
// PR, which renaming would orphan.
|
|
640
|
-
if (isNonFastForwardRejection(message)) {
|
|
641
|
-
throw new PermanentTaskError(`Push rejected for branch "${branch}": GitHub's tip is not what this deployment last ` +
|
|
642
|
-
`published, so the branch has diverged and needs reconciling. Something else moved it ` +
|
|
643
|
-
`on GitHub -- a direct push, or another CanopyCMS deployment sharing this repository.`);
|
|
644
|
-
}
|
|
551
|
+
let outcome;
|
|
552
|
+
try {
|
|
553
|
+
outcome = await ctx
|
|
554
|
+
.github()
|
|
555
|
+
.push({ branch, sha: outgoingSha, lease: marker, protectedBranches: [ctx.baseBranch] }, signal);
|
|
556
|
+
}
|
|
557
|
+
catch (err) {
|
|
558
|
+
// A retry can never make the branch not protected.
|
|
559
|
+
if (err instanceof RefusedPushError)
|
|
560
|
+
throw new PermanentTaskError(err.message);
|
|
561
|
+
if (!(err instanceof GitHubPushError))
|
|
645
562
|
throw err;
|
|
563
|
+
throwIfWorkflowRefusal(branch, err.message);
|
|
564
|
+
// A refused lease was already retried plain, and that retry rejected too: only a
|
|
565
|
+
// non-fast-forward then means the branch has genuinely diverged.
|
|
566
|
+
if (err.kind === 'rejected-after-stale-lease') {
|
|
567
|
+
if (isNonFastForwardRejection(err.message)) {
|
|
568
|
+
throw new PermanentTaskError(`Push rejected for branch "${branch}": GitHub's tip is neither the commit this ` +
|
|
569
|
+
`deployment last published nor an ancestor of what it is pushing, so the branch ` +
|
|
570
|
+
`has genuinely diverged and nothing was overwritten. Something else moved it on ` +
|
|
571
|
+
`GitHub -- a direct push, or another CanopyCMS deployment sharing this repository.`);
|
|
572
|
+
}
|
|
573
|
+
throw err.cause;
|
|
646
574
|
}
|
|
647
|
-
|
|
648
|
-
|
|
575
|
+
// An ordinary non-fast-forward rejection: GitHub has commits this
|
|
576
|
+
// deployment never published, so retrying the identical push can never
|
|
577
|
+
// succeed (DEP-L1's git-failure-is-transient carve-out does NOT apply) and
|
|
578
|
+
// it fails fast instead of burning the retry budget. Deliberately does NOT
|
|
579
|
+
// advise renaming the branch: one reaching this point usually has an open
|
|
580
|
+
// PR, which renaming would orphan.
|
|
581
|
+
if (isNonFastForwardRejection(err.message)) {
|
|
582
|
+
throw new PermanentTaskError(`Push rejected for branch "${branch}": GitHub's tip is not what this deployment last ` +
|
|
583
|
+
`published, so the branch has diverged and needs reconciling. Something else moved it ` +
|
|
584
|
+
`on GitHub -- a direct push, or another CanopyCMS deployment sharing this repository.`);
|
|
585
|
+
}
|
|
586
|
+
throw err.cause;
|
|
587
|
+
}
|
|
588
|
+
if (outcome.pastStaleLease) {
|
|
649
589
|
// The lease was refused, so GitHub is provably not at the marker: it
|
|
650
590
|
// has moved past the rewritten commit and the marker is spent.
|
|
651
591
|
await clearHistoryRewrittenMarker(ctx, branchPath, branch);
|
|
652
592
|
await recordPushedToGitHub(ctx, branchPath, branch);
|
|
653
|
-
|
|
593
|
+
logPushOutcome(ctx, branch, outcome, 'GitHub had already moved past the rewritten commit');
|
|
654
594
|
return;
|
|
655
595
|
}
|
|
656
596
|
// Clear the marker only once GitHub is confirmed to hold something other
|
|
@@ -661,7 +601,17 @@ export async function pushBranchToGitHub(ctx, branch, signal) {
|
|
|
661
601
|
await clearHistoryRewrittenMarker(ctx, branchPath, branch);
|
|
662
602
|
}
|
|
663
603
|
await recordPushedToGitHub(ctx, branchPath, branch);
|
|
664
|
-
|
|
604
|
+
logPushOutcome(ctx, branch, outcome);
|
|
605
|
+
}
|
|
606
|
+
/** An unmoved ref is routine (a task re-run after a crash), so it is logged at debug level. */
|
|
607
|
+
function logPushOutcome(ctx, branch, outcome, note) {
|
|
608
|
+
const suffix = note === undefined ? '' : `; ${note}`;
|
|
609
|
+
if (outcome.moved) {
|
|
610
|
+
workerLog(`Pushed ${branch} to GitHub (${describeMove(outcome)}${suffix})`);
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
ctx.log.debug(`${branch} already up to date on GitHub${suffix}`);
|
|
614
|
+
}
|
|
665
615
|
}
|
|
666
616
|
/**
|
|
667
617
|
* Stamp `pushedToGitHubAt`, branch delete's proof that the GitHub branch is this one. Recorded
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import type { Octokit } from '@octokit/rest';
|
|
2
1
|
import type { SanitizedBranchName } from '../paths/types.js';
|
|
3
2
|
import type { Task, TaskQueueLogger } from '../task-queue/cms-task-queue.js';
|
|
4
3
|
import type { WorkerStatusReport } from '../types.js';
|
|
5
|
-
import type {
|
|
4
|
+
import type { GitHubGateway } from './github-gateway.js';
|
|
6
5
|
/**
|
|
7
6
|
* The slice of {@link import('./cms-worker.js').CmsWorker} that its extracted
|
|
8
7
|
* clusters (task-runner.ts, git-sync.ts, rebase.ts, history-rewrite.ts) may
|
|
@@ -14,19 +13,16 @@ import type { GitHubMirror } from './github-mirror.js';
|
|
|
14
13
|
* back onto the live instance and never snapshotted, and `CmsWorker.ctx()`
|
|
15
14
|
* builds a fresh context per call. That is load-bearing, not stylistic. The
|
|
16
15
|
* `cms-worker*.test.ts` files drive the class by reaching through the instance:
|
|
17
|
-
* they
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* rebase test hooks. A context that captured any
|
|
21
|
-
* hand the extracted code the pre-test value
|
|
22
|
-
* a test's push going to github.com for real.
|
|
23
|
-
* the late binding visible at every call site,
|
|
16
|
+
* they INSTALL a GitHub gateway aimed at a local fixture repository (and an
|
|
17
|
+
* Octokit stub) with `useLocalGitHubGateway` (test-utils/worker-gateway.ts),
|
|
18
|
+
* REPLACE `executeTask` and `pushBranchToGitHub`, set `running` directly, and
|
|
19
|
+
* SUBCLASS to override the two rebase test hooks. A context that captured any
|
|
20
|
+
* of those at construction would hand the extracted code the pre-test value --
|
|
21
|
+
* which for the gateway means a test's push going to github.com for real.
|
|
22
|
+
* Functions (`ctx.github()`) make the late binding visible at every call site,
|
|
23
|
+
* which a getter would hide.
|
|
24
24
|
*/
|
|
25
25
|
export interface WorkerContext {
|
|
26
|
-
/** GitHub owner, e.g. 'safeinsights'. */
|
|
27
|
-
readonly githubOwner: string;
|
|
28
|
-
/** GitHub repo name. */
|
|
29
|
-
readonly githubRepo: string;
|
|
30
26
|
/**
|
|
31
27
|
* The base branch's RAW git ref name. Git refs (fetch/rev-list/merge against
|
|
32
28
|
* it) must use this; filesystem paths must use `sanitizedBaseBranch`.
|
|
@@ -60,35 +56,11 @@ export interface WorkerContext {
|
|
|
60
56
|
readonly maxRetries: number;
|
|
61
57
|
/** Task-queue debug logger. */
|
|
62
58
|
readonly log: TaskQueueLogger;
|
|
63
|
-
/** The worker's Octokit client, read at call time (tests replace it). */
|
|
64
|
-
octokit(): Octokit;
|
|
65
59
|
/**
|
|
66
|
-
* The
|
|
67
|
-
*
|
|
68
|
-
* auth the credential is minted on demand and lasts about an hour (see
|
|
69
|
-
* worker/github-auth.ts); the token path resolves immediately.
|
|
70
|
-
*
|
|
71
|
-
* A caller that needs the URL more than once resolves it ONCE into a local --
|
|
72
|
-
* see pushBranchToGitHub, where a second resolution inside the stale-lease
|
|
73
|
-
* catch would replace the very error being classified. Anything derived from
|
|
74
|
-
* it can embed the bot token, so a message reaching worker-status.json,
|
|
75
|
-
* branch.json or a task file goes through `redactCredentials` first.
|
|
60
|
+
* The worker's GitHub gateway (worker/github-gateway.ts), read at call time:
|
|
61
|
+
* every use of the GitHub credential and of Octokit goes through it.
|
|
76
62
|
*/
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* The worker's private GitHub mirror (worker/github-mirror.ts): every git command given a
|
|
80
|
-
* `buildGitHubUrl()` URL runs inside one of its sessions, never in `remote.git` or a clone.
|
|
81
|
-
*/
|
|
82
|
-
githubMirror(): GitHubMirror;
|
|
83
|
-
/**
|
|
84
|
-
* Re-read the GitHub credential because an operation that used it just
|
|
85
|
-
* failed, read at call time. Best-effort and NEVER throws: a failed read, or
|
|
86
|
-
* one that does not settle within `taskTimeoutMs`, is logged and swallowed,
|
|
87
|
-
* because every caller is already reporting the failure that matters. The
|
|
88
|
-
* next `buildGitHubUrl()`/`octokit()` sees any rotated value. See
|
|
89
|
-
* `CmsWorker.refreshGitHubCredential`.
|
|
90
|
-
*/
|
|
91
|
-
refreshGitHubCredential(): Promise<void>;
|
|
63
|
+
github(): GitHubGateway;
|
|
92
64
|
/**
|
|
93
65
|
* Workspace directory for a branch named by its GIT REF name — the form task
|
|
94
66
|
* payloads carry. Sanitizes; `feature/x` lives in `feature-x`.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"//": "@codemirror/language, @lezer/highlight: workaround — @mdxeditor/editor uses cm6-theme-basic-light which peer-requires these but mdxeditor doesn't declare them as dependencies",
|
|
3
3
|
"name": "canopycms",
|
|
4
|
-
"version": "0.0.68-int.
|
|
4
|
+
"version": "0.0.68-int.113",
|
|
5
5
|
"description": "CanopyCMS core package: schema-driven content, branch-aware editing, and editor UI for Next.js.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|