@kungfu-tech/buildchain 3.0.5-alpha.7 → 3.0.5
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/README.md +0 -31
- package/actions/macos-credential-island/README.md +0 -8
- package/bin/buildchain.mjs +1 -1
- package/contracts/auditable-demo-media-profiles-v1.json +0 -61
- package/contracts/evidence/auditable-demo-responsive-web-delivery-v1.json +3 -3
- package/contracts/evidence/auditable-demo-web-delivery-v1.json +3 -3
- package/dist/site/buildchain-contract.json +25 -86
- package/dist/site/buildchain-site.json +38 -64
- package/dist/site/capability-registry.json +1 -1
- package/dist/site/cli-registry.json +2 -6
- package/dist/site/controller-registry.json +3 -11
- package/dist/site/kfd-claims.json +11 -53
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +7 -7
- package/dist/site/node-api-registry.json +31 -213
- package/dist/site/page-registry.json +20 -50
- package/dist/site/public-surface-audit.json +8 -98
- package/dist/site/publication-authority-registry.json +1 -42
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +11 -11
- package/dist/site/workflow-registry.json +2 -67
- package/docs/auditable-demo.md +7 -74
- package/docs/aws-us-elastic-runner-burst-plane.md +16 -151
- package/docs/cli-reference.md +2 -24
- package/docs/node-api-reference.md +72 -90
- package/docs/publish-transaction.md +5 -7
- package/docs/release-flow.md +0 -4
- package/docs/release-governance.md +0 -39
- package/docs/reusable-build-surface.md +0 -9
- package/package.json +1 -1
- package/packages/core/buildchain-contract.js +1 -36
- package/packages/core/buildchain-publication-authority.js +0 -2
- package/packages/core/public-surface-audit.js +35 -4
- package/packages/core/release-propagation-release.js +18 -61
- package/packages/core/release-propagation-work.js +3 -7
- package/packages/core/release-propagation.js +2 -12
- package/scripts/auditable-demo-renditions.mjs +2 -7
- package/scripts/auditable-demo.mjs +21 -27
- package/scripts/aws-windows-jit-controller-core.mjs +2 -20
- package/scripts/aws-windows-jit-controller.mjs +109 -184
- package/scripts/aws-windows-jit-core.mjs +3 -21
- package/scripts/aws-windows-jit.mjs +0 -2
- package/scripts/build-standalone-binary.mjs +0 -4
- package/scripts/buildchain-cli-help.mjs +1 -2
- package/scripts/capture-package-release-propagation.mjs +1 -24
- package/scripts/check-inventory.mjs +0 -5
- package/scripts/finalize-native-artifact-signing-result.mjs +15 -37
- package/scripts/generate-site-bundle.mjs +0 -2
- package/scripts/locked-source-checkout.mjs +3 -17
- package/scripts/release-propagation.mjs +2 -77
- package/contracts/auditable-demo-scenario-v1.schema.json +0 -153
- package/packages/core/release-propagation-pickup.js +0 -386
- package/packages/core/workflow-call-contract.js +0 -308
- package/packages/core/workflow-yaml-contract.js +0 -272
- package/scripts/auditable-demo-platform.mjs +0 -506
- package/scripts/aws-macos-jit-controller-core.mjs +0 -389
- package/scripts/aws-macos-jit-controller-runtime.mjs +0 -82
- package/scripts/aws-macos-jit-controller.mjs +0 -558
- package/scripts/aws-macos-jit-job-controller.mjs +0 -401
- package/scripts/aws-windows-jit-campaign-core.mjs +0 -296
- package/scripts/aws-windows-jit-campaign.mjs +0 -202
- package/scripts/workflow-call-contract.mjs +0 -133
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
import { spawnSync } from "node:child_process";
|
|
5
|
-
import { pathToFileURL } from "node:url";
|
|
6
|
-
import {
|
|
7
|
-
createWindowsJitCampaignArmPlan,
|
|
8
|
-
windowsCampaignArmItems,
|
|
9
|
-
windowsCampaignKillArgs,
|
|
10
|
-
} from "./aws-windows-jit-campaign-core.mjs";
|
|
11
|
-
|
|
12
|
-
function arg(name, fallback = "") {
|
|
13
|
-
const index = process.argv.indexOf(`--${name}`);
|
|
14
|
-
return index === -1 ? fallback : process.argv[index + 1] || "";
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function aws(plan, serviceArgs) {
|
|
18
|
-
const profile = arg("aws-profile");
|
|
19
|
-
const result = spawnSync(
|
|
20
|
-
"aws",
|
|
21
|
-
[
|
|
22
|
-
...(profile ? ["--profile", profile] : []),
|
|
23
|
-
"--region",
|
|
24
|
-
plan.aws.region,
|
|
25
|
-
...serviceArgs,
|
|
26
|
-
],
|
|
27
|
-
{ encoding: "utf8", maxBuffer: 16 * 1024 * 1024 },
|
|
28
|
-
);
|
|
29
|
-
if (result.status !== 0) {
|
|
30
|
-
const detail = String(result.stderr || result.stdout || "")
|
|
31
|
-
.trim()
|
|
32
|
-
.slice(0, 2000);
|
|
33
|
-
throw new Error(
|
|
34
|
-
`AWS campaign mutation failed${detail ? `: ${detail}` : ""}`,
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
return result.stdout ? JSON.parse(result.stdout) : {};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function armPlan() {
|
|
41
|
-
return createWindowsJitCampaignArmPlan({
|
|
42
|
-
campaignId: arg("campaign-id"),
|
|
43
|
-
sourceSha: arg("source-sha"),
|
|
44
|
-
stateTable: arg("state-table"),
|
|
45
|
-
region: arg("region", "us-east-1"),
|
|
46
|
-
armedAt: arg("armed-at", new Date().toISOString()),
|
|
47
|
-
expiresAt: arg("expires-at"),
|
|
48
|
-
phaseSpendBaselineUsd: arg("phase-spend-baseline-usd"),
|
|
49
|
-
maxAcceptedInstances: arg("max-accepted-instances"),
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function confirm(
|
|
54
|
-
plan,
|
|
55
|
-
{ phaseSpendBaseline = true, maxAcceptedInstances = true } = {},
|
|
56
|
-
) {
|
|
57
|
-
if (arg("confirm-campaign-id") !== plan.campaign.id) {
|
|
58
|
-
throw new Error("--confirm-campaign-id must equal the campaign id");
|
|
59
|
-
}
|
|
60
|
-
if (arg("confirm-source-sha") !== plan.source.sha) {
|
|
61
|
-
throw new Error("--confirm-source-sha must equal the exact source SHA");
|
|
62
|
-
}
|
|
63
|
-
if (arg("confirm-state-table") !== plan.aws.stateTable) {
|
|
64
|
-
throw new Error(
|
|
65
|
-
"--confirm-state-table must equal the campaign state table",
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
if (
|
|
69
|
-
phaseSpendBaseline &&
|
|
70
|
-
(!arg("confirm-phase-spend-baseline-usd").trim() ||
|
|
71
|
-
Number(arg("confirm-phase-spend-baseline-usd")) !==
|
|
72
|
-
plan.limits.phaseSpendBaselineUsd)
|
|
73
|
-
) {
|
|
74
|
-
throw new Error(
|
|
75
|
-
"--confirm-phase-spend-baseline-usd must equal the phase spend baseline",
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
if (
|
|
79
|
-
maxAcceptedInstances &&
|
|
80
|
-
(!arg("confirm-max-accepted-instances").trim() ||
|
|
81
|
-
Number(arg("confirm-max-accepted-instances")) !==
|
|
82
|
-
plan.limits.maxAcceptedInstances)
|
|
83
|
-
) {
|
|
84
|
-
throw new Error(
|
|
85
|
-
"--confirm-max-accepted-instances must equal the campaign slot ceiling",
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function killSwitchTopic() {
|
|
91
|
-
const topic = arg("kill-switch-topic");
|
|
92
|
-
if (
|
|
93
|
-
!/^arn:aws:sns:us-east-1:\d{12}:kungfu-buildchain-windows-jit-[A-Za-z0-9_-]+$/.test(
|
|
94
|
-
topic,
|
|
95
|
-
)
|
|
96
|
-
) {
|
|
97
|
-
throw new Error(
|
|
98
|
-
"--kill-switch-topic must be the dedicated Windows JIT SNS ARN",
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
if (arg("confirm-kill-switch-topic") !== topic) {
|
|
102
|
-
throw new Error(
|
|
103
|
-
"--confirm-kill-switch-topic must equal the dedicated kill-switch topic",
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
return topic;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function main() {
|
|
110
|
-
const mode = process.argv[2] || "plan-arm";
|
|
111
|
-
if (["plan-arm", "arm-campaign"].includes(mode)) {
|
|
112
|
-
const plan = armPlan();
|
|
113
|
-
if (mode === "plan-arm") {
|
|
114
|
-
process.stdout.write(`${JSON.stringify(plan, null, 2)}\n`);
|
|
115
|
-
return plan;
|
|
116
|
-
}
|
|
117
|
-
confirm(plan);
|
|
118
|
-
aws(plan, [
|
|
119
|
-
"dynamodb",
|
|
120
|
-
"transact-write-items",
|
|
121
|
-
"--transact-items",
|
|
122
|
-
JSON.stringify(windowsCampaignArmItems(plan)),
|
|
123
|
-
"--output",
|
|
124
|
-
"json",
|
|
125
|
-
]);
|
|
126
|
-
const result = {
|
|
127
|
-
contract: plan.contract,
|
|
128
|
-
kind: "campaign-arm-result",
|
|
129
|
-
status: "armed",
|
|
130
|
-
campaign: plan.campaign,
|
|
131
|
-
source: plan.source,
|
|
132
|
-
aws: plan.aws,
|
|
133
|
-
limits: plan.limits,
|
|
134
|
-
};
|
|
135
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
136
|
-
return result;
|
|
137
|
-
}
|
|
138
|
-
if (mode === "kill-campaign") {
|
|
139
|
-
const now = new Date();
|
|
140
|
-
const plan = createWindowsJitCampaignArmPlan({
|
|
141
|
-
campaignId: arg("campaign-id"),
|
|
142
|
-
sourceSha: arg("source-sha"),
|
|
143
|
-
stateTable: arg("state-table"),
|
|
144
|
-
region: arg("region", "us-east-1"),
|
|
145
|
-
armedAt: now.toISOString(),
|
|
146
|
-
expiresAt: new Date(now.getTime() + 1000).toISOString(),
|
|
147
|
-
phaseSpendBaselineUsd: 0,
|
|
148
|
-
});
|
|
149
|
-
confirm(plan, {
|
|
150
|
-
phaseSpendBaseline: false,
|
|
151
|
-
maxAcceptedInstances: false,
|
|
152
|
-
});
|
|
153
|
-
const topic = killSwitchTopic();
|
|
154
|
-
aws(
|
|
155
|
-
plan,
|
|
156
|
-
windowsCampaignKillArgs(
|
|
157
|
-
plan.aws.stateTable,
|
|
158
|
-
arg("reason", "operator-kill"),
|
|
159
|
-
new Date().toISOString(),
|
|
160
|
-
),
|
|
161
|
-
);
|
|
162
|
-
const notification = aws(plan, [
|
|
163
|
-
"sns",
|
|
164
|
-
"publish",
|
|
165
|
-
"--topic-arn",
|
|
166
|
-
topic,
|
|
167
|
-
"--message",
|
|
168
|
-
JSON.stringify({
|
|
169
|
-
contract: plan.contract,
|
|
170
|
-
action: "kill-campaign",
|
|
171
|
-
campaignId: plan.campaign.id,
|
|
172
|
-
sourceSha: plan.source.sha,
|
|
173
|
-
}),
|
|
174
|
-
"--output",
|
|
175
|
-
"json",
|
|
176
|
-
]);
|
|
177
|
-
const result = {
|
|
178
|
-
contract: plan.contract,
|
|
179
|
-
kind: "campaign-kill-result",
|
|
180
|
-
status: "killed",
|
|
181
|
-
campaign: plan.campaign,
|
|
182
|
-
source: plan.source,
|
|
183
|
-
killSwitchTopic: topic,
|
|
184
|
-
notificationMessageId: notification.MessageId || "",
|
|
185
|
-
};
|
|
186
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
187
|
-
return result;
|
|
188
|
-
}
|
|
189
|
-
throw new Error(`unsupported Windows JIT campaign mode: ${mode}`);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (
|
|
193
|
-
process.argv[1] &&
|
|
194
|
-
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
195
|
-
) {
|
|
196
|
-
try {
|
|
197
|
-
main();
|
|
198
|
-
} catch (error) {
|
|
199
|
-
console.error(`::error::${error.message || error}`);
|
|
200
|
-
process.exitCode = 1;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { execFileSync } from "node:child_process";
|
|
5
|
-
import { pathToFileURL } from "node:url";
|
|
6
|
-
import { evaluateWorkflowCallContract } from "../packages/core/workflow-call-contract.js";
|
|
7
|
-
|
|
8
|
-
function usage() {
|
|
9
|
-
return `usage: node scripts/workflow-call-contract.mjs check \\
|
|
10
|
-
--caller-workflow <path> --job <id> --caller-repository <owner/repo> \\
|
|
11
|
-
--callee-root <checkout> --callee-workflow <path> --callee-repository <owner/repo> \\
|
|
12
|
-
--trusted-event <event[:type]> [--trusted-event ...] \\
|
|
13
|
-
[--expected-contract-root sha256:...] [--allow-dirty] [--output <path>]`;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function parseArgs(argv) {
|
|
17
|
-
const options = { trustedEvents: [] };
|
|
18
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
19
|
-
const arg = argv[index];
|
|
20
|
-
if (arg === "--allow-dirty") {
|
|
21
|
-
options.allowDirty = true;
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
if (!arg.startsWith("--") || !argv[index + 1])
|
|
25
|
-
throw new Error(`invalid argument: ${arg}`);
|
|
26
|
-
const value = argv[index + 1];
|
|
27
|
-
index += 1;
|
|
28
|
-
if (arg === "--trusted-event") options.trustedEvents.push(value);
|
|
29
|
-
else
|
|
30
|
-
options[
|
|
31
|
-
arg.slice(2).replace(/-([a-z])/g, (_, char) => char.toUpperCase())
|
|
32
|
-
] = value;
|
|
33
|
-
}
|
|
34
|
-
return options;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function git(root, ...args) {
|
|
38
|
-
return execFileSync("git", ["-C", root, ...args], {
|
|
39
|
-
encoding: "utf8",
|
|
40
|
-
}).trim();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function required(options, names) {
|
|
44
|
-
const missing = names.filter((name) => !options[name]);
|
|
45
|
-
if (missing.length)
|
|
46
|
-
throw new Error(`missing required options: ${missing.join(", ")}`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function checkWorkflowCall(options) {
|
|
50
|
-
const callerRoot = path.resolve(options.callerRoot || process.cwd());
|
|
51
|
-
const calleeRoot = path.resolve(options.calleeRoot);
|
|
52
|
-
const callerStatus = git(
|
|
53
|
-
callerRoot,
|
|
54
|
-
"status",
|
|
55
|
-
"--porcelain",
|
|
56
|
-
"--untracked-files=no",
|
|
57
|
-
);
|
|
58
|
-
if (callerStatus && !options.allowDirty) {
|
|
59
|
-
throw new Error(
|
|
60
|
-
"caller checkout is dirty; use --allow-dirty only for local diagnostic validation",
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
const calleeStatus = git(
|
|
64
|
-
calleeRoot,
|
|
65
|
-
"status",
|
|
66
|
-
"--porcelain",
|
|
67
|
-
"--untracked-files=no",
|
|
68
|
-
);
|
|
69
|
-
if (calleeStatus) {
|
|
70
|
-
throw new Error("callee checkout is dirty; exact pinned-ref bytes are required");
|
|
71
|
-
}
|
|
72
|
-
const callerSha = git(callerRoot, "rev-parse", "HEAD");
|
|
73
|
-
const callerTree = git(callerRoot, "rev-parse", "HEAD^{tree}");
|
|
74
|
-
const calleeSha = git(calleeRoot, "rev-parse", "HEAD");
|
|
75
|
-
const report = evaluateWorkflowCallContract({
|
|
76
|
-
callerText: fs.readFileSync(
|
|
77
|
-
path.join(callerRoot, options.callerWorkflow),
|
|
78
|
-
"utf8",
|
|
79
|
-
),
|
|
80
|
-
calleeText: fs.readFileSync(
|
|
81
|
-
path.join(calleeRoot, options.calleeWorkflow),
|
|
82
|
-
"utf8",
|
|
83
|
-
),
|
|
84
|
-
callerRepository: options.callerRepository,
|
|
85
|
-
callerWorkflowPath: options.callerWorkflow,
|
|
86
|
-
callerSha,
|
|
87
|
-
callerTree,
|
|
88
|
-
callerSourceState: callerStatus ? "diagnostic-dirty" : "clean",
|
|
89
|
-
calleeRepository: options.calleeRepository,
|
|
90
|
-
calleeWorkflowPath: options.calleeWorkflow,
|
|
91
|
-
calleeSha,
|
|
92
|
-
jobId: options.job,
|
|
93
|
-
trustedEventClasses: options.trustedEvents,
|
|
94
|
-
expectedContractRoot: options.expectedContractRoot || "",
|
|
95
|
-
});
|
|
96
|
-
if (options.output) {
|
|
97
|
-
const output = path.resolve(callerRoot, options.output);
|
|
98
|
-
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
99
|
-
fs.writeFileSync(output, `${JSON.stringify(report, null, 2)}\n`);
|
|
100
|
-
}
|
|
101
|
-
return report;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function main(argv = process.argv.slice(2)) {
|
|
105
|
-
const command = argv.shift();
|
|
106
|
-
if (command !== "check") throw new Error(usage());
|
|
107
|
-
const options = parseArgs(argv);
|
|
108
|
-
required(options, [
|
|
109
|
-
"callerWorkflow",
|
|
110
|
-
"job",
|
|
111
|
-
"callerRepository",
|
|
112
|
-
"calleeRoot",
|
|
113
|
-
"calleeWorkflow",
|
|
114
|
-
"calleeRepository",
|
|
115
|
-
]);
|
|
116
|
-
if (!options.trustedEvents.length)
|
|
117
|
-
throw new Error("at least one --trusted-event is required");
|
|
118
|
-
const report = checkWorkflowCall(options);
|
|
119
|
-
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
120
|
-
if (!report.ok) process.exitCode = 1;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (
|
|
124
|
-
process.argv[1] &&
|
|
125
|
-
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
126
|
-
) {
|
|
127
|
-
try {
|
|
128
|
-
main();
|
|
129
|
-
} catch (error) {
|
|
130
|
-
console.error(`workflow call contract: ${error.message}`);
|
|
131
|
-
process.exitCode = 1;
|
|
132
|
-
}
|
|
133
|
-
}
|