@sanity/workflow-cli 0.21.0 → 0.22.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 +14 -0
- package/dist/commands/editorial-workflows/definition/list.d.ts +16 -0
- package/dist/commands/editorial-workflows/definition/list.js +46 -16
- package/dist/commands/editorial-workflows/definition/show.d.ts +1 -0
- package/dist/commands/editorial-workflows/definition/show.js +6 -1
- package/dist/commands/editorial-workflows/deploy.d.ts +18 -0
- package/dist/commands/editorial-workflows/deploy.js +23 -9
- package/dist/commands/editorial-workflows/list.d.ts +1 -0
- package/dist/commands/editorial-workflows/list.js +15 -2
- package/dist/commands/editorial-workflows/reset-activity.d.ts +1 -0
- package/dist/commands/editorial-workflows/reset-activity.js +2 -2
- package/dist/commands/editorial-workflows/show.d.ts +1 -0
- package/dist/commands/editorial-workflows/show.js +16 -8
- package/dist/lib/context.d.ts +11 -37
- package/dist/lib/context.js +44 -40
- package/dist/lib/flags.d.ts +12 -7
- package/dist/lib/flags.js +2 -2
- package/dist/lib/read-fanout.d.ts +23 -0
- package/dist/lib/read-fanout.js +17 -1
- package/dist/lib/select-deployment.d.ts +0 -4
- package/dist/lib/select-deployment.js +1 -1
- package/dist/lib/ui.d.ts +7 -1
- package/dist/lib/ui.js +6 -2
- package/oclif.manifest.json +59 -19
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @sanity/workflow-cli
|
|
2
2
|
|
|
3
|
+
## 0.22.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1c6d4c0: Name the target deployment and resource on `deploy` progress and result lines. The spinner and completion line now read `Deploying N definition(s) to <name> (<tag>) → <resource>…` / `Processed N definition(s) · <name> (<tag>) → <resource>` (and the dry-run equivalents), so a single-deployment run — the common case, which prints no per-batch banner — always shows where the deploy landed.
|
|
8
|
+
- fb42001: The instance-keyed commands — `diagnose`, `fire-action`, `set-stage`, `abort`, `reset-activity` — now locate an instance by fanning out across every configured resource, the same way `show` and the other read commands already do. Passing an instance id is enough: on a config that spans several resources they no longer prompt for (or require) a `--deployment` / `--tag` selector — those stay as optional narrowers. As an optimization, an id carrying the engine's default `<tag>.` mint prefix probes that tag's resource first and skips the fan-out on a hit. `reset-activity` also gains the `--deployment` narrower its siblings already had.
|
|
9
|
+
- 84ff807: The read commands — `list`, `show`, `definition list`, `definition show` — now take `--json`, emitting structured output on stdout for parity with the verb commands. `show` and `definition show` print the fetched document as stored; `list` and `definition list` print their table's row view (each row annotated with the `resource` it came from) under a `truncated` flag. A JSON read spanning several resources is all-or-nothing: an unreadable resource fails the whole command rather than emitting a partial listing a script would mistake for the complete result.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- a8c460c: Colour the whole terminal-status row in the instance header — aborted rows render bold yellow, completed rows bold green — so a finished instance stands out from the neutral detail rows instead of only its timestamp carrying colour.
|
|
14
|
+
- Updated dependencies [88ba4ba]
|
|
15
|
+
- @sanity/workflow-engine@0.22.0
|
|
16
|
+
|
|
3
17
|
## 0.21.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
|
@@ -26,10 +26,26 @@ export default class DefinitionList extends WorkflowCommand {
|
|
|
26
26
|
static description: string;
|
|
27
27
|
static examples: string[];
|
|
28
28
|
static flags: {
|
|
29
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
29
30
|
limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
30
31
|
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
31
32
|
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
32
33
|
};
|
|
33
34
|
run(): Promise<void>;
|
|
34
35
|
}
|
|
36
|
+
/** Map a queried definition to its display row. Drops the stamp pair (gate
|
|
37
|
+
* plumbing, not view data) and `_id` — `tag.name.vN`, which just repeats
|
|
38
|
+
* name + tag + version, and no command takes an `_id` anyway (they take
|
|
39
|
+
* name + --tag/--version); `name vN` is this CLI's identifier format (the
|
|
40
|
+
* `definition show` title). */
|
|
41
|
+
export declare function definitionRow(r: DefinitionListRow): {
|
|
42
|
+
name: string;
|
|
43
|
+
version: number;
|
|
44
|
+
title: string;
|
|
45
|
+
tag: string;
|
|
46
|
+
stageCount: number;
|
|
47
|
+
inFlightCount: number;
|
|
48
|
+
totalInstances: number;
|
|
49
|
+
_createdAt: string;
|
|
50
|
+
};
|
|
35
51
|
export {};
|
|
@@ -3,8 +3,8 @@ import { WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, assertReadableModel,
|
|
|
3
3
|
import logSymbols from 'log-symbols';
|
|
4
4
|
import { WorkflowCommand } from "../../../lib/base-command.js";
|
|
5
5
|
import { resolveReadTargets } from "../../../lib/context.js";
|
|
6
|
-
import { tagFlags } from "../../../lib/flags.js";
|
|
7
|
-
import { runReadAcrossTargets } from "../../../lib/read-fanout.js";
|
|
6
|
+
import { jsonFlags, tagFlags } from "../../../lib/flags.js";
|
|
7
|
+
import { logJsonListing, runReadAcrossTargets } from "../../../lib/read-fanout.js";
|
|
8
8
|
import { logClippedTable } from "../../../lib/ui.js";
|
|
9
9
|
export function buildDefinitionListQuery(flags) {
|
|
10
10
|
const params = { limit: flags.limit + 1 };
|
|
@@ -45,6 +45,7 @@ export default class DefinitionList extends WorkflowCommand {
|
|
|
45
45
|
static examples = [
|
|
46
46
|
'<%= config.bin %> definition list',
|
|
47
47
|
'<%= config.bin %> definition list --tag prod',
|
|
48
|
+
'<%= config.bin %> definition list --json',
|
|
48
49
|
];
|
|
49
50
|
static flags = {
|
|
50
51
|
...tagFlags,
|
|
@@ -55,35 +56,48 @@ export default class DefinitionList extends WorkflowCommand {
|
|
|
55
56
|
name: Flags.string({
|
|
56
57
|
description: 'Filter to a single workflow definition name (e.g. product-launch).',
|
|
57
58
|
}),
|
|
59
|
+
...jsonFlags,
|
|
58
60
|
};
|
|
59
61
|
async run() {
|
|
60
62
|
const { flags } = await this.parse(DefinitionList);
|
|
61
63
|
const targets = await resolveReadTargets(flags);
|
|
62
64
|
const { groq, params } = buildDefinitionListQuery(flags);
|
|
65
|
+
if (flags.json) {
|
|
66
|
+
await logJsonListing({
|
|
67
|
+
targets,
|
|
68
|
+
key: 'definitions',
|
|
69
|
+
limit: flags.limit,
|
|
70
|
+
log: (line) => this.log(line),
|
|
71
|
+
fetch: ({ client }) => fetchDefinitionRows({ client, groq, params, limit: flags.limit }),
|
|
72
|
+
toRow: definitionRow,
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
63
76
|
const failures = await runReadAcrossTargets({
|
|
64
77
|
targets,
|
|
65
78
|
log: (line) => this.log(line),
|
|
66
79
|
run: async ({ client }) => {
|
|
67
|
-
const fetched =
|
|
68
|
-
|
|
69
|
-
})).map(assertReadableModel);
|
|
70
|
-
if (!fetched.length) {
|
|
80
|
+
const fetched = await fetchDefinitionRows({ client, groq, params, limit: flags.limit });
|
|
81
|
+
if (!fetched.rows.length) {
|
|
71
82
|
this.log(`${logSymbols.info} no definitions found`);
|
|
72
83
|
return;
|
|
73
84
|
}
|
|
74
85
|
logClippedTable({
|
|
75
|
-
rows: fetched,
|
|
86
|
+
rows: fetched.rows,
|
|
76
87
|
limit: flags.limit,
|
|
77
88
|
headers: ['workflow', 'title', 'tag', 'stages', 'in flight', 'instances', 'created'],
|
|
78
|
-
toCells: (r) =>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
toCells: (r) => {
|
|
90
|
+
const row = definitionRow(r);
|
|
91
|
+
return [
|
|
92
|
+
`${row.name} v${row.version}`,
|
|
93
|
+
row.title,
|
|
94
|
+
row.tag,
|
|
95
|
+
String(row.stageCount),
|
|
96
|
+
String(row.inFlightCount),
|
|
97
|
+
String(row.totalInstances),
|
|
98
|
+
row._createdAt,
|
|
99
|
+
];
|
|
100
|
+
},
|
|
87
101
|
log: (line) => this.log(line),
|
|
88
102
|
});
|
|
89
103
|
},
|
|
@@ -93,3 +107,19 @@ export default class DefinitionList extends WorkflowCommand {
|
|
|
93
107
|
}
|
|
94
108
|
}
|
|
95
109
|
}
|
|
110
|
+
export function definitionRow(r) {
|
|
111
|
+
return {
|
|
112
|
+
name: r.name,
|
|
113
|
+
version: r.version,
|
|
114
|
+
title: r.title,
|
|
115
|
+
tag: r.tag,
|
|
116
|
+
stageCount: r.stageCount,
|
|
117
|
+
inFlightCount: r.inFlightCount,
|
|
118
|
+
totalInstances: r.totalInstances,
|
|
119
|
+
_createdAt: r._createdAt,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async function fetchDefinitionRows({ client, groq, params, limit, }) {
|
|
123
|
+
const rows = await client.fetch(groq, params, { tag: 'definition.list' });
|
|
124
|
+
return { rows: rows.map(assertReadableModel), hasMore: rows.length > limit };
|
|
125
|
+
}
|
|
@@ -8,6 +8,7 @@ export default class DefinitionShow extends WorkflowCommand {
|
|
|
8
8
|
name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
9
9
|
};
|
|
10
10
|
static flags: {
|
|
11
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
12
|
version: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
14
|
};
|
|
@@ -6,7 +6,7 @@ import { WorkflowCommand } from "../../../lib/base-command.js";
|
|
|
6
6
|
import { resolveReadTargets, soleHitOrFail } from "../../../lib/context.js";
|
|
7
7
|
import { buildDefinitionShowQuery, buildDefinitionTagsQuery } from "../../../lib/definitions.js";
|
|
8
8
|
import { fail } from "../../../lib/fail.js";
|
|
9
|
-
import { tagFlags } from "../../../lib/flags.js";
|
|
9
|
+
import { jsonFlags, tagFlags } from "../../../lib/flags.js";
|
|
10
10
|
import { formatKeyValue, resourceLabel, sectionHeader } from "../../../lib/ui.js";
|
|
11
11
|
export default class DefinitionShow extends WorkflowCommand {
|
|
12
12
|
static aliases = ['definition:show'];
|
|
@@ -17,6 +17,7 @@ export default class DefinitionShow extends WorkflowCommand {
|
|
|
17
17
|
static flags = {
|
|
18
18
|
...tagFlags,
|
|
19
19
|
version: Flags.integer({ description: 'Specific version (default: latest).' }),
|
|
20
|
+
...jsonFlags,
|
|
20
21
|
};
|
|
21
22
|
async run() {
|
|
22
23
|
const { args, flags } = await this.parse(DefinitionShow);
|
|
@@ -32,6 +33,10 @@ export default class DefinitionShow extends WorkflowCommand {
|
|
|
32
33
|
exit: 1,
|
|
33
34
|
});
|
|
34
35
|
}
|
|
36
|
+
if (flags.json) {
|
|
37
|
+
this.log(JSON.stringify(def, null, 2));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
35
40
|
this.log(definitionHeader(def, def.tag));
|
|
36
41
|
this.log('');
|
|
37
42
|
for (const line of describeDefinition(def)) {
|
|
@@ -77,6 +77,24 @@ export declare function deploymentBanner({ deployment, index, total, }: {
|
|
|
77
77
|
index: number;
|
|
78
78
|
total: number;
|
|
79
79
|
}): string | undefined;
|
|
80
|
+
/** The deploy target as one label — the deployment's friendly name/tag
|
|
81
|
+
* ({@link deploymentLabel}) and the resource it writes to
|
|
82
|
+
* ({@link resourceLabel}) — so every progress and result line names where a
|
|
83
|
+
* deploy landed, including the common single-deployment run that prints no
|
|
84
|
+
* banner. */
|
|
85
|
+
export declare function targetLabel(deployment: WorkflowDeployment): string;
|
|
86
|
+
/** The deploy spinner's progress line — names the full target it writes to. */
|
|
87
|
+
export declare function deployStartLine(count: number, where: string): string;
|
|
88
|
+
/** The dry-run spinner's progress line — a diff reads the resource's deployed
|
|
89
|
+
* state, so it names the resource it compares against, not the deployment. */
|
|
90
|
+
export declare function dryRunStartLine(count: number, resource: string): string;
|
|
91
|
+
/** A completed deploy/dry-run result line: the past-tense verb, the count, and
|
|
92
|
+
* the full target, so a finished run always shows where it landed. */
|
|
93
|
+
export declare function resultLine({ verb, count, where, }: {
|
|
94
|
+
verb: 'Processed' | 'Diffed';
|
|
95
|
+
count: number;
|
|
96
|
+
where: string;
|
|
97
|
+
}): string;
|
|
80
98
|
/** One line per deployed definition, symbol-tagged by its result status,
|
|
81
99
|
* followed by any advisory effect-output lint warnings indented beneath it. */
|
|
82
100
|
export declare function deployResultLines(results: DeployDefinitionResult[]): string[];
|
|
@@ -13,7 +13,7 @@ import { loadWorkflowConfig } from "../../lib/load-config.js";
|
|
|
13
13
|
import { deploymentLabel, deploymentToTarget, selectDeployments, } from "../../lib/select-deployment.js";
|
|
14
14
|
import { shareDefinitionsAfterDeploy } from "../../lib/share-definitions.js";
|
|
15
15
|
import { cliTelemetry } from "../../lib/telemetry.js";
|
|
16
|
-
import { groupBanner } from "../../lib/ui.js";
|
|
16
|
+
import { groupBanner, resourceLabel } from "../../lib/ui.js";
|
|
17
17
|
export default class Deploy extends WorkflowCommand {
|
|
18
18
|
static aliases = ['deploy'];
|
|
19
19
|
static description = 'Validate, diff, and deploy workflow definitions to the resource bound by the selected deployment.';
|
|
@@ -110,14 +110,16 @@ export default class Deploy extends WorkflowCommand {
|
|
|
110
110
|
}
|
|
111
111
|
async processDeployment({ deployment, defs, dryRun, client, }) {
|
|
112
112
|
const target = deploymentToTarget(deployment);
|
|
113
|
+
const where = targetLabel(deployment);
|
|
114
|
+
const resource = resourceLabel(deployment.workflowResource);
|
|
113
115
|
if (dryRun) {
|
|
114
|
-
await this.runDryRun({ client, defs, target });
|
|
116
|
+
await this.runDryRun({ client, defs, target, where, resource });
|
|
115
117
|
return undefined;
|
|
116
118
|
}
|
|
117
|
-
return this.executeDeploy({ client, defs, target });
|
|
119
|
+
return this.executeDeploy({ client, defs, target, where });
|
|
118
120
|
}
|
|
119
|
-
async executeDeploy({ client, defs, target, }) {
|
|
120
|
-
const spinner = ora(
|
|
121
|
+
async executeDeploy({ client, defs, target, where, }) {
|
|
122
|
+
const spinner = ora(deployStartLine(defs.length, where)).start();
|
|
121
123
|
try {
|
|
122
124
|
const deployed = await workflow.deployDefinitions({
|
|
123
125
|
...target,
|
|
@@ -125,7 +127,7 @@ export default class Deploy extends WorkflowCommand {
|
|
|
125
127
|
definitions: defs,
|
|
126
128
|
telemetry: cliTelemetry().logger,
|
|
127
129
|
});
|
|
128
|
-
spinner.succeed(
|
|
130
|
+
spinner.succeed(resultLine({ verb: 'Processed', count: deployed.results.length, where }));
|
|
129
131
|
for (const line of deployResultLines(deployed.results))
|
|
130
132
|
this.log(line);
|
|
131
133
|
return deployed;
|
|
@@ -135,11 +137,11 @@ export default class Deploy extends WorkflowCommand {
|
|
|
135
137
|
throw error;
|
|
136
138
|
}
|
|
137
139
|
}
|
|
138
|
-
async runDryRun({ client, defs, target, }) {
|
|
139
|
-
const spinner = ora(
|
|
140
|
+
async runDryRun({ client, defs, target, where, resource, }) {
|
|
141
|
+
const spinner = ora(dryRunStartLine(defs.length, resource)).start();
|
|
140
142
|
try {
|
|
141
143
|
const entries = await computeDiffEntries({ client, defs, target });
|
|
142
|
-
spinner.succeed(
|
|
144
|
+
spinner.succeed(resultLine({ verb: 'Diffed', count: defs.length, where }));
|
|
143
145
|
for (const line of diffReport(entries))
|
|
144
146
|
this.log(line);
|
|
145
147
|
}
|
|
@@ -194,6 +196,18 @@ export function deploySummary({ total, failures, }) {
|
|
|
194
196
|
export function deploymentBanner({ deployment, index, total, }) {
|
|
195
197
|
return groupBanner({ label: deploymentLabel(deployment), index, total });
|
|
196
198
|
}
|
|
199
|
+
export function targetLabel(deployment) {
|
|
200
|
+
return `${deploymentLabel(deployment)} → ${resourceLabel(deployment.workflowResource)}`;
|
|
201
|
+
}
|
|
202
|
+
export function deployStartLine(count, where) {
|
|
203
|
+
return `Deploying ${count} definition(s) to ${where}…`;
|
|
204
|
+
}
|
|
205
|
+
export function dryRunStartLine(count, resource) {
|
|
206
|
+
return `Diffing ${count} definition(s) against ${resource}…`;
|
|
207
|
+
}
|
|
208
|
+
export function resultLine({ verb, count, where, }) {
|
|
209
|
+
return `${verb} ${count} definition(s) · ${where}`;
|
|
210
|
+
}
|
|
197
211
|
export function deployResultLines(results) {
|
|
198
212
|
const symbols = {
|
|
199
213
|
created: logSymbols.success,
|
|
@@ -51,6 +51,7 @@ export default class List extends WorkflowCommand {
|
|
|
51
51
|
static description: string;
|
|
52
52
|
static examples: string[];
|
|
53
53
|
static flags: {
|
|
54
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
54
55
|
'include-completed': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
55
56
|
failed: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
56
57
|
definition: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -4,8 +4,8 @@ import logSymbols from 'log-symbols';
|
|
|
4
4
|
import { WorkflowCommand } from "../../lib/base-command.js";
|
|
5
5
|
import { resolveReadTargets } from "../../lib/context.js";
|
|
6
6
|
import { failOnThrow } from "../../lib/fail.js";
|
|
7
|
-
import { tagFlags } from "../../lib/flags.js";
|
|
8
|
-
import { runReadAcrossTargets } from "../../lib/read-fanout.js";
|
|
7
|
+
import { jsonFlags, tagFlags } from "../../lib/flags.js";
|
|
8
|
+
import { logJsonListing, runReadAcrossTargets } from "../../lib/read-fanout.js";
|
|
9
9
|
import { formatAge, logClippedTable } from "../../lib/ui.js";
|
|
10
10
|
const DOCUMENT_LIST_PROJECTION = `{
|
|
11
11
|
_type,
|
|
@@ -85,6 +85,7 @@ export default class List extends WorkflowCommand {
|
|
|
85
85
|
'<%= config.bin %> list --definition productLaunch',
|
|
86
86
|
'<%= config.bin %> list --document dataset:proj:ds:article-1',
|
|
87
87
|
'<%= config.bin %> list --tag prod',
|
|
88
|
+
'<%= config.bin %> list --json',
|
|
88
89
|
];
|
|
89
90
|
static flags = {
|
|
90
91
|
...tagFlags,
|
|
@@ -107,12 +108,24 @@ export default class List extends WorkflowCommand {
|
|
|
107
108
|
description: 'Maximum rows to return.',
|
|
108
109
|
default: 50,
|
|
109
110
|
}),
|
|
111
|
+
...jsonFlags,
|
|
110
112
|
};
|
|
111
113
|
async run() {
|
|
112
114
|
const { flags } = await this.parse(List);
|
|
113
115
|
const targets = await resolveReadTargets(flags);
|
|
114
116
|
const { groq, params } = failOnThrow('Invalid --document:', () => buildListQuery(flags));
|
|
115
117
|
const document = flags.document;
|
|
118
|
+
if (flags.json) {
|
|
119
|
+
await logJsonListing({
|
|
120
|
+
targets,
|
|
121
|
+
key: 'instances',
|
|
122
|
+
limit: flags.limit,
|
|
123
|
+
log: (line) => this.log(line),
|
|
124
|
+
fetch: ({ client }) => fetchRows({ client, groq, params, document, limit: flags.limit }),
|
|
125
|
+
toRow: instanceRow,
|
|
126
|
+
});
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
116
129
|
const failures = await runReadAcrossTargets({
|
|
117
130
|
targets,
|
|
118
131
|
log: (line) => this.log(line),
|
|
@@ -11,6 +11,7 @@ export default class ResetActivity extends WorkflowCommand {
|
|
|
11
11
|
};
|
|
12
12
|
static flags: {
|
|
13
13
|
skip: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
deployment: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
15
|
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
16
|
};
|
|
16
17
|
run(): Promise<void>;
|
|
@@ -3,7 +3,7 @@ import { Args, Flags } from '@oclif/core';
|
|
|
3
3
|
import { workflow } from '@sanity/workflow-engine';
|
|
4
4
|
import { WorkflowCommand } from "../../lib/base-command.js";
|
|
5
5
|
import { resolveInstanceContext } from "../../lib/context.js";
|
|
6
|
-
import {
|
|
6
|
+
import { instanceFlags } from "../../lib/flags.js";
|
|
7
7
|
import { buildOperationArgs } from "../../lib/operation-args.js";
|
|
8
8
|
import { cascadeTail, runWriteVerb, } from "../../lib/ops-report.js";
|
|
9
9
|
export default class ResetActivity extends WorkflowCommand {
|
|
@@ -18,7 +18,7 @@ export default class ResetActivity extends WorkflowCommand {
|
|
|
18
18
|
activity: Args.string({ required: true, description: 'Activity name within the current stage.' }),
|
|
19
19
|
};
|
|
20
20
|
static flags = {
|
|
21
|
-
...
|
|
21
|
+
...instanceFlags,
|
|
22
22
|
skip: Flags.boolean({
|
|
23
23
|
default: false,
|
|
24
24
|
description: 'Bypass the activity (mark it skipped) instead of re-running it (back to active).',
|
|
@@ -8,6 +8,7 @@ export default class Show extends WorkflowCommand {
|
|
|
8
8
|
instanceId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
9
9
|
};
|
|
10
10
|
static flags: {
|
|
11
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
12
|
include: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
14
|
};
|
|
@@ -4,7 +4,7 @@ import { abortReason, displayTitle, terminalState, } from '@sanity/workflow-engi
|
|
|
4
4
|
import logSymbols from 'log-symbols';
|
|
5
5
|
import { WorkflowCommand } from "../../lib/base-command.js";
|
|
6
6
|
import { findInstance, resolveReadTargets } from "../../lib/context.js";
|
|
7
|
-
import { tagFlags } from "../../lib/flags.js";
|
|
7
|
+
import { jsonFlags, tagFlags } from "../../lib/flags.js";
|
|
8
8
|
import { formatKeyValue, formatTimestamp, sectionHeader, activityIcon } from "../../lib/ui.js";
|
|
9
9
|
export default class Show extends WorkflowCommand {
|
|
10
10
|
static aliases = ['show'];
|
|
@@ -12,6 +12,7 @@ export default class Show extends WorkflowCommand {
|
|
|
12
12
|
static examples = [
|
|
13
13
|
'<%= config.bin %> show wf-instance.abc123',
|
|
14
14
|
'<%= config.bin %> show wf-instance.abc123 --include history',
|
|
15
|
+
'<%= config.bin %> show wf-instance.abc123 --json',
|
|
15
16
|
];
|
|
16
17
|
static args = {
|
|
17
18
|
instanceId: Args.string({
|
|
@@ -22,11 +23,12 @@ export default class Show extends WorkflowCommand {
|
|
|
22
23
|
static flags = {
|
|
23
24
|
...tagFlags,
|
|
24
25
|
include: Flags.string({
|
|
25
|
-
description: 'Optional sections to include in output.',
|
|
26
|
+
description: 'Optional sections to include in rendered output (--json always carries the full document).',
|
|
26
27
|
options: ['history'],
|
|
27
28
|
multiple: true,
|
|
28
29
|
default: [],
|
|
29
30
|
}),
|
|
31
|
+
...jsonFlags,
|
|
30
32
|
};
|
|
31
33
|
async run() {
|
|
32
34
|
const { args, flags } = await this.parse(Show);
|
|
@@ -36,6 +38,10 @@ export default class Show extends WorkflowCommand {
|
|
|
36
38
|
this.error(`${logSymbols.error} no instance with id "${args.instanceId}"`, { exit: 1 });
|
|
37
39
|
}
|
|
38
40
|
const { instance } = hit;
|
|
41
|
+
if (flags.json) {
|
|
42
|
+
this.log(JSON.stringify(instance, null, 2));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
39
45
|
this.log(instanceHeader(instance));
|
|
40
46
|
this.log('');
|
|
41
47
|
for (const line of describeInstance(instance, {
|
|
@@ -55,14 +61,16 @@ export function instanceHeader(instance) {
|
|
|
55
61
|
if (terminalState(instance) === 'aborted' && instance.abortedAt !== undefined) {
|
|
56
62
|
const reason = abortReason(instance);
|
|
57
63
|
const tail = reason ? ` — ${reason}` : '';
|
|
58
|
-
const value =
|
|
59
|
-
rows.push(formatKeyValue({ key: 'Aborted', value, padTo: HEADER_PAD }));
|
|
64
|
+
const value = `${formatTimestamp(instance.abortedAt)}${tail}`;
|
|
65
|
+
rows.push(formatKeyValue({ key: 'Aborted', value, padTo: HEADER_PAD, emphasis: 'yellow' }));
|
|
66
|
+
}
|
|
67
|
+
else if (instance.completedAt) {
|
|
68
|
+
const value = formatTimestamp(instance.completedAt);
|
|
69
|
+
rows.push(formatKeyValue({ key: 'Completed', value, padTo: HEADER_PAD, emphasis: 'green' }));
|
|
60
70
|
}
|
|
61
71
|
else {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
: styleText('dim', '—');
|
|
65
|
-
rows.push(formatKeyValue({ key: 'Completed', value: completed, padTo: HEADER_PAD }));
|
|
72
|
+
const value = styleText('dim', '—');
|
|
73
|
+
rows.push(formatKeyValue({ key: 'Completed', value, padTo: HEADER_PAD }));
|
|
66
74
|
}
|
|
67
75
|
rows.push(formatKeyValue({ key: 'Tag', value: instance.tag, padTo: HEADER_PAD }));
|
|
68
76
|
return [title, ...rows].join('\n');
|
package/dist/lib/context.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { SanityClient } from '@sanity/client';
|
|
2
2
|
import { type WorkflowConfig, type WorkflowDeployment, type WorkflowInstance, type WorkflowResource } from '@sanity/workflow-engine';
|
|
3
3
|
import type { EngineScope } from './operation-args.ts';
|
|
4
|
-
import { type ChooseDeploymentName } from './select-deployment.ts';
|
|
5
4
|
export interface DeploymentContext {
|
|
6
5
|
deployment: WorkflowDeployment;
|
|
7
6
|
client: SanityClient;
|
|
@@ -51,20 +50,18 @@ export declare function dedupeResources(resources: WorkflowResource[]): Workflow
|
|
|
51
50
|
* tag (tags may repeat across deployments), or — untagged — every distinct
|
|
52
51
|
* one the config's deployments mention ({@link dedupeResources}). */
|
|
53
52
|
export declare function resolveReadResources(config: WorkflowConfig, tag: string | undefined): WorkflowResource[];
|
|
54
|
-
/** The single resource an instance-keyed command reads from when it isn't
|
|
55
|
-
* disambiguated interactively: the sole resource, the `--tag`-narrowed one, or
|
|
56
|
-
* a `fail` asking for `--deployment`/`--tag` when the config spans several. The
|
|
57
|
-
* interactive picker lives in {@link resolveInstanceResource}; listings fan out
|
|
58
|
-
* via {@link resolveReadResources} instead. */
|
|
59
|
-
export declare function resolveReadResource(config: WorkflowConfig, tag: string | undefined): WorkflowResource;
|
|
60
53
|
/**
|
|
61
|
-
* The resolution an instance-id-targeted command shares, read or write:
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
54
|
+
* The resolution an instance-id-targeted command shares, read or write: locate
|
|
55
|
+
* the instance by the same cross-resource fan-out the read/list commands use
|
|
56
|
+
* ({@link findInstance}), then hand back an authenticated client for the
|
|
57
|
+
* resource that held it plus a scope whose `tag` comes from the instance
|
|
58
|
+
* itself — never from the config's declared deployments. An instance id is
|
|
59
|
+
* globally unique and carries its own `tag`, so naming one is enough to act on
|
|
60
|
+
* it: the config only locates candidate resources, and the fan-out's
|
|
61
|
+
* {@link soleHitOrFail} rejects the collision case where several datasets hold
|
|
62
|
+
* the id (the `<tag>.` fast-path may skip that full-set check — safe, ids are
|
|
63
|
+
* collision-free). `--deployment` / `--tag` stay optional narrowers, not
|
|
64
|
+
* required disambiguators.
|
|
68
65
|
*
|
|
69
66
|
* Contrast {@link resolveContext}: the deploy/diff/delete path IS scoped to a
|
|
70
67
|
* declared deployment because it acts on the authored definition set (or a
|
|
@@ -74,29 +71,6 @@ export declare function resolveInstanceContext(flags: {
|
|
|
74
71
|
deployment?: string | undefined;
|
|
75
72
|
tag?: string | undefined;
|
|
76
73
|
}, instanceId: string): Promise<InstanceContext>;
|
|
77
|
-
/**
|
|
78
|
-
* Which resource an instance-keyed command reads from: the one `--deployment`
|
|
79
|
-
* names (resolved by its unique deployment name); otherwise, when the config
|
|
80
|
-
* spans several resources, an interactive terminal is prompted to pick a
|
|
81
|
-
* deployment ({@link canPromptOnStderr}) — its resource is the read source, the
|
|
82
|
-
* interactive counterpart to `--deployment`. A run that can't prompt falls to
|
|
83
|
-
* the sole or `--tag`-narrowed resource, or the ambiguity error
|
|
84
|
-
* ({@link resolveReadResource}). The instance id is globally unique, so this
|
|
85
|
-
* only locates the resource to look in — never the instance's `tag` partition,
|
|
86
|
-
* which the caller takes from the loaded instance. The shared path behind all
|
|
87
|
-
* four instance-keyed commands.
|
|
88
|
-
*/
|
|
89
|
-
export declare function resolveInstanceResource(config: WorkflowConfig, { deployment, tag, interactive, chooseDeployment, }: {
|
|
90
|
-
deployment?: string | undefined;
|
|
91
|
-
tag?: string | undefined;
|
|
92
|
-
interactive?: boolean | undefined;
|
|
93
|
-
chooseDeployment?: ChooseDeploymentName | undefined;
|
|
94
|
-
}): Promise<WorkflowResource>;
|
|
95
|
-
/** Fetch an instance by id, exiting cleanly when the resource has no such
|
|
96
|
-
* document — the diagnostic an operator sees on a mistyped id. Split out from
|
|
97
|
-
* {@link resolveInstanceContext} so the not-found path is unit-testable
|
|
98
|
-
* without a live config/token/client. */
|
|
99
|
-
export declare function loadInstanceOrFail(client: Pick<SanityClient, 'getDocument'>, instanceId: string): Promise<WorkflowInstance>;
|
|
100
74
|
/** A read target that turned out to hold the instance being looked up. */
|
|
101
75
|
export interface InstanceHit extends ReadTarget {
|
|
102
76
|
instance: WorkflowInstance;
|
package/dist/lib/context.js
CHANGED
|
@@ -2,19 +2,20 @@ import { assertReadableModel, resourceGdr, } from '@sanity/workflow-engine';
|
|
|
2
2
|
import { clientFor, resolveTokenOrFail } from "./client.js";
|
|
3
3
|
import { fail } from "./fail.js";
|
|
4
4
|
import { loadWorkflowConfig } from "./load-config.js";
|
|
5
|
-
import {
|
|
6
|
-
import { availableDeployments, deploymentsForTag, selectDeployment, } from "./select-deployment.js";
|
|
5
|
+
import { deploymentsForTag, selectDeployment } from "./select-deployment.js";
|
|
7
6
|
import { resourceLabel } from "./ui.js";
|
|
8
7
|
export async function resolveContext(flags) {
|
|
9
8
|
const config = await loadWorkflowConfig();
|
|
10
9
|
const deployment = await selectDeployment(config, { name: flags.deployment, tag: flags.tag });
|
|
11
10
|
return { deployment, client: clientFor(deployment.workflowResource, await resolveTokenOrFail()) };
|
|
12
11
|
}
|
|
12
|
+
function targetsFor(resources, token) {
|
|
13
|
+
return resources.map((resource) => ({ resource, client: clientFor(resource, token) }));
|
|
14
|
+
}
|
|
13
15
|
export async function resolveReadTargets(flags) {
|
|
14
16
|
const config = await loadWorkflowConfig();
|
|
15
17
|
const resources = resolveReadResources(config, flags.tag);
|
|
16
|
-
|
|
17
|
-
return resources.map((resource) => ({ resource, client: clientFor(resource, token) }));
|
|
18
|
+
return targetsFor(resources, await resolveTokenOrFail());
|
|
18
19
|
}
|
|
19
20
|
export function dedupeResources(resources) {
|
|
20
21
|
return [...new Map(resources.map((resource) => [resourceGdr(resource), resource])).values()];
|
|
@@ -29,50 +30,53 @@ export function resolveReadResources(config, tag) {
|
|
|
29
30
|
}
|
|
30
31
|
return resources;
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const LOCATE_TAG = 'instance.load';
|
|
34
|
+
export async function resolveInstanceContext(flags, instanceId) {
|
|
35
|
+
const config = await loadWorkflowConfig();
|
|
36
|
+
const token = await resolveTokenOrFail();
|
|
37
|
+
const hit = await locateInstance({ config, flags, instanceId, token });
|
|
38
|
+
if (hit === undefined) {
|
|
39
|
+
fail(`Workflow instance ${instanceId} not found`);
|
|
37
40
|
}
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
return { client: hit.client, scope: { tag: hit.instance.tag, workflowResource: hit.resource } };
|
|
42
|
+
}
|
|
43
|
+
async function locateInstance({ config, flags, instanceId, token, }) {
|
|
44
|
+
const resources = await instanceCandidateResources(config, flags);
|
|
45
|
+
const fanOut = () => findInstance({ targets: targetsFor(resources, token), instanceId, requestTag: LOCATE_TAG });
|
|
46
|
+
const explicit = flags.deployment !== undefined || flags.tag !== undefined;
|
|
47
|
+
const hinted = explicit || resources.length <= 1
|
|
48
|
+
? []
|
|
49
|
+
: hintedResources({ config, instanceId, candidates: resources });
|
|
50
|
+
if (hinted.length === 0 || hinted.length === resources.length) {
|
|
51
|
+
return fanOut();
|
|
40
52
|
}
|
|
41
|
-
const
|
|
42
|
-
|
|
53
|
+
const hit = await hintedProbe(targetsFor(hinted, token), instanceId);
|
|
54
|
+
return hit ?? fanOut();
|
|
43
55
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
async function hintedProbe(targets, instanceId) {
|
|
57
|
+
try {
|
|
58
|
+
return await findInstance({ targets, instanceId, requestTag: LOCATE_TAG });
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
50
63
|
}
|
|
51
|
-
|
|
64
|
+
async function instanceCandidateResources(config, { deployment, tag }) {
|
|
52
65
|
if (deployment !== undefined) {
|
|
53
|
-
return (await selectDeployment(config, { name: deployment, tag: undefined })).workflowResource;
|
|
54
|
-
}
|
|
55
|
-
if (tag === undefined &&
|
|
56
|
-
(interactive ?? canPromptOnStderr()) &&
|
|
57
|
-
resolveReadResources(config, undefined).length > 1) {
|
|
58
|
-
const chosen = await selectDeployment(config, {
|
|
59
|
-
name: undefined,
|
|
60
|
-
tag: undefined,
|
|
61
|
-
interactive: true,
|
|
62
|
-
chooseName: chooseDeployment,
|
|
63
|
-
});
|
|
64
|
-
return chosen.workflowResource;
|
|
66
|
+
return [(await selectDeployment(config, { name: deployment, tag: undefined })).workflowResource];
|
|
65
67
|
}
|
|
66
|
-
return
|
|
68
|
+
return resolveReadResources(config, tag);
|
|
67
69
|
}
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (!instance) {
|
|
73
|
-
fail(`Workflow instance ${instanceId} not found`);
|
|
70
|
+
function hintedResources({ config, instanceId, candidates, }) {
|
|
71
|
+
const dot = instanceId.indexOf('.');
|
|
72
|
+
if (dot <= 0) {
|
|
73
|
+
return [];
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
const prefix = instanceId.slice(0, dot);
|
|
76
|
+
const taggedGdrs = new Set(config.deployments
|
|
77
|
+
.filter((deployment) => deployment.tag === prefix)
|
|
78
|
+
.map((deployment) => resourceGdr(deployment.workflowResource)));
|
|
79
|
+
return candidates.filter((resource) => taggedGdrs.has(resourceGdr(resource)));
|
|
76
80
|
}
|
|
77
81
|
export function soleHitOrFail(hits, subject) {
|
|
78
82
|
const [sole, ...rest] = hits;
|
package/dist/lib/flags.d.ts
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
export declare const tagFlags: {
|
|
8
8
|
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
};
|
|
10
|
-
/** The
|
|
11
|
-
* `fire-action`) takes.
|
|
12
|
-
*
|
|
13
|
-
* instance
|
|
14
|
-
*
|
|
15
|
-
* `--tag`
|
|
16
|
-
*
|
|
10
|
+
/** The optional narrowers an instance-keyed command (`diagnose`, `abort`,
|
|
11
|
+
* `set-stage`, `fire-action`, `reset-activity`) takes. The command locates the
|
|
12
|
+
* instance by fanning out across every configured resource, so neither is ever
|
|
13
|
+
* required — an instance id is globally unique and carries its own `tag`. They
|
|
14
|
+
* only narrow WHERE to look: `--deployment` to the resource one deployment
|
|
15
|
+
* targets, `--tag` to the resources under that tag. The instance's partition
|
|
16
|
+
* always comes from the loaded instance, never from these. Mutually exclusive. */
|
|
17
17
|
export declare const instanceFlags: {
|
|
18
18
|
deployment: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
19
|
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -28,6 +28,11 @@ export declare const deploymentFlags: {
|
|
|
28
28
|
deployment: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
29
29
|
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
30
30
|
};
|
|
31
|
+
/** Structured stdout instead of the rendered view. One payload convention:
|
|
32
|
+
* verb commands emit their operation envelope (`instanceId` carries the doc
|
|
33
|
+
* `_id`); read commands emit document-shaped data — `show` commands print
|
|
34
|
+
* the fetched document as stored, list commands print the table's row view
|
|
35
|
+
* (plus each row's `resource`) under a `truncated` flag. */
|
|
31
36
|
export declare const jsonFlags: {
|
|
32
37
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
33
38
|
};
|
package/dist/lib/flags.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
|
-
const tagAsFilterDescription = 'Workflow environment tag (e.g. prod, test) — an optional query filter
|
|
2
|
+
const tagAsFilterDescription = 'Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.';
|
|
3
3
|
export const tagFlags = {
|
|
4
4
|
tag: Flags.string({ description: tagAsFilterDescription }),
|
|
5
5
|
};
|
|
6
6
|
export const instanceFlags = {
|
|
7
7
|
deployment: Flags.string({
|
|
8
|
-
description: 'Deployment name —
|
|
8
|
+
description: 'Deployment name — narrow the instance search to the resource that deployment targets; the tag partition still comes from the loaded instance.',
|
|
9
9
|
exclusive: ['tag'],
|
|
10
10
|
}),
|
|
11
11
|
tag: Flags.string({ description: tagAsFilterDescription, exclusive: ['deployment'] }),
|
|
@@ -6,6 +6,29 @@ export interface ReadFailure {
|
|
|
6
6
|
resource: WorkflowResource;
|
|
7
7
|
message: string;
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* The `--json` tail of a list command — the structured counterpart of
|
|
11
|
+
* {@link runReadAcrossTargets} + the clipped table: fetch every target's
|
|
12
|
+
* rows, {@link clipToLimit} each target's page, map rows through the
|
|
13
|
+
* command's row view (`toRow`, the structured analogue of the table's
|
|
14
|
+
* `toCells`), annotate each with the resource it came from (the analogue of
|
|
15
|
+
* the banner), and log one `{<key>: rows, truncated}` payload.
|
|
16
|
+
* All-or-nothing, unlike the human path's per-resource tolerance: a script
|
|
17
|
+
* would mistake a listing that silently omitted an unreadable resource for
|
|
18
|
+
* the complete result, so any failure rejects the whole read.
|
|
19
|
+
*/
|
|
20
|
+
export declare function logJsonListing<T, R>({ targets, fetch, toRow, key, limit, log, }: {
|
|
21
|
+
targets: ReadTarget[];
|
|
22
|
+
fetch: (target: ReadTarget) => Promise<{
|
|
23
|
+
rows: T[];
|
|
24
|
+
hasMore: boolean;
|
|
25
|
+
}>;
|
|
26
|
+
toRow: (row: T) => R;
|
|
27
|
+
/** The payload's row key — names what the rows are (`instances`, `definitions`). */
|
|
28
|
+
key: string;
|
|
29
|
+
limit: number;
|
|
30
|
+
log: (line: string) => void;
|
|
31
|
+
}): Promise<void>;
|
|
9
32
|
/**
|
|
10
33
|
* Run a read over every target, grouping output per resource: a banner when
|
|
11
34
|
* the read spans several, then whatever `run` logs for that target. A throw
|
package/dist/lib/read-fanout.js
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import logSymbols from 'log-symbols';
|
|
2
2
|
import { failureDetail } from "./fail.js";
|
|
3
|
-
import { groupBanner, resourceLabel } from "./ui.js";
|
|
3
|
+
import { clipToLimit, groupBanner, resourceLabel } from "./ui.js";
|
|
4
|
+
export async function logJsonListing({ targets, fetch, toRow, key, limit, log, }) {
|
|
5
|
+
const fetched = await Promise.all(targets.map(async (target) => {
|
|
6
|
+
const { rows, hasMore } = await fetch(target);
|
|
7
|
+
return {
|
|
8
|
+
rows: clipToLimit(rows, limit).rows.map((row) => ({
|
|
9
|
+
...toRow(row),
|
|
10
|
+
resource: target.resource,
|
|
11
|
+
})),
|
|
12
|
+
hasMore: hasMore || rows.length > limit,
|
|
13
|
+
};
|
|
14
|
+
}));
|
|
15
|
+
log(JSON.stringify({
|
|
16
|
+
[key]: fetched.flatMap((result) => result.rows),
|
|
17
|
+
truncated: fetched.some((result) => result.hasMore),
|
|
18
|
+
}, null, 2));
|
|
19
|
+
}
|
|
4
20
|
export async function runReadAcrossTargets({ targets, log, run, }) {
|
|
5
21
|
const failures = [];
|
|
6
22
|
for (const [index, target] of targets.entries()) {
|
|
@@ -48,10 +48,6 @@ export declare function deploymentsForTag(config: WorkflowConfig, tag: string):
|
|
|
48
48
|
* explicit opt-in — never a default.
|
|
49
49
|
*/
|
|
50
50
|
export declare function selectDeployments(config: WorkflowConfig, { name, tag, allTags, interactive, chooseName }: DeploymentSelectionOptions): Promise<WorkflowDeployment[]>;
|
|
51
|
-
/** The "Available deployments: name (tag), …" hint every ambiguity failure
|
|
52
|
-
* prints — one vocabulary across the write and instance-keyed paths, so a
|
|
53
|
-
* reader always sees both the `--deployment` names and their `--tag`s. */
|
|
54
|
-
export declare function availableDeployments(config: WorkflowConfig, deployments?: WorkflowDeployment[]): string;
|
|
55
51
|
/**
|
|
56
52
|
* Project a deployment into the engine's {@link DeployTarget} — the shape the
|
|
57
53
|
* deploy/diff verbs (`deployDefinitions`, `computeDiffEntries`, `diffEntry`)
|
|
@@ -66,7 +66,7 @@ async function chooseDeploymentName(deployments) {
|
|
|
66
66
|
choices: deployments.map(({ name, tag }) => ({ name, value: name, description: `tag: ${tag}` })),
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
-
|
|
69
|
+
function availableDeployments(config, deployments = config.deployments) {
|
|
70
70
|
return `Available deployments: ${deployments.map(deploymentLabel).join(', ')}`;
|
|
71
71
|
}
|
|
72
72
|
export function deploymentToTarget(deployment) {
|
package/dist/lib/ui.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
1
2
|
import type { ActivityStatus, WorkflowResource } from '@sanity/workflow-engine';
|
|
2
3
|
/**
|
|
3
4
|
* A bold `Title:` section heading. Mirrors the Sanity CLI's `sectionHeader`
|
|
@@ -8,12 +9,17 @@ export declare function sectionHeader(title: string): string;
|
|
|
8
9
|
* A ` key: value` detail row — a dim, padded key then the value. Mirrors
|
|
9
10
|
* the Sanity CLI's `formatKeyValue` so it folds into the shared helper on
|
|
10
11
|
* merge; pass `padTo` (the longest key's length) to align a block of rows.
|
|
12
|
+
*
|
|
13
|
+
* Pass `emphasis` (a {@link styleText} colour) to render the whole row — key
|
|
14
|
+
* and value together — bold in that colour instead of the dim-key default, so
|
|
15
|
+
* a standout row (a terminal instance status) pops from the neutral rows.
|
|
11
16
|
*/
|
|
12
|
-
export declare function formatKeyValue({ key, value, indent, padTo, }: {
|
|
17
|
+
export declare function formatKeyValue({ key, value, indent, padTo, emphasis, }: {
|
|
13
18
|
key: string;
|
|
14
19
|
value: string;
|
|
15
20
|
indent?: number;
|
|
16
21
|
padTo?: number;
|
|
22
|
+
emphasis?: Parameters<typeof styleText>[0];
|
|
17
23
|
}): string;
|
|
18
24
|
/**
|
|
19
25
|
* A borderless column table as printable lines: cyan header row, every
|
package/dist/lib/ui.js
CHANGED
|
@@ -6,9 +6,13 @@ import logSymbols from 'log-symbols';
|
|
|
6
6
|
export function sectionHeader(title) {
|
|
7
7
|
return styleText('bold', `${title}:`);
|
|
8
8
|
}
|
|
9
|
-
export function formatKeyValue({ key, value, indent = 2, padTo = 0, }) {
|
|
9
|
+
export function formatKeyValue({ key, value, indent = 2, padTo = 0, emphasis, }) {
|
|
10
10
|
const paddedKey = `${key}:`.padEnd(padTo > 0 ? padTo + 1 : key.length + 1);
|
|
11
|
-
|
|
11
|
+
const pad = ' '.repeat(indent);
|
|
12
|
+
if (emphasis) {
|
|
13
|
+
return `${pad}${styleText('bold', styleText(emphasis, `${paddedKey} ${value}`))}`;
|
|
14
|
+
}
|
|
15
|
+
return `${pad}${styleText('dim', paddedKey)} ${value}`;
|
|
12
16
|
}
|
|
13
17
|
export function formatTable(headers, rows) {
|
|
14
18
|
const width = (cell) => stripVTControlCharacters(cell).length;
|
package/oclif.manifest.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
],
|
|
19
19
|
"flags": {
|
|
20
20
|
"deployment": {
|
|
21
|
-
"description": "Deployment name —
|
|
21
|
+
"description": "Deployment name — narrow the instance search to the resource that deployment targets; the tag partition still comes from the loaded instance.",
|
|
22
22
|
"exclusive": [
|
|
23
23
|
"tag"
|
|
24
24
|
],
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"type": "option"
|
|
29
29
|
},
|
|
30
30
|
"tag": {
|
|
31
|
-
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter
|
|
31
|
+
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
|
|
32
32
|
"exclusive": [
|
|
33
33
|
"deployment"
|
|
34
34
|
],
|
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
],
|
|
166
166
|
"flags": {
|
|
167
167
|
"deployment": {
|
|
168
|
-
"description": "Deployment name —
|
|
168
|
+
"description": "Deployment name — narrow the instance search to the resource that deployment targets; the tag partition still comes from the loaded instance.",
|
|
169
169
|
"exclusive": [
|
|
170
170
|
"tag"
|
|
171
171
|
],
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
"type": "option"
|
|
176
176
|
},
|
|
177
177
|
"tag": {
|
|
178
|
-
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter
|
|
178
|
+
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
|
|
179
179
|
"exclusive": [
|
|
180
180
|
"deployment"
|
|
181
181
|
],
|
|
@@ -225,7 +225,7 @@
|
|
|
225
225
|
],
|
|
226
226
|
"flags": {
|
|
227
227
|
"deployment": {
|
|
228
|
-
"description": "Deployment name —
|
|
228
|
+
"description": "Deployment name — narrow the instance search to the resource that deployment targets; the tag partition still comes from the loaded instance.",
|
|
229
229
|
"exclusive": [
|
|
230
230
|
"tag"
|
|
231
231
|
],
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
"type": "option"
|
|
236
236
|
},
|
|
237
237
|
"tag": {
|
|
238
|
-
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter
|
|
238
|
+
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
|
|
239
239
|
"exclusive": [
|
|
240
240
|
"deployment"
|
|
241
241
|
],
|
|
@@ -299,11 +299,12 @@
|
|
|
299
299
|
"<%= config.bin %> list --include-completed",
|
|
300
300
|
"<%= config.bin %> list --definition productLaunch",
|
|
301
301
|
"<%= config.bin %> list --document dataset:proj:ds:article-1",
|
|
302
|
-
"<%= config.bin %> list --tag prod"
|
|
302
|
+
"<%= config.bin %> list --tag prod",
|
|
303
|
+
"<%= config.bin %> list --json"
|
|
303
304
|
],
|
|
304
305
|
"flags": {
|
|
305
306
|
"tag": {
|
|
306
|
-
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter
|
|
307
|
+
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
|
|
307
308
|
"name": "tag",
|
|
308
309
|
"hasDynamicHelp": false,
|
|
309
310
|
"multiple": false,
|
|
@@ -342,6 +343,12 @@
|
|
|
342
343
|
"hasDynamicHelp": false,
|
|
343
344
|
"multiple": false,
|
|
344
345
|
"type": "option"
|
|
346
|
+
},
|
|
347
|
+
"json": {
|
|
348
|
+
"description": "Emit structured JSON instead of rendered output.",
|
|
349
|
+
"name": "json",
|
|
350
|
+
"allowNo": false,
|
|
351
|
+
"type": "boolean"
|
|
345
352
|
}
|
|
346
353
|
},
|
|
347
354
|
"hasDynamicHelp": false,
|
|
@@ -429,8 +436,21 @@
|
|
|
429
436
|
"<%= config.bin %> reset-activity wf-instance.abc123 legal-review --skip"
|
|
430
437
|
],
|
|
431
438
|
"flags": {
|
|
439
|
+
"deployment": {
|
|
440
|
+
"description": "Deployment name — narrow the instance search to the resource that deployment targets; the tag partition still comes from the loaded instance.",
|
|
441
|
+
"exclusive": [
|
|
442
|
+
"tag"
|
|
443
|
+
],
|
|
444
|
+
"name": "deployment",
|
|
445
|
+
"hasDynamicHelp": false,
|
|
446
|
+
"multiple": false,
|
|
447
|
+
"type": "option"
|
|
448
|
+
},
|
|
432
449
|
"tag": {
|
|
433
|
-
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter
|
|
450
|
+
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
|
|
451
|
+
"exclusive": [
|
|
452
|
+
"deployment"
|
|
453
|
+
],
|
|
434
454
|
"name": "tag",
|
|
435
455
|
"hasDynamicHelp": false,
|
|
436
456
|
"multiple": false,
|
|
@@ -476,7 +496,7 @@
|
|
|
476
496
|
],
|
|
477
497
|
"flags": {
|
|
478
498
|
"deployment": {
|
|
479
|
-
"description": "Deployment name —
|
|
499
|
+
"description": "Deployment name — narrow the instance search to the resource that deployment targets; the tag partition still comes from the loaded instance.",
|
|
480
500
|
"exclusive": [
|
|
481
501
|
"tag"
|
|
482
502
|
],
|
|
@@ -486,7 +506,7 @@
|
|
|
486
506
|
"type": "option"
|
|
487
507
|
},
|
|
488
508
|
"tag": {
|
|
489
|
-
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter
|
|
509
|
+
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
|
|
490
510
|
"exclusive": [
|
|
491
511
|
"deployment"
|
|
492
512
|
],
|
|
@@ -540,18 +560,19 @@
|
|
|
540
560
|
"description": "Show the state, activities, and effects of a workflow instance.",
|
|
541
561
|
"examples": [
|
|
542
562
|
"<%= config.bin %> show wf-instance.abc123",
|
|
543
|
-
"<%= config.bin %> show wf-instance.abc123 --include history"
|
|
563
|
+
"<%= config.bin %> show wf-instance.abc123 --include history",
|
|
564
|
+
"<%= config.bin %> show wf-instance.abc123 --json"
|
|
544
565
|
],
|
|
545
566
|
"flags": {
|
|
546
567
|
"tag": {
|
|
547
|
-
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter
|
|
568
|
+
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
|
|
548
569
|
"name": "tag",
|
|
549
570
|
"hasDynamicHelp": false,
|
|
550
571
|
"multiple": false,
|
|
551
572
|
"type": "option"
|
|
552
573
|
},
|
|
553
574
|
"include": {
|
|
554
|
-
"description": "Optional sections to include in output.",
|
|
575
|
+
"description": "Optional sections to include in rendered output (--json always carries the full document).",
|
|
555
576
|
"name": "include",
|
|
556
577
|
"default": [],
|
|
557
578
|
"hasDynamicHelp": false,
|
|
@@ -560,6 +581,12 @@
|
|
|
560
581
|
"history"
|
|
561
582
|
],
|
|
562
583
|
"type": "option"
|
|
584
|
+
},
|
|
585
|
+
"json": {
|
|
586
|
+
"description": "Emit structured JSON instead of rendered output.",
|
|
587
|
+
"name": "json",
|
|
588
|
+
"allowNo": false,
|
|
589
|
+
"type": "boolean"
|
|
563
590
|
}
|
|
564
591
|
},
|
|
565
592
|
"hasDynamicHelp": false,
|
|
@@ -677,7 +704,7 @@
|
|
|
677
704
|
],
|
|
678
705
|
"flags": {
|
|
679
706
|
"tag": {
|
|
680
|
-
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter
|
|
707
|
+
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
|
|
681
708
|
"name": "tag",
|
|
682
709
|
"hasDynamicHelp": false,
|
|
683
710
|
"multiple": false,
|
|
@@ -843,11 +870,12 @@
|
|
|
843
870
|
"description": "List deployed workflow definitions.",
|
|
844
871
|
"examples": [
|
|
845
872
|
"<%= config.bin %> definition list",
|
|
846
|
-
"<%= config.bin %> definition list --tag prod"
|
|
873
|
+
"<%= config.bin %> definition list --tag prod",
|
|
874
|
+
"<%= config.bin %> definition list --json"
|
|
847
875
|
],
|
|
848
876
|
"flags": {
|
|
849
877
|
"tag": {
|
|
850
|
-
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter
|
|
878
|
+
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
|
|
851
879
|
"name": "tag",
|
|
852
880
|
"hasDynamicHelp": false,
|
|
853
881
|
"multiple": false,
|
|
@@ -867,6 +895,12 @@
|
|
|
867
895
|
"hasDynamicHelp": false,
|
|
868
896
|
"multiple": false,
|
|
869
897
|
"type": "option"
|
|
898
|
+
},
|
|
899
|
+
"json": {
|
|
900
|
+
"description": "Emit structured JSON instead of rendered output.",
|
|
901
|
+
"name": "json",
|
|
902
|
+
"allowNo": false,
|
|
903
|
+
"type": "boolean"
|
|
870
904
|
}
|
|
871
905
|
},
|
|
872
906
|
"hasDynamicHelp": false,
|
|
@@ -899,7 +933,7 @@
|
|
|
899
933
|
"description": "Show a deployed workflow definition.",
|
|
900
934
|
"flags": {
|
|
901
935
|
"tag": {
|
|
902
|
-
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter
|
|
936
|
+
"description": "Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources are searched; omit to span them all.",
|
|
903
937
|
"name": "tag",
|
|
904
938
|
"hasDynamicHelp": false,
|
|
905
939
|
"multiple": false,
|
|
@@ -911,6 +945,12 @@
|
|
|
911
945
|
"hasDynamicHelp": false,
|
|
912
946
|
"multiple": false,
|
|
913
947
|
"type": "option"
|
|
948
|
+
},
|
|
949
|
+
"json": {
|
|
950
|
+
"description": "Emit structured JSON instead of rendered output.",
|
|
951
|
+
"name": "json",
|
|
952
|
+
"allowNo": false,
|
|
953
|
+
"type": "boolean"
|
|
914
954
|
}
|
|
915
955
|
},
|
|
916
956
|
"hasDynamicHelp": false,
|
|
@@ -930,5 +970,5 @@
|
|
|
930
970
|
]
|
|
931
971
|
}
|
|
932
972
|
},
|
|
933
|
-
"version": "0.
|
|
973
|
+
"version": "0.22.0"
|
|
934
974
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/workflow-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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.22.0",
|
|
66
|
+
"@sanity/workflow-engine-test": "0.22.0",
|
|
67
|
+
"@sanity/workflow-examples": "0.10.1"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
|
-
"@sanity/workflow-engine": "0.
|
|
70
|
+
"@sanity/workflow-engine": "0.22.0"
|
|
71
71
|
},
|
|
72
72
|
"oclif": {
|
|
73
73
|
"bin": "sanity-workflows",
|