@runveric/cli 0.1.1 → 0.2.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 +55 -13
- package/dist/runveric.mjs +1518 -359
- package/dist/runveric.mjs.map +1 -1
- package/package.json +5 -5
package/dist/runveric.mjs
CHANGED
|
@@ -212,14 +212,18 @@ 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_V1 = exports.CATALOG_RELEASE_SCHEMA_V1 = exports.EVIDENCE_PACK_SCHEMA_VERSION = 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
|
-
exports.PACKAGE_RESULT_PROTOCOL_VERSION = "
|
|
218
|
-
exports.PACKAGE_EXECUTION_PROTOCOL_VERSION = "
|
|
219
|
-
exports.ACTION_MANIFEST_API_VERSION = "
|
|
220
|
-
exports.PACKAGE_WORK_ITEM_VERSION = "
|
|
221
|
-
exports.RUNNER_PROTOCOL_VERSION = "
|
|
222
|
-
exports.
|
|
217
|
+
exports.PACKAGE_RESULT_PROTOCOL_VERSION = "runveric.dev/package-result/v1";
|
|
218
|
+
exports.PACKAGE_EXECUTION_PROTOCOL_VERSION = "runveric.dev/package-execution/v1";
|
|
219
|
+
exports.ACTION_MANIFEST_API_VERSION = "runveric.dev/action/v1";
|
|
220
|
+
exports.PACKAGE_WORK_ITEM_VERSION = "runveric.dev/package-work-item/v1";
|
|
221
|
+
exports.RUNNER_PROTOCOL_VERSION = "runveric.dev/runner-channel/v2";
|
|
222
|
+
exports.EVIDENCE_PACK_SCHEMA_VERSION = "runveric.dev/evidence-pack/v1";
|
|
223
|
+
exports.CATALOG_RELEASE_SCHEMA_V1 = "runveric.dev/catalog-release/v1";
|
|
224
|
+
exports.CATALOG_ACTION_ARTIFACT_SCHEMA_V1 = "runveric.dev/catalog-action/v1";
|
|
225
|
+
exports.CATALOG_ARTIFACT_MEDIA_TYPE = "application/vnd.runveric.catalog-artifact+json";
|
|
226
|
+
exports.PROBLEM_TYPE_BASE = "https://runveric.dev/errors";
|
|
223
227
|
function problemType(slug) {
|
|
224
228
|
return `${exports.PROBLEM_TYPE_BASE}/${slug}`;
|
|
225
229
|
}
|
|
@@ -301,6 +305,13 @@ var require_permissions = __commonJS({
|
|
|
301
305
|
Object.defineProperty(exports, "__esModule", {
|
|
302
306
|
value: true
|
|
303
307
|
});
|
|
308
|
+
exports.PLATFORM_PERMISSIONS = void 0;
|
|
309
|
+
exports.PLATFORM_PERMISSIONS = [
|
|
310
|
+
// Which catalog release is live for every workspace (docs/121). Pinning one changes what EVERY
|
|
311
|
+
// workspace can run, so it is neither `policy:manage` (one workspace's own governance) nor
|
|
312
|
+
// `catalog:read` (the right to browse).
|
|
313
|
+
"catalog:release"
|
|
314
|
+
];
|
|
304
315
|
}
|
|
305
316
|
});
|
|
306
317
|
|
|
@@ -443,6 +454,41 @@ var require_groups = __commonJS({
|
|
|
443
454
|
}
|
|
444
455
|
});
|
|
445
456
|
|
|
457
|
+
// ../contracts/dist/catalog-policy.js
|
|
458
|
+
var require_catalog_policy = __commonJS({
|
|
459
|
+
"../contracts/dist/catalog-policy.js"(exports) {
|
|
460
|
+
"use strict";
|
|
461
|
+
Object.defineProperty(exports, "__esModule", {
|
|
462
|
+
value: true
|
|
463
|
+
});
|
|
464
|
+
exports.CATALOG_RULE_EFFECTS = exports.CATALOG_DEFAULT_MODE_WHEN_UNSET = exports.CATALOG_DEFAULT_MODES = void 0;
|
|
465
|
+
exports.effectiveCatalogAccess = effectiveCatalogAccess;
|
|
466
|
+
var CATALOG_DEFAULT_MODE_SET = {
|
|
467
|
+
allow_all: true,
|
|
468
|
+
deny_all: true
|
|
469
|
+
};
|
|
470
|
+
exports.CATALOG_DEFAULT_MODES = Object.keys(CATALOG_DEFAULT_MODE_SET);
|
|
471
|
+
exports.CATALOG_DEFAULT_MODE_WHEN_UNSET = "allow_all";
|
|
472
|
+
var CATALOG_RULE_EFFECT_SET = {
|
|
473
|
+
allow: true,
|
|
474
|
+
deny: true
|
|
475
|
+
};
|
|
476
|
+
exports.CATALOG_RULE_EFFECTS = Object.keys(CATALOG_RULE_EFFECT_SET);
|
|
477
|
+
var RULE_ACCESS = {
|
|
478
|
+
allow: "allowed",
|
|
479
|
+
deny: "blocked"
|
|
480
|
+
};
|
|
481
|
+
var UNRULED_ACCESS = {
|
|
482
|
+
allow_all: "allowed",
|
|
483
|
+
deny_all: "blocked"
|
|
484
|
+
};
|
|
485
|
+
function effectiveCatalogAccess(defaultMode, rule) {
|
|
486
|
+
return rule !== null ? RULE_ACCESS[rule] : UNRULED_ACCESS[defaultMode];
|
|
487
|
+
}
|
|
488
|
+
__name(effectiveCatalogAccess, "effectiveCatalogAccess");
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
|
|
446
492
|
// ../contracts/dist/catalog.js
|
|
447
493
|
var require_catalog = __commonJS({
|
|
448
494
|
"../contracts/dist/catalog.js"(exports) {
|
|
@@ -450,7 +496,7 @@ var require_catalog = __commonJS({
|
|
|
450
496
|
Object.defineProperty(exports, "__esModule", {
|
|
451
497
|
value: true
|
|
452
498
|
});
|
|
453
|
-
exports.NEW_ACTION_WINDOW_DAYS = exports.PARAMETER_CONTROLS = exports.ACTION_LIFECYCLE_LABELS = exports.INACTIVE_ACTION_LIFECYCLES = exports.RUNNABLE_ACTION_LIFECYCLES = exports.LISTED_ACTION_LIFECYCLES = exports.ACTION_LIFECYCLE_STANDING = exports.ACTION_LIFECYCLES = void 0;
|
|
499
|
+
exports.NEW_ACTION_WINDOW_DAYS = exports.PARAMETER_ORDER_KEY = exports.PARAMETER_CONTROLS = exports.ACTION_LIFECYCLE_LABELS = exports.INACTIVE_ACTION_LIFECYCLES = exports.RUNNABLE_ACTION_LIFECYCLES = exports.LISTED_ACTION_LIFECYCLES = exports.ACTION_LIFECYCLE_STANDING = exports.ACTION_LIFECYCLES = void 0;
|
|
454
500
|
exports.orderedParameterEntries = orderedParameterEntries;
|
|
455
501
|
exports.withParameterOrder = withParameterOrder;
|
|
456
502
|
exports.isNewAction = isNewAction;
|
|
@@ -513,9 +559,10 @@ var require_catalog = __commonJS({
|
|
|
513
559
|
"switch",
|
|
514
560
|
"info"
|
|
515
561
|
];
|
|
562
|
+
exports.PARAMETER_ORDER_KEY = "x-runveric-order";
|
|
516
563
|
function orderedParameterEntries(schema) {
|
|
517
564
|
const properties = schema.properties ?? {};
|
|
518
|
-
const declared = schema[
|
|
565
|
+
const declared = schema[exports.PARAMETER_ORDER_KEY] ?? [];
|
|
519
566
|
const seen = /* @__PURE__ */ new Set();
|
|
520
567
|
const ordered = [];
|
|
521
568
|
for (const id of declared) {
|
|
@@ -540,7 +587,7 @@ var require_catalog = __commonJS({
|
|
|
540
587
|
function withParameterOrder(schema) {
|
|
541
588
|
return {
|
|
542
589
|
...schema,
|
|
543
|
-
|
|
590
|
+
[exports.PARAMETER_ORDER_KEY]: Object.keys(schema.properties ?? {})
|
|
544
591
|
};
|
|
545
592
|
}
|
|
546
593
|
__name(withParameterOrder, "withParameterOrder");
|
|
@@ -557,6 +604,315 @@ var require_catalog = __commonJS({
|
|
|
557
604
|
}
|
|
558
605
|
});
|
|
559
606
|
|
|
607
|
+
// ../contracts/dist/catalog-release.js
|
|
608
|
+
var require_catalog_release = __commonJS({
|
|
609
|
+
"../contracts/dist/catalog-release.js"(exports) {
|
|
610
|
+
"use strict";
|
|
611
|
+
Object.defineProperty(exports, "__esModule", {
|
|
612
|
+
value: true
|
|
613
|
+
});
|
|
614
|
+
exports.ARTIFACT_DIGEST_PATTERN = exports.ARTIFACT_REPOSITORY_PATTERN = exports.CATALOG_ACTION_ARTIFACT_SCHEMA_VERSIONS = exports.CATALOG_RELEASE_SCHEMA_VERSIONS = void 0;
|
|
615
|
+
var wire_identity_1 = require_wire_identity();
|
|
616
|
+
exports.CATALOG_RELEASE_SCHEMA_VERSIONS = [
|
|
617
|
+
wire_identity_1.CATALOG_RELEASE_SCHEMA_V1
|
|
618
|
+
];
|
|
619
|
+
exports.CATALOG_ACTION_ARTIFACT_SCHEMA_VERSIONS = [
|
|
620
|
+
wire_identity_1.CATALOG_ACTION_ARTIFACT_SCHEMA_V1
|
|
621
|
+
];
|
|
622
|
+
exports.ARTIFACT_REPOSITORY_PATTERN = /^[a-z0-9][a-z0-9.-]*(:[0-9]+)?(\/[a-z0-9][a-z0-9._-]*)+$/;
|
|
623
|
+
exports.ARTIFACT_DIGEST_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
// ../contracts/dist/action-repository.js
|
|
628
|
+
var require_action_repository = __commonJS({
|
|
629
|
+
"../contracts/dist/action-repository.js"(exports) {
|
|
630
|
+
"use strict";
|
|
631
|
+
Object.defineProperty(exports, "__esModule", {
|
|
632
|
+
value: true
|
|
633
|
+
});
|
|
634
|
+
exports.ACTION_REPOSITORY_PREFIX = void 0;
|
|
635
|
+
exports.actionKeyRefusal = actionKeyRefusal;
|
|
636
|
+
exports.isActionKey = isActionKey;
|
|
637
|
+
exports.actionRepositoryName = actionRepositoryName;
|
|
638
|
+
exports.actionKeyOf = actionKeyOf;
|
|
639
|
+
exports.actionPackageRefusal = actionPackageRefusal3;
|
|
640
|
+
exports.ACTION_REPOSITORY_PREFIX = "action-";
|
|
641
|
+
var WORD = /^[a-z][a-z0-9]*$/;
|
|
642
|
+
var VERB = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]+)*$/;
|
|
643
|
+
function actionKeyRefusal(key) {
|
|
644
|
+
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.";
|
|
645
|
+
const segments = key.split(".");
|
|
646
|
+
if (segments.length !== 3) {
|
|
647
|
+
return `"${key}" is not an action key: it has ${segments.length} dot-separated segment(s), not 3; ${shape}`;
|
|
648
|
+
}
|
|
649
|
+
const [provider, resource, verb] = segments;
|
|
650
|
+
const not = /* @__PURE__ */ __name((what) => `"${key}" is not an action key: ${what}; ${shape}`, "not");
|
|
651
|
+
if (!WORD.test(provider)) return not(`its provider "${provider}" is not one lowercase word`);
|
|
652
|
+
if (!WORD.test(resource)) return not(`its resource "${resource}" is not one lowercase word`);
|
|
653
|
+
if (!VERB.test(verb)) return not(`its verb "${verb}" is not camelCase without two capitals in a row`);
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
__name(actionKeyRefusal, "actionKeyRefusal");
|
|
657
|
+
function isActionKey(key) {
|
|
658
|
+
return actionKeyRefusal(key) === null;
|
|
659
|
+
}
|
|
660
|
+
__name(isActionKey, "isActionKey");
|
|
661
|
+
var kebab = /* @__PURE__ */ __name((verb) => verb.replace(/[A-Z]/g, (capital) => `-${capital.toLowerCase()}`), "kebab");
|
|
662
|
+
function actionRepositoryName(key) {
|
|
663
|
+
const refusal = actionKeyRefusal(key);
|
|
664
|
+
if (refusal !== null) throw new Error(refusal);
|
|
665
|
+
const [provider, resource, verb] = key.split(".");
|
|
666
|
+
return `${exports.ACTION_REPOSITORY_PREFIX}${provider}-${resource}-${kebab(verb)}`;
|
|
667
|
+
}
|
|
668
|
+
__name(actionRepositoryName, "actionRepositoryName");
|
|
669
|
+
function actionKeyOf(name) {
|
|
670
|
+
if (!name.startsWith(exports.ACTION_REPOSITORY_PREFIX)) return null;
|
|
671
|
+
const [provider, resource, first2, ...rest] = name.slice(exports.ACTION_REPOSITORY_PREFIX.length).split("-");
|
|
672
|
+
if (provider === void 0 || resource === void 0 || first2 === void 0) return null;
|
|
673
|
+
const verb = first2 + rest.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
674
|
+
const key = `${provider}.${resource}.${verb}`;
|
|
675
|
+
return isActionKey(key) && actionRepositoryName(key) === name ? key : null;
|
|
676
|
+
}
|
|
677
|
+
__name(actionKeyOf, "actionKeyOf");
|
|
678
|
+
function actionPackageRefusal3(key, artifactRef) {
|
|
679
|
+
const keyRefusal = actionKeyRefusal(key);
|
|
680
|
+
if (keyRefusal !== null) return `${keyRefusal} No package can carry it.`;
|
|
681
|
+
const cut = artifactRef.lastIndexOf("/");
|
|
682
|
+
const named = artifactRef.slice(cut + 1);
|
|
683
|
+
const expected = actionRepositoryName(key);
|
|
684
|
+
if (named === expected) return null;
|
|
685
|
+
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.`;
|
|
686
|
+
}
|
|
687
|
+
__name(actionPackageRefusal3, "actionPackageRefusal");
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
// ../contracts/dist/catalog-release-spec.js
|
|
692
|
+
var require_catalog_release_spec = __commonJS({
|
|
693
|
+
"../contracts/dist/catalog-release-spec.js"(exports) {
|
|
694
|
+
"use strict";
|
|
695
|
+
Object.defineProperty(exports, "__esModule", {
|
|
696
|
+
value: true
|
|
697
|
+
});
|
|
698
|
+
exports.CatalogReleaseSpecError = void 0;
|
|
699
|
+
exports.splitArtifactReference = splitArtifactReference;
|
|
700
|
+
exports.parseCatalogReleaseSpec = parseCatalogReleaseSpec2;
|
|
701
|
+
var CatalogReleaseSpecError = class CatalogReleaseSpecError extends Error {
|
|
702
|
+
static {
|
|
703
|
+
__name(this, "CatalogReleaseSpecError");
|
|
704
|
+
}
|
|
705
|
+
constructor(message) {
|
|
706
|
+
super(message);
|
|
707
|
+
this.name = "CatalogReleaseSpecError";
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
exports.CatalogReleaseSpecError = CatalogReleaseSpecError;
|
|
711
|
+
function splitArtifactReference(reference) {
|
|
712
|
+
const at = reference.indexOf("@");
|
|
713
|
+
if (at <= 0 || at === reference.length - 1) return null;
|
|
714
|
+
return {
|
|
715
|
+
artifactRef: reference.slice(0, at),
|
|
716
|
+
digest: reference.slice(at + 1)
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
__name(splitArtifactReference, "splitArtifactReference");
|
|
720
|
+
var isMapping2 = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isMapping");
|
|
721
|
+
function parseCatalogReleaseSpec2(value) {
|
|
722
|
+
if (!isMapping2(value)) {
|
|
723
|
+
throw new CatalogReleaseSpecError("release.yaml is not a mapping of `version` and `actions`.");
|
|
724
|
+
}
|
|
725
|
+
const unknown = Object.keys(value).filter((key) => key !== "version" && key !== "actions");
|
|
726
|
+
if (unknown.length > 0) {
|
|
727
|
+
throw new CatalogReleaseSpecError(`release.yaml carries ${unknown.map((key) => `"${key}"`).join(", ")}; it has exactly two fields, \`version\` and \`actions\`.`);
|
|
728
|
+
}
|
|
729
|
+
const { version: version2, actions } = value;
|
|
730
|
+
if (typeof version2 !== "string" || version2.trim().length === 0) {
|
|
731
|
+
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.`);
|
|
732
|
+
}
|
|
733
|
+
if (actions === null || actions === void 0 || isMapping2(actions) && Object.keys(actions).length === 0) {
|
|
734
|
+
throw new CatalogReleaseSpecError("release.yaml declares no action. An empty release is refused: pinned, it would declare a catalog with nothing in it.");
|
|
735
|
+
}
|
|
736
|
+
if (!isMapping2(actions)) {
|
|
737
|
+
throw new CatalogReleaseSpecError("release.yaml's actions is not a mapping of action key to <repository>@<digest>.");
|
|
738
|
+
}
|
|
739
|
+
return {
|
|
740
|
+
version: version2,
|
|
741
|
+
actions: Object.entries(actions).map(([key, reference]) => {
|
|
742
|
+
const split = typeof reference === "string" ? splitArtifactReference(reference) : null;
|
|
743
|
+
if (split === null) {
|
|
744
|
+
throw new CatalogReleaseSpecError(`release.yaml's ${key} is ${JSON.stringify(reference)}, not <repository>@<digest>.`);
|
|
745
|
+
}
|
|
746
|
+
return {
|
|
747
|
+
key,
|
|
748
|
+
...split
|
|
749
|
+
};
|
|
750
|
+
})
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
__name(parseCatalogReleaseSpec2, "parseCatalogReleaseSpec");
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
// ../contracts/dist/catalog-lock.js
|
|
758
|
+
var require_catalog_lock = __commonJS({
|
|
759
|
+
"../contracts/dist/catalog-lock.js"(exports) {
|
|
760
|
+
"use strict";
|
|
761
|
+
Object.defineProperty(exports, "__esModule", {
|
|
762
|
+
value: true
|
|
763
|
+
});
|
|
764
|
+
exports.CatalogLockError = exports.CATALOG_LOCK_FILE = exports.CATALOG_LOCK_SCHEMA_V1 = void 0;
|
|
765
|
+
exports.parseCatalogLock = parseCatalogLock;
|
|
766
|
+
exports.catalogLockText = catalogLockText;
|
|
767
|
+
exports.authoredElsewhere = authoredElsewhere;
|
|
768
|
+
var action_repository_1 = require_action_repository();
|
|
769
|
+
var catalog_release_1 = require_catalog_release();
|
|
770
|
+
exports.CATALOG_LOCK_SCHEMA_V1 = "runveric.catalog-lock/v1";
|
|
771
|
+
exports.CATALOG_LOCK_FILE = "release.lock.json";
|
|
772
|
+
var CatalogLockError = class CatalogLockError extends Error {
|
|
773
|
+
static {
|
|
774
|
+
__name(this, "CatalogLockError");
|
|
775
|
+
}
|
|
776
|
+
constructor(message) {
|
|
777
|
+
super(message);
|
|
778
|
+
this.name = "CatalogLockError";
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
exports.CatalogLockError = CatalogLockError;
|
|
782
|
+
var SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
783
|
+
function objectAt(path, value, keys) {
|
|
784
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
785
|
+
throw new CatalogLockError(`${path} is not an object.`);
|
|
786
|
+
}
|
|
787
|
+
const unknown = Object.keys(value).filter((key) => !keys.includes(key));
|
|
788
|
+
if (unknown.length > 0) {
|
|
789
|
+
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.`);
|
|
790
|
+
}
|
|
791
|
+
return value;
|
|
792
|
+
}
|
|
793
|
+
__name(objectAt, "objectAt");
|
|
794
|
+
function stringAt(path, value, pattern, expected) {
|
|
795
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
796
|
+
throw new CatalogLockError(`${path} is not a non-empty string.`);
|
|
797
|
+
}
|
|
798
|
+
if (pattern !== void 0 && !pattern.test(value)) {
|
|
799
|
+
throw new CatalogLockError(`${path} is "${value}", which is not ${expected}.`);
|
|
800
|
+
}
|
|
801
|
+
return value;
|
|
802
|
+
}
|
|
803
|
+
__name(stringAt, "stringAt");
|
|
804
|
+
function parseCatalogLock(value) {
|
|
805
|
+
const root = objectAt("the lock", value, [
|
|
806
|
+
"schemaVersion",
|
|
807
|
+
"release",
|
|
808
|
+
"actions"
|
|
809
|
+
]);
|
|
810
|
+
if (root.schemaVersion !== exports.CATALOG_LOCK_SCHEMA_V1) {
|
|
811
|
+
throw new CatalogLockError(`the lock declares schemaVersion ${JSON.stringify(root.schemaVersion)}; this build reads ${exports.CATALOG_LOCK_SCHEMA_V1} only.`);
|
|
812
|
+
}
|
|
813
|
+
const release = objectAt("release", root.release, [
|
|
814
|
+
"artifactRef",
|
|
815
|
+
"digest",
|
|
816
|
+
"version"
|
|
817
|
+
]);
|
|
818
|
+
const lock = {
|
|
819
|
+
schemaVersion: exports.CATALOG_LOCK_SCHEMA_V1,
|
|
820
|
+
release: {
|
|
821
|
+
artifactRef: stringAt("release.artifactRef", release.artifactRef, catalog_release_1.ARTIFACT_REPOSITORY_PATTERN, "a registry repository"),
|
|
822
|
+
digest: stringAt("release.digest", release.digest, catalog_release_1.ARTIFACT_DIGEST_PATTERN, "a sha256 digest"),
|
|
823
|
+
version: stringAt("release.version", release.version)
|
|
824
|
+
},
|
|
825
|
+
actions: []
|
|
826
|
+
};
|
|
827
|
+
if (!Array.isArray(root.actions) || root.actions.length === 0) {
|
|
828
|
+
throw new CatalogLockError("actions is not a non-empty array. A release names at least one action.");
|
|
829
|
+
}
|
|
830
|
+
root.actions.forEach((entry, index) => {
|
|
831
|
+
const path = `actions[${index}]`;
|
|
832
|
+
const action = objectAt(path, entry, [
|
|
833
|
+
"key",
|
|
834
|
+
"artifactRef",
|
|
835
|
+
"digest",
|
|
836
|
+
"manifestSha256",
|
|
837
|
+
"provenance"
|
|
838
|
+
]);
|
|
839
|
+
const key = stringAt(`${path}.key`, action.key);
|
|
840
|
+
const artifactRef = stringAt(`${path}.artifactRef`, action.artifactRef, catalog_release_1.ARTIFACT_REPOSITORY_PATTERN, "a registry repository");
|
|
841
|
+
const misnamed = (0, action_repository_1.actionPackageRefusal)(key, artifactRef);
|
|
842
|
+
if (misnamed !== null) throw new CatalogLockError(`${path}: ${misnamed}`);
|
|
843
|
+
const previous = lock.actions[lock.actions.length - 1];
|
|
844
|
+
if (previous !== void 0 && !(previous.key < key)) {
|
|
845
|
+
throw new CatalogLockError(`${path}.key "${key}" follows "${previous.key}": a lock names each action once, sorted by key.`);
|
|
846
|
+
}
|
|
847
|
+
const provenance2 = objectAt(`${path}.provenance`, action.provenance, [
|
|
848
|
+
"repository",
|
|
849
|
+
"commit"
|
|
850
|
+
]);
|
|
851
|
+
lock.actions.push({
|
|
852
|
+
key,
|
|
853
|
+
artifactRef,
|
|
854
|
+
digest: stringAt(`${path}.digest`, action.digest, catalog_release_1.ARTIFACT_DIGEST_PATTERN, "a sha256 digest"),
|
|
855
|
+
manifestSha256: stringAt(`${path}.manifestSha256`, action.manifestSha256, SHA256_HEX, "64 lowercase hex characters"),
|
|
856
|
+
provenance: {
|
|
857
|
+
repository: stringAt(`${path}.provenance.repository`, provenance2.repository),
|
|
858
|
+
commit: stringAt(`${path}.provenance.commit`, provenance2.commit)
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
});
|
|
862
|
+
return lock;
|
|
863
|
+
}
|
|
864
|
+
__name(parseCatalogLock, "parseCatalogLock");
|
|
865
|
+
function catalogLockText(lock) {
|
|
866
|
+
const ordered = {
|
|
867
|
+
schemaVersion: lock.schemaVersion,
|
|
868
|
+
release: {
|
|
869
|
+
artifactRef: lock.release.artifactRef,
|
|
870
|
+
digest: lock.release.digest,
|
|
871
|
+
version: lock.release.version
|
|
872
|
+
},
|
|
873
|
+
actions: [
|
|
874
|
+
...lock.actions
|
|
875
|
+
].sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map((action) => ({
|
|
876
|
+
key: action.key,
|
|
877
|
+
artifactRef: action.artifactRef,
|
|
878
|
+
digest: action.digest,
|
|
879
|
+
manifestSha256: action.manifestSha256,
|
|
880
|
+
provenance: {
|
|
881
|
+
repository: action.provenance.repository,
|
|
882
|
+
commit: action.provenance.commit
|
|
883
|
+
}
|
|
884
|
+
}))
|
|
885
|
+
};
|
|
886
|
+
return `${JSON.stringify(ordered, null, 2)}
|
|
887
|
+
`;
|
|
888
|
+
}
|
|
889
|
+
__name(catalogLockText, "catalogLockText");
|
|
890
|
+
function authoredElsewhere(lock, key) {
|
|
891
|
+
const namespace = lock.release.artifactRef.slice(0, lock.release.artifactRef.lastIndexOf("/"));
|
|
892
|
+
const organisation = namespace.slice(namespace.lastIndexOf("/") + 1);
|
|
893
|
+
return `${key} is authored in ${organisation}/${(0, action_repository_1.actionRepositoryName)(key)} \u2014 change it there, publish, assemble, vendor`;
|
|
894
|
+
}
|
|
895
|
+
__name(authoredElsewhere, "authoredElsewhere");
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
// ../contracts/dist/catalog-manifest-placeholders.js
|
|
900
|
+
var require_catalog_manifest_placeholders = __commonJS({
|
|
901
|
+
"../contracts/dist/catalog-manifest-placeholders.js"(exports) {
|
|
902
|
+
"use strict";
|
|
903
|
+
Object.defineProperty(exports, "__esModule", {
|
|
904
|
+
value: true
|
|
905
|
+
});
|
|
906
|
+
exports.MANIFEST_PLACEHOLDER_PATTERN = exports.MANIFEST_PLACEHOLDERS = void 0;
|
|
907
|
+
var brand_1 = require_brand();
|
|
908
|
+
exports.MANIFEST_PLACEHOLDERS = {
|
|
909
|
+
BRAND_SHORT_NAME: brand_1.BRAND_SHORT_NAME,
|
|
910
|
+
BRAND_NAME: brand_1.BRAND_NAME
|
|
911
|
+
};
|
|
912
|
+
exports.MANIFEST_PLACEHOLDER_PATTERN = /\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
|
|
560
916
|
// ../contracts/dist/change-control.js
|
|
561
917
|
var require_change_control = __commonJS({
|
|
562
918
|
"../contracts/dist/change-control.js"(exports) {
|
|
@@ -856,8 +1212,8 @@ var require_package_provenance = __commonJS({
|
|
|
856
1212
|
pair.accepted,
|
|
857
1213
|
pair.revoked
|
|
858
1214
|
]);
|
|
859
|
-
function poolProvenanceAuditAction(
|
|
860
|
-
const pair = exports.POOL_PROVENANCE_AUDIT_ACTIONS[
|
|
1215
|
+
function poolProvenanceAuditAction(provenance2, accepted) {
|
|
1216
|
+
const pair = exports.POOL_PROVENANCE_AUDIT_ACTIONS[provenance2];
|
|
861
1217
|
return accepted ? pair.accepted : pair.revoked;
|
|
862
1218
|
}
|
|
863
1219
|
__name(poolProvenanceAuditAction, "poolProvenanceAuditAction");
|
|
@@ -916,7 +1272,9 @@ var require_packages = __commonJS({
|
|
|
916
1272
|
Object.defineProperty(exports, "__esModule", {
|
|
917
1273
|
value: true
|
|
918
1274
|
});
|
|
919
|
-
exports.PACKAGE_IMPORT_WARNING_REASONS = exports.PACKAGE_IMPORT_SKIP_REASON_LABELS = exports.PACKAGE_IMPORT_SKIP_REASONS = exports.PACKAGE_IMPORT_LIMITS = exports.PACKAGE_DRAFT_FILTER_LABELS = exports.PACKAGE_DRAFT_FILTERS = exports.PACKAGE_INTERPRETER_LABEL = exports.PARAMETER_DETECTION_UNAVAILABLE = exports.PARAMETER_DETECTION_SOURCES = exports.PACKAGE_PARAMETERS_UNRESOLVED_COPY = exports.PACKAGE_PARAMETERS_UNRESOLVED_REASONS = exports.PACKAGE_DRAFT_STATES = exports.PACKAGE_VERSION_MODES = exports.ACTION_UNRUNNABLE_REASONS = exports.PACKAGE_SOURCE_RECONNECT_OUTCOMES = exports.ADOPTION_REVIEW_DIRECTIONS = exports.ADOPTION_CHANGE_SEVERITY = exports.ADOPTION_CHANGE_KINDS = exports.ADOPTION_CHANGE_SEVERITIES = exports.ADOPTION_STATUSES = exports.PACKAGE_INTERPRETERS = exports.PACKAGE_RUNTIME_KINDS = exports.PACKAGE_RUNNER_CONNECTOR_KEY = void 0;
|
|
1275
|
+
exports.PACKAGE_IMPORT_WARNING_REASONS = exports.PACKAGE_IMPORT_SKIP_REASON_LABELS = exports.PACKAGE_IMPORT_SKIP_REASONS = exports.PACKAGE_IMPORT_LIMITS = exports.PACKAGE_DRAFT_FILTER_LABELS = exports.PACKAGE_DRAFT_FILTERS = exports.PACKAGE_INTERPRETER_LABEL = exports.PARAMETER_DETECTION_UNAVAILABLE = exports.PARAMETER_DETECTION_SOURCES = exports.PACKAGE_PARAMETERS_UNRESOLVED_COPY = exports.PACKAGE_PARAMETERS_UNRESOLVED_REASONS = exports.PACKAGE_DRAFT_STATES = exports.PACKAGE_VERSION_MODES = exports.ACTION_UNRUNNABLE_REASONS = exports.PACKAGE_SOURCE_RECONNECT_OUTCOMES = exports.ADOPTION_REVIEW_DIRECTIONS = exports.ADOPTION_CHANGE_SEVERITY = exports.ADOPTION_CHANGE_KINDS = exports.ADOPTION_CHANGE_SEVERITIES = exports.ADOPTION_STATUSES = exports.PACKAGE_INTERPRETERS = exports.PACKAGE_RUNTIME_KINDS = exports.LEGACY_PACKAGE_RUNNER_CONNECTOR_KEY = exports.PACKAGE_RUNNER_CONNECTOR_KEY = void 0;
|
|
1276
|
+
exports.isPackageRunnerConnectorKey = isPackageRunnerConnectorKey;
|
|
1277
|
+
exports.canonicalConnectorKey = canonicalConnectorKey;
|
|
920
1278
|
exports.packageSourceConnectedRemovalMessage = packageSourceConnectedRemovalMessage;
|
|
921
1279
|
exports.packageVersionAdoptedRemovalMessage = packageVersionAdoptedRemovalMessage;
|
|
922
1280
|
exports.actionUnrunnableCopy = actionUnrunnableCopy;
|
|
@@ -927,7 +1285,16 @@ var require_packages = __commonJS({
|
|
|
927
1285
|
__exportStar(require_package_provenance(), exports);
|
|
928
1286
|
__exportStar(require_package_tier(), exports);
|
|
929
1287
|
var brand_1 = require_brand();
|
|
930
|
-
exports.PACKAGE_RUNNER_CONNECTOR_KEY = "
|
|
1288
|
+
exports.PACKAGE_RUNNER_CONNECTOR_KEY = "runveric.package-runner";
|
|
1289
|
+
exports.LEGACY_PACKAGE_RUNNER_CONNECTOR_KEY = "opera.package-runner";
|
|
1290
|
+
function isPackageRunnerConnectorKey(connectorKey) {
|
|
1291
|
+
return connectorKey === exports.PACKAGE_RUNNER_CONNECTOR_KEY || connectorKey === exports.LEGACY_PACKAGE_RUNNER_CONNECTOR_KEY;
|
|
1292
|
+
}
|
|
1293
|
+
__name(isPackageRunnerConnectorKey, "isPackageRunnerConnectorKey");
|
|
1294
|
+
function canonicalConnectorKey(connectorKey) {
|
|
1295
|
+
return isPackageRunnerConnectorKey(connectorKey) ? exports.PACKAGE_RUNNER_CONNECTOR_KEY : connectorKey;
|
|
1296
|
+
}
|
|
1297
|
+
__name(canonicalConnectorKey, "canonicalConnectorKey");
|
|
931
1298
|
exports.PACKAGE_RUNTIME_KINDS = [
|
|
932
1299
|
"interpreter",
|
|
933
1300
|
"oci"
|
|
@@ -1248,7 +1615,7 @@ var require_packages = __commonJS({
|
|
|
1248
1615
|
"editor_metadata",
|
|
1249
1616
|
"os_metadata",
|
|
1250
1617
|
"lockfile",
|
|
1251
|
-
"
|
|
1618
|
+
"runveric_scratch",
|
|
1252
1619
|
"not_text"
|
|
1253
1620
|
];
|
|
1254
1621
|
exports.PACKAGE_IMPORT_SKIP_REASON_LABELS = {
|
|
@@ -1258,7 +1625,7 @@ var require_packages = __commonJS({
|
|
|
1258
1625
|
editor_metadata: "editor settings",
|
|
1259
1626
|
os_metadata: "operating-system metadata",
|
|
1260
1627
|
lockfile: "a dependency lockfile",
|
|
1261
|
-
|
|
1628
|
+
runveric_scratch: "scratch written by `runveric test`",
|
|
1262
1629
|
not_text: "not UTF-8 text"
|
|
1263
1630
|
};
|
|
1264
1631
|
exports.PACKAGE_IMPORT_WARNING_REASONS = [
|
|
@@ -1274,7 +1641,7 @@ var require_validation = __commonJS({
|
|
|
1274
1641
|
Object.defineProperty(exports, "__esModule", {
|
|
1275
1642
|
value: true
|
|
1276
1643
|
});
|
|
1277
|
-
exports.TARGET_COMPARISON_KINDS = exports.TARGETS_NO_SCOPE = exports.TARGETS_NOT_COUNTED = exports.TARGET_COUNT_ABSENCES = void 0;
|
|
1644
|
+
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;
|
|
1278
1645
|
exports.isTargetCountAbsence = isTargetCountAbsence;
|
|
1279
1646
|
exports.singleTarget = singleTarget;
|
|
1280
1647
|
exports.noTargetsResolved = noTargetsResolved;
|
|
@@ -1284,6 +1651,9 @@ var require_validation = __commonJS({
|
|
|
1284
1651
|
exports.eligibleTargetCount = eligibleTargetCount;
|
|
1285
1652
|
exports.targetsValidationCheck = targetsValidationCheck;
|
|
1286
1653
|
exports.targetSummaryOf = targetSummaryOf;
|
|
1654
|
+
exports.isCheckLevel = isCheckLevel;
|
|
1655
|
+
exports.isRefusalCode = isRefusalCode;
|
|
1656
|
+
exports.isDecisionCode = isDecisionCode;
|
|
1287
1657
|
exports.scopeFailureDetail = scopeFailureDetail;
|
|
1288
1658
|
exports.targetsUnavailableFrom = targetsUnavailableFrom;
|
|
1289
1659
|
exports.compareTargets = compareTargets;
|
|
@@ -1400,6 +1770,38 @@ var require_validation = __commonJS({
|
|
|
1400
1770
|
return exports.TARGETS_NOT_COUNTED;
|
|
1401
1771
|
}
|
|
1402
1772
|
__name(targetSummaryOf, "targetSummaryOf");
|
|
1773
|
+
exports.CHECK_LEVELS = [
|
|
1774
|
+
"info",
|
|
1775
|
+
"warn",
|
|
1776
|
+
"error"
|
|
1777
|
+
];
|
|
1778
|
+
function isCheckLevel(value) {
|
|
1779
|
+
return typeof value === "string" && exports.CHECK_LEVELS.includes(value);
|
|
1780
|
+
}
|
|
1781
|
+
__name(isCheckLevel, "isCheckLevel");
|
|
1782
|
+
exports.REFUSAL_CODES = [
|
|
1783
|
+
"disk.notUnattached",
|
|
1784
|
+
"disk.locked",
|
|
1785
|
+
"disk.shared",
|
|
1786
|
+
"disk.inRecoveryPlan",
|
|
1787
|
+
"disk.keyUnusable",
|
|
1788
|
+
"disk.unverifiable"
|
|
1789
|
+
];
|
|
1790
|
+
function isRefusalCode(value) {
|
|
1791
|
+
return typeof value === "string" && exports.REFUSAL_CODES.includes(value);
|
|
1792
|
+
}
|
|
1793
|
+
__name(isRefusalCode, "isRefusalCode");
|
|
1794
|
+
exports.DECISION_CODES = [
|
|
1795
|
+
"disk.recentlyDetached",
|
|
1796
|
+
"disk.customerKey",
|
|
1797
|
+
"disk.customerKeyUnverified",
|
|
1798
|
+
"disk.guestEncrypted",
|
|
1799
|
+
"disk.noSnapshot"
|
|
1800
|
+
];
|
|
1801
|
+
function isDecisionCode(value) {
|
|
1802
|
+
return typeof value === "string" && exports.DECISION_CODES.includes(value);
|
|
1803
|
+
}
|
|
1804
|
+
__name(isDecisionCode, "isDecisionCode");
|
|
1403
1805
|
function scopeFailureDetail(error) {
|
|
1404
1806
|
const raw = error instanceof Error ? error.message : String(error);
|
|
1405
1807
|
const oneLine = raw.replace(/\s+/g, " ").trim().replace(/[.!]+$/, "");
|
|
@@ -1470,7 +1872,7 @@ var require_execution = __commonJS({
|
|
|
1470
1872
|
Object.defineProperty(exports, "__esModule", {
|
|
1471
1873
|
value: true
|
|
1472
1874
|
});
|
|
1473
|
-
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;
|
|
1875
|
+
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.REFUSAL_CODES = exports.isRefusalCode = exports.isDecisionCode = exports.isCheckLevel = exports.DECISION_CODES = exports.CHECK_LEVELS = exports.SUBSCRIPTION_ID_FIELD_RENAMED = void 0;
|
|
1474
1876
|
exports.executionLogStreamSentence = executionLogStreamSentence;
|
|
1475
1877
|
exports.actionRunnabilityCopy = actionRunnabilityCopy;
|
|
1476
1878
|
exports.actionRunnabilityHeadline = actionRunnabilityHeadline;
|
|
@@ -1481,6 +1883,42 @@ var require_execution = __commonJS({
|
|
|
1481
1883
|
var brand_1 = require_brand();
|
|
1482
1884
|
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.";
|
|
1483
1885
|
var validation_1 = require_validation();
|
|
1886
|
+
Object.defineProperty(exports, "CHECK_LEVELS", {
|
|
1887
|
+
enumerable: true,
|
|
1888
|
+
get: /* @__PURE__ */ __name(function() {
|
|
1889
|
+
return validation_1.CHECK_LEVELS;
|
|
1890
|
+
}, "get")
|
|
1891
|
+
});
|
|
1892
|
+
Object.defineProperty(exports, "DECISION_CODES", {
|
|
1893
|
+
enumerable: true,
|
|
1894
|
+
get: /* @__PURE__ */ __name(function() {
|
|
1895
|
+
return validation_1.DECISION_CODES;
|
|
1896
|
+
}, "get")
|
|
1897
|
+
});
|
|
1898
|
+
Object.defineProperty(exports, "isCheckLevel", {
|
|
1899
|
+
enumerable: true,
|
|
1900
|
+
get: /* @__PURE__ */ __name(function() {
|
|
1901
|
+
return validation_1.isCheckLevel;
|
|
1902
|
+
}, "get")
|
|
1903
|
+
});
|
|
1904
|
+
Object.defineProperty(exports, "isDecisionCode", {
|
|
1905
|
+
enumerable: true,
|
|
1906
|
+
get: /* @__PURE__ */ __name(function() {
|
|
1907
|
+
return validation_1.isDecisionCode;
|
|
1908
|
+
}, "get")
|
|
1909
|
+
});
|
|
1910
|
+
Object.defineProperty(exports, "isRefusalCode", {
|
|
1911
|
+
enumerable: true,
|
|
1912
|
+
get: /* @__PURE__ */ __name(function() {
|
|
1913
|
+
return validation_1.isRefusalCode;
|
|
1914
|
+
}, "get")
|
|
1915
|
+
});
|
|
1916
|
+
Object.defineProperty(exports, "REFUSAL_CODES", {
|
|
1917
|
+
enumerable: true,
|
|
1918
|
+
get: /* @__PURE__ */ __name(function() {
|
|
1919
|
+
return validation_1.REFUSAL_CODES;
|
|
1920
|
+
}, "get")
|
|
1921
|
+
});
|
|
1484
1922
|
Object.defineProperty(exports, "compareTargets", {
|
|
1485
1923
|
enumerable: true,
|
|
1486
1924
|
get: /* @__PURE__ */ __name(function() {
|
|
@@ -1622,6 +2060,7 @@ var require_execution = __commonJS({
|
|
|
1622
2060
|
}
|
|
1623
2061
|
__name(executionLogStreamSentence, "executionLogStreamSentence");
|
|
1624
2062
|
var ACTION_RUNNABILITY_CAUSE_KIND_SET = {
|
|
2063
|
+
catalog_blocked: true,
|
|
1625
2064
|
connector_unregistered: true,
|
|
1626
2065
|
package: true,
|
|
1627
2066
|
runner_pool: true,
|
|
@@ -1631,8 +2070,12 @@ var require_execution = __commonJS({
|
|
|
1631
2070
|
};
|
|
1632
2071
|
exports.ACTION_RUNNABILITY_CAUSE_KINDS = Object.keys(ACTION_RUNNABILITY_CAUSE_KIND_SET);
|
|
1633
2072
|
exports.CONNECTOR_UNAVAILABLE_MESSAGE = "This action is not yet available to run.";
|
|
2073
|
+
var CATALOG_BLOCKED_CAUSE = "This workspace does not permit this action.";
|
|
2074
|
+
var CATALOG_BLOCKED_REMEDY = "An administrator of this workspace decides which catalog actions may run here, and can allow it.";
|
|
1634
2075
|
function actionRunnabilityCopy(cause) {
|
|
1635
2076
|
switch (cause.kind) {
|
|
2077
|
+
case "catalog_blocked":
|
|
2078
|
+
return `${CATALOG_BLOCKED_CAUSE} ${CATALOG_BLOCKED_REMEDY}`;
|
|
1636
2079
|
case "package":
|
|
1637
2080
|
return (0, packages_2.actionUnrunnableCopy)(cause.detail);
|
|
1638
2081
|
case "runner_pool": {
|
|
@@ -1664,7 +2107,7 @@ var require_execution = __commonJS({
|
|
|
1664
2107
|
}
|
|
1665
2108
|
__name(describeRoutingPlaces, "describeRoutingPlaces");
|
|
1666
2109
|
function runnerPoolClauses(cause) {
|
|
1667
|
-
const
|
|
2110
|
+
const provenance2 = cause.provenance ? `is ${packages_1.PACKAGE_PROVENANCE_CLAUSES[cause.provenance]}` : (
|
|
1668
2111
|
// every other value this sentence could take is a claim about where bytes came from.
|
|
1669
2112
|
"has no recorded provenance"
|
|
1670
2113
|
);
|
|
@@ -1682,7 +2125,7 @@ var require_execution = __commonJS({
|
|
|
1682
2125
|
* credential it acts with. A refusal that dropped those would be telling an admin the
|
|
1683
2126
|
* decision no longer matters.
|
|
1684
2127
|
*/
|
|
1685
|
-
cause: `${
|
|
2128
|
+
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.`,
|
|
1686
2129
|
remedy: `${acceptedClause(cause.accepts)} ${POOL_CONSENT_REMEDY}`
|
|
1687
2130
|
};
|
|
1688
2131
|
}
|
|
@@ -1721,6 +2164,8 @@ var require_execution = __commonJS({
|
|
|
1721
2164
|
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.`;
|
|
1722
2165
|
function actionRunnabilityHeadline(cause) {
|
|
1723
2166
|
switch (cause.kind) {
|
|
2167
|
+
case "catalog_blocked":
|
|
2168
|
+
return "this workspace does not permit its action \u2014 an administrator of this workspace can allow it";
|
|
1724
2169
|
case "package":
|
|
1725
2170
|
return (0, packages_2.actionUnrunnableHeadline)(cause.detail);
|
|
1726
2171
|
case "runner_pool":
|
|
@@ -1738,6 +2183,8 @@ var require_execution = __commonJS({
|
|
|
1738
2183
|
__name(actionRunnabilityHeadline, "actionRunnabilityHeadline");
|
|
1739
2184
|
function actionRunnabilityRefusal(cause) {
|
|
1740
2185
|
switch (cause.kind) {
|
|
2186
|
+
case "catalog_blocked":
|
|
2187
|
+
return `${CATALOG_BLOCKED_CAUSE} ${CATALOG_BLOCKED_REMEDY} Nothing was started.`;
|
|
1741
2188
|
case "package":
|
|
1742
2189
|
return (0, packages_2.actionUnrunnableRefusal)(cause.detail, cause);
|
|
1743
2190
|
case "runner_pool": {
|
|
@@ -1762,6 +2209,80 @@ var require_execution = __commonJS({
|
|
|
1762
2209
|
}
|
|
1763
2210
|
});
|
|
1764
2211
|
|
|
2212
|
+
// ../contracts/dist/decisions.js
|
|
2213
|
+
var require_decisions = __commonJS({
|
|
2214
|
+
"../contracts/dist/decisions.js"(exports) {
|
|
2215
|
+
"use strict";
|
|
2216
|
+
Object.defineProperty(exports, "__esModule", {
|
|
2217
|
+
value: true
|
|
2218
|
+
});
|
|
2219
|
+
exports.DECISION_ACKNOWLEDGEMENT = void 0;
|
|
2220
|
+
exports.recordedDecisionsOf = recordedDecisionsOf;
|
|
2221
|
+
exports.raisedDecisionCodes = raisedDecisionCodes;
|
|
2222
|
+
exports.decisionMismatch = decisionMismatch;
|
|
2223
|
+
exports.decisionMismatchSentence = decisionMismatchSentence;
|
|
2224
|
+
var validation_1 = require_validation();
|
|
2225
|
+
function recordedDecisionsOf(stored) {
|
|
2226
|
+
if (!Array.isArray(stored)) return [];
|
|
2227
|
+
const read = [];
|
|
2228
|
+
for (const entry of stored) {
|
|
2229
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
2230
|
+
const { code, acknowledgedBy, acknowledgedAt } = entry;
|
|
2231
|
+
if (!(0, validation_1.isDecisionCode)(code)) continue;
|
|
2232
|
+
if (typeof acknowledgedBy !== "string" || acknowledgedBy.length === 0) continue;
|
|
2233
|
+
if (typeof acknowledgedAt !== "string" || Number.isNaN(Date.parse(acknowledgedAt))) continue;
|
|
2234
|
+
if (read.some((decision) => decision.code === code)) continue;
|
|
2235
|
+
read.push({
|
|
2236
|
+
code,
|
|
2237
|
+
acknowledgedBy,
|
|
2238
|
+
acknowledgedAt
|
|
2239
|
+
});
|
|
2240
|
+
}
|
|
2241
|
+
return read;
|
|
2242
|
+
}
|
|
2243
|
+
__name(recordedDecisionsOf, "recordedDecisionsOf");
|
|
2244
|
+
function raisedDecisionCodes(checks) {
|
|
2245
|
+
const raised = [];
|
|
2246
|
+
for (const check of checks) {
|
|
2247
|
+
if (check.decision && !raised.includes(check.decision)) raised.push(check.decision);
|
|
2248
|
+
}
|
|
2249
|
+
return raised;
|
|
2250
|
+
}
|
|
2251
|
+
__name(raisedDecisionCodes, "raisedDecisionCodes");
|
|
2252
|
+
function decisionMismatch(raised, acknowledged) {
|
|
2253
|
+
const unacknowledged = raised.filter((code) => !acknowledged.includes(code));
|
|
2254
|
+
const notRaised = acknowledged.filter((code) => !raised.includes(code));
|
|
2255
|
+
return unacknowledged.length === 0 && notRaised.length === 0 ? null : {
|
|
2256
|
+
unacknowledged,
|
|
2257
|
+
notRaised
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
__name(decisionMismatch, "decisionMismatch");
|
|
2261
|
+
function decisionMismatchSentence(mismatch) {
|
|
2262
|
+
const parts = [];
|
|
2263
|
+
if (mismatch.unacknowledged.length > 0) {
|
|
2264
|
+
parts.push(`the plan raised ${quoteCodes(mismatch.unacknowledged)}, which ${mismatch.unacknowledged.length === 1 ? "was" : "were"} not acknowledged`);
|
|
2265
|
+
}
|
|
2266
|
+
if (mismatch.notRaised.length > 0) {
|
|
2267
|
+
parts.push(`${quoteCodes(mismatch.notRaised)} ${mismatch.notRaised.length === 1 ? "was" : "were"} acknowledged but the plan does not raise ${mismatch.notRaised.length === 1 ? "it" : "them"}`);
|
|
2268
|
+
}
|
|
2269
|
+
return `This run was not submitted: ${parts.join(", and ")}. The checks were run again at submit and decide what needs acknowledging \u2014 review the plan and submit again.`;
|
|
2270
|
+
}
|
|
2271
|
+
__name(decisionMismatchSentence, "decisionMismatchSentence");
|
|
2272
|
+
function quoteCodes(codes) {
|
|
2273
|
+
return codes.map((code) => `"${code}"`).join(", ");
|
|
2274
|
+
}
|
|
2275
|
+
__name(quoteCodes, "quoteCodes");
|
|
2276
|
+
exports.DECISION_ACKNOWLEDGEMENT = {
|
|
2277
|
+
"disk.recentlyDetached": "Delete it even though it was detached recently",
|
|
2278
|
+
"disk.customerKey": "Delete it \u2014 restoring later depends on this key staying enabled",
|
|
2279
|
+
"disk.customerKeyUnverified": "Delete it without confirmation that the key is enabled",
|
|
2280
|
+
"disk.guestEncrypted": "Delete it \u2014 restoring later depends on the guest's encryption secrets",
|
|
2281
|
+
"disk.noSnapshot": "Delete it with no snapshot \u2014 this cannot be undone"
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
});
|
|
2285
|
+
|
|
1765
2286
|
// ../contracts/dist/execution-site.js
|
|
1766
2287
|
var require_execution_site = __commonJS({
|
|
1767
2288
|
"../contracts/dist/execution-site.js"(exports) {
|
|
@@ -1846,7 +2367,13 @@ var require_resources = __commonJS({
|
|
|
1846
2367
|
Object.defineProperty(exports, "__esModule", {
|
|
1847
2368
|
value: true
|
|
1848
2369
|
});
|
|
1849
|
-
exports.DISCOVERY_TYPE_PATTERN = exports.
|
|
2370
|
+
exports.DISCOVERY_TYPE_PATTERN = exports.LEGACY_DISCOVERY_PROVIDER = exports.DISCOVERY_VOCABULARY = void 0;
|
|
2371
|
+
exports.discoveryTypesOf = discoveryTypesOf;
|
|
2372
|
+
exports.declaredDiscoveryTypes = declaredDiscoveryTypes;
|
|
2373
|
+
exports.liveDiscoveryTypes = liveDiscoveryTypes;
|
|
2374
|
+
exports.canonicalDiscoveryId = canonicalDiscoveryId;
|
|
2375
|
+
exports.resolveDiscoveryType = resolveDiscoveryType;
|
|
2376
|
+
exports.isSecretReference = isSecretReference;
|
|
1850
2377
|
exports.parseDiscoveryType = parseDiscoveryType;
|
|
1851
2378
|
exports.formatDiscoveryType = formatDiscoveryType;
|
|
1852
2379
|
exports.discoveryTypeSpec = discoveryTypeSpec;
|
|
@@ -1862,16 +2389,20 @@ var require_resources = __commonJS({
|
|
|
1862
2389
|
resourceGroup: {
|
|
1863
2390
|
requires: [],
|
|
1864
2391
|
routable: false,
|
|
1865
|
-
identity: null
|
|
2392
|
+
identity: null,
|
|
2393
|
+
secretRef: false
|
|
1866
2394
|
},
|
|
1867
2395
|
// ROUTABLE BUT NOT NAMABLE, and the two are different questions. A rule matches a vault by
|
|
1868
2396
|
// its NAME and its tags, which discovery supplies; an ARM vault id needs the resource group
|
|
1869
2397
|
// this vocabulary never asks for (`requires` is empty so vaults list subscription-wide). See
|
|
1870
2398
|
// `identity` on the spec above — null is the honest answer, not an omission.
|
|
2399
|
+
// NOT a secret reference: a vault CONTAINS secrets, and naming one reveals nothing a run
|
|
2400
|
+
// could read. The secret inside it is the reference (`keyVaultSecret` below).
|
|
1871
2401
|
keyVault: {
|
|
1872
2402
|
requires: [],
|
|
1873
2403
|
routable: true,
|
|
1874
|
-
identity: null
|
|
2404
|
+
identity: null,
|
|
2405
|
+
secretRef: false
|
|
1875
2406
|
},
|
|
1876
2407
|
// ★ A DATA-PLANE URI, NOT AN ARM PATH, AND IT IS WHAT DISCOVERY ALREADY RETURNS. The Key
|
|
1877
2408
|
// Vault SDK reports `SecretProperties.id` as `https://<vault>.vault.azure.net/secrets/<name>`
|
|
@@ -1883,27 +2414,33 @@ var require_resources = __commonJS({
|
|
|
1883
2414
|
"vault"
|
|
1884
2415
|
],
|
|
1885
2416
|
routable: true,
|
|
1886
|
-
identity: "https://{vault}.vault.azure.net/secrets/{name}"
|
|
2417
|
+
identity: "https://{vault}.vault.azure.net/secrets/{name}",
|
|
2418
|
+
// ★ THE ONE TYPE TODAY WHOSE VALUE POINTS AT A SECRET (docs/122 §3.5). The parameter holds
|
|
2419
|
+
// the secret's NAME within its vault; nothing in the platform ever reads the value behind it.
|
|
2420
|
+
secretRef: true
|
|
1887
2421
|
},
|
|
1888
2422
|
// A catalogue value. Nothing is reached by naming a region.
|
|
1889
2423
|
location: {
|
|
1890
2424
|
requires: [],
|
|
1891
2425
|
routable: false,
|
|
1892
|
-
identity: null
|
|
2426
|
+
identity: null,
|
|
2427
|
+
secretRef: false
|
|
1893
2428
|
},
|
|
1894
2429
|
managedDisk: {
|
|
1895
2430
|
requires: [
|
|
1896
2431
|
"resourceGroup"
|
|
1897
2432
|
],
|
|
1898
2433
|
routable: true,
|
|
1899
|
-
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/disks/{name}"
|
|
2434
|
+
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/disks/{name}",
|
|
2435
|
+
secretRef: false
|
|
1900
2436
|
},
|
|
1901
2437
|
sqlServer: {
|
|
1902
2438
|
requires: [
|
|
1903
2439
|
"resourceGroup"
|
|
1904
2440
|
],
|
|
1905
2441
|
routable: true,
|
|
1906
|
-
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{name}"
|
|
2442
|
+
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{name}",
|
|
2443
|
+
secretRef: false
|
|
1907
2444
|
},
|
|
1908
2445
|
sqlDatabase: {
|
|
1909
2446
|
requires: [
|
|
@@ -1911,7 +2448,8 @@ var require_resources = __commonJS({
|
|
|
1911
2448
|
"server"
|
|
1912
2449
|
],
|
|
1913
2450
|
routable: true,
|
|
1914
|
-
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{server}/databases/{name}"
|
|
2451
|
+
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{server}/databases/{name}",
|
|
2452
|
+
secretRef: false
|
|
1915
2453
|
},
|
|
1916
2454
|
// The one type where LISTING and IDENTIFYING are different questions. It lists
|
|
1917
2455
|
// SUBSCRIPTION-WIDE when no resource group is given (`virtualMachines.listAll()`,
|
|
@@ -1931,7 +2469,8 @@ var require_resources = __commonJS({
|
|
|
1931
2469
|
"resourceGroup"
|
|
1932
2470
|
],
|
|
1933
2471
|
routable: true,
|
|
1934
|
-
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{name}"
|
|
2472
|
+
identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{name}",
|
|
2473
|
+
secretRef: false
|
|
1935
2474
|
},
|
|
1936
2475
|
// The sizes ONE machine can actually be resized to right now — not the region's SKU
|
|
1937
2476
|
// catalogue. `requires` is genuinely both fields' answer here: the safe set cannot be
|
|
@@ -1945,16 +2484,53 @@ var require_resources = __commonJS({
|
|
|
1945
2484
|
"vm"
|
|
1946
2485
|
],
|
|
1947
2486
|
routable: false,
|
|
1948
|
-
identity: null
|
|
2487
|
+
identity: null,
|
|
2488
|
+
secretRef: false
|
|
1949
2489
|
}
|
|
1950
2490
|
};
|
|
1951
|
-
|
|
2491
|
+
var DECLARED_DISCOVERY = {
|
|
1952
2492
|
azure: AZURE_DISCOVERY_TYPES,
|
|
1953
2493
|
aws: {},
|
|
1954
2494
|
gcp: {},
|
|
1955
2495
|
terraform: {}
|
|
1956
2496
|
};
|
|
1957
|
-
exports.
|
|
2497
|
+
exports.DISCOVERY_VOCABULARY = DECLARED_DISCOVERY;
|
|
2498
|
+
exports.LEGACY_DISCOVERY_PROVIDER = "azure";
|
|
2499
|
+
function discoveryTypesOf(provider) {
|
|
2500
|
+
return Object.keys(exports.DISCOVERY_VOCABULARY[provider]).map((type) => formatDiscoveryType(provider, type));
|
|
2501
|
+
}
|
|
2502
|
+
__name(discoveryTypesOf, "discoveryTypesOf");
|
|
2503
|
+
function declaredDiscoveryTypes() {
|
|
2504
|
+
return enums_1.PROVIDERS.flatMap(discoveryTypesOf);
|
|
2505
|
+
}
|
|
2506
|
+
__name(declaredDiscoveryTypes, "declaredDiscoveryTypes");
|
|
2507
|
+
function liveDiscoveryTypes(registered) {
|
|
2508
|
+
const providers = new Set(registered);
|
|
2509
|
+
return enums_1.PROVIDERS.filter((provider) => providers.has(provider)).flatMap(discoveryTypesOf);
|
|
2510
|
+
}
|
|
2511
|
+
__name(liveDiscoveryTypes, "liveDiscoveryTypes");
|
|
2512
|
+
function canonicalDiscoveryId(value) {
|
|
2513
|
+
const parsed = parseDiscoveryType(value.trim(), exports.LEGACY_DISCOVERY_PROVIDER);
|
|
2514
|
+
return parsed ? formatDiscoveryType(parsed.provider, parsed.type) : null;
|
|
2515
|
+
}
|
|
2516
|
+
__name(canonicalDiscoveryId, "canonicalDiscoveryId");
|
|
2517
|
+
function resolveDiscoveryType(value) {
|
|
2518
|
+
const parsed = parseDiscoveryType(value.trim(), exports.LEGACY_DISCOVERY_PROVIDER);
|
|
2519
|
+
if (!parsed) return null;
|
|
2520
|
+
const spec = discoveryTypeSpec(parsed.provider, parsed.type);
|
|
2521
|
+
if (!spec) return null;
|
|
2522
|
+
return {
|
|
2523
|
+
id: formatDiscoveryType(parsed.provider, parsed.type),
|
|
2524
|
+
provider: parsed.provider,
|
|
2525
|
+
type: parsed.type,
|
|
2526
|
+
spec
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
__name(resolveDiscoveryType, "resolveDiscoveryType");
|
|
2530
|
+
function isSecretReference(discover) {
|
|
2531
|
+
return discover ? resolveDiscoveryType(discover)?.spec.secretRef === true : false;
|
|
2532
|
+
}
|
|
2533
|
+
__name(isSecretReference, "isSecretReference");
|
|
1958
2534
|
exports.DISCOVERY_TYPE_PATTERN = /^(?:([a-z][a-z0-9]*)\/)?([a-z][a-zA-Z0-9]{0,63})$/;
|
|
1959
2535
|
function parseDiscoveryType(value, fallbackProvider) {
|
|
1960
2536
|
const match = exports.DISCOVERY_TYPE_PATTERN.exec(value);
|
|
@@ -1978,7 +2554,9 @@ var require_resources = __commonJS({
|
|
|
1978
2554
|
}
|
|
1979
2555
|
__name(formatDiscoveryType, "formatDiscoveryType");
|
|
1980
2556
|
function discoveryTypeSpec(provider, type) {
|
|
1981
|
-
|
|
2557
|
+
if (!Object.prototype.hasOwnProperty.call(exports.DISCOVERY_VOCABULARY, provider)) return void 0;
|
|
2558
|
+
const vocabulary = exports.DISCOVERY_VOCABULARY[provider];
|
|
2559
|
+
return Object.prototype.hasOwnProperty.call(vocabulary, type) ? vocabulary[type] : void 0;
|
|
1982
2560
|
}
|
|
1983
2561
|
__name(discoveryTypeSpec, "discoveryTypeSpec");
|
|
1984
2562
|
function dependsOnLookup(keys, dependsOn) {
|
|
@@ -2279,6 +2857,28 @@ var require_runs = __commonJS({
|
|
|
2279
2857
|
}
|
|
2280
2858
|
});
|
|
2281
2859
|
|
|
2860
|
+
// ../contracts/dist/evidence-pack.js
|
|
2861
|
+
var require_evidence_pack = __commonJS({
|
|
2862
|
+
"../contracts/dist/evidence-pack.js"(exports) {
|
|
2863
|
+
"use strict";
|
|
2864
|
+
Object.defineProperty(exports, "__esModule", {
|
|
2865
|
+
value: true
|
|
2866
|
+
});
|
|
2867
|
+
exports.EVIDENCE_EXPORTED_AUDIT_ACTION = exports.EVIDENCE_DIGEST_HEADER = void 0;
|
|
2868
|
+
exports.evidencePackFilename = evidencePackFilename;
|
|
2869
|
+
exports.EVIDENCE_DIGEST_HEADER = "X-Evidence-Digest";
|
|
2870
|
+
exports.EVIDENCE_EXPORTED_AUDIT_ACTION = "operation.evidence_exported";
|
|
2871
|
+
function evidencePackFilename(operationKey, from, to) {
|
|
2872
|
+
return `${operationKey}-evidence-${compactInstant(from)}-${compactInstant(to)}.json`;
|
|
2873
|
+
}
|
|
2874
|
+
__name(evidencePackFilename, "evidencePackFilename");
|
|
2875
|
+
function compactInstant(iso) {
|
|
2876
|
+
return iso.replace(/\.\d+(?=Z|[+-]\d{2}:?\d{2}$)/, "").replace(/[-:]/g, "");
|
|
2877
|
+
}
|
|
2878
|
+
__name(compactInstant, "compactInstant");
|
|
2879
|
+
}
|
|
2880
|
+
});
|
|
2881
|
+
|
|
2282
2882
|
// ../contracts/dist/package-egress.js
|
|
2283
2883
|
var require_package_egress = __commonJS({
|
|
2284
2884
|
"../contracts/dist/package-egress.js"(exports) {
|
|
@@ -2352,18 +2952,18 @@ var require_package_execution = __commonJS({
|
|
|
2352
2952
|
exports.PACKAGE_CONTAINER_ENV = {
|
|
2353
2953
|
/** The control-plane origin the container redeems its work item at and reports to. */
|
|
2354
2954
|
callbackBaseUrl: "OPERA_CALLBACK_BASE_URL",
|
|
2355
|
-
executionId: "
|
|
2955
|
+
executionId: "RUNVERIC_EXECUTION_ID",
|
|
2356
2956
|
/** Single-use; spent on the first redemption. Never copied into the package's own env. */
|
|
2357
2957
|
runToken: "OPERA_RUN_TOKEN",
|
|
2358
2958
|
/** What the bytes it fetched must hash to. Travels beside the work item, never inside it. */
|
|
2359
|
-
contentDigest: "
|
|
2959
|
+
contentDigest: "RUNVERIC_CONTENT_DIGEST",
|
|
2360
2960
|
/** Which runtime image this is, recorded on the run. */
|
|
2361
2961
|
runnerImageDigest: "OPERA_RUNNER_IMAGE_DIGEST",
|
|
2362
2962
|
/** Where the PACKAGE's outbound traffic is pointed. Absent when it declared no network. */
|
|
2363
|
-
egressProxyUrl: "
|
|
2963
|
+
egressProxyUrl: "RUNVERIC_EGRESS_PROXY_URL",
|
|
2364
2964
|
/** The runner-only control channel. Never copied into the package's own env. */
|
|
2365
|
-
egressControlUrl: "
|
|
2366
|
-
egressControlToken: "
|
|
2965
|
+
egressControlUrl: "RUNVERIC_EGRESS_CONTROL_URL",
|
|
2966
|
+
egressControlToken: "RUNVERIC_EGRESS_CONTROL_TOKEN"
|
|
2367
2967
|
};
|
|
2368
2968
|
}
|
|
2369
2969
|
});
|
|
@@ -2375,17 +2975,17 @@ var require_runner_confinement = __commonJS({
|
|
|
2375
2975
|
Object.defineProperty(exports, "__esModule", {
|
|
2376
2976
|
value: true
|
|
2377
2977
|
});
|
|
2378
|
-
exports.
|
|
2978
|
+
exports.CONFINEMENT_FINDING_LABEL = exports.CONFINEMENT_FINDING_SENTENCE = exports.RUNNER_ISOLATION_BLOCKS_UNDECLARED_EGRESS = exports.CONFINEMENT_FINDING_BLOCKS_EGRESS = exports.CONFINEMENT_FINDINGS = exports.RUNNER_SUBSTRATE_LABEL = exports.RUNNER_SUBSTRATES = exports.EGRESS_GATE_SHUT = exports.EGRESS_GATE_OPEN = exports.EGRESS_GATE_PREFIX = exports.EGRESS_PROBE_TIMEOUT_MS = exports.EGRESS_PROBE_BLOCKED = exports.EGRESS_PROBE_REACHED = exports.EGRESS_PROBE_PREFIX = void 0;
|
|
2379
2979
|
exports.effectiveBlocksUndeclaredEgress = effectiveBlocksUndeclaredEgress;
|
|
2380
2980
|
exports.confinementOverclaims = confinementOverclaims;
|
|
2381
2981
|
exports.runnerBlocksUndeclaredEgress = runnerBlocksUndeclaredEgress;
|
|
2382
2982
|
exports.poolConfinementSentence = poolConfinementSentence;
|
|
2383
2983
|
var brand_1 = require_brand();
|
|
2384
|
-
exports.EGRESS_PROBE_PREFIX = "
|
|
2984
|
+
exports.EGRESS_PROBE_PREFIX = "runveric.egress-probe";
|
|
2385
2985
|
exports.EGRESS_PROBE_REACHED = "REACHED";
|
|
2386
2986
|
exports.EGRESS_PROBE_BLOCKED = "BLOCKED";
|
|
2387
2987
|
exports.EGRESS_PROBE_TIMEOUT_MS = 5e3;
|
|
2388
|
-
exports.EGRESS_GATE_PREFIX = "
|
|
2988
|
+
exports.EGRESS_GATE_PREFIX = "runveric.egress-gate";
|
|
2389
2989
|
exports.EGRESS_GATE_OPEN = "OPEN";
|
|
2390
2990
|
exports.EGRESS_GATE_SHUT = "SHUT";
|
|
2391
2991
|
exports.RUNNER_SUBSTRATES = [
|
|
@@ -2440,7 +3040,69 @@ var require_runner_confinement = __commonJS({
|
|
|
2440
3040
|
return `Only ${confined} of ${findings.length} runners in this pool block undeclared outbound traffic. A run cannot choose which runner claims it, so this pool is not relied on to confine egress and packages that need their allowlist enforced are not sent here.`;
|
|
2441
3041
|
}
|
|
2442
3042
|
__name(poolConfinementSentence, "poolConfinementSentence");
|
|
2443
|
-
|
|
3043
|
+
}
|
|
3044
|
+
});
|
|
3045
|
+
|
|
3046
|
+
// ../contracts/dist/runner-install.js
|
|
3047
|
+
var require_runner_install = __commonJS({
|
|
3048
|
+
"../contracts/dist/runner-install.js"(exports) {
|
|
3049
|
+
"use strict";
|
|
3050
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3051
|
+
value: true
|
|
3052
|
+
});
|
|
3053
|
+
exports.POD_ROLE_BROKER = exports.POD_EGRESS_CONFINED = exports.POD_EGRESS_LABEL = exports.POD_RUNNER = exports.POD_ROLE = exports.POD_MANAGED_BY_VALUE = exports.POD_MANAGED_BY = exports.RUNNER_NETWORK_POLICY_NAME = exports.RUNNER_REPORT_DIRECTORY = exports.RUNNER_STATE_DIRECTORY = exports.RUNNER_CONTAINER_PREFIX = exports.RUNNER_NAMESPACE = exports.RUNNER_HOST_ENV = void 0;
|
|
3054
|
+
exports.RUNNER_HOST_ENV = {
|
|
3055
|
+
/** `docker` or `kubernetes`. Absent means `docker`; present and wrong is refused by name. */
|
|
3056
|
+
substrate: "RUNVERIC_RUNNER_SUBSTRATE",
|
|
3057
|
+
/** The https origin the runner long-polls. */
|
|
3058
|
+
controlPlaneUrl: "RUNVERIC_CONTROL_PLANE_URL",
|
|
3059
|
+
/** The operator-chosen name, also the container's and the Deployment's. */
|
|
3060
|
+
runnerName: "RUNVERIC_RUNNER_NAME",
|
|
3061
|
+
/** Single-use; spent by the first registration. */
|
|
3062
|
+
registrationToken: "RUNVERIC_RUNNER_REGISTRATION_TOKEN",
|
|
3063
|
+
/** The image the dispatcher starts one container of per step. */
|
|
3064
|
+
stepImage: "RUNVERIC_RUNNER_STEP_IMAGE",
|
|
3065
|
+
/** Optional. Absent means the runner runs built-in actions only. */
|
|
3066
|
+
packageImage: "RUNVERIC_RUNNER_PACKAGE_IMAGE",
|
|
3067
|
+
/** Where the credential and the stashed reports live across restarts. */
|
|
3068
|
+
reportDirectory: "RUNVERIC_RUNNER_REPORT_DIR"
|
|
3069
|
+
};
|
|
3070
|
+
exports.RUNNER_NAMESPACE = "runveric-runner";
|
|
3071
|
+
exports.RUNNER_CONTAINER_PREFIX = "runveric-";
|
|
3072
|
+
exports.RUNNER_STATE_DIRECTORY = "/var/lib/runveric-runner";
|
|
3073
|
+
exports.RUNNER_REPORT_DIRECTORY = `${exports.RUNNER_STATE_DIRECTORY}/reports`;
|
|
3074
|
+
exports.RUNNER_NETWORK_POLICY_NAME = "runveric-confined-egress";
|
|
3075
|
+
exports.POD_MANAGED_BY = "runveric.dev/managed-by";
|
|
3076
|
+
exports.POD_MANAGED_BY_VALUE = "runveric-dispatcher";
|
|
3077
|
+
exports.POD_ROLE = "runveric.dev/role";
|
|
3078
|
+
exports.POD_RUNNER = "runveric.dev/runner";
|
|
3079
|
+
exports.POD_EGRESS_LABEL = "runveric.dev/egress";
|
|
3080
|
+
exports.POD_EGRESS_CONFINED = "confined";
|
|
3081
|
+
exports.POD_ROLE_BROKER = "egress-broker";
|
|
3082
|
+
}
|
|
3083
|
+
});
|
|
3084
|
+
|
|
3085
|
+
// ../contracts/dist/authoring-cli.js
|
|
3086
|
+
var require_authoring_cli = __commonJS({
|
|
3087
|
+
"../contracts/dist/authoring-cli.js"(exports) {
|
|
3088
|
+
"use strict";
|
|
3089
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3090
|
+
value: true
|
|
3091
|
+
});
|
|
3092
|
+
exports.RETIRED_AUTHORING_CLI_ENV = exports.AUTHORING_CLI_ENV = void 0;
|
|
3093
|
+
exports.AUTHORING_CLI_ENV = {
|
|
3094
|
+
/** `runveric publish`: the access token. */
|
|
3095
|
+
token: "RUNVERIC_TOKEN",
|
|
3096
|
+
/** `runveric publish`: the control plane's origin. */
|
|
3097
|
+
url: "RUNVERIC_URL",
|
|
3098
|
+
/** `runveric test`: the runner image to run in, digest-pinned — the Author page shows it. */
|
|
3099
|
+
runnerImage: "RUNVERIC_RUNNER_IMAGE"
|
|
3100
|
+
};
|
|
3101
|
+
exports.RETIRED_AUTHORING_CLI_ENV = {
|
|
3102
|
+
token: "OPERA_TOKEN",
|
|
3103
|
+
url: "OPERA_URL",
|
|
3104
|
+
runnerImage: "OPERA_RUNNER_IMAGE"
|
|
3105
|
+
};
|
|
2444
3106
|
}
|
|
2445
3107
|
});
|
|
2446
3108
|
|
|
@@ -2464,9 +3126,10 @@ var require_runners = __commonJS({
|
|
|
2464
3126
|
}, "get")
|
|
2465
3127
|
});
|
|
2466
3128
|
var package_provenance_1 = require_package_provenance();
|
|
2467
|
-
|
|
2468
|
-
exports.
|
|
2469
|
-
exports.
|
|
3129
|
+
var runner_install_1 = require_runner_install();
|
|
3130
|
+
exports.RUNNER_REGISTRATION_TOKEN_HEADER = "x-runveric-runner-registration-token";
|
|
3131
|
+
exports.RUNNER_CREDENTIAL_HEADER = "x-runveric-runner-credential";
|
|
3132
|
+
exports.RUNNER_AUTHORIZATION_TOKEN_HEADER = "x-runveric-runner-authorization";
|
|
2470
3133
|
exports.RUNNER_POOL_ACCEPTS_PROVENANCE_BY_DEFAULT = {
|
|
2471
3134
|
// Imported from a Git source this tenant connected. Still not accepted by default: a pool
|
|
2472
3135
|
// is a machine inside a customer's network, and nothing about it is implied by connecting a
|
|
@@ -2480,13 +3143,13 @@ var require_runners = __commonJS({
|
|
|
2480
3143
|
authored: false
|
|
2481
3144
|
};
|
|
2482
3145
|
exports.RUNNER_POOL_DEFAULT_ACCEPTS_PROVENANCE = package_provenance_1.PACKAGE_PROVENANCE_KINDS.filter((kind) => exports.RUNNER_POOL_ACCEPTS_PROVENANCE_BY_DEFAULT[kind]);
|
|
2483
|
-
function poolAcceptsProvenance(accepts,
|
|
2484
|
-
if (
|
|
2485
|
-
return accepts.includes(
|
|
3146
|
+
function poolAcceptsProvenance(accepts, provenance2) {
|
|
3147
|
+
if (provenance2 === null) return false;
|
|
3148
|
+
return accepts.includes(provenance2);
|
|
2486
3149
|
}
|
|
2487
3150
|
__name(poolAcceptsProvenance, "poolAcceptsProvenance");
|
|
2488
3151
|
function containerNameFor(runnerName) {
|
|
2489
|
-
return
|
|
3152
|
+
return `${runner_install_1.RUNNER_CONTAINER_PREFIX}${runnerName.trim()}`;
|
|
2490
3153
|
}
|
|
2491
3154
|
__name(containerNameFor, "containerNameFor");
|
|
2492
3155
|
exports.RUNNER_LIFECYCLES = [
|
|
@@ -3309,12 +3972,12 @@ var require_workspace_domain = __commonJS({
|
|
|
3309
3972
|
"challenge_expired",
|
|
3310
3973
|
"held_elsewhere"
|
|
3311
3974
|
];
|
|
3312
|
-
exports.DOMAIN_VERIFICATION_RECORD_PREFIX = "
|
|
3313
|
-
function domainVerificationRecord(domain,
|
|
3975
|
+
exports.DOMAIN_VERIFICATION_RECORD_PREFIX = "runveric-domain-verification=";
|
|
3976
|
+
function domainVerificationRecord(domain, token2) {
|
|
3314
3977
|
return {
|
|
3315
3978
|
name: domain,
|
|
3316
3979
|
type: "TXT",
|
|
3317
|
-
value: `${exports.DOMAIN_VERIFICATION_RECORD_PREFIX}${
|
|
3980
|
+
value: `${exports.DOMAIN_VERIFICATION_RECORD_PREFIX}${token2}`
|
|
3318
3981
|
};
|
|
3319
3982
|
}
|
|
3320
3983
|
__name(domainVerificationRecord, "domainVerificationRecord");
|
|
@@ -3497,9 +4160,16 @@ var require_dist = __commonJS({
|
|
|
3497
4160
|
__exportStar(require_tenant_users(), exports);
|
|
3498
4161
|
__exportStar(require_invitations(), exports);
|
|
3499
4162
|
__exportStar(require_groups(), exports);
|
|
4163
|
+
__exportStar(require_catalog_policy(), exports);
|
|
3500
4164
|
__exportStar(require_catalog(), exports);
|
|
4165
|
+
__exportStar(require_catalog_release(), exports);
|
|
4166
|
+
__exportStar(require_action_repository(), exports);
|
|
4167
|
+
__exportStar(require_catalog_release_spec(), exports);
|
|
4168
|
+
__exportStar(require_catalog_lock(), exports);
|
|
4169
|
+
__exportStar(require_catalog_manifest_placeholders(), exports);
|
|
3501
4170
|
__exportStar(require_change_control(), exports);
|
|
3502
4171
|
__exportStar(require_execution(), exports);
|
|
4172
|
+
__exportStar(require_decisions(), exports);
|
|
3503
4173
|
__exportStar(require_execution_site(), exports);
|
|
3504
4174
|
__exportStar(require_run_placement(), exports);
|
|
3505
4175
|
__exportStar(require_connections(), exports);
|
|
@@ -3510,12 +4180,15 @@ var require_dist = __commonJS({
|
|
|
3510
4180
|
__exportStar(require_scheduling(), exports);
|
|
3511
4181
|
__exportStar(require_operations(), exports);
|
|
3512
4182
|
__exportStar(require_runs(), exports);
|
|
4183
|
+
__exportStar(require_evidence_pack(), exports);
|
|
3513
4184
|
__exportStar(require_package_tier(), exports);
|
|
3514
4185
|
__exportStar(require_package_provenance(), exports);
|
|
3515
4186
|
__exportStar(require_packages(), exports);
|
|
3516
4187
|
__exportStar(require_package_egress(), exports);
|
|
3517
4188
|
__exportStar(require_package_execution(), exports);
|
|
3518
4189
|
__exportStar(require_runner_confinement(), exports);
|
|
4190
|
+
__exportStar(require_runner_install(), exports);
|
|
4191
|
+
__exportStar(require_authoring_cli(), exports);
|
|
3519
4192
|
__exportStar(require_runners(), exports);
|
|
3520
4193
|
__exportStar(require_runner_pool_presence(), exports);
|
|
3521
4194
|
__exportStar(require_routable_target(), exports);
|
|
@@ -3670,6 +4343,7 @@ var require_manifest_schema = __commonJS({
|
|
|
3670
4343
|
return contracts_1.RISK_LEVELS;
|
|
3671
4344
|
}, "get")
|
|
3672
4345
|
});
|
|
4346
|
+
var contracts_2 = require_dist();
|
|
3673
4347
|
exports.MANIFEST_FIELDS = [
|
|
3674
4348
|
"apiVersion",
|
|
3675
4349
|
"id",
|
|
@@ -3695,7 +4369,7 @@ var require_manifest_schema = __commonJS({
|
|
|
3695
4369
|
"secrets",
|
|
3696
4370
|
"credentialScopes",
|
|
3697
4371
|
"license",
|
|
3698
|
-
"
|
|
4372
|
+
"minRunvericVersion"
|
|
3699
4373
|
];
|
|
3700
4374
|
exports.PACKAGE_ID_PATTERN = /^[a-z0-9-]+\/[a-z0-9-]+$/;
|
|
3701
4375
|
exports.SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
@@ -3708,12 +4382,18 @@ var require_manifest_schema = __commonJS({
|
|
|
3708
4382
|
"properties",
|
|
3709
4383
|
"required",
|
|
3710
4384
|
"additionalProperties",
|
|
3711
|
-
//
|
|
4385
|
+
// Our own annotation, carried INSIDE the schema it orders rather than in a second
|
|
3712
4386
|
// column: the two would otherwise separate, and a schema whose order lives elsewhere is a
|
|
3713
4387
|
// schema that renders in the wrong order the first time one of them is copied without the
|
|
3714
4388
|
// other. Allowed here so a normalized schema round-trips through this validator rather than
|
|
3715
4389
|
// being rejected as an unknown field.
|
|
3716
|
-
|
|
4390
|
+
//
|
|
4391
|
+
// ⛔ THIS LIST IS A REJECT-UNKNOWN ALLOWLIST. `x-opera-order` left it LAST in docs/108 —
|
|
4392
|
+
// after the writer stopped stamping it, 0089 stripped it from every row, and the readers
|
|
4393
|
+
// stopped reading it. Nothing re-validates a STORED schema (every caller of this validator
|
|
4394
|
+
// is handed authored bytes), so the order was safe; an author's file that still carries the
|
|
4395
|
+
// retired key is now refused by name, as any unknown field is.
|
|
4396
|
+
contracts_2.PARAMETER_ORDER_KEY
|
|
3717
4397
|
];
|
|
3718
4398
|
exports.PARAMETER_PROPERTY_FIELDS = [
|
|
3719
4399
|
"type",
|
|
@@ -3814,7 +4494,7 @@ var require_manifest_validator = __commonJS({
|
|
|
3814
4494
|
});
|
|
3815
4495
|
validateCredentialScopes(value.credentialScopes, errors);
|
|
3816
4496
|
requiredString(value, "license", errors, manifest_schema_1.SPDX_ID_PATTERN, "an SPDX identifier");
|
|
3817
|
-
requiredString(value, "
|
|
4497
|
+
requiredString(value, "minRunvericVersion", errors, manifest_schema_1.SEMVER_PATTERN, "MAJOR.MINOR.PATCH");
|
|
3818
4498
|
return errors.length === 0 && runtime !== void 0 ? {
|
|
3819
4499
|
ok: true,
|
|
3820
4500
|
// The normalized runtime replaces the authored one, exactly as `discover` ids are
|
|
@@ -3853,7 +4533,7 @@ var require_manifest_validator = __commonJS({
|
|
|
3853
4533
|
const declaredType = validateParameterProperty(name, property, errors);
|
|
3854
4534
|
normalizedProperties[name] = isRecord(property) ? validateDiscoveryAnnotations(name, property, declaredType, value.properties, declarationOrder, context?.providers, errors, warnings) : property;
|
|
3855
4535
|
}
|
|
3856
|
-
validateParameterOrder(value[
|
|
4536
|
+
validateParameterOrder(value[contracts_1.PARAMETER_ORDER_KEY], contracts_1.PARAMETER_ORDER_KEY, declarationOrder, errors);
|
|
3857
4537
|
}
|
|
3858
4538
|
if (!Array.isArray(value.required) || !value.required.every(isString)) {
|
|
3859
4539
|
add(errors, "parameters.required", "Expected an array of property names.", "type");
|
|
@@ -4153,9 +4833,9 @@ var require_manifest_validator = __commonJS({
|
|
|
4153
4833
|
}
|
|
4154
4834
|
}
|
|
4155
4835
|
__name(validateDependency, "validateDependency");
|
|
4156
|
-
function validateParameterOrder(value, declarationOrder, errors) {
|
|
4836
|
+
function validateParameterOrder(value, key, declarationOrder, errors) {
|
|
4157
4837
|
if (value === void 0) return;
|
|
4158
|
-
const path =
|
|
4838
|
+
const path = `parameters.${key}`;
|
|
4159
4839
|
if (!Array.isArray(value) || !value.every(isString)) {
|
|
4160
4840
|
add(errors, path, "Expected an array of property names.", "type");
|
|
4161
4841
|
return;
|
|
@@ -5011,7 +5691,7 @@ var require_authoring_lint = __commonJS({
|
|
|
5011
5691
|
if (!isRecord(permission) || !Array.isArray(permission.actions)) return;
|
|
5012
5692
|
permission.actions.forEach((action, actionIndex) => {
|
|
5013
5693
|
if (typeof action !== "string" || action.includes("*")) return;
|
|
5014
|
-
if (permissionTokens(action).some((
|
|
5694
|
+
if (permissionTokens(action).some((token2) => haystack.includes(token2))) return;
|
|
5015
5695
|
findings.push({
|
|
5016
5696
|
severity: "warning",
|
|
5017
5697
|
rule: "package/permission-possibly-unused",
|
|
@@ -5086,7 +5766,7 @@ var require_authoring_lint = __commonJS({
|
|
|
5086
5766
|
".sh",
|
|
5087
5767
|
".py"
|
|
5088
5768
|
];
|
|
5089
|
-
var RESULT_FILE_VARIABLE = "
|
|
5769
|
+
var RESULT_FILE_VARIABLE = "RUNVERIC_RESULT_FILE";
|
|
5090
5770
|
var ENTRYPOINT_PHASES = [
|
|
5091
5771
|
{
|
|
5092
5772
|
field: "entrypoint",
|
|
@@ -5675,7 +6355,7 @@ summary: ${yamlString(`${input.name} \u2014 describe in one line what an operato
|
|
|
5675
6355
|
category: compute
|
|
5676
6356
|
providers:
|
|
5677
6357
|
- azure
|
|
5678
|
-
connectorKey:
|
|
6358
|
+
connectorKey: ${contracts_1.PACKAGE_RUNNER_CONNECTOR_KEY}
|
|
5679
6359
|
|
|
5680
6360
|
# \u2500\u2500 How it runs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
5681
6361
|
runtime:
|
|
@@ -5715,7 +6395,7 @@ secrets: []
|
|
|
5715
6395
|
|
|
5716
6396
|
# \u2500\u2500 Publishing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
5717
6397
|
license: MIT
|
|
5718
|
-
|
|
6398
|
+
minRunvericVersion: 1.0.0
|
|
5719
6399
|
`;
|
|
5720
6400
|
}
|
|
5721
6401
|
__name(manifest, "manifest");
|
|
@@ -5752,12 +6432,12 @@ minOperaVersion: 1.0.0
|
|
|
5752
6432
|
return `# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
5753
6433
|
# OPERA'S WRAPPER. This file is the CONTRACT; ${bodyPath} is YOURS.
|
|
5754
6434
|
#
|
|
5755
|
-
# ${contracts_1.BRAND_SHORT_NAME} reads exactly one thing from a package: the JSON at ${sigil}
|
|
6435
|
+
# ${contracts_1.BRAND_SHORT_NAME} reads exactly one thing from a package: the JSON at ${sigil}RUNVERIC_RESULT_FILE. A script
|
|
5756
6436
|
# that exits 0 without writing one has reported nothing, and ${contracts_1.BRAND_SHORT_NAME} says so rather than inventing
|
|
5757
6437
|
# a result. This file is what makes that impossible to get wrong by accident:
|
|
5758
6438
|
#
|
|
5759
6439
|
# * it starts ${bodyPath} \u2014 your script, unchanged;
|
|
5760
|
-
# * if your script wrote ${sigil}
|
|
6440
|
+
# * if your script wrote ${sigil}RUNVERIC_RESULT_FILE itself, this file leaves it exactly alone;
|
|
5761
6441
|
# * otherwise it writes one for you \u2014 succeeded when your script exits 0, failed with the exit
|
|
5762
6442
|
# code and a result.error when it does not \u2014 carrying whatever your script printed as the
|
|
5763
6443
|
# message.
|
|
@@ -5768,21 +6448,21 @@ minOperaVersion: 1.0.0
|
|
|
5768
6448
|
# WHERE YOUR SCRIPT RUNS. This file moves to the PACKAGE ROOT before starting yours, so a
|
|
5769
6449
|
# relative path in your script means what it meant on your laptop \u2014 \`source ./lib/util.sh\` and
|
|
5770
6450
|
# \`. ./lib/helpers.ps1\` resolve against the package, not against wherever the runner happened
|
|
5771
|
-
# to start. The package root is READ-ONLY and NON-EXECUTABLE: write to ${sigil}
|
|
6451
|
+
# to start. The package root is READ-ONLY and NON-EXECUTABLE: write to ${sigil}RUNVERIC_WORK_DIR,
|
|
5772
6452
|
# and call another script through your interpreter (\`bash ./tool.sh\`) rather than executing it
|
|
5773
|
-
# directly. ${sigil}
|
|
6453
|
+
# directly. ${sigil}RUNVERIC_PACKAGE_ROOT is exported so your script can be explicit rather than
|
|
5774
6454
|
# assume.
|
|
5775
6455
|
#
|
|
5776
6456
|
# THE CONTRACT (docs/22), identical in every language:
|
|
5777
|
-
# ${sigil}
|
|
5778
|
-
# ${sigil}
|
|
5779
|
-
# ${sigil}
|
|
5780
|
-
# ${sigil}
|
|
6457
|
+
# ${sigil}RUNVERIC_PARAMETERS_FILE the operator's answers, as JSON
|
|
6458
|
+
# ${sigil}RUNVERIC_CONTEXT_FILE non-secret run metadata (execution id, provider, scope)
|
|
6459
|
+
# ${sigil}RUNVERIC_RESULT_FILE where the result is WRITTEN \u2014 the only result channel
|
|
6460
|
+
# ${sigil}RUNVERIC_DRY_RUN "true" when the operator asked for a rehearsal. PASSED
|
|
5781
6461
|
# THROUGH, never acted on here: only your script knows which of
|
|
5782
6462
|
# its steps change anything.
|
|
5783
|
-
# ${sigil}
|
|
5784
|
-
# ${sigil}
|
|
5785
|
-
# Anything printed to stdout becomes a log line. A line starting with
|
|
6463
|
+
# ${sigil}RUNVERIC_WORK_DIR scratch space; nothing else is writable
|
|
6464
|
+
# ${sigil}RUNVERIC_PACKAGE_ROOT this package's root, and the working directory
|
|
6465
|
+
# Anything printed to stdout becomes a log line. A line starting with RUNVERIC_EVENT becomes a
|
|
5786
6466
|
# structured event.
|
|
5787
6467
|
#
|
|
5788
6468
|
# The runner starts run/${entryFile} the same way this file starts ${bodyPath}.
|
|
@@ -5829,7 +6509,7 @@ $ErrorActionPreference = 'Continue'
|
|
|
5829
6509
|
# setting only the first is a classic way to get a script that resolves paths differently from
|
|
5830
6510
|
# the tools it calls.
|
|
5831
6511
|
$operaPackageRoot = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
|
|
5832
|
-
$env:
|
|
6512
|
+
$env:RUNVERIC_PACKAGE_ROOT = $operaPackageRoot
|
|
5833
6513
|
Set-Location -LiteralPath $operaPackageRoot
|
|
5834
6514
|
[Environment]::CurrentDirectory = $operaPackageRoot
|
|
5835
6515
|
|
|
@@ -5846,7 +6526,7 @@ $operaCaptured = [System.Collections.Generic.List[string]]::new()
|
|
|
5846
6526
|
|
|
5847
6527
|
# \u2500\u2500\u2500 THE OPERATOR'S ANSWERS, BOUND TO YOUR OWN param() BLOCK \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
5848
6528
|
#
|
|
5849
|
-
# ${contracts_1.BRAND_SHORT_NAME} delivers the operator's answers as JSON at $env:
|
|
6529
|
+
# ${contracts_1.BRAND_SHORT_NAME} delivers the operator's answers as JSON at $env:RUNVERIC_PARAMETERS_FILE. A script written
|
|
5850
6530
|
# outside ${contracts_1.BRAND_SHORT_NAME} receives its values through its param() block. THOSE ARE DIFFERENT MECHANISMS,
|
|
5851
6531
|
# and nothing joins them unless this file does \u2014 so without the lines below a run form built from
|
|
5852
6532
|
# a param() block would collect six answers and hand your script none of them.
|
|
@@ -5865,7 +6545,7 @@ $operaCaptured = [System.Collections.Generic.List[string]]::new()
|
|
|
5865
6545
|
#
|
|
5866
6546
|
# An answer your script does not declare is IGNORED, not an error \u2014 your script is the authority
|
|
5867
6547
|
# on what it takes. A script with no param() block gets no arguments at all and reads
|
|
5868
|
-
# $env:
|
|
6548
|
+
# $env:RUNVERIC_PARAMETERS_FILE itself, exactly as before.
|
|
5869
6549
|
$operaDeclared = @()
|
|
5870
6550
|
try {
|
|
5871
6551
|
$operaTokens = $null
|
|
@@ -5884,10 +6564,10 @@ try {
|
|
|
5884
6564
|
}
|
|
5885
6565
|
|
|
5886
6566
|
$operaArguments = @()
|
|
5887
|
-
if ($operaDeclared.Count -gt 0 -and (Test-Path -LiteralPath $env:
|
|
6567
|
+
if ($operaDeclared.Count -gt 0 -and (Test-Path -LiteralPath $env:RUNVERIC_PARAMETERS_FILE)) {
|
|
5888
6568
|
$operaAnswers = $null
|
|
5889
6569
|
try {
|
|
5890
|
-
$operaAnswers = Get-Content -LiteralPath $env:
|
|
6570
|
+
$operaAnswers = Get-Content -LiteralPath $env:RUNVERIC_PARAMETERS_FILE -Raw | ConvertFrom-Json
|
|
5891
6571
|
} catch {
|
|
5892
6572
|
$operaAnswers = $null
|
|
5893
6573
|
}
|
|
@@ -5939,8 +6619,8 @@ $operaStatus = $LASTEXITCODE
|
|
|
5939
6619
|
|
|
5940
6620
|
# THE BODY'S OWN RESULT WINS. The wrapper fills a gap; it never overwrites what an author
|
|
5941
6621
|
# reported for themselves \u2014 outputs, checks and rollbackCapture all arrive this way.
|
|
5942
|
-
$operaWritten = (Test-Path -LiteralPath $env:
|
|
5943
|
-
((Get-Item -LiteralPath $env:
|
|
6622
|
+
$operaWritten = (Test-Path -LiteralPath $env:RUNVERIC_RESULT_FILE) -and
|
|
6623
|
+
((Get-Item -LiteralPath $env:RUNVERIC_RESULT_FILE).Length -gt 0)
|
|
5944
6624
|
if (-not $operaWritten) {
|
|
5945
6625
|
$operaOutput = ($operaCaptured -join "\`n").Trim()
|
|
5946
6626
|
# result.message is bounded at 4096 characters. Keep the TAIL, which is where a failure says
|
|
@@ -5965,7 +6645,7 @@ if (-not $operaWritten) {
|
|
|
5965
6645
|
}
|
|
5966
6646
|
}
|
|
5967
6647
|
}
|
|
5968
|
-
$operaResult | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $env:
|
|
6648
|
+
$operaResult | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $env:RUNVERIC_RESULT_FILE -Encoding utf8
|
|
5969
6649
|
}
|
|
5970
6650
|
|
|
5971
6651
|
# The body's exit code is this package's exit code. ${contracts_1.BRAND_SHORT_NAME} cross-checks the two \u2014 a result that
|
|
@@ -5981,16 +6661,16 @@ exit $operaStatus
|
|
|
5981
6661
|
return `${wrapperHeader(interpreter, "$", bodyPath)}
|
|
5982
6662
|
# THE PACKAGE ROOT, and the working directory your script starts in \u2014 so \`source ./lib/util.sh\`
|
|
5983
6663
|
# resolves against the package rather than against the scratch directory the runner starts in.
|
|
5984
|
-
|
|
5985
|
-
export
|
|
5986
|
-
cd "\${
|
|
6664
|
+
RUNVERIC_PACKAGE_ROOT="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
|
|
6665
|
+
export RUNVERIC_PACKAGE_ROOT
|
|
6666
|
+
cd "\${RUNVERIC_PACKAGE_ROOT}"
|
|
5987
6667
|
|
|
5988
6668
|
# THE ONE PLACE the author's path is written into this file. Everything below refers to the
|
|
5989
6669
|
# variable, so an entrypoint containing a quote or a \`$\` is a string here and cannot become
|
|
5990
6670
|
# syntax. The marker is how \`authoredEntrypointOf\` reads this line back \u2014 see that function.
|
|
5991
6671
|
opera_body_path=${bashLiteral(bodyPath)} # ${ENTRYPOINT_MARKER}
|
|
5992
|
-
opera_body="\${
|
|
5993
|
-
opera_capture="\${
|
|
6672
|
+
opera_body="\${RUNVERIC_PACKAGE_ROOT}/\${opera_body_path}"
|
|
6673
|
+
opera_capture="\${RUNVERIC_WORK_DIR}/.runveric-body-output"
|
|
5994
6674
|
|
|
5995
6675
|
# Both streams merged, so the transcript below is in the order the operator saw; \`tee\` keeps
|
|
5996
6676
|
# every line arriving live rather than held until the body ends. errexit is lifted for exactly
|
|
@@ -6002,17 +6682,17 @@ set -e
|
|
|
6002
6682
|
|
|
6003
6683
|
# THE BODY'S OWN RESULT WINS. The wrapper fills a gap; it never overwrites what an author
|
|
6004
6684
|
# reported for themselves \u2014 outputs, checks and rollbackCapture all arrive this way.
|
|
6005
|
-
if [ ! -s "\${
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
python3 - > "\${
|
|
6685
|
+
if [ ! -s "\${RUNVERIC_RESULT_FILE}" ]; then
|
|
6686
|
+
RUNVERIC_BODY_STATUS="\${opera_status}" RUNVERIC_BODY_CAPTURE="\${opera_capture}" \\
|
|
6687
|
+
RUNVERIC_BODY_PATH="\${opera_body_path}" \\
|
|
6688
|
+
python3 - > "\${RUNVERIC_RESULT_FILE}" <<'PY'
|
|
6009
6689
|
import json, os
|
|
6010
6690
|
|
|
6011
6691
|
${FALLBACK_RESULT_PYTHON}
|
|
6012
6692
|
|
|
6013
|
-
with open(os.environ["
|
|
6693
|
+
with open(os.environ["RUNVERIC_BODY_CAPTURE"], encoding="utf-8", errors="replace") as handle:
|
|
6014
6694
|
captured = handle.read()
|
|
6015
|
-
print(json.dumps(opera_result(int(os.environ["
|
|
6695
|
+
print(json.dumps(opera_result(int(os.environ["RUNVERIC_BODY_STATUS"]), captured, os.environ["RUNVERIC_BODY_PATH"])))
|
|
6016
6696
|
PY
|
|
6017
6697
|
fi
|
|
6018
6698
|
|
|
@@ -6037,7 +6717,7 @@ ${FALLBACK_RESULT_PYTHON}
|
|
|
6037
6717
|
# or \`import\` resolves against the package rather than against the scratch directory the runner
|
|
6038
6718
|
# starts in. os.chdir moves this process, and the body inherits it.
|
|
6039
6719
|
opera_package_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
6040
|
-
os.environ["
|
|
6720
|
+
os.environ["RUNVERIC_PACKAGE_ROOT"] = opera_package_root
|
|
6041
6721
|
os.chdir(opera_package_root)
|
|
6042
6722
|
|
|
6043
6723
|
# THE ONE PLACE the author's path is written into this file. Everything below refers to the
|
|
@@ -6064,7 +6744,7 @@ opera_status = opera_process.wait()
|
|
|
6064
6744
|
|
|
6065
6745
|
# THE BODY'S OWN RESULT WINS. The wrapper fills a gap; it never overwrites what an author
|
|
6066
6746
|
# reported for themselves \u2014 outputs, checks and rollbackCapture all arrive this way.
|
|
6067
|
-
opera_result_file = os.environ["
|
|
6747
|
+
opera_result_file = os.environ["RUNVERIC_RESULT_FILE"]
|
|
6068
6748
|
opera_written = os.path.exists(opera_result_file) and os.path.getsize(opera_result_file) > 0
|
|
6069
6749
|
if not opera_written:
|
|
6070
6750
|
with open(opera_result_file, "w", encoding="utf-8") as handle:
|
|
@@ -6084,27 +6764,27 @@ sys.exit(opera_status)
|
|
|
6084
6764
|
# from whether it exits 0 and what it printed. Anything that runs is a working action.
|
|
6085
6765
|
#
|
|
6086
6766
|
# What you are given:
|
|
6087
|
-
# $env:
|
|
6088
|
-
# $env:
|
|
6089
|
-
# $env:
|
|
6767
|
+
# $env:RUNVERIC_PARAMETERS_FILE the operator's answers, as JSON
|
|
6768
|
+
# $env:RUNVERIC_CONTEXT_FILE non-secret run metadata (execution id, provider, scope)
|
|
6769
|
+
# $env:RUNVERIC_DRY_RUN "true" when the operator asked for a rehearsal \u2014 HONOUR IT if
|
|
6090
6770
|
# anything below changes something
|
|
6091
|
-
# $env:
|
|
6771
|
+
# $env:RUNVERIC_WORK_DIR scratch space; nothing else is writable
|
|
6092
6772
|
# Anything you print becomes a log line, and becomes the run's message.
|
|
6093
6773
|
#
|
|
6094
|
-
# WHERE YOU ARE. Your script runs from the package root \u2014 $env:
|
|
6774
|
+
# WHERE YOU ARE. Your script runs from the package root \u2014 $env:RUNVERIC_PACKAGE_ROOT \u2014 so
|
|
6095
6775
|
# \`. ./lib/helpers.ps1\` resolves the way it does on your laptop. That directory is READ-ONLY
|
|
6096
|
-
# and NON-EXECUTABLE: write to $env:
|
|
6776
|
+
# and NON-EXECUTABLE: write to $env:RUNVERIC_WORK_DIR, and start another script through its
|
|
6097
6777
|
# interpreter (\`pwsh ./tool.ps1\`) rather than executing it directly.
|
|
6098
6778
|
#
|
|
6099
6779
|
# WANT MORE THAN A MESSAGE \u2014 outputs, a rollbackCapture, your own error code? Write that JSON
|
|
6100
|
-
# to $env:
|
|
6780
|
+
# to $env:RUNVERIC_RESULT_FILE yourself and the wrapper leaves it alone. run/main.ps1 says how.
|
|
6101
6781
|
|
|
6102
6782
|
Set-StrictMode -Version Latest
|
|
6103
6783
|
$ErrorActionPreference = 'Stop'
|
|
6104
6784
|
|
|
6105
|
-
$parameters = Get-Content -LiteralPath $env:
|
|
6106
|
-
$context = Get-Content -LiteralPath $env:
|
|
6107
|
-
$dryRun = $env:
|
|
6785
|
+
$parameters = Get-Content -LiteralPath $env:RUNVERIC_PARAMETERS_FILE -Raw | ConvertFrom-Json
|
|
6786
|
+
$context = Get-Content -LiteralPath $env:RUNVERIC_CONTEXT_FILE -Raw | ConvertFrom-Json
|
|
6787
|
+
$dryRun = $env:RUNVERIC_DRY_RUN -eq 'true'
|
|
6108
6788
|
|
|
6109
6789
|
# Strict mode makes reading an absent property an error rather than an empty string, so an
|
|
6110
6790
|
# optional parameter is asked for explicitly. That is the point of strict mode: the failure
|
|
@@ -6118,85 +6798,85 @@ Write-Output "Running for execution $($context.executionId)."
|
|
|
6118
6798
|
Write-Output "Note: \${note}"
|
|
6119
6799
|
|
|
6120
6800
|
if ($dryRun) {
|
|
6121
|
-
Write-Output '
|
|
6801
|
+
Write-Output 'RUNVERIC_EVENT {"level":"info","message":"Dry run \u2014 nothing was changed."}'
|
|
6122
6802
|
} else {
|
|
6123
6803
|
# Do the real work here.
|
|
6124
|
-
Write-Output '
|
|
6804
|
+
Write-Output 'RUNVERIC_EVENT {"level":"success","message":"Nothing to change yet \u2014 this is a new action."}'
|
|
6125
6805
|
}
|
|
6126
6806
|
`,
|
|
6127
6807
|
bash: `# YOUR SCRIPT. run/main.sh \u2014 ${contracts_1.BRAND_SHORT_NAME}'s wrapper \u2014 starts this file and writes the run's result
|
|
6128
6808
|
# from whether it exits 0 and what it printed. Anything that runs is a working action.
|
|
6129
6809
|
#
|
|
6130
6810
|
# What you are given:
|
|
6131
|
-
# $
|
|
6132
|
-
# $
|
|
6133
|
-
# $
|
|
6811
|
+
# $RUNVERIC_PARAMETERS_FILE the operator's answers, as JSON
|
|
6812
|
+
# $RUNVERIC_CONTEXT_FILE non-secret run metadata (execution id, provider, scope)
|
|
6813
|
+
# $RUNVERIC_DRY_RUN "true" when the operator asked for a rehearsal \u2014 HONOUR IT if
|
|
6134
6814
|
# anything below changes something
|
|
6135
|
-
# $
|
|
6815
|
+
# $RUNVERIC_WORK_DIR scratch space; nothing else is writable
|
|
6136
6816
|
# Anything you print becomes a log line, and becomes the run's message.
|
|
6137
6817
|
#
|
|
6138
|
-
# WHERE YOU ARE. Your script runs from the package root \u2014 $
|
|
6818
|
+
# WHERE YOU ARE. Your script runs from the package root \u2014 $RUNVERIC_PACKAGE_ROOT \u2014 so
|
|
6139
6819
|
# \`source ./lib/util.sh\` resolves the way it does on your laptop. That directory is READ-ONLY
|
|
6140
|
-
# and NON-EXECUTABLE: write to $
|
|
6820
|
+
# and NON-EXECUTABLE: write to $RUNVERIC_WORK_DIR, and start another script through its
|
|
6141
6821
|
# interpreter (\`bash ./tool.sh\`) rather than executing it directly.
|
|
6142
6822
|
#
|
|
6143
6823
|
# WANT MORE THAN A MESSAGE \u2014 outputs, a rollbackCapture, your own error code? Write that JSON
|
|
6144
|
-
# to $
|
|
6824
|
+
# to $RUNVERIC_RESULT_FILE yourself and the wrapper leaves it alone. run/main.sh says how.
|
|
6145
6825
|
#
|
|
6146
6826
|
# Started with \`bash -euo pipefail\`, so a failing command ends the run instead of continuing
|
|
6147
6827
|
# past it.
|
|
6148
6828
|
|
|
6149
|
-
note="$(python3 -c 'import json,os,sys; print(json.load(open(os.environ["
|
|
6150
|
-
execution_id="$(python3 -c 'import json,os,sys; print(json.load(open(os.environ["
|
|
6829
|
+
note="$(python3 -c 'import json,os,sys; print(json.load(open(os.environ["RUNVERIC_PARAMETERS_FILE"])).get("note",""))')"
|
|
6830
|
+
execution_id="$(python3 -c 'import json,os,sys; print(json.load(open(os.environ["RUNVERIC_CONTEXT_FILE"]))["executionId"])')"
|
|
6151
6831
|
|
|
6152
6832
|
echo "Running for execution \${execution_id}."
|
|
6153
6833
|
echo "Note: \${note}"
|
|
6154
6834
|
|
|
6155
|
-
if [ "\${
|
|
6156
|
-
echo '
|
|
6835
|
+
if [ "\${RUNVERIC_DRY_RUN}" = "true" ]; then
|
|
6836
|
+
echo 'RUNVERIC_EVENT {"level":"info","message":"Dry run \u2014 nothing was changed."}'
|
|
6157
6837
|
else
|
|
6158
6838
|
# Do the real work here.
|
|
6159
|
-
echo '
|
|
6839
|
+
echo 'RUNVERIC_EVENT {"level":"success","message":"Nothing to change yet \u2014 this is a new action."}'
|
|
6160
6840
|
fi
|
|
6161
6841
|
`,
|
|
6162
6842
|
python: `# YOUR SCRIPT. run/main.py \u2014 ${contracts_1.BRAND_SHORT_NAME}'s wrapper \u2014 starts this file and writes the run's result
|
|
6163
6843
|
# from whether it exits 0 and what it printed. Anything that runs is a working action.
|
|
6164
6844
|
#
|
|
6165
6845
|
# What you are given:
|
|
6166
|
-
#
|
|
6167
|
-
#
|
|
6168
|
-
#
|
|
6846
|
+
# RUNVERIC_PARAMETERS_FILE the operator's answers, as JSON
|
|
6847
|
+
# RUNVERIC_CONTEXT_FILE non-secret run metadata (execution id, provider, scope)
|
|
6848
|
+
# RUNVERIC_DRY_RUN "true" when the operator asked for a rehearsal \u2014 HONOUR IT if
|
|
6169
6849
|
# anything below changes something
|
|
6170
|
-
#
|
|
6850
|
+
# RUNVERIC_WORK_DIR scratch space; nothing else is writable
|
|
6171
6851
|
# Anything you print becomes a log line, and becomes the run's message.
|
|
6172
6852
|
#
|
|
6173
|
-
# WHERE YOU ARE. Your script runs from the package root \u2014
|
|
6853
|
+
# WHERE YOU ARE. Your script runs from the package root \u2014 RUNVERIC_PACKAGE_ROOT \u2014 so a relative
|
|
6174
6854
|
# \`open()\` or \`import\` resolves the way it does on your laptop. That directory is READ-ONLY
|
|
6175
|
-
# and NON-EXECUTABLE: write to
|
|
6855
|
+
# and NON-EXECUTABLE: write to RUNVERIC_WORK_DIR, and start another script through its interpreter
|
|
6176
6856
|
# (\`python3 ./tool.py\`) rather than executing it directly.
|
|
6177
6857
|
#
|
|
6178
6858
|
# WANT MORE THAN A MESSAGE \u2014 outputs, a rollbackCapture, your own error code? Write that JSON
|
|
6179
|
-
# to
|
|
6859
|
+
# to RUNVERIC_RESULT_FILE yourself and the wrapper leaves it alone. run/main.py says how.
|
|
6180
6860
|
|
|
6181
6861
|
import json
|
|
6182
6862
|
import os
|
|
6183
6863
|
|
|
6184
|
-
with open(os.environ["
|
|
6864
|
+
with open(os.environ["RUNVERIC_PARAMETERS_FILE"], encoding="utf-8") as handle:
|
|
6185
6865
|
parameters = json.load(handle)
|
|
6186
|
-
with open(os.environ["
|
|
6866
|
+
with open(os.environ["RUNVERIC_CONTEXT_FILE"], encoding="utf-8") as handle:
|
|
6187
6867
|
context = json.load(handle)
|
|
6188
6868
|
|
|
6189
|
-
dry_run = os.environ.get("
|
|
6869
|
+
dry_run = os.environ.get("RUNVERIC_DRY_RUN") == "true"
|
|
6190
6870
|
note = parameters.get("note", "")
|
|
6191
6871
|
|
|
6192
6872
|
print(f"Running for execution {context['executionId']}.")
|
|
6193
6873
|
print(f"Note: {note}")
|
|
6194
6874
|
|
|
6195
6875
|
if dry_run:
|
|
6196
|
-
print('
|
|
6876
|
+
print('RUNVERIC_EVENT {"level":"info","message":"Dry run \u2014 nothing was changed."}')
|
|
6197
6877
|
else:
|
|
6198
6878
|
# Do the real work here.
|
|
6199
|
-
print('
|
|
6879
|
+
print('RUNVERIC_EVENT {"level":"success","message":"Nothing to change yet \u2014 this is a new action."}')
|
|
6200
6880
|
`
|
|
6201
6881
|
};
|
|
6202
6882
|
var VALIDATE_SCRIPT = {
|
|
@@ -6209,7 +6889,7 @@ reason, you do not also need result.error.
|
|
|
6209
6889
|
|
|
6210
6890
|
Set-StrictMode -Version Latest
|
|
6211
6891
|
$ErrorActionPreference = 'Stop'
|
|
6212
|
-
$parameters = Get-Content -LiteralPath $env:
|
|
6892
|
+
$parameters = Get-Content -LiteralPath $env:RUNVERIC_PARAMETERS_FILE -Raw | ConvertFrom-Json
|
|
6213
6893
|
|
|
6214
6894
|
$checks = @(
|
|
6215
6895
|
[ordered]@{ status = 'passed'; message = 'The note is present.' }
|
|
@@ -6221,7 +6901,7 @@ $failed = @($checks | Where-Object { $_.status -eq 'failed' }).Count -gt 0
|
|
|
6221
6901
|
status = if ($failed) { 'failed' } else { 'succeeded' }
|
|
6222
6902
|
message = if ($failed) { 'Pre-flight found a blocking problem.' } else { 'Pre-flight passed.' }
|
|
6223
6903
|
checks = $checks
|
|
6224
|
-
} | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $env:
|
|
6904
|
+
} | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $env:RUNVERIC_RESULT_FILE -Encoding utf8`,
|
|
6225
6905
|
bash: `The VALIDATE phase \u2014 a pre-flight that changes nothing.
|
|
6226
6906
|
Enable it with \`validateEntrypoint: validate/\` in action.yaml, then uncomment this file.
|
|
6227
6907
|
|
|
@@ -6229,7 +6909,7 @@ A validate result carries CHECKS and no outputs. One entry per thing you checked
|
|
|
6229
6909
|
passed / warning / failed. If any check failed, set status to "failed" \u2014 the checks ARE the
|
|
6230
6910
|
reason, you do not also need result.error.
|
|
6231
6911
|
|
|
6232
|
-
python3 - <<'PY' > "\${
|
|
6912
|
+
python3 - <<'PY' > "\${RUNVERIC_RESULT_FILE}"
|
|
6233
6913
|
import json
|
|
6234
6914
|
checks = [{"status": "passed", "message": "The note is present."}]
|
|
6235
6915
|
failed = any(check["status"] == "failed" for check in checks)
|
|
@@ -6253,7 +6933,7 @@ import os
|
|
|
6253
6933
|
checks = [{"status": "passed", "message": "The note is present."}]
|
|
6254
6934
|
failed = any(check["status"] == "failed" for check in checks)
|
|
6255
6935
|
|
|
6256
|
-
with open(os.environ["
|
|
6936
|
+
with open(os.environ["RUNVERIC_RESULT_FILE"], "w", encoding="utf-8") as handle:
|
|
6257
6937
|
json.dump(
|
|
6258
6938
|
{
|
|
6259
6939
|
"protocolVersion": "${contracts_1.PACKAGE_RESULT_PROTOCOL_VERSION}",
|
|
@@ -6269,28 +6949,28 @@ with open(os.environ["OPERA_RESULT_FILE"], "w", encoding="utf-8") as handle:
|
|
|
6269
6949
|
Enable it by setting \`rollback: true\`, \`rollbackEntrypoint: rollback/\` and
|
|
6270
6950
|
\`capabilities.rollback: true\` in action.yaml, then uncomment this file.
|
|
6271
6951
|
|
|
6272
|
-
Rollback reads what the run phase captured, at $env:
|
|
6952
|
+
Rollback reads what the run phase captured, at $env:RUNVERIC_PRIOR_RESULT_FILE. That is why a
|
|
6273
6953
|
run that intends to be undoable writes \`rollbackCapture\` into its result \u2014 this phase has
|
|
6274
6954
|
nothing else to work from.
|
|
6275
6955
|
|
|
6276
6956
|
Set-StrictMode -Version Latest
|
|
6277
6957
|
$ErrorActionPreference = 'Stop'
|
|
6278
|
-
$prior = Get-Content -LiteralPath $env:
|
|
6958
|
+
$prior = Get-Content -LiteralPath $env:RUNVERIC_PRIOR_RESULT_FILE -Raw | ConvertFrom-Json
|
|
6279
6959
|
|
|
6280
6960
|
[ordered]@{
|
|
6281
6961
|
protocolVersion = '${contracts_1.PACKAGE_RESULT_PROTOCOL_VERSION}'
|
|
6282
6962
|
status = 'succeeded'
|
|
6283
6963
|
message = 'Rolled back.'
|
|
6284
|
-
} | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $env:
|
|
6964
|
+
} | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $env:RUNVERIC_RESULT_FILE -Encoding utf8`,
|
|
6285
6965
|
bash: `The ROLLBACK phase \u2014 undo what the run phase did.
|
|
6286
6966
|
Enable it by setting \`rollback: true\`, \`rollbackEntrypoint: rollback/\` and
|
|
6287
6967
|
\`capabilities.rollback: true\` in action.yaml, then uncomment this file.
|
|
6288
6968
|
|
|
6289
|
-
Rollback reads what the run phase captured, at $
|
|
6969
|
+
Rollback reads what the run phase captured, at $RUNVERIC_PRIOR_RESULT_FILE. That is why a run
|
|
6290
6970
|
that intends to be undoable writes \`rollbackCapture\` into its result \u2014 this phase has
|
|
6291
6971
|
nothing else to work from.
|
|
6292
6972
|
|
|
6293
|
-
python3 - <<'PY' > "\${
|
|
6973
|
+
python3 - <<'PY' > "\${RUNVERIC_RESULT_FILE}"
|
|
6294
6974
|
import json
|
|
6295
6975
|
print(json.dumps({
|
|
6296
6976
|
"protocolVersion": "${contracts_1.PACKAGE_RESULT_PROTOCOL_VERSION}",
|
|
@@ -6302,17 +6982,17 @@ PY`,
|
|
|
6302
6982
|
Enable it by setting \`rollback: true\`, \`rollbackEntrypoint: rollback/\` and
|
|
6303
6983
|
\`capabilities.rollback: true\` in action.yaml, then uncomment this file.
|
|
6304
6984
|
|
|
6305
|
-
Rollback reads what the run phase captured, at
|
|
6985
|
+
Rollback reads what the run phase captured, at RUNVERIC_PRIOR_RESULT_FILE. That is why a run
|
|
6306
6986
|
that intends to be undoable writes \`rollbackCapture\` into its result \u2014 this phase has
|
|
6307
6987
|
nothing else to work from.
|
|
6308
6988
|
|
|
6309
6989
|
import json
|
|
6310
6990
|
import os
|
|
6311
6991
|
|
|
6312
|
-
with open(os.environ["
|
|
6992
|
+
with open(os.environ["RUNVERIC_PRIOR_RESULT_FILE"], encoding="utf-8") as handle:
|
|
6313
6993
|
prior = json.load(handle)
|
|
6314
6994
|
|
|
6315
|
-
with open(os.environ["
|
|
6995
|
+
with open(os.environ["RUNVERIC_RESULT_FILE"], "w", encoding="utf-8") as handle:
|
|
6316
6996
|
json.dump(
|
|
6317
6997
|
{
|
|
6318
6998
|
"protocolVersion": "${contracts_1.PACKAGE_RESULT_PROTOCOL_VERSION}",
|
|
@@ -6339,7 +7019,7 @@ point of it is that the loop below works before you have written anything.
|
|
|
6339
7019
|
|
|
6340
7020
|
**\`run/${bodyFile}\` is your script. \`run/${entryFile}\` is ${contracts_1.BRAND_SHORT_NAME}'s wrapper.**
|
|
6341
7021
|
|
|
6342
|
-
${contracts_1.BRAND_SHORT_NAME} reads exactly one thing from a package: the JSON at \`
|
|
7022
|
+
${contracts_1.BRAND_SHORT_NAME} reads exactly one thing from a package: the JSON at \`RUNVERIC_RESULT_FILE\`. A package that
|
|
6343
7023
|
exits 0 without writing one has reported nothing, and ${contracts_1.BRAND_SHORT_NAME} refuses it rather than inventing a
|
|
6344
7024
|
result \u2014 that strictness is what makes every check, output and rollback capture in the product
|
|
6345
7025
|
worth trusting.
|
|
@@ -6347,7 +7027,7 @@ worth trusting.
|
|
|
6347
7027
|
\`run/${entryFile}\` is what keeps you on the right side of it. It starts \`run/${bodyFile}\`,
|
|
6348
7028
|
and then:
|
|
6349
7029
|
|
|
6350
|
-
- if your script wrote \`
|
|
7030
|
+
- if your script wrote \`RUNVERIC_RESULT_FILE\` itself, it leaves that result exactly alone;
|
|
6351
7031
|
- otherwise it writes one \u2014 **succeeded** when your script exits 0, **failed** with the exit
|
|
6352
7032
|
code and a \`result.error\` when it does not \u2014 carrying whatever your script printed as the
|
|
6353
7033
|
message.
|
|
@@ -6410,14 +7090,14 @@ script and reports the result.
|
|
|
6410
7090
|
|
|
6411
7091
|
## The result contract, and why the wrapper is there
|
|
6412
7092
|
|
|
6413
|
-
${contracts_1.BRAND_SHORT_NAME} reads exactly one thing from a package: the JSON at \`
|
|
7093
|
+
${contracts_1.BRAND_SHORT_NAME} reads exactly one thing from a package: the JSON at \`RUNVERIC_RESULT_FILE\`. A package that
|
|
6414
7094
|
exits 0 without writing one has reported nothing, and ${contracts_1.BRAND_SHORT_NAME} refuses it rather than inventing a
|
|
6415
7095
|
result \u2014 that strictness is what makes every check, output and rollback capture in the product
|
|
6416
7096
|
worth trusting.
|
|
6417
7097
|
|
|
6418
7098
|
\`run/${entryFile}\` keeps you on the right side of it. It starts \`${input.entrypoint}\`, and then:
|
|
6419
7099
|
|
|
6420
|
-
- if your script wrote \`
|
|
7100
|
+
- if your script wrote \`RUNVERIC_RESULT_FILE\` itself, it leaves that result exactly alone;
|
|
6421
7101
|
- otherwise it writes one \u2014 **succeeded** when your script exits 0, **failed** with the exit
|
|
6422
7102
|
code and a \`result.error\` when it does not \u2014 carrying whatever your script printed as the
|
|
6423
7103
|
message.
|
|
@@ -6426,12 +7106,12 @@ So a script that only prints something is already a complete action.
|
|
|
6426
7106
|
|
|
6427
7107
|
## Where your script runs
|
|
6428
7108
|
|
|
6429
|
-
From the **package root**, which is also \`
|
|
7109
|
+
From the **package root**, which is also \`RUNVERIC_PACKAGE_ROOT\`. That is what makes
|
|
6430
7110
|
\`source ./lib/util.sh\` and \`. ./lib/helpers.ps1\` resolve the way they do on your laptop.
|
|
6431
7111
|
|
|
6432
7112
|
Two things about that directory are worth knowing before your first run:
|
|
6433
7113
|
|
|
6434
|
-
- it is **read-only** \u2014 write to \`
|
|
7114
|
+
- it is **read-only** \u2014 write to \`RUNVERIC_WORK_DIR\`, the only writable location;
|
|
6435
7115
|
- it is **non-executable** \u2014 start another script through its interpreter
|
|
6436
7116
|
(\`bash ./tool.sh\`, \`pwsh ./tool.ps1\`) rather than executing it directly.
|
|
6437
7117
|
|
|
@@ -6486,7 +7166,7 @@ SOFTWARE.
|
|
|
6486
7166
|
}
|
|
6487
7167
|
__name(license, "license");
|
|
6488
7168
|
var GITIGNORE = `# runveric test writes its scratch here.
|
|
6489
|
-
.
|
|
7169
|
+
.runveric/
|
|
6490
7170
|
*.log
|
|
6491
7171
|
`;
|
|
6492
7172
|
function yamlString(value) {
|
|
@@ -6857,67 +7537,7 @@ var require_dist2 = __commonJS({
|
|
|
6857
7537
|
});
|
|
6858
7538
|
|
|
6859
7539
|
// package.json
|
|
6860
|
-
var version = "0.
|
|
6861
|
-
|
|
6862
|
-
// src/check.ts
|
|
6863
|
-
var import_action_manifest = __toESM(require_dist2());
|
|
6864
|
-
import { resolve } from "path";
|
|
6865
|
-
|
|
6866
|
-
// src/package-dir.ts
|
|
6867
|
-
import { readdir, readFile, stat } from "fs/promises";
|
|
6868
|
-
import { join, relative, sep } from "path";
|
|
6869
|
-
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
6870
|
-
".git",
|
|
6871
|
-
".opera",
|
|
6872
|
-
"node_modules",
|
|
6873
|
-
".venv",
|
|
6874
|
-
"__pycache__",
|
|
6875
|
-
".DS_Store"
|
|
6876
|
-
]);
|
|
6877
|
-
var MAX_FILES = 2e3;
|
|
6878
|
-
var MAX_TOTAL_BYTES = 16 * 1024 * 1024;
|
|
6879
|
-
var PackageDirectoryError = class extends Error {
|
|
6880
|
-
static {
|
|
6881
|
-
__name(this, "PackageDirectoryError");
|
|
6882
|
-
}
|
|
6883
|
-
};
|
|
6884
|
-
async function readPackageDirectory(root) {
|
|
6885
|
-
const properties = await stat(root).catch(() => null);
|
|
6886
|
-
if (!properties?.isDirectory()) {
|
|
6887
|
-
throw new PackageDirectoryError(`${root} is not a directory.`);
|
|
6888
|
-
}
|
|
6889
|
-
const files = /* @__PURE__ */ new Map();
|
|
6890
|
-
let totalBytes = 0;
|
|
6891
|
-
const walk = /* @__PURE__ */ __name(async (directory) => {
|
|
6892
|
-
for (const entry of await readdir(directory, {
|
|
6893
|
-
withFileTypes: true
|
|
6894
|
-
})) {
|
|
6895
|
-
if (IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
6896
|
-
const full = join(directory, entry.name);
|
|
6897
|
-
if (entry.isDirectory()) {
|
|
6898
|
-
await walk(full);
|
|
6899
|
-
continue;
|
|
6900
|
-
}
|
|
6901
|
-
if (!entry.isFile()) continue;
|
|
6902
|
-
if (files.size >= MAX_FILES) {
|
|
6903
|
-
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.`);
|
|
6904
|
-
}
|
|
6905
|
-
const contents = await readFile(full);
|
|
6906
|
-
totalBytes += contents.byteLength;
|
|
6907
|
-
if (totalBytes > MAX_TOTAL_BYTES) {
|
|
6908
|
-
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.`);
|
|
6909
|
-
}
|
|
6910
|
-
files.set(toPackagePath(root, full), contents);
|
|
6911
|
-
}
|
|
6912
|
-
}, "walk");
|
|
6913
|
-
await walk(root);
|
|
6914
|
-
return files;
|
|
6915
|
-
}
|
|
6916
|
-
__name(readPackageDirectory, "readPackageDirectory");
|
|
6917
|
-
function toPackagePath(root, full) {
|
|
6918
|
-
return relative(root, full).split(sep).join("/");
|
|
6919
|
-
}
|
|
6920
|
-
__name(toPackagePath, "toPackagePath");
|
|
7540
|
+
var version = "0.2.0";
|
|
6921
7541
|
|
|
6922
7542
|
// src/report.ts
|
|
6923
7543
|
var useColour = process.stdout.isTTY === true && !process.env.NO_COLOR;
|
|
@@ -7026,13 +7646,518 @@ function printResult(result) {
|
|
|
7026
7646
|
}
|
|
7027
7647
|
__name(printResult, "printResult");
|
|
7028
7648
|
|
|
7029
|
-
// src/
|
|
7649
|
+
// src/catalog/publish-action.ts
|
|
7650
|
+
var import_contracts2 = __toESM(require_dist());
|
|
7651
|
+
import { readFileSync } from "fs";
|
|
7652
|
+
import { dirname, resolve } from "path";
|
|
7653
|
+
import { parse as parseYaml } from "yaml";
|
|
7654
|
+
|
|
7655
|
+
// src/catalog/artifact.ts
|
|
7030
7656
|
var import_contracts = __toESM(require_dist());
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7657
|
+
import { execFileSync } from "child_process";
|
|
7658
|
+
import { createHash } from "crypto";
|
|
7659
|
+
function parseRepository(ref, flag) {
|
|
7660
|
+
if (!import_contracts.ARTIFACT_REPOSITORY_PATTERN.test(ref)) {
|
|
7661
|
+
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.`);
|
|
7662
|
+
}
|
|
7663
|
+
const cut = ref.indexOf("/");
|
|
7664
|
+
return {
|
|
7665
|
+
ref,
|
|
7666
|
+
host: ref.slice(0, cut),
|
|
7667
|
+
path: ref.slice(cut + 1),
|
|
7668
|
+
namespace: ref.slice(0, ref.lastIndexOf("/"))
|
|
7669
|
+
};
|
|
7670
|
+
}
|
|
7671
|
+
__name(parseRepository, "parseRepository");
|
|
7672
|
+
var isDigest = /* @__PURE__ */ __name((value) => import_contracts.ARTIFACT_DIGEST_PATTERN.test(value), "isDigest");
|
|
7673
|
+
var TAG_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$/;
|
|
7674
|
+
function requireTag(tag, origin2) {
|
|
7675
|
+
if (!TAG_PATTERN.test(tag)) {
|
|
7676
|
+
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.`);
|
|
7677
|
+
}
|
|
7678
|
+
}
|
|
7679
|
+
__name(requireTag, "requireTag");
|
|
7680
|
+
var LOOPBACK = /^(localhost|127\.0\.0\.1|\[::1\])(:[0-9]+)?$/;
|
|
7681
|
+
var origin = /* @__PURE__ */ __name((host) => `${LOOPBACK.test(host) ? "http" : "https"}://${host}`, "origin");
|
|
7682
|
+
var sha256 = /* @__PURE__ */ __name((bytes) => `sha256:${createHash("sha256").update(bytes).digest("hex")}`, "sha256");
|
|
7683
|
+
var EMPTY_CONFIG = Buffer.from("{}");
|
|
7684
|
+
var EMPTY_CONFIG_DESCRIPTOR = {
|
|
7685
|
+
mediaType: "application/vnd.oci.empty.v1+json",
|
|
7686
|
+
digest: sha256(EMPTY_CONFIG),
|
|
7687
|
+
size: EMPTY_CONFIG.length
|
|
7688
|
+
};
|
|
7689
|
+
var OCI_MANIFEST = "application/vnd.oci.image.manifest.v1+json";
|
|
7690
|
+
function provenance(flags, dir) {
|
|
7691
|
+
const git = /* @__PURE__ */ __name((...args) => {
|
|
7692
|
+
try {
|
|
7693
|
+
return execFileSync("git", [
|
|
7694
|
+
"-C",
|
|
7695
|
+
dir,
|
|
7696
|
+
...args
|
|
7697
|
+
], {
|
|
7698
|
+
encoding: "utf8",
|
|
7699
|
+
stdio: [
|
|
7700
|
+
"ignore",
|
|
7701
|
+
"pipe",
|
|
7702
|
+
"ignore"
|
|
7703
|
+
]
|
|
7704
|
+
}).trim();
|
|
7705
|
+
} catch {
|
|
7706
|
+
return "";
|
|
7707
|
+
}
|
|
7708
|
+
}, "git");
|
|
7709
|
+
const actions = process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}` : "";
|
|
7710
|
+
const repository = flags.repository ?? (actions || git("remote", "get-url", "origin"));
|
|
7711
|
+
const commit = flags.commit ?? (process.env.GITHUB_SHA || git("rev-parse", "HEAD"));
|
|
7712
|
+
if (!repository || !commit) {
|
|
7713
|
+
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).");
|
|
7714
|
+
}
|
|
7715
|
+
return {
|
|
7716
|
+
repository,
|
|
7717
|
+
commit,
|
|
7718
|
+
publishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7719
|
+
};
|
|
7720
|
+
}
|
|
7721
|
+
__name(provenance, "provenance");
|
|
7722
|
+
function buildArtifact(document) {
|
|
7723
|
+
const documentBytes = Buffer.from(`${JSON.stringify(document, null, 2)}
|
|
7724
|
+
`);
|
|
7725
|
+
const manifest = {
|
|
7726
|
+
schemaVersion: 2,
|
|
7727
|
+
mediaType: OCI_MANIFEST,
|
|
7728
|
+
artifactType: import_contracts.CATALOG_ARTIFACT_MEDIA_TYPE,
|
|
7729
|
+
config: EMPTY_CONFIG_DESCRIPTOR,
|
|
7730
|
+
layers: [
|
|
7731
|
+
{
|
|
7732
|
+
mediaType: import_contracts.CATALOG_ARTIFACT_MEDIA_TYPE,
|
|
7733
|
+
digest: sha256(documentBytes),
|
|
7734
|
+
size: documentBytes.length
|
|
7735
|
+
}
|
|
7736
|
+
],
|
|
7737
|
+
/*
|
|
7738
|
+
* ★ SOURCE AND REVISION ARE FOR THE REGISTRY, NOT THE RESOLVER (docs/127 A.1). GHCR connects
|
|
7739
|
+
* a package to its repository through `org.opencontainers.image.source`, and that link is what
|
|
7740
|
+
* lets the repository's workflow token keep publishing to the package its first run created.
|
|
7741
|
+
* The resolver reads none of these — the document's own provenance is the record — so an
|
|
7742
|
+
* artifact published without them still resolves.
|
|
7743
|
+
*/
|
|
7744
|
+
annotations: {
|
|
7745
|
+
"org.opencontainers.image.created": document.provenance.publishedAt,
|
|
7746
|
+
"org.opencontainers.image.source": document.provenance.repository,
|
|
7747
|
+
"org.opencontainers.image.revision": document.provenance.commit
|
|
7748
|
+
}
|
|
7749
|
+
};
|
|
7750
|
+
const manifestBytes = Buffer.from(JSON.stringify(manifest));
|
|
7751
|
+
return {
|
|
7752
|
+
documentBytes,
|
|
7753
|
+
manifestBytes,
|
|
7754
|
+
manifestDigest: sha256(manifestBytes)
|
|
7755
|
+
};
|
|
7756
|
+
}
|
|
7757
|
+
__name(buildArtifact, "buildArtifact");
|
|
7758
|
+
function basicCredential() {
|
|
7759
|
+
const pat = process.env.GHCR_TOKEN;
|
|
7760
|
+
if (pat) return Buffer.from(`x:${pat}`).toString("base64");
|
|
7761
|
+
const workflow = process.env.GITHUB_TOKEN;
|
|
7762
|
+
if (workflow) return Buffer.from(`${process.env.GITHUB_ACTOR || "x"}:${workflow}`).toString("base64");
|
|
7763
|
+
return void 0;
|
|
7764
|
+
}
|
|
7765
|
+
__name(basicCredential, "basicCredential");
|
|
7766
|
+
var tokens = /* @__PURE__ */ new Map();
|
|
7767
|
+
async function token(repository, challenge) {
|
|
7768
|
+
const field = /* @__PURE__ */ __name((name) => new RegExp(`${name}="([^"]*)"`).exec(challenge ?? "")?.[1], "field");
|
|
7769
|
+
const realm = field("realm");
|
|
7770
|
+
if (!realm) throw new Error(`${repository.host} asked for credentials without a Bearer challenge.`);
|
|
7771
|
+
const url = new URL(realm);
|
|
7772
|
+
const service = field("service");
|
|
7773
|
+
if (service) url.searchParams.set("service", service);
|
|
7774
|
+
url.searchParams.set("scope", `repository:${repository.path}:pull,push`);
|
|
7775
|
+
const credential = basicCredential();
|
|
7776
|
+
const answer = await fetch(url, {
|
|
7777
|
+
headers: credential ? {
|
|
7778
|
+
Authorization: `Basic ${credential}`
|
|
7779
|
+
} : {}
|
|
7780
|
+
});
|
|
7781
|
+
if (!answer.ok) {
|
|
7782
|
+
throw new Error(`${repository.host} refused a push token for ${repository.path}: HTTP ${answer.status}` + (credential ? "." : " \u2014 set GHCR_TOKEN to a token with write:packages, or in a workflow GITHUB_TOKEN with packages: write."));
|
|
7783
|
+
}
|
|
7784
|
+
const body = await answer.json();
|
|
7785
|
+
const value = body.token ?? body.access_token ?? "";
|
|
7786
|
+
tokens.set(repository.path, value);
|
|
7787
|
+
return value;
|
|
7788
|
+
}
|
|
7789
|
+
__name(token, "token");
|
|
7790
|
+
async function call(repository, method, path, { headers = {}, body } = {}) {
|
|
7791
|
+
const url = path.startsWith("http") ? path : `${origin(repository.host)}/v2/${repository.path}/${path}`;
|
|
7792
|
+
const send = /* @__PURE__ */ __name((bearer) => fetch(url, {
|
|
7793
|
+
method,
|
|
7794
|
+
body,
|
|
7795
|
+
redirect: "follow",
|
|
7796
|
+
headers: {
|
|
7797
|
+
...headers,
|
|
7798
|
+
...bearer ? {
|
|
7799
|
+
Authorization: `Bearer ${bearer}`
|
|
7800
|
+
} : {}
|
|
7801
|
+
}
|
|
7802
|
+
}), "send");
|
|
7803
|
+
let answer = await send(tokens.get(repository.path));
|
|
7804
|
+
if (answer.status === 401) answer = await send(await token(repository, answer.headers.get("www-authenticate")));
|
|
7805
|
+
return answer;
|
|
7806
|
+
}
|
|
7807
|
+
__name(call, "call");
|
|
7808
|
+
async function pushBlob(repository, bytes) {
|
|
7809
|
+
const digest = sha256(bytes);
|
|
7810
|
+
if ((await call(repository, "HEAD", `blobs/${digest}`)).ok) return digest;
|
|
7811
|
+
const started = await call(repository, "POST", "blobs/uploads/");
|
|
7812
|
+
if (started.status !== 202) throw new Error(`${repository.host} refused an upload: HTTP ${started.status}`);
|
|
7813
|
+
const location = new URL(started.headers.get("location") ?? "", `${origin(repository.host)}/`);
|
|
7814
|
+
location.searchParams.set("digest", digest);
|
|
7815
|
+
const put = await call(repository, "PUT", location.toString(), {
|
|
7816
|
+
headers: {
|
|
7817
|
+
"Content-Type": "application/octet-stream",
|
|
7818
|
+
"Content-Length": String(bytes.length)
|
|
7819
|
+
},
|
|
7820
|
+
body: bytes
|
|
7821
|
+
});
|
|
7822
|
+
if (put.status !== 201) throw new Error(`${repository.host} rejected blob ${digest}: HTTP ${put.status} ${await put.text()}`);
|
|
7823
|
+
return digest;
|
|
7824
|
+
}
|
|
7825
|
+
__name(pushBlob, "pushBlob");
|
|
7826
|
+
async function manifestExists(repository, digest) {
|
|
7827
|
+
const answer = await call(repository, "HEAD", `manifests/${digest}`, {
|
|
7828
|
+
headers: {
|
|
7829
|
+
Accept: OCI_MANIFEST
|
|
7830
|
+
}
|
|
7831
|
+
});
|
|
7832
|
+
return answer.ok;
|
|
7833
|
+
}
|
|
7834
|
+
__name(manifestExists, "manifestExists");
|
|
7835
|
+
async function pushArtifact(repository, artifact, tag) {
|
|
7836
|
+
await pushBlob(repository, EMPTY_CONFIG);
|
|
7837
|
+
await pushBlob(repository, artifact.documentBytes);
|
|
7838
|
+
for (const reference of [
|
|
7839
|
+
artifact.manifestDigest,
|
|
7840
|
+
tag
|
|
7841
|
+
]) {
|
|
7842
|
+
const put = await call(repository, "PUT", `manifests/${reference}`, {
|
|
7843
|
+
headers: {
|
|
7844
|
+
"Content-Type": OCI_MANIFEST
|
|
7845
|
+
},
|
|
7846
|
+
body: artifact.manifestBytes
|
|
7847
|
+
});
|
|
7848
|
+
if (put.status !== 201) {
|
|
7849
|
+
throw new Error(`${repository.host} rejected the manifest (${reference}): HTTP ${put.status} ${await put.text()}`);
|
|
7850
|
+
}
|
|
7851
|
+
}
|
|
7852
|
+
const asked = await call(repository, "HEAD", `manifests/${tag}`, {
|
|
7853
|
+
headers: {
|
|
7854
|
+
Accept: OCI_MANIFEST
|
|
7855
|
+
}
|
|
7856
|
+
});
|
|
7857
|
+
const reported = asked.headers.get("docker-content-digest");
|
|
7858
|
+
if (!asked.ok || !reported) {
|
|
7859
|
+
throw new Error(`${repository.host} could not say what :${tag} resolves to (HTTP ${asked.status}).`);
|
|
7860
|
+
}
|
|
7861
|
+
if (reported !== artifact.manifestDigest) {
|
|
7862
|
+
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.`);
|
|
7863
|
+
}
|
|
7864
|
+
return reported;
|
|
7865
|
+
}
|
|
7866
|
+
__name(pushArtifact, "pushArtifact");
|
|
7867
|
+
var defaultTag = /* @__PURE__ */ __name(() => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\..*$/, "").toLowerCase(), "defaultTag");
|
|
7868
|
+
|
|
7869
|
+
// src/catalog/flags.ts
|
|
7870
|
+
function parseCatalogFlags(argv, known, repeated = []) {
|
|
7871
|
+
const single = {};
|
|
7872
|
+
const many = {};
|
|
7873
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
7874
|
+
const argument = argv[index];
|
|
7875
|
+
const separator = argument.indexOf("=");
|
|
7876
|
+
const name = separator === -1 ? argument.slice(2) : argument.slice(2, separator);
|
|
7877
|
+
if (!argument.startsWith("--") || !known.includes(name)) throw new Error(`Unknown argument: ${argument}`);
|
|
7878
|
+
let value;
|
|
7879
|
+
if (separator !== -1) {
|
|
7880
|
+
value = argument.slice(separator + 1);
|
|
7881
|
+
} else {
|
|
7882
|
+
value = argv[index + 1];
|
|
7883
|
+
index += 1;
|
|
7884
|
+
}
|
|
7885
|
+
if (value === void 0 || value === "" || value.startsWith("--")) throw new Error(`--${name} needs a value.`);
|
|
7886
|
+
if (repeated.includes(name)) (many[name] ??= []).push(value);
|
|
7887
|
+
else single[name] = value;
|
|
7888
|
+
}
|
|
7889
|
+
return {
|
|
7890
|
+
single,
|
|
7891
|
+
many
|
|
7892
|
+
};
|
|
7893
|
+
}
|
|
7894
|
+
__name(parseCatalogFlags, "parseCatalogFlags");
|
|
7895
|
+
|
|
7896
|
+
// src/catalog/publish-action.ts
|
|
7897
|
+
var PUBLISH_ACTION_USAGE = "runveric catalog publish-action --manifest <file.yaml> --to <registry>/<namespace>/<repository>\n [--tag <tag>] [--repository <source url>] [--commit <sha>]";
|
|
7898
|
+
var isMapping = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isMapping");
|
|
7899
|
+
async function runPublishAction(argv) {
|
|
7900
|
+
const { single: flags } = parseCatalogFlags(argv, [
|
|
7901
|
+
"manifest",
|
|
7902
|
+
"to",
|
|
7903
|
+
"tag",
|
|
7904
|
+
"repository",
|
|
7905
|
+
"commit"
|
|
7906
|
+
]);
|
|
7907
|
+
if (!flags.manifest || !flags.to) throw new Error("--manifest and --to are required.");
|
|
7908
|
+
const repository = parseRepository(flags.to, "--to");
|
|
7909
|
+
const path = resolve(flags.manifest);
|
|
7910
|
+
const manifest = readFileSync(path, "utf8");
|
|
7911
|
+
let parsed;
|
|
7912
|
+
try {
|
|
7913
|
+
parsed = parseYaml(manifest);
|
|
7914
|
+
} catch (cause) {
|
|
7915
|
+
throw new Error(`${flags.manifest} is not valid YAML. (${cause instanceof Error ? cause.message : String(cause)})`);
|
|
7916
|
+
}
|
|
7917
|
+
const fields = isMapping(parsed) ? parsed : {};
|
|
7918
|
+
const key = typeof fields.key === "string" && fields.key.length > 0 ? fields.key : null;
|
|
7919
|
+
if (key === null) throw new Error(`${flags.manifest} declares no top-level \`key:\` \u2014 is it a catalog manifest?`);
|
|
7920
|
+
const misnamed = (0, import_contracts2.actionPackageRefusal)(key, repository.ref);
|
|
7921
|
+
if (misnamed !== null) throw new Error(`--to: ${misnamed}`);
|
|
7922
|
+
let tag;
|
|
7923
|
+
if (fields.declares === "definition+version") {
|
|
7924
|
+
const version2 = typeof fields.version === "string" ? fields.version : null;
|
|
7925
|
+
if (version2 === null) throw new Error(`${flags.manifest} declares "definition+version" and no version:.`);
|
|
7926
|
+
if (flags.tag !== void 0 && flags.tag !== version2) {
|
|
7927
|
+
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.`);
|
|
7928
|
+
}
|
|
7929
|
+
tag = version2;
|
|
7930
|
+
} else {
|
|
7931
|
+
tag = flags.tag ?? defaultTag();
|
|
7932
|
+
}
|
|
7933
|
+
requireTag(tag, flags.tag !== void 0 ? "--tag" : `${flags.manifest}'s version`);
|
|
7934
|
+
const artifact = buildArtifact({
|
|
7935
|
+
schemaVersion: import_contracts2.CATALOG_ACTION_ARTIFACT_SCHEMA_V1,
|
|
7936
|
+
manifest,
|
|
7937
|
+
provenance: provenance(flags, dirname(path))
|
|
7938
|
+
});
|
|
7939
|
+
const digest = await pushArtifact(repository, artifact, tag);
|
|
7940
|
+
process.stderr.write(`\u2713 ${key} published to ${repository.ref}:${tag}
|
|
7941
|
+
`);
|
|
7942
|
+
process.stderr.write(` the registry reports ${digest} \u2014 the digest a release must name.
|
|
7943
|
+
`);
|
|
7944
|
+
process.stdout.write(`${key}=${repository.ref}@${digest}
|
|
7945
|
+
`);
|
|
7946
|
+
}
|
|
7947
|
+
__name(runPublishAction, "runPublishAction");
|
|
7948
|
+
|
|
7949
|
+
// src/catalog/publish-release.ts
|
|
7950
|
+
var import_contracts3 = __toESM(require_dist());
|
|
7951
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
7952
|
+
import { parse as parseYaml2 } from "yaml";
|
|
7953
|
+
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>]";
|
|
7954
|
+
function entriesFromFlags(values) {
|
|
7955
|
+
return values.map((value) => {
|
|
7956
|
+
const match = /^([^=]+)=([^@]+)@(.+)$/.exec(value);
|
|
7957
|
+
if (!match) throw new Error(`--action "${value}" is not <key>=<repository>@<digest>.`);
|
|
7958
|
+
const [, key = "", artifactRef = "", digest = ""] = match;
|
|
7959
|
+
return {
|
|
7960
|
+
key,
|
|
7961
|
+
artifactRef,
|
|
7962
|
+
digest,
|
|
7963
|
+
label: `--action ${key}`
|
|
7964
|
+
};
|
|
7965
|
+
});
|
|
7966
|
+
}
|
|
7967
|
+
__name(entriesFromFlags, "entriesFromFlags");
|
|
7968
|
+
function specFromFile(path) {
|
|
7969
|
+
let value;
|
|
7970
|
+
try {
|
|
7971
|
+
value = parseYaml2(readFileSync2(path, "utf8"));
|
|
7972
|
+
} catch (cause) {
|
|
7973
|
+
throw new Error(`--from ${path} could not be read as YAML. (${cause instanceof Error ? cause.message : String(cause)})`);
|
|
7974
|
+
}
|
|
7975
|
+
const spec = (0, import_contracts3.parseCatalogReleaseSpec)(value);
|
|
7976
|
+
return {
|
|
7977
|
+
version: spec.version,
|
|
7978
|
+
entries: spec.actions.map((action) => ({
|
|
7979
|
+
...action,
|
|
7980
|
+
label: `${path}'s ${action.key}`
|
|
7981
|
+
}))
|
|
7982
|
+
};
|
|
7983
|
+
}
|
|
7984
|
+
__name(specFromFile, "specFromFile");
|
|
7985
|
+
async function runPublishRelease(argv) {
|
|
7986
|
+
const { single: flags, many } = parseCatalogFlags(argv, [
|
|
7987
|
+
"to",
|
|
7988
|
+
"version",
|
|
7989
|
+
"action",
|
|
7990
|
+
"from",
|
|
7991
|
+
"tag",
|
|
7992
|
+
"repository",
|
|
7993
|
+
"commit"
|
|
7994
|
+
], [
|
|
7995
|
+
"action"
|
|
7996
|
+
]);
|
|
7997
|
+
const actionFlags = many.action ?? [];
|
|
7998
|
+
if (flags.from !== void 0 && actionFlags.length > 0) {
|
|
7999
|
+
throw new Error("--from and --action are alternatives: name the release's actions in release.yaml or as flags, not both.");
|
|
8000
|
+
}
|
|
8001
|
+
if (flags.from !== void 0 && flags.version !== void 0) {
|
|
8002
|
+
throw new Error("--version is read from release.yaml when --from is given; pass one or the other.");
|
|
8003
|
+
}
|
|
8004
|
+
if (!flags.to || flags.from === void 0 && (!flags.version || actionFlags.length === 0)) {
|
|
8005
|
+
throw new Error("--to and either --from, or --version and at least one --action, are required.");
|
|
8006
|
+
}
|
|
8007
|
+
const release = parseRepository(flags.to, "--to");
|
|
8008
|
+
const { version: version2, entries } = flags.from !== void 0 ? specFromFile(flags.from) : {
|
|
8009
|
+
version: flags.version,
|
|
8010
|
+
entries: entriesFromFlags(actionFlags)
|
|
8011
|
+
};
|
|
8012
|
+
const tag = flags.tag ?? (flags.from !== void 0 ? version2 : defaultTag());
|
|
8013
|
+
requireTag(tag, flags.tag !== void 0 ? "--tag" : `${flags.from}'s version`);
|
|
8014
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8015
|
+
const actions = entries.map((entry) => {
|
|
8016
|
+
if (seen.has(entry.key)) throw new Error(`${entry.label} names "${entry.key}" twice.`);
|
|
8017
|
+
seen.add(entry.key);
|
|
8018
|
+
const repository = parseRepository(entry.artifactRef, entry.label);
|
|
8019
|
+
if (repository.namespace !== release.namespace) {
|
|
8020
|
+
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.`);
|
|
8021
|
+
}
|
|
8022
|
+
if (!isDigest(entry.digest)) {
|
|
8023
|
+
throw new Error(`${entry.label}: "${entry.digest}" is not a digest. A release names artifacts by digest only.`);
|
|
8024
|
+
}
|
|
8025
|
+
const misnamed = (0, import_contracts3.actionPackageRefusal)(entry.key, entry.artifactRef);
|
|
8026
|
+
if (misnamed !== null) throw new Error(`${entry.label}: ${misnamed}`);
|
|
8027
|
+
return {
|
|
8028
|
+
...entry,
|
|
8029
|
+
repository
|
|
8030
|
+
};
|
|
8031
|
+
});
|
|
8032
|
+
for (const action of actions) {
|
|
8033
|
+
if (!await manifestExists(action.repository, action.digest)) {
|
|
8034
|
+
throw new Error(`${action.artifactRef}@${action.digest} (${action.key}) is not in the registry.`);
|
|
8035
|
+
}
|
|
8036
|
+
}
|
|
8037
|
+
const artifact = buildArtifact({
|
|
8038
|
+
schemaVersion: import_contracts3.CATALOG_RELEASE_SCHEMA_V1,
|
|
8039
|
+
version: version2,
|
|
8040
|
+
provenance: provenance(flags, process.cwd()),
|
|
8041
|
+
actions: actions.map(({ key, artifactRef, digest: digest2 }) => ({
|
|
8042
|
+
key,
|
|
8043
|
+
artifactRef,
|
|
8044
|
+
digest: digest2
|
|
8045
|
+
}))
|
|
8046
|
+
});
|
|
8047
|
+
const digest = await pushArtifact(release, artifact, tag);
|
|
8048
|
+
process.stderr.write(`\u2713 catalog release ${version2} (${actions.length} action artifact(s)) published to ${release.ref}:${tag}
|
|
8049
|
+
`);
|
|
8050
|
+
process.stderr.write(` the registry reports ${digest} \u2014 the digest to plan and pin.
|
|
8051
|
+
`);
|
|
8052
|
+
process.stdout.write(`${release.ref}@${digest}
|
|
8053
|
+
`);
|
|
8054
|
+
}
|
|
8055
|
+
__name(runPublishRelease, "runPublishRelease");
|
|
8056
|
+
|
|
8057
|
+
// src/catalog/index.ts
|
|
8058
|
+
var CATALOG_USAGE = `runveric catalog \u2014 publish first-party catalog artifacts (docs/127)
|
|
8059
|
+
|
|
8060
|
+
${PUBLISH_ACTION_USAGE}
|
|
8061
|
+
Publish one action's manifest, verbatim, as an artifact. stdout: <key>=<repository>@<digest>
|
|
8062
|
+
|
|
8063
|
+
${PUBLISH_RELEASE_USAGE}
|
|
8064
|
+
Publish a release naming each action artifact by digest. stdout: <repository>@<digest>
|
|
8065
|
+
|
|
8066
|
+
Credentials: GHCR_TOKEN (write:packages), or in a workflow GITHUB_TOKEN with packages: write.
|
|
8067
|
+
Provenance: --repository/--commit, else GITHUB_SERVER_URL/GITHUB_REPOSITORY and GITHUB_SHA, else git.`;
|
|
8068
|
+
var SUBCOMMANDS = {
|
|
8069
|
+
"publish-action": runPublishAction,
|
|
8070
|
+
"publish-release": runPublishRelease
|
|
8071
|
+
};
|
|
8072
|
+
async function runCatalog(argv) {
|
|
8073
|
+
const [subcommand, ...rest] = argv;
|
|
8074
|
+
if (subcommand === void 0 || subcommand === "--help" || rest.includes("--help")) {
|
|
8075
|
+
process.stdout.write(`${CATALOG_USAGE}
|
|
8076
|
+
`);
|
|
8077
|
+
return subcommand === void 0 ? 1 : 0;
|
|
8078
|
+
}
|
|
8079
|
+
const run3 = SUBCOMMANDS[subcommand];
|
|
8080
|
+
if (run3 === void 0) {
|
|
8081
|
+
printError(`\u2717 "catalog ${subcommand}" is not a command. There are two: ${Object.keys(SUBCOMMANDS).join(", ")}.`);
|
|
8082
|
+
return 1;
|
|
8083
|
+
}
|
|
8084
|
+
try {
|
|
8085
|
+
await run3(rest);
|
|
8086
|
+
return 0;
|
|
8087
|
+
} catch (error) {
|
|
8088
|
+
printError(`\u2717 ${error instanceof Error ? error.message : String(error)}`);
|
|
8089
|
+
return 1;
|
|
8090
|
+
}
|
|
8091
|
+
}
|
|
8092
|
+
__name(runCatalog, "runCatalog");
|
|
8093
|
+
|
|
8094
|
+
// src/check.ts
|
|
8095
|
+
var import_action_manifest = __toESM(require_dist2());
|
|
8096
|
+
import { resolve as resolve2 } from "path";
|
|
8097
|
+
|
|
8098
|
+
// src/package-dir.ts
|
|
8099
|
+
import { readdir, readFile, stat } from "fs/promises";
|
|
8100
|
+
import { join, relative, sep } from "path";
|
|
8101
|
+
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
8102
|
+
".git",
|
|
8103
|
+
".runveric",
|
|
8104
|
+
"node_modules",
|
|
8105
|
+
".venv",
|
|
8106
|
+
"__pycache__",
|
|
8107
|
+
".DS_Store"
|
|
8108
|
+
]);
|
|
8109
|
+
var MAX_FILES = 2e3;
|
|
8110
|
+
var MAX_TOTAL_BYTES = 16 * 1024 * 1024;
|
|
8111
|
+
var PackageDirectoryError = class extends Error {
|
|
8112
|
+
static {
|
|
8113
|
+
__name(this, "PackageDirectoryError");
|
|
8114
|
+
}
|
|
8115
|
+
};
|
|
8116
|
+
async function readPackageDirectory(root) {
|
|
8117
|
+
const properties = await stat(root).catch(() => null);
|
|
8118
|
+
if (!properties?.isDirectory()) {
|
|
8119
|
+
throw new PackageDirectoryError(`${root} is not a directory.`);
|
|
8120
|
+
}
|
|
8121
|
+
const files = /* @__PURE__ */ new Map();
|
|
8122
|
+
let totalBytes = 0;
|
|
8123
|
+
const walk = /* @__PURE__ */ __name(async (directory) => {
|
|
8124
|
+
for (const entry of await readdir(directory, {
|
|
8125
|
+
withFileTypes: true
|
|
8126
|
+
})) {
|
|
8127
|
+
if (IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
8128
|
+
const full = join(directory, entry.name);
|
|
8129
|
+
if (entry.isDirectory()) {
|
|
8130
|
+
await walk(full);
|
|
8131
|
+
continue;
|
|
8132
|
+
}
|
|
8133
|
+
if (!entry.isFile()) continue;
|
|
8134
|
+
if (files.size >= MAX_FILES) {
|
|
8135
|
+
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.`);
|
|
8136
|
+
}
|
|
8137
|
+
const contents = await readFile(full);
|
|
8138
|
+
totalBytes += contents.byteLength;
|
|
8139
|
+
if (totalBytes > MAX_TOTAL_BYTES) {
|
|
8140
|
+
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.`);
|
|
8141
|
+
}
|
|
8142
|
+
files.set(toPackagePath(root, full), contents);
|
|
8143
|
+
}
|
|
8144
|
+
}, "walk");
|
|
8145
|
+
await walk(root);
|
|
8146
|
+
return files;
|
|
8147
|
+
}
|
|
8148
|
+
__name(readPackageDirectory, "readPackageDirectory");
|
|
8149
|
+
function toPackagePath(root, full) {
|
|
8150
|
+
return relative(root, full).split(sep).join("/");
|
|
8151
|
+
}
|
|
8152
|
+
__name(toPackagePath, "toPackagePath");
|
|
8153
|
+
|
|
8154
|
+
// src/check.ts
|
|
8155
|
+
var import_contracts4 = __toESM(require_dist());
|
|
8156
|
+
async function runCheck(options) {
|
|
8157
|
+
const directory = resolve2(options.directory ?? ".");
|
|
8158
|
+
let files;
|
|
8159
|
+
try {
|
|
8160
|
+
files = await readPackageDirectory(directory);
|
|
7036
8161
|
} catch (error) {
|
|
7037
8162
|
printError(style.red(error instanceof Error ? error.message : String(error)));
|
|
7038
8163
|
return 1;
|
|
@@ -7051,7 +8176,7 @@ async function runCheck(options) {
|
|
|
7051
8176
|
print(style.red("This package would be REFUSED at import."));
|
|
7052
8177
|
} else if (report.lintBlocked) {
|
|
7053
8178
|
print();
|
|
7054
|
-
print(style.red("This package would be ACCEPTED at import \u2014 and then misbehave.") + ` The errors above are not things ${
|
|
8179
|
+
print(style.red("This package would be ACCEPTED at import \u2014 and then misbehave.") + ` The errors above are not things ${import_contracts4.BRAND_SHORT_NAME} checks; they are things that go wrong at run time, silently.`);
|
|
7055
8180
|
}
|
|
7056
8181
|
if (report.advisoryCount > 0) {
|
|
7057
8182
|
print();
|
|
@@ -7071,11 +8196,11 @@ async function runCheck(options) {
|
|
|
7071
8196
|
__name(runCheck, "runCheck");
|
|
7072
8197
|
|
|
7073
8198
|
// src/init.ts
|
|
7074
|
-
var
|
|
8199
|
+
var import_contracts5 = __toESM(require_dist());
|
|
7075
8200
|
var import_action_manifest2 = __toESM(require_dist2());
|
|
7076
8201
|
import { mkdir, writeFile } from "fs/promises";
|
|
7077
8202
|
import { createInterface } from "readline/promises";
|
|
7078
|
-
import { dirname, join as join2, resolve as
|
|
8203
|
+
import { dirname as dirname2, join as join2, resolve as resolve3 } from "path";
|
|
7079
8204
|
async function runInit(options) {
|
|
7080
8205
|
const interactive = process.stdin.isTTY === true && options.yes !== true;
|
|
7081
8206
|
const ask = interactive ? prompter() : null;
|
|
@@ -7087,7 +8212,7 @@ async function runInit(options) {
|
|
|
7087
8212
|
const interpreter = await resolveInterpreter(options.interpreter, ask);
|
|
7088
8213
|
if (!interpreter) return 1;
|
|
7089
8214
|
const author = options.author?.trim() || await ask?.("Author", id.split("/")[0]) || id.split("/")[0];
|
|
7090
|
-
const directory =
|
|
8215
|
+
const directory = resolve3(options.directory ?? id.split("/")[1] ?? "action");
|
|
7091
8216
|
const files = (0, import_action_manifest2.scaffoldActionPackage)({
|
|
7092
8217
|
id,
|
|
7093
8218
|
name,
|
|
@@ -7097,7 +8222,7 @@ async function runInit(options) {
|
|
|
7097
8222
|
});
|
|
7098
8223
|
for (const [path, contents] of files) {
|
|
7099
8224
|
const target = join2(directory, path);
|
|
7100
|
-
await mkdir(
|
|
8225
|
+
await mkdir(dirname2(target), {
|
|
7101
8226
|
recursive: true
|
|
7102
8227
|
});
|
|
7103
8228
|
await writeFile(target, contents, {
|
|
@@ -7140,9 +8265,9 @@ async function resolveId(supplied, ask) {
|
|
|
7140
8265
|
}
|
|
7141
8266
|
__name(resolveId, "resolveId");
|
|
7142
8267
|
async function resolveInterpreter(supplied, ask) {
|
|
7143
|
-
const value = supplied?.trim() || await ask?.(`Interpreter (${
|
|
8268
|
+
const value = supplied?.trim() || await ask?.(`Interpreter (${import_contracts5.PACKAGE_INTERPRETERS.join(" / ")})`, "bash") || "bash";
|
|
7144
8269
|
if (!isInterpreter(value)) {
|
|
7145
|
-
printError(style.red(`"${value}" is not an interpreter ${
|
|
8270
|
+
printError(style.red(`"${value}" is not an interpreter ${import_contracts5.BRAND_SHORT_NAME} runs.`) + ` The runner image ships ${import_contracts5.PACKAGE_INTERPRETERS.join(", ")}.`);
|
|
7146
8271
|
return null;
|
|
7147
8272
|
}
|
|
7148
8273
|
return value;
|
|
@@ -7163,7 +8288,7 @@ function prompter() {
|
|
|
7163
8288
|
}
|
|
7164
8289
|
__name(prompter, "prompter");
|
|
7165
8290
|
function isInterpreter(value) {
|
|
7166
|
-
return
|
|
8291
|
+
return import_contracts5.PACKAGE_INTERPRETERS.includes(value);
|
|
7167
8292
|
}
|
|
7168
8293
|
__name(isInterpreter, "isInterpreter");
|
|
7169
8294
|
function titleCase(value) {
|
|
@@ -7173,20 +8298,38 @@ __name(titleCase, "titleCase");
|
|
|
7173
8298
|
|
|
7174
8299
|
// src/publish.ts
|
|
7175
8300
|
var import_action_manifest3 = __toESM(require_dist2());
|
|
7176
|
-
var
|
|
7177
|
-
import { resolve as
|
|
7178
|
-
|
|
7179
|
-
|
|
8301
|
+
var import_contracts6 = __toESM(require_dist());
|
|
8302
|
+
import { resolve as resolve4 } from "path";
|
|
8303
|
+
|
|
8304
|
+
// src/renamed-variable.ts
|
|
8305
|
+
function renamedVariableRefusal(current, retired, env = process.env) {
|
|
8306
|
+
if (env[current]?.trim()) return null;
|
|
8307
|
+
if (!env[retired]?.trim()) return null;
|
|
8308
|
+
return `${retired} is set, but this CLI reads ${current} \u2014 the variable was renamed in 0.1.2. Rename the export in your shell or CI; ${retired} is no longer read.`;
|
|
8309
|
+
}
|
|
8310
|
+
__name(renamedVariableRefusal, "renamedVariableRefusal");
|
|
8311
|
+
|
|
8312
|
+
// src/publish.ts
|
|
8313
|
+
var TOKEN_ENV = import_contracts6.AUTHORING_CLI_ENV.token;
|
|
8314
|
+
var URL_ENV = import_contracts6.AUTHORING_CLI_ENV.url;
|
|
7180
8315
|
async function runPublish(options) {
|
|
7181
|
-
const directory =
|
|
8316
|
+
const directory = resolve4(options.directory ?? ".");
|
|
7182
8317
|
const baseUrl = (options.to ?? process.env[URL_ENV] ?? "").trim().replace(/\/+$/, "");
|
|
7183
|
-
const
|
|
8318
|
+
const token2 = (options.token ?? process.env[TOKEN_ENV] ?? "").trim();
|
|
8319
|
+
const stale = [
|
|
8320
|
+
options.to === void 0 ? renamedVariableRefusal(URL_ENV, import_contracts6.RETIRED_AUTHORING_CLI_ENV.url) : null,
|
|
8321
|
+
options.token === void 0 ? renamedVariableRefusal(TOKEN_ENV, import_contracts6.RETIRED_AUTHORING_CLI_ENV.token) : null
|
|
8322
|
+
].filter((sentence) => sentence !== null);
|
|
8323
|
+
if (stale.length > 0) {
|
|
8324
|
+
for (const sentence of stale) printError(style.red(sentence));
|
|
8325
|
+
return 1;
|
|
8326
|
+
}
|
|
7184
8327
|
if (!baseUrl) {
|
|
7185
|
-
printError(style.red("Where to?") + ` Pass --to https://your-
|
|
8328
|
+
printError(style.red("Where to?") + ` Pass --to https://your-runveric.example.com, or set ${URL_ENV}.`);
|
|
7186
8329
|
return 1;
|
|
7187
8330
|
}
|
|
7188
|
-
if (!
|
|
7189
|
-
printError(style.red(`Publishing needs ${
|
|
8331
|
+
if (!token2) {
|
|
8332
|
+
printError(style.red(`Publishing needs ${import_contracts6.BRAND_ARTICLE} ${import_contracts6.BRAND_SHORT_NAME} access token.`) + ` Set ${TOKEN_ENV}, or pass --token. (init, check and test need no account at all.)`);
|
|
7190
8333
|
return 1;
|
|
7191
8334
|
}
|
|
7192
8335
|
let files;
|
|
@@ -7206,7 +8349,7 @@ async function runPublish(options) {
|
|
|
7206
8349
|
errors: report.errorCount,
|
|
7207
8350
|
warnings: report.warningCount
|
|
7208
8351
|
});
|
|
7209
|
-
printError(style.red("\nNot published.") + (report.validation.ok ? ` ${
|
|
8352
|
+
printError(style.red("\nNot published.") + (report.validation.ok ? ` ${import_contracts6.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."));
|
|
7210
8353
|
return 1;
|
|
7211
8354
|
}
|
|
7212
8355
|
if (report.advisoryCount > 0) {
|
|
@@ -7233,7 +8376,7 @@ async function runPublish(options) {
|
|
|
7233
8376
|
method: "POST",
|
|
7234
8377
|
headers: {
|
|
7235
8378
|
"content-type": "application/json",
|
|
7236
|
-
authorization: `Bearer ${
|
|
8379
|
+
authorization: `Bearer ${token2}`
|
|
7237
8380
|
},
|
|
7238
8381
|
body
|
|
7239
8382
|
});
|
|
@@ -7253,7 +8396,7 @@ Publish refused (${response.status}).`));
|
|
|
7253
8396
|
try {
|
|
7254
8397
|
version2 = JSON.parse(text);
|
|
7255
8398
|
} catch {
|
|
7256
|
-
printError(style.red(`${
|
|
8399
|
+
printError(style.red(`${import_contracts6.BRAND_SHORT_NAME} accepted the package but returned a response we cannot read.`));
|
|
7257
8400
|
return 1;
|
|
7258
8401
|
}
|
|
7259
8402
|
print();
|
|
@@ -7262,7 +8405,7 @@ Publish refused (${response.status}).`));
|
|
|
7262
8405
|
print(` content digest ${version2.contentDigest}`);
|
|
7263
8406
|
print(` tier ${version2.tier} ${style.grey(`(${version2.provenance})`)}`);
|
|
7264
8407
|
print();
|
|
7265
|
-
print(style.grey(`It is an immutable version now. Adopt it in ${
|
|
8408
|
+
print(style.grey(`It is an immutable version now. Adopt it in ${import_contracts6.BRAND_SHORT_NAME} to put it in the catalog \u2014 publishing does not adopt, so nobody can run it until somebody chooses to.`));
|
|
7266
8409
|
print();
|
|
7267
8410
|
return 0;
|
|
7268
8411
|
}
|
|
@@ -7274,29 +8417,102 @@ function formatProblem(text) {
|
|
|
7274
8417
|
} catch {
|
|
7275
8418
|
return ` ${text}`;
|
|
7276
8419
|
}
|
|
7277
|
-
const problem = (0,
|
|
8420
|
+
const problem = (0, import_contracts6.parseProblemDocument)(parsed);
|
|
7278
8421
|
if (!problem) return ` ${text}`;
|
|
7279
8422
|
const lines = [];
|
|
7280
8423
|
if (problem.detail) lines.push(` ${problem.detail}`);
|
|
7281
|
-
for (const error of (0,
|
|
8424
|
+
for (const error of (0, import_contracts6.problemFieldErrors)(problem)) {
|
|
7282
8425
|
lines.push(error.field ? ` ${style.bold(error.field)}: ${error.message}` : ` ${error.message}`);
|
|
7283
8426
|
}
|
|
7284
8427
|
return lines.length > 0 ? lines.join("\n") : ` ${text}`;
|
|
7285
8428
|
}
|
|
7286
8429
|
__name(formatProblem, "formatProblem");
|
|
7287
8430
|
|
|
8431
|
+
// src/runner-image.ts
|
|
8432
|
+
var import_contracts7 = __toESM(require_dist());
|
|
8433
|
+
import { execFile } from "child_process";
|
|
8434
|
+
import { promisify } from "util";
|
|
8435
|
+
var run = promisify(execFile);
|
|
8436
|
+
var LOCAL_RUNNER_IMAGE = "runveric-package-runner:local";
|
|
8437
|
+
var RUNNER_IMAGE_ENV = import_contracts7.AUTHORING_CLI_ENV.runnerImage;
|
|
8438
|
+
var RenamedVariableError = class extends Error {
|
|
8439
|
+
static {
|
|
8440
|
+
__name(this, "RenamedVariableError");
|
|
8441
|
+
}
|
|
8442
|
+
};
|
|
8443
|
+
function resolveRunnerImage(flag) {
|
|
8444
|
+
const explicit = flag?.trim();
|
|
8445
|
+
if (explicit) return {
|
|
8446
|
+
reference: explicit,
|
|
8447
|
+
origin: "flag"
|
|
8448
|
+
};
|
|
8449
|
+
const configured = process.env[RUNNER_IMAGE_ENV]?.trim();
|
|
8450
|
+
if (configured) return {
|
|
8451
|
+
reference: configured,
|
|
8452
|
+
origin: "environment"
|
|
8453
|
+
};
|
|
8454
|
+
const stale = renamedVariableRefusal(RUNNER_IMAGE_ENV, import_contracts7.RETIRED_AUTHORING_CLI_ENV.runnerImage);
|
|
8455
|
+
if (stale) throw new RenamedVariableError(stale);
|
|
8456
|
+
return {
|
|
8457
|
+
reference: LOCAL_RUNNER_IMAGE,
|
|
8458
|
+
origin: "local-build"
|
|
8459
|
+
};
|
|
8460
|
+
}
|
|
8461
|
+
__name(resolveRunnerImage, "resolveRunnerImage");
|
|
8462
|
+
function runnerImageNote(image) {
|
|
8463
|
+
switch (image.origin) {
|
|
8464
|
+
case "flag":
|
|
8465
|
+
return "from --image";
|
|
8466
|
+
case "environment":
|
|
8467
|
+
return `from ${RUNNER_IMAGE_ENV}`;
|
|
8468
|
+
case "local-build":
|
|
8469
|
+
return `locally built \u2014 NOT the image your ${import_contracts7.BRAND_SHORT_NAME} runs; set ${RUNNER_IMAGE_ENV} to match it`;
|
|
8470
|
+
}
|
|
8471
|
+
}
|
|
8472
|
+
__name(runnerImageNote, "runnerImageNote");
|
|
8473
|
+
var DockerMissingError = class extends Error {
|
|
8474
|
+
static {
|
|
8475
|
+
__name(this, "DockerMissingError");
|
|
8476
|
+
}
|
|
8477
|
+
};
|
|
8478
|
+
async function assertDockerAvailable() {
|
|
8479
|
+
try {
|
|
8480
|
+
await run("docker", [
|
|
8481
|
+
"version",
|
|
8482
|
+
"--format",
|
|
8483
|
+
"{{.Server.Version}}"
|
|
8484
|
+
]);
|
|
8485
|
+
} catch {
|
|
8486
|
+
throw new DockerMissingError(`Docker is not available. \`runveric test\` runs your package in the real ${import_contracts7.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.`);
|
|
8487
|
+
}
|
|
8488
|
+
}
|
|
8489
|
+
__name(assertDockerAvailable, "assertDockerAvailable");
|
|
8490
|
+
async function imageIsPresent(reference) {
|
|
8491
|
+
try {
|
|
8492
|
+
await run("docker", [
|
|
8493
|
+
"image",
|
|
8494
|
+
"inspect",
|
|
8495
|
+
reference
|
|
8496
|
+
]);
|
|
8497
|
+
return true;
|
|
8498
|
+
} catch {
|
|
8499
|
+
return false;
|
|
8500
|
+
}
|
|
8501
|
+
}
|
|
8502
|
+
__name(imageIsPresent, "imageIsPresent");
|
|
8503
|
+
|
|
7288
8504
|
// src/test.ts
|
|
7289
8505
|
var import_action_manifest4 = __toESM(require_dist2());
|
|
7290
8506
|
import { spawn } from "child_process";
|
|
7291
8507
|
import { mkdir as mkdir2, mkdtemp, readFile as readFile2, rm, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
7292
8508
|
import { tmpdir } from "os";
|
|
7293
|
-
import { dirname as
|
|
8509
|
+
import { dirname as dirname3, join as join3, resolve as resolve5 } from "path";
|
|
7294
8510
|
import { StringDecoder } from "string_decoder";
|
|
7295
8511
|
|
|
7296
8512
|
// src/azure-credential.ts
|
|
7297
|
-
import { execFile } from "child_process";
|
|
7298
|
-
import { promisify } from "util";
|
|
7299
|
-
var
|
|
8513
|
+
import { execFile as execFile2 } from "child_process";
|
|
8514
|
+
import { promisify as promisify2 } from "util";
|
|
8515
|
+
var run2 = promisify2(execFile2);
|
|
7300
8516
|
var AzureCredentialError = class extends Error {
|
|
7301
8517
|
static {
|
|
7302
8518
|
__name(this, "AzureCredentialError");
|
|
@@ -7313,7 +8529,7 @@ async function acquireAzureCredential() {
|
|
|
7313
8529
|
if (!account.id || !account.tenantId) {
|
|
7314
8530
|
throw new AzureCredentialError("`az account show` returned no subscription. Run `az login` and `az account set --subscription <id>` first.");
|
|
7315
8531
|
}
|
|
7316
|
-
const
|
|
8532
|
+
const token2 = await azJson([
|
|
7317
8533
|
"account",
|
|
7318
8534
|
"get-access-token",
|
|
7319
8535
|
"--resource",
|
|
@@ -7321,11 +8537,11 @@ async function acquireAzureCredential() {
|
|
|
7321
8537
|
"--output",
|
|
7322
8538
|
"json"
|
|
7323
8539
|
]);
|
|
7324
|
-
if (!
|
|
8540
|
+
if (!token2.accessToken) {
|
|
7325
8541
|
throw new AzureCredentialError("`az account get-access-token` returned no access token.");
|
|
7326
8542
|
}
|
|
7327
8543
|
return {
|
|
7328
|
-
token:
|
|
8544
|
+
token: token2.accessToken,
|
|
7329
8545
|
// The identical names the credential adapter puts in a lease, so a package reads the same
|
|
7330
8546
|
// environment here and in production.
|
|
7331
8547
|
env: {
|
|
@@ -7343,7 +8559,7 @@ __name(acquireAzureCredential, "acquireAzureCredential");
|
|
|
7343
8559
|
async function azJson(args) {
|
|
7344
8560
|
let stdout;
|
|
7345
8561
|
try {
|
|
7346
|
-
({ stdout } = await
|
|
8562
|
+
({ stdout } = await run2("az", args, {
|
|
7347
8563
|
maxBuffer: 8 * 1024 * 1024
|
|
7348
8564
|
}));
|
|
7349
8565
|
} catch (error) {
|
|
@@ -7359,91 +8575,25 @@ ${message.trim()}`);
|
|
|
7359
8575
|
}
|
|
7360
8576
|
__name(azJson, "azJson");
|
|
7361
8577
|
|
|
7362
|
-
// src/runner-image.ts
|
|
7363
|
-
var import_contracts4 = __toESM(require_dist());
|
|
7364
|
-
import { execFile as execFile2 } from "child_process";
|
|
7365
|
-
import { promisify as promisify2 } from "util";
|
|
7366
|
-
var run2 = promisify2(execFile2);
|
|
7367
|
-
var LOCAL_RUNNER_IMAGE = "opera-package-runner:local";
|
|
7368
|
-
var RUNNER_IMAGE_ENV = "OPERA_RUNNER_IMAGE";
|
|
7369
|
-
function resolveRunnerImage(flag) {
|
|
7370
|
-
const explicit = flag?.trim();
|
|
7371
|
-
if (explicit) return {
|
|
7372
|
-
reference: explicit,
|
|
7373
|
-
origin: "flag"
|
|
7374
|
-
};
|
|
7375
|
-
const configured = process.env[RUNNER_IMAGE_ENV]?.trim();
|
|
7376
|
-
if (configured) return {
|
|
7377
|
-
reference: configured,
|
|
7378
|
-
origin: "environment"
|
|
7379
|
-
};
|
|
7380
|
-
return {
|
|
7381
|
-
reference: LOCAL_RUNNER_IMAGE,
|
|
7382
|
-
origin: "local-build"
|
|
7383
|
-
};
|
|
7384
|
-
}
|
|
7385
|
-
__name(resolveRunnerImage, "resolveRunnerImage");
|
|
7386
|
-
function runnerImageNote(image) {
|
|
7387
|
-
switch (image.origin) {
|
|
7388
|
-
case "flag":
|
|
7389
|
-
return "from --image";
|
|
7390
|
-
case "environment":
|
|
7391
|
-
return `from ${RUNNER_IMAGE_ENV}`;
|
|
7392
|
-
case "local-build":
|
|
7393
|
-
return `locally built \u2014 NOT the image your ${import_contracts4.BRAND_SHORT_NAME} runs; set OPERA_RUNNER_IMAGE to match it`;
|
|
7394
|
-
}
|
|
7395
|
-
}
|
|
7396
|
-
__name(runnerImageNote, "runnerImageNote");
|
|
7397
|
-
var DockerMissingError = class extends Error {
|
|
7398
|
-
static {
|
|
7399
|
-
__name(this, "DockerMissingError");
|
|
7400
|
-
}
|
|
7401
|
-
};
|
|
7402
|
-
async function assertDockerAvailable() {
|
|
7403
|
-
try {
|
|
7404
|
-
await run2("docker", [
|
|
7405
|
-
"version",
|
|
7406
|
-
"--format",
|
|
7407
|
-
"{{.Server.Version}}"
|
|
7408
|
-
]);
|
|
7409
|
-
} catch {
|
|
7410
|
-
throw new DockerMissingError(`Docker is not available. \`runveric test\` runs your package in the real ${import_contracts4.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.`);
|
|
7411
|
-
}
|
|
7412
|
-
}
|
|
7413
|
-
__name(assertDockerAvailable, "assertDockerAvailable");
|
|
7414
|
-
async function imageIsPresent(reference) {
|
|
7415
|
-
try {
|
|
7416
|
-
await run2("docker", [
|
|
7417
|
-
"image",
|
|
7418
|
-
"inspect",
|
|
7419
|
-
reference
|
|
7420
|
-
]);
|
|
7421
|
-
return true;
|
|
7422
|
-
} catch {
|
|
7423
|
-
return false;
|
|
7424
|
-
}
|
|
7425
|
-
}
|
|
7426
|
-
__name(imageIsPresent, "imageIsPresent");
|
|
7427
|
-
|
|
7428
8578
|
// src/test.ts
|
|
7429
|
-
var
|
|
8579
|
+
var import_contracts8 = __toESM(require_dist());
|
|
7430
8580
|
var PHASES = [
|
|
7431
8581
|
"validate",
|
|
7432
8582
|
"run",
|
|
7433
8583
|
"rollback"
|
|
7434
8584
|
];
|
|
7435
8585
|
var CONTAINER = {
|
|
7436
|
-
packageRoot: "/
|
|
7437
|
-
input: "/
|
|
7438
|
-
output: "/
|
|
7439
|
-
scratch: "/
|
|
7440
|
-
creds: "/
|
|
8586
|
+
packageRoot: "/runveric/package",
|
|
8587
|
+
input: "/runveric/input",
|
|
8588
|
+
output: "/runveric/output",
|
|
8589
|
+
scratch: "/runveric/scratch",
|
|
8590
|
+
creds: "/runveric/creds"
|
|
7441
8591
|
};
|
|
7442
8592
|
async function runTest(options) {
|
|
7443
|
-
const directory =
|
|
8593
|
+
const directory = resolve5(options.directory ?? ".");
|
|
7444
8594
|
const phase = options.phase ?? "run";
|
|
7445
8595
|
if (!PHASES.includes(phase)) {
|
|
7446
|
-
printError(style.red(`"${options.phase}" is not a phase.`) + ` ${
|
|
8596
|
+
printError(style.red(`"${options.phase}" is not a phase.`) + ` ${import_contracts8.BRAND_SHORT_NAME} runs ${PHASES.join(", ")} \u2014 one at a time, so you can read each one's result.`);
|
|
7447
8597
|
return 1;
|
|
7448
8598
|
}
|
|
7449
8599
|
let files;
|
|
@@ -7476,7 +8626,13 @@ async function runTest(options) {
|
|
|
7476
8626
|
printError(style.red(error instanceof Error ? error.message : String(error)));
|
|
7477
8627
|
return 1;
|
|
7478
8628
|
}
|
|
7479
|
-
|
|
8629
|
+
let image;
|
|
8630
|
+
try {
|
|
8631
|
+
image = resolveRunnerImage(options.image);
|
|
8632
|
+
} catch (error) {
|
|
8633
|
+
printError(style.red(error instanceof Error ? error.message : String(error)));
|
|
8634
|
+
return 1;
|
|
8635
|
+
}
|
|
7480
8636
|
try {
|
|
7481
8637
|
await assertDockerAvailable();
|
|
7482
8638
|
} catch (error) {
|
|
@@ -7509,13 +8665,13 @@ async function runTest(options) {
|
|
|
7509
8665
|
print(` runner image ${image.reference} ${style.grey(`(${runnerImageNote(image)})`)}`);
|
|
7510
8666
|
print(` interpreter ${interpreter}@${manifest.runtime.version}`);
|
|
7511
8667
|
print(` entrypoint ${entrypoint}${import_action_manifest4.PACKAGE_INTERPRETER_LAUNCH[interpreter].entryFile}`);
|
|
7512
|
-
print(` identity ${credential ? `${credential.account.name} ${style.grey(`\u2014 YOUR access, not ${
|
|
8668
|
+
print(` identity ${credential ? `${credential.account.name} ${style.grey(`\u2014 YOUR access, not ${import_contracts8.BRAND_SHORT_NAME}'s brokered least-privilege identity`)}` : style.grey("none (--no-credential)")}`);
|
|
7513
8669
|
if (manifest.requiresNetwork) {
|
|
7514
|
-
print(` network ${style.yellow("open")} ${style.grey(`\u2014 your allowlist (${(manifest.egressAllowlist ?? []).join(", ")}) is NOT enforced here; ${
|
|
8670
|
+
print(` network ${style.yellow("open")} ${style.grey(`\u2014 your allowlist (${(manifest.egressAllowlist ?? []).join(", ")}) is NOT enforced here; ${import_contracts8.BRAND_SHORT_NAME} enforces it`)}`);
|
|
7515
8671
|
} else {
|
|
7516
8672
|
print(` network ${style.grey("none \u2014 this package declares requiresNetwork: false")}`);
|
|
7517
8673
|
}
|
|
7518
|
-
if (options.dryRun) print(` ${style.bold("dry run")}
|
|
8674
|
+
if (options.dryRun) print(` ${style.bold("dry run")} RUNVERIC_DRY_RUN=true`);
|
|
7519
8675
|
if (!await imageIsPresent(image.reference)) {
|
|
7520
8676
|
print(` ${style.grey("pulling the image (first run only)\u2026")}`);
|
|
7521
8677
|
}
|
|
@@ -7613,13 +8769,13 @@ async function materialize(workspace, input) {
|
|
|
7613
8769
|
})));
|
|
7614
8770
|
for (const [path, contents] of input.files) {
|
|
7615
8771
|
const target = join3(layout.packageRoot, path);
|
|
7616
|
-
await mkdir2(
|
|
8772
|
+
await mkdir2(dirname3(target), {
|
|
7617
8773
|
recursive: true
|
|
7618
8774
|
});
|
|
7619
8775
|
await writeFile2(target, contents);
|
|
7620
8776
|
}
|
|
7621
8777
|
const context = {
|
|
7622
|
-
protocolVersion:
|
|
8778
|
+
protocolVersion: import_contracts8.PACKAGE_EXECUTION_PROTOCOL_VERSION,
|
|
7623
8779
|
// A recognisable id rather than a random uuid: it appears in the package's own log lines,
|
|
7624
8780
|
// and "local-test" tells the reader instantly that this was not a real run.
|
|
7625
8781
|
executionId: "local-test",
|
|
@@ -7645,27 +8801,27 @@ async function runContainer(options) {
|
|
|
7645
8801
|
const launch = import_action_manifest4.PACKAGE_INTERPRETER_LAUNCH[options.interpreter];
|
|
7646
8802
|
const script = `${CONTAINER.packageRoot}/${options.entrypoint}${launch.entryFile}`;
|
|
7647
8803
|
const environment = {
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
8804
|
+
RUNVERIC_PROTOCOL_VERSION: import_contracts8.PACKAGE_RESULT_PROTOCOL_VERSION,
|
|
8805
|
+
RUNVERIC_PHASE: options.phase,
|
|
8806
|
+
RUNVERIC_CONTEXT_FILE: `${CONTAINER.input}/context.json`,
|
|
8807
|
+
RUNVERIC_PARAMETERS_FILE: `${CONTAINER.input}/parameters.json`,
|
|
8808
|
+
RUNVERIC_RESULT_FILE: `${CONTAINER.output}/result.json`,
|
|
8809
|
+
RUNVERIC_WORK_DIR: CONTAINER.scratch,
|
|
8810
|
+
RUNVERIC_CANCEL_FILE: `${CONTAINER.input}/cancel.requested`,
|
|
8811
|
+
RUNVERIC_DRY_RUN: options.dryRun ? "true" : "false",
|
|
7656
8812
|
// HONEST, and matching the ACA runner's own reasoning: this flag means "there is no route
|
|
7657
8813
|
// out at all". A container with `--network none` genuinely has none; one with a network
|
|
7658
8814
|
// does not, whether or not an allowlist would have applied.
|
|
7659
|
-
|
|
8815
|
+
RUNVERIC_NETWORK_ISOLATED: options.requiresNetwork ? "false" : "true",
|
|
7660
8816
|
LANG: "C.UTF-8",
|
|
7661
8817
|
LC_ALL: "C.UTF-8",
|
|
7662
8818
|
POWERSHELL_TELEMETRY_OPTOUT: "1",
|
|
7663
8819
|
DOTNET_CLI_TELEMETRY_OPTOUT: "1",
|
|
7664
8820
|
...options.layout.hasPriorResult ? {
|
|
7665
|
-
|
|
8821
|
+
RUNVERIC_PRIOR_RESULT_FILE: `${CONTAINER.input}/prior-result.json`
|
|
7666
8822
|
} : {},
|
|
7667
8823
|
...options.layout.hasCredential ? {
|
|
7668
|
-
|
|
8824
|
+
RUNVERIC_AZURE_TOKEN_FILE: `${CONTAINER.creds}/azure-token`
|
|
7669
8825
|
} : {},
|
|
7670
8826
|
...options.credentialEnv ?? {}
|
|
7671
8827
|
};
|
|
@@ -7748,7 +8904,7 @@ async function runContainer(options) {
|
|
|
7748
8904
|
__name(runContainer, "runContainer");
|
|
7749
8905
|
function emitLine(line, stderr, onEvent) {
|
|
7750
8906
|
if (line.length === 0) return;
|
|
7751
|
-
const prefix = "
|
|
8907
|
+
const prefix = "RUNVERIC_EVENT ";
|
|
7752
8908
|
if (!stderr && line.startsWith(prefix)) {
|
|
7753
8909
|
try {
|
|
7754
8910
|
const parsed = JSON.parse(line.slice(prefix.length));
|
|
@@ -7760,7 +8916,7 @@ function emitLine(line, stderr, onEvent) {
|
|
|
7760
8916
|
}
|
|
7761
8917
|
onEvent({
|
|
7762
8918
|
level: "warn",
|
|
7763
|
-
message: "Package emitted malformed
|
|
8919
|
+
message: "Package emitted malformed RUNVERIC_EVENT JSON."
|
|
7764
8920
|
});
|
|
7765
8921
|
return;
|
|
7766
8922
|
}
|
|
@@ -7772,13 +8928,13 @@ function emitLine(line, stderr, onEvent) {
|
|
|
7772
8928
|
__name(emitLine, "emitLine");
|
|
7773
8929
|
async function reportOutcome(options) {
|
|
7774
8930
|
if (options.exit.timedOut) {
|
|
7775
|
-
printError(style.red("\nThe package did not finish within 15 minutes and was killed.") + ` ${
|
|
8931
|
+
printError(style.red("\nThe package did not finish within 15 minutes and was killed.") + ` ${import_contracts8.BRAND_SHORT_NAME} enforces the \`executionTimeout\` in your manifest; this is the local ceiling.`);
|
|
7776
8932
|
return 1;
|
|
7777
8933
|
}
|
|
7778
8934
|
const exists = await stat2(options.resultPath).catch(() => null);
|
|
7779
8935
|
if (!exists?.isFile() || exists.size === 0) {
|
|
7780
8936
|
printError(style.red("\nThe package wrote no result.") + `
|
|
7781
|
-
result.json is the only channel ${
|
|
8937
|
+
result.json is the only channel ${import_contracts8.BRAND_SHORT_NAME} reads. Exiting ${options.exit.code ?? "with a signal"} without writing $RUNVERIC_RESULT_FILE is a FAILED run, whatever the script printed.
|
|
7782
8938
|
Write it as the last thing your script does, on every path \u2014 including the ones that fail.`);
|
|
7783
8939
|
return 1;
|
|
7784
8940
|
}
|
|
@@ -7786,7 +8942,7 @@ async function reportOutcome(options) {
|
|
|
7786
8942
|
try {
|
|
7787
8943
|
parsed = JSON.parse(await readFile2(options.resultPath, "utf8"));
|
|
7788
8944
|
} catch {
|
|
7789
|
-
printError(style.red("\nresult.json is not valid JSON.") + ` ${
|
|
8945
|
+
printError(style.red("\nresult.json is not valid JSON.") + ` ${import_contracts8.BRAND_SHORT_NAME} refuses the run rather than guessing what the package meant.`);
|
|
7790
8946
|
return 1;
|
|
7791
8947
|
}
|
|
7792
8948
|
let result;
|
|
@@ -7794,14 +8950,14 @@ async function reportOutcome(options) {
|
|
|
7794
8950
|
result = (0, import_action_manifest4.validatePackageResult)(parsed, options.phase);
|
|
7795
8951
|
} catch (error) {
|
|
7796
8952
|
printError(style.red(`
|
|
7797
|
-
${
|
|
8953
|
+
${import_contracts8.BRAND_SHORT_NAME} would REFUSE this result.`) + `
|
|
7798
8954
|
${error instanceof Error ? error.message : String(error)}
|
|
7799
8955
|
This is the same check the control plane runs, so fixing it here fixes it there.`);
|
|
7800
8956
|
return 1;
|
|
7801
8957
|
}
|
|
7802
8958
|
printResult(result);
|
|
7803
8959
|
if (options.exit.code !== 0) {
|
|
7804
|
-
print(style.yellow(`The package exited ${options.exit.code} but wrote a valid result. ${
|
|
8960
|
+
print(style.yellow(`The package exited ${options.exit.code} but wrote a valid result. ${import_contracts8.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.`));
|
|
7805
8961
|
print();
|
|
7806
8962
|
}
|
|
7807
8963
|
return result.status === "succeeded" ? 0 : 1;
|
|
@@ -7810,7 +8966,7 @@ __name(reportOutcome, "reportOutcome");
|
|
|
7810
8966
|
async function readJsonFile(path) {
|
|
7811
8967
|
let source;
|
|
7812
8968
|
try {
|
|
7813
|
-
source = await readFile2(
|
|
8969
|
+
source = await readFile2(resolve5(path), "utf8");
|
|
7814
8970
|
} catch {
|
|
7815
8971
|
throw new Error(`Could not read ${path}.`);
|
|
7816
8972
|
}
|
|
@@ -7828,9 +8984,9 @@ async function readJsonFile(path) {
|
|
|
7828
8984
|
__name(readJsonFile, "readJsonFile");
|
|
7829
8985
|
|
|
7830
8986
|
// src/main.ts
|
|
7831
|
-
var
|
|
8987
|
+
var import_contracts9 = __toESM(require_dist());
|
|
7832
8988
|
var USAGE = `
|
|
7833
|
-
${style.bold("runveric")} \u2014 author ${
|
|
8989
|
+
${style.bold("runveric")} \u2014 author ${import_contracts9.BRAND_ARTICLE} ${import_contracts9.BRAND_SHORT_NAME} action, without pushing anything.
|
|
7834
8990
|
|
|
7835
8991
|
${style.bold("USAGE")}
|
|
7836
8992
|
runveric <command> [options]
|
|
@@ -7838,8 +8994,9 @@ ${style.bold("USAGE")}
|
|
|
7838
8994
|
${style.bold("COMMANDS")}
|
|
7839
8995
|
init [directory] Scaffold a working action. It passes check and test immediately.
|
|
7840
8996
|
check Validate the package. Offline \u2014 no account, no cloud, no network.
|
|
7841
|
-
test Run one phase in the real ${
|
|
7842
|
-
publish Upload it to ${
|
|
8997
|
+
test Run one phase in the real ${import_contracts9.BRAND_SHORT_NAME} runner image.
|
|
8998
|
+
publish Upload it to ${import_contracts9.BRAND_SHORT_NAME} as an authored version. Needs an account.
|
|
8999
|
+
catalog <command> Publish first-party catalog artifacts. Not for authoring: see \`runveric catalog --help\`.
|
|
7843
9000
|
|
|
7844
9001
|
${style.bold("init")}
|
|
7845
9002
|
--id <publisher/name> The action id. Required.
|
|
@@ -7853,18 +9010,18 @@ ${style.bold("check")}
|
|
|
7853
9010
|
|
|
7854
9011
|
${style.bold("test")}
|
|
7855
9012
|
--phase <name> validate | run | rollback. Defaults to run.
|
|
7856
|
-
--dry-run Sets
|
|
9013
|
+
--dry-run Sets RUNVERIC_DRY_RUN=true, as an operator's rehearsal does.
|
|
7857
9014
|
--param <name=value> A parameter value. Repeatable. Defaults come from your schema.
|
|
7858
9015
|
--params-file <path> A JSON object of parameters. --param overrides it.
|
|
7859
9016
|
--prior-result <path> The run's result.json. Required shape for --phase rollback.
|
|
7860
|
-
--image <ref> The runner image. Defaults to
|
|
9017
|
+
--image <ref> The runner image. Defaults to $${RUNNER_IMAGE_ENV}.
|
|
7861
9018
|
--no-credential Skip \`az account get-access-token\`.
|
|
7862
|
-
--keep Keep the materialized /
|
|
9019
|
+
--keep Keep the materialized /runveric tree for inspection.
|
|
7863
9020
|
|
|
7864
9021
|
${style.bold("publish")}
|
|
7865
|
-
--to <url> Your ${
|
|
7866
|
-
--token <token> Access token. Defaults to
|
|
7867
|
-
--force Publish despite lint errors. Never bypasses ${
|
|
9022
|
+
--to <url> Your ${import_contracts9.BRAND_SHORT_NAME} URL. Defaults to $${URL_ENV}.
|
|
9023
|
+
--token <token> Access token. Defaults to $${TOKEN_ENV}.
|
|
9024
|
+
--force Publish despite lint errors. Never bypasses ${import_contracts9.BRAND_SHORT_NAME}'s validation.
|
|
7868
9025
|
|
|
7869
9026
|
${style.bold("EVERYWHERE")}
|
|
7870
9027
|
--dir <path> The package directory. Defaults to the current directory.
|
|
@@ -7907,7 +9064,9 @@ __name(parseArgv, "parseArgv");
|
|
|
7907
9064
|
var first = /* @__PURE__ */ __name((parsed, name) => parsed.flags.get(name)?.[0], "first");
|
|
7908
9065
|
var bool = /* @__PURE__ */ __name((parsed, name) => parsed.flags.get(name)?.[0] === "true", "bool");
|
|
7909
9066
|
async function main() {
|
|
7910
|
-
const
|
|
9067
|
+
const argv = process.argv.slice(2);
|
|
9068
|
+
if (argv[0] === "catalog") return runCatalog(argv.slice(1));
|
|
9069
|
+
const parsed = parseArgv(argv);
|
|
7911
9070
|
if (parsed.flags.has("version")) {
|
|
7912
9071
|
print(version);
|
|
7913
9072
|
return 0;
|
|
@@ -7975,7 +9134,7 @@ async function main() {
|
|
|
7975
9134
|
force: bool(parsed, "force")
|
|
7976
9135
|
});
|
|
7977
9136
|
default:
|
|
7978
|
-
printError(style.red(`"${parsed.command}" is not a command.`) + " There are four: init, check, test, publish.");
|
|
9137
|
+
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.");
|
|
7979
9138
|
return 1;
|
|
7980
9139
|
}
|
|
7981
9140
|
}
|