@sanity/workflow-cli 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -61
- package/dist/commands/abort.d.ts +5 -7
- package/dist/commands/abort.js +15 -14
- package/dist/commands/definition/delete.d.ts +4 -4
- package/dist/commands/definition/delete.js +8 -11
- package/dist/commands/definition/diff.d.ts +8 -1
- package/dist/commands/definition/diff.js +26 -25
- package/dist/commands/definition/list.d.ts +4 -4
- package/dist/commands/definition/list.js +8 -8
- package/dist/commands/definition/show.d.ts +0 -10
- package/dist/commands/definition/show.js +4 -17
- package/dist/commands/deploy.d.ts +58 -5
- package/dist/commands/deploy.js +134 -27
- package/dist/commands/diagnose.js +16 -26
- package/dist/commands/fire-action.d.ts +7 -28
- package/dist/commands/fire-action.js +29 -68
- package/dist/commands/list.d.ts +2 -2
- package/dist/commands/list.js +8 -8
- package/dist/commands/{retry-activity.d.ts → reset-activity.d.ts} +1 -1
- package/dist/commands/{retry-activity.js → reset-activity.js} +2 -2
- package/dist/commands/set-stage.d.ts +27 -3
- package/dist/commands/set-stage.js +80 -13
- package/dist/commands/show.d.ts +1 -0
- package/dist/commands/show.js +9 -3
- package/dist/commands/start.d.ts +59 -0
- package/dist/commands/start.js +169 -0
- package/dist/commands/tail.d.ts +3 -0
- package/dist/commands/tail.js +7 -3
- package/dist/lib/client.d.ts +11 -14
- package/dist/lib/client.js +45 -33
- package/dist/lib/context.d.ts +62 -0
- package/dist/lib/context.js +82 -0
- package/dist/lib/definitions.d.ts +38 -29
- package/dist/lib/definitions.js +45 -85
- package/dist/lib/diff.js +8 -0
- package/dist/lib/env.d.ts +0 -6
- package/dist/lib/env.js +3 -9
- package/dist/lib/fail.d.ts +6 -0
- package/dist/lib/fail.js +11 -1
- package/dist/lib/flags.d.ts +14 -2
- package/dist/lib/flags.js +24 -3
- package/dist/lib/load-config.d.ts +11 -0
- package/dist/lib/load-config.js +69 -0
- package/dist/lib/operation-args.d.ts +23 -9
- package/dist/lib/operation-args.js +12 -11
- package/dist/lib/ops-report.d.ts +14 -6
- package/dist/lib/ops-report.js +5 -6
- package/dist/lib/params.d.ts +7 -0
- package/dist/lib/params.js +26 -0
- package/dist/lib/select-deployment.d.ts +31 -0
- package/dist/lib/select-deployment.js +58 -0
- package/oclif.manifest.json +145 -86
- package/package.json +6 -4
- package/dist/commands/move-stage.d.ts +0 -52
- package/dist/commands/move-stage.js +0 -86
- package/dist/lib/config.d.ts +0 -18
- package/dist/lib/config.js +0 -50
|
@@ -1,45 +1,15 @@
|
|
|
1
1
|
import { styleText } from 'node:util';
|
|
2
2
|
import { Args, Command, Flags } from '@oclif/core';
|
|
3
|
-
import { workflow, } from '@sanity/workflow-engine';
|
|
3
|
+
import { deniedGuardLabels, workflow, } from '@sanity/workflow-engine';
|
|
4
4
|
import logSymbols from 'log-symbols';
|
|
5
5
|
import ora from 'ora';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
6
|
+
import { resolveInstanceContext } from "../lib/context.js";
|
|
7
|
+
import { errorMessage, fail, failOnThrow } from "../lib/fail.js";
|
|
8
|
+
import { actorFlags, jsonFlags, tagFlags } from "../lib/flags.js";
|
|
9
|
+
import { baseEngineArgs, resolveActor } from "../lib/operation-args.js";
|
|
10
|
+
import { emitWriteReport, opsAppliedLines, } from "../lib/ops-report.js";
|
|
11
|
+
import { parseParams } from "../lib/params.js";
|
|
12
12
|
import { instanceHeader } from "./show.js";
|
|
13
|
-
function errorMessage(error) {
|
|
14
|
-
return error instanceof Error ? error.message : String(error);
|
|
15
|
-
}
|
|
16
|
-
function coerceParamValue(raw) {
|
|
17
|
-
try {
|
|
18
|
-
return JSON.parse(raw);
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
// Not valid JSON — treat it as a plain string so `--param note=hi`
|
|
22
|
-
// needs no quoting. The engine validates against the action's
|
|
23
|
-
// declared param type either way.
|
|
24
|
-
return raw;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
function parseParam(pair) {
|
|
28
|
-
const eq = pair.indexOf('=');
|
|
29
|
-
if (eq < 1) {
|
|
30
|
-
throw new Error(`expected key=value, got "${pair}"`);
|
|
31
|
-
}
|
|
32
|
-
return [pair.slice(0, eq), coerceParamValue(pair.slice(eq + 1))];
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Parse repeated `--param key=value` flags into a params object. Each
|
|
36
|
-
* value is JSON-parsed so numbers/booleans/objects type correctly,
|
|
37
|
-
* falling back to the raw string. The engine validates the result against
|
|
38
|
-
* the action's declared param types.
|
|
39
|
-
*/
|
|
40
|
-
export function parseParams(pairs) {
|
|
41
|
-
return Object.fromEntries(pairs.map(parseParam));
|
|
42
|
-
}
|
|
43
13
|
function reasonText(reason) {
|
|
44
14
|
switch (reason.kind) {
|
|
45
15
|
case 'filter-failed':
|
|
@@ -49,7 +19,7 @@ function reasonText(reason) {
|
|
|
49
19
|
case 'stage-terminal':
|
|
50
20
|
return `stage '${reason.stage}' is terminal`;
|
|
51
21
|
case 'mutation-guard-denied':
|
|
52
|
-
return `blocked by mutation guard(s) ${reason.
|
|
22
|
+
return `blocked by mutation guard(s) ${deniedGuardLabels(reason.denied).join(', ')}`;
|
|
53
23
|
case 'instance-completed':
|
|
54
24
|
return `instance completed at ${reason.completedAt}`;
|
|
55
25
|
case 'requirements-unmet':
|
|
@@ -97,7 +67,7 @@ export function fireResultJson(result, ids) {
|
|
|
97
67
|
instanceId: ids.instanceId,
|
|
98
68
|
activity: ids.activity,
|
|
99
69
|
action: ids.action,
|
|
100
|
-
|
|
70
|
+
changed: result.changed,
|
|
101
71
|
cascaded: result.cascaded,
|
|
102
72
|
currentStage: result.instance.currentStage,
|
|
103
73
|
...(result.ranOps !== undefined ? { ranOps: result.ranOps } : {}),
|
|
@@ -110,16 +80,16 @@ export function fireResultJson(result, ids) {
|
|
|
110
80
|
*/
|
|
111
81
|
export function fireActionReport({ result, activity, action, }) {
|
|
112
82
|
const stage = styleText('bold', result.instance.currentStage);
|
|
113
|
-
if (!result.
|
|
83
|
+
if (!result.changed) {
|
|
114
84
|
return {
|
|
115
|
-
|
|
85
|
+
changed: false,
|
|
116
86
|
message: `fireAction returned without firing — activity '${activity}' is no longer active`,
|
|
117
87
|
opsLines: [],
|
|
118
88
|
};
|
|
119
89
|
}
|
|
120
90
|
const tail = result.cascaded > 0 ? `, then cascaded ${result.cascaded} auto-transition(s)` : '';
|
|
121
91
|
return {
|
|
122
|
-
|
|
92
|
+
changed: true,
|
|
123
93
|
message: `Fired ${styleText('bold', action)} on ${styleText('bold', activity)} — now at ${stage}${tail}`,
|
|
124
94
|
opsLines: opsAppliedLines(result.ranOps),
|
|
125
95
|
};
|
|
@@ -148,26 +118,21 @@ export default class FireAction extends Command {
|
|
|
148
118
|
multiple: true,
|
|
149
119
|
default: [],
|
|
150
120
|
}),
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}),
|
|
154
|
-
'as-role': Flags.string({
|
|
155
|
-
description: 'Role to attribute to the --as user (repeatable), so role-gated filters pass.',
|
|
156
|
-
multiple: true,
|
|
157
|
-
default: [],
|
|
158
|
-
}),
|
|
159
|
-
json: Flags.boolean({
|
|
160
|
-
description: 'Emit structured JSON instead of rendered output.',
|
|
161
|
-
default: false,
|
|
162
|
-
}),
|
|
121
|
+
...actorFlags,
|
|
122
|
+
...jsonFlags,
|
|
163
123
|
};
|
|
164
124
|
async run() {
|
|
165
125
|
const { args, flags } = await this.parse(FireAction);
|
|
166
|
-
const
|
|
167
|
-
const client = loadClient();
|
|
126
|
+
const { client, scope } = await resolveInstanceContext(flags, args.instanceId);
|
|
168
127
|
const actor = failOnThrow('fire-action usage error:', () => resolveActor(flags.as, flags['as-role']));
|
|
169
128
|
if (flags.action === undefined) {
|
|
170
|
-
await this.listActions({
|
|
129
|
+
await this.listActions({
|
|
130
|
+
client,
|
|
131
|
+
scope,
|
|
132
|
+
instanceId: args.instanceId,
|
|
133
|
+
actor,
|
|
134
|
+
json: flags.json,
|
|
135
|
+
});
|
|
171
136
|
return;
|
|
172
137
|
}
|
|
173
138
|
if (flags.activity === undefined) {
|
|
@@ -175,7 +140,7 @@ export default class FireAction extends Command {
|
|
|
175
140
|
}
|
|
176
141
|
await this.fireOne({
|
|
177
142
|
client,
|
|
178
|
-
|
|
143
|
+
scope,
|
|
179
144
|
instanceId: args.instanceId,
|
|
180
145
|
actor,
|
|
181
146
|
activity: flags.activity,
|
|
@@ -184,14 +149,12 @@ export default class FireAction extends Command {
|
|
|
184
149
|
json: flags.json,
|
|
185
150
|
});
|
|
186
151
|
}
|
|
187
|
-
async listActions({ client,
|
|
152
|
+
async listActions({ client, scope, instanceId, actor, json, }) {
|
|
188
153
|
const { evaluation, actions } = await workflow
|
|
189
154
|
.availableActions({
|
|
190
155
|
client,
|
|
191
|
-
|
|
192
|
-
workflowResource: config.workflowResource,
|
|
156
|
+
...baseEngineArgs(scope, actor),
|
|
193
157
|
instanceId,
|
|
194
|
-
access: { actor },
|
|
195
158
|
})
|
|
196
159
|
.catch((error) => fail('fire-action failed:', errorMessage(error)));
|
|
197
160
|
const stage = evaluation.instance.currentStage;
|
|
@@ -206,22 +169,20 @@ export default class FireAction extends Command {
|
|
|
206
169
|
}
|
|
207
170
|
}
|
|
208
171
|
async fireOne(opts) {
|
|
209
|
-
const { client,
|
|
172
|
+
const { client, scope, instanceId, actor, activity, action, json } = opts;
|
|
210
173
|
const params = failOnThrow('Invalid --param:', () => parseParams(opts.params));
|
|
211
174
|
const fireArgs = {
|
|
212
175
|
client,
|
|
213
|
-
|
|
214
|
-
workflowResource: config.workflowResource,
|
|
176
|
+
...baseEngineArgs(scope, actor),
|
|
215
177
|
instanceId,
|
|
216
178
|
activity,
|
|
217
179
|
action,
|
|
218
|
-
access: { actor },
|
|
219
180
|
params,
|
|
220
181
|
};
|
|
221
182
|
if (json) {
|
|
222
183
|
const result = await workflow
|
|
223
184
|
.fireAction(fireArgs)
|
|
224
|
-
.catch((error) => fail('
|
|
185
|
+
.catch((error) => fail('fire-action error:', errorMessage(error)));
|
|
225
186
|
this.log(JSON.stringify(fireResultJson(result, { instanceId, activity, action }), null, 2));
|
|
226
187
|
return;
|
|
227
188
|
}
|
|
@@ -236,7 +197,7 @@ export default class FireAction extends Command {
|
|
|
236
197
|
}
|
|
237
198
|
catch (error) {
|
|
238
199
|
spinner.fail('Action rejected');
|
|
239
|
-
fail('
|
|
200
|
+
fail('fire-action error:', errorMessage(error));
|
|
240
201
|
}
|
|
241
202
|
}
|
|
242
203
|
}
|
package/dist/commands/list.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ type InstanceStatus = 'aborted' | 'completed' | 'in-flight';
|
|
|
12
12
|
interface ListFlags {
|
|
13
13
|
'in-flight': boolean;
|
|
14
14
|
failed: boolean;
|
|
15
|
-
|
|
15
|
+
definition?: string | undefined;
|
|
16
16
|
tag?: string | undefined;
|
|
17
17
|
limit: number;
|
|
18
18
|
}
|
|
@@ -41,7 +41,7 @@ export default class List extends Command {
|
|
|
41
41
|
static flags: {
|
|
42
42
|
'in-flight': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
43
43
|
failed: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
44
|
-
|
|
44
|
+
definition: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
45
45
|
limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
46
46
|
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
47
47
|
};
|
package/dist/commands/list.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Command, Flags } from '@oclif/core';
|
|
|
3
3
|
import { WORKFLOW_INSTANCE_TYPE, tagScopeFilter } from '@sanity/workflow-engine';
|
|
4
4
|
import { Table } from 'console-table-printer';
|
|
5
5
|
import logSymbols from 'log-symbols';
|
|
6
|
-
import {
|
|
6
|
+
import { resolveReadContext } from "../lib/context.js";
|
|
7
7
|
import { tagFlags } from "../lib/flags.js";
|
|
8
8
|
import { formatAge } from "../lib/ui.js";
|
|
9
9
|
/**
|
|
@@ -15,7 +15,7 @@ export function buildListQuery(flags) {
|
|
|
15
15
|
const filters = [`_type == "${WORKFLOW_INSTANCE_TYPE}"`];
|
|
16
16
|
if (flags['in-flight'])
|
|
17
17
|
filters.push('!defined(completedAt)');
|
|
18
|
-
if (flags
|
|
18
|
+
if (flags.definition)
|
|
19
19
|
filters.push('definition == $definition');
|
|
20
20
|
// Cheap approximation of --failed: any activity in any stage with status "failed".
|
|
21
21
|
if (flags.failed)
|
|
@@ -32,8 +32,8 @@ export function buildListQuery(flags) {
|
|
|
32
32
|
lastChangedAt
|
|
33
33
|
}`;
|
|
34
34
|
const params = { limit: flags.limit };
|
|
35
|
-
if (flags
|
|
36
|
-
params['definition'] = flags
|
|
35
|
+
if (flags.definition)
|
|
36
|
+
params['definition'] = flags.definition;
|
|
37
37
|
if (flags.tag)
|
|
38
38
|
params['tag'] = flags.tag;
|
|
39
39
|
return { groq, params };
|
|
@@ -68,7 +68,7 @@ export default class List extends Command {
|
|
|
68
68
|
static examples = [
|
|
69
69
|
'<%= config.bin %> list',
|
|
70
70
|
'<%= config.bin %> list --in-flight',
|
|
71
|
-
'<%= config.bin %> list --
|
|
71
|
+
'<%= config.bin %> list --definition productLaunch',
|
|
72
72
|
'<%= config.bin %> list --tag prod',
|
|
73
73
|
];
|
|
74
74
|
static flags = {
|
|
@@ -81,8 +81,8 @@ export default class List extends Command {
|
|
|
81
81
|
description: 'Only instances with at least one failed activity.',
|
|
82
82
|
default: false,
|
|
83
83
|
}),
|
|
84
|
-
|
|
85
|
-
description:
|
|
84
|
+
definition: Flags.string({
|
|
85
|
+
description: "Only instances of this workflow definition (its `name`; the instance's `definition` field).",
|
|
86
86
|
}),
|
|
87
87
|
limit: Flags.integer({
|
|
88
88
|
description: 'Maximum rows to return.',
|
|
@@ -91,8 +91,8 @@ export default class List extends Command {
|
|
|
91
91
|
};
|
|
92
92
|
async run() {
|
|
93
93
|
const { flags } = await this.parse(List);
|
|
94
|
+
const { client } = await resolveReadContext(flags);
|
|
94
95
|
const { groq, params } = buildListQuery(flags);
|
|
95
|
-
const client = loadClient();
|
|
96
96
|
const rows = await client.fetch(groq, params);
|
|
97
97
|
if (rows.length === 0) {
|
|
98
98
|
this.log(`${logSymbols.info} no instances match`);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Args } from '@oclif/core';
|
|
2
2
|
import { StubCommand } from "../lib/stub.js";
|
|
3
|
-
export default class
|
|
3
|
+
export default class ResetActivity extends StubCommand {
|
|
4
4
|
// Stubbed, so kept off the help + published surface until it's wired.
|
|
5
5
|
static hidden = true;
|
|
6
|
-
static description = '
|
|
6
|
+
static description = 'Reset a failed activity on an in-flight instance — back to pending (re-run) or to skipped (bypass).';
|
|
7
7
|
static args = {
|
|
8
8
|
instanceId: Args.string({ required: true, description: 'Workflow instance id.' }),
|
|
9
9
|
activity: Args.string({ required: true, description: 'Activity name within the current stage.' }),
|
|
@@ -1,12 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { type SetStageArgs, type WorkflowInstance } from '@sanity/workflow-engine';
|
|
3
|
+
import { type EngineScope } from '../lib/operation-args.ts';
|
|
4
|
+
import { type WriteOutcome, type WriteReport } from '../lib/ops-report.ts';
|
|
5
|
+
export default class SetStage extends Command {
|
|
4
6
|
static description: string;
|
|
7
|
+
static examples: string[];
|
|
5
8
|
static args: {
|
|
6
9
|
instanceId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
10
|
};
|
|
8
11
|
static flags: {
|
|
9
12
|
to: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
13
|
reason: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
15
|
};
|
|
16
|
+
run(): Promise<void>;
|
|
12
17
|
}
|
|
18
|
+
/** The shared operation args ({@link buildOperationArgs}) plus the
|
|
19
|
+
* setStage-specific target. */
|
|
20
|
+
export declare function buildSetStageArgs({ scope, instanceId, targetStage, reason, }: {
|
|
21
|
+
scope: EngineScope;
|
|
22
|
+
instanceId: string;
|
|
23
|
+
targetStage: string;
|
|
24
|
+
reason: string | undefined;
|
|
25
|
+
}): SetStageArgs & EngineScope;
|
|
26
|
+
/** {@link WriteOutcome} with the terminal stamp the no-op copy reads. */
|
|
27
|
+
interface SetStageOutcome extends WriteOutcome {
|
|
28
|
+
instance: Pick<WorkflowInstance, 'currentStage' | 'completedAt'>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Turn a `setStage` result into the spinner message + ops block. Pure so
|
|
32
|
+
* the changed / terminal / already-at-target / cascaded / ops permutations
|
|
33
|
+
* can be asserted without driving a real move.
|
|
34
|
+
*/
|
|
35
|
+
export declare function setStageReport(result: SetStageOutcome, to: string): WriteReport;
|
|
36
|
+
export {};
|
|
@@ -1,18 +1,85 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
3
|
+
import { workflow } from '@sanity/workflow-engine';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import { resolveInstanceContext } from "../lib/context.js";
|
|
6
|
+
import { errorMessage, fail } from "../lib/fail.js";
|
|
7
|
+
import { tagFlags } from "../lib/flags.js";
|
|
8
|
+
import { buildOperationArgs } from "../lib/operation-args.js";
|
|
9
|
+
import { emitWriteReport, opsAppliedLines, } from "../lib/ops-report.js";
|
|
10
|
+
export default class SetStage extends Command {
|
|
11
|
+
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
|
+
static examples = [
|
|
13
|
+
'<%= config.bin %> set-stage wf-instance.abc123 --to ready',
|
|
14
|
+
"<%= config.bin %> set-stage wf-instance.abc123 --to ready --reason 'unblock for demo'",
|
|
15
|
+
];
|
|
11
16
|
static args = {
|
|
12
|
-
instanceId: Args.string({
|
|
17
|
+
instanceId: Args.string({
|
|
18
|
+
required: true,
|
|
19
|
+
description: 'Workflow instance id to move.',
|
|
20
|
+
}),
|
|
13
21
|
};
|
|
14
22
|
static flags = {
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
...tagFlags,
|
|
24
|
+
to: Flags.string({
|
|
25
|
+
required: true,
|
|
26
|
+
description: 'Target stage name.',
|
|
27
|
+
}),
|
|
28
|
+
reason: Flags.string({
|
|
29
|
+
description: 'Free-text reason — recorded on the history entry for audit.',
|
|
30
|
+
}),
|
|
31
|
+
};
|
|
32
|
+
async run() {
|
|
33
|
+
const { args, flags } = await this.parse(SetStage);
|
|
34
|
+
const { client, scope } = await resolveInstanceContext(flags, args.instanceId);
|
|
35
|
+
const spinner = ora(`Setting ${args.instanceId} → ${flags.to}…`).start();
|
|
36
|
+
try {
|
|
37
|
+
const result = await workflow.setStage({
|
|
38
|
+
client,
|
|
39
|
+
...buildSetStageArgs({
|
|
40
|
+
scope,
|
|
41
|
+
instanceId: args.instanceId,
|
|
42
|
+
targetStage: flags.to,
|
|
43
|
+
reason: flags.reason,
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
emitWriteReport({
|
|
47
|
+
spinner,
|
|
48
|
+
report: setStageReport(result, flags.to),
|
|
49
|
+
log: (line) => this.log(line),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
spinner.fail('Stage move rejected');
|
|
54
|
+
fail('set-stage error:', errorMessage(error));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** The shared operation args ({@link buildOperationArgs}) plus the
|
|
59
|
+
* setStage-specific target. */
|
|
60
|
+
export function buildSetStageArgs({ scope, instanceId, targetStage, reason, }) {
|
|
61
|
+
return { ...buildOperationArgs({ scope, instanceId, reason }), targetStage };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Turn a `setStage` result into the spinner message + ops block. Pure so
|
|
65
|
+
* the changed / terminal / already-at-target / cascaded / ops permutations
|
|
66
|
+
* can be asserted without driving a real move.
|
|
67
|
+
*/
|
|
68
|
+
export function setStageReport(result, to) {
|
|
69
|
+
const stage = styleText('bold', result.instance.currentStage);
|
|
70
|
+
if (!result.changed) {
|
|
71
|
+
// The engine no-ops in two distinct cases: a terminal instance never
|
|
72
|
+
// moves again, and a same-stage move has nothing to do.
|
|
73
|
+
const { completedAt } = result.instance;
|
|
74
|
+
const message = completedAt !== undefined
|
|
75
|
+
? `setStage changed nothing — instance is terminal (at ${stage} since ${completedAt})`
|
|
76
|
+
: `setStage changed nothing — instance is already at ${stage}`;
|
|
77
|
+
return { changed: false, message, opsLines: [] };
|
|
78
|
+
}
|
|
79
|
+
const tail = result.cascaded > 0 ? `, then cascaded ${result.cascaded} auto-transition(s)` : '';
|
|
80
|
+
return {
|
|
81
|
+
changed: true,
|
|
82
|
+
message: `Now at ${stage} (was → ${to}${tail})`,
|
|
83
|
+
opsLines: opsAppliedLines(result.ranOps),
|
|
17
84
|
};
|
|
18
85
|
}
|
package/dist/commands/show.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export default class Show extends Command {
|
|
|
8
8
|
};
|
|
9
9
|
static flags: {
|
|
10
10
|
include: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
};
|
|
12
13
|
run(): Promise<void>;
|
|
13
14
|
}
|
package/dist/commands/show.js
CHANGED
|
@@ -2,7 +2,8 @@ import { styleText } from 'node:util';
|
|
|
2
2
|
import { Args, Command, Flags } from '@oclif/core';
|
|
3
3
|
import { abortReason } from '@sanity/workflow-engine';
|
|
4
4
|
import logSymbols from 'log-symbols';
|
|
5
|
-
import {
|
|
5
|
+
import { resolveReadContext } from "../lib/context.js";
|
|
6
|
+
import { tagFlags } from "../lib/flags.js";
|
|
6
7
|
import { formatKeyValue, formatTimestamp, sectionHeader, activityIcon } from "../lib/ui.js";
|
|
7
8
|
export default class Show extends Command {
|
|
8
9
|
static description = 'Show the state, activities, and effects of a workflow instance.';
|
|
@@ -17,6 +18,7 @@ export default class Show extends Command {
|
|
|
17
18
|
}),
|
|
18
19
|
};
|
|
19
20
|
static flags = {
|
|
21
|
+
...tagFlags,
|
|
20
22
|
include: Flags.string({
|
|
21
23
|
description: 'Optional sections to include in output.',
|
|
22
24
|
options: ['history'],
|
|
@@ -26,7 +28,7 @@ export default class Show extends Command {
|
|
|
26
28
|
};
|
|
27
29
|
async run() {
|
|
28
30
|
const { args, flags } = await this.parse(Show);
|
|
29
|
-
const client =
|
|
31
|
+
const { client } = await resolveReadContext(flags);
|
|
30
32
|
const instance = await client.getDocument(args.instanceId);
|
|
31
33
|
if (!instance) {
|
|
32
34
|
this.error(`${logSymbols.error} no instance with id "${args.instanceId}"`, { exit: 1 });
|
|
@@ -79,7 +81,11 @@ export function describeInstance(instance, options) {
|
|
|
79
81
|
? styleText('dim', ` (exited ${formatTimestamp(stage.exitedAt)})`)
|
|
80
82
|
: styleText('cyan', ' (current)');
|
|
81
83
|
lines.push(` • ${stage.name}${marker}`);
|
|
82
|
-
|
|
84
|
+
// A persisted stage may carry no activities array — an activity-less stage,
|
|
85
|
+
// or an older-shape document — so treat an absent list as none rather than
|
|
86
|
+
// crash. This renders whatever is in the lake, not only freshly-stamped
|
|
87
|
+
// instances; it does not reconstruct activity state from history.
|
|
88
|
+
for (const activity of stage.activities ?? []) {
|
|
83
89
|
lines.push(` ${activityIcon[activity.status]} ${activity.name} ${styleText('dim', `[${activity.status}]`)}`);
|
|
84
90
|
}
|
|
85
91
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { type Actor, type FieldEntry, type InitialFieldValue, type StartInstanceArgs, type WorkflowInstance, type WorkflowLifecycle } from '@sanity/workflow-engine';
|
|
3
|
+
import { type EngineScope } from '../lib/operation-args.ts';
|
|
4
|
+
export default class Start extends Command {
|
|
5
|
+
static description: string;
|
|
6
|
+
static examples: string[];
|
|
7
|
+
static args: {
|
|
8
|
+
name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
9
|
+
};
|
|
10
|
+
static flags: {
|
|
11
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
as: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
'as-role': import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
version: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
field: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Why a definition can't be started standalone, or `undefined` when it can.
|
|
22
|
+
* Advisory, mirroring the Startable column in `definition list` — the engine
|
|
23
|
+
* itself does not refuse ({@link isStartableDefinition}), but the CLI has no
|
|
24
|
+
* way to supply the parent context a spawn-only child expects.
|
|
25
|
+
*/
|
|
26
|
+
export declare function startRefusal(definition: {
|
|
27
|
+
lifecycle?: WorkflowLifecycle | undefined;
|
|
28
|
+
}): string | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Type each `--field name=value` against the workflow's declared field
|
|
31
|
+
* entries — the engine takes typed {@link InitialFieldValue}s, and a value's
|
|
32
|
+
* type IS its declared entry's kind. Only `input`-sourced entries read
|
|
33
|
+
* caller values (the engine silently ignores the rest), so anything else
|
|
34
|
+
* fails here, naming the fields that ARE settable; value validation stays
|
|
35
|
+
* in the engine.
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildInitialFields({ declared, values, }: {
|
|
38
|
+
declared: Pick<FieldEntry, 'type' | 'name' | 'initialValue'>[];
|
|
39
|
+
values: Record<string, unknown>;
|
|
40
|
+
}): InitialFieldValue[];
|
|
41
|
+
/**
|
|
42
|
+
* The shared engine args ({@link baseEngineArgs}) plus the start-specific
|
|
43
|
+
* fields. Optionals spread conditionally because the engine's optional args
|
|
44
|
+
* reject an explicit `undefined` under `exactOptionalPropertyTypes`.
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildStartArgs({ scope, name, version, initialFields, actor, }: {
|
|
47
|
+
scope: EngineScope;
|
|
48
|
+
name: string;
|
|
49
|
+
version: number | undefined;
|
|
50
|
+
initialFields: InitialFieldValue[];
|
|
51
|
+
actor: Actor;
|
|
52
|
+
}): StartInstanceArgs & EngineScope;
|
|
53
|
+
/** The success spinner message. Pure so the in-flight / immediately-terminal
|
|
54
|
+
* permutations can be asserted without driving a real start. */
|
|
55
|
+
export declare function startReport(instance: Pick<WorkflowInstance, '_id' | 'currentStage' | 'completedAt'>): string;
|
|
56
|
+
/** The `--json` payload. Keys follow the CLI's `--json` vocabulary (matching
|
|
57
|
+
* `fire-action`): `instanceId` carries the document `_id`, `version` the
|
|
58
|
+
* pinned definition version; the rest mirror the instance fields. */
|
|
59
|
+
export declare function startResultJson(instance: Pick<WorkflowInstance, '_id' | 'definition' | 'pinnedVersion' | 'currentStage' | 'completedAt'>): Record<string, unknown>;
|