@straiffi/archon 1.7.8 → 1.7.9
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/client/assets/{ActivityView-DwfDSF4q.js → ActivityView-CDLQOXZm.js} +1 -1
- package/dist/client/assets/{GitHubActionsDialog-3dMCYCSZ.js → GitHubActionsDialog-DYxLX2HS.js} +1 -1
- package/dist/client/assets/{VerificationDialog-CoTfXMyb.js → VerificationDialog-DEKvZG75.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-Bdnc97XK.js → architectureDiagram-3BPJPVTR-ByNXcCIO.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-boxM3She.js → blockDiagram-GPEHLZMM-ByykLAaG.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-CJDdF7xd.js → c4Diagram-AAUBKEIU-CmWA4Pli.js} +1 -1
- package/dist/client/assets/channel-BvcRv90i.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-ClpcO-Om.js → chunk-2J33WTMH-CTgbXRh4.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-CcuL_65J.js → chunk-3OPIFGDE-D3FajVfq.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-CUGYZXkH.js → chunk-5ZQYHXKU-CUMg_Vfo.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-CApeLDyK.js → chunk-727SXJPM-Oc9MgxZD.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-8FdSvF2D.js → chunk-AQP2D5EJ-5JZboHeP.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-CZoIOF3a.js → chunk-CSCIHK7Q-BpkSMHjB.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-BigFQI5y.js → chunk-KSCS5N6A-BPBMAswO.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-DIi3SHaT.js → chunk-L5ZTLDWV-BA2sHmu7.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-B-Z0BcQy.js → chunk-LZXEDZCA-Blc9q-fQ.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-EBeba0mg.js → chunk-ND2GUHAM-GlVS74JP.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-DJBI2y53.js → chunk-NZK2D7GU-fctBFnAm.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-BqxdXhMd.js → chunk-O5CBEL6O-eGs4DDLJ.js} +1 -1
- package/dist/client/assets/{chunk-WU5MYG2G-Coe1_Cbb.js → chunk-WU5MYG2G-vxa50E7g.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-IuovJyxl.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BBJ3avX6.js +1 -0
- package/dist/client/assets/{dagre-BM42HDAG-tuTmeA0Y.js → dagre-BM42HDAG-DXGXNVUp.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-ILGCpG2F.js → diagram-2AECGRRQ-BRYr1GvB.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-C8qBZsgK.js → diagram-5GNKFQAL-BcwgMag4.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-BXlcm_Zo.js → diagram-KO2AKTUF-Delk7lbr.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-B8V5EVbD.js → diagram-LMA3HP47-BVtc_fjo.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-CWsdOqcP.js → diagram-OG6HWLK6-weIPvUz5.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-DW8pgly2.js → erDiagram-TEJ5UH35-DKde70dA.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-Djn7fOVR.js → flowDiagram-I6XJVG4X-B7Jrk-6c.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-DZ421MEy.js → ganttDiagram-6RSMTGT7-CDzWDgnA.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CCNzLPuP.js → gitGraphDiagram-PVQCEYII-DYZCryuN.js} +1 -1
- package/dist/client/assets/index-BlKOx9Vf.css +2 -0
- package/dist/client/assets/index-CmaBDvIB.js +273 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-Bd1gYgMp.js → infoDiagram-5YYISTIA-DmQOgVKK.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-txyMoatJ.js → ishikawaDiagram-YF4QCWOH-DCpu-q-Z.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-DhT3bmX6.js → journeyDiagram-JHISSGLW-DhBoglhI.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-BBHt2hfC.js → kanban-definition-UN3LZRKU-BSjvGFTY.js} +1 -1
- package/dist/client/assets/{line-KGRcT7Qz.js → line-ClKBZwDr.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-CDzt4_zR.js → mermaid-parser.core-BRA4mJ40.js} +1 -1
- package/dist/client/assets/{mermaid.core-DD_-k1aB.js → mermaid.core-BZ7kSfEt.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-DoctVTDl.js → mindmap-definition-RKZ34NQL-DoUdLHgj.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-DFlLEi70.js → pieDiagram-4H26LBE5-NVnzqltb.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-3YHOQgl7.js → quadrantDiagram-W4KKPZXB-DOITgkan.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-X4fZVL9M.js → requirementDiagram-4Y6WPE33-D0VPxrFE.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BUPnMXeG.js → sankeyDiagram-5OEKKPKP-Bx0uU7xV.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-D6QU3M64.js → sequenceDiagram-3UESZ5HK-B4lFL6cO.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-c35bu4qZ.js → stateDiagram-AJRCARHV-CsLVsMZ4.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CvrvaIdF.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-CX2y6e_b.js → timeline-definition-PNZ67QCA-BkcYpPFI.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-BDaWltjF.js → vennDiagram-CIIHVFJN-Xc5f2sRQ.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-BpBRL2sb.js → wardleyDiagram-YWT4CUSO-n3Dpk02Q.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-Bqw7X0UT.js → xychartDiagram-2RQKCTM6-BTMzF8zg.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +8 -6
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +224 -11
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/automationCheckGate.js +82 -7
- package/dist/server/lib/automationCheckGate.js.map +1 -1
- package/dist/server/lib/automationReconciliation.js +19 -0
- package/dist/server/lib/automationReconciliation.js.map +1 -1
- package/dist/server/lib/automationResumeDecision.js +57 -0
- package/dist/server/lib/automationResumeDecision.js.map +1 -1
- package/dist/server/lib/automationStageDetails.js +98 -5
- package/dist/server/lib/automationStageDetails.js.map +1 -1
- package/dist/server/lib/buildCompletionContinuation.js +109 -0
- package/dist/server/lib/buildCompletionContinuation.js.map +1 -0
- package/dist/server/lib/buildSequences.js +81 -0
- package/dist/server/lib/buildSequences.js.map +1 -1
- package/dist/server/lib/buildTicketCommit.js +219 -0
- package/dist/server/lib/buildTicketCommit.js.map +1 -0
- package/dist/server/lib/bundleAutomationReadiness.js +7 -0
- package/dist/server/lib/bundleAutomationReadiness.js.map +1 -0
- package/dist/server/lib/bundleReviewOperations.js +17 -5
- package/dist/server/lib/bundleReviewOperations.js.map +1 -1
- package/dist/server/lib/chatOperations.js +2 -2
- package/dist/server/lib/chatOperations.js.map +1 -1
- package/dist/server/lib/checkGateEvaluations.js +1 -0
- package/dist/server/lib/checkGateEvaluations.js.map +1 -1
- package/dist/server/lib/projects.js +13 -1
- package/dist/server/lib/projects.js.map +1 -1
- package/dist/server/lib/requiredCheckPolicy.js +35 -10
- package/dist/server/lib/requiredCheckPolicy.js.map +1 -1
- package/dist/server/lib/ticketAutomationCommit.js +250 -160
- package/dist/server/lib/ticketAutomationCommit.js.map +1 -1
- package/dist/server/lib/types.js.map +1 -1
- package/dist/server/workers/build.js +50 -81
- package/dist/server/workers/build.js.map +1 -1
- package/dist/server/workers/chat.js +13 -6
- package/dist/server/workers/chat.js.map +1 -1
- package/dist/server/workers/followUp.js +53 -30
- package/dist/server/workers/followUp.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/channel-BGqdAcIT.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-D5VuKRcj.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-B9GNqpRr.js +0 -1
- package/dist/client/assets/index-BRc02lbW.js +0 -273
- package/dist/client/assets/index-CZO9HL1Z.css +0 -2
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Ymy1_EHr.js +0 -1
|
@@ -12,7 +12,7 @@ import { getGitHubConnectionConfig } from './integrations/index.js';
|
|
|
12
12
|
import { upsertBundlePullRequestFromGitHub, } from './bundlePullRequests.js';
|
|
13
13
|
import { depthAtLeast, getBundleAutomationState, resolveAutomationConfig, } from './ticketAutomation.js';
|
|
14
14
|
import { completeAutomationProgressionInTransaction, completeAutomationProgression, getAutomationProgression, waitAutomationProgressionInTransaction, waitAutomationProgression, } from './automationProgression.js';
|
|
15
|
-
import { assertAutomationAuthorizationCurrent, authorizeAutomationBoundary, AutomationCheckGateError, } from './automationCheckGate.js';
|
|
15
|
+
import { assertAutomationBoundaryNotRequired, assertAutomationAuthorizationCurrent, authorizeAutomationBoundary, AutomationCheckGateError, } from './automationCheckGate.js';
|
|
16
16
|
import { AUTOMATION_STAGE_DETAILS_SCHEMA_VERSION, createAutomationStageDetails, ensureAutomationStageResult, getAutomationStageDetails, } from './automationStageDetails.js';
|
|
17
17
|
import { getProjectById } from './projects.js';
|
|
18
18
|
import { getExecutionRun, withExecutionRunTransaction } from './executionRuns.js';
|
|
@@ -160,7 +160,37 @@ const loadLiveState = (projectId, bundleId) => {
|
|
|
160
160
|
}
|
|
161
161
|
return { project, bundle, automationConfig: resolveAutomationConfig(bundle, project) };
|
|
162
162
|
};
|
|
163
|
-
const
|
|
163
|
+
const toStageCheckAuthorization = (authorization) => authorization.requirement === 'required'
|
|
164
|
+
? {
|
|
165
|
+
kind: 'required',
|
|
166
|
+
gateEvaluationId: authorization.evaluation.id,
|
|
167
|
+
policyFingerprint: authorization.policyFingerprint,
|
|
168
|
+
workspaceFingerprint: authorization.workspaceIdentity.fingerprint,
|
|
169
|
+
}
|
|
170
|
+
: {
|
|
171
|
+
kind: 'not_required',
|
|
172
|
+
policyFingerprint: authorization.policyFingerprint,
|
|
173
|
+
};
|
|
174
|
+
const assertBoundaryAuthorizationCurrent = async ({ authorization, project, bundle, progressionRunId, cwd, boundaryKind, }) => {
|
|
175
|
+
if (authorization.requirement === 'required') {
|
|
176
|
+
await assertAutomationAuthorizationCurrent({
|
|
177
|
+
project,
|
|
178
|
+
bundle,
|
|
179
|
+
progressionRunId,
|
|
180
|
+
cwd,
|
|
181
|
+
evaluationId: authorization.evaluation.id,
|
|
182
|
+
boundaryKind,
|
|
183
|
+
});
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
assertAutomationBoundaryNotRequired({
|
|
187
|
+
project,
|
|
188
|
+
bundle,
|
|
189
|
+
progressionRunId,
|
|
190
|
+
policyFingerprint: authorization.policyFingerprint,
|
|
191
|
+
});
|
|
192
|
+
};
|
|
193
|
+
const assertStageCanMutateWithPolicy = (projectId, bundleId, progressionRunId, ticket, mutationLabel, canMutateLive) => {
|
|
164
194
|
const live = loadLiveState(projectId, bundleId);
|
|
165
195
|
const progression = getAutomationProgression(projectId, bundleId);
|
|
166
196
|
const state = getBundleAutomationState(projectId, bundleId);
|
|
@@ -169,12 +199,12 @@ const assertStageCanMutate = (projectId, bundleId, progressionRunId, requiredDep
|
|
|
169
199
|
|| progression.status !== 'running'
|
|
170
200
|
|| state?.automation_progression_run_id !== progressionRunId
|
|
171
201
|
|| state.automation_paused_reason !== null
|
|
172
|
-
|| !
|
|
173
|
-
|
|
174
|
-
throw new Error(`automation no longer permits ${mutation} mutation`);
|
|
202
|
+
|| !canMutateLive(live, { ticket, progressionRunId })) {
|
|
203
|
+
throw new Error(`automation no longer permits ${mutationLabel} mutation`);
|
|
175
204
|
}
|
|
176
205
|
return live;
|
|
177
206
|
};
|
|
207
|
+
const assertStageCanMutate = (projectId, bundleId, progressionRunId, ticket, requiredDepth) => assertStageCanMutateWithPolicy(projectId, bundleId, progressionRunId, ticket, requiredDepth === 'auto_commit' ? 'commit' : requiredDepth === 'auto_push' ? 'push' : 'pull-request', live => depthAtLeast(live.automationConfig.depth, requiredDepth));
|
|
178
208
|
const githubConfigMatchesIntent = (githubConfig, intent) => (githubConfig.host.toLowerCase() === intent.repoHost.toLowerCase()
|
|
179
209
|
&& githubConfig.repo_owner.toLowerCase() === intent.repoOwner.toLowerCase()
|
|
180
210
|
&& githubConfig.repo_name.toLowerCase() === intent.repoName.toLowerCase()
|
|
@@ -322,6 +352,181 @@ const pauseBeforeStage = ({ projectId, bundleId, progressionRunId, pauseCode, re
|
|
|
322
352
|
pausedStageRunId: null,
|
|
323
353
|
broadcaster,
|
|
324
354
|
});
|
|
355
|
+
export const runAutomationCommitOperation = async ({ ticket, io, progressionRunId, allowedActiveChildRunId, retryOfRunId, mutationLabel, canCommitLive, shouldCompleteProgression, failureMode = 'return', requiredProgressionPurpose, }) => {
|
|
356
|
+
if (!ticket.project_id || !ticket.worktree_bundle_id) {
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
const projectId = ticket.project_id;
|
|
360
|
+
const bundleId = ticket.worktree_bundle_id;
|
|
361
|
+
const progression = getAutomationProgression(projectId, bundleId);
|
|
362
|
+
if (!progression || progression.id !== progressionRunId) {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
const broadcaster = asBroadcaster(io);
|
|
366
|
+
let live = loadLiveState(projectId, bundleId);
|
|
367
|
+
if (!canCommitLive(live, { ticket, progressionRunId })) {
|
|
368
|
+
completeAutomationProgression({ projectId, bundleId, progressionRunId, broadcaster });
|
|
369
|
+
emitUpdated(io, ticket.id);
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
const branch = live.bundle.branch;
|
|
373
|
+
const cwd = live.bundle.kind === 'project_root'
|
|
374
|
+
? live.project.repo_path
|
|
375
|
+
: resolveWorktreePath(branch, live.project);
|
|
376
|
+
let commitRunId = null;
|
|
377
|
+
try {
|
|
378
|
+
const status = getGitStatus(cwd, branch, live.project);
|
|
379
|
+
const precondition = evaluateCommitPrecondition(status);
|
|
380
|
+
if (!precondition.ok) {
|
|
381
|
+
throw new Error(precondition.reason);
|
|
382
|
+
}
|
|
383
|
+
if (status.files.length === 0) {
|
|
384
|
+
const authorization = await authorizeAutomationBoundary({
|
|
385
|
+
project: live.project,
|
|
386
|
+
bundle: live.bundle,
|
|
387
|
+
progressionRunId,
|
|
388
|
+
cwd,
|
|
389
|
+
boundaryKind: 'automation_commit',
|
|
390
|
+
requiredProgressionPurpose,
|
|
391
|
+
broadcaster,
|
|
392
|
+
prepare: () => {
|
|
393
|
+
const head = getGitHeadIdentity(cwd);
|
|
394
|
+
return { head, indexTreeSha: getGitIndexTreeSha(cwd) };
|
|
395
|
+
},
|
|
396
|
+
resolveIntendedIdentity: (_value, workspaceIdentity) => ({
|
|
397
|
+
kind: 'workspace', fingerprint: workspaceIdentity.fingerprint,
|
|
398
|
+
}),
|
|
399
|
+
});
|
|
400
|
+
const { head, indexTreeSha } = authorization.value;
|
|
401
|
+
const intent = {
|
|
402
|
+
branch,
|
|
403
|
+
preActionHeadSha: head.headSha,
|
|
404
|
+
preActionIndexTreeSha: indexTreeSha,
|
|
405
|
+
intendedTreeSha: head.treeSha,
|
|
406
|
+
commitMessageDigest: EMPTY_MESSAGE_DIGEST,
|
|
407
|
+
selectedContentDigest: computeGitTreeDeltaDigest(cwd, head.headSha, head.treeSha),
|
|
408
|
+
checkAuthorization: toStageCheckAuthorization(authorization),
|
|
409
|
+
};
|
|
410
|
+
commitRunId = startStage({
|
|
411
|
+
projectId,
|
|
412
|
+
bundleId,
|
|
413
|
+
ticketId: ticket.id,
|
|
414
|
+
ticketTitle: ticket.title,
|
|
415
|
+
progressionRunId,
|
|
416
|
+
allowedActiveChildRunId,
|
|
417
|
+
stageKind: 'automation_commit',
|
|
418
|
+
intent,
|
|
419
|
+
broadcaster,
|
|
420
|
+
retryOfRunId,
|
|
421
|
+
});
|
|
422
|
+
await assertBoundaryAuthorizationCurrent({ authorization, project: live.project, bundle: live.bundle, progressionRunId, cwd, boundaryKind: 'automation_commit' });
|
|
423
|
+
live = loadLiveState(projectId, bundleId);
|
|
424
|
+
const result = {
|
|
425
|
+
outcome: 'no_changes',
|
|
426
|
+
headSha: head.headSha,
|
|
427
|
+
treeSha: head.treeSha,
|
|
428
|
+
};
|
|
429
|
+
finishStage({
|
|
430
|
+
projectId,
|
|
431
|
+
bundleId,
|
|
432
|
+
progressionRunId,
|
|
433
|
+
runId: commitRunId,
|
|
434
|
+
stageKind: 'automation_commit',
|
|
435
|
+
result,
|
|
436
|
+
outcome: 'no_changes',
|
|
437
|
+
completeProgression: shouldCompleteProgression(live, result),
|
|
438
|
+
broadcaster,
|
|
439
|
+
});
|
|
440
|
+
return { runId: commitRunId, ...result };
|
|
441
|
+
}
|
|
442
|
+
const selectedPaths = getAutomationCommitPaths(status.files);
|
|
443
|
+
const message = await generateCommitMessage(cwd, live.project, selectedPaths);
|
|
444
|
+
live = loadLiveState(projectId, bundleId);
|
|
445
|
+
if (!message) {
|
|
446
|
+
throw new Error('commit message generation produced an empty result');
|
|
447
|
+
}
|
|
448
|
+
if (!canCommitLive(live, { ticket, progressionRunId })) {
|
|
449
|
+
completeAutomationProgression({ projectId, bundleId, progressionRunId, broadcaster });
|
|
450
|
+
emitUpdated(io, ticket.id);
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
const authorization = await authorizeAutomationBoundary({
|
|
454
|
+
project: live.project,
|
|
455
|
+
bundle: live.bundle,
|
|
456
|
+
progressionRunId,
|
|
457
|
+
cwd,
|
|
458
|
+
boundaryKind: 'automation_commit',
|
|
459
|
+
requiredProgressionPurpose,
|
|
460
|
+
broadcaster,
|
|
461
|
+
prepare: () => prepareAutomationCommit(cwd, branch, live.project, selectedPaths),
|
|
462
|
+
resolveIntendedIdentity: (_value, workspaceIdentity) => ({
|
|
463
|
+
kind: 'workspace', fingerprint: workspaceIdentity.fingerprint,
|
|
464
|
+
}),
|
|
465
|
+
});
|
|
466
|
+
const prepared = authorization.value;
|
|
467
|
+
const intent = {
|
|
468
|
+
branch,
|
|
469
|
+
preActionHeadSha: prepared.preActionHeadSha,
|
|
470
|
+
preActionIndexTreeSha: prepared.preActionIndexTreeSha,
|
|
471
|
+
intendedTreeSha: prepared.intendedTreeSha,
|
|
472
|
+
commitMessageDigest: digestCommitMessage(message),
|
|
473
|
+
selectedContentDigest: prepared.selectedContentDigest,
|
|
474
|
+
checkAuthorization: toStageCheckAuthorization(authorization),
|
|
475
|
+
};
|
|
476
|
+
commitRunId = startStage({
|
|
477
|
+
projectId,
|
|
478
|
+
bundleId,
|
|
479
|
+
ticketId: ticket.id,
|
|
480
|
+
ticketTitle: ticket.title,
|
|
481
|
+
progressionRunId,
|
|
482
|
+
allowedActiveChildRunId,
|
|
483
|
+
stageKind: 'automation_commit',
|
|
484
|
+
intent,
|
|
485
|
+
broadcaster,
|
|
486
|
+
retryOfRunId,
|
|
487
|
+
});
|
|
488
|
+
await assertBoundaryAuthorizationCurrent({ authorization, project: live.project, bundle: live.bundle, progressionRunId, cwd, boundaryKind: 'automation_commit' });
|
|
489
|
+
live = assertStageCanMutateWithPolicy(projectId, bundleId, progressionRunId, ticket, mutationLabel, canCommitLive);
|
|
490
|
+
const result = await commitPreparedAutomationChanges(cwd, branch, live.project, message, prepared);
|
|
491
|
+
live = loadLiveState(projectId, bundleId);
|
|
492
|
+
finishStage({
|
|
493
|
+
projectId,
|
|
494
|
+
bundleId,
|
|
495
|
+
progressionRunId,
|
|
496
|
+
runId: commitRunId,
|
|
497
|
+
stageKind: 'automation_commit',
|
|
498
|
+
result,
|
|
499
|
+
outcome: 'committed',
|
|
500
|
+
completeProgression: shouldCompleteProgression(live, result),
|
|
501
|
+
broadcaster,
|
|
502
|
+
});
|
|
503
|
+
return { outcome: 'committed', runId: commitRunId, ...result };
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
if (error instanceof AutomationCheckGateError) {
|
|
507
|
+
if (commitRunId) {
|
|
508
|
+
failStageAuthorization({ projectId, bundleId, progressionRunId, runId: commitRunId, stageKind: 'automation_commit', error, broadcaster });
|
|
509
|
+
}
|
|
510
|
+
emitBundleUpdated(io, ticket);
|
|
511
|
+
if (failureMode === 'throw') {
|
|
512
|
+
throw error;
|
|
513
|
+
}
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
const reason = `commit failed: ${getErrorMessage(error)}`;
|
|
517
|
+
if (commitRunId) {
|
|
518
|
+
failStage({ projectId, bundleId, progressionRunId, runId: commitRunId, stageKind: 'automation_commit', pauseCode: 'commit', reason, ambiguous: true, broadcaster });
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
pauseBeforeStage({ projectId, bundleId, progressionRunId, pauseCode: 'commit', reason, broadcaster });
|
|
522
|
+
}
|
|
523
|
+
emitUpdated(io, ticket.id);
|
|
524
|
+
if (failureMode === 'throw') {
|
|
525
|
+
throw error;
|
|
526
|
+
}
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
};
|
|
325
530
|
const remoteStatesEqual = (left, right) => (left.state === right.state && (left.state === 'absent' || (right.state === 'observed' && left.sha === right.sha)));
|
|
326
531
|
export const runAutomationCommitChain = async (ticket, _project, _bundle, _automationConfig, io, progressionRunId, resumeFrom, triggeringReviewRunId) => {
|
|
327
532
|
if (!ticket.project_id || !ticket.worktree_bundle_id) {
|
|
@@ -342,143 +547,30 @@ export const runAutomationCommitChain = async (ticket, _project, _bundle, _autom
|
|
|
342
547
|
|| (resumeFrom.stageKind === 'automation_push' && !isRetry)));
|
|
343
548
|
let expectedHeadSha = resumeFrom?.expectedHeadSha ?? null;
|
|
344
549
|
let live = loadLiveState(projectId, bundleId);
|
|
345
|
-
if (!depthAtLeast(live.automationConfig.depth, 'auto_commit')) {
|
|
346
|
-
completeAutomationProgression({ projectId, bundleId, progressionRunId, broadcaster });
|
|
347
|
-
emitUpdated(io, ticket.id);
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
550
|
const branch = live.bundle.branch;
|
|
351
551
|
const cwd = live.bundle.kind === 'project_root'
|
|
352
552
|
? live.project.repo_path
|
|
353
553
|
: resolveWorktreePath(branch, live.project);
|
|
354
|
-
let commitRunId = null;
|
|
355
554
|
if (!skipCommit) {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
cwd,
|
|
368
|
-
boundaryKind: 'automation_commit',
|
|
369
|
-
broadcaster,
|
|
370
|
-
resolveIntendedIdentity: workspaceIdentity => {
|
|
371
|
-
const head = getGitHeadIdentity(cwd);
|
|
372
|
-
return {
|
|
373
|
-
intendedIdentity: { kind: 'git_tree', treeSha: head.treeSha, workspaceFingerprint: workspaceIdentity.fingerprint },
|
|
374
|
-
value: { head, indexTreeSha: getGitIndexTreeSha(cwd) },
|
|
375
|
-
};
|
|
376
|
-
},
|
|
377
|
-
});
|
|
378
|
-
const { head, indexTreeSha } = authorization.value;
|
|
379
|
-
const intent = {
|
|
380
|
-
branch,
|
|
381
|
-
preActionHeadSha: head.headSha,
|
|
382
|
-
preActionIndexTreeSha: indexTreeSha,
|
|
383
|
-
intendedTreeSha: head.treeSha,
|
|
384
|
-
commitMessageDigest: EMPTY_MESSAGE_DIGEST,
|
|
385
|
-
selectedContentDigest: computeGitTreeDeltaDigest(cwd, head.headSha, head.treeSha),
|
|
386
|
-
gateEvaluationId: authorization.evaluation.id,
|
|
387
|
-
policyFingerprint: authorization.policyFingerprint,
|
|
388
|
-
workspaceFingerprint: authorization.workspaceIdentity.fingerprint,
|
|
389
|
-
};
|
|
390
|
-
commitRunId = startStage({ projectId, bundleId, ticketId: ticket.id, ticketTitle: ticket.title, progressionRunId, allowedActiveChildRunId: triggeringReviewRunId, stageKind: 'automation_commit', intent, broadcaster, retryOfRunId: resumeFrom?.stageKind === 'automation_commit' ? resumeFrom.retryOfRunId : undefined });
|
|
391
|
-
await assertAutomationAuthorizationCurrent({ project: live.project, bundle: live.bundle, progressionRunId, cwd, evaluationId: authorization.evaluation.id, boundaryKind: 'automation_commit' });
|
|
392
|
-
live = loadLiveState(projectId, bundleId);
|
|
393
|
-
finishStage({
|
|
394
|
-
projectId,
|
|
395
|
-
bundleId,
|
|
396
|
-
progressionRunId,
|
|
397
|
-
runId: commitRunId,
|
|
398
|
-
stageKind: 'automation_commit',
|
|
399
|
-
result: { outcome: 'no_changes', headSha: head.headSha, treeSha: head.treeSha },
|
|
400
|
-
outcome: 'no_changes',
|
|
401
|
-
completeProgression: !depthAtLeast(live.automationConfig.depth, 'auto_push'),
|
|
402
|
-
broadcaster,
|
|
403
|
-
});
|
|
404
|
-
expectedHeadSha = head.headSha;
|
|
405
|
-
}
|
|
406
|
-
else {
|
|
407
|
-
const selectedPaths = getAutomationCommitPaths(status.files);
|
|
408
|
-
const message = await generateCommitMessage(cwd, live.project, selectedPaths);
|
|
409
|
-
live = loadLiveState(projectId, bundleId);
|
|
410
|
-
if (!message) {
|
|
411
|
-
throw new Error('commit message generation produced an empty result');
|
|
412
|
-
}
|
|
413
|
-
if (!depthAtLeast(live.automationConfig.depth, 'auto_commit')) {
|
|
414
|
-
completeAutomationProgression({ projectId, bundleId, progressionRunId, broadcaster });
|
|
415
|
-
emitUpdated(io, ticket.id);
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
const authorization = await authorizeAutomationBoundary({
|
|
419
|
-
project: live.project,
|
|
420
|
-
bundle: live.bundle,
|
|
421
|
-
progressionRunId,
|
|
422
|
-
cwd,
|
|
423
|
-
boundaryKind: 'automation_commit',
|
|
424
|
-
broadcaster,
|
|
425
|
-
resolveIntendedIdentity: workspaceIdentity => {
|
|
426
|
-
const prepared = prepareAutomationCommit(cwd, branch, live.project, selectedPaths);
|
|
427
|
-
return {
|
|
428
|
-
intendedIdentity: { kind: 'git_tree', treeSha: prepared.intendedTreeSha, workspaceFingerprint: workspaceIdentity.fingerprint },
|
|
429
|
-
value: prepared,
|
|
430
|
-
};
|
|
431
|
-
},
|
|
432
|
-
});
|
|
433
|
-
const prepared = authorization.value;
|
|
434
|
-
const intent = {
|
|
435
|
-
branch,
|
|
436
|
-
preActionHeadSha: prepared.preActionHeadSha,
|
|
437
|
-
preActionIndexTreeSha: prepared.preActionIndexTreeSha,
|
|
438
|
-
intendedTreeSha: prepared.intendedTreeSha,
|
|
439
|
-
commitMessageDigest: digestCommitMessage(message),
|
|
440
|
-
selectedContentDigest: prepared.selectedContentDigest,
|
|
441
|
-
gateEvaluationId: authorization.evaluation.id,
|
|
442
|
-
policyFingerprint: authorization.policyFingerprint,
|
|
443
|
-
workspaceFingerprint: authorization.workspaceIdentity.fingerprint,
|
|
444
|
-
};
|
|
445
|
-
commitRunId = startStage({ projectId, bundleId, ticketId: ticket.id, ticketTitle: ticket.title, progressionRunId, allowedActiveChildRunId: triggeringReviewRunId, stageKind: 'automation_commit', intent, broadcaster, retryOfRunId: resumeFrom?.stageKind === 'automation_commit' ? resumeFrom.retryOfRunId : undefined });
|
|
446
|
-
await assertAutomationAuthorizationCurrent({ project: live.project, bundle: live.bundle, progressionRunId, cwd, evaluationId: authorization.evaluation.id, boundaryKind: 'automation_commit' });
|
|
447
|
-
live = assertStageCanMutate(projectId, bundleId, progressionRunId, 'auto_commit');
|
|
448
|
-
const result = await commitPreparedAutomationChanges(cwd, branch, live.project, message, prepared);
|
|
449
|
-
live = loadLiveState(projectId, bundleId);
|
|
450
|
-
finishStage({
|
|
451
|
-
projectId,
|
|
452
|
-
bundleId,
|
|
453
|
-
progressionRunId,
|
|
454
|
-
runId: commitRunId,
|
|
455
|
-
stageKind: 'automation_commit',
|
|
456
|
-
result,
|
|
457
|
-
outcome: 'committed',
|
|
458
|
-
completeProgression: !depthAtLeast(live.automationConfig.depth, 'auto_push'),
|
|
459
|
-
broadcaster,
|
|
460
|
-
});
|
|
461
|
-
expectedHeadSha = result.commitSha;
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
catch (error) {
|
|
465
|
-
if (error instanceof AutomationCheckGateError) {
|
|
466
|
-
if (commitRunId) {
|
|
467
|
-
failStageAuthorization({ projectId, bundleId, progressionRunId, runId: commitRunId, stageKind: 'automation_commit', error, broadcaster });
|
|
468
|
-
}
|
|
469
|
-
emitBundleUpdated(io, ticket);
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
472
|
-
const reason = `commit failed: ${getErrorMessage(error)}`;
|
|
473
|
-
if (commitRunId) {
|
|
474
|
-
failStage({ projectId, bundleId, progressionRunId, runId: commitRunId, stageKind: 'automation_commit', pauseCode: 'commit', reason, ambiguous: true, broadcaster });
|
|
475
|
-
}
|
|
476
|
-
else {
|
|
477
|
-
pauseBeforeStage({ projectId, bundleId, progressionRunId, pauseCode: 'commit', reason, broadcaster });
|
|
478
|
-
}
|
|
479
|
-
emitUpdated(io, ticket.id);
|
|
555
|
+
const commitResult = await runAutomationCommitOperation({
|
|
556
|
+
ticket,
|
|
557
|
+
io,
|
|
558
|
+
progressionRunId,
|
|
559
|
+
allowedActiveChildRunId: triggeringReviewRunId,
|
|
560
|
+
retryOfRunId: resumeFrom?.stageKind === 'automation_commit' ? resumeFrom.retryOfRunId : undefined,
|
|
561
|
+
mutationLabel: 'commit',
|
|
562
|
+
canCommitLive: current => depthAtLeast(current.automationConfig.depth, 'auto_commit'),
|
|
563
|
+
shouldCompleteProgression: current => !depthAtLeast(current.automationConfig.depth, 'auto_push'),
|
|
564
|
+
});
|
|
565
|
+
if (!commitResult) {
|
|
480
566
|
return;
|
|
481
567
|
}
|
|
568
|
+
if (commitResult.outcome === 'committed') {
|
|
569
|
+
expectedHeadSha = commitResult.commitSha;
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
expectedHeadSha = commitResult.headSha;
|
|
573
|
+
}
|
|
482
574
|
}
|
|
483
575
|
live = loadLiveState(projectId, bundleId);
|
|
484
576
|
if (getAutomationProgression(projectId, bundleId)?.id !== progressionRunId) {
|
|
@@ -515,24 +607,23 @@ export const runAutomationCommitChain = async (ticket, _project, _bundle, _autom
|
|
|
515
607
|
cwd,
|
|
516
608
|
boundaryKind: 'automation_push',
|
|
517
609
|
broadcaster,
|
|
518
|
-
|
|
610
|
+
prepare: () => {
|
|
519
611
|
const head = getGitHeadIdentity(cwd);
|
|
520
612
|
if (head.headSha !== target) {
|
|
521
613
|
throw new Error('HEAD changed while required checks were evaluated for push');
|
|
522
614
|
}
|
|
523
|
-
return
|
|
524
|
-
intendedIdentity: { kind: 'commit', commitSha: target, workspaceFingerprint: workspaceIdentity.fingerprint },
|
|
525
|
-
value: head,
|
|
526
|
-
};
|
|
615
|
+
return head;
|
|
527
616
|
},
|
|
617
|
+
resolveIntendedIdentity: (_value, workspaceIdentity) => ({
|
|
618
|
+
kind: 'commit', commitSha: target, workspaceFingerprint: workspaceIdentity.fingerprint,
|
|
619
|
+
}),
|
|
528
620
|
});
|
|
529
621
|
const intent = {
|
|
530
622
|
branch,
|
|
531
623
|
originIdentity,
|
|
532
624
|
targetLocalSha: target,
|
|
533
625
|
preActionRemote,
|
|
534
|
-
|
|
535
|
-
policyFingerprint: authorization.policyFingerprint,
|
|
626
|
+
checkAuthorization: toStageCheckAuthorization(authorization),
|
|
536
627
|
intendedTreeSha: authorization.value.treeSha,
|
|
537
628
|
resultingCommitSha: target,
|
|
538
629
|
};
|
|
@@ -540,14 +631,14 @@ export const runAutomationCommitChain = async (ticket, _project, _bundle, _autom
|
|
|
540
631
|
const current = await fetchRemoteBranchState(cwd, branch, originIdentity);
|
|
541
632
|
live = loadLiveState(projectId, bundleId);
|
|
542
633
|
if (current.state === 'observed' && current.sha === target) {
|
|
543
|
-
await
|
|
544
|
-
live = assertStageCanMutate(projectId, bundleId, progressionRunId, 'auto_push');
|
|
634
|
+
await assertBoundaryAuthorizationCurrent({ authorization, project: live.project, bundle: live.bundle, progressionRunId, cwd, boundaryKind: 'automation_push' });
|
|
635
|
+
live = assertStageCanMutate(projectId, bundleId, progressionRunId, ticket, 'auto_push');
|
|
545
636
|
finishStage({ projectId, bundleId, progressionRunId, runId: pushRunId, stageKind: 'automation_push', result: { remoteSha: target }, outcome: 'already_current', completeProgression: !depthAtLeast(live.automationConfig.depth, 'auto_pr'), broadcaster });
|
|
546
637
|
expectedHeadSha = target;
|
|
547
638
|
}
|
|
548
639
|
else if (remoteStatesEqual(current, preActionRemote)) {
|
|
549
|
-
await
|
|
550
|
-
live = assertStageCanMutate(projectId, bundleId, progressionRunId, 'auto_push');
|
|
640
|
+
await assertBoundaryAuthorizationCurrent({ authorization, project: live.project, bundle: live.bundle, progressionRunId, cwd, boundaryKind: 'automation_push' });
|
|
641
|
+
live = assertStageCanMutate(projectId, bundleId, progressionRunId, ticket, 'auto_push');
|
|
551
642
|
await pushAutomationTarget(cwd, branch, target, originIdentity);
|
|
552
643
|
live = loadLiveState(projectId, bundleId);
|
|
553
644
|
const observed = await fetchRemoteBranchState(cwd, branch, originIdentity);
|
|
@@ -613,16 +704,16 @@ export const runAutomationCommitChain = async (ticket, _project, _bundle, _autom
|
|
|
613
704
|
cwd,
|
|
614
705
|
boundaryKind: 'automation_pull_request',
|
|
615
706
|
broadcaster,
|
|
616
|
-
|
|
707
|
+
prepare: () => {
|
|
617
708
|
const head = getGitHeadIdentity(cwd);
|
|
618
709
|
if (head.headSha !== headSha) {
|
|
619
710
|
throw new Error('HEAD changed while required checks were evaluated for pull request');
|
|
620
711
|
}
|
|
621
|
-
return
|
|
622
|
-
intendedIdentity: { kind: 'commit', commitSha: headSha, workspaceFingerprint: workspaceIdentity.fingerprint },
|
|
623
|
-
value: head,
|
|
624
|
-
};
|
|
712
|
+
return head;
|
|
625
713
|
},
|
|
714
|
+
resolveIntendedIdentity: (_value, workspaceIdentity) => ({
|
|
715
|
+
kind: 'commit', commitSha: headSha, workspaceFingerprint: workspaceIdentity.fingerprint,
|
|
716
|
+
}),
|
|
626
717
|
});
|
|
627
718
|
const intent = {
|
|
628
719
|
provider: 'github',
|
|
@@ -632,8 +723,7 @@ export const runAutomationCommitChain = async (ticket, _project, _bundle, _autom
|
|
|
632
723
|
baseBranch: githubConfig.default_base_branch,
|
|
633
724
|
headBranch: branch,
|
|
634
725
|
headSha,
|
|
635
|
-
|
|
636
|
-
policyFingerprint: authorization.policyFingerprint,
|
|
726
|
+
checkAuthorization: toStageCheckAuthorization(authorization),
|
|
637
727
|
intendedTreeSha: authorization.value.treeSha,
|
|
638
728
|
resultingCommitSha: headSha,
|
|
639
729
|
};
|
|
@@ -643,8 +733,8 @@ export const runAutomationCommitChain = async (ticket, _project, _bundle, _autom
|
|
|
643
733
|
let pullRequest;
|
|
644
734
|
let outcome;
|
|
645
735
|
if (observation.outcome === 'exact') {
|
|
646
|
-
await
|
|
647
|
-
live = assertStageCanMutate(projectId, bundleId, progressionRunId, 'auto_pr');
|
|
736
|
+
await assertBoundaryAuthorizationCurrent({ authorization, project: live.project, bundle: live.bundle, progressionRunId, cwd, boundaryKind: 'automation_pull_request' });
|
|
737
|
+
live = assertStageCanMutate(projectId, bundleId, progressionRunId, ticket, 'auto_pr');
|
|
648
738
|
pullRequest = observation.pullRequest;
|
|
649
739
|
outcome = 'already_exists';
|
|
650
740
|
}
|
|
@@ -655,8 +745,8 @@ export const runAutomationCommitChain = async (ticket, _project, _bundle, _autom
|
|
|
655
745
|
if (!currentConfig || !githubConfigMatchesIntent(currentConfig, intent)) {
|
|
656
746
|
throw new Error('GitHub configuration changed while the pull-request stage was active');
|
|
657
747
|
}
|
|
658
|
-
live = assertStageCanMutate(projectId, bundleId, progressionRunId, 'auto_pr');
|
|
659
|
-
await
|
|
748
|
+
live = assertStageCanMutate(projectId, bundleId, progressionRunId, ticket, 'auto_pr');
|
|
749
|
+
await assertBoundaryAuthorizationCurrent({ authorization, project: live.project, bundle: live.bundle, progressionRunId, cwd, boundaryKind: 'automation_pull_request' });
|
|
660
750
|
if (getGitHeadIdentity(cwd).headSha !== intent.headSha) {
|
|
661
751
|
throw new Error('HEAD changed while the pull-request stage was active');
|
|
662
752
|
}
|
|
@@ -668,12 +758,12 @@ export const runAutomationCommitChain = async (ticket, _project, _bundle, _autom
|
|
|
668
758
|
if (!mutationConfig || !githubConfigMatchesIntent(mutationConfig, intent)) {
|
|
669
759
|
throw new Error('GitHub configuration changed while the pull-request stage was active');
|
|
670
760
|
}
|
|
671
|
-
live = assertStageCanMutate(projectId, bundleId, progressionRunId, 'auto_pr');
|
|
761
|
+
live = assertStageCanMutate(projectId, bundleId, progressionRunId, ticket, 'auto_pr');
|
|
672
762
|
if (getGitHeadIdentity(cwd).headSha !== intent.headSha) {
|
|
673
763
|
throw new Error('HEAD changed before pull-request creation');
|
|
674
764
|
}
|
|
675
|
-
await
|
|
676
|
-
live = assertStageCanMutate(projectId, bundleId, progressionRunId, 'auto_pr');
|
|
765
|
+
await assertBoundaryAuthorizationCurrent({ authorization, project: live.project, bundle: live.bundle, progressionRunId, cwd, boundaryKind: 'automation_pull_request' });
|
|
766
|
+
live = assertStageCanMutate(projectId, bundleId, progressionRunId, ticket, 'auto_pr');
|
|
677
767
|
pullRequest = await createGitHubPullRequest(mutationConfig, {
|
|
678
768
|
title: content.title,
|
|
679
769
|
body: content.body,
|