@intentius/plywood 0.3.7 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/image-digests.json +2 -2
- package/dist/plywood.mjs +105 -146
- package/dist/plywood.mjs.map +4 -4
- package/package.json +19 -7
package/dist/image-digests.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"ghcr.io/intentius/plywood-pulumi:0.3.
|
|
3
|
-
"ghcr.io/intentius/plywood-plunge:0.3.
|
|
2
|
+
"ghcr.io/intentius/plywood-pulumi:0.3.8-pulumi3.272.0": "sha256:b26afcdd7dd108768663bd5b3cef524b5323b5f5fff8ccb79d717c2895713ae3",
|
|
3
|
+
"ghcr.io/intentius/plywood-plunge:0.3.8-plunge0.2.0": "sha256:48878580388a54ad5635c378d4848424354f9ca4b8f67434b7614a8210446b3e"
|
|
4
4
|
}
|
package/dist/plywood.mjs
CHANGED
|
@@ -20,7 +20,7 @@ var __export = (target, all) => {
|
|
|
20
20
|
var define_PLYWOOD_IMAGE_DIGESTS_default;
|
|
21
21
|
var init_define_PLYWOOD_IMAGE_DIGESTS = __esm({
|
|
22
22
|
"<define:__PLYWOOD_IMAGE_DIGESTS__>"() {
|
|
23
|
-
define_PLYWOOD_IMAGE_DIGESTS_default = { "ghcr.io/intentius/plywood-pulumi:0.3.
|
|
23
|
+
define_PLYWOOD_IMAGE_DIGESTS_default = { "ghcr.io/intentius/plywood-pulumi:0.3.8-pulumi3.272.0": "sha256:b26afcdd7dd108768663bd5b3cef524b5323b5f5fff8ccb79d717c2895713ae3", "ghcr.io/intentius/plywood-plunge:0.3.8-plunge0.2.0": "sha256:48878580388a54ad5635c378d4848424354f9ca4b8f67434b7614a8210446b3e" };
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
|
|
@@ -409,38 +409,24 @@ function wordsForJobs(jobs2) {
|
|
|
409
409
|
function driftOpName(stack) {
|
|
410
410
|
return DRIFT_OP_PREFIX + stack.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
411
411
|
}
|
|
412
|
-
function driftIssueMarker(stack) {
|
|
413
|
-
return `<!-- plywood:drift ${stack} -->`;
|
|
414
|
-
}
|
|
415
412
|
function driftIssueTitle(stack) {
|
|
416
413
|
return `plywood: ${stack} has drifted`;
|
|
417
414
|
}
|
|
418
|
-
function
|
|
419
|
-
return
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
"
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
...input.attribution?.length ? ["Who changed each attribute, from the known writes and the audit log:", "", ...input.attribution, ""] : [],
|
|
432
|
-
`To put the cloud back to the program, merge a change to ${stack} or run \`${PRODUCT} up\`. To keep the cloud's values, change the program to match and let its preview show no changes.`,
|
|
433
|
-
...input.respond === "up" ? ["", `respond.drift is up, so this run also asked for an update of ${stack}'s wave; its gate decides.`] : [],
|
|
434
|
-
...input.runUrl ? ["", `Read by ${input.runUrl}.`] : [],
|
|
435
|
-
"",
|
|
436
|
-
`<!-- plywood:drift-fingerprint ${input.fingerprint} -->`
|
|
437
|
-
];
|
|
438
|
-
return lines2.join("\n");
|
|
439
|
-
}
|
|
440
|
-
function driftClearedComment(stack, observedAt2) {
|
|
441
|
-
return `plywood's drift check at ${observedAt2} found ${stack} matching its state, so this issue is closed.`;
|
|
415
|
+
function driftIssueWords(stack, respond) {
|
|
416
|
+
return {
|
|
417
|
+
product: PRODUCT,
|
|
418
|
+
title: () => driftIssueTitle(stack),
|
|
419
|
+
intro: ({ drifted, observedAt: observedAt2 }) => `plywood's drift check read ${stack} against the cloud at ${observedAt2} and found ${drifted.length} resource${drifted.length === 1 ? "" : "s"} that differ from the stack's state.`,
|
|
420
|
+
address: "URN",
|
|
421
|
+
advice: () => [
|
|
422
|
+
`To put the cloud back to the program, merge a change to ${stack} or run \`${PRODUCT} up\`. To keep the cloud's values, change the program to match and let its preview show no changes.`,
|
|
423
|
+
...respond === "up" ? ["", `respond.drift is up, so this run also asked for an update of ${stack}'s wave; its gate decides.`] : []
|
|
424
|
+
].join("\n"),
|
|
425
|
+
foundBy: (runUrl) => `Read by ${runUrl}.`,
|
|
426
|
+
cleared: ({ observedAt: observedAt2 }) => `plywood's drift check at ${observedAt2} found ${stack} matching its state, so this issue is closed.`
|
|
427
|
+
};
|
|
442
428
|
}
|
|
443
|
-
var PRODUCT, TAGLINE, OP_NAME, PR_GATE, VERBS, BIN, NOTIFY_WORDS, PRODUCT_WORDS, ACTION_COMMAND, POLICY_RULE_FORMS, CLOUD_NAMES, NO_IDENTITY, TEMPLATE_REQUEST_WORDS, STACK_OP_WORDS, DRIFT_OP_PREFIX, LOCK_GRAIN_WORDS, PLUNGE_TIMING_ATTRIBUTES;
|
|
429
|
+
var PRODUCT, TAGLINE, OP_NAME, PR_GATE, VERBS, BIN, NOTIFY_WORDS, PRODUCT_WORDS, ACTION_COMMAND, POLICY_RULE_FORMS, CLOUD_NAMES, NO_IDENTITY, TEMPLATE_REQUEST_WORDS, STACK_OP_WORDS, DRIFT_OP_PREFIX, DRIFT_ATTRIBUTION_HEADING, LOCK_GRAIN_WORDS, PLUNGE_TIMING_ATTRIBUTES;
|
|
444
430
|
var init_words = __esm({
|
|
445
431
|
"src/words.ts"() {
|
|
446
432
|
"use strict";
|
|
@@ -484,7 +470,7 @@ var init_words = __esm({
|
|
|
484
470
|
};
|
|
485
471
|
STACK_OP_WORDS = {
|
|
486
472
|
usage: {
|
|
487
|
-
export: `${PRODUCT} export <project/stack> --out <file
|
|
473
|
+
export: `${PRODUCT} export <project/stack> --out <file> [--version <n>]`,
|
|
488
474
|
cancel: `${PRODUCT} cancel <project/stack> [--lock <id>]`,
|
|
489
475
|
move: `${PRODUCT} move <urn>... --from <project/stack> --to <project/stack>, or ${PRODUCT} move <project/stack> --onto plunge`,
|
|
490
476
|
rollback: `${PRODUCT} rollback <project/stack> [--digest <sha256:...>], or ${PRODUCT} rollback --release <name> --env <env>`,
|
|
@@ -495,7 +481,7 @@ var init_words = __esm({
|
|
|
495
481
|
acted: (stack, name, r) => `Ran action ${name} on ${stack}: ${r.engine} replaced ${r.urn}.${r.tail ? `
|
|
496
482
|
${r.tail}` : ""}`,
|
|
497
483
|
noAction: (stack, name, declared) => declared.length ? `${stack} has no action ${name} in plywood.yml; its actions are ${declared.join(", ")}` : `${stack} declares no actions in plywood.yml; add one under stacks.${stack}.actions with a name and the URN of a command resource`,
|
|
498
|
-
exported: (stack, r) => `Exported ${stack} to ${r.file} (${r.resources} resources, ${r.bytes} bytes, sha256 ${r.sha256}). The file is readable by you alone; the ledger keeps its digest and size, never its content.`,
|
|
484
|
+
exported: (stack, r) => `Exported version ${r.version} of ${stack} to ${r.file} (${r.resources} resources, ${r.bytes} bytes, sha256 ${r.sha256}). The file is readable by you alone; the ledger keeps its digest and size, never its content.`,
|
|
499
485
|
cancelled: (stack, lock, checked) => `Released lock ${lock} on ${stack}.${checked ? "" : " The backend's lock could not be read, so it was not checked against the approved one first."}`,
|
|
500
486
|
moved: (from, to, r) => `Moved ${r.moved.length} resource${r.moved.length === 1 ? "" : "s"} from ${from} to ${to} on ${r.engine}. Both stacks preview no change.`,
|
|
501
487
|
needsLock: "cancel needs --lock <id>, the lock the stack holds: the id pulumi printed when it refused the update, or the file name under .pulumi/locks/ on a file:// backend",
|
|
@@ -509,6 +495,7 @@ ${r.tail}` : ""}`,
|
|
|
509
495
|
releaseIsEnvWide: (stack, env) => `--release rolls back every stack the release records in an environment, so it takes --env, not a stack. Run '${PRODUCT} rollback --release <name> --env ${env}', or '${PRODUCT} rollback ${stack}' for this stack's previous release.`
|
|
510
496
|
};
|
|
511
497
|
DRIFT_OP_PREFIX = "plywood-drift-";
|
|
498
|
+
DRIFT_ATTRIBUTION_HEADING = "Who changed each attribute, from the known writes and the audit log:";
|
|
512
499
|
LOCK_GRAIN_WORDS = {
|
|
513
500
|
perEngine: (engine3) => engine3 === "plunge" ? "resource, plunge's default (no ci.lockGrain; chant's pulumi lexicon sets it)" : engine3 === "pulumi" ? "stack, stock Pulumi's default (no ci.lockGrain; chant's pulumi lexicon sets it)" : "per engine: resource on plunge, stack on stock Pulumi (no ci.lockGrain; chant's pulumi lexicon sets it)"
|
|
514
501
|
};
|
|
@@ -618,47 +605,29 @@ var init_control = __esm({
|
|
|
618
605
|
var version, dependencies;
|
|
619
606
|
var init_package = __esm({
|
|
620
607
|
"package.json"() {
|
|
621
|
-
version = "0.3.
|
|
608
|
+
version = "0.3.8";
|
|
622
609
|
dependencies = {
|
|
623
|
-
"@intentius/chant": "0.124.
|
|
624
|
-
"@intentius/chant-lexicon-forgejo": "0.124.
|
|
625
|
-
"@intentius/chant-lexicon-github": "0.124.
|
|
626
|
-
"@intentius/chant-lexicon-gitlab": "0.124.
|
|
627
|
-
"@intentius/chant-lexicon-pulumi": "0.124.
|
|
610
|
+
"@intentius/chant": "0.124.87",
|
|
611
|
+
"@intentius/chant-lexicon-forgejo": "0.124.87",
|
|
612
|
+
"@intentius/chant-lexicon-github": "0.124.87",
|
|
613
|
+
"@intentius/chant-lexicon-gitlab": "0.124.87",
|
|
614
|
+
"@intentius/chant-lexicon-pulumi": "0.124.87"
|
|
628
615
|
};
|
|
629
616
|
}
|
|
630
617
|
});
|
|
631
618
|
|
|
632
619
|
// src/images.ts
|
|
633
|
-
import {
|
|
620
|
+
import { ciToolSha256, derivedImageDockerfile, productImageDockerfile } from "@intentius/chant/ci-image";
|
|
621
|
+
import { plungeInstallLine, pulumiInstallLine } from "@intentius/chant-lexicon-pulumi/ci-setup";
|
|
634
622
|
function engineDir(engine3, version2) {
|
|
635
623
|
return `${ENGINE_ROOT}/${engine3}-${version2}`;
|
|
636
624
|
}
|
|
637
625
|
function engineBin(engine3, version2) {
|
|
638
626
|
return `${engineDir(engine3, version2)}/${engine3}`;
|
|
639
627
|
}
|
|
640
|
-
function release(engine3, version2) {
|
|
641
|
-
if (engine3 === "pulumi") {
|
|
642
|
-
return {
|
|
643
|
-
base: `https://github.com/pulumi/pulumi/releases/download/v${version2}`,
|
|
644
|
-
archive: (arch) => `pulumi-v${version2}-linux-${arch}.tar.gz`,
|
|
645
|
-
checksums: `pulumi-${version2}-checksums.txt`
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
return {
|
|
649
|
-
base: `https://github.com/INTENTIUS/plunge/releases/download/plunge-v${version2}`,
|
|
650
|
-
archive: (arch) => `plunge-v${version2}-linux-${arch}.tar.gz`,
|
|
651
|
-
checksums: `plunge-v${version2}-checksums.txt`
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
628
|
function pinnedSums(engine3, version2) {
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
for (const arch of ARCHES) {
|
|
658
|
-
const sum = PINNED_SHA256[r.archive(arch)];
|
|
659
|
-
if (sum) out[arch] = sum;
|
|
660
|
-
}
|
|
661
|
-
return out;
|
|
629
|
+
const sums = ciToolSha256(engine3, version2);
|
|
630
|
+
return { ...sums.amd64 ? { x64: sums.amd64 } : {}, ...sums.arm64 ? { arm64: sums.arm64 } : {} };
|
|
662
631
|
}
|
|
663
632
|
function installLine(engine3, version2, link = true) {
|
|
664
633
|
const sums = pinnedSums(engine3, version2);
|
|
@@ -704,7 +673,7 @@ function chantPipelineOptions(p) {
|
|
|
704
673
|
const lines2 = p.engineSetup ?? [];
|
|
705
674
|
return { ...p.image ? { image: p.image } : {}, ...p.runner !== void 0 ? { runner: p.runner } : {}, ...lines2.length ? { beforeScript: [...lines2] } : {} };
|
|
706
675
|
}
|
|
707
|
-
var digests, REGISTRY, TOOL_VERSIONS,
|
|
676
|
+
var digests, REGISTRY, TOOL_VERSIONS, ENGINE_ROOT, DIGEST, PACK_DIR, RELEASE_LABEL_ARG, LABEL_REF, DOCKERFILE_HEADER, PLUGIN;
|
|
708
677
|
var init_images = __esm({
|
|
709
678
|
"src/images.ts"() {
|
|
710
679
|
"use strict";
|
|
@@ -717,17 +686,17 @@ var init_images = __esm({
|
|
|
717
686
|
/** A plunge release (INTENTIUS/plunge, tag plunge-v<ver>), not the Pulumi version it is built from. */
|
|
718
687
|
plunge: "0.2.0"
|
|
719
688
|
};
|
|
720
|
-
ARCHES = ["x64", "arm64"];
|
|
721
|
-
PINNED_SHA256 = {
|
|
722
|
-
"pulumi-v3.272.0-linux-x64.tar.gz": "58d545f2b98761051922008ebd381ecef95484d7f7a5ff7faa15d7a780ea84f6",
|
|
723
|
-
"pulumi-v3.272.0-linux-arm64.tar.gz": "4f0741901b8982c4326b702dabf483c63d2cf77c88ac42dd752b72541284a534",
|
|
724
|
-
"plunge-v0.2.0-linux-x64.tar.gz": "c9c73d05cca9ad214b7f44230a6162cdcb6ad64bbfc725bc94688b997b191495",
|
|
725
|
-
"plunge-v0.2.0-linux-arm64.tar.gz": "c176a82f038164085aa19c8ad6530d6022e560e124050e535dbe364ae0473357"
|
|
726
|
-
};
|
|
727
689
|
ENGINE_ROOT = "/opt/plywood";
|
|
728
690
|
DIGEST = /@sha256:[0-9a-f]{64}$/;
|
|
691
|
+
PACK_DIR = "images/pack";
|
|
729
692
|
RELEASE_LABEL_ARG = "CHANT_RELEASE_LABEL";
|
|
730
693
|
LABEL_REF = `\${${RELEASE_LABEL_ARG}}`;
|
|
694
|
+
DOCKERFILE_HEADER = [
|
|
695
|
+
"Rendered from packages/plywood/src/images.ts by packages/plywood/scripts/build-images.mjs.",
|
|
696
|
+
"Edit the module, then run npm run images -w @intentius/plywood. Build from the repository root",
|
|
697
|
+
`after npm pack -w @intentius/plywood --pack-destination ${PACK_DIR}, with --build-arg ${RELEASE_LABEL_ARG}=<the packed version>`,
|
|
698
|
+
"(just images passes the committed one; a release passes its label); nothing pushes it."
|
|
699
|
+
];
|
|
731
700
|
PLUGIN = /^(?:(resource|converter|language|analyzer|tool):)?([a-z0-9][a-z0-9-]*)@(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/;
|
|
732
701
|
}
|
|
733
702
|
});
|
|
@@ -2666,8 +2635,9 @@ var init_agent_jobs = __esm({
|
|
|
2666
2635
|
|
|
2667
2636
|
// src/drift-notify.ts
|
|
2668
2637
|
import { createHash } from "node:crypto";
|
|
2669
|
-
|
|
2670
|
-
|
|
2638
|
+
import { driftIssueFingerprint } from "@intentius/chant/drift-issue";
|
|
2639
|
+
function runFingerprint(results) {
|
|
2640
|
+
const parts = results.filter((r) => r.ok && r.drifted > 0).map((r) => `${r.stack} ${driftIssueFingerprint(r.records)}`).sort();
|
|
2671
2641
|
return createHash("sha256").update(parts.join("\n")).digest("hex").slice(0, 16);
|
|
2672
2642
|
}
|
|
2673
2643
|
function notifiesDrift(notify2) {
|
|
@@ -2677,7 +2647,7 @@ async function sendDriftNotice(input) {
|
|
|
2677
2647
|
if (!notifiesDrift(input.notify)) return void 0;
|
|
2678
2648
|
const drifted = input.results.filter((r) => r.ok && r.drifted > 0);
|
|
2679
2649
|
if (drifted.length === 0) return void 0;
|
|
2680
|
-
const fingerprint = runFingerprint(input.results
|
|
2650
|
+
const fingerprint = runFingerprint(input.results);
|
|
2681
2651
|
if (drifted.every((r) => r.issue?.action === "unchanged")) {
|
|
2682
2652
|
return { sent: false, reason: "no drift event was sent: every drifted stack's finding is the one its issue already holds", fingerprint };
|
|
2683
2653
|
}
|
|
@@ -3589,26 +3559,23 @@ var init_up = __esm({
|
|
|
3589
3559
|
});
|
|
3590
3560
|
|
|
3591
3561
|
// src/drift.ts
|
|
3592
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
3593
3562
|
import { resolveTargetCloudIdentity } from "@intentius/chant/ci-identity";
|
|
3563
|
+
import { ForgeFlagError, forgeTargetOptionsFromFlags, forgeTargetWith } from "@intentius/chant/forge-flags";
|
|
3564
|
+
import { forgeClient } from "@intentius/chant/pr-forge";
|
|
3565
|
+
import { keepDriftIssue } from "@intentius/chant/drift-issue";
|
|
3594
3566
|
import { existsSync as existsSync7, mkdirSync as mkdirSync4, mkdtempSync, readdirSync as readdirSync4, readFileSync as readFileSync8, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3595
3567
|
import { tmpdir } from "node:os";
|
|
3596
3568
|
import { dirname as dirname6, join as join7 } from "node:path";
|
|
3597
3569
|
function driftRows(doc) {
|
|
3598
3570
|
return (doc.records ?? []).filter((r) => DRIFT_STATUSES.includes(r.status)).map((r) => ({ urn: r.address, status: r.status, attributes: (r.attributes ?? []).map((a) => a.path).sort() })).sort((a, b) => a.urn < b.urn ? -1 : a.urn > b.urn ? 1 : 0);
|
|
3599
3571
|
}
|
|
3600
|
-
function driftFingerprint(rows) {
|
|
3601
|
-
const text = rows.map((r) => `${r.urn} ${r.status} ${r.attributes.join(",")}`).join("\n");
|
|
3602
|
-
return createHash2("sha256").update(text).digest("hex").slice(0, 16);
|
|
3603
|
-
}
|
|
3604
3572
|
function observedAt(doc) {
|
|
3605
3573
|
const times = (doc.records ?? []).map((r) => r.observedAt).filter((t) => typeof t === "string").sort();
|
|
3606
3574
|
return times[times.length - 1] ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3607
3575
|
}
|
|
3608
3576
|
async function forgeFor(kind, env) {
|
|
3609
3577
|
if (forgeOverride) return forgeOverride(kind, env);
|
|
3610
|
-
|
|
3611
|
-
return m.forgeClient(m.forgeTargetFromEnv(env, { kind }));
|
|
3578
|
+
return forgeClient(forgeTargetWith(env, { kind }));
|
|
3612
3579
|
}
|
|
3613
3580
|
function wavesHolding(doc, stacks2) {
|
|
3614
3581
|
if (!Array.isArray(doc.waves)) return "all";
|
|
@@ -3626,7 +3593,13 @@ async function drift2(args, json, table, io) {
|
|
|
3626
3593
|
}
|
|
3627
3594
|
const unknown = stackFlag.rest.filter((a) => a.startsWith("-"));
|
|
3628
3595
|
if (unknown.length) return usage(`drift does not take ${unknown.join(" ")}`, table, io);
|
|
3629
|
-
|
|
3596
|
+
let forge;
|
|
3597
|
+
try {
|
|
3598
|
+
forge = forgeTargetOptionsFromFlags({ forge: forgeFlag.value }).kind;
|
|
3599
|
+
} catch (e) {
|
|
3600
|
+
if (e instanceof ForgeFlagError) return usage(e.message, table, io);
|
|
3601
|
+
throw e;
|
|
3602
|
+
}
|
|
3630
3603
|
if (respondFlag.value !== void 0 && !DRIFT_RESPONSES.includes(respondFlag.value)) return usage(`--respond takes one of ${DRIFT_RESPONSES.join(", ")}`, table, io);
|
|
3631
3604
|
const path = findConfig(io.cwd);
|
|
3632
3605
|
if (!path) return usage(`no plywood.yml in ${io.cwd} or above it; run '${PRODUCT} init' first`, table, io);
|
|
@@ -3650,7 +3623,6 @@ async function drift2(args, json, table, io) {
|
|
|
3650
3623
|
if (strangers.length) return usage(`${strangers.join(", ")} ${strangers.length === 1 ? "is" : "are"} not under stacks in ${path}`, table, io);
|
|
3651
3624
|
if (resource.value !== void 0 && stacks2.length !== 1) return usage("--resource reads one resource of one stack; name the stack", table, io);
|
|
3652
3625
|
const respond = respondFlag.value ?? driftResponseOf(doc);
|
|
3653
|
-
const forge = forgeFlag.value;
|
|
3654
3626
|
return withChant(`${PRODUCT} drift`, table, io, async (chant) => {
|
|
3655
3627
|
const dir = mkdtempSync(join7(tmpdir(), "plywood-drift-"));
|
|
3656
3628
|
const results = [];
|
|
@@ -3703,7 +3675,7 @@ async function drift2(args, json, table, io) {
|
|
|
3703
3675
|
const failed = results.some((r) => !r.ok) || upkeepFailed;
|
|
3704
3676
|
const code = failed ? table.failed : upCode !== void 0 && upCode !== table.done ? upCode : drifted.length ? table.drift : table.done;
|
|
3705
3677
|
if (json) {
|
|
3706
|
-
io.out(JSON.stringify({ command: "drift", ok: code === table.done, exitCode: code, respond, stacks: results, ...notice ? { notify: notice } : {} }));
|
|
3678
|
+
io.out(JSON.stringify({ command: "drift", ok: code === table.done, exitCode: code, respond, stacks: results.map(({ records: _, ...r }) => r), ...notice ? { notify: notice } : {} }));
|
|
3707
3679
|
} else {
|
|
3708
3680
|
if (notice) io.err(`${PRODUCT} drift: ${notice.sent ? `sent the drift event, fingerprint ${notice.fingerprint}` : notice.reason}`);
|
|
3709
3681
|
for (const r of results) {
|
|
@@ -3725,7 +3697,6 @@ async function driftNotice(chant, doc, results, environment, io) {
|
|
|
3725
3697
|
return sendDriftNotice({
|
|
3726
3698
|
notify: notify2,
|
|
3727
3699
|
results,
|
|
3728
|
-
stackFingerprint: driftFingerprint,
|
|
3729
3700
|
chant: { ...await chantDriftNotify(), words: chant.assertProductWords(PRODUCT_WORDS) },
|
|
3730
3701
|
env: io.env,
|
|
3731
3702
|
...environment ? { environment } : {},
|
|
@@ -3759,20 +3730,21 @@ async function readStack(chant, stack, member, dir, opts) {
|
|
|
3759
3730
|
try {
|
|
3760
3731
|
if (existsSync7(file)) doc = JSON.parse(readFileSync8(file, "utf8"));
|
|
3761
3732
|
} catch (e) {
|
|
3762
|
-
return { stack, ok: false, drifted: 0, rows: [], error: `chant wrote a drift document plywood cannot read: ${e.message}` };
|
|
3733
|
+
return { stack, ok: false, drifted: 0, rows: [], records: [], error: `chant wrote a drift document plywood cannot read: ${e.message}` };
|
|
3763
3734
|
}
|
|
3764
3735
|
if (code !== 0 || !doc || doc.error) {
|
|
3765
|
-
return { stack, ok: false, drifted: 0, rows: [], error: doc?.error ? `${doc.error.code}: ${doc.error.message}` : `chant workspace drift exited ${code}` };
|
|
3736
|
+
return { stack, ok: false, drifted: 0, rows: [], records: [], error: doc?.error ? `${doc.error.code}: ${doc.error.message}` : `chant workspace drift exited ${code}` };
|
|
3766
3737
|
}
|
|
3767
3738
|
const rows = driftRows(doc);
|
|
3739
|
+
const records = (doc.records ?? []).map((r) => ({ ...r, member: r.member ?? member }));
|
|
3768
3740
|
const unread = (doc.records ?? []).filter((r) => r.status === "not-observed");
|
|
3769
3741
|
if (rows.length === 0 && unread.length > 0) {
|
|
3770
3742
|
const first = unread[0];
|
|
3771
3743
|
const why = first.unobserved?.detail ?? first.unobserved?.reason ?? "no reason given";
|
|
3772
|
-
return { stack, ok: false, drifted: 0, rows: [], error: `${unread.length} resource${unread.length === 1 ? "" : "s"} could not be read, so the stack's drift is unknown: ${first.address}: ${why}` };
|
|
3744
|
+
return { stack, ok: false, drifted: 0, rows: [], records: [], error: `${unread.length} resource${unread.length === 1 ? "" : "s"} could not be read, so the stack's drift is unknown: ${first.address}: ${why}` };
|
|
3773
3745
|
}
|
|
3774
3746
|
const attribution = rows.length && opts.attribute ? await attributeStack(opts.attribute, stack, doc) : void 0;
|
|
3775
|
-
return { stack, ok: true, drifted: rows.length, rows, observedAt: observedAt(doc), ...attribution ? { attribution } : {} };
|
|
3747
|
+
return { stack, ok: true, drifted: rows.length, rows, records, observedAt: observedAt(doc), ...attribution ? { attribution } : {} };
|
|
3776
3748
|
}
|
|
3777
3749
|
async function withBackend(backend, read) {
|
|
3778
3750
|
if (backend === void 0) return read();
|
|
@@ -3813,13 +3785,13 @@ function specHasUpdate(cwd, op) {
|
|
|
3813
3785
|
return false;
|
|
3814
3786
|
}
|
|
3815
3787
|
}
|
|
3788
|
+
function driftIssueNotes(attribution) {
|
|
3789
|
+
return attribution?.length ? [DRIFT_ATTRIBUTION_HEADING, "", ...attribution] : void 0;
|
|
3790
|
+
}
|
|
3816
3791
|
async function keepIssue(client, r, respond) {
|
|
3817
|
-
const
|
|
3818
|
-
const
|
|
3819
|
-
|
|
3820
|
-
const fingerprint = driftFingerprint(r.rows);
|
|
3821
|
-
const body = driftIssueBody({ stack: r.stack, rows: r.rows, observedAt: at, fingerprint, ...client.runUrl ? { runUrl: client.runUrl } : {}, respond, ...r.attribution?.length ? { attribution: r.attribution } : {} });
|
|
3822
|
-
return client.trackIssue(marker, { title: driftIssueTitle(r.stack), body }, { sameWhen: fingerprint });
|
|
3792
|
+
const notes = driftIssueNotes(r.attribution);
|
|
3793
|
+
const kept = await keepDriftIssue(client, { words: driftIssueWords(r.stack, respond), environment: r.stack, records: r.records, ...client.runUrl ? { runUrl: client.runUrl } : {}, ...notes ? { notes } : {} });
|
|
3794
|
+
return { action: kept.action, ...kept.number !== void 0 ? { number: kept.number } : {}, ...kept.url ? { url: kept.url } : {} };
|
|
3823
3795
|
}
|
|
3824
3796
|
function driftOpFile(doc, stacks2, schedule) {
|
|
3825
3797
|
const listed = stacksOf(doc);
|
|
@@ -3923,7 +3895,6 @@ var init_drift2 = __esm({
|
|
|
3923
3895
|
init_stacks();
|
|
3924
3896
|
init_detect();
|
|
3925
3897
|
init_render();
|
|
3926
|
-
init_routing();
|
|
3927
3898
|
init_images();
|
|
3928
3899
|
init_stages();
|
|
3929
3900
|
init_up();
|
|
@@ -6268,32 +6239,7 @@ var init_stages = __esm({
|
|
|
6268
6239
|
|
|
6269
6240
|
// src/bin.ts
|
|
6270
6241
|
init_define_PLYWOOD_IMAGE_DIGESTS();
|
|
6271
|
-
|
|
6272
|
-
// src/chant-resolve.ts
|
|
6273
|
-
init_define_PLYWOOD_IMAGE_DIGESTS();
|
|
6274
|
-
import * as nodeModule from "node:module";
|
|
6275
|
-
var CHANT_SPECIFIER = /^@intentius\/chant(?:-lexicon-[a-z0-9-]+)?(?:\/|$)/;
|
|
6276
|
-
function chantResolveHook(from) {
|
|
6277
|
-
return (specifier, context, next) => {
|
|
6278
|
-
try {
|
|
6279
|
-
return next(specifier, context);
|
|
6280
|
-
} catch (err) {
|
|
6281
|
-
const code = err.code;
|
|
6282
|
-
if (!CHANT_SPECIFIER.test(specifier) || code !== "ERR_MODULE_NOT_FOUND" && code !== "MODULE_NOT_FOUND") throw err;
|
|
6283
|
-
if (context.parentURL === from) throw err;
|
|
6284
|
-
return next(specifier, { ...context, parentURL: from });
|
|
6285
|
-
}
|
|
6286
|
-
};
|
|
6287
|
-
}
|
|
6288
|
-
var installed = false;
|
|
6289
|
-
function installChantResolution(from = import.meta.url) {
|
|
6290
|
-
if (installed) return true;
|
|
6291
|
-
const register = nodeModule.registerHooks;
|
|
6292
|
-
if (typeof register !== "function") return false;
|
|
6293
|
-
register({ resolve: chantResolveHook(from) });
|
|
6294
|
-
installed = true;
|
|
6295
|
-
return true;
|
|
6296
|
-
}
|
|
6242
|
+
import { installChantResolveFallback } from "@intentius/chant/chant-install";
|
|
6297
6243
|
|
|
6298
6244
|
// src/cli.ts
|
|
6299
6245
|
init_define_PLYWOOD_IMAGE_DIGESTS();
|
|
@@ -6345,7 +6291,7 @@ async function lockToCancel(given, stack, env) {
|
|
|
6345
6291
|
}
|
|
6346
6292
|
var cachedOps;
|
|
6347
6293
|
function chantStackOps() {
|
|
6348
|
-
cachedOps ??= import("@intentius/chant-lexicon-pulumi")
|
|
6294
|
+
cachedOps ??= import("@intentius/chant-lexicon-pulumi");
|
|
6349
6295
|
return cachedOps;
|
|
6350
6296
|
}
|
|
6351
6297
|
function readConfig(cwd) {
|
|
@@ -6420,7 +6366,7 @@ function reportOutcome(verb, outcome, gate, rules, done, json, table, io) {
|
|
|
6420
6366
|
text = done(outcome.result);
|
|
6421
6367
|
break;
|
|
6422
6368
|
case "failed":
|
|
6423
|
-
code = table.failed;
|
|
6369
|
+
code = outcome.check === "moved" ? table.moved : table.failed;
|
|
6424
6370
|
text = `${PRODUCT} ${verb}: ${outcome.error}`;
|
|
6425
6371
|
break;
|
|
6426
6372
|
}
|
|
@@ -6435,7 +6381,7 @@ function reportOutcome(verb, outcome, gate, rules, done, json, table, io) {
|
|
|
6435
6381
|
digest: outcome.digest,
|
|
6436
6382
|
...approveLine ? { approve: approveLine } : {},
|
|
6437
6383
|
...outcome.status === "done" ? { result: outcome.result } : {},
|
|
6438
|
-
...outcome.status === "failed" ? { error: outcome.error } : {},
|
|
6384
|
+
...outcome.status === "failed" ? { error: outcome.error, ...outcome.check ? { check: outcome.check } : {} } : {},
|
|
6439
6385
|
...outcome.status === "refused" ? { refusedBy: outcome.by, reason: outcome.reason } : {}
|
|
6440
6386
|
}));
|
|
6441
6387
|
} else (code === table.done ? io.out : io.err)(text);
|
|
@@ -6447,9 +6393,11 @@ async function runRequest(verb, json, table, io, call) {
|
|
|
6447
6393
|
return await call(ops);
|
|
6448
6394
|
} catch (e) {
|
|
6449
6395
|
const message = e.message;
|
|
6450
|
-
|
|
6396
|
+
const refusal = e instanceof ops.PulumiStackOpRefused ? e.code : void 0;
|
|
6397
|
+
const code = refusal === "live-run" || refusal === "held" ? table.held : table.failed;
|
|
6398
|
+
if (json) io.out(JSON.stringify({ command: verb, ok: false, exitCode: code, error: message, ...refusal ? { refusal } : {} }));
|
|
6451
6399
|
else io.err(`${PRODUCT} ${verb}: ${message}`);
|
|
6452
|
-
return
|
|
6400
|
+
return code;
|
|
6453
6401
|
}
|
|
6454
6402
|
}
|
|
6455
6403
|
function commonFlags(args) {
|
|
@@ -6464,9 +6412,11 @@ async function exportStack(args, json, table, io) {
|
|
|
6464
6412
|
if (common.missing) return usage(`${common.missing} takes a value`, table, io);
|
|
6465
6413
|
const out = takeFlag(common.rest, "--out");
|
|
6466
6414
|
if (out.missing) return usage("--out takes a file path outside the repository", table, io);
|
|
6467
|
-
const
|
|
6415
|
+
const version2 = takeFlag(out.rest, "--version");
|
|
6416
|
+
if (version2.missing) return usage("--version takes the update number, or the sha256: digest of an export, that the stack holds now", table, io);
|
|
6417
|
+
const extra = version2.rest.filter((a) => a.startsWith("-"));
|
|
6468
6418
|
if (extra.length) return usage(`export does not take ${extra.join(" ")}`, table, io);
|
|
6469
|
-
const named = positionals(
|
|
6419
|
+
const named = positionals(version2.rest);
|
|
6470
6420
|
if (named.length !== 1) return usage(`export takes one stack: ${STACK_OP_WORDS.usage.export}`, table, io);
|
|
6471
6421
|
if (!out.value) return usage(`export needs --out <file>, a path on your machine outside every git working tree: ${STACK_OP_WORDS.usage.export}`, table, io);
|
|
6472
6422
|
const read = readConfig(io.cwd);
|
|
@@ -6486,11 +6436,19 @@ async function exportStack(args, json, table, io) {
|
|
|
6486
6436
|
dir: stack.dir,
|
|
6487
6437
|
stack: stack.stack,
|
|
6488
6438
|
out: outFile,
|
|
6439
|
+
...version2.value ? { version: version2.value } : {},
|
|
6489
6440
|
...stack.engine ? { engine: stack.engine } : {}
|
|
6490
6441
|
});
|
|
6491
6442
|
return reportOutcome("export", outcome, stack.stack, rules, (r) => STACK_OP_WORDS.exported(stack.key, r), json, table, io);
|
|
6492
6443
|
});
|
|
6493
6444
|
}
|
|
6445
|
+
function cancelForge(config2, root) {
|
|
6446
|
+
const f = toChantSettings(config2).pipeline.forge;
|
|
6447
|
+
const detected = f?.kind ? void 0 : detectForge(root);
|
|
6448
|
+
const kind = f?.kind ?? detected?.kind;
|
|
6449
|
+
const url = f?.url ?? detected?.url;
|
|
6450
|
+
return { ...kind ? { kind } : {}, ...url ? { url } : {} };
|
|
6451
|
+
}
|
|
6494
6452
|
async function cancelStack(args, json, table, io) {
|
|
6495
6453
|
const common = commonFlags(args);
|
|
6496
6454
|
if (common.missing) return usage(`${common.missing} takes a value`, table, io);
|
|
@@ -6520,7 +6478,8 @@ async function cancelStack(args, json, table, io) {
|
|
|
6520
6478
|
stack: stack.stack,
|
|
6521
6479
|
lockId: lockId.lockId,
|
|
6522
6480
|
project: stack.project,
|
|
6523
|
-
...stack.engine ? { engine: stack.engine } : {}
|
|
6481
|
+
...stack.engine ? { engine: stack.engine } : {},
|
|
6482
|
+
forge: cancelForge(read.config, dirname11(read.path))
|
|
6524
6483
|
});
|
|
6525
6484
|
return reportOutcome("cancel", outcome, stack.stack, rules, (r) => STACK_OP_WORDS.cancelled(stack.key, lockId.lockId, r.lockChecked), json, table, io);
|
|
6526
6485
|
});
|
|
@@ -6592,11 +6551,11 @@ async function moveOntoPlunge(engine3, args, json, table, io) {
|
|
|
6592
6551
|
});
|
|
6593
6552
|
}
|
|
6594
6553
|
async function rollbackStack(args, json, table, io) {
|
|
6595
|
-
const
|
|
6596
|
-
const env = takeFlag(
|
|
6554
|
+
const release = takeFlag(args, "--release");
|
|
6555
|
+
const env = takeFlag(release.rest, "--env");
|
|
6597
6556
|
const digest = takeFlag(env.rest, "--digest");
|
|
6598
6557
|
const actor = takeFlag(digest.rest, "--actor");
|
|
6599
|
-
for (const [flag, f] of [["--release",
|
|
6558
|
+
for (const [flag, f] of [["--release", release], ["--env", env], ["--digest", digest], ["--actor", actor]]) {
|
|
6600
6559
|
if (f.missing) return usage(`${flag} takes a value`, table, io);
|
|
6601
6560
|
}
|
|
6602
6561
|
const switches = actor.rest.filter((a) => a === "--dry-run" || a === "--events-json");
|
|
@@ -6606,11 +6565,11 @@ async function rollbackStack(args, json, table, io) {
|
|
|
6606
6565
|
const named = positionals(rest);
|
|
6607
6566
|
if (named.length > 1) return usage(`rollback takes one stack: ${STACK_OP_WORDS.usage.rollback}`, table, io);
|
|
6608
6567
|
let argv;
|
|
6609
|
-
if (
|
|
6568
|
+
if (release.value !== void 0) {
|
|
6610
6569
|
if (named.length) return usage(STACK_OP_WORDS.releaseIsEnvWide(named[0], environmentOf(named[0])), table, io);
|
|
6611
6570
|
if (digest.value !== void 0) return usage("--release takes no --digest: the release names what each stack goes back to", table, io);
|
|
6612
6571
|
if (!env.value) return usage(`rollback --release needs --env <env>, the environment whose stacks go back: ${STACK_OP_WORDS.usage.rollback}`, table, io);
|
|
6613
|
-
argv = ["components", "rollback", env.value, "--release",
|
|
6572
|
+
argv = ["components", "rollback", env.value, "--release", release.value];
|
|
6614
6573
|
} else {
|
|
6615
6574
|
if (named.length !== 1) return usage(`rollback takes a stack, or --release <name> --env <env>: ${STACK_OP_WORDS.usage.rollback}`, table, io);
|
|
6616
6575
|
const read = readConfig(io.cwd);
|
|
@@ -7255,11 +7214,11 @@ async function upgrade(args, json, table, io) {
|
|
|
7255
7214
|
const configFile = findConfig(io.cwd);
|
|
7256
7215
|
const configDir = configFile ? dirname14(configFile) : pkgDir;
|
|
7257
7216
|
const rel = (f) => relative2(io.cwd, f) || f;
|
|
7258
|
-
const
|
|
7259
|
-
if ("error" in
|
|
7260
|
-
const chant = opts.chant ??
|
|
7217
|
+
const release = await viewRelease(exec, pkgDir, io.env, opts.version);
|
|
7218
|
+
if ("error" in release) return fail(release.error);
|
|
7219
|
+
const chant = opts.chant ?? release.chant;
|
|
7261
7220
|
const pkgText = readFileSync16(pkgFile, "utf8");
|
|
7262
|
-
const pkgMove = movePackagePins(pkgText, rel(pkgFile),
|
|
7221
|
+
const pkgMove = movePackagePins(pkgText, rel(pkgFile), release.version, chant);
|
|
7263
7222
|
const declFile = join19(configDir, "chant.workspace.json");
|
|
7264
7223
|
const declText = existsSync15(declFile) ? readFileSync16(declFile, "utf8") : void 0;
|
|
7265
7224
|
let declMove;
|
|
@@ -7274,7 +7233,7 @@ async function upgrade(args, json, table, io) {
|
|
|
7274
7233
|
if (existsSync15(join19(configDir, "chant.workspace.jsonc"))) notes.push(`chant.workspace.jsonc is yours: move its @intentius/chant* pins to ${chant} by hand`);
|
|
7275
7234
|
if (configFile) {
|
|
7276
7235
|
try {
|
|
7277
|
-
const note = imageNote(loadConfig(configFile).doc,
|
|
7236
|
+
const note = imageNote(loadConfig(configFile).doc, release.version);
|
|
7278
7237
|
if (note) notes.push(note);
|
|
7279
7238
|
} catch {
|
|
7280
7239
|
}
|
|
@@ -7287,11 +7246,11 @@ async function upgrade(args, json, table, io) {
|
|
|
7287
7246
|
install: { cwd: rel(pkgDir) || ".", command: "npm install --no-audit --no-fund" },
|
|
7288
7247
|
ci: { cwd: rel(configDir) || ".", command: `npx ${ciArgs.join(" ")}` }
|
|
7289
7248
|
};
|
|
7290
|
-
const envelope = { command: "upgrade", plywood:
|
|
7249
|
+
const envelope = { command: "upgrade", plywood: release.version, chant, moves, notes, dryRun: opts.dryRun };
|
|
7291
7250
|
const say = (line) => {
|
|
7292
7251
|
if (!json) io.err(line);
|
|
7293
7252
|
};
|
|
7294
|
-
say(`${PRODUCT} upgrade: ${PLYWOOD_PACKAGE} ${
|
|
7253
|
+
say(`${PRODUCT} upgrade: ${PLYWOOD_PACKAGE} ${release.version}, ${CHANT_PACKAGE} ${chant}`);
|
|
7295
7254
|
for (const m of moves) say(` ${m.file} ${m.name} ${m.from} -> ${m.to}`);
|
|
7296
7255
|
if (moves.length === 0) say(" every pin is already there");
|
|
7297
7256
|
if (opts.dryRun) {
|
|
@@ -7310,14 +7269,14 @@ async function upgrade(args, json, table, io) {
|
|
|
7310
7269
|
return table.done;
|
|
7311
7270
|
}
|
|
7312
7271
|
say(` $ ${steps.install.command}`);
|
|
7313
|
-
const
|
|
7314
|
-
if (
|
|
7272
|
+
const installed = await exec("npm", ["install", "--no-audit", "--no-fund"], { cwd: pkgDir, env: io.env });
|
|
7273
|
+
if (installed.code !== 0) {
|
|
7315
7274
|
for (const n of notes) say(` note: ${n}`);
|
|
7316
|
-
return fail(`npm install failed (exit ${
|
|
7317
|
-
${
|
|
7275
|
+
return fail(`npm install failed (exit ${installed.code}); package.json is moved, so fix the install and run '${steps.ci.command}'
|
|
7276
|
+
${installed.stderr.trim()}`);
|
|
7318
7277
|
}
|
|
7319
7278
|
const got = installedVersion(pkgDir);
|
|
7320
|
-
if (got !== void 0 && got !==
|
|
7279
|
+
if (got !== void 0 && got !== release.version) notes.push(`node_modules holds ${PLYWOOD_PACKAGE} ${got}, not ${release.version}; check the install`);
|
|
7321
7280
|
let ci2;
|
|
7322
7281
|
if (configFile) {
|
|
7323
7282
|
say(` $ ${steps.ci.command}`);
|
|
@@ -7359,7 +7318,7 @@ var execChild = (bin, args, opts) => new Promise((done) => {
|
|
|
7359
7318
|
|
|
7360
7319
|
// src/cli.ts
|
|
7361
7320
|
init_words();
|
|
7362
|
-
var VERSION3 = true ? "0.3.
|
|
7321
|
+
var VERSION3 = true ? "0.3.8" : "0.0.0-dev";
|
|
7363
7322
|
var stdio = {
|
|
7364
7323
|
out: (s) => process.stdout.write(s + "\n"),
|
|
7365
7324
|
err: (s) => process.stderr.write(s + "\n")
|
|
@@ -7404,7 +7363,7 @@ function helpText() {
|
|
|
7404
7363
|
" override <stack> --rule <id>... --reason <text> override a policy denial, spent once its update runs",
|
|
7405
7364
|
"",
|
|
7406
7365
|
"Stack operations (each waits for a second person's approval):",
|
|
7407
|
-
" export <stack> --out <file>
|
|
7366
|
+
" export <stack> --out <file> [--version <v>] the stack's export, or one version of it, to a file on your machine",
|
|
7408
7367
|
" cancel <stack> [--lock <id>] release a stuck stack lock; on file:// it reads the id",
|
|
7409
7368
|
" move <urn>... --from <stack> --to <stack> move resources; both stacks preview no change",
|
|
7410
7369
|
" move <stack> --onto plunge adopt the stacks onto plunge (plywood.adopt.op.ts)",
|
|
@@ -7556,6 +7515,6 @@ function config(args, table, emit) {
|
|
|
7556
7515
|
}
|
|
7557
7516
|
|
|
7558
7517
|
// src/bin.ts
|
|
7559
|
-
|
|
7518
|
+
installChantResolveFallback(import.meta.url);
|
|
7560
7519
|
process.exitCode = await main(process.argv.slice(2));
|
|
7561
7520
|
//# sourceMappingURL=plywood.mjs.map
|