@treeseed/sdk 0.12.61 → 0.12.62
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/workflow/operations.js +182 -21
- package/dist/workflow/runs.d.ts +1 -0
- package/dist/workflow/runs.js +5 -3
- package/package.json +1 -1
|
@@ -478,6 +478,14 @@ async function waitForWorkflowGates(operation, gates, ciMode, options = {}) {
|
|
|
478
478
|
env: gateEnv,
|
|
479
479
|
onProgress: options.onProgress
|
|
480
480
|
});
|
|
481
|
+
} else if (result.status === "completed" && result.conclusion !== "success" && options.retryFailedOnce) {
|
|
482
|
+
const retry = await rerunGitHubActionsFailedJobs(result, gateEnv);
|
|
483
|
+
options.onProgress?.(`[${operation}][gate][${gateWithTimeout.name}] Retrying failed jobs once for adopted immutable release run ${retry.runId}.`);
|
|
484
|
+
result = await waitForGitHubActionsGate(gateWithTimeout, {
|
|
485
|
+
operation,
|
|
486
|
+
env: gateEnv,
|
|
487
|
+
onProgress: options.onProgress
|
|
488
|
+
});
|
|
481
489
|
}
|
|
482
490
|
const normalized = {
|
|
483
491
|
name: gateWithTimeout.name,
|
|
@@ -1319,6 +1327,62 @@ function ensureReleaseTag(repoDir, tagName, commitSha, message) {
|
|
|
1319
1327
|
remote: remoteCommit ? "existing" : "pushed"
|
|
1320
1328
|
};
|
|
1321
1329
|
}
|
|
1330
|
+
async function adoptPublishedPackageRelease(pkg, version) {
|
|
1331
|
+
const commitSha = tagCommitSha(pkg.dir, version);
|
|
1332
|
+
if (!commitSha) return null;
|
|
1333
|
+
const remoteCommit = remoteTagCommit(pkg.dir, version);
|
|
1334
|
+
if (remoteCommit !== commitSha) {
|
|
1335
|
+
throw new Error(`Release tag ${version} for ${pkg.name} is inconsistent: local=${commitSha}, origin=${remoteCommit ?? "(missing)"}.`);
|
|
1336
|
+
}
|
|
1337
|
+
let taggedManifest;
|
|
1338
|
+
try {
|
|
1339
|
+
taggedManifest = JSON.parse(runGit(["show", `${commitSha}:package.json`], { cwd: pkg.dir, capture: true }));
|
|
1340
|
+
} catch (error) {
|
|
1341
|
+
throw new Error(`Release tag ${version} for ${pkg.name} does not contain a valid package.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
1342
|
+
}
|
|
1343
|
+
if (taggedManifest.name !== pkg.name || taggedManifest.version !== version) {
|
|
1344
|
+
throw new Error(`Release tag ${version} does not identify ${pkg.name}@${version}.`);
|
|
1345
|
+
}
|
|
1346
|
+
if (hasMeaningfulChanges(pkg.dir)) {
|
|
1347
|
+
throw new Error(`Cannot adopt published ${pkg.name}@${version} while ${pkg.dir} has uncommitted changes.`);
|
|
1348
|
+
}
|
|
1349
|
+
const publishedArtifacts = await verifyPublishedReleaseArtifacts(/* @__PURE__ */ new Map([[pkg.name, version]]));
|
|
1350
|
+
const previousStagingHead = remoteHeadCommit(pkg.dir, STAGING_BRANCH);
|
|
1351
|
+
if (previousStagingHead !== commitSha) {
|
|
1352
|
+
runGit([
|
|
1353
|
+
"push",
|
|
1354
|
+
`--force-with-lease=refs/heads/${STAGING_BRANCH}:${previousStagingHead}`,
|
|
1355
|
+
"origin",
|
|
1356
|
+
`${commitSha}:refs/heads/${STAGING_BRANCH}`
|
|
1357
|
+
], { cwd: pkg.dir });
|
|
1358
|
+
}
|
|
1359
|
+
const previousProductionHead = remoteHeadCommit(pkg.dir, PRODUCTION_BRANCH);
|
|
1360
|
+
if (previousProductionHead !== commitSha) {
|
|
1361
|
+
promoteCommitToProductionBranch(pkg.dir, commitSha);
|
|
1362
|
+
}
|
|
1363
|
+
if (currentBranch(pkg.dir) !== STAGING_BRANCH) {
|
|
1364
|
+
checkoutBranch(pkg.dir, STAGING_BRANCH);
|
|
1365
|
+
}
|
|
1366
|
+
if (headCommit(pkg.dir) !== commitSha) {
|
|
1367
|
+
runGit(["reset", "--hard", commitSha], { cwd: pkg.dir });
|
|
1368
|
+
}
|
|
1369
|
+
const observedStagingHead = remoteHeadCommit(pkg.dir, STAGING_BRANCH);
|
|
1370
|
+
const observedProductionHead = remoteHeadCommit(pkg.dir, PRODUCTION_BRANCH);
|
|
1371
|
+
if (headCommit(pkg.dir) !== commitSha || observedStagingHead !== commitSha || observedProductionHead !== commitSha) {
|
|
1372
|
+
throw new Error(`Published release adoption failed for ${pkg.name}@${version}; local=${headCommit(pkg.dir)}, staging=${observedStagingHead}, main=${observedProductionHead}, expected=${commitSha}.`);
|
|
1373
|
+
}
|
|
1374
|
+
return {
|
|
1375
|
+
name: pkg.name,
|
|
1376
|
+
version,
|
|
1377
|
+
commit: { commitSha, status: "adopted-published-tag" },
|
|
1378
|
+
tag: { tagName: version, local: "existing", remote: "existing" },
|
|
1379
|
+
branches: {
|
|
1380
|
+
staging: previousStagingHead === commitSha ? "existing" : "restored",
|
|
1381
|
+
production: previousProductionHead === commitSha ? "existing" : "restored"
|
|
1382
|
+
},
|
|
1383
|
+
publishedArtifacts
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1322
1386
|
function promoteCommitToProductionBranch(repoDir, commitSha) {
|
|
1323
1387
|
const expectedBefore = remoteBranchExists(repoDir, PRODUCTION_BRANCH) ? remoteHeadCommit(repoDir, PRODUCTION_BRANCH) : null;
|
|
1324
1388
|
const lease = expectedBefore ? `--force-with-lease=refs/heads/${PRODUCTION_BRANCH}:${expectedBefore}` : "--force-with-lease";
|
|
@@ -2039,7 +2103,8 @@ function findAutoResumableReleaseRun(root, branch, rootRepo, packageReports, opt
|
|
|
2039
2103
|
}
|
|
2040
2104
|
const classification = classifyWorkflowRunJournal(journal, {
|
|
2041
2105
|
currentBranch: branch,
|
|
2042
|
-
currentHeads
|
|
2106
|
+
currentHeads,
|
|
2107
|
+
acceptedReleaseHeads: acceptedPublishedReleaseHeads(root, journal, currentHeads)
|
|
2043
2108
|
});
|
|
2044
2109
|
if (classification.state !== "resumable") {
|
|
2045
2110
|
if (options.archiveStale && classification.state === "stale") {
|
|
@@ -2061,6 +2126,45 @@ function findAutoResumableReleaseRun(root, branch, rootRepo, packageReports, opt
|
|
|
2061
2126
|
return releasePlan ? releasePlanMatchesCurrentHeads(releasePlan, rootRepo, packageReports) : true;
|
|
2062
2127
|
}) ?? null;
|
|
2063
2128
|
}
|
|
2129
|
+
function acceptedPublishedReleaseHeads(root, journal, currentHeads) {
|
|
2130
|
+
if (journal.command !== "release") return {};
|
|
2131
|
+
const plan = stringRecord(journal.steps.find((step) => step.id === "release-plan")?.data);
|
|
2132
|
+
const plannedVersions = stringRecord(plan?.plannedVersions);
|
|
2133
|
+
if (!plannedVersions) return {};
|
|
2134
|
+
const repos = new Map(checkedOutWorkspacePackageRepos(root).map((repo) => [repo.name, repo.dir]));
|
|
2135
|
+
const accepted = {};
|
|
2136
|
+
for (const [name, versionValue] of Object.entries(plannedVersions)) {
|
|
2137
|
+
if (name === "@treeseed/market" || typeof versionValue !== "string") continue;
|
|
2138
|
+
const repoDir = repos.get(name);
|
|
2139
|
+
const currentHead = currentHeads[name];
|
|
2140
|
+
if (!repoDir || !currentHead) continue;
|
|
2141
|
+
let manifestVersion = null;
|
|
2142
|
+
try {
|
|
2143
|
+
manifestVersion = JSON.parse(readFileSync(resolve(repoDir, "package.json"), "utf8")).version ?? null;
|
|
2144
|
+
} catch {
|
|
2145
|
+
continue;
|
|
2146
|
+
}
|
|
2147
|
+
const tagHead = tagCommitSha(repoDir, versionValue);
|
|
2148
|
+
const remoteTagHead = remoteTagCommit(repoDir, versionValue);
|
|
2149
|
+
const productionHead = remoteHeadCommit(repoDir, PRODUCTION_BRANCH);
|
|
2150
|
+
const stagingHead = remoteHeadCommit(repoDir, STAGING_BRANCH);
|
|
2151
|
+
const exactPublishedHead = tagHead === currentHead && productionHead === currentHead && stagingHead === currentHead;
|
|
2152
|
+
const boundedInterruptedRetry = tagHead.length > 0 && remoteTagHead === tagHead && !hasMeaningfulChanges(repoDir) && [productionHead, stagingHead].every((head) => head === currentHead || head === tagHead);
|
|
2153
|
+
if (manifestVersion !== versionValue || !exactPublishedHead && !boundedInterruptedRetry) {
|
|
2154
|
+
if (process.env.TREESEED_RECONCILE_TRACE === "1") {
|
|
2155
|
+
process.stderr.write(`[release][resume-adoption] package=${name} accepted=false manifest=${manifestVersion === versionValue} tag=${tagHead === currentHead} remoteTag=${remoteTagHead === tagHead} main=${productionHead === currentHead} staging=${stagingHead === currentHead} bounded=${boundedInterruptedRetry}
|
|
2156
|
+
`);
|
|
2157
|
+
}
|
|
2158
|
+
continue;
|
|
2159
|
+
}
|
|
2160
|
+
accepted[name] = currentHead;
|
|
2161
|
+
if (process.env.TREESEED_RECONCILE_TRACE === "1") {
|
|
2162
|
+
process.stderr.write(`[release][resume-adoption] package=${name} accepted=true
|
|
2163
|
+
`);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
return accepted;
|
|
2167
|
+
}
|
|
2064
2168
|
async function executeJournalStep(root, runId, stepId, action, options = {}) {
|
|
2065
2169
|
const current = readWorkflowRunJournal(root, runId);
|
|
2066
2170
|
const step = current?.steps.find((entry) => entry.id === stepId) ?? null;
|
|
@@ -2391,21 +2495,32 @@ async function runReleaseNpmInstall(repoDir, options = {}) {
|
|
|
2391
2495
|
if (repoDir === options.workspaceRoot && lockfileRootMatchesManifest(repoDir)) {
|
|
2392
2496
|
return { status: "skipped", reason: "root-lockfile-already-matches", attempts: 0 };
|
|
2393
2497
|
}
|
|
2394
|
-
const
|
|
2395
|
-
const
|
|
2498
|
+
const baseArgs = ["install", "--package-lock-only", "--ignore-scripts", "--workspaces=false", "--no-audit", "--no-fund"];
|
|
2499
|
+
const propagationDelaysMs = [15e3, 3e4, 6e4, 12e4, 18e4];
|
|
2500
|
+
const startedAt = Date.now();
|
|
2396
2501
|
let lastDetail = "";
|
|
2397
|
-
for (let attempt = 1; attempt <=
|
|
2398
|
-
|
|
2502
|
+
for (let attempt = 1; attempt <= propagationDelaysMs.length + 1; attempt += 1) {
|
|
2503
|
+
const args = [...baseArgs, attempt === 1 ? "--prefer-offline" : "--prefer-online"];
|
|
2504
|
+
const spawnCommand = npmCommandForWorkflowSpawn(args);
|
|
2505
|
+
process.stderr.write(`[release][restore] repository=${repoDir} phase=package-lock attempt=${attempt} elapsed=${Math.ceil((Date.now() - startedAt) / 1e3)}s
|
|
2399
2506
|
`);
|
|
2400
2507
|
const result = await waitForReleaseInstall(spawnCommand.command, spawnCommand.args, repoDir, attempt);
|
|
2401
2508
|
if (result.status === 0) {
|
|
2402
2509
|
return { status: "completed", reason: null, attempts: attempt };
|
|
2403
2510
|
}
|
|
2404
2511
|
lastDetail = result.detail;
|
|
2405
|
-
|
|
2406
|
-
|
|
2512
|
+
const propagationDelayMs = propagationDelaysMs[attempt - 1];
|
|
2513
|
+
if (!/No matching version found|notarget|ETARGET|E404/u.test(lastDetail) || propagationDelayMs == null) break;
|
|
2514
|
+
let remainingMs = propagationDelayMs;
|
|
2515
|
+
while (remainingMs > 0) {
|
|
2516
|
+
process.stderr.write(`[release][restore] repository=${repoDir} phase=registry-propagation nextAttempt=${attempt + 1} remaining=${Math.ceil(remainingMs / 1e3)}s elapsed=${Math.ceil((Date.now() - startedAt) / 1e3)}s
|
|
2517
|
+
`);
|
|
2518
|
+
const sliceMs = Math.min(15e3, remainingMs);
|
|
2519
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, sliceMs));
|
|
2520
|
+
remainingMs -= sliceMs;
|
|
2521
|
+
}
|
|
2407
2522
|
}
|
|
2408
|
-
throw new Error(lastDetail || `npm ${
|
|
2523
|
+
throw new Error(lastDetail || `npm ${baseArgs.join(" ")} failed`);
|
|
2409
2524
|
}
|
|
2410
2525
|
function pathIsWithin(parent, candidate) {
|
|
2411
2526
|
const path = relative(parent, candidate);
|
|
@@ -4676,7 +4791,7 @@ async function workflowSave(helpers, input) {
|
|
|
4676
4791
|
const recursiveWorkspace = session.mode === "recursive-workspace";
|
|
4677
4792
|
const mode = session.mode;
|
|
4678
4793
|
const executionMode = normalizeExecutionMode(input);
|
|
4679
|
-
const explicitResumeRunId = helpers.context.workflow?.resumeRunId ?? null;
|
|
4794
|
+
const explicitResumeRunId = helpers.context.workflow?.resumeRunId ?? input.resumeRunId ?? null;
|
|
4680
4795
|
const autoResumeRun = executionMode === "execute" && !explicitResumeRunId ? findAutoResumableSaveRun(root, branch) : null;
|
|
4681
4796
|
rejectImplicitWorkflowResume("save", autoResumeRun);
|
|
4682
4797
|
const planAutoResumeRun = null;
|
|
@@ -4841,7 +4956,10 @@ async function workflowSave(helpers, input) {
|
|
|
4841
4956
|
resumable: true
|
|
4842
4957
|
}] : []
|
|
4843
4958
|
],
|
|
4844
|
-
|
|
4959
|
+
explicitResumeRunId ? {
|
|
4960
|
+
...helpers.context,
|
|
4961
|
+
workflow: { ...helpers.context.workflow ?? {}, resumeRunId: explicitResumeRunId }
|
|
4962
|
+
} : autoResumeRun ? {
|
|
4845
4963
|
...helpers.context,
|
|
4846
4964
|
workflow: {
|
|
4847
4965
|
...helpers.context.workflow ?? {},
|
|
@@ -5127,7 +5245,7 @@ async function workflowClose(helpers, input) {
|
|
|
5127
5245
|
const root = workspaceRoot(tenantRoot);
|
|
5128
5246
|
const executionMode = normalizeExecutionMode(input);
|
|
5129
5247
|
const session = resolveTreeseedWorkflowSession(root);
|
|
5130
|
-
const explicitResumeRunId = helpers.context.workflow?.resumeRunId ?? null;
|
|
5248
|
+
const explicitResumeRunId = helpers.context.workflow?.resumeRunId ?? input.resumeRunId ?? null;
|
|
5131
5249
|
const autoResumeRun = executionMode === "execute" && !explicitResumeRunId ? findAutoResumableTaskRun(root, "close", session.branchName) : null;
|
|
5132
5250
|
rejectImplicitWorkflowResume("close", autoResumeRun);
|
|
5133
5251
|
const planAutoResumeRun = null;
|
|
@@ -5214,7 +5332,13 @@ async function workflowClose(helpers, input) {
|
|
|
5214
5332
|
{ id: "workspace-link", description: "Restore local workspace links", repoName: rootRepo.name, repoPath: rootRepo.path, branch: STAGING_BRANCH, resumable: true },
|
|
5215
5333
|
...isManagedWorkflowWorktree(root) ? [{ id: "worktree-cleanup", description: "Remove managed workflow worktree", repoName: rootRepo.name, repoPath: rootRepo.path, branch: STAGING_BRANCH, resumable: false }] : []
|
|
5216
5334
|
],
|
|
5217
|
-
|
|
5335
|
+
explicitResumeRunId ? {
|
|
5336
|
+
...helpers.context,
|
|
5337
|
+
workflow: {
|
|
5338
|
+
...helpers.context.workflow ?? {},
|
|
5339
|
+
resumeRunId: explicitResumeRunId
|
|
5340
|
+
}
|
|
5341
|
+
} : autoResumeRun ? {
|
|
5218
5342
|
...helpers.context,
|
|
5219
5343
|
workflow: {
|
|
5220
5344
|
...helpers.context.workflow ?? {},
|
|
@@ -5611,7 +5735,7 @@ async function workflowStage(helpers, input) {
|
|
|
5611
5735
|
const root = workspaceRoot(tenantRoot);
|
|
5612
5736
|
const executionMode = normalizeExecutionMode(input);
|
|
5613
5737
|
const session = resolveTreeseedWorkflowSession(root);
|
|
5614
|
-
const explicitResumeRunId = helpers.context.workflow?.resumeRunId ?? null;
|
|
5738
|
+
const explicitResumeRunId = helpers.context.workflow?.resumeRunId ?? input.resumeRunId ?? null;
|
|
5615
5739
|
const rawAutoResumeRun = executionMode === "execute" && !explicitResumeRunId ? findAutoResumableTaskRun(root, "stage", session.branchName) : null;
|
|
5616
5740
|
rejectImplicitWorkflowResume("stage", rawAutoResumeRun);
|
|
5617
5741
|
const autoResumeRun = rawAutoResumeRun?.steps.some((step) => step.id === "preflight") ? rawAutoResumeRun : null;
|
|
@@ -6026,7 +6150,9 @@ async function workflowRelease(helpers, input) {
|
|
|
6026
6150
|
const rootRepo = createWorkspaceRootRepoReport(root);
|
|
6027
6151
|
const packageReports = createWorkspacePackageReports(root);
|
|
6028
6152
|
const releaseHelperRepos = checkedOutReleaseHelperRepos(root);
|
|
6029
|
-
const explicitResumeRunId = helpers.context.workflow?.resumeRunId ?? null;
|
|
6153
|
+
const explicitResumeRunId = helpers.context.workflow?.resumeRunId ?? input.resumeRunId ?? null;
|
|
6154
|
+
const explicitResumeJournal = explicitResumeRunId ? readWorkflowRunJournal(root, explicitResumeRunId) : null;
|
|
6155
|
+
const recordedReleasePlan = explicitResumeJournal?.command === "release" ? stringRecord(explicitResumeJournal.steps.find((step) => step.id === "release-plan")?.data) : null;
|
|
6030
6156
|
const autoResumeRun = executionMode === "execute" && !explicitResumeRunId && input.fresh !== true ? findAutoResumableReleaseRun(root, session.branchName, rootRepo, packageReports, { archiveStale: false }) : null;
|
|
6031
6157
|
const planAutoResumeRun = executionMode === "plan" && input.fresh !== true ? findAutoResumableReleaseRun(root, session.branchName, rootRepo, packageReports) : null;
|
|
6032
6158
|
const effectiveInput = autoResumeRun ? {
|
|
@@ -6037,7 +6163,7 @@ async function workflowRelease(helpers, input) {
|
|
|
6037
6163
|
const level = effectiveInput.bump ?? "patch";
|
|
6038
6164
|
const ciMode = normalizeCiMode(effectiveInput.ciMode, "release");
|
|
6039
6165
|
const packageSelection = session.packageSelection;
|
|
6040
|
-
const plannedRelease = buildReleasePlanSnapshot({
|
|
6166
|
+
const plannedRelease = recordedReleasePlan ?? buildReleasePlanSnapshot({
|
|
6041
6167
|
root,
|
|
6042
6168
|
mode: session.mode,
|
|
6043
6169
|
level,
|
|
@@ -6049,12 +6175,14 @@ async function workflowRelease(helpers, input) {
|
|
|
6049
6175
|
blockers: []
|
|
6050
6176
|
});
|
|
6051
6177
|
const selectedPackageNames = releasePlanPackageSelection(plannedRelease.packageSelection).selected;
|
|
6052
|
-
const blockers = collectReleasePlanBlockers(session, session.mode, selectedPackageNames, {
|
|
6178
|
+
const blockers = explicitResumeJournal ? [] : collectReleasePlanBlockers(session, session.mode, selectedPackageNames, {
|
|
6053
6179
|
level,
|
|
6054
6180
|
repairVersionLine: effectiveInput.repairVersionLine === true
|
|
6055
6181
|
});
|
|
6056
|
-
|
|
6057
|
-
|
|
6182
|
+
if (!explicitResumeJournal) {
|
|
6183
|
+
blockers.push(...collectReleaseHelperRepoBlockers(root));
|
|
6184
|
+
blockers.push(...stageCandidateAttestationBlockers(root));
|
|
6185
|
+
}
|
|
6058
6186
|
const selectedVersions = releasePlanVersionMap(plannedRelease.plannedVersions);
|
|
6059
6187
|
const releaseImageVersions = productionReleaseImageRefVersions(root, selectedVersions);
|
|
6060
6188
|
const releaseImageRefs = productionReleaseImageRefEnv(releaseImageVersions);
|
|
@@ -6160,7 +6288,13 @@ ${blockers.join("\n")}`, {
|
|
|
6160
6288
|
{ id: "release-back-merge", description: "Back-merge production release history into staging", repoName: rootRepo.name, repoPath: rootRepo.path, branch: STAGING_BRANCH, resumable: true },
|
|
6161
6289
|
{ id: "workspace-link", description: "Restore local workspace links after release", repoName: rootRepo.name, repoPath: rootRepo.path, branch: STAGING_BRANCH, resumable: true }
|
|
6162
6290
|
],
|
|
6163
|
-
|
|
6291
|
+
explicitResumeRunId ? {
|
|
6292
|
+
...helpers.context,
|
|
6293
|
+
workflow: {
|
|
6294
|
+
...helpers.context.workflow ?? {},
|
|
6295
|
+
resumeRunId: explicitResumeRunId
|
|
6296
|
+
}
|
|
6297
|
+
} : autoResumeRun ? {
|
|
6164
6298
|
...helpers.context,
|
|
6165
6299
|
workflow: {
|
|
6166
6300
|
...helpers.context.workflow ?? {},
|
|
@@ -6238,6 +6372,9 @@ ${rendered}`);
|
|
|
6238
6372
|
if (packageName !== "@treeseed/sdk" && pendingPackageReleases.has("@treeseed/sdk") && !dependencies.includes("@treeseed/sdk")) {
|
|
6239
6373
|
dependencies.push("@treeseed/sdk");
|
|
6240
6374
|
}
|
|
6375
|
+
if (packageName === "@treeseed/api" && pendingPackageReleases.has("@treeseed/cli") && !dependencies.includes("@treeseed/cli")) {
|
|
6376
|
+
dependencies.push("@treeseed/cli");
|
|
6377
|
+
}
|
|
6241
6378
|
return [packageName, dependencies];
|
|
6242
6379
|
}));
|
|
6243
6380
|
while (pendingPackageReleases.size > 0) {
|
|
@@ -6251,6 +6388,26 @@ ${rendered}`);
|
|
|
6251
6388
|
const pkg = packageRepoByName.get(packageName);
|
|
6252
6389
|
const version = selectedVersions.get(pkg.name);
|
|
6253
6390
|
const packageRelease = await executeJournalStep(root, workflowRun.runId, `release-${pkg.name}`, async () => {
|
|
6391
|
+
const adopted = await adoptPublishedPackageRelease(pkg, version);
|
|
6392
|
+
if (adopted) {
|
|
6393
|
+
helpers.write(`[release][packages] adopted verified published release ${pkg.name}@${version}.`, "stderr");
|
|
6394
|
+
if (pkg.name === "@treeseed/api") {
|
|
6395
|
+
const publishWait3 = await waitForWorkflowGates("release", [{
|
|
6396
|
+
name: pkg.name,
|
|
6397
|
+
repoPath: pkg.dir,
|
|
6398
|
+
workflow: releaseWorkflowForPackage(root, pkg.name),
|
|
6399
|
+
branch: version,
|
|
6400
|
+
headSha: String(stringRecord(adopted.commit)?.commitSha ?? tagCommitSha(pkg.dir, version))
|
|
6401
|
+
}], ciMode, {
|
|
6402
|
+
root,
|
|
6403
|
+
runId: workflowRun.runId,
|
|
6404
|
+
onProgress: (line, stream) => helpers.write(line, stream),
|
|
6405
|
+
retryFailedOnce: true
|
|
6406
|
+
});
|
|
6407
|
+
return { ...adopted, publishWait: publishWait3 };
|
|
6408
|
+
}
|
|
6409
|
+
return adopted;
|
|
6410
|
+
}
|
|
6254
6411
|
const metadata = await prepareAdapterReleaseMetadata(root, pkg, version);
|
|
6255
6412
|
const changelog = updateReleaseChangelog(pkg.dir, {
|
|
6256
6413
|
version,
|
|
@@ -6483,7 +6640,8 @@ async function workflowResume(helpers, input) {
|
|
|
6483
6640
|
);
|
|
6484
6641
|
const classification = classifyWorkflowRunJournal(journal, {
|
|
6485
6642
|
currentBranch: session.branchName,
|
|
6486
|
-
currentHeads
|
|
6643
|
+
currentHeads,
|
|
6644
|
+
acceptedReleaseHeads: acceptedPublishedReleaseHeads(root, journal, currentHeads)
|
|
6487
6645
|
});
|
|
6488
6646
|
if (classification.state !== "resumable") {
|
|
6489
6647
|
workflowError("resume", "resume_unavailable", `Run ${runId} is ${classification.state} and is not safe to resume.`, {
|
|
@@ -6513,7 +6671,10 @@ async function workflowResume(helpers, input) {
|
|
|
6513
6671
|
case "stage":
|
|
6514
6672
|
return workflowStage(resumedHelpers, journal.input);
|
|
6515
6673
|
case "release":
|
|
6516
|
-
return workflowRelease(resumedHelpers,
|
|
6674
|
+
return workflowRelease(resumedHelpers, {
|
|
6675
|
+
...journal.input,
|
|
6676
|
+
resumeRunId: runId
|
|
6677
|
+
});
|
|
6517
6678
|
case "destroy":
|
|
6518
6679
|
return workflowDestroy(resumedHelpers, journal.input);
|
|
6519
6680
|
default:
|
package/dist/workflow/runs.d.ts
CHANGED
|
@@ -113,6 +113,7 @@ export declare function updateWorkflowRunJournal(root: string, runId: string, up
|
|
|
113
113
|
export declare function classifyWorkflowRunJournal(journal: TreeseedWorkflowRunJournal, options?: {
|
|
114
114
|
currentBranch?: string | null;
|
|
115
115
|
currentHeads?: Record<string, string | null | undefined>;
|
|
116
|
+
acceptedReleaseHeads?: Record<string, string | null | undefined>;
|
|
116
117
|
now?: string;
|
|
117
118
|
}): TreeseedWorkflowRunClassification;
|
|
118
119
|
export declare function classifyWorkflowRunJournals(root: string, options?: Parameters<typeof classifyWorkflowRunJournal>[1]): {
|
package/dist/workflow/runs.js
CHANGED
|
@@ -406,6 +406,8 @@ function expectedPackageHeadAfterReleaseGate(journal, packageName) {
|
|
|
406
406
|
const data = releaseStepData(journal, `release-${packageName}`);
|
|
407
407
|
const backMerge = stringRecord(data?.backMerge);
|
|
408
408
|
if (typeof backMerge?.commitSha === "string") return backMerge.commitSha;
|
|
409
|
+
const commit = stringRecord(data?.commit);
|
|
410
|
+
if (typeof commit?.commitSha === "string") return commit.commitSha;
|
|
409
411
|
if (typeof data?.commitSha === "string") return data.commitSha;
|
|
410
412
|
return null;
|
|
411
413
|
}
|
|
@@ -512,9 +514,9 @@ function classifyWorkflowRunJournal(journal, options = {}) {
|
|
|
512
514
|
}
|
|
513
515
|
for (const name of selectedReleasePackageNames(releasePlan)) {
|
|
514
516
|
const currentHead = options.currentHeads[name];
|
|
515
|
-
const
|
|
516
|
-
if (currentHead &&
|
|
517
|
-
reasons.push(`${name} head changed from ${
|
|
517
|
+
const expectedHead = expectedPackageHeadAfterReleaseGate(journal, name) ?? options.acceptedReleaseHeads?.[name] ?? journalReleasePlanHead(releasePlan, name);
|
|
518
|
+
if (currentHead && expectedHead && currentHead !== expectedHead) {
|
|
519
|
+
reasons.push(`${name} head changed from ${expectedHead} to ${currentHead}`);
|
|
518
520
|
}
|
|
519
521
|
}
|
|
520
522
|
}
|