@kungfu-tech/buildchain 3.0.4-alpha.2 → 3.0.4-alpha.3
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/bin/buildchain.mjs +126 -72
- package/bin/internal/trust-release-cli.mjs +15 -537
- package/bin/internal/trust-release-command-handlers.mjs +14 -0
- package/bin/internal/trust-release-inspection-handlers.mjs +175 -0
- package/bin/internal/trust-release-release-handlers.mjs +317 -0
- package/bin/internal/trust-release-verification-handlers.mjs +306 -0
- package/dist/site/buildchain-contract.json +35 -24
- package/dist/site/buildchain-site.json +22 -10
- package/dist/site/controller-registry.json +16 -3
- package/dist/site/kfd-claims.json +9 -7
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +2 -2
- package/dist/site/node-api-registry.json +7 -7
- package/dist/site/page-registry.json +9 -4
- package/dist/site/public-surface-audit.json +56 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-model.json +7 -0
- package/dist/site/site-manifest.json +6 -6
- package/dist/site/workflow-registry.json +13 -5
- package/docs/MAP.md +1 -1
- package/docs/release-propagation.md +166 -8
- package/package.json +1 -1
- package/packages/core/buildchain-kfd-claims.js +1 -1
- package/packages/core/controller-evidence.js +8 -1
- package/packages/core/index.js +1 -13
- package/packages/core/paper-npm-bootstrap.js +492 -0
- package/packages/core/paper.js +271 -669
- package/packages/core/public-surface-cli.js +12 -1
- package/packages/core/release-passport.js +67 -71
- package/packages/core/release-propagation-common.js +64 -0
- package/packages/core/release-propagation-execution-profile.js +59 -0
- package/packages/core/release-propagation-release.js +196 -0
- package/packages/core/release-propagation-stage-evidence.js +364 -0
- package/packages/core/release-propagation-work-capture.js +64 -0
- package/packages/core/release-propagation-work-constants.js +34 -0
- package/packages/core/release-propagation-work-control.js +203 -0
- package/packages/core/release-propagation-work-transitions.js +145 -0
- package/packages/core/release-propagation-work.js +517 -0
- package/packages/core/release-propagation.js +34 -158
- package/scripts/aws-windows-jit-controller-core.mjs +269 -0
- package/scripts/aws-windows-jit-controller.mjs +502 -0
- package/scripts/check-internal-architecture.mjs +178 -52
- package/scripts/check-maintainability.mjs +76 -17
- package/scripts/generate-site-bundle.mjs +16 -7
- package/scripts/maintainability-metrics.mjs +24 -4
- package/scripts/release-propagation.mjs +126 -0
- package/scripts/resolve-artifact-transfer-mode.mjs +117 -0
- package/scripts/web-surface-core.mjs +46 -207
- package/scripts/web-surface-routing.mjs +286 -0
|
@@ -3,9 +3,17 @@ import fs from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import {
|
|
6
|
+
claimReleasePropagationWork,
|
|
7
|
+
completeReleasePropagationWork,
|
|
6
8
|
createReleasePropagationReceipt,
|
|
9
|
+
createReleasePropagationStageReceipt,
|
|
10
|
+
createReleasePropagationWork,
|
|
7
11
|
planReleasePropagation,
|
|
8
12
|
readReleasePropagationJson,
|
|
13
|
+
recordReleasePropagationStage,
|
|
14
|
+
repairReleasePropagationWork,
|
|
15
|
+
resumeReleasePropagationWork,
|
|
16
|
+
verifyReleasePropagationWork,
|
|
9
17
|
writeReleasePropagationLock,
|
|
10
18
|
} from "../packages/core/release-propagation.js";
|
|
11
19
|
|
|
@@ -25,6 +33,34 @@ function usage() {
|
|
|
25
33
|
[--production-state <state>]
|
|
26
34
|
[--observed-at <iso-8601>]
|
|
27
35
|
[--output <file>] [--json]
|
|
36
|
+
buildchain release-propagation work create --plan <json-or-path>
|
|
37
|
+
--expected-downstream-base-sha <git-sha>
|
|
38
|
+
[--work-context <json-or-path>]
|
|
39
|
+
[--target <id-or-repo>]
|
|
40
|
+
[--output <file>] [--json]
|
|
41
|
+
buildchain release-propagation work status --work <json-or-path> [--json]
|
|
42
|
+
buildchain release-propagation work resume --work <json-or-path> [--json]
|
|
43
|
+
buildchain release-propagation work claim --work <json-or-path>
|
|
44
|
+
--authority <json-or-path>
|
|
45
|
+
--expected-work-root <sha256:...>
|
|
46
|
+
[--family-state <json-or-path>]
|
|
47
|
+
[--output <file>] [--json]
|
|
48
|
+
buildchain release-propagation work receipt --work <json-or-path>
|
|
49
|
+
--receipt-input <json-or-path>
|
|
50
|
+
[--output <file>] [--json]
|
|
51
|
+
buildchain release-propagation work record --work <json-or-path>
|
|
52
|
+
--receipt <json-or-path>
|
|
53
|
+
--expected-work-root <sha256:...>
|
|
54
|
+
[--output <file>] [--json]
|
|
55
|
+
buildchain release-propagation work repair --work <json-or-path>
|
|
56
|
+
--receipt <json-or-path>
|
|
57
|
+
--expected-work-root <sha256:...>
|
|
58
|
+
[--output <file>] [--json]
|
|
59
|
+
buildchain release-propagation work complete --work <json-or-path>
|
|
60
|
+
--receipt <json-or-path>
|
|
61
|
+
--completion-decision <json-or-path>
|
|
62
|
+
--expected-work-root <sha256:...>
|
|
63
|
+
[--output <file>] [--json]
|
|
28
64
|
`;
|
|
29
65
|
}
|
|
30
66
|
|
|
@@ -49,6 +85,92 @@ function writeOutput(filePath, value) {
|
|
|
49
85
|
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
50
86
|
}
|
|
51
87
|
|
|
88
|
+
function readJsonFlag(args, name) {
|
|
89
|
+
return readReleasePropagationJson(readFlag(args, name), {
|
|
90
|
+
label: `--${name}`,
|
|
91
|
+
cwd: process.cwd(),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function readOptionalJsonFlag(args, name) {
|
|
96
|
+
return args.includes(`--${name}`) ? readJsonFlag(args, name) : undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function emitWorkResult(args, value, summary) {
|
|
100
|
+
writeOutput(readFlag(args, "output", ""), value);
|
|
101
|
+
if (hasFlag(args, "json")) {
|
|
102
|
+
printJson(value);
|
|
103
|
+
} else {
|
|
104
|
+
process.stdout.write(`${summary}\n`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function runWorkCli(args) {
|
|
109
|
+
const [mode = "", ...workArgs] = args;
|
|
110
|
+
if (!mode || mode === "--help" || mode === "-h") {
|
|
111
|
+
process.stdout.write(usage());
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (mode === "create") {
|
|
115
|
+
const result = createReleasePropagationWork({
|
|
116
|
+
plan: readJsonFlag(workArgs, "plan"),
|
|
117
|
+
target: readFlag(workArgs, "target", ""),
|
|
118
|
+
workContext: readOptionalJsonFlag(workArgs, "work-context"),
|
|
119
|
+
expectedDownstreamBaseSha: readFlag(workArgs, "expected-downstream-base-sha"),
|
|
120
|
+
});
|
|
121
|
+
emitWorkResult(workArgs, result, `release propagation work: ${result.contentRoot}`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (mode === "status" || mode === "resume") {
|
|
125
|
+
const work = readJsonFlag(workArgs, "work");
|
|
126
|
+
const result = mode === "resume"
|
|
127
|
+
? resumeReleasePropagationWork(work)
|
|
128
|
+
: verifyReleasePropagationWork(work);
|
|
129
|
+
emitWorkResult(workArgs, result, `release propagation next action: ${result.nextAction.action} ${result.currentStage}`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (mode === "claim") {
|
|
133
|
+
const result = claimReleasePropagationWork({
|
|
134
|
+
work: readJsonFlag(workArgs, "work"),
|
|
135
|
+
authority: readJsonFlag(workArgs, "authority"),
|
|
136
|
+
familyState: readOptionalJsonFlag(workArgs, "family-state"),
|
|
137
|
+
expectedWorkRoot: readFlag(workArgs, "expected-work-root"),
|
|
138
|
+
});
|
|
139
|
+
emitWorkResult(workArgs, result, `release propagation work claimed: ${result.contentRoot}`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (mode === "receipt") {
|
|
143
|
+
const work = readJsonFlag(workArgs, "work");
|
|
144
|
+
const input = readJsonFlag(workArgs, "receipt-input");
|
|
145
|
+
const result = createReleasePropagationStageReceipt({ work, ...input });
|
|
146
|
+
emitWorkResult(workArgs, result, `release propagation stage receipt: ${result.receiptRoot}`);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (mode === "record" || mode === "repair") {
|
|
150
|
+
const transition = {
|
|
151
|
+
work: readJsonFlag(workArgs, "work"),
|
|
152
|
+
receipt: readJsonFlag(workArgs, "receipt"),
|
|
153
|
+
expectedWorkRoot: readFlag(workArgs, "expected-work-root"),
|
|
154
|
+
};
|
|
155
|
+
const result = mode === "record"
|
|
156
|
+
? recordReleasePropagationStage(transition)
|
|
157
|
+
: repairReleasePropagationWork(transition);
|
|
158
|
+
emitWorkResult(workArgs, result, `release propagation work advanced: ${result.contentRoot}`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (mode === "complete") {
|
|
162
|
+
const result = completeReleasePropagationWork({
|
|
163
|
+
work: readJsonFlag(workArgs, "work"),
|
|
164
|
+
receipt: readJsonFlag(workArgs, "receipt"),
|
|
165
|
+
completionDecision: readJsonFlag(workArgs, "completion-decision"),
|
|
166
|
+
expectedWorkRoot: readFlag(workArgs, "expected-work-root"),
|
|
167
|
+
});
|
|
168
|
+
emitWorkResult(workArgs, result, `release propagation work complete: ${result.contentRoot}`);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
throw new Error(`unsupported release-propagation work command: ${mode}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
52
174
|
export function runReleasePropagationCli(argv = process.argv.slice(2)) {
|
|
53
175
|
const [mode = "", ...args] = argv;
|
|
54
176
|
if (!mode || mode === "--help" || mode === "-h") {
|
|
@@ -80,6 +202,10 @@ export function runReleasePropagationCli(argv = process.argv.slice(2)) {
|
|
|
80
202
|
}
|
|
81
203
|
return;
|
|
82
204
|
}
|
|
205
|
+
if (mode === "work") {
|
|
206
|
+
runWorkCli(args);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
83
209
|
if (mode === "write-lock") {
|
|
84
210
|
const plan = readReleasePropagationJson(readFlag(args, "plan"), {
|
|
85
211
|
label: "--plan",
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
|
|
7
|
+
const VALID_MODES = new Set(["github-artifacts", "s3-to-github-artifacts"]);
|
|
8
|
+
|
|
9
|
+
function firstValue(...values) {
|
|
10
|
+
return values.find((value) => String(value || "").trim()) || "";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function resolveArtifactTransferMode(env = process.env) {
|
|
14
|
+
const mode = env.INPUT_TRANSFER_MODE || "github-artifacts";
|
|
15
|
+
if (!VALID_MODES.has(mode)) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`artifact-transfer-mode must be github-artifacts or s3-to-github-artifacts, got: ${mode}`,
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
if (mode === "github-artifacts") {
|
|
21
|
+
return {
|
|
22
|
+
mode,
|
|
23
|
+
s3Bucket: "",
|
|
24
|
+
s3Region: "",
|
|
25
|
+
s3Prefix: "",
|
|
26
|
+
oidcAudience: "",
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const s3Bucket = firstValue(env.INPUT_S3_BUCKET, env.VAR_S3_BUCKET);
|
|
30
|
+
const s3Region = firstValue(env.INPUT_S3_REGION, env.VAR_S3_REGION);
|
|
31
|
+
const s3Prefix = firstValue(
|
|
32
|
+
env.INPUT_S3_PREFIX,
|
|
33
|
+
env.VAR_S3_PREFIX,
|
|
34
|
+
"buildchain-artifacts",
|
|
35
|
+
);
|
|
36
|
+
const uploadRole = firstValue(
|
|
37
|
+
env.INPUT_S3_UPLOAD_ROLE_ARN,
|
|
38
|
+
env.VAR_S3_UPLOAD_ROLE_ARN,
|
|
39
|
+
env.SECRET_S3_UPLOAD_ROLE_ARN,
|
|
40
|
+
env.INPUT_S3_ROLE_ARN,
|
|
41
|
+
env.VAR_S3_ROLE_ARN,
|
|
42
|
+
env.SECRET_S3_ROLE_ARN,
|
|
43
|
+
);
|
|
44
|
+
const downloadRole = firstValue(
|
|
45
|
+
env.INPUT_S3_DOWNLOAD_ROLE_ARN,
|
|
46
|
+
env.VAR_S3_DOWNLOAD_ROLE_ARN,
|
|
47
|
+
env.SECRET_S3_DOWNLOAD_ROLE_ARN,
|
|
48
|
+
env.INPUT_S3_ROLE_ARN,
|
|
49
|
+
env.VAR_S3_ROLE_ARN,
|
|
50
|
+
env.SECRET_S3_ROLE_ARN,
|
|
51
|
+
);
|
|
52
|
+
if (!s3Bucket) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
"artifact-transfer-mode=s3-to-github-artifacts requires artifact-relay-s3-bucket or BUILDCHAIN_ARTIFACT_RELAY_S3_BUCKET",
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
if (!s3Region) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"artifact-transfer-mode=s3-to-github-artifacts requires artifact-relay-s3-region or BUILDCHAIN_ARTIFACT_RELAY_S3_REGION",
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if (!uploadRole) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
"artifact-transfer-mode=s3-to-github-artifacts requires an upload role ARN input, variable, or secret",
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (!downloadRole) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
"artifact-transfer-mode=s3-to-github-artifacts requires a download role ARN input, variable, or secret",
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
const oidcAudience = firstValue(
|
|
73
|
+
env.INPUT_OIDC_AUDIENCE,
|
|
74
|
+
env.VAR_OIDC_AUDIENCE,
|
|
75
|
+
s3Region.startsWith("cn-") ? "sts.amazonaws.com.cn" : "sts.amazonaws.com",
|
|
76
|
+
);
|
|
77
|
+
return { mode, s3Bucket, s3Region, s3Prefix, oidcAudience };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function writeArtifactTransferOutputs(outputPath, resolution) {
|
|
81
|
+
if (!outputPath) {
|
|
82
|
+
throw new Error("GITHUB_OUTPUT is required");
|
|
83
|
+
}
|
|
84
|
+
fs.mkdirSync(path.dirname(path.resolve(outputPath)), { recursive: true });
|
|
85
|
+
fs.appendFileSync(
|
|
86
|
+
outputPath,
|
|
87
|
+
[
|
|
88
|
+
`mode=${resolution.mode}`,
|
|
89
|
+
`s3-bucket=${resolution.s3Bucket}`,
|
|
90
|
+
`s3-region=${resolution.s3Region}`,
|
|
91
|
+
`s3-prefix=${resolution.s3Prefix}`,
|
|
92
|
+
`oidc-audience=${resolution.oidcAudience}`,
|
|
93
|
+
"",
|
|
94
|
+
].join("\n"),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function main() {
|
|
99
|
+
const resolution = resolveArtifactTransferMode();
|
|
100
|
+
writeArtifactTransferOutputs(process.env.GITHUB_OUTPUT, resolution);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (
|
|
104
|
+
process.argv[1] &&
|
|
105
|
+
import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href
|
|
106
|
+
) {
|
|
107
|
+
try {
|
|
108
|
+
main();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error(
|
|
111
|
+
`::error::${error instanceof Error ? error.message : String(error)}`,
|
|
112
|
+
);
|
|
113
|
+
process.exitCode = 1;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export { resolveArtifactTransferMode, writeArtifactTransferOutputs };
|
|
@@ -9,6 +9,29 @@ import { fileURLToPath } from "node:url";
|
|
|
9
9
|
import { loadBuildchainConfig, validateBuildchainConfig } from "../packages/core/buildchain-config.js";
|
|
10
10
|
import { createSurfaceTimestampPolicy } from "../packages/core/surface-manifest.js";
|
|
11
11
|
import { validateInstallerPublication, verifyInstallerPublicReadback } from "./installer-publication.mjs";
|
|
12
|
+
import {
|
|
13
|
+
classifyPreviewAlias,
|
|
14
|
+
resolveChannelUrl,
|
|
15
|
+
resolvePathOnlyUrl,
|
|
16
|
+
resolveSurfaceUrl,
|
|
17
|
+
manifestPrefixFor,
|
|
18
|
+
objectPrefixFor,
|
|
19
|
+
normalizeSurfacePath,
|
|
20
|
+
surfaceDeployConfig,
|
|
21
|
+
surfaceObjectPrefixFor,
|
|
22
|
+
normalizeS3Key,
|
|
23
|
+
joinS3Key,
|
|
24
|
+
joinUrlPath,
|
|
25
|
+
urlWithPath,
|
|
26
|
+
s3Uri,
|
|
27
|
+
cdnPath,
|
|
28
|
+
cdnWildcardPath,
|
|
29
|
+
viewerWildcardPath,
|
|
30
|
+
surfaceArtifactPrefix,
|
|
31
|
+
surfaceArtifactRootFor,
|
|
32
|
+
syncStaticArtifactArgs,
|
|
33
|
+
mutableCacheControlArgs,
|
|
34
|
+
} from "./web-surface-routing.mjs";
|
|
12
35
|
|
|
13
36
|
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
14
37
|
|
|
@@ -89,231 +112,47 @@ function retentionConfig(config, channel) {
|
|
|
89
112
|
};
|
|
90
113
|
}
|
|
91
114
|
|
|
92
|
-
function classifyPreviewAlias(alias) {
|
|
93
|
-
if (/^pr-\d+$/.test(alias)) {
|
|
94
|
-
return {
|
|
95
|
-
kind: "pr",
|
|
96
|
-
mutable: true,
|
|
97
|
-
retentionClass: "preview-pr-ephemeral",
|
|
98
|
-
retentionKey: "pr_days",
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
if (/^sha-[0-9a-f]{6,40}$/i.test(alias)) {
|
|
102
|
-
return {
|
|
103
|
-
kind: "sha",
|
|
104
|
-
mutable: false,
|
|
105
|
-
retentionClass: "preview-sha-immutable",
|
|
106
|
-
retentionKey: "sha_days",
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
return {
|
|
110
|
-
kind: "custom",
|
|
111
|
-
mutable: true,
|
|
112
|
-
retentionClass: "preview-custom-ephemeral",
|
|
113
|
-
retentionKey: "pr_days",
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
115
|
|
|
117
|
-
function resolveChannelUrl(channel, alias) {
|
|
118
|
-
if (channel.urlPattern) {
|
|
119
|
-
if (!alias) {
|
|
120
|
-
throw new Error(`channel ${channel.name} requires alias for url_pattern`);
|
|
121
|
-
}
|
|
122
|
-
return channel.urlPattern.replaceAll("{alias}", alias);
|
|
123
|
-
}
|
|
124
|
-
return channel.url;
|
|
125
|
-
}
|
|
126
116
|
|
|
127
|
-
function resolvePathOnlyUrl(channel, sourcePath, alias) {
|
|
128
|
-
const base = resolveChannelUrl(channel, alias);
|
|
129
|
-
const url = new URL(base);
|
|
130
|
-
const normalizedPath = normalizeSurfacePath(sourcePath);
|
|
131
|
-
if (normalizedPath === "/") {
|
|
132
|
-
return `${url.origin}/`;
|
|
133
|
-
}
|
|
134
|
-
return new URL(normalizedPath.replace(/^\//, ""), `${url.origin}/`).toString();
|
|
135
|
-
}
|
|
136
117
|
|
|
137
|
-
function resolveSurfaceUrl({ surface, channel, channelName, alias }) {
|
|
138
|
-
if (channelName === "preview" && surface.previewUrlPattern) {
|
|
139
|
-
if (!alias) {
|
|
140
|
-
throw new Error(`surface ${surface.name} requires alias for preview_url_pattern`);
|
|
141
|
-
}
|
|
142
|
-
return surface.previewUrlPattern.replaceAll("{alias}", alias);
|
|
143
|
-
}
|
|
144
|
-
if (channelName === "staging" && surface.stagingUrl) {
|
|
145
|
-
return surface.stagingUrl;
|
|
146
|
-
}
|
|
147
|
-
if (channelName === "production" && surface.productionUrl) {
|
|
148
|
-
return surface.productionUrl;
|
|
149
|
-
}
|
|
150
|
-
if (surface.pathOnly) {
|
|
151
|
-
return resolvePathOnlyUrl(channel, surface.path, alias);
|
|
152
|
-
}
|
|
153
|
-
const key = channelName === "preview"
|
|
154
|
-
? "preview_url_pattern"
|
|
155
|
-
: `${channelName}_url`;
|
|
156
|
-
throw new Error(`surfaces.${surface.name}.${key} is required for channel ${channelName}`);
|
|
157
|
-
}
|
|
158
118
|
|
|
159
|
-
function manifestPrefixFor(deployConfig) {
|
|
160
|
-
return deployConfig.manifest_prefix || ".buildchain/deployments";
|
|
161
|
-
}
|
|
162
119
|
|
|
163
|
-
function objectPrefixFor(deployConfig, alias) {
|
|
164
|
-
if (Object.hasOwn(deployConfig, "prefix")) {
|
|
165
|
-
return normalizeS3Key(deployConfig.prefix);
|
|
166
|
-
}
|
|
167
|
-
return alias || "preview";
|
|
168
|
-
}
|
|
169
120
|
|
|
170
|
-
function normalizeSurfacePath(value) {
|
|
171
|
-
const normalized = `/${String(value || "/").replace(/^\/+/, "")}`.replace(/\/{2,}/g, "/");
|
|
172
|
-
if (normalized === "/") {
|
|
173
|
-
return normalized;
|
|
174
|
-
}
|
|
175
|
-
return normalized.endsWith("/") ? normalized : `${normalized}/`;
|
|
176
|
-
}
|
|
177
121
|
|
|
178
|
-
function surfaceDeployConfig(deployConfig, surfaceName) {
|
|
179
|
-
const overrides = deployConfig.surfaces?.[surfaceName];
|
|
180
|
-
if (!overrides || typeof overrides !== "object" || Array.isArray(overrides)) {
|
|
181
|
-
return deployConfig;
|
|
182
|
-
}
|
|
183
|
-
return {
|
|
184
|
-
...deployConfig,
|
|
185
|
-
...overrides,
|
|
186
|
-
cacheControl: deployConfig.cacheControl || overrides.cacheControl
|
|
187
|
-
? { ...(deployConfig.cacheControl || {}), ...(overrides.cacheControl || {}) }
|
|
188
|
-
: undefined,
|
|
189
|
-
surfaces: deployConfig.surfaces,
|
|
190
|
-
secretRefs: [
|
|
191
|
-
...new Set([
|
|
192
|
-
...(deployConfig.secretRefs || []),
|
|
193
|
-
...(Array.isArray(overrides.secretRefs) ? overrides.secretRefs : []),
|
|
194
|
-
]),
|
|
195
|
-
],
|
|
196
|
-
artifactPath: overrides.artifactPath || deployConfig.artifactPath,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
122
|
|
|
200
|
-
function surfaceObjectPrefixFor({ deployConfig, surface, alias }) {
|
|
201
|
-
const effective = surfaceDeployConfig(deployConfig, surface.name);
|
|
202
|
-
if (Object.hasOwn(effective, "prefix")) {
|
|
203
|
-
return normalizeS3Key(effective.prefix);
|
|
204
|
-
}
|
|
205
|
-
const root = objectPrefixFor(deployConfig, alias);
|
|
206
|
-
const surfacePath = normalizeS3Key(surface.path);
|
|
207
|
-
if (!surfacePath) {
|
|
208
|
-
return root;
|
|
209
|
-
}
|
|
210
|
-
return joinS3Key(root, surfacePath);
|
|
211
|
-
}
|
|
212
123
|
|
|
213
|
-
function normalizeS3Key(value) {
|
|
214
|
-
return String(value || "")
|
|
215
|
-
.replace(/^\/+/, "")
|
|
216
|
-
.replace(/\/{2,}/g, "/")
|
|
217
|
-
.replace(/\/+$/, "");
|
|
218
|
-
}
|
|
219
124
|
|
|
220
|
-
function joinS3Key(...parts) {
|
|
221
|
-
return normalizeS3Key(parts.filter(Boolean).join("/"));
|
|
222
|
-
}
|
|
223
125
|
|
|
224
|
-
function joinUrlPath(...parts) {
|
|
225
|
-
const raw = parts.join("/");
|
|
226
|
-
const normalized = normalizeS3Key(raw);
|
|
227
|
-
if (!normalized) {
|
|
228
|
-
return "/";
|
|
229
|
-
}
|
|
230
|
-
return raw.endsWith("/") ? `/${normalized}/` : `/${normalized}`;
|
|
231
|
-
}
|
|
232
126
|
|
|
233
|
-
function urlWithPath(baseUrl, requestPath) {
|
|
234
|
-
const url = new URL(baseUrl);
|
|
235
|
-
url.pathname = joinUrlPath(requestPath);
|
|
236
|
-
url.search = "";
|
|
237
|
-
url.hash = "";
|
|
238
|
-
return url.toString();
|
|
239
|
-
}
|
|
240
127
|
|
|
241
|
-
function s3Uri(bucket, key = "") {
|
|
242
|
-
if (!bucket) {
|
|
243
|
-
throw new Error("aws-s3-cloudfront adapter requires a bucket or target");
|
|
244
|
-
}
|
|
245
|
-
const normalizedKey = normalizeS3Key(key);
|
|
246
|
-
return normalizedKey ? `s3://${bucket}/${normalizedKey}` : `s3://${bucket}`;
|
|
247
|
-
}
|
|
248
128
|
|
|
249
|
-
function cdnPath(value) {
|
|
250
|
-
const normalized = normalizeS3Key(value);
|
|
251
|
-
return normalized ? `/${normalized}` : "/";
|
|
252
|
-
}
|
|
253
129
|
|
|
254
|
-
function cdnWildcardPath(value) {
|
|
255
|
-
const normalized = normalizeS3Key(value);
|
|
256
|
-
return normalized ? `/${normalized}/*` : "/*";
|
|
257
|
-
}
|
|
258
130
|
|
|
259
|
-
function viewerWildcardPath(binding) {
|
|
260
|
-
try {
|
|
261
|
-
return cdnWildcardPath(new URL(binding.url).pathname);
|
|
262
|
-
} catch {
|
|
263
|
-
return cdnWildcardPath(binding.objectPrefix);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
131
|
|
|
267
|
-
function surfaceArtifactPrefix(binding) {
|
|
268
|
-
return normalizeS3Key(binding.sourcePath);
|
|
269
|
-
}
|
|
270
132
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
function mutableCacheControlArgs({ artifactRoot, bucket, objectPrefix, cacheControl = "", excludePatterns = [] }) {
|
|
295
|
-
if (!cacheControl) return [];
|
|
296
|
-
const args = [
|
|
297
|
-
"s3",
|
|
298
|
-
"cp",
|
|
299
|
-
artifactRoot,
|
|
300
|
-
s3Uri(bucket, objectPrefix),
|
|
301
|
-
"--recursive",
|
|
302
|
-
"--exclude",
|
|
303
|
-
"*",
|
|
304
|
-
"--include",
|
|
305
|
-
"*.html",
|
|
306
|
-
"--include",
|
|
307
|
-
"*.json",
|
|
308
|
-
"--include",
|
|
309
|
-
"*.xml",
|
|
310
|
-
];
|
|
311
|
-
for (const pattern of excludePatterns) {
|
|
312
|
-
args.push("--exclude", pattern);
|
|
313
|
-
}
|
|
314
|
-
args.push("--cache-control", cacheControl);
|
|
315
|
-
return args;
|
|
316
|
-
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
317
156
|
|
|
318
157
|
const PUBLICATION_ARCHIVE_POLICY_CONTRACT = "kungfu-buildchain-publication-archive-policy";
|
|
319
158
|
const OBSERVED_EVIDENCE_OWNERSHIP_CONTRACT = "kungfu-buildchain-observed-evidence-ownership";
|