@kungfu-tech/buildchain 4.1.3-alpha.0 → 4.1.3-alpha.2
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/architecture/action-taxonomy.json +16 -0
- package/architecture/agent-change-map.md +245 -4
- package/architecture/ci-lane-change-budget.json +289 -0
- package/architecture/decisions/0005-minimal-consumer-contract.md +122 -0
- package/architecture/decisions/0006-business-attempt-journal.md +89 -0
- package/architecture/decisions/0007-hosted-pipeline-controller.md +106 -0
- package/architecture/decisions/0008-pipeline-product-publication.md +99 -0
- package/architecture/internal-capabilities.json +340 -4
- package/architecture/maintainability-debt.json +3 -1
- package/architecture/maintainability-policy.json +8 -8
- package/architecture/minimal-consumer-migration.json +7558 -0
- package/architecture/release-topology.json +248 -11
- package/architecture/universal-workflow-bootstrap.json +10 -0
- package/architecture/universal-workflow-capability-policy.json +6 -6
- package/architecture/workflow-taxonomy.json +55 -0
- package/contracts/release-discussion-v1.schema.json +3 -0
- package/dist/readers/business-attempt.cjs +805 -0
- package/dist/readers/release-discussion.cjs +4 -0
- package/dist/site/buildchain-contract.json +7 -5
- package/dist/site/buildchain-site.json +25 -15
- package/dist/site/capability-registry.json +3 -3
- package/dist/site/kfd-claims.json +311 -10
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +1 -1
- package/dist/site/node-api-registry.json +9 -9
- package/dist/site/page-registry.json +20 -10
- package/dist/site/public-surface-audit.json +201 -7
- package/dist/site/publication-authority-registry.json +121 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +5 -5
- package/dist/site/workflow-registry.json +416 -7
- package/docs/node-api-reference.md +12 -12
- package/docs/release-discussions.md +44 -5
- package/docs/runtime-entry.md +45 -5
- package/docs/workflow-catalog.md +5 -0
- package/package.json +4 -3
- package/packages/core/consumer/contract/entries.js +85 -0
- package/packages/core/consumer/contract/examples.js +96 -0
- package/packages/core/consumer/contract/identity.js +65 -0
- package/packages/core/consumer/contract/inspection.js +45 -0
- package/packages/core/consumer/contract/plan.js +99 -0
- package/packages/core/consumer/contract/products.js +101 -0
- package/packages/core/consumer/contract/shape.js +46 -0
- package/packages/core/dev-delivery/candidate/admission.js +9 -5
- package/packages/core/dev-delivery/candidate/reservation.js +11 -0
- package/packages/core/dev-delivery/candidate/source-paths.js +12 -4
- package/packages/core/dev-delivery/native/actions.js +7 -1
- package/packages/core/dev-delivery/native/heartbeat-action.js +16 -0
- package/packages/core/dev-delivery/native/heartbeat.js +6 -4
- package/packages/core/dev-delivery/queue/landing-action.js +5 -0
- package/packages/core/governance/buildchain-publication-authority.js +5 -0
- package/packages/core/paper/operations/bootstrap.js +2 -0
- package/packages/core/paper/paper-npm-bootstrap.js +2 -2
- package/packages/core/providers/github/attempt-index.js +60 -0
- package/packages/core/providers/github/attempt-journal.js +154 -0
- package/packages/core/providers/github/discussions/materials.js +23 -7
- package/packages/core/providers/github/discussions/transport.js +16 -5
- package/packages/core/providers/github/pipeline-checkout.js +49 -0
- package/packages/core/providers/github/pipeline-events.js +52 -0
- package/packages/core/providers/github/pipeline-integration.js +95 -0
- package/packages/core/providers/github/pipeline-policy.js +213 -0
- package/packages/core/providers/github/pipeline-product-payload.js +50 -0
- package/packages/core/providers/github/pipeline-product-release.js +191 -0
- package/packages/core/providers/github/pipeline-publication-artifacts.js +153 -0
- package/packages/core/providers/github/pipeline-run-entry.js +86 -0
- package/packages/core/providers/github/pipeline-runs.js +132 -0
- package/packages/core/providers/github/pipeline-source.js +178 -0
- package/packages/core/providers/github/pipeline-version.js +201 -0
- package/packages/core/providers/github/pipeline-worker.js +108 -0
- package/packages/core/publication/candidate/registry-hydration.js +2 -2
- package/packages/core/publication/npm/pack-preview.js +2 -2
- package/packages/core/publication/npm/pack-result.js +31 -0
- package/packages/core/publication/npm/package.js +3 -6
- package/packages/core/publication/npm/pipeline-channel.js +87 -0
- package/packages/core/publication/npm/pipeline-provider.js +109 -0
- package/packages/core/publication/npm/registry.js +12 -9
- package/packages/core/publication/pipeline/actions.js +100 -0
- package/packages/core/publication/pipeline/apply.js +182 -0
- package/packages/core/publication/pipeline/build-segments.js +107 -0
- package/packages/core/publication/pipeline/capsules.js +89 -0
- package/packages/core/publication/pipeline/context.js +39 -0
- package/packages/core/publication/pipeline/development-anchor.js +91 -0
- package/packages/core/publication/pipeline/development-current.js +63 -0
- package/packages/core/publication/pipeline/development-pr.js +85 -0
- package/packages/core/publication/pipeline/development-proof.js +76 -0
- package/packages/core/publication/pipeline/development-transition.js +112 -0
- package/packages/core/publication/pipeline/distribution.js +124 -0
- package/packages/core/publication/pipeline/documents.js +156 -0
- package/packages/core/publication/pipeline/effects.js +162 -0
- package/packages/core/publication/pipeline/files.js +66 -0
- package/packages/core/publication/pipeline/imported-materials.js +32 -0
- package/packages/core/publication/pipeline/journal.js +110 -0
- package/packages/core/publication/pipeline/next-development.js +116 -0
- package/packages/core/publication/pipeline/pack.js +151 -0
- package/packages/core/publication/pipeline/package-policy.js +22 -0
- package/packages/core/publication/pipeline/plan.js +114 -0
- package/packages/core/publication/pipeline/prepare.js +151 -0
- package/packages/core/publication/pipeline/qualification.js +170 -0
- package/packages/core/publication/pipeline/qualify.js +104 -0
- package/packages/core/publication/pipeline/recovery-admission.js +122 -0
- package/packages/core/publication/pipeline/recovery-build-download.js +80 -0
- package/packages/core/publication/pipeline/recovery-build-plan.js +122 -0
- package/packages/core/publication/pipeline/recovery-build-readback.js +68 -0
- package/packages/core/publication/pipeline/recovery-capsules.js +54 -0
- package/packages/core/publication/pipeline/recovery-import.js +80 -0
- package/packages/core/publication/pipeline/recovery-inspection.js +218 -0
- package/packages/core/publication/pipeline/recovery-materials.js +104 -0
- package/packages/core/publication/pipeline/recovery-plan.js +46 -0
- package/packages/core/publication/pipeline/recovery-prepare.js +73 -0
- package/packages/core/publication/pipeline/recovery-qualification.js +128 -0
- package/packages/core/publication/pipeline/recovery-readback.js +56 -0
- package/packages/core/publication/pipeline/recovery-signing.js +58 -0
- package/packages/core/publication/pipeline/sealed-products.js +79 -0
- package/packages/core/publication/pipeline/settle.js +43 -0
- package/packages/core/publication/pipeline/signing.js +140 -0
- package/packages/core/publication/pipeline/source-plan.js +83 -0
- package/packages/core/publication/pipeline/version.js +81 -0
- package/packages/core/publication/publication-reproducibility.js +2 -2
- package/packages/core/release/discussion/binary.js +11 -0
- package/packages/core/release/discussion/checkpoints.js +45 -4
- package/packages/core/release/discussion/envelope.js +1 -0
- package/packages/core/release/discussion/evidence.js +51 -0
- package/packages/core/release/discussion/presentation.js +92 -0
- package/packages/core/release/discussion/publication.js +3 -1
- package/packages/core/release/discussion/qualification.js +1 -0
- package/packages/core/release/discussion/reader.js +9 -0
- package/packages/core/release/discussion/session.js +2 -0
- package/packages/core/release/discussion/store.js +28 -31
- package/packages/core/release/discussion/threads.js +83 -0
- package/packages/core/release/promote-candidate/product-provider-adapters.js +4 -3
- package/packages/core/release/promote-ref/internal/npm-existing-evidence.js +2 -1
- package/packages/core/release/promote-ref/internal/publish-command.js +16 -5
- package/packages/core/runtime/buildchain-domain.wasm +0 -0
- package/packages/core/runtime/domain-wasm-artifact.js +2 -2
- package/packages/core/runtime/entry/actions.js +27 -25
- package/packages/core/runtime/entry/attempt.js +25 -0
- package/packages/core/runtime/entry/selection.js +14 -0
- package/packages/core/runtime/entry/source.js +62 -0
- package/packages/core/workflow/attempt/identity.js +165 -0
- package/packages/core/workflow/attempt/journal.js +87 -0
- package/packages/core/workflow/attempt/materials.js +72 -0
- package/packages/core/workflow/attempt/reader-entry.js +12 -0
- package/packages/core/workflow/attempt/reader.js +149 -0
- package/packages/core/workflow/attempt/records.js +153 -0
- package/packages/core/workflow/attempt/store.js +75 -0
- package/packages/core/workflow/commands/pipeline-build.mjs +23 -0
- package/packages/core/workflow/pipeline/action-output.js +11 -0
- package/packages/core/workflow/pipeline/actions.js +62 -0
- package/packages/core/workflow/pipeline/build-control.js +140 -0
- package/packages/core/workflow/pipeline/build-evidence.js +92 -0
- package/packages/core/workflow/pipeline/build-qualification.js +145 -0
- package/packages/core/workflow/pipeline/build-result.js +69 -0
- package/packages/core/workflow/pipeline/build.js +95 -0
- package/packages/core/workflow/pipeline/cancellation.js +169 -0
- package/packages/core/workflow/pipeline/channel-control.js +127 -0
- package/packages/core/workflow/pipeline/controller.js +133 -0
- package/packages/core/workflow/pipeline/delivery-control.js +204 -0
- package/packages/core/workflow/pipeline/delivery-observation.js +118 -0
- package/packages/core/workflow/pipeline/delivery-request.js +198 -0
- package/packages/core/workflow/pipeline/events.js +84 -0
- package/packages/core/workflow/pipeline/fence.js +42 -0
- package/packages/core/workflow/pipeline/group-control.js +56 -0
- package/packages/core/workflow/pipeline/guard-build.js +37 -0
- package/packages/core/workflow/pipeline/guard.js +156 -0
- package/packages/core/workflow/pipeline/host.js +114 -0
- package/packages/core/workflow/pipeline/materials.js +58 -0
- package/packages/core/workflow/pipeline/notifications.js +36 -0
- package/packages/core/workflow/pipeline/parent-notification.js +27 -0
- package/packages/core/workflow/pipeline/platforms.js +21 -0
- package/packages/core/workflow/pipeline/progress.js +108 -0
- package/packages/core/workflow/pipeline/projection.js +20 -0
- package/packages/core/workflow/pipeline/reconcile.js +194 -0
- package/packages/core/workflow/pipeline/recovery-action.js +30 -0
- package/packages/core/workflow/pipeline/recovery-admission.js +122 -0
- package/packages/core/workflow/pipeline/recovery-build-control.js +95 -0
- package/packages/core/workflow/pipeline/recovery-build-evidence.js +71 -0
- package/packages/core/workflow/pipeline/recovery-build.js +121 -0
- package/packages/core/workflow/pipeline/recovery-controller.js +91 -0
- package/packages/core/workflow/pipeline/recovery-integration.js +66 -0
- package/packages/core/workflow/pipeline/recovery-merge-proof.js +90 -0
- package/packages/core/workflow/pipeline/recovery-ownership-settlement.js +150 -0
- package/packages/core/workflow/pipeline/recovery-ownership.js +91 -0
- package/packages/core/workflow/pipeline/recovery-plan.js +126 -0
- package/packages/core/workflow/pipeline/recovery-runtime.js +68 -0
- package/packages/core/workflow/pipeline/recovery-session.js +129 -0
- package/packages/core/workflow/pipeline/recovery-transition.js +135 -0
- package/packages/core/workflow/pipeline/recovery-unrecorded-build.js +74 -0
- package/packages/core/workflow/pipeline/runtime-source.js +111 -0
- package/packages/core/workflow/pipeline/selection.js +106 -0
- package/packages/core/workflow/pipeline/session.js +72 -0
- package/packages/core/workflow/pipeline/settlement.js +137 -0
- package/packages/core/workflow/pipeline/wake.js +38 -0
- package/packages/core/workflow/pipeline/web-status.js +108 -0
- package/scripts/build-release-discussion-reader.mjs +11 -7
- package/scripts/check-pipeline-publication-topology.mjs +36 -0
- package/scripts/check-release-topology.mjs +6 -1
- package/scripts/generate-minimal-consumer-contract.mjs +155 -0
- package/scripts/verify-golden-path.mjs +5 -1
- package/templates/minimal-consumer/binary/.buildchain/buildchain.toml +45 -0
- package/templates/minimal-consumer/binary/.github/workflows/buildchain-recover.yml +30 -0
- package/templates/minimal-consumer/binary/.github/workflows/buildchain.yml +28 -0
- package/templates/minimal-consumer/binary/package.json +6 -0
- package/templates/minimal-consumer/binary/src/build.mjs +5 -0
- package/templates/minimal-consumer/binary/src/hello.c +2 -0
- package/templates/minimal-consumer/binary/src/verify.mjs +3 -0
- package/templates/minimal-consumer/npm/.buildchain/buildchain.toml +46 -0
- package/templates/minimal-consumer/npm/.github/workflows/buildchain-recover.yml +30 -0
- package/templates/minimal-consumer/npm/.github/workflows/buildchain.yml +28 -0
- package/templates/minimal-consumer/npm/package.json +6 -0
- package/templates/minimal-consumer/npm/src/build.mjs +5 -0
- package/templates/minimal-consumer/npm/src/verify.mjs +3 -0
- package/templates/minimal-consumer/paper/.buildchain/buildchain.toml +45 -0
- package/templates/minimal-consumer/paper/.github/workflows/buildchain-recover.yml +30 -0
- package/templates/minimal-consumer/paper/.github/workflows/buildchain.yml +28 -0
- package/templates/minimal-consumer/paper/package.json +6 -0
- package/templates/minimal-consumer/paper/src/build.mjs +12 -0
- package/templates/minimal-consumer/paper/src/verify.mjs +3 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { isAttemptRoot } from "./threads.js";
|
|
2
|
+
|
|
3
|
+
const text = (value) =>
|
|
4
|
+
String(value ?? "")
|
|
5
|
+
.replace(
|
|
6
|
+
/[&<>\[\]`*_\\|]/gu,
|
|
7
|
+
(character) => `&#${character.charCodeAt(0)};`,
|
|
8
|
+
)
|
|
9
|
+
.replace(/[\r\n]/gu, " ");
|
|
10
|
+
const code = (value) => `\`${String(value).replace(/[`\r\n]/gu, " ")}\``;
|
|
11
|
+
|
|
12
|
+
function runLink(repository, writer) {
|
|
13
|
+
const match = /^(\d+):(\d+)(?::.*)?$/u.exec(writer);
|
|
14
|
+
return match
|
|
15
|
+
? `[${text(writer)}](https://github.com/${repository}/actions/runs/${match[1]}/attempts/${match[2]})`
|
|
16
|
+
: code(writer);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function runtimeLink(runtime) {
|
|
20
|
+
return `[${runtime.repository}@${runtime.sha.slice(0, 12)}](https://github.com/${runtime.repository}/commit/${runtime.sha})`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function renderIntent(intent) {
|
|
24
|
+
return [
|
|
25
|
+
`## ${intent.source?.qualification ? "Qualification" : "Release"}: ${text(intent.key)}`,
|
|
26
|
+
`Repository: ${code(intent.repository)}`,
|
|
27
|
+
"Each top-level transaction comment owns one attempt. Its replies contain node results, recovery checkpoints and downloadable evidence. Recovery attempts link to their predecessors.",
|
|
28
|
+
`Expected nodes: ${intent.expectedNodes.map(code).join(" → ")}`,
|
|
29
|
+
`Intent: ${code(intent.id)}`,
|
|
30
|
+
].join("\n\n");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function attachmentRows(record, repository) {
|
|
34
|
+
return (record.payload?.attachments || []).map((attachment) => {
|
|
35
|
+
const url = new URL(attachment.downloadUrl);
|
|
36
|
+
if (
|
|
37
|
+
url.protocol !== "https:" ||
|
|
38
|
+
url.hostname !== "github.com" ||
|
|
39
|
+
!url.pathname.startsWith(`/${repository}/releases/download/`) ||
|
|
40
|
+
url.search ||
|
|
41
|
+
url.hash
|
|
42
|
+
)
|
|
43
|
+
throw new Error(
|
|
44
|
+
"Attachment download must belong to the consumer repository",
|
|
45
|
+
);
|
|
46
|
+
return `| [${text(attachment.name)}](${url.href}) | ${text(attachment.mediaType)} | ${attachment.size} | ${code(attachment.digest)} |`;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function renderEvent(record, intent, state) {
|
|
51
|
+
if (isAttemptRoot(record)) {
|
|
52
|
+
const predecessor = state.roots.get(record.predecessor);
|
|
53
|
+
return [
|
|
54
|
+
`## Attempt ${text(record.attempt)}`,
|
|
55
|
+
`Execution: ${runLink(intent.repository, record.writer)} \nRuntime: ${runtimeLink(record.runtime)}`,
|
|
56
|
+
record.predecessor
|
|
57
|
+
? `Resumes: ${predecessor?.url ? `[${text(record.predecessor)}](${predecessor.url})` : code(record.predecessor)}`
|
|
58
|
+
: "Initial execution of this release intent.",
|
|
59
|
+
`Nodes: ${intent.expectedNodes.map(code).join(" → ")}`,
|
|
60
|
+
"Results and evidence are appended below. This root preserves the original execution context.",
|
|
61
|
+
].join("\n\n");
|
|
62
|
+
}
|
|
63
|
+
const label =
|
|
64
|
+
record.kind === "checkpoint"
|
|
65
|
+
? `checkpoint ${record.sequence}`
|
|
66
|
+
: record.status;
|
|
67
|
+
const lines = [
|
|
68
|
+
`### ${text(record.node)} · ${label}`,
|
|
69
|
+
`Attempt: ${code(record.attempt)} · Writer: ${runLink(intent.repository, record.writer)}`,
|
|
70
|
+
`Runtime: ${runtimeLink(record.runtime)}`,
|
|
71
|
+
];
|
|
72
|
+
if (record.payload?.label) lines.push(text(record.payload.label));
|
|
73
|
+
if (record.payload?.code)
|
|
74
|
+
lines.push(`Failure code: ${code(record.payload.code)}`);
|
|
75
|
+
const attachments = attachmentRows(record, intent.repository);
|
|
76
|
+
if (attachments.length)
|
|
77
|
+
lines.push(
|
|
78
|
+
"| Attachment | Type | Bytes | SHA-256 |\n| --- | --- | ---: | --- |\n" +
|
|
79
|
+
attachments.join("\n"),
|
|
80
|
+
"Retained files use the transaction material archive. Access requires repository permission to read draft release assets.",
|
|
81
|
+
);
|
|
82
|
+
const { attachments: _attachments, ...details } = record.payload || {};
|
|
83
|
+
if (Object.keys(details).length) {
|
|
84
|
+
const json = JSON.stringify(details, null, 2);
|
|
85
|
+
// HTML escaping keeps payload strings from closing the presentation container.
|
|
86
|
+
lines.push(
|
|
87
|
+
`<details><summary>Record details</summary><pre>${json.slice(0, 6000).replace(/[&<>]/gu, (c) => ({ "&": "&", "<": "<", ">": ">" })[c])}${json.length > 6000 ? "\n… See the retained JSON attachment for the complete material." : ""}</pre></details>`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
lines.push(`Record: ${code(record.id)}`);
|
|
91
|
+
return lines.join("\n\n");
|
|
92
|
+
}
|
|
@@ -40,8 +40,10 @@ export async function publishWithDiscussion(
|
|
|
40
40
|
observe = () => {},
|
|
41
41
|
},
|
|
42
42
|
) {
|
|
43
|
+
let retained;
|
|
43
44
|
const journal = await openReleaseSession({
|
|
44
45
|
graphql: octokit.graphql,
|
|
46
|
+
onFailure: (node, error) => retained.diagnostics(node, error.code),
|
|
45
47
|
repository: request.repository,
|
|
46
48
|
key: request.version,
|
|
47
49
|
source: { version: request.version },
|
|
@@ -54,7 +56,7 @@ export async function publishWithDiscussion(
|
|
|
54
56
|
request["publish-transaction-override"],
|
|
55
57
|
),
|
|
56
58
|
});
|
|
57
|
-
|
|
59
|
+
retained = releaseCheckpoints({
|
|
58
60
|
session: journal.session,
|
|
59
61
|
store: journal.store,
|
|
60
62
|
octokit,
|
|
@@ -79,6 +79,7 @@ export async function executeReleaseDiscussion(request, _admission, context) {
|
|
|
79
79
|
store: journal.store,
|
|
80
80
|
octokit: context.octokit,
|
|
81
81
|
});
|
|
82
|
+
await retained.diagnostics("transport", "qualification-probe");
|
|
82
83
|
const probe = await retained.checkpoint("transport", {
|
|
83
84
|
schema: "buildchain.material-qualification/v1",
|
|
84
85
|
value: payload.key,
|
|
@@ -23,6 +23,10 @@ export function readReleaseDiscussion({ body, records }) {
|
|
|
23
23
|
new Set(intent.expectedNodes).size !== intent.expectedNodes.length
|
|
24
24
|
)
|
|
25
25
|
throw new Error("Missing or duplicate expected nodes");
|
|
26
|
+
if (intent.organization && intent.organization !== "attempt-threads/v1")
|
|
27
|
+
throw new Error(
|
|
28
|
+
"Discussion organization requires its historical runtime reader",
|
|
29
|
+
);
|
|
26
30
|
const attempts = groupAttempts(validateRecords(intent, records));
|
|
27
31
|
if (!attempts.size)
|
|
28
32
|
return {
|
|
@@ -156,6 +160,11 @@ export function discussionStatus(state) {
|
|
|
156
160
|
status: state.status,
|
|
157
161
|
attempt: state.attempt || "",
|
|
158
162
|
attempts: state.attempts,
|
|
163
|
+
threads: [...(state.roots || new Map())].map(([attempt, comment]) => ({
|
|
164
|
+
attempt,
|
|
165
|
+
commentId: comment.id,
|
|
166
|
+
url: comment.url,
|
|
167
|
+
})),
|
|
159
168
|
missingNodes: state.missingNodes,
|
|
160
169
|
handoff: state.handoff || null,
|
|
161
170
|
nodes: Object.fromEntries(
|
|
@@ -39,6 +39,7 @@ export async function openReleaseSession({
|
|
|
39
39
|
dryRun = false,
|
|
40
40
|
recover = false,
|
|
41
41
|
expectedNodes = RELEASE_NODES,
|
|
42
|
+
onFailure = async () => {},
|
|
42
43
|
}) {
|
|
43
44
|
const intent = createIntent({
|
|
44
45
|
repository,
|
|
@@ -101,6 +102,7 @@ export async function openReleaseSession({
|
|
|
101
102
|
{ code: String(error.code || "execution-failed") },
|
|
102
103
|
1,
|
|
103
104
|
);
|
|
105
|
+
await onFailure(node, error);
|
|
104
106
|
} catch (recordError) {
|
|
105
107
|
error.discussionRecordingError = recordError.message;
|
|
106
108
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readDiscussionThreads, eventParent } from "./threads.js";
|
|
2
|
+
import { renderIntent, renderEvent } from "./presentation.js";
|
|
1
3
|
import { collectDiscussionPages } from "../../providers/github/discussions/transport.js";
|
|
2
4
|
import {
|
|
3
5
|
canonicalJson,
|
|
@@ -95,11 +97,10 @@ export function releaseDiscussionStore(
|
|
|
95
97
|
discussion = await transport.create({
|
|
96
98
|
repositoryId: repository.id,
|
|
97
99
|
categoryId: selected[0].id,
|
|
98
|
-
title:
|
|
99
|
-
|
|
100
|
-
intent
|
|
101
|
-
|
|
102
|
-
),
|
|
100
|
+
title: intent.source?.qualification
|
|
101
|
+
? `Buildchain qualification: ${intent.source.qualification}`
|
|
102
|
+
: `Buildchain release: ${intent.key}`,
|
|
103
|
+
body: encodeRecord(intent, renderIntent(intent)),
|
|
103
104
|
});
|
|
104
105
|
} catch (error) {
|
|
105
106
|
discussion = await observeAfterUnknown(find, error);
|
|
@@ -110,39 +111,30 @@ export function releaseDiscussionStore(
|
|
|
110
111
|
}
|
|
111
112
|
async function read(session) {
|
|
112
113
|
const discussion = await transport.get(session.discussion.id);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const trusted = comments.filter(
|
|
118
|
-
(comment) => comment.author?.id === session.writerId,
|
|
114
|
+
const intent = assertDiscussion(
|
|
115
|
+
discussion,
|
|
116
|
+
session.intent,
|
|
117
|
+
session.writerId,
|
|
119
118
|
);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
)
|
|
125
|
-
throw new Error(
|
|
126
|
-
"A transaction record was edited; historical facts are no longer intact",
|
|
127
|
-
);
|
|
128
|
-
const records = trusted
|
|
129
|
-
.map((comment) => decodeRecord(comment.body))
|
|
130
|
-
.filter(Boolean);
|
|
119
|
+
const threads = await readDiscussionThreads(transport, {
|
|
120
|
+
...session,
|
|
121
|
+
intent,
|
|
122
|
+
discussion,
|
|
123
|
+
});
|
|
131
124
|
return {
|
|
132
125
|
discussion,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
126
|
+
...threads,
|
|
127
|
+
...readReleaseDiscussion({
|
|
128
|
+
body: discussion.body,
|
|
129
|
+
records: threads.records,
|
|
130
|
+
}),
|
|
136
131
|
};
|
|
137
132
|
}
|
|
138
133
|
async function append(session, record) {
|
|
139
134
|
if (session.dryRun) return { dryRun: true };
|
|
140
|
-
|
|
141
|
-
record,
|
|
142
|
-
`${record.node}: ${record.status} (attempt ${record.attempt})`,
|
|
143
|
-
);
|
|
135
|
+
let observed;
|
|
144
136
|
const find = async () => {
|
|
145
|
-
|
|
137
|
+
observed = await read(session);
|
|
146
138
|
const existing = observed.comments.find(
|
|
147
139
|
(comment) => decodeRecord(comment.body)?.id === record.id,
|
|
148
140
|
);
|
|
@@ -155,8 +147,13 @@ export function releaseDiscussionStore(
|
|
|
155
147
|
};
|
|
156
148
|
const existing = await find();
|
|
157
149
|
if (existing) return existing;
|
|
150
|
+
const parent = eventParent(observed, record);
|
|
151
|
+
const body = encodeRecord(
|
|
152
|
+
record,
|
|
153
|
+
renderEvent(record, session.intent, observed),
|
|
154
|
+
);
|
|
158
155
|
try {
|
|
159
|
-
await transport.append(session.discussion.id, body);
|
|
156
|
+
await transport.append(session.discussion.id, body, parent);
|
|
160
157
|
} catch (error) {
|
|
161
158
|
return observeAfterUnknown(find, error);
|
|
162
159
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { decodeRecord } from "./envelope.js";
|
|
2
|
+
import { collectDiscussionPages } from "../../providers/github/discussions/transport.js";
|
|
3
|
+
|
|
4
|
+
export function isAttemptRoot(record) {
|
|
5
|
+
return (
|
|
6
|
+
record?.node === "attempt" &&
|
|
7
|
+
record.kind === "progress" &&
|
|
8
|
+
record.status === "running" &&
|
|
9
|
+
record.sequence === 0
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Provider placement is checked before the pure reader reduces event semantics.
|
|
14
|
+
// Historical flat records retain their original representation; new writes always
|
|
15
|
+
// use roots and replies. An explicit intent organization makes placement strict.
|
|
16
|
+
export async function readDiscussionThreads(transport, session) {
|
|
17
|
+
const top = await collectDiscussionPages((after) =>
|
|
18
|
+
transport.comments(session.discussion.id, after),
|
|
19
|
+
);
|
|
20
|
+
const comments = [...top];
|
|
21
|
+
for (const root of top) {
|
|
22
|
+
if (root.replies?.totalCount === 0) continue;
|
|
23
|
+
const replies = await collectDiscussionPages((after) =>
|
|
24
|
+
transport.replies(root.id, after),
|
|
25
|
+
);
|
|
26
|
+
for (const reply of replies) {
|
|
27
|
+
if (reply.replyTo?.id !== root.id)
|
|
28
|
+
throw new Error(
|
|
29
|
+
"Discussion reply parent differs from its queried thread",
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
comments.push(...replies);
|
|
33
|
+
if (Buffer.byteLength(JSON.stringify(comments)) > 32 * 1024 * 1024)
|
|
34
|
+
throw new Error("Discussion threads exceed the complete-view byte bound");
|
|
35
|
+
}
|
|
36
|
+
const trusted = comments.filter(
|
|
37
|
+
(comment) => comment.author?.id === session.writerId,
|
|
38
|
+
);
|
|
39
|
+
const records = new Map();
|
|
40
|
+
for (const comment of trusted) {
|
|
41
|
+
const record = decodeRecord(comment.body);
|
|
42
|
+
if (!record) continue;
|
|
43
|
+
if (comment.lastEditedAt)
|
|
44
|
+
throw new Error(
|
|
45
|
+
"A transaction record was edited; historical facts are no longer intact",
|
|
46
|
+
);
|
|
47
|
+
records.set(comment.id, record);
|
|
48
|
+
}
|
|
49
|
+
const roots = new Map();
|
|
50
|
+
for (const comment of trusted) {
|
|
51
|
+
const record = records.get(comment.id);
|
|
52
|
+
if (!isAttemptRoot(record)) continue;
|
|
53
|
+
if (comment.replyTo || roots.has(record.attempt))
|
|
54
|
+
throw new Error("Ambiguous or nested Discussion attempt root");
|
|
55
|
+
roots.set(record.attempt, comment);
|
|
56
|
+
}
|
|
57
|
+
for (const comment of trusted) {
|
|
58
|
+
const record = records.get(comment.id);
|
|
59
|
+
if (!record || isAttemptRoot(record)) continue;
|
|
60
|
+
const root = roots.get(record.attempt);
|
|
61
|
+
if (comment.replyTo) {
|
|
62
|
+
if (!root || comment.replyTo.id !== root.id)
|
|
63
|
+
throw new Error(
|
|
64
|
+
"Transaction event is attached to a different attempt root",
|
|
65
|
+
);
|
|
66
|
+
} else if (session.intent.organization === "attempt-threads/v1") {
|
|
67
|
+
throw new Error("Transaction event requires its attempt root reply");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { comments: trusted, records: [...records.values()], roots };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function eventParent(state, record) {
|
|
74
|
+
const root = state.roots.get(record.attempt);
|
|
75
|
+
if (isAttemptRoot(record)) {
|
|
76
|
+
if (root)
|
|
77
|
+
throw new Error("Attempt root already exists with different content");
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
if (!root)
|
|
81
|
+
throw new Error("Open the attempt root before appending its events");
|
|
82
|
+
return root.id;
|
|
83
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { parseNpmView } from "../../publication/npm/registry.js";
|
|
2
|
+
import { readNpmPackResult } from "../../publication/npm/pack-result.js";
|
|
1
3
|
import { createOciPublicationAdapter } from "./oci-provider.js";
|
|
2
4
|
import crypto from "node:crypto";
|
|
3
5
|
import fs from "node:fs";
|
|
@@ -306,8 +308,7 @@ function createPackedPackage(context) {
|
|
|
306
308
|
{ cwd: context.cwd, encoding: "utf8" },
|
|
307
309
|
"npm pack",
|
|
308
310
|
);
|
|
309
|
-
const
|
|
310
|
-
const pack = Array.isArray(payload) ? payload[0] : payload;
|
|
311
|
+
const pack = readNpmPackResult(String(result.stdout || "[]"));
|
|
311
312
|
if (
|
|
312
313
|
pack?.name !== context.intent.packageName ||
|
|
313
314
|
pack?.version !== context.intent.version ||
|
|
@@ -363,7 +364,7 @@ async function npmReadback(context, packedPackage, effect) {
|
|
|
363
364
|
"npm-readback-failed",
|
|
364
365
|
);
|
|
365
366
|
}
|
|
366
|
-
const integrity =
|
|
367
|
+
const { integrity } = parseNpmView(result.stdout);
|
|
367
368
|
if (integrity !== expected.integrity) return conflict("npm-integrity-conflict");
|
|
368
369
|
return observed(effect, {
|
|
369
370
|
kind: "npm-package",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseNpmView } from "../../../publication/npm/registry.js";
|
|
1
2
|
import { npmPackageSpec } from "./publish-contract.js";
|
|
2
3
|
import { execNpmSync } from "./npm-distribution.js";
|
|
3
4
|
import fs from "node:fs";
|
|
@@ -14,7 +15,7 @@ export function readExistingNpmIntegrity({ cwd, artifact }) {
|
|
|
14
15
|
if (!output) {
|
|
15
16
|
throw new Error("empty dist.integrity");
|
|
16
17
|
}
|
|
17
|
-
return
|
|
18
|
+
return parseNpmView(output).integrity;
|
|
18
19
|
} catch (error) {
|
|
19
20
|
const message = error.stderr?.toString?.().trim() || error.message;
|
|
20
21
|
throw new Error(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readNpmPackResult } from "../../../publication/npm/pack-result.js";
|
|
1
2
|
import { npmPublishTransaction } from "../../../publication/npm/transaction.js";
|
|
2
3
|
import { npmPublicationEnvironment } from "../../../publication/npm/environment.js";
|
|
3
4
|
import {
|
|
@@ -15,10 +16,21 @@ import {
|
|
|
15
16
|
updateVersionStateContents,
|
|
16
17
|
} from "../../version-state.js";
|
|
17
18
|
import { publishTransactionEnvironment } from "./transaction-context.js";
|
|
18
|
-
export function runPublishCommand({
|
|
19
|
+
export function runPublishCommand({
|
|
20
|
+
cwd,
|
|
21
|
+
command,
|
|
22
|
+
provider,
|
|
23
|
+
loadedConfig,
|
|
24
|
+
env,
|
|
25
|
+
}) {
|
|
19
26
|
if (provider) {
|
|
20
|
-
if (provider.kind !== "npm" || !provider.directory || command)
|
|
21
|
-
|
|
27
|
+
if (provider.kind !== "npm" || !provider.directory || command)
|
|
28
|
+
throw new Error("Invalid or ambiguous declarative publication provider");
|
|
29
|
+
npmPublishTransaction({
|
|
30
|
+
cwd: path.resolve(cwd, provider.directory),
|
|
31
|
+
publication: npmPublicationEnvironment(env),
|
|
32
|
+
env: { ...process.env, ...env },
|
|
33
|
+
});
|
|
22
34
|
return "provider:npm";
|
|
23
35
|
}
|
|
24
36
|
const lifecyclePublish = getLifecycleStage(loadedConfig, "publish");
|
|
@@ -64,7 +76,7 @@ export function rematerializedNpmPackEnvironment({
|
|
|
64
76
|
path.join(os.tmpdir(), "buildchain-rematerialized-npm-"),
|
|
65
77
|
);
|
|
66
78
|
try {
|
|
67
|
-
const
|
|
79
|
+
const result = readNpmPackResult(
|
|
68
80
|
execNpmSync(
|
|
69
81
|
[
|
|
70
82
|
"pack",
|
|
@@ -82,7 +94,6 @@ export function rematerializedNpmPackEnvironment({
|
|
|
82
94
|
},
|
|
83
95
|
),
|
|
84
96
|
);
|
|
85
|
-
const result = Array.isArray(packed) ? packed[0] : packed;
|
|
86
97
|
if (!result?.filename)
|
|
87
98
|
throw new Error("rematerialized npm pack did not return a filename");
|
|
88
99
|
if (result.name !== pkg.name || result.version !== version) {
|
|
Binary file
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
export const DOMAIN_WASM_ABI_VERSION = 1;
|
|
3
3
|
export const DOMAIN_WASM_RUSTC = "rustc 1.96.0 (ac68faa20 2026-05-25)";
|
|
4
4
|
export const DOMAIN_WASM_SOURCE_SHA256 =
|
|
5
|
-
"
|
|
5
|
+
"9275a02bbc3daac66cac3021384bcaaf43bd52ed9af56b4f1ac67f1cbc69fb71";
|
|
6
6
|
export const DOMAIN_WASM_SHA256 =
|
|
7
|
-
"
|
|
7
|
+
"d42ab5ae8d42253d40ab49057e460285a848925cece176c4d68e63e90ca86c72";
|
|
@@ -1,45 +1,38 @@
|
|
|
1
1
|
import { resolveRecoverySource } from "./recovery.js";
|
|
2
2
|
import { getOctokit } from "@actions/github";
|
|
3
3
|
import { runtimeEntryProvider } from "./github.js";
|
|
4
|
+
import { resolveRuntimeActionSource } from "./source.js";
|
|
4
5
|
import {
|
|
5
6
|
preparedRuntimeSelection,
|
|
6
7
|
selectExecutionRuntime,
|
|
7
8
|
} from "./selection.js";
|
|
9
|
+
import { selectAttemptRecoveryRuntime } from "./attempt.js";
|
|
8
10
|
|
|
9
11
|
export async function selectExecutionRuntimeAction(
|
|
10
12
|
core,
|
|
11
13
|
env,
|
|
12
|
-
{
|
|
14
|
+
{
|
|
15
|
+
providerFactory = runtimeEntryProvider,
|
|
16
|
+
githubFactory = getOctokit,
|
|
17
|
+
pipelineLookup,
|
|
18
|
+
} = {},
|
|
13
19
|
) {
|
|
14
20
|
const retained = core.getInput("selection");
|
|
15
21
|
const transport = retained
|
|
16
22
|
? preparedRuntimeSelection(JSON.parse(retained))
|
|
17
23
|
: undefined;
|
|
18
24
|
const github = githubFactory(core.getInput("token", { required: true }));
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
eventName: env.GITHUB_EVENT_NAME,
|
|
31
|
-
});
|
|
32
|
-
await reader.authorize({ origin: "runtime-parameter" });
|
|
33
|
-
source = await resolveRecoverySource(
|
|
34
|
-
{
|
|
35
|
-
repository: source.repository,
|
|
36
|
-
runId: resumeRunId,
|
|
37
|
-
currentRunId: env.GITHUB_RUN_ID,
|
|
38
|
-
workflow: env.GITHUB_WORKFLOW_REF?.slice(source.repository.length + 1),
|
|
39
|
-
},
|
|
40
|
-
reader.readRun,
|
|
41
|
-
);
|
|
42
|
-
}
|
|
25
|
+
const {
|
|
26
|
+
source: resolvedSource,
|
|
27
|
+
continuation,
|
|
28
|
+
resumeRunId,
|
|
29
|
+
pipelineAttempt,
|
|
30
|
+
} = await resolveRuntimeActionSource(core, env, {
|
|
31
|
+
providerFactory,
|
|
32
|
+
github,
|
|
33
|
+
pipelineLookup,
|
|
34
|
+
});
|
|
35
|
+
let source = resolvedSource;
|
|
43
36
|
const provider = providerFactory(github, {
|
|
44
37
|
sourceRepository: source.repository,
|
|
45
38
|
sourceSha: source.sha,
|
|
@@ -91,6 +84,15 @@ export async function selectExecutionRuntimeAction(
|
|
|
91
84
|
selection.source?.sha !== source.sha
|
|
92
85
|
)
|
|
93
86
|
throw new Error("Runtime transport changed the admitted consumer source");
|
|
87
|
+
} else if (continuation?.recovery && (!runtimeRef || pipelineAttempt)) {
|
|
88
|
+
if (runtimeRef)
|
|
89
|
+
throw new Error(
|
|
90
|
+
"An admitted recovery runtime can change only through a new recovery attempt",
|
|
91
|
+
);
|
|
92
|
+
selection = await selectAttemptRecoveryRuntime(
|
|
93
|
+
continuation.recovery,
|
|
94
|
+
provider,
|
|
95
|
+
);
|
|
94
96
|
} else {
|
|
95
97
|
selection = await selectExecutionRuntime(
|
|
96
98
|
{
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { validateRuntime } from "../../release/discussion/envelope.js";
|
|
2
|
+
import { selectExecutionRuntime } from "./selection.js";
|
|
3
|
+
|
|
4
|
+
// This input is read from the canonical current attempt, never from workflow
|
|
5
|
+
// inputs. Continuation retains its admitted repair without reopening an override.
|
|
6
|
+
export async function selectAttemptRecoveryRuntime(recovery, provider) {
|
|
7
|
+
validateRuntime(recovery.runtime);
|
|
8
|
+
if (
|
|
9
|
+
!/^attempt-[0-9a-f]{64}$/u.test(recovery.attempt || "") ||
|
|
10
|
+
!/^sha256:[0-9a-f]{64}$/u.test(recovery.recordRoot || "") ||
|
|
11
|
+
recovery.runtime.repository !== "kungfu-systems/buildchain"
|
|
12
|
+
)
|
|
13
|
+
throw new Error(
|
|
14
|
+
"Attempt runtime continuation requires its canonical recovery admission",
|
|
15
|
+
);
|
|
16
|
+
const selected = await selectExecutionRuntime(
|
|
17
|
+
{ runtimeRef: recovery.runtime.sha },
|
|
18
|
+
{
|
|
19
|
+
...provider,
|
|
20
|
+
authorize: (input) =>
|
|
21
|
+
provider.authorize({ ...input, origin: "attempt-recovery" }),
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
return { ...selected, origin: "attempt-recovery", recovery };
|
|
25
|
+
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { consumerContractLock } from "../../consumer/contract/identity.js";
|
|
2
|
+
import { canonicalJson } from "../../release/discussion/envelope.js";
|
|
3
|
+
|
|
1
4
|
const exactSha = /^[a-f0-9]{40}$/iu;
|
|
2
5
|
const repository = "kungfu-systems/buildchain";
|
|
3
6
|
const trainRef =
|
|
@@ -28,6 +31,14 @@ export function runtimeSelector({ runtimeRef = "", lock, workflowSha }) {
|
|
|
28
31
|
return { ref: runtimeRef, origin: "runtime-parameter" };
|
|
29
32
|
}
|
|
30
33
|
if (lock) {
|
|
34
|
+
if (lock.schema === "buildchain.consumer-contract-lock/v2") {
|
|
35
|
+
const validated = consumerContractLock(lock);
|
|
36
|
+
if (canonicalJson(validated) !== canonicalJson(lock))
|
|
37
|
+
throw new Error(
|
|
38
|
+
"Minimal consumer contract lock contains unsupported fields",
|
|
39
|
+
);
|
|
40
|
+
return { ref: validated.runtime.sha, origin: "consumer-contract-lock" };
|
|
41
|
+
}
|
|
31
42
|
if (lock.contract !== "kungfu-buildchain-contract-lock")
|
|
32
43
|
throw new Error("Unsupported Buildchain contract lock");
|
|
33
44
|
const sha = commit(lock.buildchain?.resolvedSha, "Contract lock runtime");
|
|
@@ -92,6 +103,9 @@ export async function selectExecutionRuntime(
|
|
|
92
103
|
...(selected.origin === "contract-lock"
|
|
93
104
|
? { contract: { digest: request.lock.buildchain.contractDigest } }
|
|
94
105
|
: {}),
|
|
106
|
+
...(selected.origin === "consumer-contract-lock"
|
|
107
|
+
? { contract: { consumerConfigDigest: request.lock.configDigest } }
|
|
108
|
+
: {}),
|
|
95
109
|
class:
|
|
96
110
|
selected.origin === "contract-lock" &&
|
|
97
111
|
request.lock.buildchain.ref === "v4-alpha"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { resolveRecoverySource } from "./recovery.js";
|
|
2
|
+
import {
|
|
3
|
+
pipelineRuntimeContinuation,
|
|
4
|
+
pipelineRecoveryContinuation,
|
|
5
|
+
} from "../../workflow/pipeline/runtime-source.js";
|
|
6
|
+
|
|
7
|
+
export async function resolveRuntimeActionSource(
|
|
8
|
+
core,
|
|
9
|
+
env,
|
|
10
|
+
{ providerFactory, github, pipelineLookup },
|
|
11
|
+
) {
|
|
12
|
+
let source = {
|
|
13
|
+
repository: core.getInput("source-repository") || env.GITHUB_REPOSITORY,
|
|
14
|
+
sha: core.getInput("source-sha") || env.GITHUB_SHA,
|
|
15
|
+
ref: env.GITHUB_REF,
|
|
16
|
+
};
|
|
17
|
+
const pipelineAttempt = core.getInput("pipeline-attempt");
|
|
18
|
+
const recoveryAttempt = core.getInput("recovery-attempt");
|
|
19
|
+
const resumeRunId = core.getInput("resume-run-id");
|
|
20
|
+
if (
|
|
21
|
+
[pipelineAttempt, recoveryAttempt, resumeRunId].filter(Boolean).length > 1
|
|
22
|
+
)
|
|
23
|
+
throw new Error("Runtime source selectors are mutually exclusive");
|
|
24
|
+
let continuation;
|
|
25
|
+
if (recoveryAttempt) {
|
|
26
|
+
continuation = await pipelineRecoveryContinuation(
|
|
27
|
+
recoveryAttempt,
|
|
28
|
+
source.repository,
|
|
29
|
+
core.getInput("token", { required: true }),
|
|
30
|
+
pipelineLookup,
|
|
31
|
+
);
|
|
32
|
+
source = continuation.source;
|
|
33
|
+
}
|
|
34
|
+
if (pipelineAttempt) {
|
|
35
|
+
continuation = await pipelineRuntimeContinuation(
|
|
36
|
+
pipelineAttempt,
|
|
37
|
+
source.repository,
|
|
38
|
+
core.getInput("token", { required: true }),
|
|
39
|
+
pipelineLookup,
|
|
40
|
+
);
|
|
41
|
+
source = continuation.source;
|
|
42
|
+
}
|
|
43
|
+
if (resumeRunId) {
|
|
44
|
+
const reader = providerFactory(github, {
|
|
45
|
+
sourceRepository: source.repository,
|
|
46
|
+
sourceSha: source.sha,
|
|
47
|
+
actor: env.GITHUB_ACTOR,
|
|
48
|
+
eventName: env.GITHUB_EVENT_NAME,
|
|
49
|
+
});
|
|
50
|
+
await reader.authorize({ origin: "runtime-parameter" });
|
|
51
|
+
source = await resolveRecoverySource(
|
|
52
|
+
{
|
|
53
|
+
repository: source.repository,
|
|
54
|
+
runId: resumeRunId,
|
|
55
|
+
currentRunId: env.GITHUB_RUN_ID,
|
|
56
|
+
workflow: env.GITHUB_WORKFLOW_REF?.slice(source.repository.length + 1),
|
|
57
|
+
},
|
|
58
|
+
reader.readRun,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return { source, continuation, resumeRunId, pipelineAttempt };
|
|
62
|
+
}
|