@straiffi/archon 1.8.23 → 1.10.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/dist/client/assets/{ActivityView-BaIZ8eHC.js → ActivityView-Y5IaJiim.js} +1 -1
- package/dist/client/assets/GitHubActionsDialog-GWVxWGPK.js +1 -0
- package/dist/client/assets/VerificationDialog-C4RMuI4y.js +4 -0
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-Dl5UF67P.js → architectureDiagram-3BPJPVTR-CeM6gbLN.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-DCcERfrZ.js → blockDiagram-GPEHLZMM-BP4hta96.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-BGhbASFm.js → c4Diagram-AAUBKEIU-5aJScvhn.js} +1 -1
- package/dist/client/assets/channel-B3DjZXbm.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-C1ARozTV.js → chunk-2J33WTMH-BKc2IelV.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-BEovLizF.js → chunk-3OPIFGDE-DCc15la5.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-C5MmlLa_.js → chunk-5ZQYHXKU-DSD9HbTp.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-8y8jBk4n.js → chunk-727SXJPM-DrAjnKiA.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-q-mByAd3.js → chunk-AQP2D5EJ-DxKnpHfQ.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-BOx2mLSz.js → chunk-CSCIHK7Q-DLejrAB_.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-zEwQFq7q.js → chunk-KSCS5N6A-BWRNmF8S.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-C80z6rY_.js → chunk-L5ZTLDWV-D48i966f.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-BLaEsGPd.js → chunk-LZXEDZCA-DLhg6Bok.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-x26BZ95L.js → chunk-ND2GUHAM-B4-7cOEq.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-D2WAxr1p.js → chunk-NZK2D7GU-D2ttXA_d.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-DDPCrzKh.js → chunk-O5CBEL6O-BrWrOVCq.js} +1 -1
- package/dist/client/assets/{chunk-WU5MYG2G-CE-JcvIc.js → chunk-WU5MYG2G-CLcPwxPe.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-DproSkn2.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-JVMMzJxJ.js +1 -0
- package/dist/client/assets/{dagre-BM42HDAG-Bf7wpUk_.js → dagre-BM42HDAG-DIXN8_VX.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-BQoKmc3B.js → diagram-2AECGRRQ-D2wjdCij.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-D2jarPLn.js → diagram-5GNKFQAL-E1Og_PHE.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-Bcbbjvsl.js → diagram-KO2AKTUF-B6P1Ud8a.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-t862RRBQ.js → diagram-LMA3HP47-C3Jxq56v.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-CacbMcO0.js → diagram-OG6HWLK6-Do9LVhdL.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-DRQ79baI.js → erDiagram-TEJ5UH35-DLb5E4-f.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-BfW4KEdc.js → flowDiagram-I6XJVG4X-CcTs_wU9.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-BPIl0TAj.js → ganttDiagram-6RSMTGT7-q9ez5fri.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CRIusBMV.js → gitGraphDiagram-PVQCEYII-BcaKiz4c.js} +1 -1
- package/dist/client/assets/index-1CokwSFm.css +2 -0
- package/dist/client/assets/index-CSOKbu3w.js +276 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-DTKzK8EJ.js → infoDiagram-5YYISTIA-Cu0w4frE.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-DVIHZLvL.js → ishikawaDiagram-YF4QCWOH-CJ47Z-ak.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-CQBZ4tCx.js → journeyDiagram-JHISSGLW-fAL_Zcvs.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-D7naegR0.js → kanban-definition-UN3LZRKU-BTNIybMC.js} +1 -1
- package/dist/client/assets/{line-CizZlaeV.js → line-BvsadeZR.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-NrDqgpZa.js → mermaid-parser.core-C0EkH2y_.js} +1 -1
- package/dist/client/assets/{mermaid.core-CwVO3oX0.js → mermaid.core-BU5QIIbY.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-BWxy3Aa_.js → mindmap-definition-RKZ34NQL-CWbSYK8f.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-B3gXpmBn.js → pieDiagram-4H26LBE5-t4w2cB-y.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Dpp4g8Al.js → quadrantDiagram-W4KKPZXB-Cqasvrpa.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-lFKXnxml.js → requirementDiagram-4Y6WPE33-_l-mY-tb.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BBdtwqrS.js → sankeyDiagram-5OEKKPKP-Cr9YrZ-Y.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-ANpVZzqm.js → sequenceDiagram-3UESZ5HK-DvzZhQpb.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-D58ZxmKY.js → stateDiagram-AJRCARHV-cFjPpAO3.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-btHMH5Cg.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-B6FK8IQC.js → timeline-definition-PNZ67QCA-BjRNrEJY.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-lFWtZEEY.js → vennDiagram-CIIHVFJN-xs9afjrw.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DteAVp4l.js → wardleyDiagram-YWT4CUSO-DH68ZYti.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-CNcDlRLN.js → xychartDiagram-2RQKCTM6-DLW5ktTU.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +225 -12
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +80 -26
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/agent.js +368 -143
- package/dist/server/lib/agent.js.map +1 -1
- package/dist/server/lib/agentProvider.js.map +1 -1
- package/dist/server/lib/approvalReconciliation.js +14 -2
- package/dist/server/lib/approvalReconciliation.js.map +1 -1
- package/dist/server/lib/buildExecutionEnvironment.js +251 -57
- package/dist/server/lib/buildExecutionEnvironment.js.map +1 -1
- package/dist/server/lib/chatOperations.js +125 -3
- package/dist/server/lib/chatOperations.js.map +1 -1
- package/dist/server/lib/chatPostBuildRuns.js +12 -6
- package/dist/server/lib/chatPostBuildRuns.js.map +1 -1
- package/dist/server/lib/chatTargets.js +2 -1
- package/dist/server/lib/chatTargets.js.map +1 -1
- package/dist/server/lib/checkDefinitionCwd.js +34 -0
- package/dist/server/lib/checkDefinitionCwd.js.map +1 -0
- package/dist/server/lib/checkExecutionEnvironment.js +95 -0
- package/dist/server/lib/checkExecutionEnvironment.js.map +1 -0
- package/dist/server/lib/checkPolicyDefinitionIds.js +41 -0
- package/dist/server/lib/checkPolicyDefinitionIds.js.map +1 -0
- package/dist/server/lib/checkRepairDispatch.js +2 -0
- package/dist/server/lib/checkRepairDispatch.js.map +1 -1
- package/dist/server/lib/checkRunner.js +371 -175
- package/dist/server/lib/checkRunner.js.map +1 -1
- package/dist/server/lib/checkRuns.js +99 -8
- package/dist/server/lib/checkRuns.js.map +1 -1
- package/dist/server/lib/claudeAgentProvider.js +3 -6
- package/dist/server/lib/claudeAgentProvider.js.map +1 -1
- package/dist/server/lib/claudeStdin.js +27 -0
- package/dist/server/lib/claudeStdin.js.map +1 -0
- package/dist/server/lib/completedAssistantOutput.js +38 -0
- package/dist/server/lib/completedAssistantOutput.js.map +1 -0
- package/dist/server/lib/consequentialActionAuthority.js +99 -8
- package/dist/server/lib/consequentialActionAuthority.js.map +1 -1
- package/dist/server/lib/consequentialActions.js +42 -2
- package/dist/server/lib/consequentialActions.js.map +1 -1
- package/dist/server/lib/dockerSandboxCredentials.js +10 -3
- package/dist/server/lib/dockerSandboxCredentials.js.map +1 -1
- package/dist/server/lib/dockerSandboxEnvironmentOperations.js +9 -8
- package/dist/server/lib/dockerSandboxEnvironmentOperations.js.map +1 -1
- package/dist/server/lib/dockerSandboxExecutionEnvironment.js +149 -74
- package/dist/server/lib/dockerSandboxExecutionEnvironment.js.map +1 -1
- package/dist/server/lib/dockerSandboxExecutionProvider.js +22 -14
- package/dist/server/lib/dockerSandboxExecutionProvider.js.map +1 -1
- package/dist/server/lib/dockerSandboxNetworkPolicy.js +23 -14
- package/dist/server/lib/dockerSandboxNetworkPolicy.js.map +1 -1
- package/dist/server/lib/dockerSandboxReleaseProfiles.js +91 -0
- package/dist/server/lib/dockerSandboxReleaseProfiles.js.map +1 -0
- package/dist/server/lib/dockerSandboxRuntimeController.js +16 -1
- package/dist/server/lib/dockerSandboxRuntimeController.js.map +1 -1
- package/dist/server/lib/executionCredentialCapabilities.js +9 -6
- package/dist/server/lib/executionCredentialCapabilities.js.map +1 -1
- package/dist/server/lib/executionEnvironment.js.map +1 -1
- package/dist/server/lib/executionEnvironmentLifecycle.js +7 -0
- package/dist/server/lib/executionEnvironmentLifecycle.js.map +1 -1
- package/dist/server/lib/executionEnvironmentOperations.js +241 -0
- package/dist/server/lib/executionEnvironmentOperations.js.map +1 -0
- package/dist/server/lib/executionEnvironmentReconciliation.js +98 -3
- package/dist/server/lib/executionEnvironmentReconciliation.js.map +1 -1
- package/dist/server/lib/executionEnvironmentRoutes.js +66 -27
- package/dist/server/lib/executionEnvironmentRoutes.js.map +1 -1
- package/dist/server/lib/executionEnvironmentValidation.js +2 -2
- package/dist/server/lib/executionEnvironmentValidation.js.map +1 -1
- package/dist/server/lib/executionEnvironmentValidationOperations.js +58 -31
- package/dist/server/lib/executionEnvironmentValidationOperations.js.map +1 -1
- package/dist/server/lib/executionEnvironments.js +187 -9
- package/dist/server/lib/executionEnvironments.js.map +1 -1
- package/dist/server/lib/executionProfileCheckPolicy.js +14 -0
- package/dist/server/lib/executionProfileCheckPolicy.js.map +1 -0
- package/dist/server/lib/executionProfileRoutes.js +9 -1
- package/dist/server/lib/executionProfileRoutes.js.map +1 -1
- package/dist/server/lib/executionProfileStore.js +5 -3
- package/dist/server/lib/executionProfileStore.js.map +1 -1
- package/dist/server/lib/executionProfiles.js +97 -15
- package/dist/server/lib/executionProfiles.js.map +1 -1
- package/dist/server/lib/executionReconciliation.js +43 -6
- package/dist/server/lib/executionReconciliation.js.map +1 -1
- package/dist/server/lib/executionRunAuthority.js +115 -24
- package/dist/server/lib/executionRunAuthority.js.map +1 -1
- package/dist/server/lib/executionTargets.js +30 -4
- package/dist/server/lib/executionTargets.js.map +1 -1
- package/dist/server/lib/executionWorkloads.js +75 -0
- package/dist/server/lib/executionWorkloads.js.map +1 -0
- package/dist/server/lib/git.js +10 -1
- package/dist/server/lib/git.js.map +1 -1
- package/dist/server/lib/opencodeAgentProvider.js +3 -1
- package/dist/server/lib/opencodeAgentProvider.js.map +1 -1
- package/dist/server/lib/opencodeSessionOperations.js +5 -3
- package/dist/server/lib/opencodeSessionOperations.js.map +1 -1
- package/dist/server/lib/planQuestions.js +2 -1
- package/dist/server/lib/planQuestions.js.map +1 -1
- package/dist/server/lib/postBuildCheckRuns.js +9 -5
- package/dist/server/lib/postBuildCheckRuns.js.map +1 -1
- package/dist/server/lib/preview.js +47 -6
- package/dist/server/lib/preview.js.map +1 -1
- package/dist/server/lib/previewProxy.js +11 -3
- package/dist/server/lib/previewProxy.js.map +1 -1
- package/dist/server/lib/productionCheckApprovals.js +97 -35
- package/dist/server/lib/productionCheckApprovals.js.map +1 -1
- package/dist/server/lib/projectMemorySuggestions.js +29 -6
- package/dist/server/lib/projectMemorySuggestions.js.map +1 -1
- package/dist/server/lib/requiredCheckPolicy.js +3 -40
- package/dist/server/lib/requiredCheckPolicy.js.map +1 -1
- package/dist/server/lib/reviewAgentWatchdog.js +75 -0
- package/dist/server/lib/reviewAgentWatchdog.js.map +1 -0
- package/dist/server/lib/reviewFindingReconciliation.js +176 -0
- package/dist/server/lib/reviewFindingReconciliation.js.map +1 -0
- package/dist/server/lib/reviewFindings.js +33 -11
- package/dist/server/lib/reviewFindings.js.map +1 -1
- package/dist/server/lib/run.js +370 -80
- package/dist/server/lib/run.js.map +1 -1
- package/dist/server/lib/serviceRuns.js +196 -0
- package/dist/server/lib/serviceRuns.js.map +1 -0
- package/dist/server/lib/sessionTargets.js +34 -0
- package/dist/server/lib/sessionTargets.js.map +1 -1
- package/dist/server/lib/shell.js +20 -0
- package/dist/server/lib/shell.js.map +1 -1
- package/dist/server/lib/specArtifact.js +36 -15
- package/dist/server/lib/specArtifact.js.map +1 -1
- package/dist/server/lib/specBundleGroups.js +4 -4
- package/dist/server/lib/specBundleGroups.js.map +1 -1
- package/dist/server/lib/specOperations.js +2 -0
- package/dist/server/lib/specOperations.js.map +1 -1
- package/dist/server/lib/specReviewOperations.js +2 -0
- package/dist/server/lib/specReviewOperations.js.map +1 -1
- package/dist/server/lib/specTicketMaterialization.js +37 -24
- package/dist/server/lib/specTicketMaterialization.js.map +1 -1
- package/dist/server/lib/ticketFollowUpOperations.js +9 -4
- package/dist/server/lib/ticketFollowUpOperations.js.map +1 -1
- package/dist/server/lib/ticketRunOperations.js +1 -0
- package/dist/server/lib/ticketRunOperations.js.map +1 -1
- package/dist/server/lib/types.js +1 -0
- package/dist/server/lib/types.js.map +1 -1
- package/dist/server/lib/workloadExecutionEnvironment.js +2 -0
- package/dist/server/lib/workloadExecutionEnvironment.js.map +1 -0
- package/dist/server/lib/workspaceIdentity.js +30 -6
- package/dist/server/lib/workspaceIdentity.js.map +1 -1
- package/dist/server/workers/build.js +39 -8
- package/dist/server/workers/build.js.map +1 -1
- package/dist/server/workers/chat.js +142 -15
- package/dist/server/workers/chat.js.map +1 -1
- package/dist/server/workers/followUp.js +77 -20
- package/dist/server/workers/followUp.js.map +1 -1
- package/dist/server/workers/plan.js +295 -48
- package/dist/server/workers/plan.js.map +1 -1
- package/dist/server/workers/planFollowUp.js +77 -7
- package/dist/server/workers/planFollowUp.js.map +1 -1
- package/dist/server/workers/review.js +149 -38
- package/dist/server/workers/review.js.map +1 -1
- package/dist/server/workers/specReview.js +43 -5
- package/dist/server/workers/specReview.js.map +1 -1
- package/dist/server/workers/specSession.js +156 -31
- package/dist/server/workers/specSession.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/GitHubActionsDialog-BFCqikDY.js +0 -1
- package/dist/client/assets/VerificationDialog-B1_6HMdt.js +0 -4
- package/dist/client/assets/channel-BfskvH2B.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-D4HPfxL3.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DQQ_WqAU.js +0 -1
- package/dist/client/assets/index-CZ1z72fo.js +0 -276
- package/dist/client/assets/index-SMj5ZmMs.css +0 -2
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Cv8cmDfz.js +0 -1
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
|
-
import { spawn } from 'child_process';
|
|
3
2
|
import db from '../db.js';
|
|
4
|
-
import { existsSync, lstatSync
|
|
5
|
-
import { basename, relative, resolve } from 'path';
|
|
3
|
+
import { existsSync, lstatSync } from 'fs';
|
|
6
4
|
import { StringDecoder } from 'string_decoder';
|
|
7
|
-
import {
|
|
5
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
6
|
+
import { findActiveCheckBatchForTarget, findActiveCheckAutoFixForTarget, canAutoFixStartCheckBatch, bindCheckBatchEnvironment, captureCheckAttemptPlacement, getCheckBatchDetails, getCheckAttemptDetails, inspectCheckBatchAggregation, listCheckAttemptDetails, rerunCheckBatch, setCheckBatchStopRequested, } from './checkRuns.js';
|
|
8
7
|
import { getCheckDefinitionFingerprint } from './checkDefinitions.js';
|
|
9
8
|
import { captureCheckWorkspaceIdentity } from './checkWorkspaceIdentity.js';
|
|
10
9
|
import { getProjectById, getProjectCheckDefinitions } from './projects.js';
|
|
11
10
|
import { aggregateCheckBatch, completeCheckAttempt, startCheckAttempt, withExecutionRunTransaction, } from './executionRuns.js';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { createGovernedHostRuntimeEnvironment } from './workspaceProcessEnvironment.js';
|
|
11
|
+
import { resolveDefinitionCwd } from './checkDefinitionCwd.js';
|
|
12
|
+
import { getHostShellArguments, resolveShell } from './shell.js';
|
|
15
13
|
import { cancelApprovalRequest, consumeApprovalRequest, createApprovalRequestInTransaction, emitApprovalRequestCreated, getApprovalRequest, invalidateApprovalRequest, registerApprovalRequestTerminalHandler, } from './approvalRequests.js';
|
|
16
14
|
import { fingerprintConsequentialAction } from './consequentialActions.js';
|
|
17
15
|
import { buildProductionCheckAction, PRODUCTION_CHECK_APPROVAL_TTL_MS } from './productionCheckApprovals.js';
|
|
18
16
|
import { appendCheckAttemptEvidence, appendCheckDiagnostics, completeCheckDiagnosticParser, initializeCheckDiagnosticParser, } from './checkDiagnostics.js';
|
|
19
17
|
import { createCheckDiagnosticParserSession } from './checkDiagnosticParsers.js';
|
|
18
|
+
import { resolveCheckBatchExecutionEnvironment } from './checkExecutionEnvironment.js';
|
|
19
|
+
import { createExecutionProfileSelectionMetadata } from './executionRunAuthority.js';
|
|
20
20
|
const MAX_CONCURRENT_CHECK_ATTEMPTS = 2;
|
|
21
21
|
const OUTPUT_FLUSH_BYTES = 32 * 1024;
|
|
22
22
|
const OUTPUT_FLUSH_DELAY_MS = 25;
|
|
@@ -42,6 +42,8 @@ const invalidatedBatches = new Set();
|
|
|
42
42
|
const stoppedBatches = new Set();
|
|
43
43
|
const forcedAttemptOutcomes = new Map();
|
|
44
44
|
const batchWaiters = new Map();
|
|
45
|
+
const batchExecutions = new Map();
|
|
46
|
+
const batchReleasePromises = new Map();
|
|
45
47
|
let admittedAttempts = 0;
|
|
46
48
|
let acceptingAdmissions = true;
|
|
47
49
|
let inFlightAdmissions = 0;
|
|
@@ -76,63 +78,13 @@ export const closeCheckRunnerAdmissions = () => {
|
|
|
76
78
|
export const openCheckRunnerAdmissions = () => {
|
|
77
79
|
acceptingAdmissions = true;
|
|
78
80
|
};
|
|
79
|
-
const resolveShell = () => process.env.SHELL?.trim() || '/bin/zsh';
|
|
80
81
|
export const getCheckShellArguments = (shell, command, governedProductionEnvironment = false) => {
|
|
81
|
-
|
|
82
|
-
return ['-i', '-c', command];
|
|
83
|
-
}
|
|
84
|
-
const shellName = basename(shell);
|
|
85
|
-
if (shellName === 'zsh') {
|
|
86
|
-
return ['-f', '-c', command];
|
|
87
|
-
}
|
|
88
|
-
if (shellName === 'bash') {
|
|
89
|
-
return ['--noprofile', '--norc', '-c', command];
|
|
90
|
-
}
|
|
91
|
-
if (shellName === 'fish') {
|
|
92
|
-
return ['--no-config', '-c', command];
|
|
93
|
-
}
|
|
94
|
-
return ['-c', command];
|
|
95
|
-
};
|
|
96
|
-
const createShellEnvironment = (cwd, definition, governedProductionEnvironment = false) => {
|
|
97
|
-
const env = definition.consequence === 'production' && governedProductionEnvironment
|
|
98
|
-
? createGovernedHostRuntimeEnvironment(cwd).variables
|
|
99
|
-
: createWorkspaceProcessEnvironment(cwd);
|
|
100
|
-
delete env.PORT;
|
|
101
|
-
return env;
|
|
82
|
+
return getHostShellArguments(shell, command, !governedProductionEnvironment);
|
|
102
83
|
};
|
|
103
84
|
const identitiesMatch = (left, right) => (left.state === 'available'
|
|
104
85
|
&& right.state === 'available'
|
|
105
86
|
&& left.fingerprint === right.fingerprint);
|
|
106
|
-
|
|
107
|
-
const pathFromRoot = relative(root, candidate).replaceAll('\\', '/');
|
|
108
|
-
return pathFromRoot === '' || (!pathFromRoot.startsWith('..') && !pathFromRoot.includes('/../'));
|
|
109
|
-
};
|
|
110
|
-
export const resolveDefinitionCwd = (workspaceCwd, definition) => {
|
|
111
|
-
const workspacePath = resolveCommandWorkspacePath(definition);
|
|
112
|
-
if (!workspacePath) {
|
|
113
|
-
return workspaceCwd;
|
|
114
|
-
}
|
|
115
|
-
const resolvedPath = resolve(workspaceCwd, workspacePath);
|
|
116
|
-
if (!isPathInside(workspaceCwd, resolvedPath)) {
|
|
117
|
-
throw new Error(`Check definition ${definition.id} working directory must stay inside the target workspace`);
|
|
118
|
-
}
|
|
119
|
-
let realWorkspacePath;
|
|
120
|
-
let realResolvedPath;
|
|
121
|
-
try {
|
|
122
|
-
realWorkspacePath = realpathSync(workspaceCwd);
|
|
123
|
-
realResolvedPath = realpathSync(resolvedPath);
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
throw new Error(`Check definition ${definition.id} working directory is unavailable`);
|
|
127
|
-
}
|
|
128
|
-
if (!isPathInside(realWorkspacePath, realResolvedPath)) {
|
|
129
|
-
throw new Error(`Check definition ${definition.id} working directory must stay inside the target workspace`);
|
|
130
|
-
}
|
|
131
|
-
if (!lstatSync(realResolvedPath).isDirectory()) {
|
|
132
|
-
throw new Error(`Check definition ${definition.id} working directory is unavailable`);
|
|
133
|
-
}
|
|
134
|
-
return definition.cwd?.trim() ? realResolvedPath : workspaceCwd;
|
|
135
|
-
};
|
|
87
|
+
export { resolveDefinitionCwd } from './checkDefinitionCwd.js';
|
|
136
88
|
const validateDefinitions = (selectedDefinitions, availableDefinitions) => {
|
|
137
89
|
if (selectedDefinitions.length === 0) {
|
|
138
90
|
throw new Error('At least one check definition must be selected');
|
|
@@ -298,29 +250,93 @@ const appendProcessOutput = (record, text) => {
|
|
|
298
250
|
record.flushTimer = setTimeout(() => requestOutputFlush(record), OUTPUT_FLUSH_DELAY_MS);
|
|
299
251
|
}
|
|
300
252
|
};
|
|
301
|
-
const
|
|
302
|
-
if (
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
307
|
-
catch (error) {
|
|
308
|
-
if (error.code === 'ESRCH') {
|
|
309
|
-
return;
|
|
253
|
+
const terminateProcess = (record) => {
|
|
254
|
+
if (record.batchExecution.environment.provider === 'docker_sandbox' && record.terminalCause !== 'user_stop') {
|
|
255
|
+
for (const sibling of processes.values()) {
|
|
256
|
+
if (sibling.batchRunId === record.batchRunId && sibling.attemptRunId !== record.attemptRunId) {
|
|
257
|
+
sibling.terminalCause ??= 'environment_loss';
|
|
310
258
|
}
|
|
311
259
|
}
|
|
260
|
+
void captureCheckWorkspaceIdentity(record.cwd).then(identity => (failQueuedBatch(record.batchRunId, 'environment_loss', identity))).catch(error => {
|
|
261
|
+
console.error('Unable to terminalize queued attempts after sandbox loss', {
|
|
262
|
+
batch_run_id: record.batchRunId,
|
|
263
|
+
error: error instanceof Error ? error.message : String(error),
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
void record.handle.interrupt('SIGTERM');
|
|
268
|
+
if (!record.killTimer) {
|
|
269
|
+
record.killTimer = setTimeout(() => void record.handle.interrupt('SIGKILL'), STOP_GRACE_MS);
|
|
312
270
|
}
|
|
271
|
+
};
|
|
272
|
+
const releaseBatchExecution = (batchRunId) => {
|
|
273
|
+
const existing = batchReleasePromises.get(batchRunId);
|
|
274
|
+
if (existing) {
|
|
275
|
+
return existing;
|
|
276
|
+
}
|
|
277
|
+
const execution = batchExecutions.get(batchRunId);
|
|
278
|
+
if (!execution) {
|
|
279
|
+
return Promise.resolve();
|
|
280
|
+
}
|
|
281
|
+
const release = execution.execution.release().finally(() => {
|
|
282
|
+
batchExecutions.delete(batchRunId);
|
|
283
|
+
batchReleasePromises.delete(batchRunId);
|
|
284
|
+
});
|
|
285
|
+
batchReleasePromises.set(batchRunId, release);
|
|
286
|
+
return release;
|
|
287
|
+
};
|
|
288
|
+
const restoreBatchExecution = async (input) => {
|
|
289
|
+
if (!input.batch.batch_environment || !input.batch.runtime_compatibility || !input.attempt.placement) {
|
|
290
|
+
throw new Error('Check batch execution evidence is incomplete');
|
|
291
|
+
}
|
|
292
|
+
const project = getProjectById(input.projectId);
|
|
293
|
+
if (!project) {
|
|
294
|
+
throw new Error('The check batch project is unavailable');
|
|
295
|
+
}
|
|
296
|
+
const execution = await resolveCheckBatchExecutionEnvironment({
|
|
297
|
+
project,
|
|
298
|
+
batchRunId: input.batch.execution_run_id,
|
|
299
|
+
targetKind: input.batch.target_kind,
|
|
300
|
+
targetBundleId: input.batch.target_bundle_id,
|
|
301
|
+
cwd: input.workspaceCwd,
|
|
302
|
+
pinnedEnvironment: input.batch.batch_environment,
|
|
303
|
+
});
|
|
304
|
+
const restoredPlacement = execution.createAttemptPlacement(input.attempt.execution_run_id);
|
|
305
|
+
if (!isDeepStrictEqual(execution.environment, input.batch.batch_environment)
|
|
306
|
+
|| !isDeepStrictEqual(execution.compatibility, input.batch.runtime_compatibility)
|
|
307
|
+
|| !isDeepStrictEqual(restoredPlacement, input.attempt.placement)) {
|
|
308
|
+
await execution.execution.release();
|
|
309
|
+
throw new Error('The persisted check batch execution environment could not be restored exactly');
|
|
310
|
+
}
|
|
311
|
+
batchExecutions.set(input.batch.execution_run_id, execution);
|
|
312
|
+
return execution;
|
|
313
|
+
};
|
|
314
|
+
const rollbackUnadmittedBatch = async (input) => {
|
|
315
|
+
await releaseBatchExecution(input.batchRunId);
|
|
313
316
|
try {
|
|
314
|
-
|
|
317
|
+
db.transaction(() => {
|
|
318
|
+
db.prepare('DELETE FROM execution_runs WHERE parent_run_id = ? AND project_id = ?').run(input.batchRunId, input.projectId);
|
|
319
|
+
db.prepare('DELETE FROM execution_runs WHERE id = ? AND project_id = ?').run(input.batchRunId, input.projectId);
|
|
320
|
+
})();
|
|
321
|
+
return;
|
|
315
322
|
}
|
|
316
323
|
catch {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
+
const message = input.error instanceof Error ? input.error.message : String(input.error);
|
|
325
|
+
for (const attemptRunId of input.attemptRunIds) {
|
|
326
|
+
const attempt = getCheckAttemptDetails(input.projectId, attemptRunId);
|
|
327
|
+
if (!attempt?.outcome) {
|
|
328
|
+
completeCheckAttempt({
|
|
329
|
+
projectId: input.projectId,
|
|
330
|
+
runId: attemptRunId,
|
|
331
|
+
outcome: 'infrastructure_failure',
|
|
332
|
+
finalWorkspaceIdentity: input.identity,
|
|
333
|
+
exitError: `Check batch environment admission failed: ${message}`,
|
|
334
|
+
}, input.emitter);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
aggregateCheckBatch(input.projectId, input.batchRunId, input.emitter, {
|
|
338
|
+
finalWorkspaceIdentity: input.identity,
|
|
339
|
+
});
|
|
324
340
|
}
|
|
325
341
|
};
|
|
326
342
|
const waitForProcessFinalization = (record) => {
|
|
@@ -364,6 +380,7 @@ const aggregateBatchAfterAttempt = async (entry) => {
|
|
|
364
380
|
aggregateCheckBatch(entry.projectId, entry.batchRunId, entry.emitter, {
|
|
365
381
|
finalWorkspaceIdentity: batchFinalIdentity,
|
|
366
382
|
});
|
|
383
|
+
await releaseBatchExecution(entry.batchRunId);
|
|
367
384
|
};
|
|
368
385
|
const completeAttempt = async (entry, outcome, finalIdentity, options = {}) => {
|
|
369
386
|
completeCheckAttempt({
|
|
@@ -390,6 +407,7 @@ const finalizeProcess = (record, code, signal, error) => {
|
|
|
390
407
|
clearTimeout(record.killTimer);
|
|
391
408
|
}
|
|
392
409
|
record.finalizationPromise = (async () => {
|
|
410
|
+
await record.placementVerificationPromise;
|
|
393
411
|
appendProcessOutput(record, record.stdoutDecoder.end());
|
|
394
412
|
appendProcessOutput(record, record.stderrDecoder.end());
|
|
395
413
|
try {
|
|
@@ -455,7 +473,7 @@ const failQueuedBatch = async (batchRunId, outcome, identity) => {
|
|
|
455
473
|
await completeAttempt(entry, outcome, identity, {
|
|
456
474
|
exitError: outcome === 'workspace_changed_before_start'
|
|
457
475
|
? 'Workspace identity changed before this check could start'
|
|
458
|
-
: null,
|
|
476
|
+
: outcome === 'environment_loss' ? 'The batch-owned sandbox was lost while sibling attempts were active' : null,
|
|
459
477
|
});
|
|
460
478
|
}
|
|
461
479
|
};
|
|
@@ -486,7 +504,7 @@ const runQueuedAttempt = async (entry) => {
|
|
|
486
504
|
if (entry.approvalRequestId) {
|
|
487
505
|
const request = getApprovalRequest(entry.projectId, entry.approvalRequestId, { emitter: entry.emitter });
|
|
488
506
|
const batch = getCheckBatchDetails(entry.projectId, entry.batchRunId);
|
|
489
|
-
if (!request || request.status !== 'consumed' || !batch) {
|
|
507
|
+
if (!request || request.status !== 'consumed' || request.action.kind !== 'custom_check' || !batch) {
|
|
490
508
|
throw new Error('Production check approval is no longer consumable');
|
|
491
509
|
}
|
|
492
510
|
const currentDefinitions = getProjectCheckDefinitions(getProjectById(entry.projectId));
|
|
@@ -502,6 +520,9 @@ const runQueuedAttempt = async (entry) => {
|
|
|
502
520
|
batch,
|
|
503
521
|
workspaceIdentity: identity,
|
|
504
522
|
expiresAt: request.expires_at,
|
|
523
|
+
launcher: request.action.target.launcher ?? 'host_shell',
|
|
524
|
+
batchEnvironment: request.action.target.batch_environment ?? null,
|
|
525
|
+
attemptPlacement: entry.expectedPlacement,
|
|
505
526
|
});
|
|
506
527
|
if (fingerprintConsequentialAction(current.action) !== request.action_fingerprint) {
|
|
507
528
|
throw new Error('Production check action changed before spawn');
|
|
@@ -513,15 +534,27 @@ const runQueuedAttempt = async (entry) => {
|
|
|
513
534
|
runId: entry.attemptRunId,
|
|
514
535
|
}, entry.emitter);
|
|
515
536
|
await new Promise((resolveAttempt, rejectAttempt) => {
|
|
537
|
+
let handle;
|
|
516
538
|
let child;
|
|
517
539
|
try {
|
|
518
540
|
const shell = resolveShell();
|
|
519
|
-
|
|
541
|
+
const runtimeEnvironment = entry.batchExecution.execution.runtimeEnvironment;
|
|
542
|
+
const governedHostEnvironment = entry.batchExecution.execution.provider.kind === 'host'
|
|
543
|
+
&& runtimeEnvironment.kind === 'host'
|
|
544
|
+
&& runtimeEnvironment.inheritance === 'governed';
|
|
545
|
+
handle = entry.batchExecution.execution.provider.execute({
|
|
546
|
+
phase: 'agent',
|
|
547
|
+
target: entry.batchExecution.target,
|
|
548
|
+
command: shell,
|
|
549
|
+
args: getCheckShellArguments(shell, entry.definition.command, entry.governedProductionEnvironment || governedHostEnvironment),
|
|
520
550
|
cwd: spawnCwd,
|
|
551
|
+
launcherEnvironment: entry.batchExecution.execution.launcherEnvironment,
|
|
552
|
+
runtimeEnvironment: entry.batchExecution.execution.runtimeEnvironment,
|
|
521
553
|
detached: process.platform !== 'win32',
|
|
522
|
-
env: createShellEnvironment(entry.cwd, entry.definition, entry.governedProductionEnvironment),
|
|
523
554
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
555
|
+
placementExecutionRunId: entry.attemptRunId,
|
|
524
556
|
});
|
|
557
|
+
child = handle.child;
|
|
525
558
|
}
|
|
526
559
|
catch (error) {
|
|
527
560
|
const spawnError = error instanceof Error ? error : new Error(String(error));
|
|
@@ -533,6 +566,7 @@ const runQueuedAttempt = async (entry) => {
|
|
|
533
566
|
const record = {
|
|
534
567
|
...entry,
|
|
535
568
|
child,
|
|
569
|
+
handle,
|
|
536
570
|
stdoutDecoder: new StringDecoder('utf8'),
|
|
537
571
|
stderrDecoder: new StringDecoder('utf8'),
|
|
538
572
|
pendingOutput: '',
|
|
@@ -545,6 +579,7 @@ const runQueuedAttempt = async (entry) => {
|
|
|
545
579
|
spawned: false,
|
|
546
580
|
finalized: false,
|
|
547
581
|
finalizationPromise: null,
|
|
582
|
+
placementVerificationPromise: Promise.resolve(),
|
|
548
583
|
diagnosticParser: createCheckDiagnosticParserSession({
|
|
549
584
|
definition: entry.definition,
|
|
550
585
|
workspaceCwd: entry.cwd,
|
|
@@ -554,6 +589,22 @@ const runQueuedAttempt = async (entry) => {
|
|
|
554
589
|
persistenceFailure: null,
|
|
555
590
|
};
|
|
556
591
|
processes.set(entry.attemptRunId, record);
|
|
592
|
+
record.placementVerificationPromise = Promise.all([
|
|
593
|
+
handle.placementFailure,
|
|
594
|
+
handle.placementIdentity ?? Promise.resolve(null),
|
|
595
|
+
]).then(([failure, placement]) => {
|
|
596
|
+
if (failure
|
|
597
|
+
|| (handle.placementIdentity && !placement)
|
|
598
|
+
|| (placement && !isDeepStrictEqual(placement, entry.expectedPlacement))) {
|
|
599
|
+
record.terminalCause ??= 'infrastructure_failure';
|
|
600
|
+
terminateProcess(record);
|
|
601
|
+
}
|
|
602
|
+
}).catch(() => {
|
|
603
|
+
if (!record.terminalCause) {
|
|
604
|
+
record.terminalCause ??= 'infrastructure_failure';
|
|
605
|
+
terminateProcess(record);
|
|
606
|
+
}
|
|
607
|
+
});
|
|
557
608
|
child.once('spawn', () => {
|
|
558
609
|
record.spawned = true;
|
|
559
610
|
});
|
|
@@ -629,6 +680,8 @@ const enqueueCheckAttempts = (input) => {
|
|
|
629
680
|
emitter: input.emitter,
|
|
630
681
|
approvalRequestId: attempt.approvalRequestId,
|
|
631
682
|
governedProductionEnvironment: attempt.governedProductionEnvironment,
|
|
683
|
+
batchExecution: input.batchExecution,
|
|
684
|
+
expectedPlacement: attempt.expectedPlacement,
|
|
632
685
|
})));
|
|
633
686
|
pumpScheduler();
|
|
634
687
|
};
|
|
@@ -670,7 +723,11 @@ export const startCheckBatch = async (input, emitter) => withCheckAdmission(asyn
|
|
|
670
723
|
if (initialIdentity.state !== 'available') {
|
|
671
724
|
throw new Error(`Check workspace identity is unavailable: ${initialIdentity.message}`);
|
|
672
725
|
}
|
|
673
|
-
const
|
|
726
|
+
const project = getProjectById(input.projectId);
|
|
727
|
+
if (!project) {
|
|
728
|
+
throw new Error(`Project ${input.projectId} was not found`);
|
|
729
|
+
}
|
|
730
|
+
const currentDefinitions = getProjectCheckDefinitions(project);
|
|
674
731
|
validateDefinitions(input.selectedDefinitions, currentDefinitions);
|
|
675
732
|
for (const definition of input.selectedDefinitions) {
|
|
676
733
|
resolveDefinitionCwd(input.cwd, definition);
|
|
@@ -685,86 +742,136 @@ export const startCheckBatch = async (input, emitter) => withCheckAdmission(asyn
|
|
|
685
742
|
const scopeLinks = targetBundleId
|
|
686
743
|
? [{ entityType: 'bundle', entityId: targetBundleId, relationship: 'context' }]
|
|
687
744
|
: [];
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
kind: 'check_batch',
|
|
702
|
-
initiatedBy: input.automationParentRunId || input.autoFixParentRunId ? 'automation' : input.postBuildParentRunId ? 'system' : 'user',
|
|
703
|
-
parentRunId: input.automationParentRunId ?? input.postBuildParentRunId ?? input.autoFixParentRunId ?? null,
|
|
704
|
-
domainStatus: 'queued',
|
|
705
|
-
metadata: input.automationBoundaryKind ? { automation_boundary_kind: input.automationBoundaryKind } : null,
|
|
706
|
-
sourceReference: input.sourceReference ?? null,
|
|
707
|
-
links: scopeLinks,
|
|
708
|
-
eventType: 'check_batch_created',
|
|
709
|
-
allowedActiveAutomationChildRunId: input.allowedActiveAutomationChildRunId,
|
|
710
|
-
checkBatchDetails: {
|
|
711
|
-
...checkTarget,
|
|
712
|
-
targetLabel: input.targetLabel,
|
|
713
|
-
selectedDefinitions: input.selectedDefinitions,
|
|
714
|
-
initialWorkspaceIdentity: initialIdentity,
|
|
715
|
-
rerunOfBatchRunId: input.rerunOfBatchRunId,
|
|
716
|
-
automationBoundaryPurpose,
|
|
717
|
-
purpose: input.autoFixParentRunId ? 'auto_fix' : input.postBuildParentRunId ? 'post_build' : input.automationParentRunId ? 'automation_gate' : 'manual',
|
|
718
|
-
causalSourceRunId: input.autoFixOriginBatchRunId ?? input.postBuildParentRunId ?? null,
|
|
719
|
-
},
|
|
720
|
-
});
|
|
721
|
-
for (const attempt of attempts) {
|
|
745
|
+
let batchExecution;
|
|
746
|
+
let placements;
|
|
747
|
+
try {
|
|
748
|
+
withExecutionRunTransaction(transaction => {
|
|
749
|
+
const transactionConflict = findActiveCheckBatchForTarget(input.projectId, input.targetKind, targetBundleId);
|
|
750
|
+
if (transactionConflict) {
|
|
751
|
+
throw new Error(`Check batch ${transactionConflict} is already active for this target`);
|
|
752
|
+
}
|
|
753
|
+
const transactionAutoFixConflict = findActiveCheckAutoFixForTarget(input.projectId, input.targetKind, targetBundleId);
|
|
754
|
+
if (transactionAutoFixConflict && transactionAutoFixConflict !== input.autoFixParentRunId) {
|
|
755
|
+
throw new Error(`Check auto-fix ${transactionAutoFixConflict} is already active for this target`);
|
|
756
|
+
}
|
|
757
|
+
assertAutoFixAdmission();
|
|
722
758
|
transaction.createExecutionRun({
|
|
723
|
-
id:
|
|
759
|
+
id: batchRunId,
|
|
724
760
|
projectId: input.projectId,
|
|
725
|
-
kind: '
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
links: scopeLinks,
|
|
761
|
+
kind: 'check_batch',
|
|
762
|
+
initiatedBy: input.automationParentRunId || input.autoFixParentRunId ? 'automation' : input.postBuildParentRunId ? 'system' : 'user',
|
|
763
|
+
parentRunId: input.automationParentRunId ?? input.postBuildParentRunId ?? input.autoFixParentRunId ?? null,
|
|
729
764
|
domainStatus: 'queued',
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
765
|
+
metadata: {
|
|
766
|
+
...createExecutionProfileSelectionMetadata(input.projectId),
|
|
767
|
+
...(input.automationBoundaryKind ? { automation_boundary_kind: input.automationBoundaryKind } : {}),
|
|
768
|
+
},
|
|
769
|
+
sourceReference: input.sourceReference ?? null,
|
|
770
|
+
links: scopeLinks,
|
|
771
|
+
eventType: 'check_batch_created',
|
|
772
|
+
allowedActiveAutomationChildRunId: input.allowedActiveAutomationChildRunId,
|
|
773
|
+
checkBatchDetails: {
|
|
774
|
+
...checkTarget,
|
|
775
|
+
targetLabel: input.targetLabel,
|
|
776
|
+
selectedDefinitions: input.selectedDefinitions,
|
|
734
777
|
initialWorkspaceIdentity: initialIdentity,
|
|
735
|
-
|
|
778
|
+
rerunOfBatchRunId: input.rerunOfBatchRunId,
|
|
779
|
+
automationBoundaryPurpose,
|
|
780
|
+
purpose: input.autoFixParentRunId ? 'auto_fix' : input.postBuildParentRunId ? 'post_build' : input.automationParentRunId ? 'automation_gate' : 'manual',
|
|
781
|
+
causalSourceRunId: input.autoFixOriginBatchRunId ?? input.postBuildParentRunId ?? null,
|
|
736
782
|
},
|
|
737
783
|
});
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
784
|
+
for (const attempt of attempts) {
|
|
785
|
+
transaction.createExecutionRun({
|
|
786
|
+
id: attempt.runId,
|
|
787
|
+
projectId: input.projectId,
|
|
788
|
+
kind: 'check_attempt',
|
|
789
|
+
parentRunId: batchRunId,
|
|
790
|
+
sourceReference: { kind: 'project', id: input.projectId },
|
|
791
|
+
links: scopeLinks,
|
|
792
|
+
domainStatus: 'queued',
|
|
793
|
+
eventType: 'check_attempt_created',
|
|
794
|
+
checkAttemptDetails: {
|
|
795
|
+
batchRunId,
|
|
796
|
+
definition: attempt.definition,
|
|
797
|
+
initialWorkspaceIdentity: initialIdentity,
|
|
798
|
+
rerunOfAttemptRunId: input.rerunAttemptRunIds?.[attempt.definition.id] ?? null,
|
|
799
|
+
},
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
input.onBatchCreated?.(batchRunId);
|
|
803
|
+
}, emitter);
|
|
804
|
+
batchExecution = await resolveCheckBatchExecutionEnvironment({
|
|
805
|
+
project,
|
|
806
|
+
batchRunId,
|
|
807
|
+
targetKind: input.targetKind,
|
|
808
|
+
targetBundleId,
|
|
809
|
+
cwd: input.cwd,
|
|
810
|
+
});
|
|
811
|
+
batchExecutions.set(batchRunId, batchExecution);
|
|
812
|
+
bindCheckBatchEnvironment({
|
|
813
|
+
projectId: input.projectId,
|
|
814
|
+
batchRunId,
|
|
815
|
+
compatibility: batchExecution.compatibility,
|
|
816
|
+
environment: batchExecution.environment,
|
|
817
|
+
});
|
|
818
|
+
placements = new Map(attempts.map(attempt => {
|
|
819
|
+
const placement = batchExecution.createAttemptPlacement(attempt.runId);
|
|
820
|
+
captureCheckAttemptPlacement({ projectId: input.projectId, attemptRunId: attempt.runId, placement });
|
|
821
|
+
return [attempt.runId, placement];
|
|
822
|
+
}));
|
|
823
|
+
withExecutionRunTransaction(transaction => {
|
|
824
|
+
const batch = getCheckBatchDetails(input.projectId, batchRunId);
|
|
825
|
+
for (const attempt of attempts.filter(candidate => candidate.definition.consequence === 'production')) {
|
|
826
|
+
const expiresAt = new Date(Date.now() + PRODUCTION_CHECK_APPROVAL_TTL_MS).toISOString();
|
|
827
|
+
const placement = placements.get(attempt.runId);
|
|
828
|
+
const sandboxEnvironment = batchExecution.environment.provider === 'docker_sandbox'
|
|
829
|
+
? {
|
|
830
|
+
id: batchExecution.environment.environment_id,
|
|
831
|
+
revision: batchExecution.environment.environment_revision,
|
|
832
|
+
validation_fingerprint: batchExecution.environment.environment_validation_fingerprint,
|
|
833
|
+
}
|
|
834
|
+
: null;
|
|
835
|
+
const approval = buildProductionCheckAction({
|
|
836
|
+
projectId: input.projectId,
|
|
837
|
+
workspaceCwd: input.cwd,
|
|
838
|
+
effectiveCwd: resolveDefinitionCwd(input.cwd, attempt.definition),
|
|
839
|
+
definition: attempt.definition,
|
|
840
|
+
batch,
|
|
841
|
+
workspaceIdentity: initialIdentity,
|
|
842
|
+
expiresAt,
|
|
843
|
+
launcher: sandboxEnvironment ? 'docker_sandbox_exec' : 'host_shell',
|
|
844
|
+
batchEnvironment: sandboxEnvironment,
|
|
845
|
+
attemptPlacement: placement,
|
|
846
|
+
});
|
|
847
|
+
createdApprovals.push(createApprovalRequestInTransaction({
|
|
848
|
+
projectId: input.projectId,
|
|
849
|
+
executionRunId: attempt.runId,
|
|
850
|
+
action: approval.action,
|
|
851
|
+
authority: approval.authority,
|
|
852
|
+
expiresAt,
|
|
853
|
+
}, transaction));
|
|
854
|
+
}
|
|
855
|
+
transaction.transitionExecutionRun({
|
|
743
856
|
projectId: input.projectId,
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
workspaceIdentity: initialIdentity,
|
|
749
|
-
expiresAt,
|
|
857
|
+
runId: batchRunId,
|
|
858
|
+
status: 'running',
|
|
859
|
+
domainStatus: 'running',
|
|
860
|
+
eventType: 'check_batch_started',
|
|
750
861
|
});
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
authority: approval.authority,
|
|
756
|
-
expiresAt,
|
|
757
|
-
}, transaction));
|
|
758
|
-
}
|
|
759
|
-
input.onBatchCreated?.(batchRunId);
|
|
760
|
-
transaction.transitionExecutionRun({
|
|
862
|
+
}, emitter);
|
|
863
|
+
}
|
|
864
|
+
catch (error) {
|
|
865
|
+
await rollbackUnadmittedBatch({
|
|
761
866
|
projectId: input.projectId,
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
867
|
+
batchRunId,
|
|
868
|
+
attemptRunIds: attempts.map(attempt => attempt.runId),
|
|
869
|
+
identity: initialIdentity,
|
|
870
|
+
error,
|
|
871
|
+
emitter,
|
|
766
872
|
});
|
|
767
|
-
|
|
873
|
+
throw error;
|
|
874
|
+
}
|
|
768
875
|
for (const approval of createdApprovals) {
|
|
769
876
|
emitApprovalRequestCreated(emitter, approval);
|
|
770
877
|
}
|
|
@@ -773,8 +880,12 @@ export const startCheckBatch = async (input, emitter) => withCheckAdmission(asyn
|
|
|
773
880
|
projectId: input.projectId,
|
|
774
881
|
batchRunId,
|
|
775
882
|
cwd: input.cwd,
|
|
776
|
-
attempts: ordinaryAttempts
|
|
883
|
+
attempts: ordinaryAttempts.map(attempt => ({
|
|
884
|
+
...attempt,
|
|
885
|
+
expectedPlacement: placements.get(attempt.runId),
|
|
886
|
+
})),
|
|
777
887
|
initialIdentity,
|
|
888
|
+
batchExecution,
|
|
778
889
|
emitter,
|
|
779
890
|
});
|
|
780
891
|
return getCheckBatchDetails(input.projectId, batchRunId);
|
|
@@ -797,7 +908,11 @@ export const rerunCheckBatchAndSchedule = async (input, emitter) => withCheckAdm
|
|
|
797
908
|
if (initialIdentity.state !== 'available') {
|
|
798
909
|
throw new Error(`Check workspace identity is unavailable: ${initialIdentity.message}`);
|
|
799
910
|
}
|
|
800
|
-
const
|
|
911
|
+
const project = getProjectById(input.projectId);
|
|
912
|
+
if (!project) {
|
|
913
|
+
throw new Error(`Project ${input.projectId} was not found`);
|
|
914
|
+
}
|
|
915
|
+
const currentDefinitions = getProjectCheckDefinitions(project);
|
|
801
916
|
const currentById = new Map(currentDefinitions.map(definition => [definition.id, definition]));
|
|
802
917
|
for (const definitionId of selectedDefinitionIds) {
|
|
803
918
|
const definition = currentById.get(definitionId);
|
|
@@ -815,29 +930,76 @@ export const rerunCheckBatchAndSchedule = async (input, emitter) => withCheckAdm
|
|
|
815
930
|
selection: input.selection,
|
|
816
931
|
availableDefinitions: currentDefinitions,
|
|
817
932
|
initialWorkspaceIdentity: initialIdentity,
|
|
933
|
+
profileSelectionMetadata: createExecutionProfileSelectionMetadata(input.projectId),
|
|
818
934
|
});
|
|
819
|
-
const batch = getCheckBatchDetails(input.projectId, result.batchRunId);
|
|
820
|
-
for (const attempt of result.attempts.filter(candidate => candidate.definition.consequence === 'production')) {
|
|
821
|
-
const expiresAt = new Date(Date.now() + PRODUCTION_CHECK_APPROVAL_TTL_MS).toISOString();
|
|
822
|
-
const approval = buildProductionCheckAction({
|
|
823
|
-
projectId: input.projectId,
|
|
824
|
-
workspaceCwd: input.cwd,
|
|
825
|
-
effectiveCwd: resolveDefinitionCwd(input.cwd, attempt.definition),
|
|
826
|
-
definition: attempt.definition,
|
|
827
|
-
batch,
|
|
828
|
-
workspaceIdentity: initialIdentity,
|
|
829
|
-
expiresAt,
|
|
830
|
-
});
|
|
831
|
-
createdApprovals.push(createApprovalRequestInTransaction({
|
|
832
|
-
projectId: input.projectId,
|
|
833
|
-
executionRunId: attempt.runId,
|
|
834
|
-
action: approval.action,
|
|
835
|
-
authority: approval.authority,
|
|
836
|
-
expiresAt,
|
|
837
|
-
}, transaction));
|
|
838
|
-
}
|
|
839
935
|
return result;
|
|
840
936
|
}, emitter);
|
|
937
|
+
let batchExecution;
|
|
938
|
+
let placements;
|
|
939
|
+
try {
|
|
940
|
+
batchExecution = await resolveCheckBatchExecutionEnvironment({
|
|
941
|
+
project,
|
|
942
|
+
batchRunId: created.batchRunId,
|
|
943
|
+
targetKind: created.batch.target_kind,
|
|
944
|
+
targetBundleId: created.batch.target_bundle_id,
|
|
945
|
+
cwd: input.cwd,
|
|
946
|
+
});
|
|
947
|
+
batchExecutions.set(created.batchRunId, batchExecution);
|
|
948
|
+
bindCheckBatchEnvironment({
|
|
949
|
+
projectId: input.projectId,
|
|
950
|
+
batchRunId: created.batchRunId,
|
|
951
|
+
compatibility: batchExecution.compatibility,
|
|
952
|
+
environment: batchExecution.environment,
|
|
953
|
+
});
|
|
954
|
+
placements = new Map(created.attempts.map(attempt => {
|
|
955
|
+
const placement = batchExecution.createAttemptPlacement(attempt.runId);
|
|
956
|
+
captureCheckAttemptPlacement({ projectId: input.projectId, attemptRunId: attempt.runId, placement });
|
|
957
|
+
return [attempt.runId, placement];
|
|
958
|
+
}));
|
|
959
|
+
withExecutionRunTransaction(transaction => {
|
|
960
|
+
const batch = getCheckBatchDetails(input.projectId, created.batchRunId);
|
|
961
|
+
for (const attempt of created.attempts.filter(candidate => candidate.definition.consequence === 'production')) {
|
|
962
|
+
const expiresAt = new Date(Date.now() + PRODUCTION_CHECK_APPROVAL_TTL_MS).toISOString();
|
|
963
|
+
const sandboxEnvironment = batchExecution.environment.provider === 'docker_sandbox'
|
|
964
|
+
? {
|
|
965
|
+
id: batchExecution.environment.environment_id,
|
|
966
|
+
revision: batchExecution.environment.environment_revision,
|
|
967
|
+
validation_fingerprint: batchExecution.environment.environment_validation_fingerprint,
|
|
968
|
+
}
|
|
969
|
+
: null;
|
|
970
|
+
const approval = buildProductionCheckAction({
|
|
971
|
+
projectId: input.projectId,
|
|
972
|
+
workspaceCwd: input.cwd,
|
|
973
|
+
effectiveCwd: resolveDefinitionCwd(input.cwd, attempt.definition),
|
|
974
|
+
definition: attempt.definition,
|
|
975
|
+
batch,
|
|
976
|
+
workspaceIdentity: initialIdentity,
|
|
977
|
+
expiresAt,
|
|
978
|
+
launcher: sandboxEnvironment ? 'docker_sandbox_exec' : 'host_shell',
|
|
979
|
+
batchEnvironment: sandboxEnvironment,
|
|
980
|
+
attemptPlacement: placements.get(attempt.runId),
|
|
981
|
+
});
|
|
982
|
+
createdApprovals.push(createApprovalRequestInTransaction({
|
|
983
|
+
projectId: input.projectId,
|
|
984
|
+
executionRunId: attempt.runId,
|
|
985
|
+
action: approval.action,
|
|
986
|
+
authority: approval.authority,
|
|
987
|
+
expiresAt,
|
|
988
|
+
}, transaction));
|
|
989
|
+
}
|
|
990
|
+
}, emitter);
|
|
991
|
+
}
|
|
992
|
+
catch (error) {
|
|
993
|
+
await rollbackUnadmittedBatch({
|
|
994
|
+
projectId: input.projectId,
|
|
995
|
+
batchRunId: created.batchRunId,
|
|
996
|
+
attemptRunIds: created.attempts.map(attempt => attempt.runId),
|
|
997
|
+
identity: initialIdentity,
|
|
998
|
+
error,
|
|
999
|
+
emitter,
|
|
1000
|
+
});
|
|
1001
|
+
throw error;
|
|
1002
|
+
}
|
|
841
1003
|
for (const approval of createdApprovals) {
|
|
842
1004
|
emitApprovalRequestCreated(emitter, approval);
|
|
843
1005
|
}
|
|
@@ -846,12 +1008,16 @@ export const rerunCheckBatchAndSchedule = async (input, emitter) => withCheckAdm
|
|
|
846
1008
|
projectId: input.projectId,
|
|
847
1009
|
batchRunId: created.batchRunId,
|
|
848
1010
|
cwd: input.cwd,
|
|
849
|
-
attempts: ordinaryAttempts
|
|
1011
|
+
attempts: ordinaryAttempts.map(attempt => ({
|
|
1012
|
+
...attempt,
|
|
1013
|
+
expectedPlacement: placements.get(attempt.runId),
|
|
1014
|
+
})),
|
|
850
1015
|
initialIdentity,
|
|
1016
|
+
batchExecution,
|
|
851
1017
|
emitter,
|
|
852
1018
|
});
|
|
853
1019
|
return {
|
|
854
|
-
batch: created.
|
|
1020
|
+
batch: getCheckBatchDetails(input.projectId, created.batchRunId),
|
|
855
1021
|
attempts: listCheckAttemptDetails(input.projectId, created.batchRunId),
|
|
856
1022
|
};
|
|
857
1023
|
});
|
|
@@ -927,7 +1093,20 @@ export const resumeProductionCheckApproval = async (projectId, requestId, emitte
|
|
|
927
1093
|
invalidateApprovalRequest({ projectId, requestId, reason: 'check_batch_no_longer_active', emitter });
|
|
928
1094
|
return false;
|
|
929
1095
|
}
|
|
1096
|
+
if (!batch.batch_environment || !batch.runtime_compatibility || !attempt.placement) {
|
|
1097
|
+
invalidateApprovalRequest({ projectId, requestId, reason: 'check_batch_environment_unavailable', emitter });
|
|
1098
|
+
return false;
|
|
1099
|
+
}
|
|
1100
|
+
const attemptPlacement = attempt.placement;
|
|
930
1101
|
const workspaceCwd = request.action.workspace_git.workspace_path;
|
|
1102
|
+
let batchExecution = batchExecutions.get(batch.execution_run_id);
|
|
1103
|
+
try {
|
|
1104
|
+
batchExecution ??= await restoreBatchExecution({ projectId, workspaceCwd, batch, attempt });
|
|
1105
|
+
}
|
|
1106
|
+
catch {
|
|
1107
|
+
invalidateApprovalRequest({ projectId, requestId, reason: 'check_batch_environment_unavailable', emitter });
|
|
1108
|
+
return false;
|
|
1109
|
+
}
|
|
931
1110
|
let currentIdentity;
|
|
932
1111
|
let currentDefinition;
|
|
933
1112
|
try {
|
|
@@ -964,6 +1143,9 @@ export const resumeProductionCheckApproval = async (projectId, requestId, emitte
|
|
|
964
1143
|
batch,
|
|
965
1144
|
workspaceIdentity: currentIdentity,
|
|
966
1145
|
expiresAt: request.expires_at,
|
|
1146
|
+
launcher: request.action.target.launcher ?? 'host_shell',
|
|
1147
|
+
batchEnvironment: request.action.target.batch_environment ?? null,
|
|
1148
|
+
attemptPlacement,
|
|
967
1149
|
});
|
|
968
1150
|
}
|
|
969
1151
|
catch {
|
|
@@ -986,8 +1168,10 @@ export const resumeProductionCheckApproval = async (projectId, requestId, emitte
|
|
|
986
1168
|
definition: currentDefinition,
|
|
987
1169
|
approvalRequestId: requestId,
|
|
988
1170
|
governedProductionEnvironment: request.authority.selected_profile.compatibility === 'governed',
|
|
1171
|
+
expectedPlacement: attemptPlacement,
|
|
989
1172
|
}],
|
|
990
1173
|
initialIdentity: attempt.initial_workspace_identity,
|
|
1174
|
+
batchExecution,
|
|
991
1175
|
emitter,
|
|
992
1176
|
});
|
|
993
1177
|
return true;
|
|
@@ -1003,6 +1187,15 @@ export const finalizeProductionCheckApproval = (projectId, requestId, emitter) =
|
|
|
1003
1187
|
}
|
|
1004
1188
|
aggregateCheckBatch(projectId, attempt.batch_run_id, emitter);
|
|
1005
1189
|
notifyBatchWaiters(projectId, attempt.batch_run_id);
|
|
1190
|
+
const batch = getCheckBatchDetails(projectId, attempt.batch_run_id);
|
|
1191
|
+
if (batch && ['completed', 'failed', 'cancelled', 'interrupted'].includes(batch.execution_status)) {
|
|
1192
|
+
void releaseBatchExecution(attempt.batch_run_id).catch(error => {
|
|
1193
|
+
console.error('Check batch environment release failed', {
|
|
1194
|
+
batch_run_id: attempt.batch_run_id,
|
|
1195
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1196
|
+
});
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1006
1199
|
};
|
|
1007
1200
|
registerApprovalRequestTerminalHandler((request, emitter) => {
|
|
1008
1201
|
if (request.action_kind === 'custom_check') {
|
|
@@ -1083,6 +1276,9 @@ export const shutdownCheckRunner = async () => {
|
|
|
1083
1276
|
}));
|
|
1084
1277
|
}
|
|
1085
1278
|
finally {
|
|
1279
|
+
await Promise.all([...batchIds]
|
|
1280
|
+
.filter(batchRunId => !approvalHeldBatchIds.has(batchRunId))
|
|
1281
|
+
.map(releaseBatchExecution));
|
|
1086
1282
|
for (const batchRunId of batchIds) {
|
|
1087
1283
|
for (const attempt of durableAttempts.filter(candidate => candidate.batch_run_id === batchRunId)) {
|
|
1088
1284
|
forcedAttemptOutcomes.delete(attempt.attempt_run_id);
|