@kungfu-tech/buildchain 3.0.6 → 3.0.7-alpha.1
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 +4 -2
- package/actions/promote-buildchain-ref/README.md +10 -0
- package/actions/release-tail/README.md +17 -0
- package/bin/buildchain.mjs +11 -0
- package/bin/internal/command-registry.mjs +2 -0
- package/contracts/auditable-demo-scenario-v1.schema.json +1 -1
- package/contracts/engineering-housekeeper-v1.schema.json +143 -0
- package/contracts/fixtures/engineering-housekeeper-v1/cases.json +68 -0
- package/contracts/fixtures/release-tail-capabilities-v1/kungfu-alpha.json +273 -0
- package/contracts/publication-rehearsal-capsule-v1.schema.json +173 -0
- package/contracts/release-tail-capabilities-v1.schema.json +199 -0
- package/contracts/release-tail-provider-bindings-v1.schema.json +56 -0
- package/dist/site/agent-index.json +3 -0
- package/dist/site/artifact-schemas.json +6 -0
- package/dist/site/buildchain-contract.json +42 -27
- package/dist/site/buildchain-site.json +384 -33
- package/dist/site/capability-registry.json +15 -12
- package/dist/site/cli-registry.json +102 -0
- package/dist/site/controller-registry.json +19 -3
- package/dist/site/kfd-claims.json +359 -17
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +53 -7
- package/dist/site/node-api-registry.json +5882 -3605
- package/dist/site/page-registry.json +348 -21
- package/dist/site/public-surface-audit.json +396 -16
- package/dist/site/publication-authority-registry.json +106 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +7 -0
- package/dist/site/schemas/publication-rehearsal-capsule-v1.schema.json +269 -0
- package/dist/site/schemas/release-tail-capabilities-v1.schema.json +353 -0
- package/dist/site/schemas/release-tail-provider-bindings-v1.schema.json +94 -0
- package/dist/site/site-manifest.json +35 -11
- package/dist/site/workflow-registry.json +204 -10
- package/docs/MAP.md +6 -2
- package/docs/auditable-demo.md +2 -2
- package/docs/cli-reference.md +136 -0
- package/docs/dev-delivery-warrant.md +49 -4
- package/docs/engineering-housekeeper.md +138 -0
- package/docs/lifecycle-protocol.md +4 -2
- package/docs/node-api-reference.md +495 -281
- package/docs/publication-rehearsal.md +94 -0
- package/docs/release-governance.md +17 -2
- package/docs/release-tail-contract.md +160 -0
- package/docs/release-tail-provider-plane.md +120 -0
- package/docs/reusable-build-surface.md +11 -0
- package/package.json +11 -3
- package/packages/core/artifact-signing.js +61 -0
- package/packages/core/buildchain-agent-manuals.js +3 -0
- package/packages/core/buildchain-config.js +66 -6
- package/packages/core/buildchain-kfd-claims.js +1 -1
- package/packages/core/buildchain-publication-authority.js +5 -0
- package/packages/core/controller-evidence.js +2 -1
- package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
- package/packages/core/dev-delivery-warrant-shadow.js +502 -0
- package/packages/core/dev-delivery-warrant.js +15 -6
- package/packages/core/diagnostics.js +8 -3
- package/packages/core/engineering-housekeeper-github-client.js +222 -0
- package/packages/core/engineering-housekeeper-github.js +501 -0
- package/packages/core/engineering-housekeeper.js +259 -0
- package/packages/core/index.js +42 -0
- package/packages/core/kfd-gate.js +45 -15
- package/packages/core/paper-agent-entry.js +11 -5
- package/packages/core/paper-repository.js +1 -0
- package/packages/core/paper-scaffold-content.js +21 -0
- package/packages/core/paper.js +28 -2
- package/packages/core/publication-rehearsal-projection.js +173 -0
- package/packages/core/publication-rehearsal-runtime.js +921 -0
- package/packages/core/release-passport.js +130 -20
- package/packages/core/release-tail-compatibility.js +60 -0
- package/packages/core/release-tail-provider-adapters.js +461 -0
- package/packages/core/release-tail-provider-plane.js +1228 -0
- package/scripts/assemble-publication-artifact-admission.mjs +1 -1
- package/scripts/assemble-self-publication-admission.mjs +2 -2
- package/scripts/audit-publication-control-plane.mjs +1 -1
- package/scripts/auditable-demo-bundle-verification.mjs +2 -3
- package/scripts/auditable-demo-platform.mjs +2 -2
- package/scripts/auditable-demo-renditions.mjs +1 -1
- package/scripts/auditable-demo.mjs +2 -2
- package/scripts/build-contract-core.mjs +8 -3
- package/scripts/build-standalone-binary.mjs +14 -3
- package/scripts/buildchain-cli-help.mjs +13 -0
- package/scripts/check-core-mechanism-inventory.mjs +347 -0
- package/scripts/check-inventory.mjs +10 -8
- package/scripts/check-maintainability.mjs +9 -2
- package/scripts/check-release-tail-contract.mjs +435 -0
- package/scripts/dev-delivery-warrant.mjs +31 -4
- package/scripts/dev-pr-auto-merge.mjs +30 -4
- package/scripts/dev-pr-delivery-warrant.mjs +50 -0
- package/scripts/engineering-housekeeper-workflow.mjs +394 -0
- package/scripts/generate-channel-promotion-workflow.mjs +10 -8
- package/scripts/generate-site-bundle.mjs +47 -4
- package/scripts/init-repo.mjs +26 -2
- package/scripts/inspect-artifact-signing-requests.mjs +6 -0
- package/scripts/materialize-self-release-candidate-version.mjs +137 -0
- package/scripts/publication-commit-evidence.mjs +69 -23
- package/scripts/release-candidate-resolver.mjs +16 -10
- package/scripts/release-tail.mjs +159 -0
- package/scripts/resume-from-candidate-run.mjs +123 -9
- package/scripts/seal-artifact-signing-requests.mjs +6 -0
- package/scripts/site-capability-metadata.mjs +13 -0
- package/scripts/v4-architecture.mjs +600 -0
- package/scripts/web-surface-core.mjs +8 -2
- package/scripts/workflow-call-contract.mjs +184 -5
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
GitHubHousekeeperClient,
|
|
6
|
+
applyGitHubHousekeeperPlan,
|
|
7
|
+
collectGitHubHousekeeperInventory,
|
|
8
|
+
} from "../packages/core/engineering-housekeeper-github.js";
|
|
9
|
+
|
|
10
|
+
const VALID_MODES = new Set(["report", "apply"]);
|
|
11
|
+
const VALID_SCOPES = new Set(["branches", "pull-requests"]);
|
|
12
|
+
const DEFAULT_OUTPUT_DIRECTORY = ".buildchain/engineering-housekeeper";
|
|
13
|
+
|
|
14
|
+
function requiredString(value, field) {
|
|
15
|
+
const normalized = String(value || "").trim();
|
|
16
|
+
if (!normalized) throw new Error(`${field} is required`);
|
|
17
|
+
return normalized;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function boolOption(value, fallback = false) {
|
|
21
|
+
if (value === undefined || value === null || value === "") return fallback;
|
|
22
|
+
if (typeof value === "boolean") return value;
|
|
23
|
+
const normalized = String(value).trim().toLowerCase();
|
|
24
|
+
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
25
|
+
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
26
|
+
throw new Error(`boolean input must be true or false, got: ${value}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function positiveInteger(value, fallback, field) {
|
|
30
|
+
const selected =
|
|
31
|
+
value === undefined || value === "" ? fallback : Number(value);
|
|
32
|
+
if (!Number.isInteger(selected) || selected < 1) {
|
|
33
|
+
throw new Error(`${field} must be a positive integer`);
|
|
34
|
+
}
|
|
35
|
+
return selected;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function splitPatterns(value, fallback) {
|
|
39
|
+
const normalized = String(value || "").trim();
|
|
40
|
+
if (!normalized) return [...fallback];
|
|
41
|
+
return [
|
|
42
|
+
...new Set(
|
|
43
|
+
normalized
|
|
44
|
+
.split(/[\n,]+/)
|
|
45
|
+
.map((entry) => entry.trim())
|
|
46
|
+
.filter(Boolean),
|
|
47
|
+
),
|
|
48
|
+
].sort();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function normalizeRepository(value) {
|
|
52
|
+
const repository = requiredString(value, "repository");
|
|
53
|
+
if (!/^[^/\s]+\/[^/\s]+$/.test(repository)) {
|
|
54
|
+
throw new Error(`repository must be owner/repo, got: ${repository}`);
|
|
55
|
+
}
|
|
56
|
+
return repository;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function normalizeMode(value) {
|
|
60
|
+
const mode = String(value || "report")
|
|
61
|
+
.trim()
|
|
62
|
+
.toLowerCase();
|
|
63
|
+
if (!VALID_MODES.has(mode)) {
|
|
64
|
+
throw new Error(`mode must be report or apply, got: ${value || "<empty>"}`);
|
|
65
|
+
}
|
|
66
|
+
return mode;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function normalizeHousekeeperWorkflowOptions(options = {}) {
|
|
70
|
+
const mode = normalizeMode(options.mode ?? process.env.HOUSEKEEPER_MODE);
|
|
71
|
+
const applyEnabled = boolOption(
|
|
72
|
+
options.applyEnabled ?? process.env.HOUSEKEEPER_APPLY_ENABLED,
|
|
73
|
+
false,
|
|
74
|
+
);
|
|
75
|
+
if (mode === "apply" && !applyEnabled) {
|
|
76
|
+
throw new Error("apply mode requires apply-enabled=true");
|
|
77
|
+
}
|
|
78
|
+
if (mode === "report" && applyEnabled) {
|
|
79
|
+
throw new Error("apply-enabled=true is only valid when mode=apply");
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
mode,
|
|
83
|
+
applyEnabled,
|
|
84
|
+
repository: normalizeRepository(
|
|
85
|
+
options.repository ||
|
|
86
|
+
process.env.HOUSEKEEPER_REPOSITORY ||
|
|
87
|
+
process.env.GITHUB_REPOSITORY,
|
|
88
|
+
),
|
|
89
|
+
targetBranch: requiredString(
|
|
90
|
+
options.targetBranch || process.env.HOUSEKEEPER_TARGET_BRANCH,
|
|
91
|
+
"target-branch",
|
|
92
|
+
).replace(/^refs\/heads\//, ""),
|
|
93
|
+
staleDays: positiveInteger(
|
|
94
|
+
options.staleDays ?? process.env.HOUSEKEEPER_STALE_DAYS,
|
|
95
|
+
30,
|
|
96
|
+
"stale-days",
|
|
97
|
+
),
|
|
98
|
+
maxActions: positiveInteger(
|
|
99
|
+
options.maxActions ?? process.env.HOUSEKEEPER_MAX_ACTIONS,
|
|
100
|
+
20,
|
|
101
|
+
"max-actions",
|
|
102
|
+
),
|
|
103
|
+
protectedPatterns: splitPatterns(
|
|
104
|
+
options.protectedPatterns ?? process.env.HOUSEKEEPER_PROTECTED_PATTERNS,
|
|
105
|
+
["dev/**", "alpha/**", "release/**", "publish-gate/**"],
|
|
106
|
+
),
|
|
107
|
+
retainedPatterns: splitPatterns(
|
|
108
|
+
options.retainedPatterns ?? process.env.HOUSEKEEPER_RETAINED_PATTERNS,
|
|
109
|
+
["train/**", "authority/**"],
|
|
110
|
+
),
|
|
111
|
+
stalePullRequestLabel: String(
|
|
112
|
+
options.stalePullRequestLabel ??
|
|
113
|
+
process.env.HOUSEKEEPER_STALE_PR_LABEL ??
|
|
114
|
+
"",
|
|
115
|
+
).trim(),
|
|
116
|
+
observedAt: String(
|
|
117
|
+
options.observedAt ||
|
|
118
|
+
process.env.HOUSEKEEPER_OBSERVED_AT ||
|
|
119
|
+
new Date().toISOString(),
|
|
120
|
+
),
|
|
121
|
+
appliedAt: String(
|
|
122
|
+
options.appliedAt ||
|
|
123
|
+
process.env.HOUSEKEEPER_APPLIED_AT ||
|
|
124
|
+
new Date().toISOString(),
|
|
125
|
+
),
|
|
126
|
+
outputDirectory: path.resolve(
|
|
127
|
+
String(
|
|
128
|
+
options.outputDirectory ||
|
|
129
|
+
process.env.HOUSEKEEPER_OUTPUT_DIRECTORY ||
|
|
130
|
+
DEFAULT_OUTPUT_DIRECTORY,
|
|
131
|
+
),
|
|
132
|
+
),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function workflowPolicy(options) {
|
|
137
|
+
return {
|
|
138
|
+
protectedPatterns: options.protectedPatterns,
|
|
139
|
+
retainedPatterns: options.retainedPatterns,
|
|
140
|
+
pullRequests: {
|
|
141
|
+
reportStale: true,
|
|
142
|
+
label: options.stalePullRequestLabel,
|
|
143
|
+
autoClose: false,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function actionIdentity(action) {
|
|
149
|
+
return action.name
|
|
150
|
+
? `${action.kind}:${action.name}`
|
|
151
|
+
: `${action.kind}:#${action.number}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function decisionFor(entry) {
|
|
155
|
+
if (entry.kind === "branch") return entry.decision;
|
|
156
|
+
return entry.actions.length > 0 ? entry.actions.join(",") : "report-only";
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function renderHousekeeperWorkflowReport(
|
|
160
|
+
plan,
|
|
161
|
+
receipt,
|
|
162
|
+
{ mode, scope = "all" } = {},
|
|
163
|
+
) {
|
|
164
|
+
const lines = [
|
|
165
|
+
"## Engineering Housekeeper",
|
|
166
|
+
"",
|
|
167
|
+
`Mode: \`${mode || "report"}\``,
|
|
168
|
+
`Scope: \`${scope}\``,
|
|
169
|
+
`Repository: \`${plan.repository}\``,
|
|
170
|
+
`Target ref: \`${plan.target.name}@${plan.target.headOid}\``,
|
|
171
|
+
`Observed at: \`${plan.observedAt}\``,
|
|
172
|
+
`Plan root: \`${plan.planRoot}\``,
|
|
173
|
+
`Receipt root: \`${receipt.receiptRoot}\``,
|
|
174
|
+
"",
|
|
175
|
+
"### Decisions",
|
|
176
|
+
"",
|
|
177
|
+
"| Subject | Observed ref | Decision | Reason codes |",
|
|
178
|
+
"| --- | --- | --- | --- |",
|
|
179
|
+
];
|
|
180
|
+
for (const entry of plan.inventory) {
|
|
181
|
+
const subject =
|
|
182
|
+
entry.kind === "branch" ? entry.name : `PR #${entry.number}`;
|
|
183
|
+
lines.push(
|
|
184
|
+
`| \`${subject}\` | \`${entry.headOid}\` | ${decisionFor(entry)} | \`${entry.reasonCodes.join(",")}\` |`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (plan.inventory.length === 0)
|
|
188
|
+
lines.push("| - | - | retain | `inventory.empty` |");
|
|
189
|
+
lines.push(
|
|
190
|
+
"",
|
|
191
|
+
"### Outcomes",
|
|
192
|
+
"",
|
|
193
|
+
"| Action | Outcome | Details |",
|
|
194
|
+
"| --- | --- | --- |",
|
|
195
|
+
);
|
|
196
|
+
for (const outcome of receipt.outcomes) {
|
|
197
|
+
const details =
|
|
198
|
+
outcome.reasonCodes?.join(",") || outcome.providerError?.operation || "-";
|
|
199
|
+
lines.push(
|
|
200
|
+
`| \`${outcome.action}\` | ${outcome.status} | \`${details}\` |`,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
if (receipt.outcomes.length === 0)
|
|
204
|
+
lines.push("| - | no-op | `no-actions-in-scope` |");
|
|
205
|
+
return `${lines.join("\n")}\n`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function writeJson(filePath, value) {
|
|
209
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
210
|
+
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function writeText(filePath, value) {
|
|
214
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
215
|
+
fs.writeFileSync(filePath, value);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function writeOutputs(outputs, outputFile = process.env.GITHUB_OUTPUT) {
|
|
219
|
+
if (!outputFile) return;
|
|
220
|
+
const lines = Object.entries(outputs).map(
|
|
221
|
+
([key, value]) => `${key}=${String(value).replace(/\n/g, "%0A")}`,
|
|
222
|
+
);
|
|
223
|
+
fs.appendFileSync(outputFile, `${lines.join("\n")}\n`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function appendSummary(
|
|
227
|
+
markdown,
|
|
228
|
+
summaryFile = process.env.GITHUB_STEP_SUMMARY,
|
|
229
|
+
) {
|
|
230
|
+
if (summaryFile) fs.appendFileSync(summaryFile, markdown);
|
|
231
|
+
else process.stdout.write(markdown);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export async function createHousekeeperWorkflowPlan(
|
|
235
|
+
optionsInput = {},
|
|
236
|
+
clientInput,
|
|
237
|
+
) {
|
|
238
|
+
const options = normalizeHousekeeperWorkflowOptions(optionsInput);
|
|
239
|
+
const client =
|
|
240
|
+
clientInput ||
|
|
241
|
+
new GitHubHousekeeperClient({
|
|
242
|
+
token: requiredString(process.env.GITHUB_TOKEN, "GitHub token"),
|
|
243
|
+
});
|
|
244
|
+
const plan = await collectGitHubHousekeeperInventory({
|
|
245
|
+
client,
|
|
246
|
+
repository: options.repository,
|
|
247
|
+
targetBranch: options.targetBranch,
|
|
248
|
+
observedAt: options.observedAt,
|
|
249
|
+
staleDays: options.staleDays,
|
|
250
|
+
policy: workflowPolicy(options),
|
|
251
|
+
});
|
|
252
|
+
const receipt = await applyGitHubHousekeeperPlan({
|
|
253
|
+
client,
|
|
254
|
+
plan,
|
|
255
|
+
dryRun: true,
|
|
256
|
+
appliedAt: options.appliedAt,
|
|
257
|
+
staleDays: options.staleDays,
|
|
258
|
+
maxActions: options.maxActions,
|
|
259
|
+
});
|
|
260
|
+
return { options, plan, receipt };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function selectHousekeeperActions(plan, scope, maxActions) {
|
|
264
|
+
if (!VALID_SCOPES.has(scope))
|
|
265
|
+
throw new Error(`scope must be branches or pull-requests, got: ${scope}`);
|
|
266
|
+
return plan.actions
|
|
267
|
+
.slice(0, positiveInteger(maxActions, 20, "max-actions"))
|
|
268
|
+
.filter((action) =>
|
|
269
|
+
scope === "branches"
|
|
270
|
+
? action.kind === "delete-branch"
|
|
271
|
+
: action.kind.endsWith("-pull-request"),
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export async function applyHousekeeperWorkflowScope({
|
|
276
|
+
options: optionsInput = {},
|
|
277
|
+
plan,
|
|
278
|
+
scope,
|
|
279
|
+
client: clientInput,
|
|
280
|
+
}) {
|
|
281
|
+
const options = normalizeHousekeeperWorkflowOptions(optionsInput);
|
|
282
|
+
if (options.mode !== "apply" || !options.applyEnabled) {
|
|
283
|
+
throw new Error("scope apply requires mode=apply and apply-enabled=true");
|
|
284
|
+
}
|
|
285
|
+
if (
|
|
286
|
+
plan.repository !== options.repository ||
|
|
287
|
+
plan.target.name !== options.targetBranch
|
|
288
|
+
) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
"plan repository or target branch does not match current workflow inputs",
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
const client =
|
|
294
|
+
clientInput ||
|
|
295
|
+
new GitHubHousekeeperClient({
|
|
296
|
+
token: requiredString(process.env.GITHUB_TOKEN, "GitHub token"),
|
|
297
|
+
});
|
|
298
|
+
const scopedPlan = {
|
|
299
|
+
...plan,
|
|
300
|
+
actions: selectHousekeeperActions(plan, scope, options.maxActions),
|
|
301
|
+
};
|
|
302
|
+
const receipt = await applyGitHubHousekeeperPlan({
|
|
303
|
+
client,
|
|
304
|
+
plan: scopedPlan,
|
|
305
|
+
dryRun: false,
|
|
306
|
+
appliedAt: options.appliedAt,
|
|
307
|
+
staleDays: options.staleDays,
|
|
308
|
+
maxActions: Math.max(1, scopedPlan.actions.length),
|
|
309
|
+
});
|
|
310
|
+
return { options, plan, scopedPlan, receipt };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async function planCommand() {
|
|
314
|
+
const result = await createHousekeeperWorkflowPlan();
|
|
315
|
+
const planPath = path.join(result.options.outputDirectory, "plan.json");
|
|
316
|
+
const reportPath = path.join(result.options.outputDirectory, "report.md");
|
|
317
|
+
const receiptPath = path.join(
|
|
318
|
+
result.options.outputDirectory,
|
|
319
|
+
"report-receipt.json",
|
|
320
|
+
);
|
|
321
|
+
const report = renderHousekeeperWorkflowReport(result.plan, result.receipt, {
|
|
322
|
+
mode: result.options.mode,
|
|
323
|
+
});
|
|
324
|
+
writeJson(planPath, result.plan);
|
|
325
|
+
writeJson(receiptPath, result.receipt);
|
|
326
|
+
writeText(reportPath, report);
|
|
327
|
+
appendSummary(report);
|
|
328
|
+
writeOutputs({
|
|
329
|
+
"plan-path": planPath,
|
|
330
|
+
"report-path": reportPath,
|
|
331
|
+
"report-receipt-path": receiptPath,
|
|
332
|
+
"plan-root": result.plan.planRoot,
|
|
333
|
+
"report-receipt-root": result.receipt.receiptRoot,
|
|
334
|
+
"action-count": result.plan.actions.length,
|
|
335
|
+
"branch-action-count": result.plan.actions.filter(
|
|
336
|
+
(action) => action.kind === "delete-branch",
|
|
337
|
+
).length,
|
|
338
|
+
"pull-request-action-count": result.plan.actions.filter((action) =>
|
|
339
|
+
action.kind.endsWith("-pull-request"),
|
|
340
|
+
).length,
|
|
341
|
+
outcome:
|
|
342
|
+
result.plan.actions.length === 0
|
|
343
|
+
? "no-actions"
|
|
344
|
+
: `${result.options.mode}-ready`,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function applyCommand(scope) {
|
|
349
|
+
const planPath = path.resolve(
|
|
350
|
+
requiredString(process.env.HOUSEKEEPER_PLAN_PATH, "HOUSEKEEPER_PLAN_PATH"),
|
|
351
|
+
);
|
|
352
|
+
const plan = JSON.parse(fs.readFileSync(planPath, "utf8"));
|
|
353
|
+
const result = await applyHousekeeperWorkflowScope({ plan, scope });
|
|
354
|
+
const receiptPath = path.join(
|
|
355
|
+
result.options.outputDirectory,
|
|
356
|
+
`${scope}-receipt.json`,
|
|
357
|
+
);
|
|
358
|
+
const reportPath = path.join(
|
|
359
|
+
result.options.outputDirectory,
|
|
360
|
+
`${scope}-report.md`,
|
|
361
|
+
);
|
|
362
|
+
const report = renderHousekeeperWorkflowReport(result.plan, result.receipt, {
|
|
363
|
+
mode: result.options.mode,
|
|
364
|
+
scope,
|
|
365
|
+
});
|
|
366
|
+
writeJson(receiptPath, result.receipt);
|
|
367
|
+
writeText(reportPath, report);
|
|
368
|
+
appendSummary(report);
|
|
369
|
+
writeOutputs({
|
|
370
|
+
"receipt-path": receiptPath,
|
|
371
|
+
"report-path": reportPath,
|
|
372
|
+
"receipt-root": result.receipt.receiptRoot,
|
|
373
|
+
"outcome-count": result.receipt.outcomes.length,
|
|
374
|
+
"selected-action-count": result.scopedPlan.actions.length,
|
|
375
|
+
outcome: result.scopedPlan.actions.length === 0 ? "no-actions" : "applied",
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async function main() {
|
|
380
|
+
const command = process.argv[2];
|
|
381
|
+
if (command === "plan") return planCommand();
|
|
382
|
+
if (command === "apply")
|
|
383
|
+
return applyCommand(requiredString(process.argv[3], "scope"));
|
|
384
|
+
throw new Error(
|
|
385
|
+
"usage: engineering-housekeeper-workflow.mjs plan | apply <branches|pull-requests>",
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
390
|
+
main().catch((error) => {
|
|
391
|
+
console.error(error.stack || error.message);
|
|
392
|
+
process.exit(1);
|
|
393
|
+
});
|
|
394
|
+
}
|
|
@@ -20,6 +20,7 @@ const internalInputs = new Set([
|
|
|
20
20
|
"promotion-publication-channel",
|
|
21
21
|
"promotion-target-ref",
|
|
22
22
|
"promotion-override-used",
|
|
23
|
+
"publication-authority-workflow-path",
|
|
23
24
|
]);
|
|
24
25
|
|
|
25
26
|
function blockBetween(source, start, end) {
|
|
@@ -116,6 +117,9 @@ function forwardedInputs(inputNames, { includeInternal = true, unsupportedInputs
|
|
|
116
117
|
if (name === "promotion-override-used") {
|
|
117
118
|
return ` ${name}: \${{ needs.resolve-promotion.outputs.override-used == 'true' }}`;
|
|
118
119
|
}
|
|
120
|
+
if (name === "publication-authority-workflow-path") {
|
|
121
|
+
return " publication-authority-workflow-path: .github/workflows/.release-candidate-promote.yml";
|
|
122
|
+
}
|
|
119
123
|
return routed
|
|
120
124
|
? ` ${name}: \${{ needs.resolve-promotion.outputs.${routed} }}`
|
|
121
125
|
: ` ${name}: \${{ inputs.${name} }}`;
|
|
@@ -255,15 +259,13 @@ jobs:
|
|
|
255
259
|
exit 1
|
|
256
260
|
fi
|
|
257
261
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
exit 1
|
|
262
|
+
if [[ "\${ref}" =~ ^[0-9A-Fa-f]{40}$ ]]; then sha="\${ref,,}"; else
|
|
263
|
+
remote_url="https://github.com/\${repository}.git"; refs="$(git ls-remote "\${remote_url}" "refs/heads/\${ref}" "refs/tags/\${ref}" "refs/tags/\${ref}^{}")"
|
|
264
|
+
head_sha="$(printf '%s\\n' "\${refs}" | awk -v name="refs/heads/\${ref}" '$2 == name { print tolower($1) }')"
|
|
265
|
+
tag_sha="$(printf '%s\\n' "\${refs}" | awk -v peeled="refs/tags/\${ref}^{}" -v name="refs/tags/\${ref}" '$2 == peeled { print tolower($1); found=1 } $2 == name && !found { fallback=tolower($1) } END { if (!found && fallback != "") print fallback }')"
|
|
266
|
+
if [[ -n "\${head_sha}" && -n "\${tag_sha}" && "\${head_sha}" != "\${tag_sha}" ]]; then echo "::error::Promotion router ref is ambiguous between branch and tag"; exit 1; fi
|
|
267
|
+
sha="\${head_sha:-\${tag_sha}}"
|
|
265
268
|
fi
|
|
266
|
-
sha="\${head_sha:-\${tag_sha}}"
|
|
267
269
|
if [[ ! "\${sha}" =~ ^[0-9a-f]{40}$ ]]; then
|
|
268
270
|
echo "::error::Promotion router ref did not resolve to one exact commit SHA"
|
|
269
271
|
exit 1
|
|
@@ -332,11 +332,16 @@ function capabilityGroup(id) {
|
|
|
332
332
|
return id;
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
-
function publicSurfaceLifecycle({
|
|
335
|
+
function publicSurfaceLifecycle({
|
|
336
|
+
owner,
|
|
337
|
+
maturity,
|
|
338
|
+
nonDuplicationRationale,
|
|
339
|
+
introducedVersion = "pre-3.0.2-alpha.4",
|
|
340
|
+
}) {
|
|
336
341
|
return {
|
|
337
342
|
owner,
|
|
338
343
|
maturity,
|
|
339
|
-
introducedVersion
|
|
344
|
+
introducedVersion,
|
|
340
345
|
compatibilityPromise: "preserved-through-the-v3-major-line",
|
|
341
346
|
deprecationReplacement: "",
|
|
342
347
|
sunsetCondition: "explicit-breaking-change-review-in-a-future-major-line",
|
|
@@ -360,12 +365,16 @@ const manualMetaById = new Map(Object.entries({
|
|
|
360
365
|
"auditable-demo": { capabilityGroup: "reusable-build", audience: ["consumer", "agent"], maturity: "preview", order: 207 },
|
|
361
366
|
"binary-distribution": { capabilityGroup: "release-passport-trust", audience: ["release-operator", "agent"], maturity: "stable", order: 110 },
|
|
362
367
|
"publish-transaction": { capabilityGroup: "release-passport-trust", audience: ["release-operator"], maturity: "stable", order: 120 },
|
|
368
|
+
"release-tail-contract": { capabilityGroup: "release-passport-trust", audience: ["release-operator", "agent", "maintainer"], maturity: "draft", order: 122 },
|
|
369
|
+
"release-tail-provider-plane": { capabilityGroup: "release-passport-trust", audience: ["release-operator", "agent", "maintainer"], maturity: "preview", order: 123 },
|
|
370
|
+
"publication-rehearsal": { capabilityGroup: "release-passport-trust", audience: ["release-operator", "agent", "maintainer"], maturity: "preview", order: 124 },
|
|
363
371
|
"release-activation-transaction": { capabilityGroup: "release-passport-trust", audience: ["release-operator", "agent"], maturity: "preview", order: 125 },
|
|
364
372
|
"release-candidate": { capabilityGroup: "reusable-build", audience: ["release-operator", "consumer"], maturity: "stable", order: 130 },
|
|
365
373
|
"stable-candidate-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer"], maturity: "preview", order: 135 },
|
|
366
374
|
"dev-qualification-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 136 },
|
|
367
375
|
"dev-alpha-candidate-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 137 },
|
|
368
376
|
"observed-evidence-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 140 },
|
|
377
|
+
"engineering-housekeeper": { capabilityGroup: "governance-versioning", audience: ["maintainer", "consumer", "agent"], maturity: "preview", order: 145 },
|
|
369
378
|
"reusable-build-surface": { capabilityGroup: "reusable-build", audience: ["consumer", "release-operator"], maturity: "stable", order: 200 },
|
|
370
379
|
"lifecycle-protocol": { capabilityGroup: "reusable-build", audience: ["consumer", "developer"], maturity: "stable", order: 210 },
|
|
371
380
|
"runtime-train-validation": { capabilityGroup: "governance-versioning", audience: ["maintainer", "consumer"], maturity: "stable", order: 220 },
|
|
@@ -514,16 +523,18 @@ function buildCapabilityRegistry({ docs, pages, cliRegistry, manualRegistry, nod
|
|
|
514
523
|
|
|
515
524
|
function workflowCapabilityGroup(entry) {
|
|
516
525
|
if (entry.id === "github-artifact-attestation") return capabilityGroup("release-passport-trust");
|
|
526
|
+
if (entry.id === "release-tail") return capabilityGroup("release-passport-trust");
|
|
517
527
|
if (["web-surface", "release-propagation"].includes(entry.id)) return capabilityGroup("site-and-propagation");
|
|
518
528
|
if (["build", "release-candidate-promote", "publication-artifact", "paper-release"].includes(entry.id)) return capabilityGroup("reusable-build");
|
|
519
529
|
if (["buildchain-ref-promotion", "release-line-bootstrap"].includes(entry.id)) return capabilityGroup("release-passport-trust");
|
|
520
|
-
if (entry.id.includes("patrol") || entry.id.includes("dev-pr-auto-merge") || entry.id.includes("dev-delivery-warrant") || entry.id.includes("buildchain-dev-delivery")) return capabilityGroup("governance-versioning");
|
|
530
|
+
if (entry.id.includes("patrol") || entry.id.includes("housekeeper") || entry.id.includes("dev-pr-auto-merge") || entry.id.includes("dev-delivery-warrant") || entry.id.includes("buildchain-dev-delivery")) return capabilityGroup("governance-versioning");
|
|
521
531
|
if (entry.status === "repository-internal" || entry.status === "compatibility-fixture") return capabilityGroup("api-cli-reference");
|
|
522
532
|
return capabilityGroup("api-cli-reference");
|
|
523
533
|
}
|
|
524
534
|
|
|
525
535
|
function actionCapabilityGroup(id) {
|
|
526
536
|
if (id === "github-artifact-attestation") return capabilityGroup("release-passport-trust");
|
|
537
|
+
if (id === "release-tail") return capabilityGroup("release-passport-trust");
|
|
527
538
|
if (id === "promote-buildchain-ref") return capabilityGroup("release-passport-trust");
|
|
528
539
|
if (id === "run-lifecycle" || id === "validate-config") return capabilityGroup("reusable-build");
|
|
529
540
|
if (id === "report-buildchain-issue") return capabilityGroup("observability-diagnostics");
|
|
@@ -727,6 +738,7 @@ function buildSiteBundle() {
|
|
|
727
738
|
"docs/dev-qualification-patrol.md",
|
|
728
739
|
"docs/dev-alpha-candidate-patrol.md",
|
|
729
740
|
"docs/observed-evidence-patrol.md",
|
|
741
|
+
"docs/engineering-housekeeper.md",
|
|
730
742
|
"docs/release-governance.md",
|
|
731
743
|
"docs/release-passport.md",
|
|
732
744
|
"docs/controller-evidence.md",
|
|
@@ -776,6 +788,7 @@ function buildSiteBundle() {
|
|
|
776
788
|
["release-candidate-promote", "release-governance"],
|
|
777
789
|
["paper-release", "reusable-build"],
|
|
778
790
|
["release-propagation", "release-propagation"],
|
|
791
|
+
["release-tail", "release-tail-provider-plane"],
|
|
779
792
|
["dev-pr-auto-merge", "dev-governance"],
|
|
780
793
|
["buildchain-dev-delivery", "dev-governance"],
|
|
781
794
|
["github-governance-audit", "dev-governance"],
|
|
@@ -785,6 +798,10 @@ function buildSiteBundle() {
|
|
|
785
798
|
["buildchain-patrol-daily", "repository-patrol"],
|
|
786
799
|
["buildchain-patrol-weekly", "repository-patrol"],
|
|
787
800
|
["buildchain-patrol-monthly", "repository-patrol"],
|
|
801
|
+
["engineering-housekeeper", "repository-patrol"],
|
|
802
|
+
["engineering-housekeeper-daily", "repository-patrol"],
|
|
803
|
+
["engineering-housekeeper-weekly", "repository-patrol"],
|
|
804
|
+
["engineering-housekeeper-monthly", "repository-patrol"],
|
|
788
805
|
["stable-candidate-patrol", "repository-patrol"],
|
|
789
806
|
["dev-qualification-patrol", "repository-patrol"],
|
|
790
807
|
["dev-alpha-candidate-patrol", "repository-patrol"],
|
|
@@ -802,8 +819,13 @@ function buildSiteBundle() {
|
|
|
802
819
|
["candidate-lab", "repository-internal"],
|
|
803
820
|
["build-surface-fixture", "repository-internal"],
|
|
804
821
|
["buildchain-stable-candidate-qualification", "repository-internal"],
|
|
822
|
+
["engineering-housekeeper", "preview"],
|
|
823
|
+
["engineering-housekeeper-daily", "preview"],
|
|
824
|
+
["engineering-housekeeper-weekly", "preview"],
|
|
825
|
+
["engineering-housekeeper-monthly", "preview"],
|
|
805
826
|
["self-hosted-runner-smoke", "compatibility-fixture"],
|
|
806
827
|
]);
|
|
828
|
+
const engineeringHousekeeper = entry.id.startsWith("engineering-housekeeper");
|
|
807
829
|
return {
|
|
808
830
|
...entry,
|
|
809
831
|
surface: surfaceById.get(entry.id) || (entry.path.includes("/.") ? "reusable-workflow" : "repository-workflow"),
|
|
@@ -815,7 +837,10 @@ function buildSiteBundle() {
|
|
|
815
837
|
...publicSurfaceLifecycle({
|
|
816
838
|
owner: "buildchain-workflows",
|
|
817
839
|
maturity: statusById.get(entry.id) || "active",
|
|
818
|
-
|
|
840
|
+
introducedVersion: engineeringHousekeeper ? packageJson.version : undefined,
|
|
841
|
+
nonDuplicationRationale: engineeringHousekeeper
|
|
842
|
+
? "One reusable policy and evidence boundary owns Engineering Housekeeper execution; scheduled callers contain cadence values only."
|
|
843
|
+
: "Existing workflow identity retained for caller compatibility and repository orchestration.",
|
|
819
844
|
}),
|
|
820
845
|
};
|
|
821
846
|
}),
|
|
@@ -901,6 +926,9 @@ function buildSiteBundle() {
|
|
|
901
926
|
"buildchain.release.json",
|
|
902
927
|
"release-passport-check-manifest.json",
|
|
903
928
|
"schemas/release-passport-v1.schema.json",
|
|
929
|
+
"schemas/publication-rehearsal-capsule-v1.schema.json",
|
|
930
|
+
"schemas/release-tail-capabilities-v1.schema.json",
|
|
931
|
+
"schemas/release-tail-provider-bindings-v1.schema.json",
|
|
904
932
|
"schemas/kfd-agent-hub-adoption.schema.json",
|
|
905
933
|
"schemas/kfd-product-gate-input-v1.schema.json",
|
|
906
934
|
"schemas/kfd-support-projection-v1.schema.json",
|
|
@@ -936,6 +964,9 @@ function buildSiteBundle() {
|
|
|
936
964
|
"artifact-schemas.json",
|
|
937
965
|
"release-passport-check-manifest.json",
|
|
938
966
|
"schemas/release-passport-v1.schema.json",
|
|
967
|
+
"schemas/publication-rehearsal-capsule-v1.schema.json",
|
|
968
|
+
"schemas/release-tail-capabilities-v1.schema.json",
|
|
969
|
+
"schemas/release-tail-provider-bindings-v1.schema.json",
|
|
939
970
|
"schemas/kfd-agent-hub-adoption.schema.json",
|
|
940
971
|
"schemas/kfd-product-gate-input-v1.schema.json",
|
|
941
972
|
"schemas/kfd-support-projection-v1.schema.json",
|
|
@@ -1067,6 +1098,9 @@ function buildSiteBundle() {
|
|
|
1067
1098
|
"artifact-schemas.json",
|
|
1068
1099
|
"release-passport-check-manifest.json",
|
|
1069
1100
|
"schemas/release-passport-v1.schema.json",
|
|
1101
|
+
"schemas/publication-rehearsal-capsule-v1.schema.json",
|
|
1102
|
+
"schemas/release-tail-capabilities-v1.schema.json",
|
|
1103
|
+
"schemas/release-tail-provider-bindings-v1.schema.json",
|
|
1070
1104
|
"schemas/kfd-agent-hub-adoption.schema.json",
|
|
1071
1105
|
"schemas/kfd-product-gate-input-v1.schema.json",
|
|
1072
1106
|
"schemas/kfd-support-projection-v1.schema.json",
|
|
@@ -1242,6 +1276,15 @@ function buildSiteBundle() {
|
|
|
1242
1276
|
"artifact-schemas.json": artifactSchemas,
|
|
1243
1277
|
"release-passport-check-manifest.json": createReleasePassportCheckManifest(),
|
|
1244
1278
|
"schemas/release-passport-v1.schema.json": RELEASE_PASSPORT_SCHEMA,
|
|
1279
|
+
"schemas/publication-rehearsal-capsule-v1.schema.json": readJson(
|
|
1280
|
+
"contracts/publication-rehearsal-capsule-v1.schema.json",
|
|
1281
|
+
),
|
|
1282
|
+
"schemas/release-tail-capabilities-v1.schema.json": readJson(
|
|
1283
|
+
"contracts/release-tail-capabilities-v1.schema.json",
|
|
1284
|
+
),
|
|
1285
|
+
"schemas/release-tail-provider-bindings-v1.schema.json": readJson(
|
|
1286
|
+
"contracts/release-tail-provider-bindings-v1.schema.json",
|
|
1287
|
+
),
|
|
1245
1288
|
"schemas/kfd-agent-hub-adoption.schema.json": KFD_AGENT_HUB_ADOPTION_SCHEMA,
|
|
1246
1289
|
"schemas/kfd-product-gate-input-v1.schema.json": KFD_PRODUCT_GATE_INPUT_SCHEMA,
|
|
1247
1290
|
"schemas/kfd-support-projection-v1.schema.json": KFD_SUPPORT_PROJECTION_SCHEMA,
|
package/scripts/init-repo.mjs
CHANGED
|
@@ -4,6 +4,12 @@ import path from "node:path";
|
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { BUILDCHAIN_CONFIG_PATH } from "../packages/core/buildchain-layout.js";
|
|
6
6
|
import { detectPackageManager, assertPackageManager } from "../packages/core/package-manager.js";
|
|
7
|
+
import {
|
|
8
|
+
PUBLICATION_REHEARSAL_WORKFLOW_PATH,
|
|
9
|
+
appendPublicationRehearsalToml,
|
|
10
|
+
mergePublicationRehearsalAgentInstructions,
|
|
11
|
+
publicationRehearsalWorkflow,
|
|
12
|
+
} from "../packages/core/publication-rehearsal-projection.js";
|
|
7
13
|
|
|
8
14
|
const BUILDCHAIN_WORKFLOW_REF = "kungfu-systems/buildchain/.github/workflows/.build.yml@v3";
|
|
9
15
|
const DEFAULT_PUBLICATION_LATEX_IMAGE = "ghcr.io/kungfu-systems/build-images/latex-pdf-builder";
|
|
@@ -401,6 +407,13 @@ function writeIfAllowed(filePath, content, { force }) {
|
|
|
401
407
|
return filePath;
|
|
402
408
|
}
|
|
403
409
|
|
|
410
|
+
function writeManagedAgentEntry(filePath, current) {
|
|
411
|
+
const content = mergePublicationRehearsalAgentInstructions(current);
|
|
412
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
413
|
+
fs.writeFileSync(filePath, content);
|
|
414
|
+
return filePath;
|
|
415
|
+
}
|
|
416
|
+
|
|
404
417
|
export function initBuildchainRepo({
|
|
405
418
|
cwd = process.cwd(),
|
|
406
419
|
type = "package",
|
|
@@ -411,7 +424,7 @@ export function initBuildchainRepo({
|
|
|
411
424
|
} = {}) {
|
|
412
425
|
const resolvedCwd = path.resolve(cwd);
|
|
413
426
|
const manager = detectOrDefaultPackageManager(resolvedCwd, packageManager);
|
|
414
|
-
const toml = (() => {
|
|
427
|
+
const toml = appendPublicationRehearsalToml((() => {
|
|
415
428
|
if (type === "package") {
|
|
416
429
|
return packageToml(resolvedCwd, manager);
|
|
417
430
|
}
|
|
@@ -431,7 +444,12 @@ export function initBuildchainRepo({
|
|
|
431
444
|
return anchoredPackageToml(resolvedCwd, manager);
|
|
432
445
|
}
|
|
433
446
|
throw new Error("init --type must be one of package, native, web-surface, infra-contract, publication-artifact, or anchored-package");
|
|
434
|
-
})();
|
|
447
|
+
})());
|
|
448
|
+
|
|
449
|
+
const agentsPath = path.join(resolvedCwd, "AGENTS.md");
|
|
450
|
+
const currentAgents = fs.existsSync(agentsPath)
|
|
451
|
+
? fs.readFileSync(agentsPath, "utf8")
|
|
452
|
+
: "";
|
|
435
453
|
|
|
436
454
|
const written = [
|
|
437
455
|
writeIfAllowed(path.join(resolvedCwd, BUILDCHAIN_CONFIG_PATH), toml, { force }),
|
|
@@ -446,6 +464,12 @@ export function initBuildchainRepo({
|
|
|
446
464
|
}),
|
|
447
465
|
{ force },
|
|
448
466
|
),
|
|
467
|
+
writeIfAllowed(
|
|
468
|
+
path.join(resolvedCwd, PUBLICATION_REHEARSAL_WORKFLOW_PATH),
|
|
469
|
+
publicationRehearsalWorkflow("v3"),
|
|
470
|
+
{ force },
|
|
471
|
+
),
|
|
472
|
+
writeManagedAgentEntry(agentsPath, currentAgents),
|
|
449
473
|
];
|
|
450
474
|
|
|
451
475
|
if (type === "anchored-package" && !fs.existsSync(path.join(resolvedCwd, "release.json"))) {
|
|
@@ -82,6 +82,10 @@ export function inspectArtifactSigningRequests({
|
|
|
82
82
|
if (seen.has(key))
|
|
83
83
|
throw new Error(`duplicate artifact signing request: ${key}`);
|
|
84
84
|
seen.add(key);
|
|
85
|
+
const {
|
|
86
|
+
entitlementsProfile = "none",
|
|
87
|
+
entitlementsPaths = [],
|
|
88
|
+
} = request.signature;
|
|
85
89
|
const item = {
|
|
86
90
|
id: request.artifact.id,
|
|
87
91
|
slug: safeId(request.artifact.id),
|
|
@@ -104,6 +108,8 @@ export function inspectArtifactSigningRequests({
|
|
|
104
108
|
sourceSha: request.source.sha,
|
|
105
109
|
sourceTreeSha: request.source.treeSha,
|
|
106
110
|
transportFormat: request.artifact.transport?.format || "",
|
|
111
|
+
entitlementsProfile,
|
|
112
|
+
entitlementsPaths: entitlementsPaths.join(","),
|
|
107
113
|
};
|
|
108
114
|
if (request.signature.profile === "detached-signature-v1")
|
|
109
115
|
matrices.detached.push(item);
|