@kungfu-tech/buildchain 3.0.5-alpha.8 → 3.0.6-alpha.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/contracts/auditable-demo-scenario-v1.schema.json +11 -0
- package/dist/site/buildchain-contract.json +42 -22
- package/dist/site/buildchain-site.json +73 -27
- package/dist/site/capability-registry.json +2 -2
- package/dist/site/cli-registry.json +5 -3
- package/dist/site/controller-registry.json +19 -3
- package/dist/site/kfd-claims.json +58 -14
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +7 -6
- package/dist/site/node-api-registry.json +335 -8
- package/dist/site/page-registry.json +63 -17
- package/dist/site/public-surface-audit.json +94 -11
- package/dist/site/publication-authority-registry.json +21 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +10 -10
- package/dist/site/workflow-registry.json +53 -4
- package/docs/MAP.md +4 -1
- package/docs/auditable-demo.md +19 -3
- package/docs/cli-reference.md +15 -4
- package/docs/dev-alpha-candidate-patrol.md +14 -5
- package/docs/dev-qualification-patrol.md +108 -0
- package/docs/node-api-reference.md +42 -9
- package/docs/release-propagation.md +93 -0
- package/docs/reusable-build-surface.md +44 -12
- package/docs/stable-candidate-patrol.md +5 -2
- package/package.json +1 -1
- package/packages/core/buildchain-publication-authority.js +1 -0
- package/packages/core/release-propagation-agent-entry.js +185 -0
- package/packages/core/release-propagation-push.js +293 -0
- package/packages/core/release-propagation-stage-evidence.js +36 -0
- package/packages/core/release-propagation-work.js +1 -1
- package/packages/core/release-propagation.js +15 -0
- package/scripts/artifact-signing-controller-core.mjs +525 -0
- package/scripts/artifact-signing-controller.mjs +302 -0
- package/scripts/artifact-signing-delegation.mjs +51 -3
- package/scripts/auditable-demo-platform.mjs +59 -1
- package/scripts/auditable-demo-transport-smoke.mjs +176 -0
- package/scripts/build-standalone-binary.mjs +3 -1
- package/scripts/buildchain-cli-help.mjs +1 -1
- package/scripts/check-inventory.mjs +1 -0
- package/scripts/compiler-cache-evidence.mjs +90 -7
- package/scripts/dev-alpha-candidate-patrol.mjs +80 -5
- package/scripts/dev-pr-auto-merge.mjs +4 -4
- package/scripts/dev-qualification-patrol.mjs +594 -0
- package/scripts/dispatch-artifact-signing-authority.mjs +312 -61
- package/scripts/generate-site-bundle.mjs +3 -0
- package/scripts/inspect-artifact-signing-requests.mjs +12 -0
- package/scripts/release-propagation.mjs +73 -0
- package/scripts/run-lifecycle-core.mjs +25 -0
- package/scripts/site-capability-metadata.mjs +1 -1
- package/scripts/stable-candidate-patrol.mjs +30 -4
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
2
|
+
import {
|
|
3
|
+
assertPlainObject,
|
|
4
|
+
assertString,
|
|
5
|
+
sha256Json,
|
|
6
|
+
} from "./release-propagation-common.js";
|
|
7
|
+
import {
|
|
8
|
+
assertCommitSha,
|
|
9
|
+
assertContentRoot,
|
|
10
|
+
contentRoot,
|
|
11
|
+
} from "./release-propagation-work-control.js";
|
|
12
|
+
import { verifyReleasePropagationWork } from "./release-propagation-work.js";
|
|
13
|
+
|
|
14
|
+
export const RELEASE_PROPAGATION_PUSH_PLAN_CONTRACT =
|
|
15
|
+
"kungfu-buildchain-release-propagation-push-plan";
|
|
16
|
+
export const RELEASE_PROPAGATION_PUSH_RESULT_CONTRACT =
|
|
17
|
+
"kungfu-buildchain-release-propagation-push-result";
|
|
18
|
+
|
|
19
|
+
function branchName(value, label) {
|
|
20
|
+
const branch = assertString(value, label);
|
|
21
|
+
if (
|
|
22
|
+
branch.startsWith("-") ||
|
|
23
|
+
branch.startsWith("refs/") ||
|
|
24
|
+
branch.includes("..") ||
|
|
25
|
+
branch.includes("@{") ||
|
|
26
|
+
/[\s~^:?*[\\]/.test(branch) ||
|
|
27
|
+
branch.endsWith(".") ||
|
|
28
|
+
branch.endsWith("/") ||
|
|
29
|
+
branch.includes("//")
|
|
30
|
+
) {
|
|
31
|
+
throw new Error(`${label} is not a safe Git branch name`);
|
|
32
|
+
}
|
|
33
|
+
return branch;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function remoteName(value) {
|
|
37
|
+
const name = assertString(value, "push repository state.remoteName");
|
|
38
|
+
if (!/^[A-Za-z0-9._-]+$/.test(name) || name.startsWith("-")) {
|
|
39
|
+
throw new Error("push repository state.remoteName is unsafe");
|
|
40
|
+
}
|
|
41
|
+
return name;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function optionalCommit(value, label) {
|
|
45
|
+
if (value === "") return "";
|
|
46
|
+
return assertCommitSha(value, label);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeRepositoryState(value) {
|
|
50
|
+
const state = assertPlainObject(value, "push repository state");
|
|
51
|
+
return {
|
|
52
|
+
repository: assertString(
|
|
53
|
+
state.repository,
|
|
54
|
+
"push repository state.repository",
|
|
55
|
+
),
|
|
56
|
+
remoteName: remoteName(state.remoteName),
|
|
57
|
+
currentBranch: branchName(
|
|
58
|
+
state.currentBranch,
|
|
59
|
+
"push repository state.currentBranch",
|
|
60
|
+
),
|
|
61
|
+
sourceRevision: assertCommitSha(
|
|
62
|
+
state.sourceRevision,
|
|
63
|
+
"push repository state.sourceRevision",
|
|
64
|
+
),
|
|
65
|
+
remoteBaseRevision: assertCommitSha(
|
|
66
|
+
state.remoteBaseRevision,
|
|
67
|
+
"push repository state.remoteBaseRevision",
|
|
68
|
+
),
|
|
69
|
+
remoteBranchRevision: optionalCommit(
|
|
70
|
+
state.remoteBranchRevision,
|
|
71
|
+
"push repository state.remoteBranchRevision",
|
|
72
|
+
),
|
|
73
|
+
expectedBaseIsAncestor: state.expectedBaseIsAncestor === true,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function createReleasePropagationPushPlan({
|
|
78
|
+
work: workInput,
|
|
79
|
+
expectedWorkRoot,
|
|
80
|
+
repositoryState: stateInput,
|
|
81
|
+
} = {}) {
|
|
82
|
+
const status = verifyReleasePropagationWork(workInput);
|
|
83
|
+
const expectedRoot = assertContentRoot(expectedWorkRoot, "expectedWorkRoot");
|
|
84
|
+
if (status.contentRoot !== expectedRoot) {
|
|
85
|
+
throw new Error("propagation work changed before push planning");
|
|
86
|
+
}
|
|
87
|
+
if (
|
|
88
|
+
status.lifecycle !== "ready" ||
|
|
89
|
+
status.currentStage !== "push-branch" ||
|
|
90
|
+
status.work.authority.mode !== "execute"
|
|
91
|
+
) {
|
|
92
|
+
throw new Error("propagation work is not ready for the push-branch stage");
|
|
93
|
+
}
|
|
94
|
+
const state = normalizeRepositoryState(stateInput);
|
|
95
|
+
const expectedBranch = branchName(
|
|
96
|
+
status.work.downstream.branch,
|
|
97
|
+
"propagation work downstream.branch",
|
|
98
|
+
);
|
|
99
|
+
if (state.repository !== status.work.downstream.repository) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
"push remote repository disagrees with the propagation target",
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (state.currentBranch !== expectedBranch) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
"current branch disagrees with the exact propagation branch",
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (state.remoteBaseRevision !== status.work.downstream.expectedBaseSha) {
|
|
110
|
+
throw new Error("downstream base advanced after the propagation capture");
|
|
111
|
+
}
|
|
112
|
+
if (!state.expectedBaseIsAncestor) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
"propagation branch is not based on the expected downstream base",
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const destinationRef = `refs/heads/${expectedBranch}`;
|
|
118
|
+
const body = {
|
|
119
|
+
schemaVersion: 1,
|
|
120
|
+
contract: RELEASE_PROPAGATION_PUSH_PLAN_CONTRACT,
|
|
121
|
+
workId: status.workId,
|
|
122
|
+
expectedWorkRoot: expectedRoot,
|
|
123
|
+
repository: state.repository,
|
|
124
|
+
remoteName: state.remoteName,
|
|
125
|
+
sourceRef: "HEAD",
|
|
126
|
+
sourceRevision: state.sourceRevision,
|
|
127
|
+
destinationRef,
|
|
128
|
+
expectedBaseRevision: status.work.downstream.expectedBaseSha,
|
|
129
|
+
expectedOldRevision: state.remoteBranchRevision,
|
|
130
|
+
pushMode: "fast-forward-only-exact-refspec",
|
|
131
|
+
argv: ["push", "--porcelain", state.remoteName, `HEAD:${destinationRef}`],
|
|
132
|
+
};
|
|
133
|
+
return { ...body, planRoot: sha256Json(body) };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function git(cwd, args) {
|
|
137
|
+
return execFileSync("git", args, {
|
|
138
|
+
cwd,
|
|
139
|
+
encoding: "utf8",
|
|
140
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
141
|
+
}).trim();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function repositoryFromRemoteUrl(value) {
|
|
145
|
+
const remoteUrl = String(value || "").trim();
|
|
146
|
+
if (!remoteUrl || /[?#]/.test(remoteUrl)) {
|
|
147
|
+
throw new Error("push remote URL must not contain query or fragment data");
|
|
148
|
+
}
|
|
149
|
+
const ssh = remoteUrl.match(
|
|
150
|
+
/^[^@\s]+@github\.com:([^/\s]+\/[^/\s]+?)(?:\.git)?$/,
|
|
151
|
+
);
|
|
152
|
+
if (ssh) return ssh[1];
|
|
153
|
+
const https = remoteUrl.match(
|
|
154
|
+
/^https:\/\/github\.com\/([^/\s]+\/[^/\s]+?)(?:\.git)?$/,
|
|
155
|
+
);
|
|
156
|
+
if (https) return https[1];
|
|
157
|
+
throw new Error("push remote must be an exact GitHub repository URL");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function lsRemote(cwd, remote, ref) {
|
|
161
|
+
const output = git(cwd, ["ls-remote", "--refs", remote, ref]);
|
|
162
|
+
if (!output) return "";
|
|
163
|
+
const rows = output.split("\n").filter(Boolean);
|
|
164
|
+
if (rows.length !== 1) {
|
|
165
|
+
throw new Error(`expected exactly one remote ref for ${ref}`);
|
|
166
|
+
}
|
|
167
|
+
const [revision, observedRef] = rows[0].split(/\s+/);
|
|
168
|
+
if (observedRef !== ref) {
|
|
169
|
+
throw new Error(`remote ref lookup disagrees with ${ref}`);
|
|
170
|
+
}
|
|
171
|
+
return assertCommitSha(revision, `remote ${ref}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function inspectReleasePropagationPushState({
|
|
175
|
+
work: workInput,
|
|
176
|
+
cwd = process.cwd(),
|
|
177
|
+
remote = "origin",
|
|
178
|
+
} = {}) {
|
|
179
|
+
const status = verifyReleasePropagationWork(workInput);
|
|
180
|
+
const expectedBranch = branchName(
|
|
181
|
+
status.work.downstream.branch,
|
|
182
|
+
"propagation work downstream.branch",
|
|
183
|
+
);
|
|
184
|
+
const baseRef = branchName(
|
|
185
|
+
status.work.downstream.baseRef,
|
|
186
|
+
"propagation work downstream.baseRef",
|
|
187
|
+
);
|
|
188
|
+
const normalizedRemote = remoteName(remote);
|
|
189
|
+
const repository = repositoryFromRemoteUrl(
|
|
190
|
+
git(cwd, ["remote", "get-url", normalizedRemote]),
|
|
191
|
+
);
|
|
192
|
+
const sourceRevision = assertCommitSha(
|
|
193
|
+
git(cwd, ["rev-parse", "HEAD"]),
|
|
194
|
+
"push source revision",
|
|
195
|
+
);
|
|
196
|
+
const ancestor = spawnSync(
|
|
197
|
+
"git",
|
|
198
|
+
[
|
|
199
|
+
"merge-base",
|
|
200
|
+
"--is-ancestor",
|
|
201
|
+
status.work.downstream.expectedBaseSha,
|
|
202
|
+
sourceRevision,
|
|
203
|
+
],
|
|
204
|
+
{ cwd, encoding: "utf8" },
|
|
205
|
+
);
|
|
206
|
+
if (ancestor.error) throw ancestor.error;
|
|
207
|
+
if (ancestor.status !== 0) {
|
|
208
|
+
throw new Error(
|
|
209
|
+
"propagation branch is not based on the expected downstream base",
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
repository,
|
|
214
|
+
remoteName: normalizedRemote,
|
|
215
|
+
currentBranch: git(cwd, ["branch", "--show-current"]),
|
|
216
|
+
sourceRevision,
|
|
217
|
+
remoteBaseRevision: lsRemote(
|
|
218
|
+
cwd,
|
|
219
|
+
normalizedRemote,
|
|
220
|
+
`refs/heads/${baseRef}`,
|
|
221
|
+
),
|
|
222
|
+
remoteBranchRevision: lsRemote(
|
|
223
|
+
cwd,
|
|
224
|
+
normalizedRemote,
|
|
225
|
+
`refs/heads/${expectedBranch}`,
|
|
226
|
+
),
|
|
227
|
+
expectedBaseIsAncestor: true,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function executeReleasePropagationPush({
|
|
232
|
+
work,
|
|
233
|
+
expectedWorkRoot,
|
|
234
|
+
cwd = process.cwd(),
|
|
235
|
+
remote = "origin",
|
|
236
|
+
} = {}) {
|
|
237
|
+
const repositoryState = inspectReleasePropagationPushState({
|
|
238
|
+
work,
|
|
239
|
+
cwd,
|
|
240
|
+
remote,
|
|
241
|
+
});
|
|
242
|
+
const plan = createReleasePropagationPushPlan({
|
|
243
|
+
work,
|
|
244
|
+
expectedWorkRoot,
|
|
245
|
+
repositoryState,
|
|
246
|
+
});
|
|
247
|
+
let mutation = true;
|
|
248
|
+
let output = "";
|
|
249
|
+
if (plan.expectedOldRevision === plan.sourceRevision) {
|
|
250
|
+
mutation = false;
|
|
251
|
+
} else {
|
|
252
|
+
output = git(cwd, plan.argv);
|
|
253
|
+
}
|
|
254
|
+
const observedRevision = lsRemote(cwd, plan.remoteName, plan.destinationRef);
|
|
255
|
+
if (observedRevision !== plan.sourceRevision) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
"remote branch readback disagrees with the pushed source revision",
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
const claims = {
|
|
261
|
+
provider: "git",
|
|
262
|
+
repository: plan.repository,
|
|
263
|
+
remoteName: plan.remoteName,
|
|
264
|
+
sourceRef: plan.sourceRef,
|
|
265
|
+
sourceRevision: plan.sourceRevision,
|
|
266
|
+
destinationRef: plan.destinationRef,
|
|
267
|
+
expectedBaseRevision: plan.expectedBaseRevision,
|
|
268
|
+
expectedOldRevision: plan.expectedOldRevision,
|
|
269
|
+
observedRevision,
|
|
270
|
+
pushMode: plan.pushMode,
|
|
271
|
+
argv: plan.argv,
|
|
272
|
+
mutation,
|
|
273
|
+
};
|
|
274
|
+
const evidence = {
|
|
275
|
+
kind: "git-branch-reconciliation",
|
|
276
|
+
root: contentRoot(claims),
|
|
277
|
+
locator: "buildchain://release-propagation/branch-reconciliation",
|
|
278
|
+
repository: plan.repository,
|
|
279
|
+
revision: observedRevision,
|
|
280
|
+
httpStatus: null,
|
|
281
|
+
bytes: Buffer.byteLength(output),
|
|
282
|
+
claims,
|
|
283
|
+
};
|
|
284
|
+
const body = {
|
|
285
|
+
schemaVersion: 1,
|
|
286
|
+
contract: RELEASE_PROPAGATION_PUSH_RESULT_CONTRACT,
|
|
287
|
+
status: mutation ? "pushed" : "already-current",
|
|
288
|
+
mutation,
|
|
289
|
+
plan,
|
|
290
|
+
evidence,
|
|
291
|
+
};
|
|
292
|
+
return { ...body, resultRoot: sha256Json(body) };
|
|
293
|
+
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
optionalString,
|
|
5
5
|
} from "./release-propagation-common.js";
|
|
6
6
|
import {
|
|
7
|
+
assertCommitSha,
|
|
7
8
|
assertContentRoot,
|
|
8
9
|
assertExactFields,
|
|
9
10
|
assertIsoTimestamp,
|
|
@@ -107,6 +108,40 @@ function verifyIndependentReview(work, receipt) {
|
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
|
|
111
|
+
function verifyPushedBranch(work, receipt) {
|
|
112
|
+
const reconciliation = receipt.evidence.find((entry) => entry.kind === "git-branch-reconciliation");
|
|
113
|
+
const claims = exactClaims(reconciliation, [
|
|
114
|
+
"provider", "repository", "remoteName", "sourceRef", "sourceRevision",
|
|
115
|
+
"destinationRef", "expectedBaseRevision", "expectedOldRevision",
|
|
116
|
+
"observedRevision", "pushMode", "argv", "mutation",
|
|
117
|
+
], "push-branch evidence claims");
|
|
118
|
+
if (claims.provider !== "git"
|
|
119
|
+
|| claims.repository !== work.downstream.repository
|
|
120
|
+
|| claims.sourceRef !== "HEAD"
|
|
121
|
+
|| claims.destinationRef !== `refs/heads/${work.downstream.branch}`
|
|
122
|
+
|| claims.expectedBaseRevision !== work.downstream.expectedBaseSha
|
|
123
|
+
|| claims.pushMode !== "fast-forward-only-exact-refspec") {
|
|
124
|
+
throw new Error("push-branch evidence does not bind the exact propagation target");
|
|
125
|
+
}
|
|
126
|
+
assertCommitSha(claims.sourceRevision, "push-branch source revision");
|
|
127
|
+
assertCommitSha(claims.observedRevision, "push-branch observed revision");
|
|
128
|
+
if (claims.expectedOldRevision !== "") {
|
|
129
|
+
assertCommitSha(claims.expectedOldRevision, "push-branch expected-old revision");
|
|
130
|
+
}
|
|
131
|
+
const expectedArgv = ["push", "--porcelain", claims.remoteName, `HEAD:${claims.destinationRef}`];
|
|
132
|
+
if (JSON.stringify(claims.argv) !== JSON.stringify(expectedArgv)
|
|
133
|
+
|| claims.argv.some((value) => value === "--force" || value.startsWith("--force-"))) {
|
|
134
|
+
throw new Error("push-branch evidence must use the exact non-force refspec");
|
|
135
|
+
}
|
|
136
|
+
if (claims.sourceRevision !== claims.observedRevision
|
|
137
|
+
|| reconciliation.revision !== claims.observedRevision) {
|
|
138
|
+
throw new Error("push-branch evidence readback disagrees with the source revision");
|
|
139
|
+
}
|
|
140
|
+
if (typeof claims.mutation !== "boolean") {
|
|
141
|
+
throw new Error("push-branch evidence mutation must be boolean");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
110
145
|
function verifyProductionDeployment(work, receipt) {
|
|
111
146
|
const deployment = receipt.evidence.find((entry) => entry.kind === "production-deployment");
|
|
112
147
|
const claims = exactClaims(deployment, [
|
|
@@ -237,6 +272,7 @@ export function validateStageEvidence({ work, receipt }) {
|
|
|
237
272
|
]).has(receipt.stage)) {
|
|
238
273
|
for (const entry of receipt.evidence) assertHttpsLocator(entry.locator, receipt.stage);
|
|
239
274
|
}
|
|
275
|
+
if (receipt.stage === "push-branch") verifyPushedBranch(work, receipt);
|
|
240
276
|
if (receipt.stage === "independent-review") verifyIndependentReview(work, receipt);
|
|
241
277
|
if (receipt.stage === "production-deploy") verifyProductionDeployment(work, receipt);
|
|
242
278
|
if (receipt.stage === "online-readback") verifyOnlineReadback(work, receipt.evidence);
|
|
@@ -77,7 +77,7 @@ function workCommands(executionProfile = null, repository = "", branch = "", bas
|
|
|
77
77
|
stages: {
|
|
78
78
|
materialize: profile.updateCommand || "<consumer update command from the exact execution profile>",
|
|
79
79
|
"verify-release": profile.verifyCommand || "<consumer verification command from the exact execution profile>",
|
|
80
|
-
"push-branch":
|
|
80
|
+
"push-branch": "buildchain release-propagation work push-branch --work <work.json> --expected-work-root <sha256:...> --cwd <downstream-worktree> --remote origin --execute --json",
|
|
81
81
|
"pull-request": `gh pr create --repo ${repository || "<downstream-repository>"} --base ${baseRef || "<base-ref>"} --head ${branch || "<managed-branch>"}`,
|
|
82
82
|
preview: `gh run list --repo ${repository || "<downstream-repository>"} --workflow ${workflow} --branch ${branch || "<managed-branch>"} --event pull_request --json databaseId,status,conclusion,url,headSha`,
|
|
83
83
|
"independent-review": `gh pr review <pr-number> --repo ${repository || "<downstream-repository>"} --approve`,
|
|
@@ -486,3 +486,18 @@ export {
|
|
|
486
486
|
normalizeManualUpstreamPickupConfig,
|
|
487
487
|
resolveNpmRegistryRelease,
|
|
488
488
|
} from "./release-propagation-pickup.js";
|
|
489
|
+
export {
|
|
490
|
+
RELEASE_PROPAGATION_FAILURE_MATRIX,
|
|
491
|
+
RELEASE_PROPAGATION_FAILURE_MATRIX_CONTRACT,
|
|
492
|
+
SITE_UPSTREAM_AGENT_ENTRY_CONTRACT,
|
|
493
|
+
classifyReleasePropagationCondition,
|
|
494
|
+
normalizeSiteUpstreamIntent,
|
|
495
|
+
planSiteUpstreamAgentEntry,
|
|
496
|
+
} from "./release-propagation-agent-entry.js";
|
|
497
|
+
export {
|
|
498
|
+
RELEASE_PROPAGATION_PUSH_PLAN_CONTRACT,
|
|
499
|
+
RELEASE_PROPAGATION_PUSH_RESULT_CONTRACT,
|
|
500
|
+
createReleasePropagationPushPlan,
|
|
501
|
+
executeReleasePropagationPush,
|
|
502
|
+
inspectReleasePropagationPushState,
|
|
503
|
+
} from "./release-propagation-push.js";
|