@runveric/cli 0.1.2 → 0.3.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/README.md +45 -3
- package/dist/runveric.mjs +2557 -154
- package/dist/runveric.mjs.map +1 -1
- package/package.json +2 -2
package/dist/runveric.mjs
CHANGED
|
@@ -212,13 +212,19 @@ var require_wire_identity = __commonJS({
|
|
|
212
212
|
Object.defineProperty(exports, "__esModule", {
|
|
213
213
|
value: true
|
|
214
214
|
});
|
|
215
|
-
exports.PROBLEM_TYPE_BASE = exports.RUNNER_PROTOCOL_VERSION = exports.PACKAGE_WORK_ITEM_VERSION = exports.ACTION_MANIFEST_API_VERSION = exports.PACKAGE_EXECUTION_PROTOCOL_VERSION = exports.PACKAGE_RESULT_PROTOCOL_VERSION = void 0;
|
|
215
|
+
exports.PROBLEM_TYPE_BASE = exports.CATALOG_ARTIFACT_MEDIA_TYPE = exports.CATALOG_ACTION_ARTIFACT_SCHEMA_V2 = exports.CATALOG_ACTION_ARTIFACT_SCHEMA_V1 = exports.CATALOG_RELEASE_SCHEMA_V1 = exports.EVIDENCE_PACK_SCHEMA_VERSION = exports.ACTION_PROTOCOL_V1 = exports.RUNNER_PROTOCOL_VERSION = exports.PACKAGE_WORK_ITEM_VERSION = exports.ACTION_MANIFEST_API_VERSION = exports.PACKAGE_EXECUTION_PROTOCOL_VERSION = exports.PACKAGE_RESULT_PROTOCOL_VERSION = void 0;
|
|
216
216
|
exports.problemType = problemType;
|
|
217
217
|
exports.PACKAGE_RESULT_PROTOCOL_VERSION = "runveric.dev/package-result/v1";
|
|
218
218
|
exports.PACKAGE_EXECUTION_PROTOCOL_VERSION = "runveric.dev/package-execution/v1";
|
|
219
219
|
exports.ACTION_MANIFEST_API_VERSION = "runveric.dev/action/v1";
|
|
220
220
|
exports.PACKAGE_WORK_ITEM_VERSION = "runveric.dev/package-work-item/v1";
|
|
221
221
|
exports.RUNNER_PROTOCOL_VERSION = "runveric.dev/runner-channel/v2";
|
|
222
|
+
exports.ACTION_PROTOCOL_V1 = "runveric.dev/action-protocol/v1";
|
|
223
|
+
exports.EVIDENCE_PACK_SCHEMA_VERSION = "runveric.dev/evidence-pack/v1";
|
|
224
|
+
exports.CATALOG_RELEASE_SCHEMA_V1 = "runveric.dev/catalog-release/v1";
|
|
225
|
+
exports.CATALOG_ACTION_ARTIFACT_SCHEMA_V1 = "runveric.dev/catalog-action/v1";
|
|
226
|
+
exports.CATALOG_ACTION_ARTIFACT_SCHEMA_V2 = "runveric.dev/catalog-action/v2";
|
|
227
|
+
exports.CATALOG_ARTIFACT_MEDIA_TYPE = "application/vnd.runveric.catalog-artifact+json";
|
|
222
228
|
exports.PROBLEM_TYPE_BASE = "https://runveric.dev/errors";
|
|
223
229
|
function problemType(slug) {
|
|
224
230
|
return `${exports.PROBLEM_TYPE_BASE}/${slug}`;
|
|
@@ -301,6 +307,13 @@ var require_permissions = __commonJS({
|
|
|
301
307
|
Object.defineProperty(exports, "__esModule", {
|
|
302
308
|
value: true
|
|
303
309
|
});
|
|
310
|
+
exports.PLATFORM_PERMISSIONS = void 0;
|
|
311
|
+
exports.PLATFORM_PERMISSIONS = [
|
|
312
|
+
// Which catalog release is live for every workspace (docs/121). Pinning one changes what EVERY
|
|
313
|
+
// workspace can run, so it is neither `policy:manage` (one workspace's own governance) nor
|
|
314
|
+
// `catalog:read` (the right to browse).
|
|
315
|
+
"catalog:release"
|
|
316
|
+
];
|
|
304
317
|
}
|
|
305
318
|
});
|
|
306
319
|
|
|
@@ -443,6 +456,41 @@ var require_groups = __commonJS({
|
|
|
443
456
|
}
|
|
444
457
|
});
|
|
445
458
|
|
|
459
|
+
// ../contracts/dist/catalog-policy.js
|
|
460
|
+
var require_catalog_policy = __commonJS({
|
|
461
|
+
"../contracts/dist/catalog-policy.js"(exports) {
|
|
462
|
+
"use strict";
|
|
463
|
+
Object.defineProperty(exports, "__esModule", {
|
|
464
|
+
value: true
|
|
465
|
+
});
|
|
466
|
+
exports.CATALOG_RULE_EFFECTS = exports.CATALOG_DEFAULT_MODE_WHEN_UNSET = exports.CATALOG_DEFAULT_MODES = void 0;
|
|
467
|
+
exports.effectiveCatalogAccess = effectiveCatalogAccess;
|
|
468
|
+
var CATALOG_DEFAULT_MODE_SET = {
|
|
469
|
+
allow_all: true,
|
|
470
|
+
deny_all: true
|
|
471
|
+
};
|
|
472
|
+
exports.CATALOG_DEFAULT_MODES = Object.keys(CATALOG_DEFAULT_MODE_SET);
|
|
473
|
+
exports.CATALOG_DEFAULT_MODE_WHEN_UNSET = "allow_all";
|
|
474
|
+
var CATALOG_RULE_EFFECT_SET = {
|
|
475
|
+
allow: true,
|
|
476
|
+
deny: true
|
|
477
|
+
};
|
|
478
|
+
exports.CATALOG_RULE_EFFECTS = Object.keys(CATALOG_RULE_EFFECT_SET);
|
|
479
|
+
var RULE_ACCESS = {
|
|
480
|
+
allow: "allowed",
|
|
481
|
+
deny: "blocked"
|
|
482
|
+
};
|
|
483
|
+
var UNRULED_ACCESS = {
|
|
484
|
+
allow_all: "allowed",
|
|
485
|
+
deny_all: "blocked"
|
|
486
|
+
};
|
|
487
|
+
function effectiveCatalogAccess(defaultMode, rule) {
|
|
488
|
+
return rule !== null ? RULE_ACCESS[rule] : UNRULED_ACCESS[defaultMode];
|
|
489
|
+
}
|
|
490
|
+
__name(effectiveCatalogAccess, "effectiveCatalogAccess");
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
|
|
446
494
|
// ../contracts/dist/catalog.js
|
|
447
495
|
var require_catalog = __commonJS({
|
|
448
496
|
"../contracts/dist/catalog.js"(exports) {
|
|
@@ -558,6 +606,456 @@ var require_catalog = __commonJS({
|
|
|
558
606
|
}
|
|
559
607
|
});
|
|
560
608
|
|
|
609
|
+
// ../contracts/dist/catalog-release.js
|
|
610
|
+
var require_catalog_release = __commonJS({
|
|
611
|
+
"../contracts/dist/catalog-release.js"(exports) {
|
|
612
|
+
"use strict";
|
|
613
|
+
Object.defineProperty(exports, "__esModule", {
|
|
614
|
+
value: true
|
|
615
|
+
});
|
|
616
|
+
exports.CatalogReleaseStateError = exports.CATALOG_SYNC_STALE_AFTER_MINUTES = exports.CATALOG_SYNC_INTERVAL_MINUTES = exports.CATALOG_SYNC_SOURCES = exports.CATALOG_SYNC_RUN_OUTCOMES = exports.ARTIFACT_DIGEST_PATTERN = exports.ARTIFACT_REPOSITORY_PATTERN = exports.CATALOG_ACTION_ARTIFACT_SCHEMA_VERSIONS = exports.CATALOG_RELEASE_SCHEMA_VERSIONS = void 0;
|
|
617
|
+
exports.parseCatalogReleaseState = parseCatalogReleaseState;
|
|
618
|
+
var wire_identity_1 = require_wire_identity();
|
|
619
|
+
exports.CATALOG_RELEASE_SCHEMA_VERSIONS = [
|
|
620
|
+
wire_identity_1.CATALOG_RELEASE_SCHEMA_V1
|
|
621
|
+
];
|
|
622
|
+
exports.CATALOG_ACTION_ARTIFACT_SCHEMA_VERSIONS = [
|
|
623
|
+
wire_identity_1.CATALOG_ACTION_ARTIFACT_SCHEMA_V1,
|
|
624
|
+
wire_identity_1.CATALOG_ACTION_ARTIFACT_SCHEMA_V2
|
|
625
|
+
];
|
|
626
|
+
exports.ARTIFACT_REPOSITORY_PATTERN = /^[a-z0-9][a-z0-9.-]*(:[0-9]+)?(\/[a-z0-9][a-z0-9._-]*)+$/;
|
|
627
|
+
exports.ARTIFACT_DIGEST_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
628
|
+
exports.CATALOG_SYNC_RUN_OUTCOMES = [
|
|
629
|
+
"applied",
|
|
630
|
+
"refused",
|
|
631
|
+
"superseded"
|
|
632
|
+
];
|
|
633
|
+
exports.CATALOG_SYNC_SOURCES = [
|
|
634
|
+
"directory",
|
|
635
|
+
"release"
|
|
636
|
+
];
|
|
637
|
+
exports.CATALOG_SYNC_INTERVAL_MINUTES = 15;
|
|
638
|
+
exports.CATALOG_SYNC_STALE_AFTER_MINUTES = exports.CATALOG_SYNC_INTERVAL_MINUTES * 2;
|
|
639
|
+
var CatalogReleaseStateError = class CatalogReleaseStateError extends Error {
|
|
640
|
+
static {
|
|
641
|
+
__name(this, "CatalogReleaseStateError");
|
|
642
|
+
}
|
|
643
|
+
constructor(message) {
|
|
644
|
+
super(message);
|
|
645
|
+
this.name = "CatalogReleaseStateError";
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
exports.CatalogReleaseStateError = CatalogReleaseStateError;
|
|
649
|
+
function stateObject(path, value) {
|
|
650
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
651
|
+
throw new CatalogReleaseStateError(`${path} is not an object.`);
|
|
652
|
+
}
|
|
653
|
+
return value;
|
|
654
|
+
}
|
|
655
|
+
__name(stateObject, "stateObject");
|
|
656
|
+
function stateString(path, value, pattern, expected = "a non-empty string") {
|
|
657
|
+
if (typeof value !== "string" || value === "" || pattern !== void 0 && !pattern.test(value)) {
|
|
658
|
+
throw new CatalogReleaseStateError(`${path} is ${JSON.stringify(value)}, not ${expected}.`);
|
|
659
|
+
}
|
|
660
|
+
return value;
|
|
661
|
+
}
|
|
662
|
+
__name(stateString, "stateString");
|
|
663
|
+
function stateInstant(path, value) {
|
|
664
|
+
const text = stateString(path, value);
|
|
665
|
+
if (Number.isNaN(Date.parse(text))) {
|
|
666
|
+
throw new CatalogReleaseStateError(`${path} is "${text}", which is not a timestamp.`);
|
|
667
|
+
}
|
|
668
|
+
return text;
|
|
669
|
+
}
|
|
670
|
+
__name(stateInstant, "stateInstant");
|
|
671
|
+
var orNull = /* @__PURE__ */ __name((value, read) => value === null ? null : read(value), "orNull");
|
|
672
|
+
function stateMember(path, value, members) {
|
|
673
|
+
if (typeof value === "string" && members.includes(value)) return value;
|
|
674
|
+
throw new CatalogReleaseStateError(`${path} is ${JSON.stringify(value)}; this build reads ${members.join(", ")}.`);
|
|
675
|
+
}
|
|
676
|
+
__name(stateMember, "stateMember");
|
|
677
|
+
function parseSyncRun(value) {
|
|
678
|
+
const run3 = stateObject("lastSync", value);
|
|
679
|
+
const source = stateMember("lastSync.source", run3.source, exports.CATALOG_SYNC_SOURCES);
|
|
680
|
+
const release = orNull(run3.release, (raw) => {
|
|
681
|
+
const fields = stateObject("lastSync.release", raw);
|
|
682
|
+
return {
|
|
683
|
+
artifactRef: stateString("lastSync.release.artifactRef", fields.artifactRef, exports.ARTIFACT_REPOSITORY_PATTERN, "a registry repository"),
|
|
684
|
+
digest: stateString("lastSync.release.digest", fields.digest, exports.ARTIFACT_DIGEST_PATTERN, "a sha256 digest"),
|
|
685
|
+
version: orNull(fields.version, (label) => stateString("lastSync.release.version", label))
|
|
686
|
+
};
|
|
687
|
+
});
|
|
688
|
+
if (source === "release" !== (release !== null)) {
|
|
689
|
+
throw new CatalogReleaseStateError(`lastSync.source is "${source}" and lastSync.release is ${release === null ? "null" : "set"}; a run names a release exactly when a release declared it.`);
|
|
690
|
+
}
|
|
691
|
+
return {
|
|
692
|
+
startedAt: stateInstant("lastSync.startedAt", run3.startedAt),
|
|
693
|
+
finishedAt: stateInstant("lastSync.finishedAt", run3.finishedAt),
|
|
694
|
+
imageTag: orNull(run3.imageTag, (tag) => stateString("lastSync.imageTag", tag)),
|
|
695
|
+
source,
|
|
696
|
+
release,
|
|
697
|
+
outcome: stateMember("lastSync.outcome", run3.outcome, exports.CATALOG_SYNC_RUN_OUTCOMES),
|
|
698
|
+
detail: stateString("lastSync.detail", run3.detail)
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
__name(parseSyncRun, "parseSyncRun");
|
|
702
|
+
function parsePinned(value) {
|
|
703
|
+
const pin = stateObject("release", value);
|
|
704
|
+
return {
|
|
705
|
+
artifactRef: stateString("release.artifactRef", pin.artifactRef, exports.ARTIFACT_REPOSITORY_PATTERN, "a registry repository"),
|
|
706
|
+
digest: stateString("release.digest", pin.digest, exports.ARTIFACT_DIGEST_PATTERN, "a sha256 digest"),
|
|
707
|
+
version: stateString("release.version", pin.version),
|
|
708
|
+
provenance: orNull(pin.provenance, (raw) => {
|
|
709
|
+
const fields = stateObject("release.provenance", raw);
|
|
710
|
+
return {
|
|
711
|
+
repository: stateString("release.provenance.repository", fields.repository),
|
|
712
|
+
commit: stateString("release.provenance.commit", fields.commit)
|
|
713
|
+
};
|
|
714
|
+
}),
|
|
715
|
+
pinnedBy: stateString("release.pinnedBy", pin.pinnedBy),
|
|
716
|
+
pinnedByName: orNull(pin.pinnedByName, (name) => stateString("release.pinnedByName", name)),
|
|
717
|
+
pinnedAt: stateInstant("release.pinnedAt", pin.pinnedAt)
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
__name(parsePinned, "parsePinned");
|
|
721
|
+
function parseCatalogReleaseState(value) {
|
|
722
|
+
const state = stateObject("the catalog release state", value);
|
|
723
|
+
for (const field of [
|
|
724
|
+
"release",
|
|
725
|
+
"imageTag",
|
|
726
|
+
"lastSync",
|
|
727
|
+
"liveSince"
|
|
728
|
+
]) {
|
|
729
|
+
if (!(field in state)) throw new CatalogReleaseStateError(`${field} is missing.`);
|
|
730
|
+
}
|
|
731
|
+
const release = orNull(state.release, parsePinned);
|
|
732
|
+
const liveSince = orNull(state.liveSince, (instant) => stateInstant("liveSince", instant));
|
|
733
|
+
if (release === null && liveSince !== null) {
|
|
734
|
+
throw new CatalogReleaseStateError("liveSince is set with nothing pinned; only a pin goes live.");
|
|
735
|
+
}
|
|
736
|
+
return {
|
|
737
|
+
release,
|
|
738
|
+
imageTag: orNull(state.imageTag, (tag) => stateString("imageTag", tag)),
|
|
739
|
+
lastSync: orNull(state.lastSync, parseSyncRun),
|
|
740
|
+
liveSince
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
__name(parseCatalogReleaseState, "parseCatalogReleaseState");
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
// ../contracts/dist/action-repository.js
|
|
748
|
+
var require_action_repository = __commonJS({
|
|
749
|
+
"../contracts/dist/action-repository.js"(exports) {
|
|
750
|
+
"use strict";
|
|
751
|
+
Object.defineProperty(exports, "__esModule", {
|
|
752
|
+
value: true
|
|
753
|
+
});
|
|
754
|
+
exports.ACTION_SOURCE_REPOSITORY_PREFIX = exports.ACTION_REPOSITORY_PREFIX = void 0;
|
|
755
|
+
exports.actionKeyRefusal = actionKeyRefusal;
|
|
756
|
+
exports.isActionKey = isActionKey;
|
|
757
|
+
exports.actionRepositoryName = actionRepositoryName;
|
|
758
|
+
exports.actionSourceRepository = actionSourceRepository2;
|
|
759
|
+
exports.actionKeyOf = actionKeyOf;
|
|
760
|
+
exports.actionPackageRefusal = actionPackageRefusal4;
|
|
761
|
+
exports.ACTION_REPOSITORY_PREFIX = "action-";
|
|
762
|
+
var WORD = /^[a-z][a-z0-9]*$/;
|
|
763
|
+
var VERB = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]+)*$/;
|
|
764
|
+
function actionKeyRefusal(key) {
|
|
765
|
+
const shape = "an action key is <provider>.<resource>.<verb>: provider and resource are one lowercase word (a-z, then a-z or 0-9), the verb is camelCase with no two capitals in a row \u2014 so the key maps to exactly one repository name and back.";
|
|
766
|
+
const segments = key.split(".");
|
|
767
|
+
if (segments.length !== 3) {
|
|
768
|
+
return `"${key}" is not an action key: it has ${segments.length} dot-separated segment(s), not 3; ${shape}`;
|
|
769
|
+
}
|
|
770
|
+
const [provider, resource, verb] = segments;
|
|
771
|
+
const not = /* @__PURE__ */ __name((what) => `"${key}" is not an action key: ${what}; ${shape}`, "not");
|
|
772
|
+
if (!WORD.test(provider)) return not(`its provider "${provider}" is not one lowercase word`);
|
|
773
|
+
if (!WORD.test(resource)) return not(`its resource "${resource}" is not one lowercase word`);
|
|
774
|
+
if (!VERB.test(verb)) return not(`its verb "${verb}" is not camelCase without two capitals in a row`);
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
__name(actionKeyRefusal, "actionKeyRefusal");
|
|
778
|
+
function isActionKey(key) {
|
|
779
|
+
return actionKeyRefusal(key) === null;
|
|
780
|
+
}
|
|
781
|
+
__name(isActionKey, "isActionKey");
|
|
782
|
+
var kebab = /* @__PURE__ */ __name((verb) => verb.replace(/[A-Z]/g, (capital) => `-${capital.toLowerCase()}`), "kebab");
|
|
783
|
+
function actionRepositoryName(key) {
|
|
784
|
+
const refusal = actionKeyRefusal(key);
|
|
785
|
+
if (refusal !== null) throw new Error(refusal);
|
|
786
|
+
const [provider, resource, verb] = key.split(".");
|
|
787
|
+
return `${exports.ACTION_REPOSITORY_PREFIX}${provider}-${resource}-${kebab(verb)}`;
|
|
788
|
+
}
|
|
789
|
+
__name(actionRepositoryName, "actionRepositoryName");
|
|
790
|
+
exports.ACTION_SOURCE_REPOSITORY_PREFIX = "https://github.com/runveric/";
|
|
791
|
+
function actionSourceRepository2(key) {
|
|
792
|
+
return `${exports.ACTION_SOURCE_REPOSITORY_PREFIX}${actionRepositoryName(key)}`;
|
|
793
|
+
}
|
|
794
|
+
__name(actionSourceRepository2, "actionSourceRepository");
|
|
795
|
+
function actionKeyOf(name) {
|
|
796
|
+
if (!name.startsWith(exports.ACTION_REPOSITORY_PREFIX)) return null;
|
|
797
|
+
const [provider, resource, first2, ...rest] = name.slice(exports.ACTION_REPOSITORY_PREFIX.length).split("-");
|
|
798
|
+
if (provider === void 0 || resource === void 0 || first2 === void 0) return null;
|
|
799
|
+
const verb = first2 + rest.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
800
|
+
const key = `${provider}.${resource}.${verb}`;
|
|
801
|
+
return isActionKey(key) && actionRepositoryName(key) === name ? key : null;
|
|
802
|
+
}
|
|
803
|
+
__name(actionKeyOf, "actionKeyOf");
|
|
804
|
+
function actionPackageRefusal4(key, artifactRef) {
|
|
805
|
+
const keyRefusal = actionKeyRefusal(key);
|
|
806
|
+
if (keyRefusal !== null) return `${keyRefusal} No package can carry it.`;
|
|
807
|
+
const cut = artifactRef.lastIndexOf("/");
|
|
808
|
+
const named = artifactRef.slice(cut + 1);
|
|
809
|
+
const expected = actionRepositoryName(key);
|
|
810
|
+
if (named === expected) return null;
|
|
811
|
+
return `action "${key}" is published only from a package named "${expected}" (${artifactRef.slice(0, cut + 1)}${expected}), not "${named}". An action's package carries its key's name, so an artifact cannot claim an identity its package does not carry.`;
|
|
812
|
+
}
|
|
813
|
+
__name(actionPackageRefusal4, "actionPackageRefusal");
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
// ../contracts/dist/catalog-release-spec.js
|
|
818
|
+
var require_catalog_release_spec = __commonJS({
|
|
819
|
+
"../contracts/dist/catalog-release-spec.js"(exports) {
|
|
820
|
+
"use strict";
|
|
821
|
+
Object.defineProperty(exports, "__esModule", {
|
|
822
|
+
value: true
|
|
823
|
+
});
|
|
824
|
+
exports.CatalogReleaseSpecError = void 0;
|
|
825
|
+
exports.splitArtifactReference = splitArtifactReference;
|
|
826
|
+
exports.parseCatalogReleaseSpec = parseCatalogReleaseSpec3;
|
|
827
|
+
var CatalogReleaseSpecError = class CatalogReleaseSpecError extends Error {
|
|
828
|
+
static {
|
|
829
|
+
__name(this, "CatalogReleaseSpecError");
|
|
830
|
+
}
|
|
831
|
+
constructor(message) {
|
|
832
|
+
super(message);
|
|
833
|
+
this.name = "CatalogReleaseSpecError";
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
exports.CatalogReleaseSpecError = CatalogReleaseSpecError;
|
|
837
|
+
function splitArtifactReference(reference) {
|
|
838
|
+
const at = reference.indexOf("@");
|
|
839
|
+
if (at <= 0 || at === reference.length - 1) return null;
|
|
840
|
+
return {
|
|
841
|
+
artifactRef: reference.slice(0, at),
|
|
842
|
+
digest: reference.slice(at + 1)
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
__name(splitArtifactReference, "splitArtifactReference");
|
|
846
|
+
var isMapping3 = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isMapping");
|
|
847
|
+
function parseCatalogReleaseSpec3(value) {
|
|
848
|
+
if (!isMapping3(value)) {
|
|
849
|
+
throw new CatalogReleaseSpecError("release.yaml is not a mapping of `version` and `actions`.");
|
|
850
|
+
}
|
|
851
|
+
const unknown = Object.keys(value).filter((key) => key !== "version" && key !== "actions");
|
|
852
|
+
if (unknown.length > 0) {
|
|
853
|
+
throw new CatalogReleaseSpecError(`release.yaml carries ${unknown.map((key) => `"${key}"`).join(", ")}; it has exactly two fields, \`version\` and \`actions\`.`);
|
|
854
|
+
}
|
|
855
|
+
const { version: version2, actions } = value;
|
|
856
|
+
if (typeof version2 !== "string" || version2.trim().length === 0) {
|
|
857
|
+
throw new CatalogReleaseSpecError(`release.yaml's version is ${JSON.stringify(version2 ?? null)}, not a label. Write it as a quoted string \u2014 version: "2026.10.1" \u2014 because YAML reads an unquoted 2026.10 as a number.`);
|
|
858
|
+
}
|
|
859
|
+
if (actions === null || actions === void 0 || isMapping3(actions) && Object.keys(actions).length === 0) {
|
|
860
|
+
throw new CatalogReleaseSpecError("release.yaml declares no action. An empty release is refused: pinned, it would declare a catalog with nothing in it.");
|
|
861
|
+
}
|
|
862
|
+
if (!isMapping3(actions)) {
|
|
863
|
+
throw new CatalogReleaseSpecError("release.yaml's actions is not a mapping of action key to <repository>@<digest>.");
|
|
864
|
+
}
|
|
865
|
+
return {
|
|
866
|
+
version: version2,
|
|
867
|
+
actions: Object.entries(actions).map(([key, reference]) => {
|
|
868
|
+
const split = typeof reference === "string" ? splitArtifactReference(reference) : null;
|
|
869
|
+
if (split === null) {
|
|
870
|
+
throw new CatalogReleaseSpecError(`release.yaml's ${key} is ${JSON.stringify(reference)}, not <repository>@<digest>.`);
|
|
871
|
+
}
|
|
872
|
+
return {
|
|
873
|
+
key,
|
|
874
|
+
...split
|
|
875
|
+
};
|
|
876
|
+
})
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
__name(parseCatalogReleaseSpec3, "parseCatalogReleaseSpec");
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
// ../contracts/dist/catalog-lock.js
|
|
884
|
+
var require_catalog_lock = __commonJS({
|
|
885
|
+
"../contracts/dist/catalog-lock.js"(exports) {
|
|
886
|
+
"use strict";
|
|
887
|
+
Object.defineProperty(exports, "__esModule", {
|
|
888
|
+
value: true
|
|
889
|
+
});
|
|
890
|
+
exports.MANIFEST_SHA256_PATTERN = exports.CatalogLockError = exports.CATALOG_LOCK_ENGINE_EXECUTION = exports.CATALOG_LOCK_FILE = exports.CATALOG_LOCK_SCHEMA_V1 = void 0;
|
|
891
|
+
exports.parseCatalogLock = parseCatalogLock;
|
|
892
|
+
exports.catalogLockText = catalogLockText;
|
|
893
|
+
exports.authoredElsewhere = authoredElsewhere;
|
|
894
|
+
var action_repository_1 = require_action_repository();
|
|
895
|
+
var catalog_release_1 = require_catalog_release();
|
|
896
|
+
exports.CATALOG_LOCK_SCHEMA_V1 = "runveric.catalog-lock/v1";
|
|
897
|
+
exports.CATALOG_LOCK_FILE = "release.lock.json";
|
|
898
|
+
exports.CATALOG_LOCK_ENGINE_EXECUTION = "engine";
|
|
899
|
+
var CatalogLockError = class CatalogLockError extends Error {
|
|
900
|
+
static {
|
|
901
|
+
__name(this, "CatalogLockError");
|
|
902
|
+
}
|
|
903
|
+
constructor(message) {
|
|
904
|
+
super(message);
|
|
905
|
+
this.name = "CatalogLockError";
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
exports.CatalogLockError = CatalogLockError;
|
|
909
|
+
exports.MANIFEST_SHA256_PATTERN = /^[0-9a-f]{64}$/;
|
|
910
|
+
function objectAt(path, value, keys) {
|
|
911
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
912
|
+
throw new CatalogLockError(`${path} is not an object.`);
|
|
913
|
+
}
|
|
914
|
+
const unknown = Object.keys(value).filter((key) => !keys.includes(key));
|
|
915
|
+
if (unknown.length > 0) {
|
|
916
|
+
throw new CatalogLockError(`${path} carries ${unknown.map((key) => `"${key}"`).join(", ")}, which a lock does not have. A lock is written by catalog:vendor and never edited; re-vendor instead.`);
|
|
917
|
+
}
|
|
918
|
+
return value;
|
|
919
|
+
}
|
|
920
|
+
__name(objectAt, "objectAt");
|
|
921
|
+
function stringAt(path, value, pattern, expected) {
|
|
922
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
923
|
+
throw new CatalogLockError(`${path} is not a non-empty string.`);
|
|
924
|
+
}
|
|
925
|
+
if (pattern !== void 0 && !pattern.test(value)) {
|
|
926
|
+
throw new CatalogLockError(`${path} is "${value}", which is not ${expected}.`);
|
|
927
|
+
}
|
|
928
|
+
return value;
|
|
929
|
+
}
|
|
930
|
+
__name(stringAt, "stringAt");
|
|
931
|
+
function parseCatalogLock(value) {
|
|
932
|
+
const root = objectAt("the lock", value, [
|
|
933
|
+
"schemaVersion",
|
|
934
|
+
"release",
|
|
935
|
+
"actions"
|
|
936
|
+
]);
|
|
937
|
+
if (root.schemaVersion !== exports.CATALOG_LOCK_SCHEMA_V1) {
|
|
938
|
+
throw new CatalogLockError(`the lock declares schemaVersion ${JSON.stringify(root.schemaVersion)}; this build reads ${exports.CATALOG_LOCK_SCHEMA_V1} only.`);
|
|
939
|
+
}
|
|
940
|
+
const release = objectAt("release", root.release, [
|
|
941
|
+
"artifactRef",
|
|
942
|
+
"digest",
|
|
943
|
+
"version"
|
|
944
|
+
]);
|
|
945
|
+
const lock = {
|
|
946
|
+
schemaVersion: exports.CATALOG_LOCK_SCHEMA_V1,
|
|
947
|
+
release: {
|
|
948
|
+
artifactRef: stringAt("release.artifactRef", release.artifactRef, catalog_release_1.ARTIFACT_REPOSITORY_PATTERN, "a registry repository"),
|
|
949
|
+
digest: stringAt("release.digest", release.digest, catalog_release_1.ARTIFACT_DIGEST_PATTERN, "a sha256 digest"),
|
|
950
|
+
version: stringAt("release.version", release.version)
|
|
951
|
+
},
|
|
952
|
+
actions: []
|
|
953
|
+
};
|
|
954
|
+
if (!Array.isArray(root.actions) || root.actions.length === 0) {
|
|
955
|
+
throw new CatalogLockError("actions is not a non-empty array. A release names at least one action.");
|
|
956
|
+
}
|
|
957
|
+
root.actions.forEach((entry, index) => {
|
|
958
|
+
const path = `actions[${index}]`;
|
|
959
|
+
const action = objectAt(path, entry, [
|
|
960
|
+
"key",
|
|
961
|
+
"artifactRef",
|
|
962
|
+
"digest",
|
|
963
|
+
"manifestSha256",
|
|
964
|
+
"provenance",
|
|
965
|
+
"executes",
|
|
966
|
+
"imageDigest"
|
|
967
|
+
]);
|
|
968
|
+
const key = stringAt(`${path}.key`, action.key);
|
|
969
|
+
const artifactRef = stringAt(`${path}.artifactRef`, action.artifactRef, catalog_release_1.ARTIFACT_REPOSITORY_PATTERN, "a registry repository");
|
|
970
|
+
const misnamed = (0, action_repository_1.actionPackageRefusal)(key, artifactRef);
|
|
971
|
+
if (misnamed !== null) throw new CatalogLockError(`${path}: ${misnamed}`);
|
|
972
|
+
const previous = lock.actions[lock.actions.length - 1];
|
|
973
|
+
if (previous !== void 0 && !(previous.key < key)) {
|
|
974
|
+
throw new CatalogLockError(`${path}.key "${key}" follows "${previous.key}": a lock names each action once, sorted by key.`);
|
|
975
|
+
}
|
|
976
|
+
const provenance2 = objectAt(`${path}.provenance`, action.provenance, [
|
|
977
|
+
"repository",
|
|
978
|
+
"commit"
|
|
979
|
+
]);
|
|
980
|
+
if (action.executes === void 0 !== (action.imageDigest === void 0)) {
|
|
981
|
+
throw new CatalogLockError(`${path} carries ${action.executes === void 0 ? "imageDigest without executes" : "executes without imageDigest"}; an engine action records both, a built-in neither.`);
|
|
982
|
+
}
|
|
983
|
+
if (action.executes !== void 0 && action.executes !== exports.CATALOG_LOCK_ENGINE_EXECUTION) {
|
|
984
|
+
throw new CatalogLockError(`${path}.executes is ${JSON.stringify(action.executes)}; the one value it takes is "${exports.CATALOG_LOCK_ENGINE_EXECUTION}".`);
|
|
985
|
+
}
|
|
986
|
+
lock.actions.push({
|
|
987
|
+
key,
|
|
988
|
+
artifactRef,
|
|
989
|
+
digest: stringAt(`${path}.digest`, action.digest, catalog_release_1.ARTIFACT_DIGEST_PATTERN, "a sha256 digest"),
|
|
990
|
+
manifestSha256: stringAt(`${path}.manifestSha256`, action.manifestSha256, exports.MANIFEST_SHA256_PATTERN, "64 lowercase hex characters"),
|
|
991
|
+
provenance: {
|
|
992
|
+
repository: stringAt(`${path}.provenance.repository`, provenance2.repository),
|
|
993
|
+
commit: stringAt(`${path}.provenance.commit`, provenance2.commit)
|
|
994
|
+
},
|
|
995
|
+
...action.executes === void 0 ? {} : {
|
|
996
|
+
executes: exports.CATALOG_LOCK_ENGINE_EXECUTION,
|
|
997
|
+
imageDigest: stringAt(`${path}.imageDigest`, action.imageDigest, catalog_release_1.ARTIFACT_DIGEST_PATTERN, "a sha256 digest")
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
});
|
|
1001
|
+
return lock;
|
|
1002
|
+
}
|
|
1003
|
+
__name(parseCatalogLock, "parseCatalogLock");
|
|
1004
|
+
function catalogLockText(lock) {
|
|
1005
|
+
const ordered = {
|
|
1006
|
+
schemaVersion: lock.schemaVersion,
|
|
1007
|
+
release: {
|
|
1008
|
+
artifactRef: lock.release.artifactRef,
|
|
1009
|
+
digest: lock.release.digest,
|
|
1010
|
+
version: lock.release.version
|
|
1011
|
+
},
|
|
1012
|
+
actions: [
|
|
1013
|
+
...lock.actions
|
|
1014
|
+
].sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map((action) => ({
|
|
1015
|
+
key: action.key,
|
|
1016
|
+
artifactRef: action.artifactRef,
|
|
1017
|
+
digest: action.digest,
|
|
1018
|
+
manifestSha256: action.manifestSha256,
|
|
1019
|
+
provenance: {
|
|
1020
|
+
repository: action.provenance.repository,
|
|
1021
|
+
commit: action.provenance.commit
|
|
1022
|
+
},
|
|
1023
|
+
...action.executes === void 0 || action.imageDigest === void 0 ? {} : {
|
|
1024
|
+
executes: action.executes,
|
|
1025
|
+
imageDigest: action.imageDigest
|
|
1026
|
+
}
|
|
1027
|
+
}))
|
|
1028
|
+
};
|
|
1029
|
+
return `${JSON.stringify(ordered, null, 2)}
|
|
1030
|
+
`;
|
|
1031
|
+
}
|
|
1032
|
+
__name(catalogLockText, "catalogLockText");
|
|
1033
|
+
function authoredElsewhere(lock, key) {
|
|
1034
|
+
const namespace = lock.release.artifactRef.slice(0, lock.release.artifactRef.lastIndexOf("/"));
|
|
1035
|
+
const organisation = namespace.slice(namespace.lastIndexOf("/") + 1);
|
|
1036
|
+
return `${key} is authored in ${organisation}/${(0, action_repository_1.actionRepositoryName)(key)} \u2014 change it there, publish, assemble, vendor`;
|
|
1037
|
+
}
|
|
1038
|
+
__name(authoredElsewhere, "authoredElsewhere");
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
// ../contracts/dist/catalog-manifest-placeholders.js
|
|
1043
|
+
var require_catalog_manifest_placeholders = __commonJS({
|
|
1044
|
+
"../contracts/dist/catalog-manifest-placeholders.js"(exports) {
|
|
1045
|
+
"use strict";
|
|
1046
|
+
Object.defineProperty(exports, "__esModule", {
|
|
1047
|
+
value: true
|
|
1048
|
+
});
|
|
1049
|
+
exports.MANIFEST_PLACEHOLDER_PATTERN = exports.MANIFEST_PLACEHOLDERS = void 0;
|
|
1050
|
+
var brand_1 = require_brand();
|
|
1051
|
+
exports.MANIFEST_PLACEHOLDERS = {
|
|
1052
|
+
BRAND_SHORT_NAME: brand_1.BRAND_SHORT_NAME,
|
|
1053
|
+
BRAND_NAME: brand_1.BRAND_NAME
|
|
1054
|
+
};
|
|
1055
|
+
exports.MANIFEST_PLACEHOLDER_PATTERN = /\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;
|
|
1056
|
+
}
|
|
1057
|
+
});
|
|
1058
|
+
|
|
561
1059
|
// ../contracts/dist/change-control.js
|
|
562
1060
|
var require_change_control = __commonJS({
|
|
563
1061
|
"../contracts/dist/change-control.js"(exports) {
|
|
@@ -857,8 +1355,8 @@ var require_package_provenance = __commonJS({
|
|
|
857
1355
|
pair.accepted,
|
|
858
1356
|
pair.revoked
|
|
859
1357
|
]);
|
|
860
|
-
function poolProvenanceAuditAction(
|
|
861
|
-
const pair = exports.POOL_PROVENANCE_AUDIT_ACTIONS[
|
|
1358
|
+
function poolProvenanceAuditAction(provenance2, accepted) {
|
|
1359
|
+
const pair = exports.POOL_PROVENANCE_AUDIT_ACTIONS[provenance2];
|
|
862
1360
|
return accepted ? pair.accepted : pair.revoked;
|
|
863
1361
|
}
|
|
864
1362
|
__name(poolProvenanceAuditAction, "poolProvenanceAuditAction");
|
|
@@ -1286,7 +1784,7 @@ var require_validation = __commonJS({
|
|
|
1286
1784
|
Object.defineProperty(exports, "__esModule", {
|
|
1287
1785
|
value: true
|
|
1288
1786
|
});
|
|
1289
|
-
exports.TARGET_COMPARISON_KINDS = exports.TARGETS_NO_SCOPE = exports.TARGETS_NOT_COUNTED = exports.TARGET_COUNT_ABSENCES = void 0;
|
|
1787
|
+
exports.TARGET_COMPARISON_KINDS = exports.DECISION_CODES = exports.REFUSAL_CODES = exports.CHECK_LEVELS = exports.TARGETS_NO_SCOPE = exports.TARGETS_NOT_COUNTED = exports.TARGET_COUNT_ABSENCES = void 0;
|
|
1290
1788
|
exports.isTargetCountAbsence = isTargetCountAbsence;
|
|
1291
1789
|
exports.singleTarget = singleTarget;
|
|
1292
1790
|
exports.noTargetsResolved = noTargetsResolved;
|
|
@@ -1296,6 +1794,10 @@ var require_validation = __commonJS({
|
|
|
1296
1794
|
exports.eligibleTargetCount = eligibleTargetCount;
|
|
1297
1795
|
exports.targetsValidationCheck = targetsValidationCheck;
|
|
1298
1796
|
exports.targetSummaryOf = targetSummaryOf;
|
|
1797
|
+
exports.isCheckLevel = isCheckLevel;
|
|
1798
|
+
exports.isRefusalCode = isRefusalCode;
|
|
1799
|
+
exports.isDecisionCode = isDecisionCode;
|
|
1800
|
+
exports.validationCheckOf = validationCheckOf;
|
|
1299
1801
|
exports.scopeFailureDetail = scopeFailureDetail;
|
|
1300
1802
|
exports.targetsUnavailableFrom = targetsUnavailableFrom;
|
|
1301
1803
|
exports.compareTargets = compareTargets;
|
|
@@ -1412,6 +1914,95 @@ var require_validation = __commonJS({
|
|
|
1412
1914
|
return exports.TARGETS_NOT_COUNTED;
|
|
1413
1915
|
}
|
|
1414
1916
|
__name(targetSummaryOf, "targetSummaryOf");
|
|
1917
|
+
exports.CHECK_LEVELS = [
|
|
1918
|
+
"info",
|
|
1919
|
+
"warn",
|
|
1920
|
+
"error"
|
|
1921
|
+
];
|
|
1922
|
+
function isCheckLevel(value) {
|
|
1923
|
+
return typeof value === "string" && exports.CHECK_LEVELS.includes(value);
|
|
1924
|
+
}
|
|
1925
|
+
__name(isCheckLevel, "isCheckLevel");
|
|
1926
|
+
exports.REFUSAL_CODES = [
|
|
1927
|
+
"disk.notUnattached",
|
|
1928
|
+
"disk.locked",
|
|
1929
|
+
"disk.shared",
|
|
1930
|
+
"disk.inRecoveryPlan",
|
|
1931
|
+
"disk.keyUnusable",
|
|
1932
|
+
"disk.unverifiable"
|
|
1933
|
+
];
|
|
1934
|
+
function isRefusalCode(value) {
|
|
1935
|
+
return typeof value === "string" && exports.REFUSAL_CODES.includes(value);
|
|
1936
|
+
}
|
|
1937
|
+
__name(isRefusalCode, "isRefusalCode");
|
|
1938
|
+
exports.DECISION_CODES = [
|
|
1939
|
+
"disk.recentlyDetached",
|
|
1940
|
+
"disk.customerKey",
|
|
1941
|
+
"disk.customerKeyUnverified",
|
|
1942
|
+
"disk.guestEncrypted",
|
|
1943
|
+
"disk.noSnapshot",
|
|
1944
|
+
// A confidential VM's disk: its snapshot keeps the VM's guest state, region-bound, and restoring
|
|
1945
|
+
// one is unverified (docs/126 §6.6.1) — the operator's call, never a refusal.
|
|
1946
|
+
"disk.confidential"
|
|
1947
|
+
];
|
|
1948
|
+
function isDecisionCode(value) {
|
|
1949
|
+
return typeof value === "string" && exports.DECISION_CODES.includes(value);
|
|
1950
|
+
}
|
|
1951
|
+
__name(isDecisionCode, "isDecisionCode");
|
|
1952
|
+
function validationCheckOf(value) {
|
|
1953
|
+
const raw = typeof value === "object" && value !== null ? value : {};
|
|
1954
|
+
const level = raw.level;
|
|
1955
|
+
if (!isCheckLevel(level)) {
|
|
1956
|
+
return {
|
|
1957
|
+
refusal: `A check level of "${String(level)}" is not in the vocabulary.`
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
if (typeof raw.title !== "string" || raw.title.length === 0) {
|
|
1961
|
+
return {
|
|
1962
|
+
refusal: "Every check must carry a title."
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1965
|
+
if (raw.decision !== void 0 && raw.refusal !== void 0) {
|
|
1966
|
+
return {
|
|
1967
|
+
refusal: `A check carrying both a decision ("${String(raw.decision)}") and a refusal ("${String(raw.refusal)}") is not a check any producer of ours can build.`
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
if (raw.decision !== void 0) {
|
|
1971
|
+
if (!isDecisionCode(raw.decision) || level === "info") {
|
|
1972
|
+
return {
|
|
1973
|
+
refusal: `A check's decision of "${String(raw.decision)}" at level "${level}" is not one this channel accepts.`
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1976
|
+
return {
|
|
1977
|
+
check: {
|
|
1978
|
+
level,
|
|
1979
|
+
title: raw.title,
|
|
1980
|
+
decision: raw.decision
|
|
1981
|
+
}
|
|
1982
|
+
};
|
|
1983
|
+
}
|
|
1984
|
+
if (raw.refusal !== void 0) {
|
|
1985
|
+
if (!isRefusalCode(raw.refusal) || level !== "error") {
|
|
1986
|
+
return {
|
|
1987
|
+
refusal: `A check's refusal of "${String(raw.refusal)}" at level "${level}" is not one this channel accepts.`
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
return {
|
|
1991
|
+
check: {
|
|
1992
|
+
level,
|
|
1993
|
+
title: raw.title,
|
|
1994
|
+
refusal: raw.refusal
|
|
1995
|
+
}
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
return {
|
|
1999
|
+
check: {
|
|
2000
|
+
level,
|
|
2001
|
+
title: raw.title
|
|
2002
|
+
}
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
__name(validationCheckOf, "validationCheckOf");
|
|
1415
2006
|
function scopeFailureDetail(error) {
|
|
1416
2007
|
const raw = error instanceof Error ? error.message : String(error);
|
|
1417
2008
|
const oneLine = raw.replace(/\s+/g, " ").trim().replace(/[.!]+$/, "");
|
|
@@ -1482,7 +2073,7 @@ var require_execution = __commonJS({
|
|
|
1482
2073
|
Object.defineProperty(exports, "__esModule", {
|
|
1483
2074
|
value: true
|
|
1484
2075
|
});
|
|
1485
|
-
exports.ACCEPTING_A_PROVENANCE_IS_NOT_A_CAPABILITY = exports.RUNNER_PACKAGE_RUNTIME_NOT_PINNED = exports.CONNECTOR_UNAVAILABLE_MESSAGE = exports.ACTION_RUNNABILITY_CAUSE_KINDS = exports.EXECUTION_LOG_ACTIVITY_PHRASE = exports.EXECUTION_LOG_CONNECTION_PHRASE = exports.EXECUTION_LOG_STREAM_PHASES = exports.EXECUTION_LOG_ACTIVITIES = exports.targetsValidationCheck = exports.targetSummarySentence = exports.targetSummaryOf = exports.targetSummaryLabel = exports.targetsUnavailableFrom = exports.targetsUnavailable = exports.targetComparisonSentence = exports.targetComparisonLevel = exports.targetComparisonLabel = exports.TARGETS_NOT_COUNTED = exports.TARGETS_NO_SCOPE = exports.TARGET_COUNT_ABSENCES = exports.TARGET_COMPARISON_KINDS = exports.singleTarget = exports.scopeFailureDetail = exports.noTargetsResolved = exports.isTargetCountAbsence = exports.eligibleTargetCount = exports.compareTargets = exports.SUBSCRIPTION_ID_FIELD_RENAMED = void 0;
|
|
2076
|
+
exports.ACCEPTING_A_PROVENANCE_IS_NOT_A_CAPABILITY = exports.RUNNER_PACKAGE_RUNTIME_NOT_PINNED = exports.CONNECTOR_UNAVAILABLE_MESSAGE = exports.ACTION_RUNNABILITY_CAUSE_KINDS = exports.EXECUTION_LOG_ACTIVITY_PHRASE = exports.EXECUTION_LOG_CONNECTION_PHRASE = exports.EXECUTION_LOG_STREAM_PHASES = exports.EXECUTION_LOG_ACTIVITIES = exports.validationCheckOf = exports.targetsValidationCheck = exports.targetSummarySentence = exports.targetSummaryOf = exports.targetSummaryLabel = exports.targetsUnavailableFrom = exports.targetsUnavailable = exports.targetComparisonSentence = exports.targetComparisonLevel = exports.targetComparisonLabel = exports.TARGETS_NOT_COUNTED = exports.TARGETS_NO_SCOPE = exports.TARGET_COUNT_ABSENCES = exports.TARGET_COMPARISON_KINDS = exports.singleTarget = exports.scopeFailureDetail = exports.noTargetsResolved = exports.isTargetCountAbsence = exports.eligibleTargetCount = exports.compareTargets = exports.REFUSAL_CODES = exports.isRefusalCode = exports.isDecisionCode = exports.isCheckLevel = exports.DECISION_CODES = exports.CHECK_LEVELS = exports.SUBSCRIPTION_ID_FIELD_RENAMED = void 0;
|
|
1486
2077
|
exports.executionLogStreamSentence = executionLogStreamSentence;
|
|
1487
2078
|
exports.actionRunnabilityCopy = actionRunnabilityCopy;
|
|
1488
2079
|
exports.actionRunnabilityHeadline = actionRunnabilityHeadline;
|
|
@@ -1493,6 +2084,42 @@ var require_execution = __commonJS({
|
|
|
1493
2084
|
var brand_1 = require_brand();
|
|
1494
2085
|
exports.SUBSCRIPTION_ID_FIELD_RENAMED = "`subscriptionId` is not accepted on this route. It named an `azure_subscription.id` while this route has always resolved a `connection_scope.id`. The two were the same value while one shim gave a scope the registry row's id; a scope discovered since has an id of its own, and the registry id is either nobody's scope or another connector's. Send `connectionScopeId` \u2014 the value `ExecutionDto.connectionScopeId` reports for a run.";
|
|
1495
2086
|
var validation_1 = require_validation();
|
|
2087
|
+
Object.defineProperty(exports, "CHECK_LEVELS", {
|
|
2088
|
+
enumerable: true,
|
|
2089
|
+
get: /* @__PURE__ */ __name(function() {
|
|
2090
|
+
return validation_1.CHECK_LEVELS;
|
|
2091
|
+
}, "get")
|
|
2092
|
+
});
|
|
2093
|
+
Object.defineProperty(exports, "DECISION_CODES", {
|
|
2094
|
+
enumerable: true,
|
|
2095
|
+
get: /* @__PURE__ */ __name(function() {
|
|
2096
|
+
return validation_1.DECISION_CODES;
|
|
2097
|
+
}, "get")
|
|
2098
|
+
});
|
|
2099
|
+
Object.defineProperty(exports, "isCheckLevel", {
|
|
2100
|
+
enumerable: true,
|
|
2101
|
+
get: /* @__PURE__ */ __name(function() {
|
|
2102
|
+
return validation_1.isCheckLevel;
|
|
2103
|
+
}, "get")
|
|
2104
|
+
});
|
|
2105
|
+
Object.defineProperty(exports, "isDecisionCode", {
|
|
2106
|
+
enumerable: true,
|
|
2107
|
+
get: /* @__PURE__ */ __name(function() {
|
|
2108
|
+
return validation_1.isDecisionCode;
|
|
2109
|
+
}, "get")
|
|
2110
|
+
});
|
|
2111
|
+
Object.defineProperty(exports, "isRefusalCode", {
|
|
2112
|
+
enumerable: true,
|
|
2113
|
+
get: /* @__PURE__ */ __name(function() {
|
|
2114
|
+
return validation_1.isRefusalCode;
|
|
2115
|
+
}, "get")
|
|
2116
|
+
});
|
|
2117
|
+
Object.defineProperty(exports, "REFUSAL_CODES", {
|
|
2118
|
+
enumerable: true,
|
|
2119
|
+
get: /* @__PURE__ */ __name(function() {
|
|
2120
|
+
return validation_1.REFUSAL_CODES;
|
|
2121
|
+
}, "get")
|
|
2122
|
+
});
|
|
1496
2123
|
Object.defineProperty(exports, "compareTargets", {
|
|
1497
2124
|
enumerable: true,
|
|
1498
2125
|
get: /* @__PURE__ */ __name(function() {
|
|
@@ -1607,6 +2234,12 @@ var require_execution = __commonJS({
|
|
|
1607
2234
|
return validation_1.targetsValidationCheck;
|
|
1608
2235
|
}, "get")
|
|
1609
2236
|
});
|
|
2237
|
+
Object.defineProperty(exports, "validationCheckOf", {
|
|
2238
|
+
enumerable: true,
|
|
2239
|
+
get: /* @__PURE__ */ __name(function() {
|
|
2240
|
+
return validation_1.validationCheckOf;
|
|
2241
|
+
}, "get")
|
|
2242
|
+
});
|
|
1610
2243
|
exports.EXECUTION_LOG_ACTIVITIES = [
|
|
1611
2244
|
"executing",
|
|
1612
2245
|
"queued",
|
|
@@ -1634,6 +2267,7 @@ var require_execution = __commonJS({
|
|
|
1634
2267
|
}
|
|
1635
2268
|
__name(executionLogStreamSentence, "executionLogStreamSentence");
|
|
1636
2269
|
var ACTION_RUNNABILITY_CAUSE_KIND_SET = {
|
|
2270
|
+
catalog_blocked: true,
|
|
1637
2271
|
connector_unregistered: true,
|
|
1638
2272
|
package: true,
|
|
1639
2273
|
runner_pool: true,
|
|
@@ -1643,8 +2277,12 @@ var require_execution = __commonJS({
|
|
|
1643
2277
|
};
|
|
1644
2278
|
exports.ACTION_RUNNABILITY_CAUSE_KINDS = Object.keys(ACTION_RUNNABILITY_CAUSE_KIND_SET);
|
|
1645
2279
|
exports.CONNECTOR_UNAVAILABLE_MESSAGE = "This action is not yet available to run.";
|
|
2280
|
+
var CATALOG_BLOCKED_CAUSE = "This workspace does not permit this action.";
|
|
2281
|
+
var CATALOG_BLOCKED_REMEDY = "An administrator of this workspace decides which catalog actions may run here, and can allow it.";
|
|
1646
2282
|
function actionRunnabilityCopy(cause) {
|
|
1647
2283
|
switch (cause.kind) {
|
|
2284
|
+
case "catalog_blocked":
|
|
2285
|
+
return `${CATALOG_BLOCKED_CAUSE} ${CATALOG_BLOCKED_REMEDY}`;
|
|
1648
2286
|
case "package":
|
|
1649
2287
|
return (0, packages_2.actionUnrunnableCopy)(cause.detail);
|
|
1650
2288
|
case "runner_pool": {
|
|
@@ -1676,7 +2314,7 @@ var require_execution = __commonJS({
|
|
|
1676
2314
|
}
|
|
1677
2315
|
__name(describeRoutingPlaces, "describeRoutingPlaces");
|
|
1678
2316
|
function runnerPoolClauses(cause) {
|
|
1679
|
-
const
|
|
2317
|
+
const provenance2 = cause.provenance ? `is ${packages_1.PACKAGE_PROVENANCE_CLAUSES[cause.provenance]}` : (
|
|
1680
2318
|
// every other value this sentence could take is a claim about where bytes came from.
|
|
1681
2319
|
"has no recorded provenance"
|
|
1682
2320
|
);
|
|
@@ -1694,7 +2332,7 @@ var require_execution = __commonJS({
|
|
|
1694
2332
|
* credential it acts with. A refusal that dropped those would be telling an admin the
|
|
1695
2333
|
* decision no longer matters.
|
|
1696
2334
|
*/
|
|
1697
|
-
cause: `${
|
|
2335
|
+
cause: `${provenance2}, and would run on the private runner pool "${cause.poolName}", because that is where runs using this connection execute \u2014 on a machine you own, in a container that shares its host's kernel and acts with this connection's credential.`,
|
|
1698
2336
|
remedy: `${acceptedClause(cause.accepts)} ${POOL_CONSENT_REMEDY}`
|
|
1699
2337
|
};
|
|
1700
2338
|
}
|
|
@@ -1733,6 +2371,8 @@ var require_execution = __commonJS({
|
|
|
1733
2371
|
var POOL_CONSENT_REMEDY = `${brand_1.BRAND_SHORT_NAME} does not vouch for what runs on your machines \u2014 the owner of this pool does. An administrator can accept this provenance under Settings \u2192 Runners, and until they do, nothing of this provenance will be sent there. Built-in actions still run in that pool.`;
|
|
1734
2372
|
function actionRunnabilityHeadline(cause) {
|
|
1735
2373
|
switch (cause.kind) {
|
|
2374
|
+
case "catalog_blocked":
|
|
2375
|
+
return "this workspace does not permit its action \u2014 an administrator of this workspace can allow it";
|
|
1736
2376
|
case "package":
|
|
1737
2377
|
return (0, packages_2.actionUnrunnableHeadline)(cause.detail);
|
|
1738
2378
|
case "runner_pool":
|
|
@@ -1750,6 +2390,8 @@ var require_execution = __commonJS({
|
|
|
1750
2390
|
__name(actionRunnabilityHeadline, "actionRunnabilityHeadline");
|
|
1751
2391
|
function actionRunnabilityRefusal(cause) {
|
|
1752
2392
|
switch (cause.kind) {
|
|
2393
|
+
case "catalog_blocked":
|
|
2394
|
+
return `${CATALOG_BLOCKED_CAUSE} ${CATALOG_BLOCKED_REMEDY} Nothing was started.`;
|
|
1753
2395
|
case "package":
|
|
1754
2396
|
return (0, packages_2.actionUnrunnableRefusal)(cause.detail, cause);
|
|
1755
2397
|
case "runner_pool": {
|
|
@@ -1774,6 +2416,130 @@ var require_execution = __commonJS({
|
|
|
1774
2416
|
}
|
|
1775
2417
|
});
|
|
1776
2418
|
|
|
2419
|
+
// ../contracts/dist/decisions.js
|
|
2420
|
+
var require_decisions = __commonJS({
|
|
2421
|
+
"../contracts/dist/decisions.js"(exports) {
|
|
2422
|
+
"use strict";
|
|
2423
|
+
Object.defineProperty(exports, "__esModule", {
|
|
2424
|
+
value: true
|
|
2425
|
+
});
|
|
2426
|
+
exports.DECISION_ACKNOWLEDGEMENT = exports.TARGET_DISPOSITIONS = exports.UNDECIDED_AT_RUN = void 0;
|
|
2427
|
+
exports.recordedDecisionsOf = recordedDecisionsOf;
|
|
2428
|
+
exports.settleDecisionChecks = settleDecisionChecks;
|
|
2429
|
+
exports.raisedDecisionCodes = raisedDecisionCodes;
|
|
2430
|
+
exports.decisionMismatch = decisionMismatch;
|
|
2431
|
+
exports.decisionMismatchSentence = decisionMismatchSentence;
|
|
2432
|
+
exports.targetDecisionsMismatchSentence = targetDecisionsMismatchSentence;
|
|
2433
|
+
exports.decisionsMadeAtSubmit = decisionsMadeAtSubmit;
|
|
2434
|
+
exports.isTargetDisposition = isTargetDisposition;
|
|
2435
|
+
var validation_1 = require_validation();
|
|
2436
|
+
function recordedDecisionsOf(stored) {
|
|
2437
|
+
if (!Array.isArray(stored)) return [];
|
|
2438
|
+
const read = [];
|
|
2439
|
+
for (const entry of stored) {
|
|
2440
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
2441
|
+
const { code, acknowledgedBy, acknowledgedAt } = entry;
|
|
2442
|
+
if (!(0, validation_1.isDecisionCode)(code)) continue;
|
|
2443
|
+
if (typeof acknowledgedBy !== "string" || acknowledgedBy.length === 0) continue;
|
|
2444
|
+
if (typeof acknowledgedAt !== "string" || Number.isNaN(Date.parse(acknowledgedAt))) continue;
|
|
2445
|
+
if (read.some((decision) => decision.code === code)) continue;
|
|
2446
|
+
read.push({
|
|
2447
|
+
code,
|
|
2448
|
+
acknowledgedBy,
|
|
2449
|
+
acknowledgedAt
|
|
2450
|
+
});
|
|
2451
|
+
}
|
|
2452
|
+
return read;
|
|
2453
|
+
}
|
|
2454
|
+
__name(recordedDecisionsOf, "recordedDecisionsOf");
|
|
2455
|
+
exports.UNDECIDED_AT_RUN = "Nobody acknowledged this before the run, so it was not acted on.";
|
|
2456
|
+
function settleDecisionChecks(checks, inHand) {
|
|
2457
|
+
if (inHand.phase === "plan") return [
|
|
2458
|
+
...checks
|
|
2459
|
+
];
|
|
2460
|
+
return checks.map((check) => {
|
|
2461
|
+
if (!check.decision || check.level === "error") return check;
|
|
2462
|
+
if (inHand.recorded.some((decision) => decision.code === check.decision)) return check;
|
|
2463
|
+
return {
|
|
2464
|
+
...check,
|
|
2465
|
+
level: "error",
|
|
2466
|
+
title: `${check.title} ${exports.UNDECIDED_AT_RUN}`
|
|
2467
|
+
};
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
__name(settleDecisionChecks, "settleDecisionChecks");
|
|
2471
|
+
function raisedDecisionCodes(checks) {
|
|
2472
|
+
const raised = [];
|
|
2473
|
+
for (const check of checks) {
|
|
2474
|
+
if (check.decision && !raised.includes(check.decision)) raised.push(check.decision);
|
|
2475
|
+
}
|
|
2476
|
+
return raised;
|
|
2477
|
+
}
|
|
2478
|
+
__name(raisedDecisionCodes, "raisedDecisionCodes");
|
|
2479
|
+
function decisionMismatch(raised, acknowledged) {
|
|
2480
|
+
const unacknowledged = raised.filter((code) => !acknowledged.includes(code));
|
|
2481
|
+
const notRaised = acknowledged.filter((code) => !raised.includes(code));
|
|
2482
|
+
return unacknowledged.length === 0 && notRaised.length === 0 ? null : {
|
|
2483
|
+
unacknowledged,
|
|
2484
|
+
notRaised
|
|
2485
|
+
};
|
|
2486
|
+
}
|
|
2487
|
+
__name(decisionMismatch, "decisionMismatch");
|
|
2488
|
+
function decisionMismatchSentence(mismatch, checks = []) {
|
|
2489
|
+
return `This run was not submitted: the checks were run again at submit, and they no longer match what was acknowledged. ${mismatchParts(mismatch, checks).join(" ")} Review the plan and submit again.`;
|
|
2490
|
+
}
|
|
2491
|
+
__name(decisionMismatchSentence, "decisionMismatchSentence");
|
|
2492
|
+
function targetDecisionsMismatchSentence(mismatches) {
|
|
2493
|
+
const parts = mismatches.map((target) => target.unknown ? `${target.label}: no longer one of this step's targets.` : `${target.label}: ${mismatchParts(target, target.checks).join(" ")}`);
|
|
2494
|
+
const count = mismatches.length;
|
|
2495
|
+
return `This run was not started: the checks were run again, and for ${count} ${count === 1 ? "target" : "targets"} they no longer match what was decided. ${parts.join(" ")} Review the plan and start the run again.`;
|
|
2496
|
+
}
|
|
2497
|
+
__name(targetDecisionsMismatchSentence, "targetDecisionsMismatchSentence");
|
|
2498
|
+
function mismatchParts(mismatch, checks) {
|
|
2499
|
+
const parts = [];
|
|
2500
|
+
for (const code of mismatch.unacknowledged) {
|
|
2501
|
+
const sentence = checks.find((check) => check.decision === code)?.title ?? exports.DECISION_ACKNOWLEDGEMENT[code];
|
|
2502
|
+
parts.push(`Not acknowledged: ${withStop(sentence)}`);
|
|
2503
|
+
}
|
|
2504
|
+
for (const code of mismatch.notRaised) {
|
|
2505
|
+
parts.push(`Acknowledged, but no longer raised: \u201C${exports.DECISION_ACKNOWLEDGEMENT[code]}\u201D.`);
|
|
2506
|
+
}
|
|
2507
|
+
return parts;
|
|
2508
|
+
}
|
|
2509
|
+
__name(mismatchParts, "mismatchParts");
|
|
2510
|
+
function decisionsMadeAtSubmit(codes, by, at) {
|
|
2511
|
+
return codes.map((code) => ({
|
|
2512
|
+
code,
|
|
2513
|
+
acknowledgedBy: by,
|
|
2514
|
+
acknowledgedAt: at.toISOString()
|
|
2515
|
+
}));
|
|
2516
|
+
}
|
|
2517
|
+
__name(decisionsMadeAtSubmit, "decisionsMadeAtSubmit");
|
|
2518
|
+
exports.TARGET_DISPOSITIONS = [
|
|
2519
|
+
"ready",
|
|
2520
|
+
"left_out",
|
|
2521
|
+
"refused"
|
|
2522
|
+
];
|
|
2523
|
+
function isTargetDisposition(value) {
|
|
2524
|
+
return typeof value === "string" && exports.TARGET_DISPOSITIONS.includes(value);
|
|
2525
|
+
}
|
|
2526
|
+
__name(isTargetDisposition, "isTargetDisposition");
|
|
2527
|
+
function withStop(sentence) {
|
|
2528
|
+
const trimmed = sentence.trim();
|
|
2529
|
+
return /[.!?]$/.test(trimmed) ? trimmed : `${trimmed}.`;
|
|
2530
|
+
}
|
|
2531
|
+
__name(withStop, "withStop");
|
|
2532
|
+
exports.DECISION_ACKNOWLEDGEMENT = {
|
|
2533
|
+
"disk.recentlyDetached": "Delete it even though it was detached recently",
|
|
2534
|
+
"disk.customerKey": "Delete it \u2014 restoring later depends on this key staying enabled",
|
|
2535
|
+
"disk.customerKeyUnverified": "Delete it without confirmation that the key is enabled",
|
|
2536
|
+
"disk.guestEncrypted": "Delete it \u2014 restoring later depends on the guest's encryption secrets",
|
|
2537
|
+
"disk.noSnapshot": "Delete it with no snapshot \u2014 this cannot be undone",
|
|
2538
|
+
"disk.confidential": "Delete it, accepting that restoring a confidential VM disk is not verified"
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
});
|
|
2542
|
+
|
|
1777
2543
|
// ../contracts/dist/execution-site.js
|
|
1778
2544
|
var require_execution_site = __commonJS({
|
|
1779
2545
|
"../contracts/dist/execution-site.js"(exports) {
|
|
@@ -1858,7 +2624,13 @@ var require_resources = __commonJS({
|
|
|
1858
2624
|
Object.defineProperty(exports, "__esModule", {
|
|
1859
2625
|
value: true
|
|
1860
2626
|
});
|
|
1861
|
-
exports.DISCOVERY_TYPE_PATTERN = exports.
|
|
2627
|
+
exports.DISCOVERY_TYPE_PATTERN = exports.LEGACY_DISCOVERY_PROVIDER = exports.DISCOVERY_VOCABULARY = void 0;
|
|
2628
|
+
exports.discoveryTypesOf = discoveryTypesOf;
|
|
2629
|
+
exports.declaredDiscoveryTypes = declaredDiscoveryTypes;
|
|
2630
|
+
exports.liveDiscoveryTypes = liveDiscoveryTypes;
|
|
2631
|
+
exports.canonicalDiscoveryId = canonicalDiscoveryId;
|
|
2632
|
+
exports.resolveDiscoveryType = resolveDiscoveryType;
|
|
2633
|
+
exports.isSecretReference = isSecretReference;
|
|
1862
2634
|
exports.parseDiscoveryType = parseDiscoveryType;
|
|
1863
2635
|
exports.formatDiscoveryType = formatDiscoveryType;
|
|
1864
2636
|
exports.discoveryTypeSpec = discoveryTypeSpec;
|
|
@@ -1874,16 +2646,20 @@ var require_resources = __commonJS({
|
|
|
1874
2646
|
resourceGroup: {
|
|
1875
2647
|
requires: [],
|
|
1876
2648
|
routable: false,
|
|
1877
|
-
identity: null
|
|
2649
|
+
identity: null,
|
|
2650
|
+
secretRef: false
|
|
1878
2651
|
},
|
|
1879
2652
|
// ROUTABLE BUT NOT NAMABLE, and the two are different questions. A rule matches a vault by
|
|
1880
2653
|
// its NAME and its tags, which discovery supplies; an ARM vault id needs the resource group
|
|
1881
2654
|
// this vocabulary never asks for (`requires` is empty so vaults list subscription-wide). See
|
|
1882
2655
|
// `identity` on the spec above — null is the honest answer, not an omission.
|
|
2656
|
+
// NOT a secret reference: a vault CONTAINS secrets, and naming one reveals nothing a run
|
|
2657
|
+
// could read. The secret inside it is the reference (`keyVaultSecret` below).
|
|
1883
2658
|
keyVault: {
|
|
1884
2659
|
requires: [],
|
|
1885
2660
|
routable: true,
|
|
1886
|
-
identity: null
|
|
2661
|
+
identity: null,
|
|
2662
|
+
secretRef: false
|
|
1887
2663
|
},
|
|
1888
2664
|
// ★ A DATA-PLANE URI, NOT AN ARM PATH, AND IT IS WHAT DISCOVERY ALREADY RETURNS. The Key
|
|
1889
2665
|
// Vault SDK reports `SecretProperties.id` as `https://<vault>.vault.azure.net/secrets/<name>`
|
|
@@ -1895,27 +2671,33 @@ var require_resources = __commonJS({
|
|
|
1895
2671
|
"vault"
|
|
1896
2672
|
],
|
|
1897
2673
|
routable: true,
|
|
1898
|
-
identity: "https://{vault}.vault.azure.net/secrets/{name}"
|
|
2674
|
+
identity: "https://{vault}.vault.azure.net/secrets/{name}",
|
|
2675
|
+
// ★ THE ONE TYPE TODAY WHOSE VALUE POINTS AT A SECRET (docs/122 §3.5). The parameter holds
|
|
2676
|
+
// the secret's NAME within its vault; nothing in the platform ever reads the value behind it.
|
|
2677
|
+
secretRef: true
|
|
1899
2678
|
},
|
|
1900
2679
|
// A catalogue value. Nothing is reached by naming a region.
|
|
1901
2680
|
location: {
|
|
1902
2681
|
requires: [],
|
|
1903
2682
|
routable: false,
|
|
1904
|
-
identity: null
|
|
2683
|
+
identity: null,
|
|
2684
|
+
secretRef: false
|
|
1905
2685
|
},
|
|
1906
2686
|
managedDisk: {
|
|
1907
2687
|
requires: [
|
|
1908
2688
|
"resourceGroup"
|
|
1909
2689
|
],
|
|
1910
2690
|
routable: true,
|
|
1911
|
-
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/disks/{name}"
|
|
2691
|
+
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/disks/{name}",
|
|
2692
|
+
secretRef: false
|
|
1912
2693
|
},
|
|
1913
2694
|
sqlServer: {
|
|
1914
2695
|
requires: [
|
|
1915
2696
|
"resourceGroup"
|
|
1916
2697
|
],
|
|
1917
2698
|
routable: true,
|
|
1918
|
-
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{name}"
|
|
2699
|
+
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{name}",
|
|
2700
|
+
secretRef: false
|
|
1919
2701
|
},
|
|
1920
2702
|
sqlDatabase: {
|
|
1921
2703
|
requires: [
|
|
@@ -1923,7 +2705,8 @@ var require_resources = __commonJS({
|
|
|
1923
2705
|
"server"
|
|
1924
2706
|
],
|
|
1925
2707
|
routable: true,
|
|
1926
|
-
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{server}/databases/{name}"
|
|
2708
|
+
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{server}/databases/{name}",
|
|
2709
|
+
secretRef: false
|
|
1927
2710
|
},
|
|
1928
2711
|
// The one type where LISTING and IDENTIFYING are different questions. It lists
|
|
1929
2712
|
// SUBSCRIPTION-WIDE when no resource group is given (`virtualMachines.listAll()`,
|
|
@@ -1943,7 +2726,8 @@ var require_resources = __commonJS({
|
|
|
1943
2726
|
"resourceGroup"
|
|
1944
2727
|
],
|
|
1945
2728
|
routable: true,
|
|
1946
|
-
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{name}"
|
|
2729
|
+
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{name}",
|
|
2730
|
+
secretRef: false
|
|
1947
2731
|
},
|
|
1948
2732
|
// The sizes ONE machine can actually be resized to right now — not the region's SKU
|
|
1949
2733
|
// catalogue. `requires` is genuinely both fields' answer here: the safe set cannot be
|
|
@@ -1957,16 +2741,53 @@ var require_resources = __commonJS({
|
|
|
1957
2741
|
"vm"
|
|
1958
2742
|
],
|
|
1959
2743
|
routable: false,
|
|
1960
|
-
identity: null
|
|
2744
|
+
identity: null,
|
|
2745
|
+
secretRef: false
|
|
1961
2746
|
}
|
|
1962
2747
|
};
|
|
1963
|
-
|
|
2748
|
+
var DECLARED_DISCOVERY = {
|
|
1964
2749
|
azure: AZURE_DISCOVERY_TYPES,
|
|
1965
2750
|
aws: {},
|
|
1966
2751
|
gcp: {},
|
|
1967
2752
|
terraform: {}
|
|
1968
2753
|
};
|
|
1969
|
-
exports.
|
|
2754
|
+
exports.DISCOVERY_VOCABULARY = DECLARED_DISCOVERY;
|
|
2755
|
+
exports.LEGACY_DISCOVERY_PROVIDER = "azure";
|
|
2756
|
+
function discoveryTypesOf(provider) {
|
|
2757
|
+
return Object.keys(exports.DISCOVERY_VOCABULARY[provider]).map((type) => formatDiscoveryType(provider, type));
|
|
2758
|
+
}
|
|
2759
|
+
__name(discoveryTypesOf, "discoveryTypesOf");
|
|
2760
|
+
function declaredDiscoveryTypes() {
|
|
2761
|
+
return enums_1.PROVIDERS.flatMap(discoveryTypesOf);
|
|
2762
|
+
}
|
|
2763
|
+
__name(declaredDiscoveryTypes, "declaredDiscoveryTypes");
|
|
2764
|
+
function liveDiscoveryTypes(registered) {
|
|
2765
|
+
const providers = new Set(registered);
|
|
2766
|
+
return enums_1.PROVIDERS.filter((provider) => providers.has(provider)).flatMap(discoveryTypesOf);
|
|
2767
|
+
}
|
|
2768
|
+
__name(liveDiscoveryTypes, "liveDiscoveryTypes");
|
|
2769
|
+
function canonicalDiscoveryId(value) {
|
|
2770
|
+
const parsed = parseDiscoveryType(value.trim(), exports.LEGACY_DISCOVERY_PROVIDER);
|
|
2771
|
+
return parsed ? formatDiscoveryType(parsed.provider, parsed.type) : null;
|
|
2772
|
+
}
|
|
2773
|
+
__name(canonicalDiscoveryId, "canonicalDiscoveryId");
|
|
2774
|
+
function resolveDiscoveryType(value) {
|
|
2775
|
+
const parsed = parseDiscoveryType(value.trim(), exports.LEGACY_DISCOVERY_PROVIDER);
|
|
2776
|
+
if (!parsed) return null;
|
|
2777
|
+
const spec = discoveryTypeSpec(parsed.provider, parsed.type);
|
|
2778
|
+
if (!spec) return null;
|
|
2779
|
+
return {
|
|
2780
|
+
id: formatDiscoveryType(parsed.provider, parsed.type),
|
|
2781
|
+
provider: parsed.provider,
|
|
2782
|
+
type: parsed.type,
|
|
2783
|
+
spec
|
|
2784
|
+
};
|
|
2785
|
+
}
|
|
2786
|
+
__name(resolveDiscoveryType, "resolveDiscoveryType");
|
|
2787
|
+
function isSecretReference(discover) {
|
|
2788
|
+
return discover ? resolveDiscoveryType(discover)?.spec.secretRef === true : false;
|
|
2789
|
+
}
|
|
2790
|
+
__name(isSecretReference, "isSecretReference");
|
|
1970
2791
|
exports.DISCOVERY_TYPE_PATTERN = /^(?:([a-z][a-z0-9]*)\/)?([a-z][a-zA-Z0-9]{0,63})$/;
|
|
1971
2792
|
function parseDiscoveryType(value, fallbackProvider) {
|
|
1972
2793
|
const match = exports.DISCOVERY_TYPE_PATTERN.exec(value);
|
|
@@ -1990,7 +2811,9 @@ var require_resources = __commonJS({
|
|
|
1990
2811
|
}
|
|
1991
2812
|
__name(formatDiscoveryType, "formatDiscoveryType");
|
|
1992
2813
|
function discoveryTypeSpec(provider, type) {
|
|
1993
|
-
|
|
2814
|
+
if (!Object.prototype.hasOwnProperty.call(exports.DISCOVERY_VOCABULARY, provider)) return void 0;
|
|
2815
|
+
const vocabulary = exports.DISCOVERY_VOCABULARY[provider];
|
|
2816
|
+
return Object.prototype.hasOwnProperty.call(vocabulary, type) ? vocabulary[type] : void 0;
|
|
1994
2817
|
}
|
|
1995
2818
|
__name(discoveryTypeSpec, "discoveryTypeSpec");
|
|
1996
2819
|
function dependsOnLookup(keys, dependsOn) {
|
|
@@ -2076,8 +2899,59 @@ var require_governance = __commonJS({
|
|
|
2076
2899
|
value: true
|
|
2077
2900
|
});
|
|
2078
2901
|
exports.ANY_POLICY_MATCH = void 0;
|
|
2902
|
+
exports.approvalAssessmentOf = approvalAssessmentOf;
|
|
2903
|
+
exports.planAssessmentOf = planAssessmentOf;
|
|
2079
2904
|
exports.approvalOutcome = approvalOutcome;
|
|
2080
2905
|
exports.policyScopeSentence = policyScopeSentence;
|
|
2906
|
+
var decisions_1 = require_decisions();
|
|
2907
|
+
function approvalAssessmentOf(checks, decided) {
|
|
2908
|
+
const assessment = {
|
|
2909
|
+
facts: [],
|
|
2910
|
+
warnings: [],
|
|
2911
|
+
decisions: []
|
|
2912
|
+
};
|
|
2913
|
+
for (const check of checks) {
|
|
2914
|
+
if (check.decision) {
|
|
2915
|
+
const answer = decided.find((decision) => decision.code === check.decision);
|
|
2916
|
+
if (answer) assessment.decisions.push({
|
|
2917
|
+
...answer,
|
|
2918
|
+
title: check.title
|
|
2919
|
+
});
|
|
2920
|
+
} else if (check.level === "warn") {
|
|
2921
|
+
assessment.warnings.push(check.title);
|
|
2922
|
+
} else if (check.level === "info") {
|
|
2923
|
+
assessment.facts.push(check.title);
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
return assessment;
|
|
2927
|
+
}
|
|
2928
|
+
__name(approvalAssessmentOf, "approvalAssessmentOf");
|
|
2929
|
+
function planAssessmentOf(value) {
|
|
2930
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
2931
|
+
const a = value;
|
|
2932
|
+
if (!Array.isArray(a.facts) || !Array.isArray(a.warnings) || !Array.isArray(a.decisions)) {
|
|
2933
|
+
return null;
|
|
2934
|
+
}
|
|
2935
|
+
const decisions = [];
|
|
2936
|
+
for (const entry of a.decisions) {
|
|
2937
|
+
const [decision] = (0, decisions_1.recordedDecisionsOf)([
|
|
2938
|
+
entry
|
|
2939
|
+
]);
|
|
2940
|
+
const title = typeof entry === "object" && entry !== null ? entry.title : null;
|
|
2941
|
+
if (!decision || typeof title !== "string") return null;
|
|
2942
|
+
decisions.push({
|
|
2943
|
+
...decision,
|
|
2944
|
+
title
|
|
2945
|
+
});
|
|
2946
|
+
}
|
|
2947
|
+
const strings = /* @__PURE__ */ __name((list) => list.filter((item) => typeof item === "string"), "strings");
|
|
2948
|
+
return {
|
|
2949
|
+
facts: strings(a.facts),
|
|
2950
|
+
warnings: strings(a.warnings),
|
|
2951
|
+
decisions
|
|
2952
|
+
};
|
|
2953
|
+
}
|
|
2954
|
+
__name(planAssessmentOf, "planAssessmentOf");
|
|
2081
2955
|
exports.ANY_POLICY_MATCH = {
|
|
2082
2956
|
environments: [],
|
|
2083
2957
|
riskLevels: [],
|
|
@@ -2188,7 +3062,10 @@ var require_operations = __commonJS({
|
|
|
2188
3062
|
Object.defineProperty(exports, "__esModule", {
|
|
2189
3063
|
value: true
|
|
2190
3064
|
});
|
|
2191
|
-
exports.OPERATION_RUN_TARGET_STATUSES = exports.OPERATION_RUN_STEP_STATUSES = exports.OPERATION_RUN_STATUSES = exports.STEP_FAILURE_POLICIES = exports.OPERATION_STEP_KINDS = exports.DRY_RUN_OUTCOMES = exports.OPERATION_VERIFICATION_LIFECYCLES = exports.OPERATION_HEALTH_STATES = exports.OPERATION_LIFECYCLES = void 0;
|
|
3065
|
+
exports.PREVIEW_UNCHECKED_SENTENCE = exports.PREVIEW_UNCHECKED_REASONS = exports.OPERATION_RUN_TARGET_STATUSES = exports.OPERATION_RUN_STEP_STATUSES = exports.OPERATION_RUN_STATUSES = exports.STEP_FAILURE_POLICIES = exports.OPERATION_STEP_KINDS = exports.DRY_RUN_OUTCOMES = exports.OPERATION_VERIFICATION_LIFECYCLES = exports.OPERATION_HEALTH_STATES = exports.OPERATION_LIFECYCLES = void 0;
|
|
3066
|
+
exports.targetPlanOf = targetPlanOf;
|
|
3067
|
+
var governance_1 = require_governance();
|
|
3068
|
+
var decisions_1 = require_decisions();
|
|
2192
3069
|
exports.OPERATION_LIFECYCLES = [
|
|
2193
3070
|
"draft",
|
|
2194
3071
|
"published",
|
|
@@ -2255,9 +3132,38 @@ var require_operations = __commonJS({
|
|
|
2255
3132
|
"rolled_back",
|
|
2256
3133
|
"rollback_failed"
|
|
2257
3134
|
];
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
3135
|
+
function targetPlanOf(stored) {
|
|
3136
|
+
if (!Array.isArray(stored)) return null;
|
|
3137
|
+
const plan = [];
|
|
3138
|
+
for (const entry of stored) {
|
|
3139
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
|
|
3140
|
+
const { ref, label, disposition, decisions, assessment, refusal } = entry;
|
|
3141
|
+
if (typeof ref !== "string" || ref.length === 0) continue;
|
|
3142
|
+
if (typeof label !== "string" || !(0, decisions_1.isTargetDisposition)(disposition)) continue;
|
|
3143
|
+
if (refusal !== null && typeof refusal !== "string") continue;
|
|
3144
|
+
plan.push({
|
|
3145
|
+
ref,
|
|
3146
|
+
label,
|
|
3147
|
+
disposition,
|
|
3148
|
+
decisions: (0, decisions_1.recordedDecisionsOf)(decisions),
|
|
3149
|
+
assessment: (0, governance_1.planAssessmentOf)(assessment),
|
|
3150
|
+
refusal: refusal ?? null
|
|
3151
|
+
});
|
|
3152
|
+
}
|
|
3153
|
+
return plan;
|
|
3154
|
+
}
|
|
3155
|
+
__name(targetPlanOf, "targetPlanOf");
|
|
3156
|
+
exports.PREVIEW_UNCHECKED_REASONS = [
|
|
3157
|
+
"depends_on_earlier_output",
|
|
3158
|
+
"connector_plans_no_checks"
|
|
3159
|
+
];
|
|
3160
|
+
exports.PREVIEW_UNCHECKED_SENTENCE = {
|
|
3161
|
+
depends_on_earlier_output: "Not checked before the run: this step's parameters come from an earlier step's outputs, which exist only once that step has run. Each target is checked when its execution is submitted, and a target that would need a decision is not acted on.",
|
|
3162
|
+
connector_plans_no_checks: "This action runs no checks of its own before the run."
|
|
3163
|
+
};
|
|
3164
|
+
}
|
|
3165
|
+
});
|
|
3166
|
+
|
|
2261
3167
|
// ../contracts/dist/runs.js
|
|
2262
3168
|
var require_runs = __commonJS({
|
|
2263
3169
|
"../contracts/dist/runs.js"(exports) {
|
|
@@ -2291,6 +3197,28 @@ var require_runs = __commonJS({
|
|
|
2291
3197
|
}
|
|
2292
3198
|
});
|
|
2293
3199
|
|
|
3200
|
+
// ../contracts/dist/evidence-pack.js
|
|
3201
|
+
var require_evidence_pack = __commonJS({
|
|
3202
|
+
"../contracts/dist/evidence-pack.js"(exports) {
|
|
3203
|
+
"use strict";
|
|
3204
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3205
|
+
value: true
|
|
3206
|
+
});
|
|
3207
|
+
exports.EVIDENCE_EXPORTED_AUDIT_ACTION = exports.EVIDENCE_DIGEST_HEADER = void 0;
|
|
3208
|
+
exports.evidencePackFilename = evidencePackFilename;
|
|
3209
|
+
exports.EVIDENCE_DIGEST_HEADER = "X-Evidence-Digest";
|
|
3210
|
+
exports.EVIDENCE_EXPORTED_AUDIT_ACTION = "operation.evidence_exported";
|
|
3211
|
+
function evidencePackFilename(operationKey, from, to) {
|
|
3212
|
+
return `${operationKey}-evidence-${compactInstant(from)}-${compactInstant(to)}.json`;
|
|
3213
|
+
}
|
|
3214
|
+
__name(evidencePackFilename, "evidencePackFilename");
|
|
3215
|
+
function compactInstant(iso) {
|
|
3216
|
+
return iso.replace(/\.\d+(?=Z|[+-]\d{2}:?\d{2}$)/, "").replace(/[-:]/g, "");
|
|
3217
|
+
}
|
|
3218
|
+
__name(compactInstant, "compactInstant");
|
|
3219
|
+
}
|
|
3220
|
+
});
|
|
3221
|
+
|
|
2294
3222
|
// ../contracts/dist/package-egress.js
|
|
2295
3223
|
var require_package_egress = __commonJS({
|
|
2296
3224
|
"../contracts/dist/package-egress.js"(exports) {
|
|
@@ -2355,7 +3283,7 @@ var require_package_execution = __commonJS({
|
|
|
2355
3283
|
Object.defineProperty(exports, "__esModule", {
|
|
2356
3284
|
value: true
|
|
2357
3285
|
});
|
|
2358
|
-
exports.PACKAGE_CONTAINER_ENV = exports.PACKAGE_PHASES = void 0;
|
|
3286
|
+
exports.RUN_TOKEN_REJECTED = exports.RUN_TOKEN_HEADER = exports.PACKAGE_RUNNER_ENTRYPOINT = exports.PACKAGE_CONTAINER_ENV = exports.PACKAGE_PHASES = void 0;
|
|
2359
3287
|
exports.PACKAGE_PHASES = [
|
|
2360
3288
|
"validate",
|
|
2361
3289
|
"run",
|
|
@@ -2377,6 +3305,11 @@ var require_package_execution = __commonJS({
|
|
|
2377
3305
|
egressControlUrl: "RUNVERIC_EGRESS_CONTROL_URL",
|
|
2378
3306
|
egressControlToken: "RUNVERIC_EGRESS_CONTROL_TOKEN"
|
|
2379
3307
|
};
|
|
3308
|
+
exports.PACKAGE_RUNNER_ENTRYPOINT = [
|
|
3309
|
+
"/opt/runveric/runner/launch.sh"
|
|
3310
|
+
];
|
|
3311
|
+
exports.RUN_TOKEN_HEADER = "x-runveric-run-token";
|
|
3312
|
+
exports.RUN_TOKEN_REJECTED = "This run token is not valid for this execution.";
|
|
2380
3313
|
}
|
|
2381
3314
|
});
|
|
2382
3315
|
|
|
@@ -2555,9 +3488,9 @@ var require_runners = __commonJS({
|
|
|
2555
3488
|
authored: false
|
|
2556
3489
|
};
|
|
2557
3490
|
exports.RUNNER_POOL_DEFAULT_ACCEPTS_PROVENANCE = package_provenance_1.PACKAGE_PROVENANCE_KINDS.filter((kind) => exports.RUNNER_POOL_ACCEPTS_PROVENANCE_BY_DEFAULT[kind]);
|
|
2558
|
-
function poolAcceptsProvenance(accepts,
|
|
2559
|
-
if (
|
|
2560
|
-
return accepts.includes(
|
|
3491
|
+
function poolAcceptsProvenance(accepts, provenance2) {
|
|
3492
|
+
if (provenance2 === null) return false;
|
|
3493
|
+
return accepts.includes(provenance2);
|
|
2561
3494
|
}
|
|
2562
3495
|
__name(poolAcceptsProvenance, "poolAcceptsProvenance");
|
|
2563
3496
|
function containerNameFor(runnerName) {
|
|
@@ -2627,6 +3560,803 @@ var require_runners = __commonJS({
|
|
|
2627
3560
|
}
|
|
2628
3561
|
});
|
|
2629
3562
|
|
|
3563
|
+
// ../contracts/dist/scoped-credential.js
|
|
3564
|
+
var require_scoped_credential = __commonJS({
|
|
3565
|
+
"../contracts/dist/scoped-credential.js"(exports) {
|
|
3566
|
+
"use strict";
|
|
3567
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3568
|
+
value: true
|
|
3569
|
+
});
|
|
3570
|
+
exports.RunnerStepCredentialExpiredError = exports.RunnerStepCredentialError = exports.STEP_CREDENTIAL_FAILURE = exports.ENGINE_ACTION_TOKEN_HOLDER = exports.RUNNER_STEP_TOKEN_HOLDER = exports.StaticScopedCredential = void 0;
|
|
3571
|
+
var StaticScopedCredential = class StaticScopedCredential {
|
|
3572
|
+
static {
|
|
3573
|
+
__name(this, "StaticScopedCredential");
|
|
3574
|
+
}
|
|
3575
|
+
now;
|
|
3576
|
+
holder;
|
|
3577
|
+
byScope;
|
|
3578
|
+
constructor(tokens2, now = Date.now, holder = exports.RUNNER_STEP_TOKEN_HOLDER) {
|
|
3579
|
+
this.now = now;
|
|
3580
|
+
this.holder = holder;
|
|
3581
|
+
this.byScope = new Map(tokens2.map((token2) => [
|
|
3582
|
+
token2.scope,
|
|
3583
|
+
token2
|
|
3584
|
+
]));
|
|
3585
|
+
}
|
|
3586
|
+
async getToken(scopes) {
|
|
3587
|
+
const requested = typeof scopes === "string" ? [
|
|
3588
|
+
scopes
|
|
3589
|
+
] : scopes;
|
|
3590
|
+
for (const scope of requested) {
|
|
3591
|
+
const token2 = this.byScope.get(scope);
|
|
3592
|
+
if (token2) {
|
|
3593
|
+
if (token2.expiresOnTimestamp <= this.now()) {
|
|
3594
|
+
throw new RunnerStepCredentialExpiredError(scope, token2.expiresOnTimestamp, this.now(), this.holder);
|
|
3595
|
+
}
|
|
3596
|
+
return {
|
|
3597
|
+
token: token2.token,
|
|
3598
|
+
expiresOnTimestamp: token2.expiresOnTimestamp
|
|
3599
|
+
};
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
throw new RunnerStepCredentialError(requested, [
|
|
3603
|
+
...this.byScope.keys()
|
|
3604
|
+
], this.holder);
|
|
3605
|
+
}
|
|
3606
|
+
};
|
|
3607
|
+
exports.StaticScopedCredential = StaticScopedCredential;
|
|
3608
|
+
exports.RUNNER_STEP_TOKEN_HOLDER = {
|
|
3609
|
+
noun: "step",
|
|
3610
|
+
whereDeclared: "The channel mints exactly what a connector's azureAudiences declares, so this connector reaches an Azure resource it does not say it reaches.",
|
|
3611
|
+
whyNoMore: "A step container is handed minted bytes and nothing it can use to get another, so the step ran longer than its own credential and cannot continue."
|
|
3612
|
+
};
|
|
3613
|
+
exports.ENGINE_ACTION_TOKEN_HOLDER = {
|
|
3614
|
+
noun: "action",
|
|
3615
|
+
whereDeclared: "The engine mints exactly the audiences an action's manifest declares in execution.audiences, so this action reaches an Azure resource it does not say it reaches.",
|
|
3616
|
+
whyNoMore: "An action is handed minted bytes and nothing it can use to get another, so it ran longer than its own credential and cannot continue."
|
|
3617
|
+
};
|
|
3618
|
+
exports.STEP_CREDENTIAL_FAILURE = "runvericStepCredentialFailure";
|
|
3619
|
+
var RunnerStepCredentialError = class RunnerStepCredentialError extends Error {
|
|
3620
|
+
static {
|
|
3621
|
+
__name(this, "RunnerStepCredentialError");
|
|
3622
|
+
}
|
|
3623
|
+
constructor(requested, available, holder = exports.RUNNER_STEP_TOKEN_HOLDER) {
|
|
3624
|
+
super(`This ${holder.noun} was authorized for ${available.length > 0 ? available.map(quote).join(", ") : "no scopes"} and asked for ${requested.map(quote).join(", ")}. ${holder.whereDeclared}`);
|
|
3625
|
+
this.name = "RunnerStepCredentialError";
|
|
3626
|
+
}
|
|
3627
|
+
/** Not a fact about the resource — see {@link STEP_CREDENTIAL_FAILURE}. */
|
|
3628
|
+
[exports.STEP_CREDENTIAL_FAILURE] = true;
|
|
3629
|
+
};
|
|
3630
|
+
exports.RunnerStepCredentialError = RunnerStepCredentialError;
|
|
3631
|
+
var RunnerStepCredentialExpiredError = class RunnerStepCredentialExpiredError extends Error {
|
|
3632
|
+
static {
|
|
3633
|
+
__name(this, "RunnerStepCredentialExpiredError");
|
|
3634
|
+
}
|
|
3635
|
+
constructor(scope, expiresOnTimestamp, now, holder = exports.RUNNER_STEP_TOKEN_HOLDER) {
|
|
3636
|
+
super(`This ${holder.noun}'s token for ${quote(scope)} expired ${Math.round((now - expiresOnTimestamp) / 1e3)}s ago, at ${new Date(expiresOnTimestamp).toISOString()}. ${holder.whyNoMore}`);
|
|
3637
|
+
this.name = "RunnerStepCredentialExpiredError";
|
|
3638
|
+
}
|
|
3639
|
+
/** Not a fact about the resource — see {@link STEP_CREDENTIAL_FAILURE}. */
|
|
3640
|
+
[exports.STEP_CREDENTIAL_FAILURE] = true;
|
|
3641
|
+
};
|
|
3642
|
+
exports.RunnerStepCredentialExpiredError = RunnerStepCredentialExpiredError;
|
|
3643
|
+
function quote(scope) {
|
|
3644
|
+
return `"${scope}"`;
|
|
3645
|
+
}
|
|
3646
|
+
__name(quote, "quote");
|
|
3647
|
+
}
|
|
3648
|
+
});
|
|
3649
|
+
|
|
3650
|
+
// ../contracts/dist/action-protocol.js
|
|
3651
|
+
var require_action_protocol = __commonJS({
|
|
3652
|
+
"../contracts/dist/action-protocol.js"(exports) {
|
|
3653
|
+
"use strict";
|
|
3654
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3655
|
+
value: true
|
|
3656
|
+
});
|
|
3657
|
+
exports.ActionProtocolError = exports.JWT_SHAPE = exports.REDACTED_SECRET = exports.ENGINE_RUN_ROUTES = exports.ACTION_PROBE_ENV = exports.ACTION_RUNTIME_PROBE_FLAG = exports.ACTION_CONTAINER_ENV = exports.MAX_PLAN_VALIDATE_TARGETS = exports.ACTION_PROTOCOL_LIMITS = exports.ACTION_EXECUTION_KINDS = exports.ENGINE_CONNECTOR_KEY = exports.ACTION_CONTRACTS = void 0;
|
|
3658
|
+
exports.isActionContract = isActionContract;
|
|
3659
|
+
exports.engineRunUrl = engineRunUrl;
|
|
3660
|
+
exports.actionAudienceRefusal = actionAudienceRefusal;
|
|
3661
|
+
exports.actionTokenScope = actionTokenScope;
|
|
3662
|
+
exports.redactSecrets = redactSecrets;
|
|
3663
|
+
exports.redactJwtShaped = redactJwtShaped;
|
|
3664
|
+
exports.carriesJwtShaped = carriesJwtShaped;
|
|
3665
|
+
exports.jsonByteLength = jsonByteLength;
|
|
3666
|
+
exports.parseActionInvocation = parseActionInvocation;
|
|
3667
|
+
exports.parseActionEvent = parseActionEvent;
|
|
3668
|
+
exports.tooManyEvents = tooManyEvents;
|
|
3669
|
+
exports.parseActionCheckpoint = parseActionCheckpoint;
|
|
3670
|
+
exports.parseCheckpointAck = parseCheckpointAck;
|
|
3671
|
+
exports.parseActionReport = parseActionReport;
|
|
3672
|
+
var wire_identity_1 = require_wire_identity();
|
|
3673
|
+
var package_execution_1 = require_package_execution();
|
|
3674
|
+
var validation_1 = require_validation();
|
|
3675
|
+
exports.ACTION_CONTRACTS = [
|
|
3676
|
+
wire_identity_1.ACTION_PROTOCOL_V1
|
|
3677
|
+
];
|
|
3678
|
+
function isActionContract(value) {
|
|
3679
|
+
return typeof value === "string" && exports.ACTION_CONTRACTS.includes(value);
|
|
3680
|
+
}
|
|
3681
|
+
__name(isActionContract, "isActionContract");
|
|
3682
|
+
exports.ENGINE_CONNECTOR_KEY = "runveric.engine";
|
|
3683
|
+
exports.ACTION_EXECUTION_KINDS = [
|
|
3684
|
+
"engine"
|
|
3685
|
+
];
|
|
3686
|
+
exports.ACTION_PROTOCOL_LIMITS = {
|
|
3687
|
+
maxEvents: 500,
|
|
3688
|
+
maxEventBytes: 2048,
|
|
3689
|
+
maxReportBytes: 262144,
|
|
3690
|
+
maxRestorePointBytes: 65536
|
|
3691
|
+
};
|
|
3692
|
+
exports.MAX_PLAN_VALIDATE_TARGETS = 200;
|
|
3693
|
+
exports.ACTION_CONTAINER_ENV = {
|
|
3694
|
+
runToken: "RUNVERIC_ACTION_RUN_TOKEN",
|
|
3695
|
+
callbackBaseUrl: "RUNVERIC_ACTION_CALLBACK_BASE_URL",
|
|
3696
|
+
runId: "RUNVERIC_ACTION_RUN_ID",
|
|
3697
|
+
imageDigest: "RUNVERIC_ACTION_IMAGE_DIGEST"
|
|
3698
|
+
};
|
|
3699
|
+
exports.ACTION_RUNTIME_PROBE_FLAG = "--probe";
|
|
3700
|
+
exports.ACTION_PROBE_ENV = {
|
|
3701
|
+
identities: "RUNVERIC_PROBE_IDENTITIES"
|
|
3702
|
+
};
|
|
3703
|
+
exports.ENGINE_RUN_ROUTES = {
|
|
3704
|
+
base: "internal/engine-runs",
|
|
3705
|
+
invocation: "invocation",
|
|
3706
|
+
events: "events",
|
|
3707
|
+
checkpoint: "checkpoint",
|
|
3708
|
+
report: "report"
|
|
3709
|
+
};
|
|
3710
|
+
function engineRunUrl(callbackBaseUrl, runId, message) {
|
|
3711
|
+
const origin2 = callbackBaseUrl.replace(/\/+$/, "");
|
|
3712
|
+
return `${origin2}/${exports.ENGINE_RUN_ROUTES.base}/${encodeURIComponent(runId)}/${exports.ENGINE_RUN_ROUTES[message]}`;
|
|
3713
|
+
}
|
|
3714
|
+
__name(engineRunUrl, "engineRunUrl");
|
|
3715
|
+
function actionAudienceRefusal(value) {
|
|
3716
|
+
if (typeof value === "string" && AUDIENCE_ORIGIN.test(value)) return null;
|
|
3717
|
+
return `"${String(value)}" is not an audience. An audience is the https origin of the resource a token is for \u2014 lowercase, with no port, path or trailing slash, e.g. "https://management.azure.com".`;
|
|
3718
|
+
}
|
|
3719
|
+
__name(actionAudienceRefusal, "actionAudienceRefusal");
|
|
3720
|
+
var AUDIENCE_ORIGIN = /^https:\/\/[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/;
|
|
3721
|
+
function actionTokenScope(audience) {
|
|
3722
|
+
return `${audience}/.default`;
|
|
3723
|
+
}
|
|
3724
|
+
__name(actionTokenScope, "actionTokenScope");
|
|
3725
|
+
exports.REDACTED_SECRET = "[redacted]";
|
|
3726
|
+
function redactSecrets(value, secrets) {
|
|
3727
|
+
const present = secrets.filter((secret) => secret.length > 0);
|
|
3728
|
+
if (present.length === 0) return value;
|
|
3729
|
+
return mapStrings(value, (text) => present.reduce((out, secret) => out.split(secret).join(exports.REDACTED_SECRET), text));
|
|
3730
|
+
}
|
|
3731
|
+
__name(redactSecrets, "redactSecrets");
|
|
3732
|
+
exports.JWT_SHAPE = /eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*/;
|
|
3733
|
+
function redactJwtShaped(value) {
|
|
3734
|
+
const everyMatch = new RegExp(exports.JWT_SHAPE.source, "g");
|
|
3735
|
+
return mapStrings(value, (text) => text.replace(everyMatch, exports.REDACTED_SECRET));
|
|
3736
|
+
}
|
|
3737
|
+
__name(redactJwtShaped, "redactJwtShaped");
|
|
3738
|
+
function carriesJwtShaped(value) {
|
|
3739
|
+
let found = false;
|
|
3740
|
+
mapStrings(value, (text) => {
|
|
3741
|
+
found ||= exports.JWT_SHAPE.test(text);
|
|
3742
|
+
return text;
|
|
3743
|
+
});
|
|
3744
|
+
return found;
|
|
3745
|
+
}
|
|
3746
|
+
__name(carriesJwtShaped, "carriesJwtShaped");
|
|
3747
|
+
function mapStrings(value, scrub) {
|
|
3748
|
+
const walk = /* @__PURE__ */ __name((node) => {
|
|
3749
|
+
if (typeof node === "string") return scrub(node);
|
|
3750
|
+
if (Array.isArray(node)) return node.map(walk);
|
|
3751
|
+
if (node !== null && typeof node === "object") {
|
|
3752
|
+
return Object.fromEntries(Object.entries(node).map(([key, item]) => [
|
|
3753
|
+
scrub(key),
|
|
3754
|
+
walk(item)
|
|
3755
|
+
]));
|
|
3756
|
+
}
|
|
3757
|
+
return node;
|
|
3758
|
+
}, "walk");
|
|
3759
|
+
return walk(value);
|
|
3760
|
+
}
|
|
3761
|
+
__name(mapStrings, "mapStrings");
|
|
3762
|
+
function jsonByteLength(value) {
|
|
3763
|
+
const text = JSON.stringify(value) ?? "";
|
|
3764
|
+
let bytes = 0;
|
|
3765
|
+
for (const char of text) {
|
|
3766
|
+
const point = char.codePointAt(0) ?? 0;
|
|
3767
|
+
bytes += point < 128 ? 1 : point < 2048 ? 2 : point < 65536 ? 3 : 4;
|
|
3768
|
+
}
|
|
3769
|
+
return bytes;
|
|
3770
|
+
}
|
|
3771
|
+
__name(jsonByteLength, "jsonByteLength");
|
|
3772
|
+
var ActionProtocolError = class ActionProtocolError extends Error {
|
|
3773
|
+
static {
|
|
3774
|
+
__name(this, "ActionProtocolError");
|
|
3775
|
+
}
|
|
3776
|
+
constructor(message) {
|
|
3777
|
+
super(message);
|
|
3778
|
+
this.name = "ActionProtocolError";
|
|
3779
|
+
}
|
|
3780
|
+
};
|
|
3781
|
+
exports.ActionProtocolError = ActionProtocolError;
|
|
3782
|
+
function parseActionInvocation(body) {
|
|
3783
|
+
const raw = objectOf(body, "The invocation");
|
|
3784
|
+
onlyKeys(raw, INVOCATION_KEYS, "The invocation");
|
|
3785
|
+
if (!isActionContract(raw.protocol)) {
|
|
3786
|
+
throw new ActionProtocolError(`The invocation speaks ${String(raw.protocol)}, and this runtime speaks ${exports.ACTION_CONTRACTS.join(", ")}.`);
|
|
3787
|
+
}
|
|
3788
|
+
const phase = raw.phase;
|
|
3789
|
+
if (typeof phase !== "string" || !package_execution_1.PACKAGE_PHASES.includes(phase)) {
|
|
3790
|
+
throw new ActionProtocolError(`An invocation phase of "${String(phase)}" is not in the vocabulary.`);
|
|
3791
|
+
}
|
|
3792
|
+
const actionRaw = objectOf(raw.action, "The invocation's action");
|
|
3793
|
+
onlyKeys(actionRaw, [
|
|
3794
|
+
"key",
|
|
3795
|
+
"version",
|
|
3796
|
+
"image"
|
|
3797
|
+
], "The invocation's action");
|
|
3798
|
+
const action = {
|
|
3799
|
+
key: nonEmpty(actionRaw.key, "The action's key"),
|
|
3800
|
+
version: nonEmpty(actionRaw.version, "The action's version"),
|
|
3801
|
+
image: digestPinned(actionRaw.image)
|
|
3802
|
+
};
|
|
3803
|
+
const executionRaw = objectOf(raw.execution, "The invocation's execution");
|
|
3804
|
+
onlyKeys(executionRaw, [
|
|
3805
|
+
"id",
|
|
3806
|
+
"tenantId",
|
|
3807
|
+
"correlationId",
|
|
3808
|
+
"dryRun"
|
|
3809
|
+
], "The invocation's execution");
|
|
3810
|
+
if (typeof executionRaw.dryRun !== "boolean") {
|
|
3811
|
+
throw new ActionProtocolError("The invocation's execution must say whether it is a dry run.");
|
|
3812
|
+
}
|
|
3813
|
+
const execution = {
|
|
3814
|
+
id: nonEmpty(executionRaw.id, "The execution's id"),
|
|
3815
|
+
tenantId: nonEmpty(executionRaw.tenantId, "The execution's tenant"),
|
|
3816
|
+
correlationId: nonEmpty(executionRaw.correlationId, "The execution's correlation id"),
|
|
3817
|
+
dryRun: executionRaw.dryRun
|
|
3818
|
+
};
|
|
3819
|
+
const limits = parseLimits(raw.limits);
|
|
3820
|
+
const decisions = parseDecisions(raw.decisions);
|
|
3821
|
+
const targets = parseTargets(raw.targets);
|
|
3822
|
+
const credentials = {
|
|
3823
|
+
tokens: parseTokens(objectOf(raw.credentials, "The invocation's credentials"))
|
|
3824
|
+
};
|
|
3825
|
+
const restorePoint = nullableObject(raw.restorePoint, "The invocation's restore point");
|
|
3826
|
+
const priorResult = nullableObject(raw.priorResult, "The invocation's prior result");
|
|
3827
|
+
if (restorePoint !== null && jsonByteLength(restorePoint) > limits.maxRestorePointBytes) {
|
|
3828
|
+
throw new ActionProtocolError(`The invocation's restore point is larger than ${limits.maxRestorePointBytes} bytes.`);
|
|
3829
|
+
}
|
|
3830
|
+
const base = {
|
|
3831
|
+
protocol: raw.protocol,
|
|
3832
|
+
action,
|
|
3833
|
+
execution,
|
|
3834
|
+
targets,
|
|
3835
|
+
credentials,
|
|
3836
|
+
limits
|
|
3837
|
+
};
|
|
3838
|
+
if (phase === "validate") {
|
|
3839
|
+
if (raw.mode !== "plan" && raw.mode !== "run") {
|
|
3840
|
+
throw new ActionProtocolError(`A validate invocation's mode of "${String(raw.mode)}" is not "plan" or "run".`);
|
|
3841
|
+
}
|
|
3842
|
+
if (raw.mode !== decisions.phase) {
|
|
3843
|
+
throw new ActionProtocolError(`A validate invocation in ${raw.mode} mode carries ${decisions.phase}-phase decisions; the two are the same fact.`);
|
|
3844
|
+
}
|
|
3845
|
+
assertTargetCount(targets.length, raw.mode === "plan" ? exports.MAX_PLAN_VALIDATE_TARGETS : 1, `A ${raw.mode}-mode validate`);
|
|
3846
|
+
if (restorePoint !== null || priorResult !== null) {
|
|
3847
|
+
throw new ActionProtocolError("A validate invocation carries no restore point and no prior result.");
|
|
3848
|
+
}
|
|
3849
|
+
return {
|
|
3850
|
+
...base,
|
|
3851
|
+
phase,
|
|
3852
|
+
mode: raw.mode,
|
|
3853
|
+
decisions,
|
|
3854
|
+
restorePoint: null,
|
|
3855
|
+
priorResult: null
|
|
3856
|
+
};
|
|
3857
|
+
}
|
|
3858
|
+
if (raw.mode !== void 0) {
|
|
3859
|
+
throw new ActionProtocolError(`A ${phase} invocation has no mode \u2014 only a validate is asked in a mode.`);
|
|
3860
|
+
}
|
|
3861
|
+
if (decisions.phase !== "run") {
|
|
3862
|
+
throw new ActionProtocolError(`A ${phase} invocation carries a run's decisions, never a plan's.`);
|
|
3863
|
+
}
|
|
3864
|
+
assertTargetCount(targets.length, 1, `A ${phase}`);
|
|
3865
|
+
if (phase === "run") {
|
|
3866
|
+
if (priorResult !== null) {
|
|
3867
|
+
throw new ActionProtocolError("A run invocation carries no prior result \u2014 only a rollback reads one.");
|
|
3868
|
+
}
|
|
3869
|
+
return {
|
|
3870
|
+
...base,
|
|
3871
|
+
phase,
|
|
3872
|
+
decisions,
|
|
3873
|
+
restorePoint,
|
|
3874
|
+
priorResult: null
|
|
3875
|
+
};
|
|
3876
|
+
}
|
|
3877
|
+
return {
|
|
3878
|
+
...base,
|
|
3879
|
+
phase: "rollback",
|
|
3880
|
+
decisions,
|
|
3881
|
+
restorePoint,
|
|
3882
|
+
priorResult
|
|
3883
|
+
};
|
|
3884
|
+
}
|
|
3885
|
+
__name(parseActionInvocation, "parseActionInvocation");
|
|
3886
|
+
function parseActionEvent(body, limits, alreadyReceived) {
|
|
3887
|
+
if (alreadyReceived >= limits.maxEvents) throw tooManyEvents(limits);
|
|
3888
|
+
const raw = objectOf(body, "An event");
|
|
3889
|
+
onlyKeys(raw, [
|
|
3890
|
+
"level",
|
|
3891
|
+
"message",
|
|
3892
|
+
"data"
|
|
3893
|
+
], "An event");
|
|
3894
|
+
if (jsonByteLength(raw) > limits.maxEventBytes) {
|
|
3895
|
+
throw new ActionProtocolError(`An event is larger than ${limits.maxEventBytes} bytes.`);
|
|
3896
|
+
}
|
|
3897
|
+
if (typeof raw.level !== "string" || !Object.hasOwn(EVENT_LEVELS, raw.level)) {
|
|
3898
|
+
throw new ActionProtocolError(`An event level of "${String(raw.level)}" is not in the vocabulary.`);
|
|
3899
|
+
}
|
|
3900
|
+
const message = nonEmpty(raw.message, "An event's message");
|
|
3901
|
+
if (raw.data === void 0) return {
|
|
3902
|
+
level: raw.level,
|
|
3903
|
+
message
|
|
3904
|
+
};
|
|
3905
|
+
return {
|
|
3906
|
+
level: raw.level,
|
|
3907
|
+
message,
|
|
3908
|
+
data: jsonObject(raw.data, "An event's data")
|
|
3909
|
+
};
|
|
3910
|
+
}
|
|
3911
|
+
__name(parseActionEvent, "parseActionEvent");
|
|
3912
|
+
function tooManyEvents(limits) {
|
|
3913
|
+
return new ActionProtocolError(`The action sent more than ${limits.maxEvents} events.`);
|
|
3914
|
+
}
|
|
3915
|
+
__name(tooManyEvents, "tooManyEvents");
|
|
3916
|
+
function parseActionCheckpoint(body, limits) {
|
|
3917
|
+
const raw = objectOf(body, "A checkpoint");
|
|
3918
|
+
onlyKeys(raw, [
|
|
3919
|
+
"kind",
|
|
3920
|
+
"record"
|
|
3921
|
+
], "A checkpoint");
|
|
3922
|
+
if (raw.kind !== "restore_point") {
|
|
3923
|
+
throw new ActionProtocolError(`A checkpoint of kind "${String(raw.kind)}" is not one this contract knows.`);
|
|
3924
|
+
}
|
|
3925
|
+
const record = jsonObject(raw.record, "A checkpoint's record");
|
|
3926
|
+
if (jsonByteLength(record) > limits.maxRestorePointBytes) {
|
|
3927
|
+
throw new ActionProtocolError(`A restore point is larger than ${limits.maxRestorePointBytes} bytes.`);
|
|
3928
|
+
}
|
|
3929
|
+
return {
|
|
3930
|
+
kind: "restore_point",
|
|
3931
|
+
record
|
|
3932
|
+
};
|
|
3933
|
+
}
|
|
3934
|
+
__name(parseActionCheckpoint, "parseActionCheckpoint");
|
|
3935
|
+
function parseCheckpointAck(body) {
|
|
3936
|
+
const raw = objectOf(body, "A checkpoint acknowledgement");
|
|
3937
|
+
onlyKeys(raw, [
|
|
3938
|
+
"acknowledged"
|
|
3939
|
+
], "A checkpoint acknowledgement");
|
|
3940
|
+
if (raw.acknowledged !== true) {
|
|
3941
|
+
throw new ActionProtocolError("The checkpoint was not acknowledged.");
|
|
3942
|
+
}
|
|
3943
|
+
return {
|
|
3944
|
+
acknowledged: true
|
|
3945
|
+
};
|
|
3946
|
+
}
|
|
3947
|
+
__name(parseCheckpointAck, "parseCheckpointAck");
|
|
3948
|
+
function parseActionReport(body, sent) {
|
|
3949
|
+
const raw = objectOf(body, "The report");
|
|
3950
|
+
onlyKeys(raw, [
|
|
3951
|
+
"phase",
|
|
3952
|
+
"checks",
|
|
3953
|
+
"outcome"
|
|
3954
|
+
], "The report");
|
|
3955
|
+
if (jsonByteLength(raw) > sent.limits.maxReportBytes) {
|
|
3956
|
+
throw new ActionProtocolError(`The report is larger than ${sent.limits.maxReportBytes} bytes.`);
|
|
3957
|
+
}
|
|
3958
|
+
if (raw.phase !== sent.phase) {
|
|
3959
|
+
throw new ActionProtocolError(`The report is for phase "${String(raw.phase)}", but this invocation is for "${sent.phase}".`);
|
|
3960
|
+
}
|
|
3961
|
+
if (sent.phase === "validate") {
|
|
3962
|
+
if (raw.checks === void 0 === (raw.outcome === void 0)) {
|
|
3963
|
+
throw new ActionProtocolError("A validate report carries checks, or \u2014 when they could not run \u2014 a failed outcome; exactly one of the two.");
|
|
3964
|
+
}
|
|
3965
|
+
if (raw.outcome !== void 0) {
|
|
3966
|
+
const outcome = parseOutcome(raw.outcome);
|
|
3967
|
+
if (outcome.status !== "failed") {
|
|
3968
|
+
throw new ActionProtocolError(`A validate report's outcome can only say the checks failed to run, never "${outcome.status}" \u2014 the engine decides what checks mean.`);
|
|
3969
|
+
}
|
|
3970
|
+
return {
|
|
3971
|
+
phase: "validate",
|
|
3972
|
+
outcome
|
|
3973
|
+
};
|
|
3974
|
+
}
|
|
3975
|
+
const byRef = objectOf(raw.checks, "A validate report's checks");
|
|
3976
|
+
const expected = new Set(sent.targets.map((target) => target.ref));
|
|
3977
|
+
for (const ref of Object.keys(byRef)) {
|
|
3978
|
+
if (!expected.has(ref)) {
|
|
3979
|
+
throw new ActionProtocolError(`The report answers for "${ref}", which is not one of the targets it was given.`);
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
const checks = sent.targets.map(({ ref }) => {
|
|
3983
|
+
if (!Object.hasOwn(byRef, ref)) {
|
|
3984
|
+
throw new ActionProtocolError(`The report says nothing about "${ref}", one of the targets it was given.`);
|
|
3985
|
+
}
|
|
3986
|
+
const list = byRef[ref];
|
|
3987
|
+
if (!Array.isArray(list)) {
|
|
3988
|
+
throw new ActionProtocolError(`The checks for "${ref}" must be an array.`);
|
|
3989
|
+
}
|
|
3990
|
+
return {
|
|
3991
|
+
ref,
|
|
3992
|
+
checks: list.map((entry) => {
|
|
3993
|
+
const parsed = (0, validation_1.validationCheckOf)(entry);
|
|
3994
|
+
if ("refusal" in parsed) throw new ActionProtocolError(`The checks for "${ref}": ${parsed.refusal}`);
|
|
3995
|
+
return parsed.check;
|
|
3996
|
+
})
|
|
3997
|
+
};
|
|
3998
|
+
});
|
|
3999
|
+
return {
|
|
4000
|
+
phase: "validate",
|
|
4001
|
+
checks
|
|
4002
|
+
};
|
|
4003
|
+
}
|
|
4004
|
+
if (raw.checks !== void 0) {
|
|
4005
|
+
throw new ActionProtocolError(`A ${sent.phase} report carries an outcome, never checks \u2014 those are the validate phase's answer.`);
|
|
4006
|
+
}
|
|
4007
|
+
return {
|
|
4008
|
+
phase: sent.phase,
|
|
4009
|
+
outcome: parseOutcome(raw.outcome)
|
|
4010
|
+
};
|
|
4011
|
+
}
|
|
4012
|
+
__name(parseActionReport, "parseActionReport");
|
|
4013
|
+
var INVOCATION_KEYS = [
|
|
4014
|
+
"protocol",
|
|
4015
|
+
"action",
|
|
4016
|
+
"phase",
|
|
4017
|
+
"mode",
|
|
4018
|
+
"execution",
|
|
4019
|
+
"targets",
|
|
4020
|
+
"decisions",
|
|
4021
|
+
"restorePoint",
|
|
4022
|
+
"priorResult",
|
|
4023
|
+
"credentials",
|
|
4024
|
+
"limits"
|
|
4025
|
+
];
|
|
4026
|
+
var EVENT_LEVELS = {
|
|
4027
|
+
info: true,
|
|
4028
|
+
warn: true,
|
|
4029
|
+
error: true,
|
|
4030
|
+
success: true
|
|
4031
|
+
};
|
|
4032
|
+
var OUTCOME_STATUSES = {
|
|
4033
|
+
succeeded: true,
|
|
4034
|
+
failed: true,
|
|
4035
|
+
cancelled: true
|
|
4036
|
+
};
|
|
4037
|
+
function parseOutcome(value) {
|
|
4038
|
+
if (value === void 0) {
|
|
4039
|
+
throw new ActionProtocolError("A run or rollback report must carry an outcome.");
|
|
4040
|
+
}
|
|
4041
|
+
const raw = objectOf(value, "An outcome");
|
|
4042
|
+
onlyKeys(raw, [
|
|
4043
|
+
"status",
|
|
4044
|
+
"message",
|
|
4045
|
+
"data"
|
|
4046
|
+
], "An outcome");
|
|
4047
|
+
if (typeof raw.status !== "string" || !Object.hasOwn(OUTCOME_STATUSES, raw.status)) {
|
|
4048
|
+
throw new ActionProtocolError(`An outcome status of "${String(raw.status)}" is not in the vocabulary.`);
|
|
4049
|
+
}
|
|
4050
|
+
const status = raw.status;
|
|
4051
|
+
const message = nonEmpty(raw.message, "An outcome's message");
|
|
4052
|
+
if (raw.data === void 0) return {
|
|
4053
|
+
status,
|
|
4054
|
+
message
|
|
4055
|
+
};
|
|
4056
|
+
return {
|
|
4057
|
+
status,
|
|
4058
|
+
message,
|
|
4059
|
+
data: jsonObject(raw.data, "An outcome's data")
|
|
4060
|
+
};
|
|
4061
|
+
}
|
|
4062
|
+
__name(parseOutcome, "parseOutcome");
|
|
4063
|
+
function parseLimits(value) {
|
|
4064
|
+
const raw = objectOf(value, "The invocation's limits");
|
|
4065
|
+
onlyKeys(raw, [
|
|
4066
|
+
"deadline",
|
|
4067
|
+
"maxEvents",
|
|
4068
|
+
"maxEventBytes",
|
|
4069
|
+
"maxReportBytes",
|
|
4070
|
+
"maxRestorePointBytes"
|
|
4071
|
+
], "The invocation's limits");
|
|
4072
|
+
return {
|
|
4073
|
+
deadline: isoInstant(raw.deadline, "The deadline"),
|
|
4074
|
+
maxEvents: positiveInteger(raw.maxEvents, "maxEvents"),
|
|
4075
|
+
maxEventBytes: positiveInteger(raw.maxEventBytes, "maxEventBytes"),
|
|
4076
|
+
maxReportBytes: positiveInteger(raw.maxReportBytes, "maxReportBytes"),
|
|
4077
|
+
maxRestorePointBytes: positiveInteger(raw.maxRestorePointBytes, "maxRestorePointBytes")
|
|
4078
|
+
};
|
|
4079
|
+
}
|
|
4080
|
+
__name(parseLimits, "parseLimits");
|
|
4081
|
+
function parseDecisions(value) {
|
|
4082
|
+
const raw = objectOf(value, "The invocation's decisions");
|
|
4083
|
+
if (raw.phase === "plan") {
|
|
4084
|
+
onlyKeys(raw, [
|
|
4085
|
+
"phase"
|
|
4086
|
+
], "A plan's decisions");
|
|
4087
|
+
return {
|
|
4088
|
+
phase: "plan"
|
|
4089
|
+
};
|
|
4090
|
+
}
|
|
4091
|
+
if (raw.phase !== "run") {
|
|
4092
|
+
throw new ActionProtocolError(`A decisions phase of "${String(raw.phase)}" is not "plan" or "run".`);
|
|
4093
|
+
}
|
|
4094
|
+
onlyKeys(raw, [
|
|
4095
|
+
"phase",
|
|
4096
|
+
"recorded"
|
|
4097
|
+
], "A run's decisions");
|
|
4098
|
+
if (!Array.isArray(raw.recorded)) {
|
|
4099
|
+
throw new ActionProtocolError("A run's decisions must carry the recorded list, even when it is empty.");
|
|
4100
|
+
}
|
|
4101
|
+
const recorded = raw.recorded.map((entry) => {
|
|
4102
|
+
const decision = objectOf(entry, "A recorded decision");
|
|
4103
|
+
onlyKeys(decision, [
|
|
4104
|
+
"code",
|
|
4105
|
+
"acknowledgedBy",
|
|
4106
|
+
"acknowledgedAt"
|
|
4107
|
+
], "A recorded decision");
|
|
4108
|
+
if (!(0, validation_1.isDecisionCode)(decision.code)) {
|
|
4109
|
+
throw new ActionProtocolError(`A recorded decision of "${String(decision.code)}" is not in the vocabulary.`);
|
|
4110
|
+
}
|
|
4111
|
+
return {
|
|
4112
|
+
code: decision.code,
|
|
4113
|
+
acknowledgedBy: nonEmpty(decision.acknowledgedBy, "Who acknowledged a decision"),
|
|
4114
|
+
acknowledgedAt: isoInstant(decision.acknowledgedAt, "When a decision was acknowledged")
|
|
4115
|
+
};
|
|
4116
|
+
});
|
|
4117
|
+
if (new Set(recorded.map((decision) => decision.code)).size !== recorded.length) {
|
|
4118
|
+
throw new ActionProtocolError("A run's decisions record one code twice.");
|
|
4119
|
+
}
|
|
4120
|
+
return {
|
|
4121
|
+
phase: "run",
|
|
4122
|
+
recorded
|
|
4123
|
+
};
|
|
4124
|
+
}
|
|
4125
|
+
__name(parseDecisions, "parseDecisions");
|
|
4126
|
+
function parseTargets(value) {
|
|
4127
|
+
if (!Array.isArray(value)) {
|
|
4128
|
+
throw new ActionProtocolError("The invocation's targets must be an array.");
|
|
4129
|
+
}
|
|
4130
|
+
const targets = value.map((entry) => {
|
|
4131
|
+
const raw = objectOf(entry, "A target");
|
|
4132
|
+
onlyKeys(raw, [
|
|
4133
|
+
"ref",
|
|
4134
|
+
"parameters"
|
|
4135
|
+
], "A target");
|
|
4136
|
+
return {
|
|
4137
|
+
ref: nonEmpty(raw.ref, "A target's ref"),
|
|
4138
|
+
parameters: jsonObject(raw.parameters, "A target's parameters")
|
|
4139
|
+
};
|
|
4140
|
+
});
|
|
4141
|
+
if (new Set(targets.map((target) => target.ref)).size !== targets.length) {
|
|
4142
|
+
throw new ActionProtocolError("The invocation names one target twice.");
|
|
4143
|
+
}
|
|
4144
|
+
return targets;
|
|
4145
|
+
}
|
|
4146
|
+
__name(parseTargets, "parseTargets");
|
|
4147
|
+
function assertTargetCount(count, max, what) {
|
|
4148
|
+
if (count < 1 || count > max) {
|
|
4149
|
+
throw new ActionProtocolError(max === 1 ? `${what} acts on exactly one target; this invocation names ${count}.` : `${what} checks between 1 and ${max} targets; this invocation names ${count}.`);
|
|
4150
|
+
}
|
|
4151
|
+
}
|
|
4152
|
+
__name(assertTargetCount, "assertTargetCount");
|
|
4153
|
+
function parseTokens(credentials) {
|
|
4154
|
+
onlyKeys(credentials, [
|
|
4155
|
+
"tokens"
|
|
4156
|
+
], "The invocation's credentials");
|
|
4157
|
+
if (!Array.isArray(credentials.tokens)) {
|
|
4158
|
+
throw new ActionProtocolError("The invocation's credentials must carry a tokens array, even when it is empty.");
|
|
4159
|
+
}
|
|
4160
|
+
const tokens2 = credentials.tokens.map((entry) => {
|
|
4161
|
+
const raw = objectOf(entry, "A token");
|
|
4162
|
+
onlyKeys(raw, [
|
|
4163
|
+
"audience",
|
|
4164
|
+
"token",
|
|
4165
|
+
"expiresOn"
|
|
4166
|
+
], "A token");
|
|
4167
|
+
const refusal = actionAudienceRefusal(raw.audience);
|
|
4168
|
+
if (refusal !== null) throw new ActionProtocolError(refusal);
|
|
4169
|
+
return {
|
|
4170
|
+
audience: raw.audience,
|
|
4171
|
+
token: nonEmpty(raw.token, "A token's value"),
|
|
4172
|
+
expiresOn: isoInstant(raw.expiresOn, "A token's expiry")
|
|
4173
|
+
};
|
|
4174
|
+
});
|
|
4175
|
+
if (new Set(tokens2.map((token2) => token2.audience)).size !== tokens2.length) {
|
|
4176
|
+
throw new ActionProtocolError("The invocation carries two tokens for one audience.");
|
|
4177
|
+
}
|
|
4178
|
+
return tokens2;
|
|
4179
|
+
}
|
|
4180
|
+
__name(parseTokens, "parseTokens");
|
|
4181
|
+
var DIGEST_PINNED_IMAGE = /^[a-z0-9][a-z0-9._\-/:]*@sha256:[0-9a-f]{64}$/;
|
|
4182
|
+
function digestPinned(value) {
|
|
4183
|
+
if (typeof value !== "string" || !DIGEST_PINNED_IMAGE.test(value)) {
|
|
4184
|
+
throw new ActionProtocolError(`The action's image "${String(value)}" is not addressed by digest. Nothing on the execution path resolves a tag (docs/128 \xA713.2).`);
|
|
4185
|
+
}
|
|
4186
|
+
return value;
|
|
4187
|
+
}
|
|
4188
|
+
__name(digestPinned, "digestPinned");
|
|
4189
|
+
function objectOf(value, what) {
|
|
4190
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
4191
|
+
throw new ActionProtocolError(`${what} must be an object.`);
|
|
4192
|
+
}
|
|
4193
|
+
return value;
|
|
4194
|
+
}
|
|
4195
|
+
__name(objectOf, "objectOf");
|
|
4196
|
+
function onlyKeys(raw, allowed, what) {
|
|
4197
|
+
for (const key of Object.keys(raw)) {
|
|
4198
|
+
if (!allowed.includes(key)) {
|
|
4199
|
+
throw new ActionProtocolError(`${what} carries "${key}", which this contract does not have.`);
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
__name(onlyKeys, "onlyKeys");
|
|
4204
|
+
function nonEmpty(value, what) {
|
|
4205
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
4206
|
+
throw new ActionProtocolError(`${what} must be a non-empty string.`);
|
|
4207
|
+
}
|
|
4208
|
+
return value;
|
|
4209
|
+
}
|
|
4210
|
+
__name(nonEmpty, "nonEmpty");
|
|
4211
|
+
function positiveInteger(value, what) {
|
|
4212
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
4213
|
+
throw new ActionProtocolError(`The limit "${what}" must be a positive integer.`);
|
|
4214
|
+
}
|
|
4215
|
+
return value;
|
|
4216
|
+
}
|
|
4217
|
+
__name(positiveInteger, "positiveInteger");
|
|
4218
|
+
function isoInstant(value, what) {
|
|
4219
|
+
if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
|
|
4220
|
+
throw new ActionProtocolError(`${what} must be an ISO-8601 instant.`);
|
|
4221
|
+
}
|
|
4222
|
+
return value;
|
|
4223
|
+
}
|
|
4224
|
+
__name(isoInstant, "isoInstant");
|
|
4225
|
+
function nullableObject(value, what) {
|
|
4226
|
+
return value === null ? null : jsonObject(value, what);
|
|
4227
|
+
}
|
|
4228
|
+
__name(nullableObject, "nullableObject");
|
|
4229
|
+
function jsonObject(value, what) {
|
|
4230
|
+
if (!isJsonObject(value)) {
|
|
4231
|
+
throw new ActionProtocolError(`${what} must be a JSON object.`);
|
|
4232
|
+
}
|
|
4233
|
+
return value;
|
|
4234
|
+
}
|
|
4235
|
+
__name(jsonObject, "jsonObject");
|
|
4236
|
+
function isJsonObject(value) {
|
|
4237
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
4238
|
+
const prototype = Object.getPrototypeOf(value);
|
|
4239
|
+
if (prototype !== Object.prototype && prototype !== null) return false;
|
|
4240
|
+
return Object.values(value).every(isJsonValue);
|
|
4241
|
+
}
|
|
4242
|
+
__name(isJsonObject, "isJsonObject");
|
|
4243
|
+
function isJsonValue(value) {
|
|
4244
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
4245
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
4246
|
+
if (Array.isArray(value)) return value.every(isJsonValue);
|
|
4247
|
+
return isJsonObject(value);
|
|
4248
|
+
}
|
|
4249
|
+
__name(isJsonValue, "isJsonValue");
|
|
4250
|
+
}
|
|
4251
|
+
});
|
|
4252
|
+
|
|
4253
|
+
// ../contracts/dist/action-signing.js
|
|
4254
|
+
var require_action_signing = __commonJS({
|
|
4255
|
+
"../contracts/dist/action-signing.js"(exports) {
|
|
4256
|
+
"use strict";
|
|
4257
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4258
|
+
value: true
|
|
4259
|
+
});
|
|
4260
|
+
exports.ACTION_SIGNATURE_REPOSITORY = exports.IMAGE_REVISION_LABEL = exports.IMAGE_SOURCE_LABEL = exports.COSIGN_SIGN_PREDICATE_TYPE = exports.IN_TOTO_STATEMENT_TYPE = exports.DSSE_IN_TOTO_PAYLOAD_TYPE = exports.SIGSTORE_BUNDLE_MEDIA_TYPE = void 0;
|
|
4261
|
+
exports.SIGSTORE_BUNDLE_MEDIA_TYPE = "application/vnd.dev.sigstore.bundle.v0.3+json";
|
|
4262
|
+
exports.DSSE_IN_TOTO_PAYLOAD_TYPE = "application/vnd.in-toto+json";
|
|
4263
|
+
exports.IN_TOTO_STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
|
|
4264
|
+
exports.COSIGN_SIGN_PREDICATE_TYPE = "https://sigstore.dev/cosign/sign/v1";
|
|
4265
|
+
exports.IMAGE_SOURCE_LABEL = "org.opencontainers.image.source";
|
|
4266
|
+
exports.IMAGE_REVISION_LABEL = "org.opencontainers.image.revision";
|
|
4267
|
+
exports.ACTION_SIGNATURE_REPOSITORY = "ghcr.io/runveric/action-signatures";
|
|
4268
|
+
}
|
|
4269
|
+
});
|
|
4270
|
+
|
|
4271
|
+
// ../contracts/dist/oci-media-types.js
|
|
4272
|
+
var require_oci_media_types = __commonJS({
|
|
4273
|
+
"../contracts/dist/oci-media-types.js"(exports) {
|
|
4274
|
+
"use strict";
|
|
4275
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4276
|
+
value: true
|
|
4277
|
+
});
|
|
4278
|
+
exports.IMAGE_MANIFEST_MEDIA_TYPES = exports.MANIFEST_MEDIA_TYPES = exports.DOCKER_MANIFEST_LIST = exports.OCI_IMAGE_INDEX = exports.DOCKER_IMAGE_MANIFEST = exports.OCI_IMAGE_MANIFEST = void 0;
|
|
4279
|
+
exports.OCI_IMAGE_MANIFEST = "application/vnd.oci.image.manifest.v1+json";
|
|
4280
|
+
exports.DOCKER_IMAGE_MANIFEST = "application/vnd.docker.distribution.manifest.v2+json";
|
|
4281
|
+
exports.OCI_IMAGE_INDEX = "application/vnd.oci.image.index.v1+json";
|
|
4282
|
+
exports.DOCKER_MANIFEST_LIST = "application/vnd.docker.distribution.manifest.list.v2+json";
|
|
4283
|
+
exports.MANIFEST_MEDIA_TYPES = [
|
|
4284
|
+
exports.OCI_IMAGE_MANIFEST,
|
|
4285
|
+
exports.DOCKER_IMAGE_MANIFEST,
|
|
4286
|
+
exports.OCI_IMAGE_INDEX,
|
|
4287
|
+
exports.DOCKER_MANIFEST_LIST
|
|
4288
|
+
];
|
|
4289
|
+
exports.IMAGE_MANIFEST_MEDIA_TYPES = [
|
|
4290
|
+
exports.OCI_IMAGE_MANIFEST,
|
|
4291
|
+
exports.DOCKER_IMAGE_MANIFEST
|
|
4292
|
+
];
|
|
4293
|
+
}
|
|
4294
|
+
});
|
|
4295
|
+
|
|
4296
|
+
// ../contracts/dist/action-image.js
|
|
4297
|
+
var require_action_image = __commonJS({
|
|
4298
|
+
"../contracts/dist/action-image.js"(exports) {
|
|
4299
|
+
"use strict";
|
|
4300
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4301
|
+
value: true
|
|
4302
|
+
});
|
|
4303
|
+
exports.MAX_IMAGE_MANIFEST_BYTES = exports.ACTION_RUNTIME_IMAGE = exports.ACTION_RUNTIME_REPOSITORY = void 0;
|
|
4304
|
+
exports.imageConfigDescriptor = imageConfigDescriptor2;
|
|
4305
|
+
exports.actionImageRefusal = actionImageRefusal2;
|
|
4306
|
+
var action_repository_1 = require_action_repository();
|
|
4307
|
+
var action_signing_1 = require_action_signing();
|
|
4308
|
+
var oci_media_types_1 = require_oci_media_types();
|
|
4309
|
+
exports.ACTION_RUNTIME_REPOSITORY = "ghcr.io/runveric/action-runtime";
|
|
4310
|
+
exports.ACTION_RUNTIME_IMAGE = `${exports.ACTION_RUNTIME_REPOSITORY}@sha256:0774c0768c6d1b11e7224ce5bca925549e369fed75eeb412531c1dce10bc8a8a`;
|
|
4311
|
+
exports.MAX_IMAGE_MANIFEST_BYTES = 64 * 1024;
|
|
4312
|
+
var isRecord = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isRecord");
|
|
4313
|
+
function imageConfigDescriptor2(manifest) {
|
|
4314
|
+
if (!isRecord(manifest) || typeof manifest.mediaType !== "string") return null;
|
|
4315
|
+
if (!oci_media_types_1.IMAGE_MANIFEST_MEDIA_TYPES.includes(manifest.mediaType) || manifest.schemaVersion !== 2) return null;
|
|
4316
|
+
const config = manifest.config;
|
|
4317
|
+
if (!isRecord(config) || typeof config.digest !== "string") return null;
|
|
4318
|
+
if (typeof config.size !== "number" || !Number.isInteger(config.size) || config.size < 0) return null;
|
|
4319
|
+
return {
|
|
4320
|
+
digest: config.digest,
|
|
4321
|
+
size: config.size
|
|
4322
|
+
};
|
|
4323
|
+
}
|
|
4324
|
+
__name(imageConfigDescriptor2, "imageConfigDescriptor");
|
|
4325
|
+
function actionImageRefusal2(input) {
|
|
4326
|
+
const { key, image, manifest, config, provenance: provenance2 } = input;
|
|
4327
|
+
const mediaType = isRecord(manifest) ? manifest.mediaType : void 0;
|
|
4328
|
+
if (typeof mediaType !== "string" || !oci_media_types_1.IMAGE_MANIFEST_MEDIA_TYPES.includes(mediaType)) {
|
|
4329
|
+
return `${image} is ${typeof mediaType === "string" ? `a ${mediaType}` : "a manifest of no declared type"}, not one image. An engine action's digest names exactly one linux/amd64 image manifest \u2014 never an index of several.`;
|
|
4330
|
+
}
|
|
4331
|
+
if (imageConfigDescriptor2(manifest) === null) {
|
|
4332
|
+
return `${image} is not an image manifest \u2014 it names no config descriptor (schemaVersion 2, a digest and a size).`;
|
|
4333
|
+
}
|
|
4334
|
+
if (!isRecord(config)) {
|
|
4335
|
+
return `${image}'s config is not a JSON object, so it cannot say what it was built for.`;
|
|
4336
|
+
}
|
|
4337
|
+
const platform = `${String(config.os)}/${String(config.architecture)}`;
|
|
4338
|
+
if (platform !== "linux/amd64") {
|
|
4339
|
+
return `${image} is built for ${platform}; an engine action runs on linux/amd64, which is what Container Apps Jobs run.`;
|
|
4340
|
+
}
|
|
4341
|
+
const labels = isRecord(config.config) && isRecord(config.config.Labels) ? config.config.Labels : {};
|
|
4342
|
+
const expected = (0, action_repository_1.actionSourceRepository)(key);
|
|
4343
|
+
const source = labels[action_signing_1.IMAGE_SOURCE_LABEL];
|
|
4344
|
+
if (source !== expected) {
|
|
4345
|
+
return `the image of action "${key}" says it was built in ${source === void 0 ? "no repository (it carries no " + action_signing_1.IMAGE_SOURCE_LABEL + " label)" : JSON.stringify(source)}, not ${expected}. A signature binds a digest and never a name, so an image built for another action cannot run under this one's manifest.`;
|
|
4346
|
+
}
|
|
4347
|
+
if (provenance2.repository !== expected) {
|
|
4348
|
+
return `the artifact of action "${key}" says it was authored in ${JSON.stringify(provenance2.repository)}, not ${expected}, which its image names.`;
|
|
4349
|
+
}
|
|
4350
|
+
const revision = labels[action_signing_1.IMAGE_REVISION_LABEL];
|
|
4351
|
+
if (revision !== provenance2.commit) {
|
|
4352
|
+
return `the image of action "${key}" says it was built at ${revision === void 0 ? "no commit (it carries no " + action_signing_1.IMAGE_REVISION_LABEL + " label)" : JSON.stringify(revision)}, not ${provenance2.commit}, the commit its artifact was published from. An image and the manifest it runs under come from one commit, so neither can be swapped for another's.`;
|
|
4353
|
+
}
|
|
4354
|
+
return null;
|
|
4355
|
+
}
|
|
4356
|
+
__name(actionImageRefusal2, "actionImageRefusal");
|
|
4357
|
+
}
|
|
4358
|
+
});
|
|
4359
|
+
|
|
2630
4360
|
// ../contracts/dist/runner-pool-presence.js
|
|
2631
4361
|
var require_runner_pool_presence = __commonJS({
|
|
2632
4362
|
"../contracts/dist/runner-pool-presence.js"(exports) {
|
|
@@ -3385,11 +5115,11 @@ var require_workspace_domain = __commonJS({
|
|
|
3385
5115
|
"held_elsewhere"
|
|
3386
5116
|
];
|
|
3387
5117
|
exports.DOMAIN_VERIFICATION_RECORD_PREFIX = "runveric-domain-verification=";
|
|
3388
|
-
function domainVerificationRecord(domain,
|
|
5118
|
+
function domainVerificationRecord(domain, token2) {
|
|
3389
5119
|
return {
|
|
3390
5120
|
name: domain,
|
|
3391
5121
|
type: "TXT",
|
|
3392
|
-
value: `${exports.DOMAIN_VERIFICATION_RECORD_PREFIX}${
|
|
5122
|
+
value: `${exports.DOMAIN_VERIFICATION_RECORD_PREFIX}${token2}`
|
|
3393
5123
|
};
|
|
3394
5124
|
}
|
|
3395
5125
|
__name(domainVerificationRecord, "domainVerificationRecord");
|
|
@@ -3572,9 +5302,16 @@ var require_dist = __commonJS({
|
|
|
3572
5302
|
__exportStar(require_tenant_users(), exports);
|
|
3573
5303
|
__exportStar(require_invitations(), exports);
|
|
3574
5304
|
__exportStar(require_groups(), exports);
|
|
5305
|
+
__exportStar(require_catalog_policy(), exports);
|
|
3575
5306
|
__exportStar(require_catalog(), exports);
|
|
5307
|
+
__exportStar(require_catalog_release(), exports);
|
|
5308
|
+
__exportStar(require_action_repository(), exports);
|
|
5309
|
+
__exportStar(require_catalog_release_spec(), exports);
|
|
5310
|
+
__exportStar(require_catalog_lock(), exports);
|
|
5311
|
+
__exportStar(require_catalog_manifest_placeholders(), exports);
|
|
3576
5312
|
__exportStar(require_change_control(), exports);
|
|
3577
5313
|
__exportStar(require_execution(), exports);
|
|
5314
|
+
__exportStar(require_decisions(), exports);
|
|
3578
5315
|
__exportStar(require_execution_site(), exports);
|
|
3579
5316
|
__exportStar(require_run_placement(), exports);
|
|
3580
5317
|
__exportStar(require_connections(), exports);
|
|
@@ -3585,6 +5322,7 @@ var require_dist = __commonJS({
|
|
|
3585
5322
|
__exportStar(require_scheduling(), exports);
|
|
3586
5323
|
__exportStar(require_operations(), exports);
|
|
3587
5324
|
__exportStar(require_runs(), exports);
|
|
5325
|
+
__exportStar(require_evidence_pack(), exports);
|
|
3588
5326
|
__exportStar(require_package_tier(), exports);
|
|
3589
5327
|
__exportStar(require_package_provenance(), exports);
|
|
3590
5328
|
__exportStar(require_packages(), exports);
|
|
@@ -3594,6 +5332,11 @@ var require_dist = __commonJS({
|
|
|
3594
5332
|
__exportStar(require_runner_install(), exports);
|
|
3595
5333
|
__exportStar(require_authoring_cli(), exports);
|
|
3596
5334
|
__exportStar(require_runners(), exports);
|
|
5335
|
+
__exportStar(require_scoped_credential(), exports);
|
|
5336
|
+
__exportStar(require_action_protocol(), exports);
|
|
5337
|
+
__exportStar(require_action_signing(), exports);
|
|
5338
|
+
__exportStar(require_oci_media_types(), exports);
|
|
5339
|
+
__exportStar(require_action_image(), exports);
|
|
3597
5340
|
__exportStar(require_runner_pool_presence(), exports);
|
|
3598
5341
|
__exportStar(require_routable_target(), exports);
|
|
3599
5342
|
__exportStar(require_runner_pool_routing(), exports);
|
|
@@ -3831,6 +5574,7 @@ var require_manifest_validator = __commonJS({
|
|
|
3831
5574
|
exports.canonicalJson = canonicalJson;
|
|
3832
5575
|
exports.sha256Hex = sha256Hex;
|
|
3833
5576
|
exports.isSafeRelativePath = isSafeRelativePath;
|
|
5577
|
+
exports.isEgressDestination = isEgressDestination;
|
|
3834
5578
|
var node_crypto_1 = __require("crypto");
|
|
3835
5579
|
var node_net_1 = __require("net");
|
|
3836
5580
|
var contracts_1 = require_dist();
|
|
@@ -5095,7 +6839,7 @@ var require_authoring_lint = __commonJS({
|
|
|
5095
6839
|
if (!isRecord(permission) || !Array.isArray(permission.actions)) return;
|
|
5096
6840
|
permission.actions.forEach((action, actionIndex) => {
|
|
5097
6841
|
if (typeof action !== "string" || action.includes("*")) return;
|
|
5098
|
-
if (permissionTokens(action).some((
|
|
6842
|
+
if (permissionTokens(action).some((token2) => haystack.includes(token2))) return;
|
|
5099
6843
|
findings.push({
|
|
5100
6844
|
severity: "warning",
|
|
5101
6845
|
rule: "package/permission-possibly-unused",
|
|
@@ -5242,7 +6986,7 @@ var require_parameters_location = __commonJS({
|
|
|
5242
6986
|
});
|
|
5243
6987
|
exports.resolveParametersFile = resolveParametersFile;
|
|
5244
6988
|
exports.resolveParametersFileOf = resolveParametersFileOf;
|
|
5245
|
-
exports.readManifest =
|
|
6989
|
+
exports.readManifest = readManifest2;
|
|
5246
6990
|
var yaml_1 = __require("yaml");
|
|
5247
6991
|
var manifest_validator_1 = require_manifest_validator();
|
|
5248
6992
|
var package_validation_1 = require_package_validation();
|
|
@@ -5253,7 +6997,7 @@ var require_parameters_location = __commonJS({
|
|
|
5253
6997
|
reason: "manifest_missing"
|
|
5254
6998
|
};
|
|
5255
6999
|
}
|
|
5256
|
-
const manifest =
|
|
7000
|
+
const manifest = readManifest2(manifestSource);
|
|
5257
7001
|
if (!manifest) return {
|
|
5258
7002
|
resolved: false,
|
|
5259
7003
|
reason: "manifest_unparseable"
|
|
@@ -5281,7 +7025,7 @@ var require_parameters_location = __commonJS({
|
|
|
5281
7025
|
return resolveParametersFile(files.get(package_validation_1.MANIFEST_FILE));
|
|
5282
7026
|
}
|
|
5283
7027
|
__name(resolveParametersFileOf, "resolveParametersFileOf");
|
|
5284
|
-
function
|
|
7028
|
+
function readManifest2(source) {
|
|
5285
7029
|
try {
|
|
5286
7030
|
const document = (0, yaml_1.parseDocument)(source);
|
|
5287
7031
|
if (document.errors.length > 0) return void 0;
|
|
@@ -5291,7 +7035,7 @@ var require_parameters_location = __commonJS({
|
|
|
5291
7035
|
return void 0;
|
|
5292
7036
|
}
|
|
5293
7037
|
}
|
|
5294
|
-
__name(
|
|
7038
|
+
__name(readManifest2, "readManifest");
|
|
5295
7039
|
}
|
|
5296
7040
|
});
|
|
5297
7041
|
|
|
@@ -6623,7 +8367,8 @@ var require_dist2 = __commonJS({
|
|
|
6623
8367
|
Object.defineProperty(exports, "__esModule", {
|
|
6624
8368
|
value: true
|
|
6625
8369
|
});
|
|
6626
|
-
exports.
|
|
8370
|
+
exports.reservedImportPaths = exports.generateImportedPackage = exports.authoredEntrypointOf = exports.authoredBodyPath = exports.powerShellDetection = exports.parseProbeOutput = exports.mapDeclaredParameters = exports.POWERSHELL_PARAM_PROBE = exports.pathSegments = exports.offsetToLocation = exports.locate = exports.interpreterForPath = exports.interpreterEntryFileList = exports.PACKAGE_INTERPRETER_EXTENSIONS = exports.PACKAGE_INTERPRETER_LAUNCH = exports.lintActionPackage = exports.sortFindings = exports.mergeFindings = exports.findingFromValidation = exports.resolveParametersFileOf = exports.resolveParametersFile = exports.readManifest = exports.checkActionPackage = exports.validateActionPackage = exports.REQUIRED_PACKAGE_FILES = exports.MANIFEST_FILE = exports.validatePackageResult = exports.PackageProtocolError = exports.normalizePackageRuntime = exports.checkOciImageReference = exports.PackageRuntimeDeclarationError = exports.PARAMETER_CONTROLS = exports.PARAMETER_PROPERTY_FIELDS = exports.PARAMETER_SCHEMA_FIELDS = exports.CREDENTIAL_SCOPE_PATTERN = exports.SPDX_ID_PATTERN = exports.DURATION_PATTERN = exports.SEMVER_PATTERN = exports.PACKAGE_ID_PATTERN = exports.MANIFEST_FIELDS = exports.RISK_LEVELS = exports.PROVIDERS = exports.ACTION_CATEGORIES = exports.isEgressDestination = exports.isSafeRelativePath = exports.sha256Hex = exports.canonicalJson = exports.validateParameterSchema = exports.validateManifest = exports.parseAndValidateManifest = void 0;
|
|
8371
|
+
exports.scaffoldActionPackage = void 0;
|
|
6627
8372
|
var manifest_validator_1 = require_manifest_validator();
|
|
6628
8373
|
Object.defineProperty(exports, "parseAndValidateManifest", {
|
|
6629
8374
|
enumerable: true,
|
|
@@ -6661,6 +8406,12 @@ var require_dist2 = __commonJS({
|
|
|
6661
8406
|
return manifest_validator_1.isSafeRelativePath;
|
|
6662
8407
|
}, "get")
|
|
6663
8408
|
});
|
|
8409
|
+
Object.defineProperty(exports, "isEgressDestination", {
|
|
8410
|
+
enumerable: true,
|
|
8411
|
+
get: /* @__PURE__ */ __name(function() {
|
|
8412
|
+
return manifest_validator_1.isEgressDestination;
|
|
8413
|
+
}, "get")
|
|
8414
|
+
});
|
|
6664
8415
|
var manifest_schema_1 = require_manifest_schema();
|
|
6665
8416
|
Object.defineProperty(exports, "ACTION_CATEGORIES", {
|
|
6666
8417
|
enumerable: true,
|
|
@@ -6941,67 +8692,7 @@ var require_dist2 = __commonJS({
|
|
|
6941
8692
|
});
|
|
6942
8693
|
|
|
6943
8694
|
// package.json
|
|
6944
|
-
var version = "0.
|
|
6945
|
-
|
|
6946
|
-
// src/check.ts
|
|
6947
|
-
var import_action_manifest = __toESM(require_dist2());
|
|
6948
|
-
import { resolve } from "path";
|
|
6949
|
-
|
|
6950
|
-
// src/package-dir.ts
|
|
6951
|
-
import { readdir, readFile, stat } from "fs/promises";
|
|
6952
|
-
import { join, relative, sep } from "path";
|
|
6953
|
-
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
6954
|
-
".git",
|
|
6955
|
-
".runveric",
|
|
6956
|
-
"node_modules",
|
|
6957
|
-
".venv",
|
|
6958
|
-
"__pycache__",
|
|
6959
|
-
".DS_Store"
|
|
6960
|
-
]);
|
|
6961
|
-
var MAX_FILES = 2e3;
|
|
6962
|
-
var MAX_TOTAL_BYTES = 16 * 1024 * 1024;
|
|
6963
|
-
var PackageDirectoryError = class extends Error {
|
|
6964
|
-
static {
|
|
6965
|
-
__name(this, "PackageDirectoryError");
|
|
6966
|
-
}
|
|
6967
|
-
};
|
|
6968
|
-
async function readPackageDirectory(root) {
|
|
6969
|
-
const properties = await stat(root).catch(() => null);
|
|
6970
|
-
if (!properties?.isDirectory()) {
|
|
6971
|
-
throw new PackageDirectoryError(`${root} is not a directory.`);
|
|
6972
|
-
}
|
|
6973
|
-
const files = /* @__PURE__ */ new Map();
|
|
6974
|
-
let totalBytes = 0;
|
|
6975
|
-
const walk = /* @__PURE__ */ __name(async (directory) => {
|
|
6976
|
-
for (const entry of await readdir(directory, {
|
|
6977
|
-
withFileTypes: true
|
|
6978
|
-
})) {
|
|
6979
|
-
if (IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
6980
|
-
const full = join(directory, entry.name);
|
|
6981
|
-
if (entry.isDirectory()) {
|
|
6982
|
-
await walk(full);
|
|
6983
|
-
continue;
|
|
6984
|
-
}
|
|
6985
|
-
if (!entry.isFile()) continue;
|
|
6986
|
-
if (files.size >= MAX_FILES) {
|
|
6987
|
-
throw new PackageDirectoryError(`This directory holds more than ${MAX_FILES} files. That is almost certainly not a single action package \u2014 run this from the package directory itself.`);
|
|
6988
|
-
}
|
|
6989
|
-
const contents = await readFile(full);
|
|
6990
|
-
totalBytes += contents.byteLength;
|
|
6991
|
-
if (totalBytes > MAX_TOTAL_BYTES) {
|
|
6992
|
-
throw new PackageDirectoryError(`This directory holds more than ${MAX_TOTAL_BYTES / (1024 * 1024)}MB. An action package is source, not artifacts \u2014 add build output to .gitignore.`);
|
|
6993
|
-
}
|
|
6994
|
-
files.set(toPackagePath(root, full), contents);
|
|
6995
|
-
}
|
|
6996
|
-
}, "walk");
|
|
6997
|
-
await walk(root);
|
|
6998
|
-
return files;
|
|
6999
|
-
}
|
|
7000
|
-
__name(readPackageDirectory, "readPackageDirectory");
|
|
7001
|
-
function toPackagePath(root, full) {
|
|
7002
|
-
return relative(root, full).split(sep).join("/");
|
|
7003
|
-
}
|
|
7004
|
-
__name(toPackagePath, "toPackagePath");
|
|
8695
|
+
var version = "0.3.0";
|
|
7005
8696
|
|
|
7006
8697
|
// src/report.ts
|
|
7007
8698
|
var useColour = process.stdout.isTTY === true && !process.env.NO_COLOR;
|
|
@@ -7110,10 +8801,719 @@ function printResult(result) {
|
|
|
7110
8801
|
}
|
|
7111
8802
|
__name(printResult, "printResult");
|
|
7112
8803
|
|
|
7113
|
-
// src/
|
|
8804
|
+
// src/catalog/publish-action.ts
|
|
8805
|
+
var import_contracts3 = __toESM(require_dist());
|
|
8806
|
+
import { readFileSync } from "fs";
|
|
8807
|
+
import { dirname, resolve } from "path";
|
|
8808
|
+
import { parse as parseYaml } from "yaml";
|
|
8809
|
+
|
|
8810
|
+
// src/catalog/artifact.ts
|
|
7114
8811
|
var import_contracts = __toESM(require_dist());
|
|
8812
|
+
import { execFileSync } from "child_process";
|
|
8813
|
+
import { createHash } from "crypto";
|
|
8814
|
+
function parseRepository(ref, flag) {
|
|
8815
|
+
if (!import_contracts.ARTIFACT_REPOSITORY_PATTERN.test(ref)) {
|
|
8816
|
+
throw new Error(`${flag} "${ref}" is not a bare registry repository (host[:port]/path, lowercase, no scheme, no tag, no digest). A catalog artifact is addressed by digest; tags are for humans.`);
|
|
8817
|
+
}
|
|
8818
|
+
const cut = ref.indexOf("/");
|
|
8819
|
+
return {
|
|
8820
|
+
ref,
|
|
8821
|
+
host: ref.slice(0, cut),
|
|
8822
|
+
path: ref.slice(cut + 1),
|
|
8823
|
+
namespace: ref.slice(0, ref.lastIndexOf("/"))
|
|
8824
|
+
};
|
|
8825
|
+
}
|
|
8826
|
+
__name(parseRepository, "parseRepository");
|
|
8827
|
+
var isDigest = /* @__PURE__ */ __name((value) => import_contracts.ARTIFACT_DIGEST_PATTERN.test(value), "isDigest");
|
|
8828
|
+
var TAG_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$/;
|
|
8829
|
+
function requireTag(tag, origin2) {
|
|
8830
|
+
if (!TAG_PATTERN.test(tag)) {
|
|
8831
|
+
throw new Error(`${origin2} gives the tag "${tag}", which a registry cannot hold: letters, digits, "_", "." and "-", starting with a letter, digit or "_", at most 128 characters. Pass --tag.`);
|
|
8832
|
+
}
|
|
8833
|
+
}
|
|
8834
|
+
__name(requireTag, "requireTag");
|
|
8835
|
+
var LOOPBACK = /^(localhost|127\.0\.0\.1|\[::1\])(:[0-9]+)?$/;
|
|
8836
|
+
var origin = /* @__PURE__ */ __name((host) => `${LOOPBACK.test(host) ? "http" : "https"}://${host}`, "origin");
|
|
8837
|
+
var sha256 = /* @__PURE__ */ __name((bytes) => `sha256:${createHash("sha256").update(bytes).digest("hex")}`, "sha256");
|
|
8838
|
+
var EMPTY_CONFIG = Buffer.from("{}");
|
|
8839
|
+
var EMPTY_CONFIG_DESCRIPTOR = {
|
|
8840
|
+
mediaType: "application/vnd.oci.empty.v1+json",
|
|
8841
|
+
digest: sha256(EMPTY_CONFIG),
|
|
8842
|
+
size: EMPTY_CONFIG.length
|
|
8843
|
+
};
|
|
8844
|
+
var MANIFEST_ACCEPT = import_contracts.MANIFEST_MEDIA_TYPES.join(", ");
|
|
8845
|
+
function provenance(flags, dir) {
|
|
8846
|
+
const git = /* @__PURE__ */ __name((...args) => {
|
|
8847
|
+
try {
|
|
8848
|
+
return execFileSync("git", [
|
|
8849
|
+
"-C",
|
|
8850
|
+
dir,
|
|
8851
|
+
...args
|
|
8852
|
+
], {
|
|
8853
|
+
encoding: "utf8",
|
|
8854
|
+
stdio: [
|
|
8855
|
+
"ignore",
|
|
8856
|
+
"pipe",
|
|
8857
|
+
"ignore"
|
|
8858
|
+
]
|
|
8859
|
+
}).trim();
|
|
8860
|
+
} catch {
|
|
8861
|
+
return "";
|
|
8862
|
+
}
|
|
8863
|
+
}, "git");
|
|
8864
|
+
const actions = process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}` : "";
|
|
8865
|
+
const repository = flags.repository ?? (actions || git("remote", "get-url", "origin"));
|
|
8866
|
+
const commit = flags.commit ?? (process.env.GITHUB_SHA || git("rev-parse", "HEAD"));
|
|
8867
|
+
if (!repository || !commit) {
|
|
8868
|
+
throw new Error("Provenance is required and could not be determined. Pass --repository <url> and --commit <sha> (in GitHub Actions, GITHUB_REPOSITORY and GITHUB_SHA supply them).");
|
|
8869
|
+
}
|
|
8870
|
+
return {
|
|
8871
|
+
repository,
|
|
8872
|
+
commit,
|
|
8873
|
+
publishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8874
|
+
};
|
|
8875
|
+
}
|
|
8876
|
+
__name(provenance, "provenance");
|
|
8877
|
+
function buildArtifact(document) {
|
|
8878
|
+
const documentBytes = Buffer.from(`${JSON.stringify(document, null, 2)}
|
|
8879
|
+
`);
|
|
8880
|
+
const manifest = {
|
|
8881
|
+
schemaVersion: 2,
|
|
8882
|
+
mediaType: import_contracts.OCI_IMAGE_MANIFEST,
|
|
8883
|
+
artifactType: import_contracts.CATALOG_ARTIFACT_MEDIA_TYPE,
|
|
8884
|
+
config: EMPTY_CONFIG_DESCRIPTOR,
|
|
8885
|
+
layers: [
|
|
8886
|
+
{
|
|
8887
|
+
mediaType: import_contracts.CATALOG_ARTIFACT_MEDIA_TYPE,
|
|
8888
|
+
digest: sha256(documentBytes),
|
|
8889
|
+
size: documentBytes.length
|
|
8890
|
+
}
|
|
8891
|
+
],
|
|
8892
|
+
/*
|
|
8893
|
+
* ★ SOURCE AND REVISION ARE FOR THE REGISTRY, NOT THE RESOLVER (docs/127 A.1). GHCR connects
|
|
8894
|
+
* a package to its repository through `org.opencontainers.image.source`, and that link is what
|
|
8895
|
+
* lets the repository's workflow token keep publishing to the package its first run created.
|
|
8896
|
+
* The resolver reads none of these — the document's own provenance is the record — so an
|
|
8897
|
+
* artifact published without them still resolves.
|
|
8898
|
+
*/
|
|
8899
|
+
annotations: {
|
|
8900
|
+
"org.opencontainers.image.created": document.provenance.publishedAt,
|
|
8901
|
+
"org.opencontainers.image.source": document.provenance.repository,
|
|
8902
|
+
"org.opencontainers.image.revision": document.provenance.commit
|
|
8903
|
+
}
|
|
8904
|
+
};
|
|
8905
|
+
const manifestBytes = Buffer.from(JSON.stringify(manifest));
|
|
8906
|
+
return {
|
|
8907
|
+
documentBytes,
|
|
8908
|
+
manifestBytes,
|
|
8909
|
+
manifestDigest: sha256(manifestBytes)
|
|
8910
|
+
};
|
|
8911
|
+
}
|
|
8912
|
+
__name(buildArtifact, "buildArtifact");
|
|
8913
|
+
function basicCredential() {
|
|
8914
|
+
const pat = process.env.GHCR_TOKEN;
|
|
8915
|
+
if (pat) return Buffer.from(`x:${pat}`).toString("base64");
|
|
8916
|
+
const workflow = process.env.GITHUB_TOKEN;
|
|
8917
|
+
if (workflow) return Buffer.from(`${process.env.GITHUB_ACTOR || "x"}:${workflow}`).toString("base64");
|
|
8918
|
+
return void 0;
|
|
8919
|
+
}
|
|
8920
|
+
__name(basicCredential, "basicCredential");
|
|
8921
|
+
var tokens = /* @__PURE__ */ new Map();
|
|
8922
|
+
async function token(repository, challenge, access) {
|
|
8923
|
+
const field = /* @__PURE__ */ __name((name) => new RegExp(`${name}="([^"]*)"`).exec(challenge ?? "")?.[1], "field");
|
|
8924
|
+
const realm = field("realm");
|
|
8925
|
+
if (!realm) throw new Error(`${repository.host} asked for credentials without a Bearer challenge.`);
|
|
8926
|
+
const url = new URL(realm);
|
|
8927
|
+
const service = field("service");
|
|
8928
|
+
if (service) url.searchParams.set("service", service);
|
|
8929
|
+
url.searchParams.set("scope", `repository:${repository.path}:${access}`);
|
|
8930
|
+
const credential = basicCredential();
|
|
8931
|
+
const answer = await fetch(url, {
|
|
8932
|
+
headers: credential ? {
|
|
8933
|
+
Authorization: `Basic ${credential}`
|
|
8934
|
+
} : {}
|
|
8935
|
+
});
|
|
8936
|
+
if (!answer.ok) {
|
|
8937
|
+
throw new Error(`${repository.host} refused a ${access === "pull" ? "pull" : "push"} token for ${repository.path}: HTTP ${answer.status}` + (credential || access === "pull" ? "." : " \u2014 set GHCR_TOKEN to a token with write:packages, or in a workflow GITHUB_TOKEN with packages: write."));
|
|
8938
|
+
}
|
|
8939
|
+
const body = await answer.json();
|
|
8940
|
+
const value = body.token ?? body.access_token ?? "";
|
|
8941
|
+
tokens.set(`${repository.path} ${access}`, value);
|
|
8942
|
+
return value;
|
|
8943
|
+
}
|
|
8944
|
+
__name(token, "token");
|
|
8945
|
+
async function call(repository, method, path, { headers = {}, body, access = "pull,push" } = {}) {
|
|
8946
|
+
const url = path.startsWith("http") ? path : `${origin(repository.host)}/v2/${repository.path}/${path}`;
|
|
8947
|
+
const send = /* @__PURE__ */ __name((bearer) => fetch(url, {
|
|
8948
|
+
method,
|
|
8949
|
+
body,
|
|
8950
|
+
redirect: "follow",
|
|
8951
|
+
headers: {
|
|
8952
|
+
...headers,
|
|
8953
|
+
...bearer ? {
|
|
8954
|
+
Authorization: `Bearer ${bearer}`
|
|
8955
|
+
} : {}
|
|
8956
|
+
}
|
|
8957
|
+
}), "send");
|
|
8958
|
+
let answer = await send(tokens.get(`${repository.path} ${access}`));
|
|
8959
|
+
if (answer.status === 401) answer = await send(await token(repository, answer.headers.get("www-authenticate"), access));
|
|
8960
|
+
return answer;
|
|
8961
|
+
}
|
|
8962
|
+
__name(call, "call");
|
|
8963
|
+
async function pushBlob(repository, bytes) {
|
|
8964
|
+
const digest = sha256(bytes);
|
|
8965
|
+
if ((await call(repository, "HEAD", `blobs/${digest}`)).ok) return digest;
|
|
8966
|
+
const started = await call(repository, "POST", "blobs/uploads/");
|
|
8967
|
+
if (started.status !== 202) throw new Error(`${repository.host} refused an upload: HTTP ${started.status}`);
|
|
8968
|
+
const location = new URL(started.headers.get("location") ?? "", `${origin(repository.host)}/`);
|
|
8969
|
+
location.searchParams.set("digest", digest);
|
|
8970
|
+
const put = await call(repository, "PUT", location.toString(), {
|
|
8971
|
+
headers: {
|
|
8972
|
+
"Content-Type": "application/octet-stream",
|
|
8973
|
+
"Content-Length": String(bytes.length)
|
|
8974
|
+
},
|
|
8975
|
+
body: bytes
|
|
8976
|
+
});
|
|
8977
|
+
if (put.status !== 201) throw new Error(`${repository.host} rejected blob ${digest}: HTTP ${put.status} ${await put.text()}`);
|
|
8978
|
+
return digest;
|
|
8979
|
+
}
|
|
8980
|
+
__name(pushBlob, "pushBlob");
|
|
8981
|
+
async function manifestExists(repository, digest) {
|
|
8982
|
+
const answer = await call(repository, "HEAD", `manifests/${digest}`, {
|
|
8983
|
+
headers: {
|
|
8984
|
+
Accept: import_contracts.OCI_IMAGE_MANIFEST
|
|
8985
|
+
}
|
|
8986
|
+
});
|
|
8987
|
+
return answer.ok;
|
|
8988
|
+
}
|
|
8989
|
+
__name(manifestExists, "manifestExists");
|
|
8990
|
+
async function readVerified(repository, kind, digest, size = null) {
|
|
8991
|
+
if (!isDigest(digest)) throw new Error(`"${digest}" is not a digest, so ${repository.ref} cannot be asked for it.`);
|
|
8992
|
+
const answer = await call(repository, "GET", `${kind}/${digest}`, {
|
|
8993
|
+
access: "pull",
|
|
8994
|
+
headers: kind === "manifests" ? {
|
|
8995
|
+
Accept: MANIFEST_ACCEPT
|
|
8996
|
+
} : {}
|
|
8997
|
+
});
|
|
8998
|
+
if (!answer.ok) {
|
|
8999
|
+
throw new Error(`${repository.ref}@${digest} could not be read: HTTP ${answer.status}${answer.status === 404 ? " \u2014 it is not in the registry" : ""}.`);
|
|
9000
|
+
}
|
|
9001
|
+
const bytes = Buffer.from(await answer.arrayBuffer());
|
|
9002
|
+
const served = sha256(bytes);
|
|
9003
|
+
if (served !== digest || size !== null && bytes.length !== size) {
|
|
9004
|
+
throw new Error(`${repository.host} served ${bytes.length} bytes hashing to ${served} for ${repository.ref}@${digest}${size === null ? "" : ` (${size} bytes named)`}. Refused: a digest this command did not compute is not a pin.`);
|
|
9005
|
+
}
|
|
9006
|
+
return bytes;
|
|
9007
|
+
}
|
|
9008
|
+
__name(readVerified, "readVerified");
|
|
9009
|
+
var readManifest = /* @__PURE__ */ __name((repository, digest) => readVerified(repository, "manifests", digest), "readManifest");
|
|
9010
|
+
var readBlob = /* @__PURE__ */ __name((repository, digest, size) => readVerified(repository, "blobs", digest, size), "readBlob");
|
|
9011
|
+
function parseJsonBytes(bytes, what) {
|
|
9012
|
+
try {
|
|
9013
|
+
return JSON.parse(new TextDecoder("utf-8", {
|
|
9014
|
+
fatal: true
|
|
9015
|
+
}).decode(bytes));
|
|
9016
|
+
} catch (cause) {
|
|
9017
|
+
throw new Error(`${what} is not UTF-8 JSON. (${cause instanceof Error ? cause.message : String(cause)})`);
|
|
9018
|
+
}
|
|
9019
|
+
}
|
|
9020
|
+
__name(parseJsonBytes, "parseJsonBytes");
|
|
9021
|
+
async function readArtifactDocument(repository, digest) {
|
|
9022
|
+
const what = `${repository.ref}@${digest}`;
|
|
9023
|
+
const manifest = parseJsonBytes(await readManifest(repository, digest), `the manifest of ${what}`);
|
|
9024
|
+
const layers = Array.isArray(manifest?.layers) ? manifest.layers : [];
|
|
9025
|
+
const layer = layers[0];
|
|
9026
|
+
if (manifest?.mediaType !== import_contracts.OCI_IMAGE_MANIFEST || layers.length !== 1 || layer?.mediaType !== import_contracts.CATALOG_ARTIFACT_MEDIA_TYPE || typeof layer.digest !== "string" || typeof layer.size !== "number") {
|
|
9027
|
+
throw new Error(`${what} is not a catalog artifact: expected one OCI manifest with exactly one ${import_contracts.CATALOG_ARTIFACT_MEDIA_TYPE} layer.`);
|
|
9028
|
+
}
|
|
9029
|
+
return parseJsonBytes(await readBlob(repository, layer.digest, layer.size), `the document of ${what}`);
|
|
9030
|
+
}
|
|
9031
|
+
__name(readArtifactDocument, "readArtifactDocument");
|
|
9032
|
+
async function pushArtifact(repository, artifact, tag) {
|
|
9033
|
+
await pushBlob(repository, EMPTY_CONFIG);
|
|
9034
|
+
await pushBlob(repository, artifact.documentBytes);
|
|
9035
|
+
for (const reference of [
|
|
9036
|
+
artifact.manifestDigest,
|
|
9037
|
+
tag
|
|
9038
|
+
]) {
|
|
9039
|
+
const put = await call(repository, "PUT", `manifests/${reference}`, {
|
|
9040
|
+
headers: {
|
|
9041
|
+
"Content-Type": import_contracts.OCI_IMAGE_MANIFEST
|
|
9042
|
+
},
|
|
9043
|
+
body: artifact.manifestBytes
|
|
9044
|
+
});
|
|
9045
|
+
if (put.status !== 201) {
|
|
9046
|
+
throw new Error(`${repository.host} rejected the manifest (${reference}): HTTP ${put.status} ${await put.text()}`);
|
|
9047
|
+
}
|
|
9048
|
+
}
|
|
9049
|
+
const asked = await call(repository, "HEAD", `manifests/${tag}`, {
|
|
9050
|
+
headers: {
|
|
9051
|
+
Accept: import_contracts.OCI_IMAGE_MANIFEST
|
|
9052
|
+
}
|
|
9053
|
+
});
|
|
9054
|
+
const reported = asked.headers.get("docker-content-digest");
|
|
9055
|
+
if (!asked.ok || !reported) {
|
|
9056
|
+
throw new Error(`${repository.host} could not say what :${tag} resolves to (HTTP ${asked.status}).`);
|
|
9057
|
+
}
|
|
9058
|
+
if (reported !== artifact.manifestDigest) {
|
|
9059
|
+
throw new Error(`${repository.host} reports :${tag} as ${reported}, but the bytes sent hash to ${artifact.manifestDigest}. Something re-encoded the manifest; nothing printed here would name what is stored. Refusing to report a digest.`);
|
|
9060
|
+
}
|
|
9061
|
+
return reported;
|
|
9062
|
+
}
|
|
9063
|
+
__name(pushArtifact, "pushArtifact");
|
|
9064
|
+
var defaultTag = /* @__PURE__ */ __name(() => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\..*$/, "").toLowerCase(), "defaultTag");
|
|
9065
|
+
|
|
9066
|
+
// src/catalog/flags.ts
|
|
9067
|
+
function parseCatalogFlags(argv, known, repeated = []) {
|
|
9068
|
+
const single2 = {};
|
|
9069
|
+
const many = {};
|
|
9070
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
9071
|
+
const argument = argv[index];
|
|
9072
|
+
const separator = argument.indexOf("=");
|
|
9073
|
+
const name = separator === -1 ? argument.slice(2) : argument.slice(2, separator);
|
|
9074
|
+
if (!argument.startsWith("--") || !known.includes(name)) throw new Error(`Unknown argument: ${argument}`);
|
|
9075
|
+
let value;
|
|
9076
|
+
if (separator !== -1) {
|
|
9077
|
+
value = argument.slice(separator + 1);
|
|
9078
|
+
} else {
|
|
9079
|
+
value = argv[index + 1];
|
|
9080
|
+
index += 1;
|
|
9081
|
+
}
|
|
9082
|
+
if (value === void 0 || value === "" || value.startsWith("--")) throw new Error(`--${name} needs a value.`);
|
|
9083
|
+
if (repeated.includes(name)) (many[name] ??= []).push(value);
|
|
9084
|
+
else single2[name] = value;
|
|
9085
|
+
}
|
|
9086
|
+
return {
|
|
9087
|
+
single: single2,
|
|
9088
|
+
many
|
|
9089
|
+
};
|
|
9090
|
+
}
|
|
9091
|
+
__name(parseCatalogFlags, "parseCatalogFlags");
|
|
9092
|
+
|
|
9093
|
+
// src/catalog/image.ts
|
|
9094
|
+
var import_contracts2 = __toESM(require_dist());
|
|
9095
|
+
function parseImageReference(value, origin2) {
|
|
9096
|
+
const at = value.lastIndexOf("@");
|
|
9097
|
+
const digest = at === -1 ? "" : value.slice(at + 1);
|
|
9098
|
+
if (at === -1 || !isDigest(digest)) {
|
|
9099
|
+
throw new Error(`${origin2} "${value}" is not <repository>@<digest>. An engine action's image is named by its digest \u2014 sha256: and 64 lowercase hex \u2014 never by a tag, which a registry can repoint.`);
|
|
9100
|
+
}
|
|
9101
|
+
const repository = parseRepository(value.slice(0, at), origin2);
|
|
9102
|
+
return {
|
|
9103
|
+
repository,
|
|
9104
|
+
digest,
|
|
9105
|
+
ref: `${repository.ref}@${digest}`
|
|
9106
|
+
};
|
|
9107
|
+
}
|
|
9108
|
+
__name(parseImageReference, "parseImageReference");
|
|
9109
|
+
async function imageRefusal(image, key, provenance2) {
|
|
9110
|
+
const manifestBytes = await readManifest(image.repository, image.digest);
|
|
9111
|
+
if (manifestBytes.length > import_contracts2.MAX_IMAGE_MANIFEST_BYTES) {
|
|
9112
|
+
return `the image manifest of ${image.ref} is ${manifestBytes.length} bytes; the limit is ${import_contracts2.MAX_IMAGE_MANIFEST_BYTES}.`;
|
|
9113
|
+
}
|
|
9114
|
+
const manifest = parseJsonBytes(manifestBytes, `the image manifest of ${image.ref}`);
|
|
9115
|
+
const descriptor = (0, import_contracts2.imageConfigDescriptor)(manifest);
|
|
9116
|
+
const config = descriptor === null ? null : parseJsonBytes(await readBlob(image.repository, descriptor.digest, descriptor.size), `the image config of ${image.ref}`);
|
|
9117
|
+
return (0, import_contracts2.actionImageRefusal)({
|
|
9118
|
+
key,
|
|
9119
|
+
image: image.ref,
|
|
9120
|
+
manifest,
|
|
9121
|
+
config,
|
|
9122
|
+
provenance: provenance2
|
|
9123
|
+
});
|
|
9124
|
+
}
|
|
9125
|
+
__name(imageRefusal, "imageRefusal");
|
|
9126
|
+
|
|
9127
|
+
// src/catalog/publish-action.ts
|
|
9128
|
+
var PUBLISH_ACTION_USAGE = "runveric catalog publish-action --manifest <file.yaml> --to <registry>/<namespace>/<repository>\n [--image <repository>@<digest>] [--tag <tag>]\n [--repository <source url>] [--commit <sha>]";
|
|
9129
|
+
var isMapping = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isMapping");
|
|
9130
|
+
async function runPublishAction(argv) {
|
|
9131
|
+
const { single: flags } = parseCatalogFlags(argv, [
|
|
9132
|
+
"manifest",
|
|
9133
|
+
"to",
|
|
9134
|
+
"image",
|
|
9135
|
+
"tag",
|
|
9136
|
+
"repository",
|
|
9137
|
+
"commit"
|
|
9138
|
+
]);
|
|
9139
|
+
if (!flags.manifest || !flags.to) throw new Error("--manifest and --to are required.");
|
|
9140
|
+
const repository = parseRepository(flags.to, "--to");
|
|
9141
|
+
const image = flags.image === void 0 ? null : parseImageReference(flags.image, "--image");
|
|
9142
|
+
if (image !== null && image.repository.ref !== repository.ref) {
|
|
9143
|
+
throw new Error(`--image ${image.ref} is outside the artifact's repository ${repository.ref}. An engine action's image lives in its own package, so its artifact names it by digest alone (docs/128 \xA717.2 D2) \u2014 push the image to ${repository.ref}.`);
|
|
9144
|
+
}
|
|
9145
|
+
const path = resolve(flags.manifest);
|
|
9146
|
+
const manifest = readFileSync(path, "utf8");
|
|
9147
|
+
let parsed;
|
|
9148
|
+
try {
|
|
9149
|
+
parsed = parseYaml(manifest);
|
|
9150
|
+
} catch (cause) {
|
|
9151
|
+
throw new Error(`${flags.manifest} is not valid YAML. (${cause instanceof Error ? cause.message : String(cause)})`);
|
|
9152
|
+
}
|
|
9153
|
+
const fields = isMapping(parsed) ? parsed : {};
|
|
9154
|
+
const key = typeof fields.key === "string" && fields.key.length > 0 ? fields.key : null;
|
|
9155
|
+
if (key === null) throw new Error(`${flags.manifest} declares no top-level \`key:\` \u2014 is it a catalog manifest?`);
|
|
9156
|
+
const misnamed = (0, import_contracts3.actionPackageRefusal)(key, repository.ref);
|
|
9157
|
+
if (misnamed !== null) throw new Error(`--to: ${misnamed}`);
|
|
9158
|
+
const executes = isMapping(fields.execution);
|
|
9159
|
+
if (executes && image === null) {
|
|
9160
|
+
throw new Error(`${flags.manifest} declares execution, so it is an engine action: publish it with --image <repository>@<digest>, the image its workflow built. Without one the server refuses it at plan.`);
|
|
9161
|
+
}
|
|
9162
|
+
if (!executes && image !== null) {
|
|
9163
|
+
throw new Error(`${flags.manifest} declares no execution, so nothing would ever run --image ${image.ref}. A built-in action is published without one.`);
|
|
9164
|
+
}
|
|
9165
|
+
let tag;
|
|
9166
|
+
if (fields.declares === "definition+version") {
|
|
9167
|
+
const version2 = typeof fields.version === "string" ? fields.version : null;
|
|
9168
|
+
if (version2 === null) throw new Error(`${flags.manifest} declares "definition+version" and no version:.`);
|
|
9169
|
+
if (flags.tag !== void 0 && flags.tag !== version2) {
|
|
9170
|
+
throw new Error(`${flags.manifest} declares version ${version2}, and the tag is ${flags.tag}. Tag the commit ${version2}, or change version: in ${flags.manifest} first.`);
|
|
9171
|
+
}
|
|
9172
|
+
tag = version2;
|
|
9173
|
+
} else {
|
|
9174
|
+
tag = flags.tag ?? defaultTag();
|
|
9175
|
+
}
|
|
9176
|
+
requireTag(tag, flags.tag !== void 0 ? "--tag" : `${flags.manifest}'s version`);
|
|
9177
|
+
const recorded = provenance(flags, dirname(path));
|
|
9178
|
+
if (image !== null) {
|
|
9179
|
+
const refusal = await imageRefusal(image, key, recorded);
|
|
9180
|
+
if (refusal !== null) throw new Error(`--image: ${refusal}`);
|
|
9181
|
+
}
|
|
9182
|
+
const artifact = buildArtifact(image === null ? {
|
|
9183
|
+
schemaVersion: import_contracts3.CATALOG_ACTION_ARTIFACT_SCHEMA_V1,
|
|
9184
|
+
manifest,
|
|
9185
|
+
provenance: recorded
|
|
9186
|
+
} : {
|
|
9187
|
+
schemaVersion: import_contracts3.CATALOG_ACTION_ARTIFACT_SCHEMA_V2,
|
|
9188
|
+
manifest,
|
|
9189
|
+
provenance: recorded,
|
|
9190
|
+
image: {
|
|
9191
|
+
digest: image.digest
|
|
9192
|
+
}
|
|
9193
|
+
});
|
|
9194
|
+
const digest = await pushArtifact(repository, artifact, tag);
|
|
9195
|
+
process.stderr.write(`\u2713 ${key} published to ${repository.ref}:${tag}${image === null ? "" : `, naming the image ${image.digest}`}
|
|
9196
|
+
`);
|
|
9197
|
+
process.stderr.write(` the registry reports ${digest} \u2014 the digest a release must name.
|
|
9198
|
+
`);
|
|
9199
|
+
process.stdout.write(`${key}=${repository.ref}@${digest}
|
|
9200
|
+
`);
|
|
9201
|
+
}
|
|
9202
|
+
__name(runPublishAction, "runPublishAction");
|
|
9203
|
+
|
|
9204
|
+
// src/catalog/publish-release.ts
|
|
9205
|
+
var import_contracts4 = __toESM(require_dist());
|
|
9206
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
9207
|
+
import { parse as parseYaml2 } from "yaml";
|
|
9208
|
+
var PUBLISH_RELEASE_USAGE = "runveric catalog publish-release (--from <release.yaml> | --action <key>=<repository>@<digest> \u2026)\n --to <registry>/<namespace>/<repository> [--version <label>]\n [--tag <tag>] [--repository <source url>] [--commit <sha>]";
|
|
9209
|
+
function entriesFromFlags(values) {
|
|
9210
|
+
return values.map((value) => {
|
|
9211
|
+
const match = /^([^=]+)=([^@]+)@(.+)$/.exec(value);
|
|
9212
|
+
if (!match) throw new Error(`--action "${value}" is not <key>=<repository>@<digest>.`);
|
|
9213
|
+
const [, key = "", artifactRef = "", digest = ""] = match;
|
|
9214
|
+
return {
|
|
9215
|
+
key,
|
|
9216
|
+
artifactRef,
|
|
9217
|
+
digest,
|
|
9218
|
+
label: `--action ${key}`
|
|
9219
|
+
};
|
|
9220
|
+
});
|
|
9221
|
+
}
|
|
9222
|
+
__name(entriesFromFlags, "entriesFromFlags");
|
|
9223
|
+
function specFromFile(path) {
|
|
9224
|
+
let value;
|
|
9225
|
+
try {
|
|
9226
|
+
value = parseYaml2(readFileSync2(path, "utf8"));
|
|
9227
|
+
} catch (cause) {
|
|
9228
|
+
throw new Error(`--from ${path} could not be read as YAML. (${cause instanceof Error ? cause.message : String(cause)})`);
|
|
9229
|
+
}
|
|
9230
|
+
const spec = (0, import_contracts4.parseCatalogReleaseSpec)(value);
|
|
9231
|
+
return {
|
|
9232
|
+
version: spec.version,
|
|
9233
|
+
entries: spec.actions.map((action) => ({
|
|
9234
|
+
...action,
|
|
9235
|
+
label: `${path}'s ${action.key}`
|
|
9236
|
+
}))
|
|
9237
|
+
};
|
|
9238
|
+
}
|
|
9239
|
+
__name(specFromFile, "specFromFile");
|
|
9240
|
+
async function runPublishRelease(argv) {
|
|
9241
|
+
const { single: flags, many } = parseCatalogFlags(argv, [
|
|
9242
|
+
"to",
|
|
9243
|
+
"version",
|
|
9244
|
+
"action",
|
|
9245
|
+
"from",
|
|
9246
|
+
"tag",
|
|
9247
|
+
"repository",
|
|
9248
|
+
"commit"
|
|
9249
|
+
], [
|
|
9250
|
+
"action"
|
|
9251
|
+
]);
|
|
9252
|
+
const actionFlags = many.action ?? [];
|
|
9253
|
+
if (flags.from !== void 0 && actionFlags.length > 0) {
|
|
9254
|
+
throw new Error("--from and --action are alternatives: name the release's actions in release.yaml or as flags, not both.");
|
|
9255
|
+
}
|
|
9256
|
+
if (flags.from !== void 0 && flags.version !== void 0) {
|
|
9257
|
+
throw new Error("--version is read from release.yaml when --from is given; pass one or the other.");
|
|
9258
|
+
}
|
|
9259
|
+
if (!flags.to || flags.from === void 0 && (!flags.version || actionFlags.length === 0)) {
|
|
9260
|
+
throw new Error("--to and either --from, or --version and at least one --action, are required.");
|
|
9261
|
+
}
|
|
9262
|
+
const release = parseRepository(flags.to, "--to");
|
|
9263
|
+
const { version: version2, entries } = flags.from !== void 0 ? specFromFile(flags.from) : {
|
|
9264
|
+
version: flags.version,
|
|
9265
|
+
entries: entriesFromFlags(actionFlags)
|
|
9266
|
+
};
|
|
9267
|
+
const tag = flags.tag ?? (flags.from !== void 0 ? version2 : defaultTag());
|
|
9268
|
+
requireTag(tag, flags.tag !== void 0 ? "--tag" : `${flags.from}'s version`);
|
|
9269
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9270
|
+
const actions = entries.map((entry) => {
|
|
9271
|
+
if (seen.has(entry.key)) throw new Error(`${entry.label} names "${entry.key}" twice.`);
|
|
9272
|
+
seen.add(entry.key);
|
|
9273
|
+
const repository = parseRepository(entry.artifactRef, entry.label);
|
|
9274
|
+
if (repository.namespace !== release.namespace) {
|
|
9275
|
+
throw new Error(`${entry.label} lives in "${repository.namespace}", outside the release's namespace "${release.namespace}". The server refuses a release that sends it to another host or org.`);
|
|
9276
|
+
}
|
|
9277
|
+
if (!isDigest(entry.digest)) {
|
|
9278
|
+
throw new Error(`${entry.label}: "${entry.digest}" is not a digest. A release names artifacts by digest only.`);
|
|
9279
|
+
}
|
|
9280
|
+
const misnamed = (0, import_contracts4.actionPackageRefusal)(entry.key, entry.artifactRef);
|
|
9281
|
+
if (misnamed !== null) throw new Error(`${entry.label}: ${misnamed}`);
|
|
9282
|
+
return {
|
|
9283
|
+
...entry,
|
|
9284
|
+
repository
|
|
9285
|
+
};
|
|
9286
|
+
});
|
|
9287
|
+
for (const action of actions) {
|
|
9288
|
+
if (!await manifestExists(action.repository, action.digest)) {
|
|
9289
|
+
throw new Error(`${action.artifactRef}@${action.digest} (${action.key}) is not in the registry.`);
|
|
9290
|
+
}
|
|
9291
|
+
}
|
|
9292
|
+
const artifact = buildArtifact({
|
|
9293
|
+
schemaVersion: import_contracts4.CATALOG_RELEASE_SCHEMA_V1,
|
|
9294
|
+
version: version2,
|
|
9295
|
+
provenance: provenance(flags, process.cwd()),
|
|
9296
|
+
actions: actions.map(({ key, artifactRef, digest: digest2 }) => ({
|
|
9297
|
+
key,
|
|
9298
|
+
artifactRef,
|
|
9299
|
+
digest: digest2
|
|
9300
|
+
}))
|
|
9301
|
+
});
|
|
9302
|
+
const digest = await pushArtifact(release, artifact, tag);
|
|
9303
|
+
process.stderr.write(`\u2713 catalog release ${version2} (${actions.length} action artifact(s)) published to ${release.ref}:${tag}
|
|
9304
|
+
`);
|
|
9305
|
+
process.stderr.write(` the registry reports ${digest} \u2014 the digest to plan and pin.
|
|
9306
|
+
`);
|
|
9307
|
+
process.stdout.write(`${release.ref}@${digest}
|
|
9308
|
+
`);
|
|
9309
|
+
}
|
|
9310
|
+
__name(runPublishRelease, "runPublishRelease");
|
|
9311
|
+
|
|
9312
|
+
// src/catalog/verify-image.ts
|
|
9313
|
+
var import_contracts5 = __toESM(require_dist());
|
|
9314
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
9315
|
+
import { parse as parseYaml3 } from "yaml";
|
|
9316
|
+
var VERIFY_IMAGE_USAGE = "runveric catalog verify-image (<repository>@<digest> --key <action key> --commit <sha> | --from <release.yaml>)";
|
|
9317
|
+
var COMMIT = /^[0-9a-f]{40}$/;
|
|
9318
|
+
var isMapping2 = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isMapping");
|
|
9319
|
+
function single(value, flags) {
|
|
9320
|
+
if (!flags.key || !flags.commit) throw new Error(`verify-image ${value} needs --key <action key> and --commit <sha>.`);
|
|
9321
|
+
if (!COMMIT.test(flags.commit)) throw new Error(`--commit "${flags.commit}" is not a 40-hex commit.`);
|
|
9322
|
+
const image = parseImageReference(value, "verify-image");
|
|
9323
|
+
const misnamed = (0, import_contracts5.actionPackageRefusal)(flags.key, image.repository.ref);
|
|
9324
|
+
if (misnamed !== null) throw new Error(misnamed);
|
|
9325
|
+
return {
|
|
9326
|
+
key: flags.key,
|
|
9327
|
+
image,
|
|
9328
|
+
provenance: {
|
|
9329
|
+
repository: (0, import_contracts5.actionSourceRepository)(flags.key),
|
|
9330
|
+
commit: flags.commit
|
|
9331
|
+
},
|
|
9332
|
+
label: `--key ${flags.key}`
|
|
9333
|
+
};
|
|
9334
|
+
}
|
|
9335
|
+
__name(single, "single");
|
|
9336
|
+
async function fromRelease(path) {
|
|
9337
|
+
let value;
|
|
9338
|
+
try {
|
|
9339
|
+
value = parseYaml3(readFileSync3(path, "utf8"));
|
|
9340
|
+
} catch (cause) {
|
|
9341
|
+
throw new Error(`--from ${path} could not be read as YAML. (${cause instanceof Error ? cause.message : String(cause)})`);
|
|
9342
|
+
}
|
|
9343
|
+
const candidates = [];
|
|
9344
|
+
for (const action of (0, import_contracts5.parseCatalogReleaseSpec)(value).actions) {
|
|
9345
|
+
const label = `${path}'s ${action.key}`;
|
|
9346
|
+
const misnamed = (0, import_contracts5.actionPackageRefusal)(action.key, action.artifactRef);
|
|
9347
|
+
if (misnamed !== null) throw new Error(`${label}: ${misnamed}`);
|
|
9348
|
+
if (!isDigest(action.digest)) throw new Error(`${label}: "${action.digest}" is not a digest.`);
|
|
9349
|
+
const repository = parseRepository(action.artifactRef, label);
|
|
9350
|
+
const read = await readArtifactDocument(repository, action.digest);
|
|
9351
|
+
const document = isMapping2(read) ? read : {};
|
|
9352
|
+
const schemaVersion = document.schemaVersion;
|
|
9353
|
+
if (schemaVersion === import_contracts5.CATALOG_ACTION_ARTIFACT_SCHEMA_V1) continue;
|
|
9354
|
+
if (schemaVersion !== import_contracts5.CATALOG_ACTION_ARTIFACT_SCHEMA_V2) {
|
|
9355
|
+
throw new Error(`${label}: ${repository.ref}@${action.digest} declares schemaVersion ${JSON.stringify(schemaVersion)}; this CLI reads ${import_contracts5.CATALOG_ACTION_ARTIFACT_SCHEMA_V1} and ${import_contracts5.CATALOG_ACTION_ARTIFACT_SCHEMA_V2}.`);
|
|
9356
|
+
}
|
|
9357
|
+
const image = isMapping2(document.image) ? document.image.digest : void 0;
|
|
9358
|
+
const recorded = isMapping2(document.provenance) ? document.provenance : {};
|
|
9359
|
+
if (typeof image !== "string" || typeof recorded.repository !== "string" || typeof recorded.commit !== "string") {
|
|
9360
|
+
throw new Error(`${label}: its ${import_contracts5.CATALOG_ACTION_ARTIFACT_SCHEMA_V2} artifact carries no image digest or no provenance.`);
|
|
9361
|
+
}
|
|
9362
|
+
candidates.push({
|
|
9363
|
+
key: action.key,
|
|
9364
|
+
// ★ The image lives in the artifact's own package (D2) — the artifact names a digest, never a place.
|
|
9365
|
+
image: parseImageReference(`${repository.ref}@${image}`, label),
|
|
9366
|
+
provenance: {
|
|
9367
|
+
repository: recorded.repository,
|
|
9368
|
+
commit: recorded.commit
|
|
9369
|
+
},
|
|
9370
|
+
label
|
|
9371
|
+
});
|
|
9372
|
+
}
|
|
9373
|
+
return candidates;
|
|
9374
|
+
}
|
|
9375
|
+
__name(fromRelease, "fromRelease");
|
|
9376
|
+
async function runVerifyImage(argv) {
|
|
9377
|
+
const image = argv[0] !== void 0 && !argv[0].startsWith("--") ? argv[0] : void 0;
|
|
9378
|
+
const { single: flags } = parseCatalogFlags(image === void 0 ? argv : argv.slice(1), [
|
|
9379
|
+
"key",
|
|
9380
|
+
"commit",
|
|
9381
|
+
"from"
|
|
9382
|
+
]);
|
|
9383
|
+
if (image !== void 0 && flags.from !== void 0) {
|
|
9384
|
+
throw new Error("An image and --from are alternatives: verify one image, or every engine action release.yaml names.");
|
|
9385
|
+
}
|
|
9386
|
+
if (image === void 0 && flags.from === void 0) {
|
|
9387
|
+
throw new Error(`Name an image or a release. Usage: ${VERIFY_IMAGE_USAGE}`);
|
|
9388
|
+
}
|
|
9389
|
+
if (flags.from !== void 0 && (flags.key !== void 0 || flags.commit !== void 0)) {
|
|
9390
|
+
throw new Error("--key and --commit are read from each artifact when --from is given; pass one or the other.");
|
|
9391
|
+
}
|
|
9392
|
+
const candidates = image !== void 0 ? [
|
|
9393
|
+
single(image, flags)
|
|
9394
|
+
] : await fromRelease(flags.from);
|
|
9395
|
+
for (const candidate of candidates) {
|
|
9396
|
+
const refusal = await imageRefusal(candidate.image, candidate.key, candidate.provenance);
|
|
9397
|
+
if (refusal !== null) throw new Error(`${candidate.label}: ${refusal} Nothing was signed.`);
|
|
9398
|
+
}
|
|
9399
|
+
for (const candidate of candidates) {
|
|
9400
|
+
process.stderr.write(`\u2713 ${candidate.key}: ${candidate.image.ref} \u2014 built in ${candidate.provenance.repository} at ${candidate.provenance.commit}
|
|
9401
|
+
`);
|
|
9402
|
+
process.stdout.write(`${candidate.image.ref}
|
|
9403
|
+
`);
|
|
9404
|
+
}
|
|
9405
|
+
if (candidates.length === 0) process.stderr.write(`\u2713 ${flags.from} names no engine action \u2014 no image to sign.
|
|
9406
|
+
`);
|
|
9407
|
+
}
|
|
9408
|
+
__name(runVerifyImage, "runVerifyImage");
|
|
9409
|
+
|
|
9410
|
+
// src/catalog/index.ts
|
|
9411
|
+
var CATALOG_USAGE = `runveric catalog \u2014 publish first-party catalog artifacts (docs/127)
|
|
9412
|
+
|
|
9413
|
+
${PUBLISH_ACTION_USAGE}
|
|
9414
|
+
Publish one action's manifest, verbatim, as an artifact \u2014 with --image, an engine action's
|
|
9415
|
+
artifact naming its image by digest. stdout: <key>=<repository>@<digest>
|
|
9416
|
+
|
|
9417
|
+
${PUBLISH_RELEASE_USAGE}
|
|
9418
|
+
Publish a release naming each action artifact by digest. stdout: <repository>@<digest>
|
|
9419
|
+
|
|
9420
|
+
${VERIFY_IMAGE_USAGE}
|
|
9421
|
+
Judge an engine action's image by the server's rule before it is signed.
|
|
9422
|
+
stdout: one <repository>@<digest> per image that passed
|
|
9423
|
+
|
|
9424
|
+
Credentials: GHCR_TOKEN (write:packages), or in a workflow GITHUB_TOKEN with packages: write.
|
|
9425
|
+
Provenance: --repository/--commit, else GITHUB_SERVER_URL/GITHUB_REPOSITORY and GITHUB_SHA, else git.`;
|
|
9426
|
+
var SUBCOMMANDS = {
|
|
9427
|
+
"publish-action": runPublishAction,
|
|
9428
|
+
"publish-release": runPublishRelease,
|
|
9429
|
+
"verify-image": runVerifyImage
|
|
9430
|
+
};
|
|
9431
|
+
async function runCatalog(argv) {
|
|
9432
|
+
const [subcommand, ...rest] = argv;
|
|
9433
|
+
if (subcommand === void 0 || subcommand === "--help" || rest.includes("--help")) {
|
|
9434
|
+
process.stdout.write(`${CATALOG_USAGE}
|
|
9435
|
+
`);
|
|
9436
|
+
return subcommand === void 0 ? 1 : 0;
|
|
9437
|
+
}
|
|
9438
|
+
const run3 = SUBCOMMANDS[subcommand];
|
|
9439
|
+
if (run3 === void 0) {
|
|
9440
|
+
printError(`\u2717 "catalog ${subcommand}" is not a command. There are ${Object.keys(SUBCOMMANDS).length}: ${Object.keys(SUBCOMMANDS).join(", ")}.`);
|
|
9441
|
+
return 1;
|
|
9442
|
+
}
|
|
9443
|
+
try {
|
|
9444
|
+
await run3(rest);
|
|
9445
|
+
return 0;
|
|
9446
|
+
} catch (error) {
|
|
9447
|
+
printError(`\u2717 ${error instanceof Error ? error.message : String(error)}`);
|
|
9448
|
+
return 1;
|
|
9449
|
+
}
|
|
9450
|
+
}
|
|
9451
|
+
__name(runCatalog, "runCatalog");
|
|
9452
|
+
|
|
9453
|
+
// src/check.ts
|
|
9454
|
+
var import_action_manifest = __toESM(require_dist2());
|
|
9455
|
+
import { resolve as resolve2 } from "path";
|
|
9456
|
+
|
|
9457
|
+
// src/package-dir.ts
|
|
9458
|
+
import { readdir, readFile, stat } from "fs/promises";
|
|
9459
|
+
import { join, relative, sep } from "path";
|
|
9460
|
+
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
9461
|
+
".git",
|
|
9462
|
+
".runveric",
|
|
9463
|
+
"node_modules",
|
|
9464
|
+
".venv",
|
|
9465
|
+
"__pycache__",
|
|
9466
|
+
".DS_Store"
|
|
9467
|
+
]);
|
|
9468
|
+
var MAX_FILES = 2e3;
|
|
9469
|
+
var MAX_TOTAL_BYTES = 16 * 1024 * 1024;
|
|
9470
|
+
var PackageDirectoryError = class extends Error {
|
|
9471
|
+
static {
|
|
9472
|
+
__name(this, "PackageDirectoryError");
|
|
9473
|
+
}
|
|
9474
|
+
};
|
|
9475
|
+
async function readPackageDirectory(root) {
|
|
9476
|
+
const properties = await stat(root).catch(() => null);
|
|
9477
|
+
if (!properties?.isDirectory()) {
|
|
9478
|
+
throw new PackageDirectoryError(`${root} is not a directory.`);
|
|
9479
|
+
}
|
|
9480
|
+
const files = /* @__PURE__ */ new Map();
|
|
9481
|
+
let totalBytes = 0;
|
|
9482
|
+
const walk = /* @__PURE__ */ __name(async (directory) => {
|
|
9483
|
+
for (const entry of await readdir(directory, {
|
|
9484
|
+
withFileTypes: true
|
|
9485
|
+
})) {
|
|
9486
|
+
if (IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
9487
|
+
const full = join(directory, entry.name);
|
|
9488
|
+
if (entry.isDirectory()) {
|
|
9489
|
+
await walk(full);
|
|
9490
|
+
continue;
|
|
9491
|
+
}
|
|
9492
|
+
if (!entry.isFile()) continue;
|
|
9493
|
+
if (files.size >= MAX_FILES) {
|
|
9494
|
+
throw new PackageDirectoryError(`This directory holds more than ${MAX_FILES} files. That is almost certainly not a single action package \u2014 run this from the package directory itself.`);
|
|
9495
|
+
}
|
|
9496
|
+
const contents = await readFile(full);
|
|
9497
|
+
totalBytes += contents.byteLength;
|
|
9498
|
+
if (totalBytes > MAX_TOTAL_BYTES) {
|
|
9499
|
+
throw new PackageDirectoryError(`This directory holds more than ${MAX_TOTAL_BYTES / (1024 * 1024)}MB. An action package is source, not artifacts \u2014 add build output to .gitignore.`);
|
|
9500
|
+
}
|
|
9501
|
+
files.set(toPackagePath(root, full), contents);
|
|
9502
|
+
}
|
|
9503
|
+
}, "walk");
|
|
9504
|
+
await walk(root);
|
|
9505
|
+
return files;
|
|
9506
|
+
}
|
|
9507
|
+
__name(readPackageDirectory, "readPackageDirectory");
|
|
9508
|
+
function toPackagePath(root, full) {
|
|
9509
|
+
return relative(root, full).split(sep).join("/");
|
|
9510
|
+
}
|
|
9511
|
+
__name(toPackagePath, "toPackagePath");
|
|
9512
|
+
|
|
9513
|
+
// src/check.ts
|
|
9514
|
+
var import_contracts6 = __toESM(require_dist());
|
|
7115
9515
|
async function runCheck(options) {
|
|
7116
|
-
const directory =
|
|
9516
|
+
const directory = resolve2(options.directory ?? ".");
|
|
7117
9517
|
let files;
|
|
7118
9518
|
try {
|
|
7119
9519
|
files = await readPackageDirectory(directory);
|
|
@@ -7135,7 +9535,7 @@ async function runCheck(options) {
|
|
|
7135
9535
|
print(style.red("This package would be REFUSED at import."));
|
|
7136
9536
|
} else if (report.lintBlocked) {
|
|
7137
9537
|
print();
|
|
7138
|
-
print(style.red("This package would be ACCEPTED at import \u2014 and then misbehave.") + ` The errors above are not things ${
|
|
9538
|
+
print(style.red("This package would be ACCEPTED at import \u2014 and then misbehave.") + ` The errors above are not things ${import_contracts6.BRAND_SHORT_NAME} checks; they are things that go wrong at run time, silently.`);
|
|
7139
9539
|
}
|
|
7140
9540
|
if (report.advisoryCount > 0) {
|
|
7141
9541
|
print();
|
|
@@ -7155,11 +9555,11 @@ async function runCheck(options) {
|
|
|
7155
9555
|
__name(runCheck, "runCheck");
|
|
7156
9556
|
|
|
7157
9557
|
// src/init.ts
|
|
7158
|
-
var
|
|
9558
|
+
var import_contracts7 = __toESM(require_dist());
|
|
7159
9559
|
var import_action_manifest2 = __toESM(require_dist2());
|
|
7160
9560
|
import { mkdir, writeFile } from "fs/promises";
|
|
7161
9561
|
import { createInterface } from "readline/promises";
|
|
7162
|
-
import { dirname, join as join2, resolve as
|
|
9562
|
+
import { dirname as dirname2, join as join2, resolve as resolve3 } from "path";
|
|
7163
9563
|
async function runInit(options) {
|
|
7164
9564
|
const interactive = process.stdin.isTTY === true && options.yes !== true;
|
|
7165
9565
|
const ask = interactive ? prompter() : null;
|
|
@@ -7171,7 +9571,7 @@ async function runInit(options) {
|
|
|
7171
9571
|
const interpreter = await resolveInterpreter(options.interpreter, ask);
|
|
7172
9572
|
if (!interpreter) return 1;
|
|
7173
9573
|
const author = options.author?.trim() || await ask?.("Author", id.split("/")[0]) || id.split("/")[0];
|
|
7174
|
-
const directory =
|
|
9574
|
+
const directory = resolve3(options.directory ?? id.split("/")[1] ?? "action");
|
|
7175
9575
|
const files = (0, import_action_manifest2.scaffoldActionPackage)({
|
|
7176
9576
|
id,
|
|
7177
9577
|
name,
|
|
@@ -7181,7 +9581,7 @@ async function runInit(options) {
|
|
|
7181
9581
|
});
|
|
7182
9582
|
for (const [path, contents] of files) {
|
|
7183
9583
|
const target = join2(directory, path);
|
|
7184
|
-
await mkdir(
|
|
9584
|
+
await mkdir(dirname2(target), {
|
|
7185
9585
|
recursive: true
|
|
7186
9586
|
});
|
|
7187
9587
|
await writeFile(target, contents, {
|
|
@@ -7224,9 +9624,9 @@ async function resolveId(supplied, ask) {
|
|
|
7224
9624
|
}
|
|
7225
9625
|
__name(resolveId, "resolveId");
|
|
7226
9626
|
async function resolveInterpreter(supplied, ask) {
|
|
7227
|
-
const value = supplied?.trim() || await ask?.(`Interpreter (${
|
|
9627
|
+
const value = supplied?.trim() || await ask?.(`Interpreter (${import_contracts7.PACKAGE_INTERPRETERS.join(" / ")})`, "bash") || "bash";
|
|
7228
9628
|
if (!isInterpreter(value)) {
|
|
7229
|
-
printError(style.red(`"${value}" is not an interpreter ${
|
|
9629
|
+
printError(style.red(`"${value}" is not an interpreter ${import_contracts7.BRAND_SHORT_NAME} runs.`) + ` The runner image ships ${import_contracts7.PACKAGE_INTERPRETERS.join(", ")}.`);
|
|
7230
9630
|
return null;
|
|
7231
9631
|
}
|
|
7232
9632
|
return value;
|
|
@@ -7247,7 +9647,7 @@ function prompter() {
|
|
|
7247
9647
|
}
|
|
7248
9648
|
__name(prompter, "prompter");
|
|
7249
9649
|
function isInterpreter(value) {
|
|
7250
|
-
return
|
|
9650
|
+
return import_contracts7.PACKAGE_INTERPRETERS.includes(value);
|
|
7251
9651
|
}
|
|
7252
9652
|
__name(isInterpreter, "isInterpreter");
|
|
7253
9653
|
function titleCase(value) {
|
|
@@ -7257,8 +9657,8 @@ __name(titleCase, "titleCase");
|
|
|
7257
9657
|
|
|
7258
9658
|
// src/publish.ts
|
|
7259
9659
|
var import_action_manifest3 = __toESM(require_dist2());
|
|
7260
|
-
var
|
|
7261
|
-
import { resolve as
|
|
9660
|
+
var import_contracts8 = __toESM(require_dist());
|
|
9661
|
+
import { resolve as resolve4 } from "path";
|
|
7262
9662
|
|
|
7263
9663
|
// src/renamed-variable.ts
|
|
7264
9664
|
function renamedVariableRefusal(current, retired, env = process.env) {
|
|
@@ -7269,15 +9669,15 @@ function renamedVariableRefusal(current, retired, env = process.env) {
|
|
|
7269
9669
|
__name(renamedVariableRefusal, "renamedVariableRefusal");
|
|
7270
9670
|
|
|
7271
9671
|
// src/publish.ts
|
|
7272
|
-
var TOKEN_ENV =
|
|
7273
|
-
var URL_ENV =
|
|
9672
|
+
var TOKEN_ENV = import_contracts8.AUTHORING_CLI_ENV.token;
|
|
9673
|
+
var URL_ENV = import_contracts8.AUTHORING_CLI_ENV.url;
|
|
7274
9674
|
async function runPublish(options) {
|
|
7275
|
-
const directory =
|
|
9675
|
+
const directory = resolve4(options.directory ?? ".");
|
|
7276
9676
|
const baseUrl = (options.to ?? process.env[URL_ENV] ?? "").trim().replace(/\/+$/, "");
|
|
7277
|
-
const
|
|
9677
|
+
const token2 = (options.token ?? process.env[TOKEN_ENV] ?? "").trim();
|
|
7278
9678
|
const stale = [
|
|
7279
|
-
options.to === void 0 ? renamedVariableRefusal(URL_ENV,
|
|
7280
|
-
options.token === void 0 ? renamedVariableRefusal(TOKEN_ENV,
|
|
9679
|
+
options.to === void 0 ? renamedVariableRefusal(URL_ENV, import_contracts8.RETIRED_AUTHORING_CLI_ENV.url) : null,
|
|
9680
|
+
options.token === void 0 ? renamedVariableRefusal(TOKEN_ENV, import_contracts8.RETIRED_AUTHORING_CLI_ENV.token) : null
|
|
7281
9681
|
].filter((sentence) => sentence !== null);
|
|
7282
9682
|
if (stale.length > 0) {
|
|
7283
9683
|
for (const sentence of stale) printError(style.red(sentence));
|
|
@@ -7287,8 +9687,8 @@ async function runPublish(options) {
|
|
|
7287
9687
|
printError(style.red("Where to?") + ` Pass --to https://your-runveric.example.com, or set ${URL_ENV}.`);
|
|
7288
9688
|
return 1;
|
|
7289
9689
|
}
|
|
7290
|
-
if (!
|
|
7291
|
-
printError(style.red(`Publishing needs ${
|
|
9690
|
+
if (!token2) {
|
|
9691
|
+
printError(style.red(`Publishing needs ${import_contracts8.BRAND_ARTICLE} ${import_contracts8.BRAND_SHORT_NAME} access token.`) + ` Set ${TOKEN_ENV}, or pass --token. (init, check and test need no account at all.)`);
|
|
7292
9692
|
return 1;
|
|
7293
9693
|
}
|
|
7294
9694
|
let files;
|
|
@@ -7308,7 +9708,7 @@ async function runPublish(options) {
|
|
|
7308
9708
|
errors: report.errorCount,
|
|
7309
9709
|
warnings: report.warningCount
|
|
7310
9710
|
});
|
|
7311
|
-
printError(style.red("\nNot published.") + (report.validation.ok ? ` ${
|
|
9711
|
+
printError(style.red("\nNot published.") + (report.validation.ok ? ` ${import_contracts8.BRAND_SHORT_NAME} would accept this, but the problems above make it misbehave at run time. Fix them, or pass --force if you disagree.` : " Fix the errors above and publish again."));
|
|
7312
9712
|
return 1;
|
|
7313
9713
|
}
|
|
7314
9714
|
if (report.advisoryCount > 0) {
|
|
@@ -7335,7 +9735,7 @@ async function runPublish(options) {
|
|
|
7335
9735
|
method: "POST",
|
|
7336
9736
|
headers: {
|
|
7337
9737
|
"content-type": "application/json",
|
|
7338
|
-
authorization: `Bearer ${
|
|
9738
|
+
authorization: `Bearer ${token2}`
|
|
7339
9739
|
},
|
|
7340
9740
|
body
|
|
7341
9741
|
});
|
|
@@ -7355,7 +9755,7 @@ Publish refused (${response.status}).`));
|
|
|
7355
9755
|
try {
|
|
7356
9756
|
version2 = JSON.parse(text);
|
|
7357
9757
|
} catch {
|
|
7358
|
-
printError(style.red(`${
|
|
9758
|
+
printError(style.red(`${import_contracts8.BRAND_SHORT_NAME} accepted the package but returned a response we cannot read.`));
|
|
7359
9759
|
return 1;
|
|
7360
9760
|
}
|
|
7361
9761
|
print();
|
|
@@ -7364,7 +9764,7 @@ Publish refused (${response.status}).`));
|
|
|
7364
9764
|
print(` content digest ${version2.contentDigest}`);
|
|
7365
9765
|
print(` tier ${version2.tier} ${style.grey(`(${version2.provenance})`)}`);
|
|
7366
9766
|
print();
|
|
7367
|
-
print(style.grey(`It is an immutable version now. Adopt it in ${
|
|
9767
|
+
print(style.grey(`It is an immutable version now. Adopt it in ${import_contracts8.BRAND_SHORT_NAME} to put it in the catalog \u2014 publishing does not adopt, so nobody can run it until somebody chooses to.`));
|
|
7368
9768
|
print();
|
|
7369
9769
|
return 0;
|
|
7370
9770
|
}
|
|
@@ -7376,11 +9776,11 @@ function formatProblem(text) {
|
|
|
7376
9776
|
} catch {
|
|
7377
9777
|
return ` ${text}`;
|
|
7378
9778
|
}
|
|
7379
|
-
const problem = (0,
|
|
9779
|
+
const problem = (0, import_contracts8.parseProblemDocument)(parsed);
|
|
7380
9780
|
if (!problem) return ` ${text}`;
|
|
7381
9781
|
const lines = [];
|
|
7382
9782
|
if (problem.detail) lines.push(` ${problem.detail}`);
|
|
7383
|
-
for (const error of (0,
|
|
9783
|
+
for (const error of (0, import_contracts8.problemFieldErrors)(problem)) {
|
|
7384
9784
|
lines.push(error.field ? ` ${style.bold(error.field)}: ${error.message}` : ` ${error.message}`);
|
|
7385
9785
|
}
|
|
7386
9786
|
return lines.length > 0 ? lines.join("\n") : ` ${text}`;
|
|
@@ -7388,12 +9788,12 @@ function formatProblem(text) {
|
|
|
7388
9788
|
__name(formatProblem, "formatProblem");
|
|
7389
9789
|
|
|
7390
9790
|
// src/runner-image.ts
|
|
7391
|
-
var
|
|
9791
|
+
var import_contracts9 = __toESM(require_dist());
|
|
7392
9792
|
import { execFile } from "child_process";
|
|
7393
9793
|
import { promisify } from "util";
|
|
7394
9794
|
var run = promisify(execFile);
|
|
7395
9795
|
var LOCAL_RUNNER_IMAGE = "runveric-package-runner:local";
|
|
7396
|
-
var RUNNER_IMAGE_ENV =
|
|
9796
|
+
var RUNNER_IMAGE_ENV = import_contracts9.AUTHORING_CLI_ENV.runnerImage;
|
|
7397
9797
|
var RenamedVariableError = class extends Error {
|
|
7398
9798
|
static {
|
|
7399
9799
|
__name(this, "RenamedVariableError");
|
|
@@ -7410,7 +9810,7 @@ function resolveRunnerImage(flag) {
|
|
|
7410
9810
|
reference: configured,
|
|
7411
9811
|
origin: "environment"
|
|
7412
9812
|
};
|
|
7413
|
-
const stale = renamedVariableRefusal(RUNNER_IMAGE_ENV,
|
|
9813
|
+
const stale = renamedVariableRefusal(RUNNER_IMAGE_ENV, import_contracts9.RETIRED_AUTHORING_CLI_ENV.runnerImage);
|
|
7414
9814
|
if (stale) throw new RenamedVariableError(stale);
|
|
7415
9815
|
return {
|
|
7416
9816
|
reference: LOCAL_RUNNER_IMAGE,
|
|
@@ -7425,7 +9825,7 @@ function runnerImageNote(image) {
|
|
|
7425
9825
|
case "environment":
|
|
7426
9826
|
return `from ${RUNNER_IMAGE_ENV}`;
|
|
7427
9827
|
case "local-build":
|
|
7428
|
-
return `locally built \u2014 NOT the image your ${
|
|
9828
|
+
return `locally built \u2014 NOT the image your ${import_contracts9.BRAND_SHORT_NAME} runs; set ${RUNNER_IMAGE_ENV} to match it`;
|
|
7429
9829
|
}
|
|
7430
9830
|
}
|
|
7431
9831
|
__name(runnerImageNote, "runnerImageNote");
|
|
@@ -7442,7 +9842,7 @@ async function assertDockerAvailable() {
|
|
|
7442
9842
|
"{{.Server.Version}}"
|
|
7443
9843
|
]);
|
|
7444
9844
|
} catch {
|
|
7445
|
-
throw new DockerMissingError(`Docker is not available. \`runveric test\` runs your package in the real ${
|
|
9845
|
+
throw new DockerMissingError(`Docker is not available. \`runveric test\` runs your package in the real ${import_contracts9.BRAND_SHORT_NAME} runner image, which needs a container runtime \u2014 install Docker Desktop (or start it), then try again. \`runveric check\` needs nothing and still works.`);
|
|
7446
9846
|
}
|
|
7447
9847
|
}
|
|
7448
9848
|
__name(assertDockerAvailable, "assertDockerAvailable");
|
|
@@ -7465,7 +9865,7 @@ var import_action_manifest4 = __toESM(require_dist2());
|
|
|
7465
9865
|
import { spawn } from "child_process";
|
|
7466
9866
|
import { mkdir as mkdir2, mkdtemp, readFile as readFile2, rm, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
7467
9867
|
import { tmpdir } from "os";
|
|
7468
|
-
import { dirname as
|
|
9868
|
+
import { dirname as dirname3, join as join3, resolve as resolve5 } from "path";
|
|
7469
9869
|
import { StringDecoder } from "string_decoder";
|
|
7470
9870
|
|
|
7471
9871
|
// src/azure-credential.ts
|
|
@@ -7488,7 +9888,7 @@ async function acquireAzureCredential() {
|
|
|
7488
9888
|
if (!account.id || !account.tenantId) {
|
|
7489
9889
|
throw new AzureCredentialError("`az account show` returned no subscription. Run `az login` and `az account set --subscription <id>` first.");
|
|
7490
9890
|
}
|
|
7491
|
-
const
|
|
9891
|
+
const token2 = await azJson([
|
|
7492
9892
|
"account",
|
|
7493
9893
|
"get-access-token",
|
|
7494
9894
|
"--resource",
|
|
@@ -7496,11 +9896,11 @@ async function acquireAzureCredential() {
|
|
|
7496
9896
|
"--output",
|
|
7497
9897
|
"json"
|
|
7498
9898
|
]);
|
|
7499
|
-
if (!
|
|
9899
|
+
if (!token2.accessToken) {
|
|
7500
9900
|
throw new AzureCredentialError("`az account get-access-token` returned no access token.");
|
|
7501
9901
|
}
|
|
7502
9902
|
return {
|
|
7503
|
-
token:
|
|
9903
|
+
token: token2.accessToken,
|
|
7504
9904
|
// The identical names the credential adapter puts in a lease, so a package reads the same
|
|
7505
9905
|
// environment here and in production.
|
|
7506
9906
|
env: {
|
|
@@ -7535,7 +9935,7 @@ ${message.trim()}`);
|
|
|
7535
9935
|
__name(azJson, "azJson");
|
|
7536
9936
|
|
|
7537
9937
|
// src/test.ts
|
|
7538
|
-
var
|
|
9938
|
+
var import_contracts10 = __toESM(require_dist());
|
|
7539
9939
|
var PHASES = [
|
|
7540
9940
|
"validate",
|
|
7541
9941
|
"run",
|
|
@@ -7549,10 +9949,10 @@ var CONTAINER = {
|
|
|
7549
9949
|
creds: "/runveric/creds"
|
|
7550
9950
|
};
|
|
7551
9951
|
async function runTest(options) {
|
|
7552
|
-
const directory =
|
|
9952
|
+
const directory = resolve5(options.directory ?? ".");
|
|
7553
9953
|
const phase = options.phase ?? "run";
|
|
7554
9954
|
if (!PHASES.includes(phase)) {
|
|
7555
|
-
printError(style.red(`"${options.phase}" is not a phase.`) + ` ${
|
|
9955
|
+
printError(style.red(`"${options.phase}" is not a phase.`) + ` ${import_contracts10.BRAND_SHORT_NAME} runs ${PHASES.join(", ")} \u2014 one at a time, so you can read each one's result.`);
|
|
7556
9956
|
return 1;
|
|
7557
9957
|
}
|
|
7558
9958
|
let files;
|
|
@@ -7624,9 +10024,9 @@ async function runTest(options) {
|
|
|
7624
10024
|
print(` runner image ${image.reference} ${style.grey(`(${runnerImageNote(image)})`)}`);
|
|
7625
10025
|
print(` interpreter ${interpreter}@${manifest.runtime.version}`);
|
|
7626
10026
|
print(` entrypoint ${entrypoint}${import_action_manifest4.PACKAGE_INTERPRETER_LAUNCH[interpreter].entryFile}`);
|
|
7627
|
-
print(` identity ${credential ? `${credential.account.name} ${style.grey(`\u2014 YOUR access, not ${
|
|
10027
|
+
print(` identity ${credential ? `${credential.account.name} ${style.grey(`\u2014 YOUR access, not ${import_contracts10.BRAND_SHORT_NAME}'s brokered least-privilege identity`)}` : style.grey("none (--no-credential)")}`);
|
|
7628
10028
|
if (manifest.requiresNetwork) {
|
|
7629
|
-
print(` network ${style.yellow("open")} ${style.grey(`\u2014 your allowlist (${(manifest.egressAllowlist ?? []).join(", ")}) is NOT enforced here; ${
|
|
10029
|
+
print(` network ${style.yellow("open")} ${style.grey(`\u2014 your allowlist (${(manifest.egressAllowlist ?? []).join(", ")}) is NOT enforced here; ${import_contracts10.BRAND_SHORT_NAME} enforces it`)}`);
|
|
7630
10030
|
} else {
|
|
7631
10031
|
print(` network ${style.grey("none \u2014 this package declares requiresNetwork: false")}`);
|
|
7632
10032
|
}
|
|
@@ -7728,13 +10128,13 @@ async function materialize(workspace, input) {
|
|
|
7728
10128
|
})));
|
|
7729
10129
|
for (const [path, contents] of input.files) {
|
|
7730
10130
|
const target = join3(layout.packageRoot, path);
|
|
7731
|
-
await mkdir2(
|
|
10131
|
+
await mkdir2(dirname3(target), {
|
|
7732
10132
|
recursive: true
|
|
7733
10133
|
});
|
|
7734
10134
|
await writeFile2(target, contents);
|
|
7735
10135
|
}
|
|
7736
10136
|
const context = {
|
|
7737
|
-
protocolVersion:
|
|
10137
|
+
protocolVersion: import_contracts10.PACKAGE_EXECUTION_PROTOCOL_VERSION,
|
|
7738
10138
|
// A recognisable id rather than a random uuid: it appears in the package's own log lines,
|
|
7739
10139
|
// and "local-test" tells the reader instantly that this was not a real run.
|
|
7740
10140
|
executionId: "local-test",
|
|
@@ -7760,7 +10160,7 @@ async function runContainer(options) {
|
|
|
7760
10160
|
const launch = import_action_manifest4.PACKAGE_INTERPRETER_LAUNCH[options.interpreter];
|
|
7761
10161
|
const script = `${CONTAINER.packageRoot}/${options.entrypoint}${launch.entryFile}`;
|
|
7762
10162
|
const environment = {
|
|
7763
|
-
RUNVERIC_PROTOCOL_VERSION:
|
|
10163
|
+
RUNVERIC_PROTOCOL_VERSION: import_contracts10.PACKAGE_RESULT_PROTOCOL_VERSION,
|
|
7764
10164
|
RUNVERIC_PHASE: options.phase,
|
|
7765
10165
|
RUNVERIC_CONTEXT_FILE: `${CONTAINER.input}/context.json`,
|
|
7766
10166
|
RUNVERIC_PARAMETERS_FILE: `${CONTAINER.input}/parameters.json`,
|
|
@@ -7887,13 +10287,13 @@ function emitLine(line, stderr, onEvent) {
|
|
|
7887
10287
|
__name(emitLine, "emitLine");
|
|
7888
10288
|
async function reportOutcome(options) {
|
|
7889
10289
|
if (options.exit.timedOut) {
|
|
7890
|
-
printError(style.red("\nThe package did not finish within 15 minutes and was killed.") + ` ${
|
|
10290
|
+
printError(style.red("\nThe package did not finish within 15 minutes and was killed.") + ` ${import_contracts10.BRAND_SHORT_NAME} enforces the \`executionTimeout\` in your manifest; this is the local ceiling.`);
|
|
7891
10291
|
return 1;
|
|
7892
10292
|
}
|
|
7893
10293
|
const exists = await stat2(options.resultPath).catch(() => null);
|
|
7894
10294
|
if (!exists?.isFile() || exists.size === 0) {
|
|
7895
10295
|
printError(style.red("\nThe package wrote no result.") + `
|
|
7896
|
-
result.json is the only channel ${
|
|
10296
|
+
result.json is the only channel ${import_contracts10.BRAND_SHORT_NAME} reads. Exiting ${options.exit.code ?? "with a signal"} without writing $RUNVERIC_RESULT_FILE is a FAILED run, whatever the script printed.
|
|
7897
10297
|
Write it as the last thing your script does, on every path \u2014 including the ones that fail.`);
|
|
7898
10298
|
return 1;
|
|
7899
10299
|
}
|
|
@@ -7901,7 +10301,7 @@ async function reportOutcome(options) {
|
|
|
7901
10301
|
try {
|
|
7902
10302
|
parsed = JSON.parse(await readFile2(options.resultPath, "utf8"));
|
|
7903
10303
|
} catch {
|
|
7904
|
-
printError(style.red("\nresult.json is not valid JSON.") + ` ${
|
|
10304
|
+
printError(style.red("\nresult.json is not valid JSON.") + ` ${import_contracts10.BRAND_SHORT_NAME} refuses the run rather than guessing what the package meant.`);
|
|
7905
10305
|
return 1;
|
|
7906
10306
|
}
|
|
7907
10307
|
let result;
|
|
@@ -7909,14 +10309,14 @@ async function reportOutcome(options) {
|
|
|
7909
10309
|
result = (0, import_action_manifest4.validatePackageResult)(parsed, options.phase);
|
|
7910
10310
|
} catch (error) {
|
|
7911
10311
|
printError(style.red(`
|
|
7912
|
-
${
|
|
10312
|
+
${import_contracts10.BRAND_SHORT_NAME} would REFUSE this result.`) + `
|
|
7913
10313
|
${error instanceof Error ? error.message : String(error)}
|
|
7914
10314
|
This is the same check the control plane runs, so fixing it here fixes it there.`);
|
|
7915
10315
|
return 1;
|
|
7916
10316
|
}
|
|
7917
10317
|
printResult(result);
|
|
7918
10318
|
if (options.exit.code !== 0) {
|
|
7919
|
-
print(style.yellow(`The package exited ${options.exit.code} but wrote a valid result. ${
|
|
10319
|
+
print(style.yellow(`The package exited ${options.exit.code} but wrote a valid result. ${import_contracts10.BRAND_SHORT_NAME} reads the result, not the exit code \u2014 but a non-zero exit usually means something failed after the result was written.`));
|
|
7920
10320
|
print();
|
|
7921
10321
|
}
|
|
7922
10322
|
return result.status === "succeeded" ? 0 : 1;
|
|
@@ -7925,7 +10325,7 @@ __name(reportOutcome, "reportOutcome");
|
|
|
7925
10325
|
async function readJsonFile(path) {
|
|
7926
10326
|
let source;
|
|
7927
10327
|
try {
|
|
7928
|
-
source = await readFile2(
|
|
10328
|
+
source = await readFile2(resolve5(path), "utf8");
|
|
7929
10329
|
} catch {
|
|
7930
10330
|
throw new Error(`Could not read ${path}.`);
|
|
7931
10331
|
}
|
|
@@ -7943,9 +10343,9 @@ async function readJsonFile(path) {
|
|
|
7943
10343
|
__name(readJsonFile, "readJsonFile");
|
|
7944
10344
|
|
|
7945
10345
|
// src/main.ts
|
|
7946
|
-
var
|
|
10346
|
+
var import_contracts11 = __toESM(require_dist());
|
|
7947
10347
|
var USAGE = `
|
|
7948
|
-
${style.bold("runveric")} \u2014 author ${
|
|
10348
|
+
${style.bold("runveric")} \u2014 author ${import_contracts11.BRAND_ARTICLE} ${import_contracts11.BRAND_SHORT_NAME} action, without pushing anything.
|
|
7949
10349
|
|
|
7950
10350
|
${style.bold("USAGE")}
|
|
7951
10351
|
runveric <command> [options]
|
|
@@ -7953,8 +10353,9 @@ ${style.bold("USAGE")}
|
|
|
7953
10353
|
${style.bold("COMMANDS")}
|
|
7954
10354
|
init [directory] Scaffold a working action. It passes check and test immediately.
|
|
7955
10355
|
check Validate the package. Offline \u2014 no account, no cloud, no network.
|
|
7956
|
-
test Run one phase in the real ${
|
|
7957
|
-
publish Upload it to ${
|
|
10356
|
+
test Run one phase in the real ${import_contracts11.BRAND_SHORT_NAME} runner image.
|
|
10357
|
+
publish Upload it to ${import_contracts11.BRAND_SHORT_NAME} as an authored version. Needs an account.
|
|
10358
|
+
catalog <command> Publish first-party catalog artifacts. Not for authoring: see \`runveric catalog --help\`.
|
|
7958
10359
|
|
|
7959
10360
|
${style.bold("init")}
|
|
7960
10361
|
--id <publisher/name> The action id. Required.
|
|
@@ -7977,9 +10378,9 @@ ${style.bold("test")}
|
|
|
7977
10378
|
--keep Keep the materialized /runveric tree for inspection.
|
|
7978
10379
|
|
|
7979
10380
|
${style.bold("publish")}
|
|
7980
|
-
--to <url> Your ${
|
|
10381
|
+
--to <url> Your ${import_contracts11.BRAND_SHORT_NAME} URL. Defaults to $${URL_ENV}.
|
|
7981
10382
|
--token <token> Access token. Defaults to $${TOKEN_ENV}.
|
|
7982
|
-
--force Publish despite lint errors. Never bypasses ${
|
|
10383
|
+
--force Publish despite lint errors. Never bypasses ${import_contracts11.BRAND_SHORT_NAME}'s validation.
|
|
7983
10384
|
|
|
7984
10385
|
${style.bold("EVERYWHERE")}
|
|
7985
10386
|
--dir <path> The package directory. Defaults to the current directory.
|
|
@@ -8022,7 +10423,9 @@ __name(parseArgv, "parseArgv");
|
|
|
8022
10423
|
var first = /* @__PURE__ */ __name((parsed, name) => parsed.flags.get(name)?.[0], "first");
|
|
8023
10424
|
var bool = /* @__PURE__ */ __name((parsed, name) => parsed.flags.get(name)?.[0] === "true", "bool");
|
|
8024
10425
|
async function main() {
|
|
8025
|
-
const
|
|
10426
|
+
const argv = process.argv.slice(2);
|
|
10427
|
+
if (argv[0] === "catalog") return runCatalog(argv.slice(1));
|
|
10428
|
+
const parsed = parseArgv(argv);
|
|
8026
10429
|
if (parsed.flags.has("version")) {
|
|
8027
10430
|
print(version);
|
|
8028
10431
|
return 0;
|
|
@@ -8090,7 +10493,7 @@ async function main() {
|
|
|
8090
10493
|
force: bool(parsed, "force")
|
|
8091
10494
|
});
|
|
8092
10495
|
default:
|
|
8093
|
-
printError(style.red(`"${parsed.command}" is not a command.`) + " There are four: init, check, test, publish.");
|
|
10496
|
+
printError(style.red(`"${parsed.command}" is not a command.`) + " There are four: init, check, test, publish \u2014 and catalog, which publishes first-party catalog artifacts.");
|
|
8094
10497
|
return 1;
|
|
8095
10498
|
}
|
|
8096
10499
|
}
|