@intentius/chant-lexicon-pulumi 0.124.57 → 0.124.58

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 CHANGED
@@ -39,16 +39,19 @@ A program that reads another stack through `StackReference` orders its stack aft
39
39
 
40
40
  ## Gated stack operations
41
41
 
42
- `src/stack-ops.ts` ([#4578](https://github.com/INTENTIUS/chant/issues/4578)) runs three stack operations, each only after its gated request (#4551) is approved on the workspace ledger by someone other than the requester, under the op `pulumi-stack-ops`. The first call records the request and returns the `chant approve` command; the same call after the approval spends it, runs once, and records how it ended.
42
+ `src/stack-ops.ts` ([#4578](https://github.com/INTENTIUS/chant/issues/4578)) runs stack operations, each only after its gated request (#4551) is approved on the workspace ledger by someone other than the requester, under the op `pulumi-stack-ops`. The first call records the request and returns the `chant approve` command; the same call after the approval spends it, runs once, and records how it ended.
43
43
 
44
44
  | Function | Request | What runs after approval |
45
45
  |---|---|---|
46
46
  | `gatedStackExport` | `stack-export` of `<stack>` | `pulumi stack export --stack <s>` (or plunge's), written mode 600 to a file outside every git working tree; refused in CI |
47
47
  | `gatedStackCancel` | `stack-cancel` of `<stack>` with its lock id | `pulumi cancel --yes --stack <s>`, once the lock is still the approved one (read from a `file://` backend's lock directory); refused on plunge, which has no stack lock |
48
48
  | `gatedStackMove` | `stack-move` of `move:<source>:<dest>` with the URNs | `pulumi state move --source <s> --dest <d> --yes <urn>...`, or `plunge stack move --stack <s> --dest <d> <urn>... --json --yes` after plunge's plan; both stacks preview first, and the move runs only when the previews change nothing but the moved resources |
49
+ | `gatedStackAction` | `stack-action` of `action:<stack>:<name>` with the URN and the preview's digest | `pulumi up --yes --target <urn> --replace <urn> --stack <s>` (or plunge's), once ([#4654](https://github.com/INTENTIUS/chant/issues/4654)); see below |
49
50
 
50
51
  `pulumiStackLockIds({ dir, stack })` ([#4615](https://github.com/INTENTIUS/chant/issues/4615)) reads the lock ids a stack holds now, the same read `gatedStackCancel` checks with, so a product can offer the id to approve. It answers `checked: false` on a backend other than `file://`, and no ids for a plunge stack.
51
52
 
53
+ A day-2 action on a stack is a `command:local:Command` or `command:remote:Command` resource whose replacement is the operation, such as a key rotation. `gatedStackAction({ dir, stack, urn, name, engine, ... })` refuses any other resource type with `action-type` before anything runs. Each call previews `preview --json --target <urn> --replace <urn> --stack <s>` and refuses with `action-scope`, naming the other URNs, when the preview changes anything besides that one replacement; nothing is asked for then. The request binds the stack, the URN and the preview's digest, so a preview that changed after the approval asks again. Once approved, the update runs, and the finished line keeps `result: applied` (or `failed`) with `detail.result` set to `ok` (or `failed`), the exit code and the last lines of output. The same call after it ran answers `spent`, unless it passes `anew`. A stack on plunge runs both through plunge.
54
+
52
55
  The writes go through `runGatedPulumi` and `runGatedPlunge` in `src/run.ts`, whose fixed forms (`PULUMI_GATED_COMMANDS`, `PLUNGE_GATED_COMMANDS`) are the only ones allowed.
53
56
 
54
57
  ## Drift: `chant workspace drift`
package/dist/index.d.ts CHANGED
@@ -16,7 +16,7 @@ export { pulumiEngineEvents, pulumiStreamPreview, pulumiAppliedSteps, pulumiPoli
16
16
  export { readPulumiDrift, type PulumiDriftRunners } from "./drift-read.js";
17
17
  export { pulumiAuditCatalog } from "./lint/audit-catalog.js";
18
18
  export { PulumiWatchOp, type PulumiWatchOpConfig, type PulumiWatchOpResources } from "./composites/pulumi-watch-op.js";
19
- export { gatedStackExport, gatedStackCancel, gatedStackMove, moveLeftovers, diyStackLockIds, pulumiStackLockIds, type PulumiStackLocks, PULUMI_STACK_OPS_OP, type PulumiStackOpOutcome, type PulumiStackOpCommon, type PulumiStackOpKind } from "./stack-ops.js";
19
+ export { gatedStackExport, gatedStackCancel, gatedStackMove, gatedStackAction, checkStackActionUrn, stackActionScope, stackActionSubject, PulumiStackActionRefused, PULUMI_ACTION_TYPE_PREFIX, type PulumiStackActionOptions, type PulumiStackActionResult, type PulumiStackActionRefusal, moveLeftovers, diyStackLockIds, pulumiStackLockIds, type PulumiStackLocks, PULUMI_STACK_OPS_OP, type PulumiStackOpOutcome, type PulumiStackOpCommon, type PulumiStackOpKind } from "./stack-ops.js";
20
20
  export { initReviewStack, removeReviewStack, parseReviewStackLocation, prNumberOf, pulumiTemplateStack, reviewConfigSource, reviewStackFor, reviewStackLocation, reviewStackTeardown, REVIEW_STACK_TEARDOWN, type ReviewStackOptions, type ReviewStackOutcome, type ReviewStackTeardown, type ReviewStackTeardownOptions, } from "./review-stack.js";
21
21
  export { pulumiStackReferences, parsePulumiStackName, stackReferenceOwner, PULUMI_STACK_REFERENCE_TYPE, type PulumiStackReference } from "./stack-references.js";
22
22
  export { PulumiAdoptOp, type PulumiAdoptOpConfig, type PulumiAdoptOpResources, type PulumiAdoptStack } from "./composites/pulumi-adopt-op.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ export { readPulumiDrift } from "./drift-read.js";
18
18
  export { pulumiAuditCatalog } from "./lint/audit-catalog.js";
19
19
  // The Preview/Drift watch composite and its step builders (#4494).
20
20
  export { PulumiWatchOp } from "./composites/pulumi-watch-op.js";
21
- export { gatedStackExport, gatedStackCancel, gatedStackMove, moveLeftovers, diyStackLockIds, pulumiStackLockIds, PULUMI_STACK_OPS_OP } from "./stack-ops.js";
21
+ export { gatedStackExport, gatedStackCancel, gatedStackMove, gatedStackAction, checkStackActionUrn, stackActionScope, stackActionSubject, PulumiStackActionRefused, PULUMI_ACTION_TYPE_PREFIX, moveLeftovers, diyStackLockIds, pulumiStackLockIds, PULUMI_STACK_OPS_OP } from "./stack-ops.js";
22
22
  // Review stacks (INTENTIUS/plywood#15): pr-<n> stacks only.
23
23
  export { initReviewStack, removeReviewStack, parseReviewStackLocation, prNumberOf, pulumiTemplateStack, reviewConfigSource, reviewStackFor, reviewStackLocation, reviewStackTeardown, REVIEW_STACK_TEARDOWN, } from "./review-stack.js";
24
24
  export { pulumiStackReferences, parsePulumiStackName, stackReferenceOwner, PULUMI_STACK_REFERENCE_TYPE } from "./stack-references.js";
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "bd4f4c6d8c0e2162c083f09b24e92eaef8d4d6000a8a3754a20051c8d325e939",
4
+ "manifest.json": "d9975374a07d5764ef1b0b209c48fc2ec1b4ec4669808c6d6b1b6429d23a97d6",
5
5
  "meta.json": "ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356",
6
6
  "types/index.d.ts": "883bcdf6a960e21360e79df010ca2087719fffec60fc39541c5ecd32d661e328",
7
7
  "rules/member-dir-absolute.ts": "4805d5e247ad544f942e1b2c36fc15a5087ca0aacaf804183a269021bd60e276",
@@ -25,5 +25,5 @@
25
25
  "skills/chant-pulumi-stack-export.md": "aadd67f5e067de61dfda43d57492bca08bd22429e4baf314d6856a810dc0e8dc",
26
26
  "skills/chant-pulumi-plunge.md": "8a7f2f9f1124744dee9645905f141057ca01f5a88e34e1bc3858d69406d7ff3a"
27
27
  },
28
- "composite": "24002abc5170f337f1d8d38e0a8be8e4aa119a771e14ed829945a0ebbc198060"
28
+ "composite": "a4603282326e0ed1abf7f6c24fd702e35975fbbc9b1cdee72341269a8f9a9ac1"
29
29
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pulumi",
3
- "version": "0.124.57",
3
+ "version": "0.124.58",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "Pulumi"
6
6
  }
package/dist/run.d.ts CHANGED
@@ -167,9 +167,25 @@ export declare function runAdoptPlunge(run: PulumiRunner, args: string[], cwd: s
167
167
  *
168
168
  * - `pulumi cancel --yes --stack <stack>`: releases a stuck update's lock;
169
169
  * - `pulumi state move --source <stack> --dest <stack> --yes <urn>...`: moves
170
- * resources between stock stacks, never `--include-parents`.
170
+ * resources between stock stacks, never `--include-parents`;
171
+ * - `pulumi up --yes --target <urn> --replace <urn> --stack <stack>`: a
172
+ * day-2 action (#4654), the replacement of one `command:` resource, the
173
+ * same URN in both places. Its preview, `preview --json --target <urn>
174
+ * --replace <urn> --stack <stack>`, is a read and passes
175
+ * {@link runAllowedPulumi}. `up` takes `--replace` here only:
176
+ * {@link PULUMI_APPLY_COMMANDS} still refuses it.
171
177
  */
172
178
  export declare const PULUMI_GATED_COMMANDS: readonly string[];
179
+ /** A stack action's preview (#4654): `preview --json --target <urn> --replace <urn> --stack <stack>`, a read. */
180
+ export declare function pulumiActionPreviewArgs(opts: {
181
+ stack: string;
182
+ urn: string;
183
+ }): string[];
184
+ /** A stack action's update (#4654): `up --yes --target <urn> --replace <urn> --stack <stack>`, under `pulumi` or `plunge`. */
185
+ export declare function pulumiActionUpArgs(opts: {
186
+ stack: string;
187
+ urn: string;
188
+ }): string[];
173
189
  /**
174
190
  * plunge's resource move (INTENTIUS/plunge#56) in its two forms. The first
175
191
  * prints the plan and writes nothing, so it passes {@link runAllowedPlunge};
@@ -197,7 +213,7 @@ export declare function plungeMoveArgs(opts: {
197
213
  export declare function runGatedPulumi(run: PulumiRunner, args: string[], cwd: string, env?: Record<string, string>): Promise<{
198
214
  stdout: string;
199
215
  }>;
200
- /** Run the moving form of {@link PLUNGE_GATED_COMMANDS}, refusing any other argv. Only `./stack-ops.ts` calls it, after the request is spent. */
216
+ /** Run a writing form of {@link PLUNGE_GATED_COMMANDS}, the move or a stack action's update, refusing any other argv. Only `./stack-ops.ts` calls it, after the request is spent. */
201
217
  export declare function runGatedPlunge(run: PulumiRunner, args: string[], cwd: string, env?: Record<string, string>): Promise<{
202
218
  stdout: string;
203
219
  }>;
package/dist/run.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
package/dist/run.js CHANGED
@@ -438,12 +438,39 @@ export async function runAdoptPlunge(run, args, cwd, env) {
438
438
  *
439
439
  * - `pulumi cancel --yes --stack <stack>`: releases a stuck update's lock;
440
440
  * - `pulumi state move --source <stack> --dest <stack> --yes <urn>...`: moves
441
- * resources between stock stacks, never `--include-parents`.
441
+ * resources between stock stacks, never `--include-parents`;
442
+ * - `pulumi up --yes --target <urn> --replace <urn> --stack <stack>`: a
443
+ * day-2 action (#4654), the replacement of one `command:` resource, the
444
+ * same URN in both places. Its preview, `preview --json --target <urn>
445
+ * --replace <urn> --stack <stack>`, is a read and passes
446
+ * {@link runAllowedPulumi}. `up` takes `--replace` here only:
447
+ * {@link PULUMI_APPLY_COMMANDS} still refuses it.
442
448
  */
443
449
  export const PULUMI_GATED_COMMANDS = [
444
450
  "pulumi cancel --yes --stack <stack>",
445
451
  "pulumi state move --source <stack> --dest <stack> --yes <urn>...",
452
+ "pulumi up --yes --target <urn> --replace <urn> --stack <stack>",
446
453
  ];
454
+ /** A stack action's preview (#4654): `preview --json --target <urn> --replace <urn> --stack <stack>`, a read. */
455
+ export function pulumiActionPreviewArgs(opts) {
456
+ return ["preview", "--json", "--target", opts.urn, "--replace", opts.urn, "--stack", opts.stack];
457
+ }
458
+ /** A stack action's update (#4654): `up --yes --target <urn> --replace <urn> --stack <stack>`, under `pulumi` or `plunge`. */
459
+ export function pulumiActionUpArgs(opts) {
460
+ return ["up", "--yes", "--target", opts.urn, "--replace", opts.urn, "--stack", opts.stack];
461
+ }
462
+ /** Exactly {@link pulumiActionUpArgs}' form: one URN, targeted and replaced, on one stack. */
463
+ function allowedActionUp(args) {
464
+ return (args.length === 8 &&
465
+ args[0] === "up" &&
466
+ args[1] === "--yes" &&
467
+ args[2] === "--target" &&
468
+ isUrn(args[3]) &&
469
+ args[4] === "--replace" &&
470
+ args[5] === args[3] &&
471
+ args[6] === "--stack" &&
472
+ isValue(args[7]));
473
+ }
447
474
  /**
448
475
  * plunge's resource move (INTENTIUS/plunge#56) in its two forms. The first
449
476
  * prints the plan and writes nothing, so it passes {@link runAllowedPlunge};
@@ -452,6 +479,7 @@ export const PULUMI_GATED_COMMANDS = [
452
479
  export const PLUNGE_GATED_COMMANDS = [
453
480
  "plunge stack move --stack <stack> --dest <stack> <urn>... --json",
454
481
  "plunge stack move --stack <stack> --dest <stack> <urn>... --json --yes",
482
+ "plunge up --yes --target <urn> --replace <urn> --stack <stack>",
455
483
  ];
456
484
  const isUrn = (v) => typeof v === "string" && v.startsWith("urn:pulumi:");
457
485
  /** `cancel --yes --stack <stack>`. */
@@ -480,6 +508,8 @@ function allowedGatedPulumi(args) {
480
508
  const urns = args.slice(7);
481
509
  return args[2] === "--source" && isValue(args[3]) && args[4] === "--dest" && isValue(args[5]) && args[6] === "--yes" && urns.length > 0 && urns.every(isUrn);
482
510
  }
511
+ if (args[0] === "up")
512
+ return allowedActionUp(args);
483
513
  return false;
484
514
  }
485
515
  /** Run one of {@link PULUMI_GATED_COMMANDS}, refusing any other argv. Only `./stack-ops.ts` calls it, after the request is spent. */
@@ -489,9 +519,13 @@ export async function runGatedPulumi(run, args, cwd, env) {
489
519
  }
490
520
  return run(args, env ? { cwd, env } : { cwd });
491
521
  }
492
- /** Run the moving form of {@link PLUNGE_GATED_COMMANDS}, refusing any other argv. Only `./stack-ops.ts` calls it, after the request is spent. */
522
+ /** Run a writing form of {@link PLUNGE_GATED_COMMANDS}, the move or a stack action's update, refusing any other argv. Only `./stack-ops.ts` calls it, after the request is spent. */
493
523
  export async function runGatedPlunge(run, args, cwd, env) {
494
- if (!allowedPlungeMove(args, true)) {
524
+ if (args[0] === "up") {
525
+ if (!allowedActionUp(args))
526
+ throw new Error(`chant runs a plunge stack action only as ${PLUNGE_GATED_COMMANDS[2]}, not plunge ${args.join(" ")}`);
527
+ }
528
+ else if (!allowedPlungeMove(args, true)) {
495
529
  throw new Error(`chant moves plunge resources only as ${PLUNGE_GATED_COMMANDS[1]}, not plunge ${args.join(" ")}`);
496
530
  }
497
531
  return run(args, env ? { cwd, env } : { cwd });
@@ -4,7 +4,7 @@ import { type PulumiMemberEngine } from "./member-engine.js";
4
4
  import { type PulumiRunner } from "./run.js";
5
5
  /** The op the requests are recorded under, unless the caller names its own. */
6
6
  export declare const PULUMI_STACK_OPS_OP = "pulumi-stack-ops";
7
- export type PulumiStackOpKind = "stack-export" | "stack-cancel" | "stack-move";
7
+ export type PulumiStackOpKind = "stack-export" | "stack-cancel" | "stack-move" | "stack-action";
8
8
  /** chant's gated-request API (#4551), injectable for tests. */
9
9
  export interface GatedRequestApi {
10
10
  requestGate: typeof RequestGate;
@@ -172,4 +172,63 @@ export declare function moveLeftovers(urns: readonly string[], source: ChangeSet
172
172
  * for an empty or non-URN list or two engines.
173
173
  */
174
174
  export declare function gatedStackMove(opts: PulumiStackMoveOptions): Promise<PulumiStackOpOutcome<PulumiStackMoveResult>>;
175
+ /** Why a stack action was refused before anything was asked (#4654). */
176
+ export type PulumiStackActionRefusal = "action-urn" | "action-type" | "action-scope";
177
+ /** A stack action refused before its request: `code` says why, `urns` names what the preview changed besides the action, for `action-scope`. */
178
+ export declare class PulumiStackActionRefused extends Error {
179
+ readonly code: PulumiStackActionRefusal;
180
+ readonly urns: string[];
181
+ constructor(code: PulumiStackActionRefusal, message: string, urns?: string[]);
182
+ }
183
+ /** The resource types an action may name: the command provider's (`command:local:Command`, `command:remote:Command`). */
184
+ export declare const PULUMI_ACTION_TYPE_PREFIX = "command:";
185
+ /** The subject an action's request is asked and approved under: `action:<stack>:<name>`, or the URN in place of a name. */
186
+ export declare const stackActionSubject: (stack: string, nameOrUrn: string) => string;
187
+ export interface PulumiStackActionOptions extends PulumiStackOpCommon {
188
+ /** The stack's directory, where `Pulumi.yaml` is. */
189
+ dir: string;
190
+ cwd?: string;
191
+ /** The stack, as `--stack` takes it. */
192
+ stack: string;
193
+ engine?: PulumiMemberEngine;
194
+ /** The `command:` resource whose replacement is the action. */
195
+ urn: string;
196
+ /** The action's name in the caller's config, which the subject carries in place of the URN. */
197
+ name?: string;
198
+ /** Ask anew even when the same request applied already. Absent, such a call answers `spent`, so a retried job never runs the action twice. */
199
+ anew?: boolean;
200
+ }
201
+ export interface PulumiStackActionResult {
202
+ urn: string;
203
+ engine: PulumiMemberEngine;
204
+ /** The preview's digest the request bound. */
205
+ preview: string;
206
+ /** The update's exit code: 0, since a failed update ends `failed`. */
207
+ exit: number;
208
+ /** The update's last lines of output. */
209
+ tail: string;
210
+ }
211
+ /**
212
+ * Check an action's URN before anything runs: a Pulumi URN on `stack` whose
213
+ * type is a command provider's. Throws {@link PulumiStackActionRefused}.
214
+ */
215
+ export declare function checkStackActionUrn(stack: string, urn: string): void;
216
+ /**
217
+ * What a preview changes besides the action's replacement (#4654): each URN
218
+ * with a changing step other than `urn`, and `urn` itself when it changes
219
+ * other than by replacement or is not replaced at all. Empty when the
220
+ * preview replaces `urn` and nothing else.
221
+ */
222
+ export declare function stackActionScope(urn: string, preview: unknown): {
223
+ others: string[];
224
+ problem?: string;
225
+ };
226
+ /**
227
+ * A day-2 action on a stack (#4654): the replacement of one `command:`
228
+ * resource, previewed, asked for under a gated request, and run once a
229
+ * second identity approves. Refused before anything runs for a URN that is
230
+ * not a command resource's (`action-type`), and before anything is asked for
231
+ * a preview that changes more than that replacement (`action-scope`).
232
+ */
233
+ export declare function gatedStackAction(opts: PulumiStackActionOptions): Promise<PulumiStackOpOutcome<PulumiStackActionResult>>;
175
234
  //# sourceMappingURL=stack-ops.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stack-ops.d.ts","sourceRoot":"","sources":["../src/stack-ops.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stack-ops.d.ts","sourceRoot":"","sources":["../src/stack-ops.ts"],"names":[],"mappings":"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"}
package/dist/stack-ops.js CHANGED
@@ -16,6 +16,7 @@
16
16
  * | export | `stack-export` | `<stack>` | `stack`, `engine`, `deliver: requester` |
17
17
  * | cancel | `stack-cancel` | `<stack>` | `stack`, `lockId` |
18
18
  * | move | `stack-move` | `move:<source>:<dest>` | `source`, `dest`, `engine`, `urns` (sorted, comma-joined) |
19
+ * | action | `stack-action` | `action:<stack>:<name>` (the URN without a name) | `stack`, `urn`, `engine`, `preview` (the preview's digest), `name` when given |
19
20
  *
20
21
  * The first call records the pending fact and answers `waiting` with the
21
22
  * `chant approve` command. A later call with the same request reuses the
@@ -47,17 +48,30 @@
47
48
  * again afterwards; a change left on either finishes the request `failed`
48
49
  * with the changes named.
49
50
  *
51
+ * - action (#4654): a day-2 action is a `command:` resource
52
+ * (`command:local:Command`, `command:remote:Command`) whose replacement is
53
+ * the operation. Its type is checked before anything runs (`action-type`).
54
+ * Each call previews `preview --json --target <urn> --replace <urn>`, and
55
+ * refuses (`action-scope`) unless the preview changes that URN alone, by
56
+ * a replacement. The request binds the preview's digest, so a preview
57
+ * that moved after the approval asks again. Once approved, `up --yes
58
+ * --target <urn> --replace <urn> --stack <s>` under the stack's engine,
59
+ * and the finished line keeps chant's `applied | failed` with
60
+ * `detail.result` `ok` or `failed`, the exit code and the output's last
61
+ * lines. A request that applied answers `spent` to the same call, unless
62
+ * the caller asks `anew`.
63
+ *
50
64
  * The writes go through `runGatedPulumi` and `runGatedPlunge`
51
65
  * (`./run.ts`), whose fixed forms are the whole of what may run.
52
66
  */
53
67
  import { createHash } from "node:crypto";
54
68
  import { existsSync, readdirSync, writeFileSync } from "node:fs";
55
69
  import { dirname, isAbsolute, join, resolve } from "node:path";
56
- import { pulumiNodeKey } from "@intentius/chant/pulumi-urn";
70
+ import { parsePulumiUrn, pulumiNodeKey } from "@intentius/chant/pulumi-urn";
57
71
  import { pulumiStackBackend } from "./adopt.js";
58
- import { pulumiExportResources } from "./change-set.js";
72
+ import { pulumiExportResources, pulumiPreviewDigest, pulumiSteps, PULUMI_UNCHANGED_OPS } from "./change-set.js";
59
73
  import { pulumiMemberEngine } from "./member-engine.js";
60
- import { defaultPlungeRunner, defaultPulumiRunner, plungeMoveArgs, previewPulumiMemberRead, pulumiCancelArgs, pulumiRunFailure, pulumiStateMoveArgs, runAllowedPlunge, runAllowedPulumi, runGatedPlunge, runGatedPulumi, } from "./run.js";
74
+ import { defaultPlungeRunner, defaultPulumiRunner, plungeMoveArgs, previewPulumiMemberRead, pulumiActionPreviewArgs, pulumiActionUpArgs, pulumiCancelArgs, pulumiRunFailure, pulumiStateMoveArgs, runAllowedPlunge, runAllowedPulumi, runGatedPlunge, runGatedPulumi, } from "./run.js";
61
75
  import { isObject } from "./values.js";
62
76
  /** The op the requests are recorded under, unless the caller names its own. */
63
77
  export const PULUMI_STACK_OPS_OP = "pulumi-stack-ops";
@@ -80,12 +94,13 @@ const sameFields = (a, b) => Object.keys(a).length === Object.keys(b).length &&
80
94
  * every operation. `precheck` runs after the approval and before the request
81
95
  * is spent, so a refusal there leaves the approval unspent.
82
96
  */
83
- async function askThenAct(common, kind, subject, fields, description, act, precheck) {
97
+ async function askThenAct(common, kind, subject, fields, description, act, precheck, options = {}) {
84
98
  const api = await gatedApi(common.gated);
85
99
  const op = common.op ?? PULUMI_STACK_OPS_OP;
86
100
  const cwd = common.ledgerCwd ?? process.cwd();
101
+ // With reuseApplied (#4654), a request with these fields that applied already is asked again as itself, so it answers `spent`.
87
102
  const standing = (await api.readGatedRequests(op, { cwd }).catch(() => []))
88
- .filter((r) => !r.applied && r.request.kind === kind && r.request.subject === subject && r.request.by === common.by && sameFields(r.request.fields, fields))
103
+ .filter((r) => (options.reuseApplied || !r.applied) && r.request.kind === kind && r.request.subject === subject && r.request.by === common.by && sameFields(r.request.fields, fields))
89
104
  .at(-1);
90
105
  const request = { kind, subject, by: common.by, at: standing?.request.at ?? common.now ?? new Date().toISOString(), fields };
91
106
  const asked = await api.requestGate({
@@ -398,3 +413,121 @@ export async function gatedStackMove(opts) {
398
413
  }
399
414
  });
400
415
  }
416
+ /** A stack action refused before its request: `code` says why, `urns` names what the preview changed besides the action, for `action-scope`. */
417
+ export class PulumiStackActionRefused extends Error {
418
+ constructor(code, message, urns = []) {
419
+ super(`${code}: ${message}`);
420
+ this.name = "PulumiStackActionRefused";
421
+ this.code = code;
422
+ this.urns = urns;
423
+ }
424
+ }
425
+ /** The resource types an action may name: the command provider's (`command:local:Command`, `command:remote:Command`). */
426
+ export const PULUMI_ACTION_TYPE_PREFIX = "command:";
427
+ /** An action's name as a caller's config gives it, for the request's subject. */
428
+ const ACTION_NAME = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,99}$/;
429
+ /** The subject an action's request is asked and approved under: `action:<stack>:<name>`, or the URN in place of a name. */
430
+ export const stackActionSubject = (stack, nameOrUrn) => `action:${stack}:${nameOrUrn}`;
431
+ /** The last `n` lines of `text`, trimmed. */
432
+ const lastLines = (text, n) => (text ?? "").trim().split("\n").slice(-n).join("\n");
433
+ /**
434
+ * Check an action's URN before anything runs: a Pulumi URN on `stack` whose
435
+ * type is a command provider's. Throws {@link PulumiStackActionRefused}.
436
+ */
437
+ export function checkStackActionUrn(stack, urn) {
438
+ const parsed = parsePulumiUrn(urn);
439
+ if (!parsed)
440
+ throw new PulumiStackActionRefused("action-urn", `${JSON.stringify(urn)} is not a resource URN (urn:pulumi:<stack>::<project>::<type>::<name>)`);
441
+ const stackName = stack.split("/").at(-1);
442
+ if (parsed.stack !== stackName)
443
+ throw new PulumiStackActionRefused("action-urn", `${urn} names stack ${parsed.stack}, and the action runs on ${stack}`);
444
+ if (!parsed.type.startsWith(PULUMI_ACTION_TYPE_PREFIX)) {
445
+ throw new PulumiStackActionRefused("action-type", `${urn} is a ${parsed.type}, and an action is a command resource (command:local:Command or command:remote:Command), whose replacement runs it; replacing any other resource is an update`);
446
+ }
447
+ }
448
+ /**
449
+ * What a preview changes besides the action's replacement (#4654): each URN
450
+ * with a changing step other than `urn`, and `urn` itself when it changes
451
+ * other than by replacement or is not replaced at all. Empty when the
452
+ * preview replaces `urn` and nothing else.
453
+ */
454
+ export function stackActionScope(urn, preview) {
455
+ const steps = pulumiSteps(preview) ?? [];
456
+ const others = new Set();
457
+ let replaced = false;
458
+ let problem;
459
+ for (const s of steps) {
460
+ const op = s.op ?? "";
461
+ if (PULUMI_UNCHANGED_OPS.has(op))
462
+ continue;
463
+ if (s.urn !== urn) {
464
+ others.add(s.urn);
465
+ continue;
466
+ }
467
+ if (op === "replace" || op === "create-replacement" || op === "delete-replaced")
468
+ replaced = true;
469
+ else
470
+ problem = `the preview shows ${op} of ${urn}, not its replacement`;
471
+ }
472
+ if (!problem && !replaced)
473
+ problem = `the preview does not replace ${urn}`;
474
+ return { others: [...others].sort(), ...(problem ? { problem } : {}) };
475
+ }
476
+ /**
477
+ * A day-2 action on a stack (#4654): the replacement of one `command:`
478
+ * resource, previewed, asked for under a gated request, and run once a
479
+ * second identity approves. Refused before anything runs for a URN that is
480
+ * not a command resource's (`action-type`), and before anything is asked for
481
+ * a preview that changes more than that replacement (`action-scope`).
482
+ */
483
+ export async function gatedStackAction(opts) {
484
+ checkStackActionUrn(opts.stack, opts.urn);
485
+ if (opts.name !== undefined && !ACTION_NAME.test(opts.name))
486
+ throw new Error(`an action's name is letters, digits, "_", "." and "-", not ${JSON.stringify(opts.name)}`);
487
+ const env = opts.env ?? process.env;
488
+ const dir = stackDirOf(opts.dir, opts.cwd);
489
+ const engine = engineOf(dir, opts.engine, env);
490
+ const target = { stack: opts.stack, urn: opts.urn };
491
+ const previewArgs = pulumiActionPreviewArgs(target);
492
+ let stdout;
493
+ try {
494
+ ({ stdout } = engine === "plunge" ? await runAllowedPlunge(opts.plunge ?? defaultPlungeRunner, previewArgs, dir) : await runAllowedPulumi(opts.run ?? defaultPulumiRunner, previewArgs, dir));
495
+ }
496
+ catch (err) {
497
+ throw new Error(`${engine} ${previewArgs.join(" ")} failed: ${why(err, engine)}`);
498
+ }
499
+ let preview;
500
+ try {
501
+ preview = JSON.parse(stdout);
502
+ }
503
+ catch {
504
+ throw new Error(`${engine} ${previewArgs.join(" ")} printed no JSON`);
505
+ }
506
+ const scope = stackActionScope(opts.urn, preview);
507
+ if (scope.others.length > 0) {
508
+ throw new PulumiStackActionRefused("action-scope", `the preview of ${opts.stack} replacing ${opts.urn} also changes ${scope.others.join(", ")}, and an action replaces its command resource alone; nothing was asked for`, scope.others);
509
+ }
510
+ if (scope.problem)
511
+ throw new PulumiStackActionRefused("action-scope", `${scope.problem}; nothing was asked for`);
512
+ const digest = pulumiPreviewDigest(preview, { changedOnly: true });
513
+ const subject = stackActionSubject(opts.stack, opts.name ?? opts.urn);
514
+ const fields = { stack: opts.stack, urn: opts.urn, engine, preview: digest, ...(opts.name ? { name: opts.name } : {}) };
515
+ return askThenAct(opts, "stack-action", subject, fields, `action ${opts.name ?? opts.urn} on stack ${opts.stack}: replace ${opts.urn}, requested by ${opts.by}`, async () => {
516
+ const args = pulumiActionUpArgs(target);
517
+ try {
518
+ const done = engine === "plunge" ? await runGatedPlunge(opts.plunge ?? defaultPlungeRunner, args, dir) : await runGatedPulumi(opts.run ?? defaultPulumiRunner, args, dir);
519
+ const tail = lastLines(done.stdout, 10);
520
+ return { result: { urn: opts.urn, engine, preview: digest, exit: 0, tail }, detail: { result: "ok", exit: 0, tail: tail.slice(-400), urn: opts.urn } };
521
+ }
522
+ catch (err) {
523
+ const f = pulumiRunFailure(err);
524
+ const exit = f.exitCode ?? 1;
525
+ const tail = lastLines(`${f.stdout ?? ""}\n${f.stderr ?? ""}`, 10);
526
+ return {
527
+ result: { urn: opts.urn, engine, preview: digest, exit, tail },
528
+ detail: { result: "failed", exit, tail: tail.slice(-400), urn: opts.urn },
529
+ failed: `${engine} ${args.join(" ")} failed: ${why(err, engine)}`,
530
+ };
531
+ }
532
+ }, undefined, { reuseApplied: !opts.anew });
533
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-pulumi",
3
- "version": "0.124.57",
3
+ "version": "0.124.58",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src/",
@@ -54,7 +54,7 @@
54
54
  "access": "public"
55
55
  },
56
56
  "peerDependencies": {
57
- "@intentius/chant": "^0.124.57",
57
+ "@intentius/chant": "^0.124.58",
58
58
  "zod": "^4.3.6"
59
59
  },
60
60
  "description": "pulumi lexicon for chant: a Pulumi program as a workspace member, its preview in the change set and its refresh as drift records",
package/src/index.ts CHANGED
@@ -108,7 +108,7 @@ export { readPulumiDrift, type PulumiDriftRunners } from "./drift-read";
108
108
  export { pulumiAuditCatalog } from "./lint/audit-catalog";
109
109
  // The Preview/Drift watch composite and its step builders (#4494).
110
110
  export { PulumiWatchOp, type PulumiWatchOpConfig, type PulumiWatchOpResources } from "./composites/pulumi-watch-op";
111
- export { gatedStackExport, gatedStackCancel, gatedStackMove, moveLeftovers, diyStackLockIds, pulumiStackLockIds, type PulumiStackLocks, PULUMI_STACK_OPS_OP, type PulumiStackOpOutcome, type PulumiStackOpCommon, type PulumiStackOpKind } from "./stack-ops";
111
+ export { gatedStackExport, gatedStackCancel, gatedStackMove, gatedStackAction, checkStackActionUrn, stackActionScope, stackActionSubject, PulumiStackActionRefused, PULUMI_ACTION_TYPE_PREFIX, type PulumiStackActionOptions, type PulumiStackActionResult, type PulumiStackActionRefusal, moveLeftovers, diyStackLockIds, pulumiStackLockIds, type PulumiStackLocks, PULUMI_STACK_OPS_OP, type PulumiStackOpOutcome, type PulumiStackOpCommon, type PulumiStackOpKind } from "./stack-ops";
112
112
  // Review stacks (INTENTIUS/plywood#15): pr-<n> stacks only.
113
113
  export {
114
114
  initReviewStack,
package/src/run.test.ts CHANGED
@@ -14,6 +14,8 @@ import {
14
14
  plungeAdoptArgs,
15
15
  plungeGraphArgs,
16
16
  plungeMoveArgs,
17
+ pulumiActionPreviewArgs,
18
+ pulumiActionUpArgs,
17
19
  pulumiCancelArgs,
18
20
  pulumiStateMoveArgs,
19
21
  runGatedPlunge,
@@ -93,6 +95,44 @@ describe("gated stack operations (#4578)", () => {
93
95
  });
94
96
  });
95
97
 
98
+ describe("a stack action's forms (#4654)", () => {
99
+ const u = "urn:pulumi:prod-us::shop::command:local:Command::rotate";
100
+ const other = "urn:pulumi:prod-us::shop::command:local:Command::flush";
101
+ test("the preview is a read; the update passes the gated entry points only", async () => {
102
+ const { run, calls } = fake(() => "{}");
103
+ await runAllowedPulumi(run, pulumiActionPreviewArgs({ stack: "prod-us", urn: u }), "/tmp");
104
+ await runAllowedPlunge(run, pulumiActionPreviewArgs({ stack: "prod-us", urn: u }), "/tmp");
105
+ await runGatedPulumi(run, pulumiActionUpArgs({ stack: "prod-us", urn: u }), "/tmp");
106
+ await runGatedPlunge(run, pulumiActionUpArgs({ stack: "prod-us", urn: u }), "/tmp");
107
+ const up = ["up", "--yes", "--target", u, "--replace", u, "--stack", "prod-us"];
108
+ expect(calls.map((c) => c.args)).toEqual([
109
+ ["preview", "--json", "--target", u, "--replace", u, "--stack", "prod-us"],
110
+ ["preview", "--json", "--target", u, "--replace", u, "--stack", "prod-us"],
111
+ up,
112
+ up,
113
+ ]);
114
+ });
115
+
116
+ test("no other update form gets through", async () => {
117
+ const { run, calls } = fake(() => "{}");
118
+ const up = pulumiActionUpArgs({ stack: "prod-us", urn: u });
119
+ await expect(runAllowedPulumi(run, up, "/tmp")).rejects.toThrow(/not pulumi up/);
120
+ await expect(runApplyPulumi(run, up, "/tmp")).rejects.toThrow();
121
+ for (const args of [
122
+ ["up", "--yes", "--target", u, "--replace", other, "--stack", "prod-us"],
123
+ ["up", "--yes", "--replace", u, "--stack", "prod-us"],
124
+ ["up", "--yes", "--target", u, "--replace", u, "--stack", "prod-us", "--skip-preview"],
125
+ ["up", "--target", u, "--replace", u, "--stack", "prod-us"],
126
+ ["up", "--yes", "--target", "rotate", "--replace", "rotate", "--stack", "prod-us"],
127
+ ["up", "--yes", "--target", u, "--replace", u, "--stack", "--json"],
128
+ ]) {
129
+ await expect(runGatedPulumi(run, args, "/tmp")).rejects.toThrow(/gated Pulumi stack operation only as/);
130
+ await expect(runGatedPlunge(run, args, "/tmp")).rejects.toThrow(/plunge stack action only as/);
131
+ }
132
+ expect(calls).toEqual([]);
133
+ });
134
+ });
135
+
96
136
  describe("plunge stack adopt (#4562)", () => {
97
137
  test("the preview form passes runAllowedPlunge, and only runAdoptPlunge runs --yes", async () => {
98
138
  const { run, calls } = fake(() => "{}");
package/src/run.ts CHANGED
@@ -492,13 +492,45 @@ export async function runAdoptPlunge(run: PulumiRunner, args: string[], cwd: str
492
492
  *
493
493
  * - `pulumi cancel --yes --stack <stack>`: releases a stuck update's lock;
494
494
  * - `pulumi state move --source <stack> --dest <stack> --yes <urn>...`: moves
495
- * resources between stock stacks, never `--include-parents`.
495
+ * resources between stock stacks, never `--include-parents`;
496
+ * - `pulumi up --yes --target <urn> --replace <urn> --stack <stack>`: a
497
+ * day-2 action (#4654), the replacement of one `command:` resource, the
498
+ * same URN in both places. Its preview, `preview --json --target <urn>
499
+ * --replace <urn> --stack <stack>`, is a read and passes
500
+ * {@link runAllowedPulumi}. `up` takes `--replace` here only:
501
+ * {@link PULUMI_APPLY_COMMANDS} still refuses it.
496
502
  */
497
503
  export const PULUMI_GATED_COMMANDS: readonly string[] = [
498
504
  "pulumi cancel --yes --stack <stack>",
499
505
  "pulumi state move --source <stack> --dest <stack> --yes <urn>...",
506
+ "pulumi up --yes --target <urn> --replace <urn> --stack <stack>",
500
507
  ];
501
508
 
509
+ /** A stack action's preview (#4654): `preview --json --target <urn> --replace <urn> --stack <stack>`, a read. */
510
+ export function pulumiActionPreviewArgs(opts: { stack: string; urn: string }): string[] {
511
+ return ["preview", "--json", "--target", opts.urn, "--replace", opts.urn, "--stack", opts.stack];
512
+ }
513
+
514
+ /** A stack action's update (#4654): `up --yes --target <urn> --replace <urn> --stack <stack>`, under `pulumi` or `plunge`. */
515
+ export function pulumiActionUpArgs(opts: { stack: string; urn: string }): string[] {
516
+ return ["up", "--yes", "--target", opts.urn, "--replace", opts.urn, "--stack", opts.stack];
517
+ }
518
+
519
+ /** Exactly {@link pulumiActionUpArgs}' form: one URN, targeted and replaced, on one stack. */
520
+ function allowedActionUp(args: readonly string[]): boolean {
521
+ return (
522
+ args.length === 8 &&
523
+ args[0] === "up" &&
524
+ args[1] === "--yes" &&
525
+ args[2] === "--target" &&
526
+ isUrn(args[3]) &&
527
+ args[4] === "--replace" &&
528
+ args[5] === args[3] &&
529
+ args[6] === "--stack" &&
530
+ isValue(args[7])
531
+ );
532
+ }
533
+
502
534
  /**
503
535
  * plunge's resource move (INTENTIUS/plunge#56) in its two forms. The first
504
536
  * prints the plan and writes nothing, so it passes {@link runAllowedPlunge};
@@ -507,6 +539,7 @@ export const PULUMI_GATED_COMMANDS: readonly string[] = [
507
539
  export const PLUNGE_GATED_COMMANDS: readonly string[] = [
508
540
  "plunge stack move --stack <stack> --dest <stack> <urn>... --json",
509
541
  "plunge stack move --stack <stack> --dest <stack> <urn>... --json --yes",
542
+ "plunge up --yes --target <urn> --replace <urn> --stack <stack>",
510
543
  ];
511
544
 
512
545
  const isUrn = (v: string | undefined): v is string => typeof v === "string" && v.startsWith("urn:pulumi:");
@@ -539,6 +572,7 @@ function allowedGatedPulumi(args: string[]): boolean {
539
572
  const urns = args.slice(7);
540
573
  return args[2] === "--source" && isValue(args[3]) && args[4] === "--dest" && isValue(args[5]) && args[6] === "--yes" && urns.length > 0 && urns.every(isUrn);
541
574
  }
575
+ if (args[0] === "up") return allowedActionUp(args);
542
576
  return false;
543
577
  }
544
578
 
@@ -550,9 +584,11 @@ export async function runGatedPulumi(run: PulumiRunner, args: string[], cwd: str
550
584
  return run(args, env ? { cwd, env } : { cwd });
551
585
  }
552
586
 
553
- /** Run the moving form of {@link PLUNGE_GATED_COMMANDS}, refusing any other argv. Only `./stack-ops.ts` calls it, after the request is spent. */
587
+ /** Run a writing form of {@link PLUNGE_GATED_COMMANDS}, the move or a stack action's update, refusing any other argv. Only `./stack-ops.ts` calls it, after the request is spent. */
554
588
  export async function runGatedPlunge(run: PulumiRunner, args: string[], cwd: string, env?: Record<string, string>): Promise<{ stdout: string }> {
555
- if (!allowedPlungeMove(args, true)) {
589
+ if (args[0] === "up") {
590
+ if (!allowedActionUp(args)) throw new Error(`chant runs a plunge stack action only as ${PLUNGE_GATED_COMMANDS[2]}, not plunge ${args.join(" ")}`);
591
+ } else if (!allowedPlungeMove(args, true)) {
556
592
  throw new Error(`chant moves plunge resources only as ${PLUNGE_GATED_COMMANDS[1]}, not plunge ${args.join(" ")}`);
557
593
  }
558
594
  return run(args, env ? { cwd, env } : { cwd });
@@ -16,8 +16,11 @@ import {
16
16
  diyStackLockIds,
17
17
  gatedStackCancel,
18
18
  gatedStackExport,
19
+ gatedStackAction,
19
20
  gatedStackMove,
20
21
  moveLeftovers,
22
+ PulumiStackActionRefused,
23
+ stackActionScope,
21
24
  pulumiStackLockIds,
22
25
  PULUMI_STACK_OPS_OP,
23
26
  type GatedRequestApi,
@@ -284,3 +287,112 @@ describe("the stack lock reader (chant#4615)", () => {
284
287
  expect(await pulumiStackLockIds({ dir, stack: "dev", engine: "plunge", env: {} })).toEqual({ checked: true, ids: [] });
285
288
  });
286
289
  });
290
+
291
+ describe("action (chant#4654)", () => {
292
+ const rotate = "urn:pulumi:prod-us::shop::command:local:Command::rotate";
293
+ const bucket = "urn:pulumi:prod-us::shop::aws:s3/bucket:Bucket::b";
294
+ const stackUrn = "urn:pulumi:prod-us::shop::pulumi:pulumi:Stack::shop-prod-us";
295
+ const replaceSteps = (urn: string) => [
296
+ { op: "create-replacement", urn, newState: { type: "command:local:Command", inputs: { create: "./rotate.sh" } } },
297
+ { op: "replace", urn, newState: { type: "command:local:Command", inputs: { create: "./rotate.sh" } } },
298
+ { op: "delete-replaced", urn, oldState: { type: "command:local:Command", inputs: { create: "./rotate.sh" } } },
299
+ ];
300
+ const doc = (steps: unknown[]) => JSON.stringify({ steps: [{ op: "same", urn: stackUrn }, ...steps] });
301
+ const previewArgs = ["preview", "--json", "--target", rotate, "--replace", rotate, "--stack", "prod-us"];
302
+ const upArgs = ["up", "--yes", "--target", rotate, "--replace", rotate, "--stack", "prod-us"];
303
+
304
+ test("the first call previews the replacement and answers waiting, with no update", async () => {
305
+ const l = ledger();
306
+ const pulumi = runner(() => doc(replaceSteps(rotate)));
307
+ const first = await gatedStackAction({ by: "alice", rules: { mode: "ledger" }, gated: l.api, env: {}, run: pulumi.run, dir: stackDir(), stack: "prod-us", engine: "pulumi", urn: rotate, name: "rotate" });
308
+ expect(first.status).toBe("waiting");
309
+ expect(first.digest).toMatch(/^sha256:/);
310
+ expect(pulumi.calls).toEqual([previewArgs]);
311
+ expect(pulumi.calls.some((c) => c[0] === "up")).toBe(false);
312
+ expect(first.request).toMatchObject({
313
+ kind: "stack-action",
314
+ subject: "action:prod-us:rotate",
315
+ fields: { stack: "prod-us", urn: rotate, engine: "pulumi", name: "rotate", preview: expect.stringMatching(/sha256:/) },
316
+ });
317
+ expect(l.spent.size).toBe(0);
318
+ });
319
+
320
+ test("a preview that replaces a second resource is refused action-scope, naming it, and asks for nothing", async () => {
321
+ const l = ledger();
322
+ const pulumi = runner(() => doc([...replaceSteps(rotate), ...replaceSteps(bucket)]));
323
+ const call = gatedStackAction({ by: "alice", rules: { mode: "ledger" }, gated: l.api, env: {}, run: pulumi.run, dir: stackDir(), stack: "prod-us", engine: "pulumi", urn: rotate });
324
+ await expect(call).rejects.toThrow(PulumiStackActionRefused);
325
+ await expect(call).rejects.toMatchObject({ code: "action-scope", urns: [bucket] });
326
+ await expect(call).rejects.toThrow(new RegExp(`^action-scope: .*${bucket.replace(/[$.]/g, "\\$&")}`));
327
+ expect(l.pending).toEqual([]);
328
+ expect(pulumi.calls.every((c) => c[0] === "preview")).toBe(true);
329
+ });
330
+
331
+ test("a preview that does not replace the URN, or updates it in place, is refused action-scope", () => {
332
+ expect(stackActionScope(rotate, JSON.parse(doc([])))).toEqual({ others: [], problem: `the preview does not replace ${rotate}` });
333
+ expect(stackActionScope(rotate, JSON.parse(doc([{ op: "update", urn: rotate }])))).toMatchObject({ problem: `the preview shows update of ${rotate}, not its replacement` });
334
+ expect(stackActionScope(rotate, JSON.parse(doc(replaceSteps(rotate))))).toEqual({ others: [] });
335
+ });
336
+
337
+ test("a URN of another type is refused action-type before any engine call", async () => {
338
+ const l = ledger();
339
+ const pulumi = runner(() => doc([]));
340
+ const common = { by: "alice", rules: { mode: "ledger" as const }, gated: l.api, env: {}, run: pulumi.run, dir: stackDir(), stack: "prod-us", engine: "pulumi" as const };
341
+ await expect(gatedStackAction({ ...common, urn: bucket })).rejects.toMatchObject({ code: "action-type" });
342
+ await expect(gatedStackAction({ ...common, urn: bucket })).rejects.toThrow(/aws:s3\/bucket:Bucket/);
343
+ await expect(gatedStackAction({ ...common, urn: "rotate" })).rejects.toMatchObject({ code: "action-urn" });
344
+ await expect(gatedStackAction({ ...common, urn: "urn:pulumi:dev::shop::command:local:Command::rotate" })).rejects.toMatchObject({ code: "action-urn" });
345
+ expect(pulumi.calls).toEqual([]);
346
+ expect(l.pending).toEqual([]);
347
+ });
348
+
349
+ test("after a second identity approves, the update runs once, applied and finished are written, and a repeat is spent", async () => {
350
+ const l = ledger();
351
+ const pulumi = runner((args) => (args[0] === "up" ? "Updating (prod-us)\n ++ command:local:Command rotate created replacement\nResources: 1 replaced" : doc(replaceSteps(rotate))));
352
+ const opts = { by: "alice", rules: { mode: "ledger" as const }, gated: l.api, env: {}, run: pulumi.run, dir: stackDir(), stack: "prod-us", engine: "pulumi" as const, urn: rotate, name: "rotate" };
353
+ const first = await gatedStackAction(opts);
354
+ l.approve(first.digest, "alice");
355
+ expect((await gatedStackAction(opts)).status).toBe("refused");
356
+ expect(pulumi.calls.filter((c) => c[0] === "up")).toEqual([]);
357
+
358
+ l.approve(first.digest, "bob");
359
+ const done = await gatedStackAction(opts);
360
+ expect(done.status).toBe("done");
361
+ expect(done.digest).toBe(first.digest);
362
+ if (done.status !== "done") throw new Error("not done");
363
+ expect(done.result).toMatchObject({ urn: rotate, engine: "pulumi", exit: 0, tail: expect.stringContaining("1 replaced") });
364
+ expect(pulumi.calls.filter((c) => c[0] === "up")).toEqual([upArgs]);
365
+ expect(l.spent.has(first.digest)).toBe(true);
366
+ expect(l.finished).toEqual([{ digest: first.digest, result: "applied", detail: expect.objectContaining({ result: "ok", exit: 0, urn: rotate }) }]);
367
+
368
+ const again = await gatedStackAction(opts);
369
+ expect(again.status).toBe("spent");
370
+ expect(again.digest).toBe(first.digest);
371
+ expect(pulumi.calls.filter((c) => c[0] === "up")).toHaveLength(1);
372
+ // Asked anew, the same action is a new request that waits for its own approval.
373
+ const anew = await gatedStackAction({ ...opts, anew: true, now: "2000-01-01T00:00:00.000Z" });
374
+ expect(anew.status).toBe("waiting");
375
+ expect(pulumi.calls.filter((c) => c[0] === "up")).toHaveLength(1);
376
+ });
377
+
378
+ test("a failed update finishes failed with the exit code and the output's tail", async () => {
379
+ const l = ledger();
380
+ const pulumi = runner((args) => (args[0] === "up" ? Object.assign(new Error("exit 255"), { code: 255, stdout: "error: rotate.sh exited 3" }) : doc(replaceSteps(rotate))));
381
+ const opts = { by: "alice", rules: { mode: "ledger" as const }, gated: l.api, env: {}, run: pulumi.run, dir: stackDir(), stack: "prod-us", engine: "pulumi" as const, urn: rotate };
382
+ l.approve((await gatedStackAction(opts)).digest, "bob");
383
+ const failed = await gatedStackAction(opts);
384
+ expect(failed.status).toBe("failed");
385
+ expect(l.finished[0]).toMatchObject({ result: "failed", detail: { result: "failed", exit: 255, tail: "error: rotate.sh exited 3", urn: rotate } });
386
+ });
387
+
388
+ test("a stack on plunge previews and updates under plunge", async () => {
389
+ const l = ledger();
390
+ const plunge = runner((args) => (args[0] === "up" ? "done" : doc(replaceSteps(rotate))));
391
+ const opts = { by: "alice", rules: { mode: "ledger" as const }, gated: l.api, env: {}, plunge: plunge.run, dir: stackDir(), stack: "prod-us", engine: "plunge" as const, urn: rotate };
392
+ const first = await gatedStackAction(opts);
393
+ expect(first.request.fields.engine).toBe("plunge");
394
+ l.approve(first.digest, "bob");
395
+ expect((await gatedStackAction(opts)).status).toBe("done");
396
+ expect(plunge.calls).toEqual([previewArgs, previewArgs, upArgs]);
397
+ });
398
+ });
package/src/stack-ops.ts CHANGED
@@ -16,6 +16,7 @@
16
16
  * | export | `stack-export` | `<stack>` | `stack`, `engine`, `deliver: requester` |
17
17
  * | cancel | `stack-cancel` | `<stack>` | `stack`, `lockId` |
18
18
  * | move | `stack-move` | `move:<source>:<dest>` | `source`, `dest`, `engine`, `urns` (sorted, comma-joined) |
19
+ * | action | `stack-action` | `action:<stack>:<name>` (the URN without a name) | `stack`, `urn`, `engine`, `preview` (the preview's digest), `name` when given |
19
20
  *
20
21
  * The first call records the pending fact and answers `waiting` with the
21
22
  * `chant approve` command. A later call with the same request reuses the
@@ -47,6 +48,19 @@
47
48
  * again afterwards; a change left on either finishes the request `failed`
48
49
  * with the changes named.
49
50
  *
51
+ * - action (#4654): a day-2 action is a `command:` resource
52
+ * (`command:local:Command`, `command:remote:Command`) whose replacement is
53
+ * the operation. Its type is checked before anything runs (`action-type`).
54
+ * Each call previews `preview --json --target <urn> --replace <urn>`, and
55
+ * refuses (`action-scope`) unless the preview changes that URN alone, by
56
+ * a replacement. The request binds the preview's digest, so a preview
57
+ * that moved after the approval asks again. Once approved, `up --yes
58
+ * --target <urn> --replace <urn> --stack <s>` under the stack's engine,
59
+ * and the finished line keeps chant's `applied | failed` with
60
+ * `detail.result` `ok` or `failed`, the exit code and the output's last
61
+ * lines. A request that applied answers `spent` to the same call, unless
62
+ * the caller asks `anew`.
63
+ *
50
64
  * The writes go through `runGatedPulumi` and `runGatedPlunge`
51
65
  * (`./run.ts`), whose fixed forms are the whole of what may run.
52
66
  */
@@ -63,15 +77,17 @@ import type {
63
77
  GatedRequestRules,
64
78
  RequestGateResult,
65
79
  } from "@intentius/chant/lifecycle/gated-request";
66
- import { pulumiNodeKey } from "@intentius/chant/pulumi-urn";
80
+ import { parsePulumiUrn, pulumiNodeKey } from "@intentius/chant/pulumi-urn";
67
81
  import { pulumiStackBackend } from "./adopt";
68
- import { pulumiExportResources } from "./change-set";
82
+ import { pulumiExportResources, pulumiPreviewDigest, pulumiSteps, PULUMI_UNCHANGED_OPS } from "./change-set";
69
83
  import { pulumiMemberEngine, type PulumiMemberEngine } from "./member-engine";
70
84
  import {
71
85
  defaultPlungeRunner,
72
86
  defaultPulumiRunner,
73
87
  plungeMoveArgs,
74
88
  previewPulumiMemberRead,
89
+ pulumiActionPreviewArgs,
90
+ pulumiActionUpArgs,
75
91
  pulumiCancelArgs,
76
92
  pulumiRunFailure,
77
93
  pulumiStateMoveArgs,
@@ -86,7 +102,7 @@ import { isObject } from "./values";
86
102
  /** The op the requests are recorded under, unless the caller names its own. */
87
103
  export const PULUMI_STACK_OPS_OP = "pulumi-stack-ops";
88
104
 
89
- export type PulumiStackOpKind = "stack-export" | "stack-cancel" | "stack-move";
105
+ export type PulumiStackOpKind = "stack-export" | "stack-cancel" | "stack-move" | "stack-action";
90
106
 
91
107
  /** chant's gated-request API (#4551), injectable for tests. */
92
108
  export interface GatedRequestApi {
@@ -152,12 +168,14 @@ async function askThenAct<T>(
152
168
  description: string,
153
169
  act: () => Promise<{ result: T; detail: Record<string, string | number | boolean>; failed?: string }>,
154
170
  precheck?: () => Promise<void>,
171
+ options: { reuseApplied?: boolean } = {},
155
172
  ): Promise<PulumiStackOpOutcome<T>> {
156
173
  const api = await gatedApi(common.gated);
157
174
  const op = common.op ?? PULUMI_STACK_OPS_OP;
158
175
  const cwd = common.ledgerCwd ?? process.cwd();
176
+ // With reuseApplied (#4654), a request with these fields that applied already is asked again as itself, so it answers `spent`.
159
177
  const standing = (await api.readGatedRequests(op, { cwd }).catch(() => []))
160
- .filter((r) => !r.applied && r.request.kind === kind && r.request.subject === subject && r.request.by === common.by && sameFields(r.request.fields, fields))
178
+ .filter((r) => (options.reuseApplied || !r.applied) && r.request.kind === kind && r.request.subject === subject && r.request.by === common.by && sameFields(r.request.fields, fields))
161
179
  .at(-1);
162
180
  const request: GatedRequest = { kind, subject, by: common.by, at: standing?.request.at ?? common.now ?? new Date().toISOString(), fields };
163
181
  const asked: RequestGateResult = await api.requestGate({
@@ -550,3 +568,170 @@ export async function gatedStackMove(opts: PulumiStackMoveOptions): Promise<Pulu
550
568
  },
551
569
  );
552
570
  }
571
+
572
+ // ── action ───────────────────────────────────────────────────────────────
573
+
574
+ /** Why a stack action was refused before anything was asked (#4654). */
575
+ export type PulumiStackActionRefusal = "action-urn" | "action-type" | "action-scope";
576
+
577
+ /** A stack action refused before its request: `code` says why, `urns` names what the preview changed besides the action, for `action-scope`. */
578
+ export class PulumiStackActionRefused extends Error {
579
+ readonly code: PulumiStackActionRefusal;
580
+ readonly urns: string[];
581
+ constructor(code: PulumiStackActionRefusal, message: string, urns: string[] = []) {
582
+ super(`${code}: ${message}`);
583
+ this.name = "PulumiStackActionRefused";
584
+ this.code = code;
585
+ this.urns = urns;
586
+ }
587
+ }
588
+
589
+ /** The resource types an action may name: the command provider's (`command:local:Command`, `command:remote:Command`). */
590
+ export const PULUMI_ACTION_TYPE_PREFIX = "command:";
591
+
592
+ /** An action's name as a caller's config gives it, for the request's subject. */
593
+ const ACTION_NAME = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,99}$/;
594
+
595
+ /** The subject an action's request is asked and approved under: `action:<stack>:<name>`, or the URN in place of a name. */
596
+ export const stackActionSubject = (stack: string, nameOrUrn: string): string => `action:${stack}:${nameOrUrn}`;
597
+
598
+ export interface PulumiStackActionOptions extends PulumiStackOpCommon {
599
+ /** The stack's directory, where `Pulumi.yaml` is. */
600
+ dir: string;
601
+ cwd?: string;
602
+ /** The stack, as `--stack` takes it. */
603
+ stack: string;
604
+ engine?: PulumiMemberEngine;
605
+ /** The `command:` resource whose replacement is the action. */
606
+ urn: string;
607
+ /** The action's name in the caller's config, which the subject carries in place of the URN. */
608
+ name?: string;
609
+ /** Ask anew even when the same request applied already. Absent, such a call answers `spent`, so a retried job never runs the action twice. */
610
+ anew?: boolean;
611
+ }
612
+
613
+ export interface PulumiStackActionResult {
614
+ urn: string;
615
+ engine: PulumiMemberEngine;
616
+ /** The preview's digest the request bound. */
617
+ preview: string;
618
+ /** The update's exit code: 0, since a failed update ends `failed`. */
619
+ exit: number;
620
+ /** The update's last lines of output. */
621
+ tail: string;
622
+ }
623
+
624
+ /** The last `n` lines of `text`, trimmed. */
625
+ const lastLines = (text: string | undefined, n: number): string =>
626
+ (text ?? "").trim().split("\n").slice(-n).join("\n");
627
+
628
+ /**
629
+ * Check an action's URN before anything runs: a Pulumi URN on `stack` whose
630
+ * type is a command provider's. Throws {@link PulumiStackActionRefused}.
631
+ */
632
+ export function checkStackActionUrn(stack: string, urn: string): void {
633
+ const parsed = parsePulumiUrn(urn);
634
+ if (!parsed) throw new PulumiStackActionRefused("action-urn", `${JSON.stringify(urn)} is not a resource URN (urn:pulumi:<stack>::<project>::<type>::<name>)`);
635
+ const stackName = stack.split("/").at(-1);
636
+ if (parsed.stack !== stackName) throw new PulumiStackActionRefused("action-urn", `${urn} names stack ${parsed.stack}, and the action runs on ${stack}`);
637
+ if (!parsed.type.startsWith(PULUMI_ACTION_TYPE_PREFIX)) {
638
+ throw new PulumiStackActionRefused(
639
+ "action-type",
640
+ `${urn} is a ${parsed.type}, and an action is a command resource (command:local:Command or command:remote:Command), whose replacement runs it; replacing any other resource is an update`,
641
+ );
642
+ }
643
+ }
644
+
645
+ /**
646
+ * What a preview changes besides the action's replacement (#4654): each URN
647
+ * with a changing step other than `urn`, and `urn` itself when it changes
648
+ * other than by replacement or is not replaced at all. Empty when the
649
+ * preview replaces `urn` and nothing else.
650
+ */
651
+ export function stackActionScope(urn: string, preview: unknown): { others: string[]; problem?: string } {
652
+ const steps = pulumiSteps(preview) ?? [];
653
+ const others = new Set<string>();
654
+ let replaced = false;
655
+ let problem: string | undefined;
656
+ for (const s of steps) {
657
+ const op = s.op ?? "";
658
+ if (PULUMI_UNCHANGED_OPS.has(op)) continue;
659
+ if (s.urn !== urn) {
660
+ others.add(s.urn!);
661
+ continue;
662
+ }
663
+ if (op === "replace" || op === "create-replacement" || op === "delete-replaced") replaced = true;
664
+ else problem = `the preview shows ${op} of ${urn}, not its replacement`;
665
+ }
666
+ if (!problem && !replaced) problem = `the preview does not replace ${urn}`;
667
+ return { others: [...others].sort(), ...(problem ? { problem } : {}) };
668
+ }
669
+
670
+ /**
671
+ * A day-2 action on a stack (#4654): the replacement of one `command:`
672
+ * resource, previewed, asked for under a gated request, and run once a
673
+ * second identity approves. Refused before anything runs for a URN that is
674
+ * not a command resource's (`action-type`), and before anything is asked for
675
+ * a preview that changes more than that replacement (`action-scope`).
676
+ */
677
+ export async function gatedStackAction(opts: PulumiStackActionOptions): Promise<PulumiStackOpOutcome<PulumiStackActionResult>> {
678
+ checkStackActionUrn(opts.stack, opts.urn);
679
+ if (opts.name !== undefined && !ACTION_NAME.test(opts.name)) throw new Error(`an action's name is letters, digits, "_", "." and "-", not ${JSON.stringify(opts.name)}`);
680
+ const env = opts.env ?? process.env;
681
+ const dir = stackDirOf(opts.dir, opts.cwd);
682
+ const engine = engineOf(dir, opts.engine, env);
683
+ const target = { stack: opts.stack, urn: opts.urn };
684
+
685
+ const previewArgs = pulumiActionPreviewArgs(target);
686
+ let stdout: string;
687
+ try {
688
+ ({ stdout } = engine === "plunge" ? await runAllowedPlunge(opts.plunge ?? defaultPlungeRunner, previewArgs, dir) : await runAllowedPulumi(opts.run ?? defaultPulumiRunner, previewArgs, dir));
689
+ } catch (err) {
690
+ throw new Error(`${engine} ${previewArgs.join(" ")} failed: ${why(err, engine)}`);
691
+ }
692
+ let preview: unknown;
693
+ try {
694
+ preview = JSON.parse(stdout);
695
+ } catch {
696
+ throw new Error(`${engine} ${previewArgs.join(" ")} printed no JSON`);
697
+ }
698
+ const scope = stackActionScope(opts.urn, preview);
699
+ if (scope.others.length > 0) {
700
+ throw new PulumiStackActionRefused(
701
+ "action-scope",
702
+ `the preview of ${opts.stack} replacing ${opts.urn} also changes ${scope.others.join(", ")}, and an action replaces its command resource alone; nothing was asked for`,
703
+ scope.others,
704
+ );
705
+ }
706
+ if (scope.problem) throw new PulumiStackActionRefused("action-scope", `${scope.problem}; nothing was asked for`);
707
+
708
+ const digest = pulumiPreviewDigest(preview, { changedOnly: true });
709
+ const subject = stackActionSubject(opts.stack, opts.name ?? opts.urn);
710
+ const fields: Record<string, string> = { stack: opts.stack, urn: opts.urn, engine, preview: digest, ...(opts.name ? { name: opts.name } : {}) };
711
+ return askThenAct(
712
+ opts,
713
+ "stack-action",
714
+ subject,
715
+ fields,
716
+ `action ${opts.name ?? opts.urn} on stack ${opts.stack}: replace ${opts.urn}, requested by ${opts.by}`,
717
+ async () => {
718
+ const args = pulumiActionUpArgs(target);
719
+ try {
720
+ const done = engine === "plunge" ? await runGatedPlunge(opts.plunge ?? defaultPlungeRunner, args, dir) : await runGatedPulumi(opts.run ?? defaultPulumiRunner, args, dir);
721
+ const tail = lastLines(done.stdout, 10);
722
+ return { result: { urn: opts.urn, engine, preview: digest, exit: 0, tail }, detail: { result: "ok", exit: 0, tail: tail.slice(-400), urn: opts.urn } };
723
+ } catch (err) {
724
+ const f = pulumiRunFailure(err);
725
+ const exit = f.exitCode ?? 1;
726
+ const tail = lastLines(`${f.stdout ?? ""}\n${f.stderr ?? ""}`, 10);
727
+ return {
728
+ result: { urn: opts.urn, engine, preview: digest, exit, tail },
729
+ detail: { result: "failed", exit, tail: tail.slice(-400), urn: opts.urn },
730
+ failed: `${engine} ${args.join(" ")} failed: ${why(err, engine)}`,
731
+ };
732
+ }
733
+ },
734
+ undefined,
735
+ { reuseApplied: !opts.anew },
736
+ );
737
+ }