@sanity/workflow-cli 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/commands/editorial-workflows/set-stage.d.ts +27 -2
- package/dist/commands/editorial-workflows/set-stage.js +32 -7
- package/dist/lib/context.d.ts +4 -0
- package/dist/lib/context.js +5 -1
- package/dist/lib/prompt.d.ts +3 -0
- package/oclif.manifest.json +3 -3
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @sanity/workflow-cli
|
|
2
2
|
|
|
3
|
+
## 0.23.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 795b4bb: `set-stage` now makes `--to` optional: omit it on an interactive terminal and the command presents a picker of the instance's declared stages, with the current stage marked and the cursor opened on it. Because `set-stage` is an admin override, every declared stage is offered — not just reachable transitions. The candidates come from the instance's pinned definition snapshot (the same model the engine validates the move against), so the menu can never offer a stage the move would reject. Off a non-interactive terminal `--to` stays required, so scripted use is unaffected. The engine now exports `parseDefinitionSnapshotValue` for reading a definition snapshot from a minimal instance shape.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [94d311e]
|
|
12
|
+
- Updated dependencies [795b4bb]
|
|
13
|
+
- @sanity/workflow-engine@0.23.0
|
|
14
|
+
|
|
3
15
|
## 0.22.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type SetStageArgs, type WorkflowInstance } from '@sanity/workflow-engine';
|
|
1
|
+
import { type SetStageArgs, type Stage, type WorkflowInstance } from '@sanity/workflow-engine';
|
|
2
2
|
import { WorkflowCommand } from '../../lib/base-command.ts';
|
|
3
3
|
import { type EngineScope } from '../../lib/operation-args.ts';
|
|
4
4
|
import { type WriteOutcome, type WriteReport } from '../../lib/ops-report.ts';
|
|
@@ -10,13 +10,38 @@ export default class SetStage extends WorkflowCommand {
|
|
|
10
10
|
instanceId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
11
11
|
};
|
|
12
12
|
static flags: {
|
|
13
|
-
to: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
to: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
14
|
reason: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
15
|
deployment: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
16
|
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
17
|
};
|
|
18
18
|
run(): Promise<void>;
|
|
19
19
|
}
|
|
20
|
+
/** Picks a target stage; injected in tests to drive the selection without a TTY. */
|
|
21
|
+
type ChooseTargetStage = (stages: readonly Stage[], currentStage: string) => Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* The picker choices for the admin override: every declared stage, with the
|
|
24
|
+
* current one labelled so it reads as "here" wherever the cursor sits. `value`
|
|
25
|
+
* stays the bare stage name — what {@link workflow.setStage} receives.
|
|
26
|
+
*/
|
|
27
|
+
export declare function stageChoices(stages: readonly Stage[], currentStage: string): {
|
|
28
|
+
name: string;
|
|
29
|
+
value: string;
|
|
30
|
+
}[];
|
|
31
|
+
/**
|
|
32
|
+
* The stage to move to: the explicit `--to`, else — on an interactive terminal
|
|
33
|
+
* — a pick from the instance's declared stages, else a fail asking for `--to`.
|
|
34
|
+
* Every declared stage is offered (set-stage is an admin override, not a
|
|
35
|
+
* transition), sourced from the instance's pinned snapshot: the same model the
|
|
36
|
+
* engine validates the move against, so the menu can't offer a stage it would
|
|
37
|
+
* reject. Parsed only here on the prompt path — a `--to` move never touches it.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveTargetStage({ to, instance, interactive, choose, }: {
|
|
40
|
+
to: string | undefined;
|
|
41
|
+
instance: Pick<WorkflowInstance, '_id' | 'definitionSnapshot' | 'currentStage'>;
|
|
42
|
+
interactive?: boolean | undefined;
|
|
43
|
+
choose?: ChooseTargetStage;
|
|
44
|
+
}): Promise<string>;
|
|
20
45
|
/** The shared operation args ({@link buildOperationArgs}) plus the
|
|
21
46
|
* setStage-specific target. */
|
|
22
47
|
export declare function buildSetStageArgs({ scope, instanceId, targetStage, reason, }: {
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { styleText } from 'node:util';
|
|
2
2
|
import { Args, Flags } from '@oclif/core';
|
|
3
|
-
import { workflow } from '@sanity/workflow-engine';
|
|
3
|
+
import { parseDefinitionSnapshotValue, workflow, } from '@sanity/workflow-engine';
|
|
4
4
|
import { WorkflowCommand } from "../../lib/base-command.js";
|
|
5
5
|
import { resolveInstanceContext } from "../../lib/context.js";
|
|
6
|
+
import { fail } from "../../lib/fail.js";
|
|
6
7
|
import { instanceFlags } from "../../lib/flags.js";
|
|
7
8
|
import { buildOperationArgs } from "../../lib/operation-args.js";
|
|
8
9
|
import { cascadeTail, opsAppliedLines, runWriteVerb, } from "../../lib/ops-report.js";
|
|
10
|
+
import { canPromptOnStderr, selectOnStderr } from "../../lib/prompt.js";
|
|
9
11
|
export default class SetStage extends WorkflowCommand {
|
|
10
12
|
static aliases = ['set-stage'];
|
|
11
13
|
static description = "Force an instance into a stage, regardless of its declared transitions and filters — the engine's setStage admin override. The target stage's enter lifecycle still runs (auto-activities start, stage guards reconcile), and the post-move cascade can immediately auto-transition the instance onward.";
|
|
12
14
|
static examples = [
|
|
13
15
|
'<%= config.bin %> set-stage wf-instance.abc123 --to ready',
|
|
16
|
+
'<%= config.bin %> set-stage wf-instance.abc123',
|
|
14
17
|
"<%= config.bin %> set-stage wf-instance.abc123 --to ready --reason 'unblock for demo'",
|
|
15
18
|
];
|
|
16
19
|
static args = {
|
|
@@ -22,8 +25,7 @@ export default class SetStage extends WorkflowCommand {
|
|
|
22
25
|
static flags = {
|
|
23
26
|
...instanceFlags,
|
|
24
27
|
to: Flags.string({
|
|
25
|
-
|
|
26
|
-
description: 'Target stage name.',
|
|
28
|
+
description: 'Target stage name. Omit on an interactive terminal to pick from the workflow’s stages.',
|
|
27
29
|
}),
|
|
28
30
|
reason: Flags.string({
|
|
29
31
|
description: 'Free-text reason — recorded on the history entry for audit.',
|
|
@@ -31,9 +33,10 @@ export default class SetStage extends WorkflowCommand {
|
|
|
31
33
|
};
|
|
32
34
|
async run() {
|
|
33
35
|
const { args, flags } = await this.parse(SetStage);
|
|
34
|
-
const { client, scope } = await resolveInstanceContext(flags, args.instanceId);
|
|
36
|
+
const { client, scope, instance } = await resolveInstanceContext(flags, args.instanceId);
|
|
37
|
+
const targetStage = await resolveTargetStage({ to: flags.to, instance });
|
|
35
38
|
await runWriteVerb({
|
|
36
|
-
startLabel: `Setting ${args.instanceId} → ${
|
|
39
|
+
startLabel: `Setting ${args.instanceId} → ${targetStage}…`,
|
|
37
40
|
failLabel: 'Stage move rejected',
|
|
38
41
|
failHeadline: 'set-stage error:',
|
|
39
42
|
run: () => workflow.setStage({
|
|
@@ -41,15 +44,37 @@ export default class SetStage extends WorkflowCommand {
|
|
|
41
44
|
...buildSetStageArgs({
|
|
42
45
|
scope,
|
|
43
46
|
instanceId: args.instanceId,
|
|
44
|
-
targetStage
|
|
47
|
+
targetStage,
|
|
45
48
|
reason: flags.reason,
|
|
46
49
|
}),
|
|
47
50
|
}),
|
|
48
|
-
report: (result) => setStageReport(result,
|
|
51
|
+
report: (result) => setStageReport(result, targetStage),
|
|
49
52
|
log: (line) => this.log(line),
|
|
50
53
|
});
|
|
51
54
|
}
|
|
52
55
|
}
|
|
56
|
+
export function stageChoices(stages, currentStage) {
|
|
57
|
+
return stages.map((stage) => ({
|
|
58
|
+
name: stage.name === currentStage ? `${stage.name} (current)` : stage.name,
|
|
59
|
+
value: stage.name,
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
function chooseTargetStage(stages, currentStage) {
|
|
63
|
+
return selectOnStderr({
|
|
64
|
+
message: 'Select target stage (admin override — any stage allowed)',
|
|
65
|
+
choices: stageChoices(stages, currentStage),
|
|
66
|
+
default: currentStage,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export async function resolveTargetStage({ to, instance, interactive, choose = chooseTargetStage, }) {
|
|
70
|
+
if (to !== undefined) {
|
|
71
|
+
return to;
|
|
72
|
+
}
|
|
73
|
+
if (!(interactive ?? canPromptOnStderr())) {
|
|
74
|
+
fail('set-stage needs a target stage — pass --to <stage>.');
|
|
75
|
+
}
|
|
76
|
+
return choose(parseDefinitionSnapshotValue(instance).stages, instance.currentStage);
|
|
77
|
+
}
|
|
53
78
|
export function buildSetStageArgs({ scope, instanceId, targetStage, reason, }) {
|
|
54
79
|
return { ...buildOperationArgs({ scope, instanceId, reason }), targetStage };
|
|
55
80
|
}
|
package/dist/lib/context.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export interface InstanceContext {
|
|
|
10
10
|
/** The engine scope derived from the instance itself — `{tag, workflowResource}`
|
|
11
11
|
* ready to pass straight to an engine verb, so callers never re-assemble it. */
|
|
12
12
|
scope: EngineScope;
|
|
13
|
+
/** The located instance. Already fetched by the fan-out to derive the scope,
|
|
14
|
+
* so a command that needs its state (set-stage's stage picker reads the
|
|
15
|
+
* pinned definition snapshot and `currentStage`) reuses it, not re-fetch. */
|
|
16
|
+
instance: WorkflowInstance;
|
|
13
17
|
}
|
|
14
18
|
/**
|
|
15
19
|
* The resolution a WRITE shares: discover the config, pick the deployment for
|
package/dist/lib/context.js
CHANGED
|
@@ -38,7 +38,11 @@ export async function resolveInstanceContext(flags, instanceId) {
|
|
|
38
38
|
if (hit === undefined) {
|
|
39
39
|
fail(`Workflow instance ${instanceId} not found`);
|
|
40
40
|
}
|
|
41
|
-
return {
|
|
41
|
+
return {
|
|
42
|
+
client: hit.client,
|
|
43
|
+
scope: { tag: hit.instance.tag, workflowResource: hit.resource },
|
|
44
|
+
instance: hit.instance,
|
|
45
|
+
};
|
|
42
46
|
}
|
|
43
47
|
async function locateInstance({ config, flags, instanceId, token, }) {
|
|
44
48
|
const resources = await instanceCandidateResources(config, flags);
|
package/dist/lib/prompt.d.ts
CHANGED
|
@@ -22,4 +22,7 @@ export declare function selectOnStderr(config: {
|
|
|
22
22
|
value: string;
|
|
23
23
|
description?: string;
|
|
24
24
|
}[];
|
|
25
|
+
/** The value the cursor starts on — e.g. an instance's current stage, so the
|
|
26
|
+
* picker opens where it already is. */
|
|
27
|
+
default?: string;
|
|
25
28
|
}): Promise<string>;
|
package/oclif.manifest.json
CHANGED
|
@@ -492,6 +492,7 @@
|
|
|
492
492
|
"description": "Force an instance into a stage, regardless of its declared transitions and filters — the engine's setStage admin override. The target stage's enter lifecycle still runs (auto-activities start, stage guards reconcile), and the post-move cascade can immediately auto-transition the instance onward.",
|
|
493
493
|
"examples": [
|
|
494
494
|
"<%= config.bin %> set-stage wf-instance.abc123 --to ready",
|
|
495
|
+
"<%= config.bin %> set-stage wf-instance.abc123",
|
|
495
496
|
"<%= config.bin %> set-stage wf-instance.abc123 --to ready --reason 'unblock for demo'"
|
|
496
497
|
],
|
|
497
498
|
"flags": {
|
|
@@ -516,9 +517,8 @@
|
|
|
516
517
|
"type": "option"
|
|
517
518
|
},
|
|
518
519
|
"to": {
|
|
519
|
-
"description": "Target stage name.",
|
|
520
|
+
"description": "Target stage name. Omit on an interactive terminal to pick from the workflow’s stages.",
|
|
520
521
|
"name": "to",
|
|
521
|
-
"required": true,
|
|
522
522
|
"hasDynamicHelp": false,
|
|
523
523
|
"multiple": false,
|
|
524
524
|
"type": "option"
|
|
@@ -970,5 +970,5 @@
|
|
|
970
970
|
]
|
|
971
971
|
}
|
|
972
972
|
},
|
|
973
|
-
"version": "0.
|
|
973
|
+
"version": "0.23.0"
|
|
974
974
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/workflow-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Command-line tool for deploying, inspecting, and administering Sanity workflow definitions and instances.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -62,12 +62,12 @@
|
|
|
62
62
|
"@types/node": "^24.12.4",
|
|
63
63
|
"oclif": "^4.23.16",
|
|
64
64
|
"vitest": "^4.1.8",
|
|
65
|
-
"@sanity/workflow-engine": "0.
|
|
66
|
-
"@sanity/workflow-engine-test": "0.
|
|
67
|
-
"@sanity/workflow-examples": "0.10.
|
|
65
|
+
"@sanity/workflow-engine": "0.23.0",
|
|
66
|
+
"@sanity/workflow-engine-test": "0.23.0",
|
|
67
|
+
"@sanity/workflow-examples": "0.10.2"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
|
-
"@sanity/workflow-engine": "0.
|
|
70
|
+
"@sanity/workflow-engine": "0.23.0"
|
|
71
71
|
},
|
|
72
72
|
"oclif": {
|
|
73
73
|
"bin": "sanity-workflows",
|