@xemahq/xema 0.51.0 → 0.52.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/dist/commands/edition.js +1 -1
- package/dist/commands/edition.js.map +1 -1
- package/dist/edition/verify-image-evidence.bundle.mjs +21 -3
- package/dist/edition/verify-image-evidence.bundle.provenance.json +62 -62
- package/dist/runtime/edition/verb-map.d.ts +1 -0
- package/dist/runtime/edition/verb-map.d.ts.map +1 -1
- package/dist/runtime/edition/verb-map.js +2 -0
- package/dist/runtime/edition/verb-map.js.map +1 -1
- package/edition/README.md +3 -0
- package/edition/assemble-producer-input.mjs +244 -83
- package/edition/assemble-third-party-input.mjs +336 -0
- package/edition/evidence-input-mode.mjs +154 -0
- package/edition/image-evidence.mjs +19 -1
- package/edition/kernel-contract-authority.mjs +10 -2
- package/edition/kernel-package.mjs +13 -0
- package/edition/lib.mjs +51 -37
- package/edition/oci-evidence-verbs.mjs +21 -6
- package/edition/oras.mjs +5 -4
- package/edition/produce-image-source-evidence.mjs +33 -17
- package/edition/registry-credentials.mjs +24 -2
- package/edition/third-party-source-evidence.mjs +8 -1
- package/edition/third-party-source-flags.mjs +1 -1
- package/package.json +2 -2
|
@@ -132,6 +132,13 @@ const SOURCE_REPO_PATTERN = /^(?:repos|submodules)\/[a-zA-Z0-9_.-]+$/;
|
|
|
132
132
|
// The kernel's `CanonicalIdentifierSchema` shape (runId / subject id).
|
|
133
133
|
const CANONICAL_IDENTIFIER_PATTERN =
|
|
134
134
|
/^[a-zA-Z0-9](?:[a-zA-Z0-9._:/@+-]{0,254}[a-zA-Z0-9])?$/;
|
|
135
|
+
/** `--subject-id` has the kernel's `CanonicalIdentifierSchema` shape. */
|
|
136
|
+
export function assertCanonicalSubjectId(subjectId) {
|
|
137
|
+
if (!CANONICAL_IDENTIFIER_PATTERN.test(subjectId ?? '')) {
|
|
138
|
+
throw new Error(`--subject-id ${JSON.stringify(subjectId ?? null)} is not a canonical identifier`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
135
142
|
// cosign v2 attaches every attestation of a subject as one layer of the
|
|
136
143
|
// single `sha256-<subject-digest>.att` manifest; each layer is a DSSE
|
|
137
144
|
// envelope whose `predicateType` annotation names the attestation.
|
|
@@ -848,6 +855,54 @@ export async function resolveImageArtifactEvidence({
|
|
|
848
855
|
if (!SHA256_HEX_PATTERN.test(sourceTreeHash ?? '')) {
|
|
849
856
|
throw new Error('sourceTreeHash must be 64 lowercase sha256 hex characters');
|
|
850
857
|
}
|
|
858
|
+
assertReferenceProvenance(referenceProvenance);
|
|
859
|
+
const { authority, repository } = splitImageRef(imageRepository);
|
|
860
|
+
const declarations = loadRegistryCredentialDeclarations(distributionsRoot);
|
|
861
|
+
const credential = resolveRegistryCredential({ authority, declarations, env });
|
|
862
|
+
const client = registryFor
|
|
863
|
+
? registryFor({ authority, credential })
|
|
864
|
+
: new OrasRegistry({ credential, fetchImpl });
|
|
865
|
+
return publishedArtifactEvidence({
|
|
866
|
+
client,
|
|
867
|
+
repository,
|
|
868
|
+
imageRepository,
|
|
869
|
+
read: { tag: sourceTreeHash },
|
|
870
|
+
expectedDigest: manifestDigest,
|
|
871
|
+
coordinate: `${imageRepository}:${sourceTreeHash}`,
|
|
872
|
+
referenceProvenance,
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* The artifact section of a producer subject read BY DIGEST: the manifest at
|
|
878
|
+
* `<imageRepository>@<manifestDigest>` (its bytes must hash to that digest),
|
|
879
|
+
* its hash-proven platforms and its cosign SBOM + SLSA attestation digests. A
|
|
880
|
+
* third-party publisher's image carries no `:<sourceTreeHash>` tag
|
|
881
|
+
* (`evidence assemble-third-party-input`), so nothing is read by tag.
|
|
882
|
+
*/
|
|
883
|
+
export async function resolveImageArtifactEvidenceByDigest({
|
|
884
|
+
client,
|
|
885
|
+
imageRepository,
|
|
886
|
+
manifestDigest,
|
|
887
|
+
referenceProvenance,
|
|
888
|
+
}) {
|
|
889
|
+
if (!SHA256_DIGEST_PATTERN.test(manifestDigest ?? '')) {
|
|
890
|
+
throw new Error('the image manifest digest must be an exact sha256:<64-hex> digest');
|
|
891
|
+
}
|
|
892
|
+
assertReferenceProvenance(referenceProvenance);
|
|
893
|
+
const { repository } = splitImageRef(imageRepository);
|
|
894
|
+
return publishedArtifactEvidence({
|
|
895
|
+
client,
|
|
896
|
+
repository,
|
|
897
|
+
imageRepository,
|
|
898
|
+
read: { digest: manifestDigest },
|
|
899
|
+
expectedDigest: manifestDigest,
|
|
900
|
+
coordinate: `${imageRepository}@${manifestDigest}`,
|
|
901
|
+
referenceProvenance,
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function assertReferenceProvenance(referenceProvenance) {
|
|
851
906
|
if (
|
|
852
907
|
typeof referenceProvenance !== 'object' ||
|
|
853
908
|
referenceProvenance === null ||
|
|
@@ -858,19 +913,29 @@ export async function resolveImageArtifactEvidence({
|
|
|
858
913
|
'it is what binds the selected provenance attestation to the primary build',
|
|
859
914
|
);
|
|
860
915
|
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/** The artifact evidence of the manifest `read` names (exactly one of `{tag}` or `{digest}`), which must be `expectedDigest`. */
|
|
919
|
+
async function publishedArtifactEvidence({
|
|
920
|
+
client,
|
|
921
|
+
repository,
|
|
922
|
+
imageRepository,
|
|
923
|
+
read,
|
|
924
|
+
expectedDigest,
|
|
925
|
+
coordinate,
|
|
926
|
+
referenceProvenance,
|
|
927
|
+
}) {
|
|
928
|
+
const top =
|
|
929
|
+
read.tag === undefined
|
|
930
|
+
? await client.getManifestByDigest(repository, read.digest)
|
|
931
|
+
: await client.getManifestByTag(repository, read.tag);
|
|
932
|
+
if (top.digest !== expectedDigest) {
|
|
870
933
|
throw new Error(
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
934
|
+
read.tag === undefined
|
|
935
|
+
? `${coordinate} was served as ${top.digest}: the registry returned a manifest that is not the digest it was read by`
|
|
936
|
+
: `${coordinate} serves ${top.digest}, not the caller-declared ${expectedDigest} — ` +
|
|
937
|
+
'the image was not published from this source identity (wrong build commit, ' +
|
|
938
|
+
'or the cross-repo record at the build commit differs from the one this source carries)',
|
|
874
939
|
);
|
|
875
940
|
}
|
|
876
941
|
// Proves EVERY platform by its own hash-verified image config and refuses
|
|
@@ -879,7 +944,7 @@ export async function resolveImageArtifactEvidence({
|
|
|
879
944
|
const published = await resolvePublishedPlatforms({
|
|
880
945
|
client,
|
|
881
946
|
repository,
|
|
882
|
-
|
|
947
|
+
...read,
|
|
883
948
|
coordinate,
|
|
884
949
|
});
|
|
885
950
|
if (published.indexDigest !== top.digest) {
|
|
@@ -944,6 +1009,69 @@ export async function resolveImageArtifactEvidence({
|
|
|
944
1009
|
*/
|
|
945
1010
|
export const DEFAULT_PRODUCER_TTL_SECONDS = 3 * 24 * 60 * 60;
|
|
946
1011
|
|
|
1012
|
+
/** Where the ceiling on a producer lifetime is held: the closed set of producer kinds. */
|
|
1013
|
+
export const ProducerTtlCeilingSource = Object.freeze({
|
|
1014
|
+
/** First party: the data root's trust policy `maxProducerTtlSeconds`, read here. */
|
|
1015
|
+
TrustPolicy: 'trust-policy',
|
|
1016
|
+
/**
|
|
1017
|
+
* A third-party publisher on public inputs: the vendor policy's `maxProducerTtlSeconds`, enforced by
|
|
1018
|
+
* the third-party pin writer (`index runtime-image-source --trusted-write --third-party`) and `verify-evidence`.
|
|
1019
|
+
*/
|
|
1020
|
+
VendorAdmission: 'vendor-admission',
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
/** The latest instant an RFC 3339 `expiresAt` (four-digit year) can name. */
|
|
1024
|
+
const LATEST_EXPIRES_AT = Date.UTC(9999, 11, 31, 23, 59, 59);
|
|
1025
|
+
|
|
1026
|
+
/** `--ttl-seconds` as given, or undefined when it is not. */
|
|
1027
|
+
export const producerTtlSecondsFlag = (value) => (value === undefined ? undefined : Number(value));
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* The producer lifetime of an input issued at `now`. The default is DEFAULT_PRODUCER_TTL_SECONDS, bowed to the
|
|
1031
|
+
* ceiling when it is read here (`ceiling.source` TrustPolicy, `ceiling.maxTtlSeconds`); an explicit value above that
|
|
1032
|
+
* ceiling is refused. With VendorAdmission the ceiling is the vendor policy's, enforced by the third-party pin writer
|
|
1033
|
+
* (`index runtime-image-source --trusted-write --third-party`) and `verify-evidence`; this CLI holds no second one. Either way a value that is not a positive integer, or whose `expiresAt` is past the last
|
|
1034
|
+
* instant RFC 3339 can name, is refused by name.
|
|
1035
|
+
*/
|
|
1036
|
+
export function resolveProducerTtlSeconds({ ttlSeconds, now, ceiling }) {
|
|
1037
|
+
let maxTtlSeconds;
|
|
1038
|
+
if (ceiling?.source === ProducerTtlCeilingSource.TrustPolicy) {
|
|
1039
|
+
({ maxTtlSeconds } = ceiling);
|
|
1040
|
+
if (!Number.isSafeInteger(maxTtlSeconds) || maxTtlSeconds < 1) {
|
|
1041
|
+
throw new Error(`the trust policy's maxProducerTtlSeconds ${String(maxTtlSeconds)} is not a positive integer`);
|
|
1042
|
+
}
|
|
1043
|
+
} else if (ceiling?.source === ProducerTtlCeilingSource.VendorAdmission) {
|
|
1044
|
+
maxTtlSeconds = null;
|
|
1045
|
+
} else {
|
|
1046
|
+
throw new Error(`a producer TTL ceiling source is one of ${Object.values(ProducerTtlCeilingSource).join(', ')}`);
|
|
1047
|
+
}
|
|
1048
|
+
// The DEFAULT bows to the policy ceiling; an EXPLICIT `--ttl-seconds` above
|
|
1049
|
+
// it still fails below. Tightening `maxProducerTtlSeconds` is a legitimate
|
|
1050
|
+
// operator decision, and it must not break every producer until someone
|
|
1051
|
+
// edits a constant in this file — but a caller who NAMES a lifetime the
|
|
1052
|
+
// verifier will reject has made a mistake, and gets told so.
|
|
1053
|
+
const effective =
|
|
1054
|
+
ttlSeconds ?? (maxTtlSeconds === null ? DEFAULT_PRODUCER_TTL_SECONDS : Math.min(DEFAULT_PRODUCER_TTL_SECONDS, maxTtlSeconds));
|
|
1055
|
+
if (maxTtlSeconds !== null && (!Number.isSafeInteger(effective) || effective < 1 || effective > maxTtlSeconds)) {
|
|
1056
|
+
throw new Error(
|
|
1057
|
+
`--ttl-seconds must be an integer between 1 and the trust policy's maxProducerTtlSeconds (${maxTtlSeconds})`,
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
if (!Number.isSafeInteger(effective) || effective < 1) {
|
|
1061
|
+
throw new Error(
|
|
1062
|
+
`--ttl-seconds must be a positive integer number of seconds (got ${String(ttlSeconds)}); the vendor policy's ` +
|
|
1063
|
+
'maxProducerTtlSeconds is its ceiling, enforced by the pin writer and verify-evidence',
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
if (now + effective * 1000 > LATEST_EXPIRES_AT) {
|
|
1067
|
+
throw new Error(
|
|
1068
|
+
`--ttl-seconds ${effective} puts expiresAt past ${new Date(LATEST_EXPIRES_AT).toISOString()}, the last instant an ` +
|
|
1069
|
+
'RFC 3339 time can name',
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
return effective;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
947
1075
|
function readProducerTtlSeconds(distributionsRoot) {
|
|
948
1076
|
const declared = editionWorkspaceConfig().evidenceTrustPolicy.policy;
|
|
949
1077
|
if (declared === null) {
|
|
@@ -1011,9 +1139,7 @@ export async function assembleProducerInput({
|
|
|
1011
1139
|
`--subject-kind "${String(subjectKind)}" must be one of ${subjectKinds.join(', ')}`,
|
|
1012
1140
|
);
|
|
1013
1141
|
}
|
|
1014
|
-
|
|
1015
|
-
throw new Error('--subject-id is not a canonical identifier');
|
|
1016
|
-
}
|
|
1142
|
+
assertCanonicalSubjectId(subjectId);
|
|
1017
1143
|
if (!SOURCE_REPO_PATTERN.test(sourceRepo ?? '')) {
|
|
1018
1144
|
throw new Error(
|
|
1019
1145
|
`--source-repo "${String(sourceRepo)}" must be an aggregator-plane repository (repos/<name> or submodules/<name>)`,
|
|
@@ -1090,24 +1216,42 @@ export async function assembleProducerInput({
|
|
|
1090
1216
|
fetchImpl,
|
|
1091
1217
|
registryFor,
|
|
1092
1218
|
});
|
|
1093
|
-
const
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
effectiveTtlSeconds
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1219
|
+
const effectiveTtlSeconds = resolveProducerTtlSeconds({
|
|
1220
|
+
ttlSeconds,
|
|
1221
|
+
now,
|
|
1222
|
+
ceiling: { source: ProducerTtlCeilingSource.TrustPolicy, maxTtlSeconds: readProducerTtlSeconds(distributionsRoot) },
|
|
1223
|
+
});
|
|
1224
|
+
const input = producerInputDocument({
|
|
1225
|
+
contractAuthority,
|
|
1226
|
+
context,
|
|
1227
|
+
builder,
|
|
1228
|
+
now,
|
|
1229
|
+
ttlSeconds: effectiveTtlSeconds,
|
|
1230
|
+
subject: {
|
|
1231
|
+
kind: subjectKind,
|
|
1232
|
+
id: subjectId,
|
|
1233
|
+
repositoryUrl: source.repositoryUrl,
|
|
1234
|
+
repo: sourceRepo,
|
|
1235
|
+
path: sourcePath,
|
|
1236
|
+
sourceCommit,
|
|
1237
|
+
sourceManifestHash: source.sourceManifestHash,
|
|
1238
|
+
sourceTreeHash: source.sourceTreeHash,
|
|
1239
|
+
imageRepository,
|
|
1240
|
+
...artifact,
|
|
1241
|
+
},
|
|
1242
|
+
});
|
|
1243
|
+
assertAssembledProducerInput({ input, schemas, contractAuthority, context });
|
|
1244
|
+
return input;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
/**
|
|
1248
|
+
* The producer input of ONE subject, in the exact shape
|
|
1249
|
+
* `produce-image-source-evidence.mjs` accepts. `context` is
|
|
1250
|
+
* {@link assertWorkflowContext}'s and `builder` {@link deriveBuilder}'s; the
|
|
1251
|
+
* subject carries every per-subject field (source identity and artifact).
|
|
1252
|
+
*/
|
|
1253
|
+
export function producerInputDocument({ contractAuthority, context, builder, now, ttlSeconds, subject }) {
|
|
1254
|
+
return {
|
|
1111
1255
|
schemaVersion: 1,
|
|
1112
1256
|
contractAuthority: { ...contractAuthority },
|
|
1113
1257
|
sourceHashAlgorithm: imageInputSourceHashAlgorithm(),
|
|
@@ -1131,31 +1275,36 @@ export async function assembleProducerInput({
|
|
|
1131
1275
|
runId: context.runId,
|
|
1132
1276
|
runAttempt: context.runAttempt,
|
|
1133
1277
|
issuedAt: rfc3339(now),
|
|
1134
|
-
expiresAt: rfc3339(now +
|
|
1135
|
-
expectedSubjects: [`${
|
|
1278
|
+
expiresAt: rfc3339(now + ttlSeconds * 1000),
|
|
1279
|
+
expectedSubjects: [`${subject.kind}:${subject.id}`],
|
|
1136
1280
|
subjects: [
|
|
1137
1281
|
{
|
|
1138
|
-
kind:
|
|
1139
|
-
id:
|
|
1140
|
-
repositoryUrl:
|
|
1141
|
-
repo:
|
|
1142
|
-
path:
|
|
1143
|
-
sourceCommit,
|
|
1144
|
-
sourceManifestHash:
|
|
1145
|
-
sourceTreeHash:
|
|
1146
|
-
imageRepository,
|
|
1147
|
-
manifestDigest:
|
|
1148
|
-
manifestMediaType:
|
|
1149
|
-
platforms:
|
|
1150
|
-
sbomDigest:
|
|
1151
|
-
provenanceDigest:
|
|
1282
|
+
kind: subject.kind,
|
|
1283
|
+
id: subject.id,
|
|
1284
|
+
repositoryUrl: subject.repositoryUrl,
|
|
1285
|
+
repo: subject.repo,
|
|
1286
|
+
path: subject.path,
|
|
1287
|
+
sourceCommit: subject.sourceCommit,
|
|
1288
|
+
sourceManifestHash: subject.sourceManifestHash,
|
|
1289
|
+
sourceTreeHash: subject.sourceTreeHash,
|
|
1290
|
+
imageRepository: subject.imageRepository,
|
|
1291
|
+
manifestDigest: subject.manifestDigest,
|
|
1292
|
+
manifestMediaType: subject.manifestMediaType,
|
|
1293
|
+
platforms: subject.platforms,
|
|
1294
|
+
sbomDigest: subject.sbomDigest,
|
|
1295
|
+
provenanceDigest: subject.provenanceDigest,
|
|
1152
1296
|
},
|
|
1153
1297
|
],
|
|
1154
1298
|
};
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
/**
|
|
1302
|
+
* Prove an assembled input satisfies the producer BEFORE anything reads it:
|
|
1303
|
+
* the exact field contract, then the kernel statement schema the producer
|
|
1304
|
+
* builds (the same parse produce-image-source-evidence.mjs runs before
|
|
1305
|
+
* signing), then the GitHub context the producer compares against.
|
|
1306
|
+
*/
|
|
1307
|
+
export function assertAssembledProducerInput({ input, schemas, contractAuthority, context }) {
|
|
1159
1308
|
assertProducerInput(input, contractAuthority);
|
|
1160
1309
|
buildImageSourceStatements(input, schemas, contractAuthority);
|
|
1161
1310
|
assertGitHubProducerContext(input, {
|
|
@@ -1164,23 +1313,13 @@ export async function assembleProducerInput({
|
|
|
1164
1313
|
sha: context.sha,
|
|
1165
1314
|
workflowRef: context.workflowRef,
|
|
1166
1315
|
});
|
|
1167
|
-
return input;
|
|
1168
1316
|
}
|
|
1169
1317
|
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
return value;
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
async function main() {
|
|
1179
|
-
const args = configureEditionWorkspaceFromArgv(process.argv.slice(2), [
|
|
1180
|
-
WorkspaceRoot.Data,
|
|
1181
|
-
WorkspaceRoot.Sources,
|
|
1182
|
-
WorkspaceRoot.Kernel,
|
|
1183
|
-
]);
|
|
1318
|
+
/**
|
|
1319
|
+
* The `--name value` pairs of an assembler's argv: each flag once, each with a
|
|
1320
|
+
* value, none outside `known`.
|
|
1321
|
+
*/
|
|
1322
|
+
export function readAssemblerFlags(args, known) {
|
|
1184
1323
|
const flags = new Map();
|
|
1185
1324
|
for (let index = 0; index < args.length; index += 2) {
|
|
1186
1325
|
const flag = args[index];
|
|
@@ -1197,6 +1336,39 @@ async function main() {
|
|
|
1197
1336
|
}
|
|
1198
1337
|
flags.set(name, value);
|
|
1199
1338
|
}
|
|
1339
|
+
for (const name of flags.keys()) {
|
|
1340
|
+
if (!known.has(name)) {
|
|
1341
|
+
throw new Error(`unknown option "--${name}"`);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
return flags;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
/**
|
|
1348
|
+
* Write an assembled input to `output`. The output directory is caller scratch
|
|
1349
|
+
* space — created deterministically (the producer action's input-manifest
|
|
1350
|
+
* must be repository-relative, so the caller cannot sidestep this by pointing
|
|
1351
|
+
* at RUNNER_TEMP). `wx` refuses to overwrite an existing manifest.
|
|
1352
|
+
*/
|
|
1353
|
+
export function writeProducerInputFile(output, input) {
|
|
1354
|
+
mkdirSync(dirname(output), { recursive: true });
|
|
1355
|
+
writeFileSync(output, serializeJson(input), { flag: 'wx', mode: 0o600 });
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
function requireValue(flags, name) {
|
|
1359
|
+
const value = flags.get(name);
|
|
1360
|
+
if (value === undefined) {
|
|
1361
|
+
throw new Error(`--${name} is required`);
|
|
1362
|
+
}
|
|
1363
|
+
return value;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
async function main() {
|
|
1367
|
+
const args = configureEditionWorkspaceFromArgv(process.argv.slice(2), [
|
|
1368
|
+
WorkspaceRoot.Data,
|
|
1369
|
+
WorkspaceRoot.Sources,
|
|
1370
|
+
WorkspaceRoot.Kernel,
|
|
1371
|
+
]);
|
|
1200
1372
|
const known = new Set([
|
|
1201
1373
|
'output',
|
|
1202
1374
|
'image-repository',
|
|
@@ -1211,11 +1383,7 @@ async function main() {
|
|
|
1211
1383
|
'provenance',
|
|
1212
1384
|
'ttl-seconds',
|
|
1213
1385
|
]);
|
|
1214
|
-
|
|
1215
|
-
if (!known.has(name)) {
|
|
1216
|
-
throw new Error(`unknown option "--${name}"`);
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1386
|
+
const flags = readAssemblerFlags(args, known);
|
|
1219
1387
|
const output = resolve(requireValue(flags, 'output'));
|
|
1220
1388
|
const input = await assembleProducerInput({
|
|
1221
1389
|
imageRepository: requireValue(flags, 'image-repository'),
|
|
@@ -1228,16 +1396,9 @@ async function main() {
|
|
|
1228
1396
|
provenancePath: resolve(requireValue(flags, 'provenance')),
|
|
1229
1397
|
sourceManifestHash: flags.get('source-manifest-hash'),
|
|
1230
1398
|
sourceTreeHash: flags.get('source-tree-hash'),
|
|
1231
|
-
ttlSeconds: flags.
|
|
1232
|
-
? Number(flags.get('ttl-seconds'))
|
|
1233
|
-
: undefined,
|
|
1399
|
+
ttlSeconds: producerTtlSecondsFlag(flags.get('ttl-seconds')),
|
|
1234
1400
|
});
|
|
1235
|
-
|
|
1236
|
-
// (the producer action's input-manifest must be repository-relative, so the
|
|
1237
|
-
// caller cannot sidestep this by pointing at RUNNER_TEMP). `wx` below still
|
|
1238
|
-
// refuses to overwrite an existing manifest.
|
|
1239
|
-
mkdirSync(dirname(output), { recursive: true });
|
|
1240
|
-
writeFileSync(output, serializeJson(input), { flag: 'wx', mode: 0o600 });
|
|
1401
|
+
writeProducerInputFile(output, input);
|
|
1241
1402
|
console.log(
|
|
1242
1403
|
`[assemble-producer-input] wrote ${output} for subject ` +
|
|
1243
1404
|
`${input.subjects[0].kind}:${input.subjects[0].id} ` +
|