@sanity/workflow-cli 0.9.0 → 0.11.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 +516 -0
- package/README.md +71 -19
- package/bin/run.js +0 -4
- package/dist/commands/abort.d.ts +4 -6
- package/dist/commands/abort.js +13 -24
- package/dist/commands/definition/delete.d.ts +2 -2
- package/dist/commands/definition/delete.js +14 -27
- package/dist/commands/definition/diff.d.ts +2 -2
- package/dist/commands/definition/diff.js +3 -12
- package/dist/commands/definition/list.d.ts +3 -4
- package/dist/commands/definition/list.js +41 -47
- package/dist/commands/definition/show.d.ts +35 -3
- package/dist/commands/definition/show.js +44 -24
- package/dist/commands/deploy.d.ts +11 -5
- package/dist/commands/deploy.js +54 -57
- package/dist/commands/diagnose.d.ts +22 -4
- package/dist/commands/diagnose.js +60 -56
- package/dist/commands/fire-action.d.ts +2 -4
- package/dist/commands/fire-action.js +39 -62
- package/dist/commands/list.d.ts +18 -10
- package/dist/commands/list.js +77 -69
- package/dist/commands/reset-activity.js +0 -1
- package/dist/commands/set-stage.d.ts +2 -2
- package/dist/commands/set-stage.js +14 -30
- package/dist/commands/show.d.ts +2 -2
- package/dist/commands/show.js +12 -24
- package/dist/commands/start.d.ts +4 -28
- package/dist/commands/start.js +19 -80
- package/dist/commands/tail.d.ts +2 -2
- package/dist/commands/tail.js +34 -36
- package/dist/hooks/finally/telemetry.d.ts +8 -0
- package/dist/hooks/finally/telemetry.js +13 -0
- package/dist/hooks/prerun/telemetry.d.ts +8 -0
- package/dist/hooks/prerun/telemetry.js +9 -0
- package/dist/index.js +0 -3
- package/dist/lib/base-command.d.ts +14 -0
- package/dist/lib/base-command.js +10 -0
- package/dist/lib/client.js +13 -30
- package/dist/lib/context.d.ts +49 -13
- package/dist/lib/context.js +49 -48
- package/dist/lib/definitions.d.ts +10 -0
- package/dist/lib/definitions.js +11 -24
- package/dist/lib/diff.d.ts +5 -2
- package/dist/lib/diff.js +61 -27
- package/dist/lib/env.d.ts +4 -0
- package/dist/lib/env.js +4 -3
- package/dist/lib/fail.d.ts +19 -6
- package/dist/lib/fail.js +26 -26
- package/dist/lib/flags.d.ts +0 -7
- package/dist/lib/flags.js +0 -17
- package/dist/lib/load-config.d.ts +11 -7
- package/dist/lib/load-config.js +40 -20
- package/dist/lib/operation-args.d.ts +14 -23
- package/dist/lib/operation-args.js +6 -38
- package/dist/lib/ops-report.d.ts +18 -10
- package/dist/lib/ops-report.js +20 -16
- package/dist/lib/params.js +0 -8
- package/dist/lib/read-fanout.d.ts +22 -0
- package/dist/lib/read-fanout.js +28 -0
- package/dist/lib/select-deployment.js +0 -21
- package/dist/lib/share-definitions.d.ts +120 -0
- package/dist/lib/share-definitions.js +187 -0
- package/dist/lib/stub.js +0 -7
- package/dist/lib/telemetry-setup.d.ts +70 -0
- package/dist/lib/telemetry-setup.js +99 -0
- package/dist/lib/telemetry.d.ts +131 -0
- package/dist/lib/telemetry.js +94 -0
- package/dist/lib/ui.d.ts +33 -1
- package/dist/lib/ui.js +32 -22
- package/oclif.manifest.json +21 -52
- package/package.json +14 -8
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { styleText } from 'node:util';
|
|
2
|
-
import { Args,
|
|
2
|
+
import { Args, Flags } from '@oclif/core';
|
|
3
3
|
import { isTerminalStage, } from '@sanity/workflow-engine';
|
|
4
4
|
import logSymbols from 'log-symbols';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { WorkflowCommand } from "../../lib/base-command.js";
|
|
6
|
+
import { resolveReadTargets, soleHitOrFail } from "../../lib/context.js";
|
|
7
|
+
import { buildDefinitionShowQuery, buildDefinitionTagsQuery } from "../../lib/definitions.js";
|
|
8
|
+
import { fail } from "../../lib/fail.js";
|
|
7
9
|
import { tagFlags } from "../../lib/flags.js";
|
|
8
|
-
import { formatKeyValue, sectionHeader } from "../../lib/ui.js";
|
|
9
|
-
export default class DefinitionShow extends
|
|
10
|
+
import { formatKeyValue, resourceLabel, sectionHeader } from "../../lib/ui.js";
|
|
11
|
+
export default class DefinitionShow extends WorkflowCommand {
|
|
10
12
|
static description = 'Show a deployed workflow definition.';
|
|
11
13
|
static args = {
|
|
12
14
|
name: Args.string({ required: true, description: 'Workflow definition name.' }),
|
|
@@ -17,18 +19,17 @@ export default class DefinitionShow extends Command {
|
|
|
17
19
|
};
|
|
18
20
|
async run() {
|
|
19
21
|
const { args, flags } = await this.parse(DefinitionShow);
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
+
const targets = await resolveReadTargets(flags);
|
|
23
|
+
const def = await resolveShownDefinition({
|
|
24
|
+
targets,
|
|
22
25
|
name: args.name,
|
|
23
26
|
tag: flags.tag,
|
|
24
27
|
version: flags.version,
|
|
25
28
|
});
|
|
26
|
-
// A deployed document: the authored {@link WorkflowDefinition} plus the
|
|
27
|
-
// deploy-stamped envelope (`version`, `contentHash`, `tag`).
|
|
28
|
-
const def = await client.fetch(groq, params);
|
|
29
29
|
if (!def) {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
this.error(`${logSymbols.error} no definition "${args.name}" (${versionLabel(flags.version)})`, {
|
|
31
|
+
exit: 1,
|
|
32
|
+
});
|
|
32
33
|
}
|
|
33
34
|
this.log(definitionHeader(def, def.tag));
|
|
34
35
|
this.log('');
|
|
@@ -37,11 +38,37 @@ export default class DefinitionShow extends Command {
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
export async function resolveShownDefinition({ targets, name, tag, version, }) {
|
|
42
|
+
if (tag === undefined) {
|
|
43
|
+
await assertSingleTagPartition({ targets, name });
|
|
44
|
+
}
|
|
45
|
+
const { groq, params } = buildDefinitionShowQuery({ name, tag, version });
|
|
46
|
+
const hits = await fetchDefinitionHits({ targets, groq, params });
|
|
47
|
+
return soleHitOrFail(hits, `Definition "${name}"`)?.def;
|
|
48
|
+
}
|
|
49
|
+
export function versionLabel(version) {
|
|
50
|
+
return version === undefined ? 'latest' : `v${version}`;
|
|
51
|
+
}
|
|
52
|
+
async function assertSingleTagPartition({ targets, name, }) {
|
|
53
|
+
const { groq, params } = buildDefinitionTagsQuery(name);
|
|
54
|
+
await Promise.all(targets.map(async (target) => {
|
|
55
|
+
const tags = await target.client.fetch(groq, params, { tag: 'definition.show' });
|
|
56
|
+
if (tags.length > 1) {
|
|
57
|
+
fail(`Definition "${name}" exists in several tag partitions in ` +
|
|
58
|
+
`${resourceLabel(target.resource)} — pass --tag to choose one.`, tags.toSorted().join('\n'));
|
|
59
|
+
}
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
export async function fetchDefinitionHits({ targets, groq, params, }) {
|
|
63
|
+
const probes = await Promise.all(targets.map(async (target) => {
|
|
64
|
+
const def = await target.client.fetch(groq, params, {
|
|
65
|
+
tag: 'definition.show',
|
|
66
|
+
});
|
|
67
|
+
return def === null ? undefined : { resource: target.resource, def };
|
|
68
|
+
}));
|
|
69
|
+
return probes.filter((hit) => hit !== undefined);
|
|
70
|
+
}
|
|
71
|
+
const HEADER_PAD = 11;
|
|
45
72
|
export function definitionHeader(def, tag) {
|
|
46
73
|
const rows = [
|
|
47
74
|
formatKeyValue({ key: 'Title', value: def.title, padTo: HEADER_PAD }),
|
|
@@ -56,18 +83,15 @@ export function definitionHeader(def, tag) {
|
|
|
56
83
|
}
|
|
57
84
|
return [styleText('bold', `${def.name} v${def.version}`), ...rows].join('\n');
|
|
58
85
|
}
|
|
59
|
-
/** One `· action` line, annotated with the terminal status its `status.set` op sets, if any. */
|
|
60
86
|
function actionLine(action) {
|
|
61
87
|
const statusOp = (action.ops ?? []).find((op) => op.type === 'status.set');
|
|
62
88
|
const status = statusOp ? styleText('dim', ` → ${statusOp.status}`) : '';
|
|
63
89
|
return ` · ${action.name}${status}`;
|
|
64
90
|
}
|
|
65
|
-
/** A `- activity` line followed by one line per action it exposes. */
|
|
66
91
|
function activityBlock(activity) {
|
|
67
92
|
const title = activity.title ? styleText('dim', ` — ${activity.title}`) : '';
|
|
68
93
|
return [` - ${activity.name}${title}`, ...(activity.actions ?? []).map(actionLine)];
|
|
69
94
|
}
|
|
70
|
-
/** A `• stage` line (with initial/terminal markers) followed by its activity blocks. */
|
|
71
95
|
function stageBlock(stage, initialStage) {
|
|
72
96
|
const title = stage.title ? styleText('dim', ` — ${stage.title}`) : '';
|
|
73
97
|
const terminal = isTerminalStage(stage) ? styleText('dim', ' (terminal)') : '';
|
|
@@ -77,10 +101,6 @@ function stageBlock(stage, initialStage) {
|
|
|
77
101
|
...(stage.activities ?? []).flatMap(activityBlock),
|
|
78
102
|
];
|
|
79
103
|
}
|
|
80
|
-
/**
|
|
81
|
-
* The body lines for `definition show`: every stage with its activities and each
|
|
82
|
-
* activity's actions. Pure so it can be asserted without driving the oclif command.
|
|
83
|
-
*/
|
|
84
104
|
export function describeDefinition(def) {
|
|
85
105
|
return [
|
|
86
106
|
sectionHeader('Stages'),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Command } from '@oclif/core';
|
|
2
1
|
import { type DeployDefinitionResult, type WorkflowDefinition, type WorkflowDeployment } from '@sanity/workflow-engine';
|
|
2
|
+
import { WorkflowCommand } from '../lib/base-command.ts';
|
|
3
3
|
/** A deployment paired with the definitions selected + validated for it. */
|
|
4
4
|
interface DeployBatch {
|
|
5
5
|
deployment: WorkflowDeployment;
|
|
@@ -10,7 +10,7 @@ interface DeployFailure {
|
|
|
10
10
|
deployment: Pick<WorkflowDeployment, 'name' | 'tag'>;
|
|
11
11
|
message: string;
|
|
12
12
|
}
|
|
13
|
-
export default class Deploy extends
|
|
13
|
+
export default class Deploy extends WorkflowCommand {
|
|
14
14
|
static description: string;
|
|
15
15
|
static examples: string[];
|
|
16
16
|
static flags: {
|
|
@@ -18,9 +18,13 @@ export default class Deploy extends Command {
|
|
|
18
18
|
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
19
19
|
check: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
20
20
|
only: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
'share-defs': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
21
22
|
tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
23
|
};
|
|
23
24
|
run(): Promise<void>;
|
|
25
|
+
/** Deploy (or diff, for `--dry-run`) one deployment's definitions.
|
|
26
|
+
* Returns the per-definition results for a real deploy; `undefined`
|
|
27
|
+
* for a dry run, which writes nothing. */
|
|
24
28
|
private processDeployment;
|
|
25
29
|
private executeDeploy;
|
|
26
30
|
private runDryRun;
|
|
@@ -42,6 +46,8 @@ export declare function buildBatches(deployments: WorkflowDeployment[], only: st
|
|
|
42
46
|
* to their message for {@link deploySummary}. A single-batch run rethrows
|
|
43
47
|
* instead: it has no summary, so a throw from before the deploy spinner starts
|
|
44
48
|
* would otherwise surface nowhere — and propagating keeps the stack visible.
|
|
49
|
+
* An auth rejection also rethrows regardless of batch count: a dead token is
|
|
50
|
+
* never batch-local, so continuing would just repeat the same failure.
|
|
45
51
|
*/
|
|
46
52
|
export declare function reconcileBatches({ batches, log, processBatch, }: {
|
|
47
53
|
batches: DeployBatch[];
|
|
@@ -62,14 +68,14 @@ export declare function deploySummary({ total, failures, }: {
|
|
|
62
68
|
/**
|
|
63
69
|
* The banner printed above a deployment's output so a multi-deployment run is
|
|
64
70
|
* attributable. `undefined` for a single-target run — it keeps its original
|
|
65
|
-
* unprefixed output.
|
|
66
|
-
* first.
|
|
71
|
+
* unprefixed output.
|
|
67
72
|
*/
|
|
68
73
|
export declare function deploymentBanner({ deployment, index, total, }: {
|
|
69
74
|
deployment: Pick<WorkflowDeployment, 'name' | 'tag'>;
|
|
70
75
|
index: number;
|
|
71
76
|
total: number;
|
|
72
77
|
}): string | undefined;
|
|
73
|
-
/** One line per deployed definition, symbol-tagged by its result status
|
|
78
|
+
/** One line per deployed definition, symbol-tagged by its result status,
|
|
79
|
+
* followed by any advisory effect-output lint warnings indented beneath it. */
|
|
74
80
|
export declare function deployResultLines(results: DeployDefinitionResult[]): string[];
|
|
75
81
|
export {};
|
package/dist/commands/deploy.js
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { styleText } from 'node:util';
|
|
2
|
-
import {
|
|
3
|
-
import { computeDiffEntries, workflow, } from '@sanity/workflow-engine';
|
|
2
|
+
import { Flags } from '@oclif/core';
|
|
3
|
+
import { computeDiffEntries, errorMessage, workflow, } from '@sanity/workflow-engine';
|
|
4
4
|
import logSymbols from 'log-symbols';
|
|
5
5
|
import ora from 'ora';
|
|
6
|
+
import { WorkflowCommand } from "../lib/base-command.js";
|
|
6
7
|
import { clientFor, resolveTokenOrFail } from "../lib/client.js";
|
|
7
8
|
import { selectDefinitions, validateOrFail } from "../lib/definitions.js";
|
|
8
9
|
import { diffReport } from "../lib/diff.js";
|
|
9
|
-
import {
|
|
10
|
+
import { fail, isAuthRejection } from "../lib/fail.js";
|
|
10
11
|
import { tagFlags } from "../lib/flags.js";
|
|
11
12
|
import { loadWorkflowConfig } from "../lib/load-config.js";
|
|
12
13
|
import { deploymentToTarget, selectDeployments } from "../lib/select-deployment.js";
|
|
13
|
-
|
|
14
|
+
import { shareDefinitionsAfterDeploy } from "../lib/share-definitions.js";
|
|
15
|
+
import { cliTelemetry } from "../lib/telemetry.js";
|
|
16
|
+
import { groupBanner } from "../lib/ui.js";
|
|
17
|
+
export default class Deploy extends WorkflowCommand {
|
|
14
18
|
static description = 'Validate, diff, and deploy workflow definitions to the resource bound by the selected deployment.';
|
|
15
19
|
static examples = [
|
|
16
20
|
'<%= config.bin %> deploy --tag prod',
|
|
@@ -37,17 +41,17 @@ export default class Deploy extends Command {
|
|
|
37
41
|
only: Flags.string({
|
|
38
42
|
description: 'Limit deploy/check/diff to a single workflow definition by name. Every targeted deployment must contain it.',
|
|
39
43
|
}),
|
|
44
|
+
'share-defs': Flags.boolean({
|
|
45
|
+
allowNo: true,
|
|
46
|
+
description: 'Share the definition documents newly created by this deploy with Sanity — the full document, verbatim (structure, names, filters, effect configuration, seeded values), plus its deployment coordinates (project and dataset, or resource id); never content documents, instances, or your Sanity auth token. Opt-out: an interactive terminal is asked once (the answer is remembered), while unattended runs (CI / non-TTY / DO_NOT_TRACK) share nothing unless --share-defs is passed. Use --no-share-defs to opt out. An explicit --share-defs also sends telemetry for this deploy regardless of CI / DO_NOT_TRACK.',
|
|
47
|
+
}),
|
|
40
48
|
};
|
|
41
49
|
async run() {
|
|
42
50
|
const { flags } = await this.parse(Deploy);
|
|
43
51
|
validateModeFlags(flags.check, flags['dry-run']);
|
|
44
52
|
const config = await loadWorkflowConfig();
|
|
45
|
-
// Validate every deployment up front (below), so a bad definition fails
|
|
46
|
-
// before any write — never a partial reconcile from a validation error.
|
|
47
53
|
const batches = buildBatches(selectDeployments(config, { tag: flags.tag, allTags: flags['all-tags'] }), flags.only);
|
|
48
54
|
const log = (line) => this.log(line);
|
|
49
|
-
// buildBatches already validated everything offline — --check only has to
|
|
50
|
-
// report that pass, so it never resolves a token or builds a client.
|
|
51
55
|
if (flags.check) {
|
|
52
56
|
await reconcileBatches({
|
|
53
57
|
batches,
|
|
@@ -58,36 +62,63 @@ export default class Deploy extends Command {
|
|
|
58
62
|
});
|
|
59
63
|
return;
|
|
60
64
|
}
|
|
61
|
-
// One token up front, so an --all-tags run authenticates once, not per deployment.
|
|
62
65
|
const token = await resolveTokenOrFail();
|
|
66
|
+
const candidates = [];
|
|
63
67
|
const failures = await reconcileBatches({
|
|
64
68
|
batches,
|
|
65
69
|
log,
|
|
66
|
-
processBatch: ({ deployment, defs }) =>
|
|
70
|
+
processBatch: async ({ deployment, defs }) => {
|
|
71
|
+
const client = clientFor(deployment.workflowResource, token);
|
|
72
|
+
const deployed = await this.processDeployment({
|
|
73
|
+
deployment,
|
|
74
|
+
defs,
|
|
75
|
+
dryRun: flags['dry-run'],
|
|
76
|
+
client,
|
|
77
|
+
});
|
|
78
|
+
if (deployed !== undefined) {
|
|
79
|
+
candidates.push({
|
|
80
|
+
client,
|
|
81
|
+
tag: deployment.tag,
|
|
82
|
+
resource: deployment.workflowResource,
|
|
83
|
+
results: deployed.results,
|
|
84
|
+
deployId: deployed.deployId,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
},
|
|
67
88
|
});
|
|
68
89
|
for (const line of deploySummary({ total: batches.length, failures })) {
|
|
69
90
|
log(line);
|
|
70
91
|
}
|
|
92
|
+
await shareDefinitionsAfterDeploy({
|
|
93
|
+
flag: flags['share-defs'],
|
|
94
|
+
candidates,
|
|
95
|
+
warn: (message) => this.warn(message),
|
|
96
|
+
});
|
|
71
97
|
if (failures.length > 0) {
|
|
72
98
|
this.exit(1);
|
|
73
99
|
}
|
|
74
100
|
}
|
|
75
|
-
async processDeployment({ deployment, defs, dryRun,
|
|
101
|
+
async processDeployment({ deployment, defs, dryRun, client, }) {
|
|
76
102
|
const target = deploymentToTarget(deployment);
|
|
77
|
-
const client = clientFor(deployment.workflowResource, token);
|
|
78
103
|
if (dryRun) {
|
|
79
104
|
await this.runDryRun({ client, defs, target });
|
|
80
|
-
return;
|
|
105
|
+
return undefined;
|
|
81
106
|
}
|
|
82
|
-
|
|
107
|
+
return this.executeDeploy({ client, defs, target });
|
|
83
108
|
}
|
|
84
109
|
async executeDeploy({ client, defs, target, }) {
|
|
85
110
|
const spinner = ora(`Deploying ${defs.length} definition(s)…`).start();
|
|
86
111
|
try {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
112
|
+
const deployed = await workflow.deployDefinitions({
|
|
113
|
+
...target,
|
|
114
|
+
client,
|
|
115
|
+
definitions: defs,
|
|
116
|
+
telemetry: cliTelemetry().logger,
|
|
117
|
+
});
|
|
118
|
+
spinner.succeed(`Processed ${deployed.results.length} definition(s)`);
|
|
119
|
+
for (const line of deployResultLines(deployed.results))
|
|
90
120
|
this.log(line);
|
|
121
|
+
return deployed;
|
|
91
122
|
}
|
|
92
123
|
catch (error) {
|
|
93
124
|
spinner.fail(`Deploy failed — ${errorMessage(error)}`);
|
|
@@ -108,24 +139,14 @@ export default class Deploy extends Command {
|
|
|
108
139
|
}
|
|
109
140
|
}
|
|
110
141
|
}
|
|
111
|
-
/** Reject the contradictory `--check --dry-run` combination. */
|
|
112
142
|
export function validateModeFlags(check, dryRun) {
|
|
113
143
|
if (check && dryRun) {
|
|
114
144
|
fail('Pass either --check or --dry-run, not both.');
|
|
115
145
|
}
|
|
116
146
|
}
|
|
117
|
-
/** The one identity format for a deployment in output — banner, failure
|
|
118
|
-
* summary, and validation attribution all render it the same way. */
|
|
119
147
|
function deploymentLabel({ name, tag }) {
|
|
120
148
|
return `${name} (${tag})`;
|
|
121
149
|
}
|
|
122
|
-
/**
|
|
123
|
-
* Select and validate every deployment's definitions up front. Validation runs
|
|
124
|
-
* before any network write ({@link validateOrFail} exits on a bad definition),
|
|
125
|
-
* so a whole-config deploy can never leave a partial reconcile behind a broken
|
|
126
|
-
* batch. In a multi-deployment run the failure names its deployment — it exits
|
|
127
|
-
* before any banner could attribute it.
|
|
128
|
-
*/
|
|
129
150
|
export function buildBatches(deployments, only) {
|
|
130
151
|
return deployments.map((deployment) => {
|
|
131
152
|
const context = deployments.length > 1 ? `${deploymentLabel(deployment)} — ` : '';
|
|
@@ -134,14 +155,6 @@ export function buildBatches(deployments, only) {
|
|
|
134
155
|
return { deployment, defs };
|
|
135
156
|
});
|
|
136
157
|
}
|
|
137
|
-
/**
|
|
138
|
-
* Run `processBatch` over each batch, continuing past a failure so one bad
|
|
139
|
-
* deployment can't strand the rest — deployments are independent, and deploys
|
|
140
|
-
* are idempotent, so a partial run is safe to re-run. Failures are flattened
|
|
141
|
-
* to their message for {@link deploySummary}. A single-batch run rethrows
|
|
142
|
-
* instead: it has no summary, so a throw from before the deploy spinner starts
|
|
143
|
-
* would otherwise surface nowhere — and propagating keeps the stack visible.
|
|
144
|
-
*/
|
|
145
158
|
export async function reconcileBatches({ batches, log, processBatch, }) {
|
|
146
159
|
const failures = [];
|
|
147
160
|
for (const [index, batch] of batches.entries()) {
|
|
@@ -153,7 +166,7 @@ export async function reconcileBatches({ batches, log, processBatch, }) {
|
|
|
153
166
|
await processBatch(batch);
|
|
154
167
|
}
|
|
155
168
|
catch (error) {
|
|
156
|
-
if (batches.length === 1) {
|
|
169
|
+
if (batches.length === 1 || isAuthRejection(error)) {
|
|
157
170
|
throw error;
|
|
158
171
|
}
|
|
159
172
|
failures.push({ deployment: batch.deployment, message: errorMessage(error) });
|
|
@@ -161,13 +174,6 @@ export async function reconcileBatches({ batches, log, processBatch, }) {
|
|
|
161
174
|
}
|
|
162
175
|
return failures;
|
|
163
176
|
}
|
|
164
|
-
/**
|
|
165
|
-
* The closing summary for a multi-deployment run that had failures: the
|
|
166
|
-
* succeeded/failed tally and each failed deployment. Empty for a clean run.
|
|
167
|
-
* The wording is mode-neutral because a `--dry-run` failure lands here too,
|
|
168
|
-
* where nothing was deployed. Single-batch runs never reach it — their lone
|
|
169
|
-
* failure rethrows out of {@link reconcileBatches}.
|
|
170
|
-
*/
|
|
171
177
|
export function deploySummary({ total, failures, }) {
|
|
172
178
|
if (failures.length === 0) {
|
|
173
179
|
return [];
|
|
@@ -178,26 +184,17 @@ export function deploySummary({ total, failures, }) {
|
|
|
178
184
|
...failures.map((f) => styleText('red', ` ${logSymbols.error} ${deploymentLabel(f.deployment)} — ${f.message}`)),
|
|
179
185
|
];
|
|
180
186
|
}
|
|
181
|
-
/**
|
|
182
|
-
* The banner printed above a deployment's output so a multi-deployment run is
|
|
183
|
-
* attributable. `undefined` for a single-target run — it keeps its original
|
|
184
|
-
* unprefixed output. A leading blank line separates every deployment after the
|
|
185
|
-
* first.
|
|
186
|
-
*/
|
|
187
187
|
export function deploymentBanner({ deployment, index, total, }) {
|
|
188
|
-
|
|
189
|
-
return undefined;
|
|
190
|
-
}
|
|
191
|
-
const gap = index > 0 ? '\n' : '';
|
|
192
|
-
return `${gap}${styleText('bold', `▸ ${deploymentLabel(deployment)}`)}`;
|
|
188
|
+
return groupBanner({ label: deploymentLabel(deployment), index, total });
|
|
193
189
|
}
|
|
194
|
-
/** One line per deployed definition, symbol-tagged by its result status. */
|
|
195
190
|
export function deployResultLines(results) {
|
|
196
191
|
const symbols = {
|
|
197
192
|
created: logSymbols.success,
|
|
198
193
|
};
|
|
199
|
-
return results.
|
|
194
|
+
return results.flatMap((r) => {
|
|
200
195
|
const symbol = symbols[r.status] ?? logSymbols.warning;
|
|
201
|
-
|
|
196
|
+
const head = ` ${symbol} ${r.status.padEnd(9)} ${r.name} v${r.version}`;
|
|
197
|
+
const warnings = (r.warnings ?? []).map((w) => ` ${logSymbols.warning} ${w}`);
|
|
198
|
+
return [head, ...warnings];
|
|
202
199
|
});
|
|
203
200
|
}
|
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { type Diagnosis, type DiagnoseInput, type SuggestedRemediation, type WorkflowEvaluation } from '@sanity/workflow-engine';
|
|
2
|
+
import { WorkflowCommand } from '../lib/base-command.ts';
|
|
3
|
+
/** The `--json` transitions payload: raw derived state (atom GROQ, negation,
|
|
4
|
+
* pivotality, solvable requirement) — structure for scripts, never baked
|
|
5
|
+
* English. Pure so the shape is a testable contract. */
|
|
6
|
+
export declare function rawTransitionProjection(evaluation: WorkflowEvaluation): {
|
|
7
|
+
name: string;
|
|
8
|
+
to: string;
|
|
9
|
+
whenSatisfied: boolean;
|
|
10
|
+
unevaluable: boolean;
|
|
11
|
+
blockedBy: {
|
|
12
|
+
requirement?: import("@sanity/groq-condition-describe").AtomRequirement;
|
|
13
|
+
groq: string;
|
|
14
|
+
negated: boolean;
|
|
15
|
+
pivotal: boolean;
|
|
16
|
+
}[];
|
|
17
|
+
}[];
|
|
3
18
|
/**
|
|
4
19
|
* The diagnosis body: the verdict line, the current stage's activities +
|
|
5
20
|
* transitions as evidence (for any in-flight state), and the cause block with
|
|
@@ -8,12 +23,15 @@ import { type Diagnosis, type DiagnoseInput, type SuggestedRemediation } from '@
|
|
|
8
23
|
* {@link SuggestedRemediation}s rather than re-deriving them, so the rendered
|
|
9
24
|
* fix block and the `--json` output stay one computation.
|
|
10
25
|
*/
|
|
11
|
-
export declare function renderDiagnosis({ diagnosis, input, remediations, }: {
|
|
26
|
+
export declare function renderDiagnosis({ diagnosis, input, remediations, explanations, }: {
|
|
12
27
|
diagnosis: Diagnosis;
|
|
13
28
|
input: DiagnoseInput;
|
|
14
29
|
remediations: SuggestedRemediation[];
|
|
30
|
+
/** Insight summaries per unsatisfied transition name — rendered under the
|
|
31
|
+
* transition line so "stuck" reads as "stuck until X". */
|
|
32
|
+
explanations?: ReadonlyMap<string, string> | undefined;
|
|
15
33
|
}): string[];
|
|
16
|
-
export default class Diagnose extends
|
|
34
|
+
export default class Diagnose extends WorkflowCommand {
|
|
17
35
|
static description: string;
|
|
18
36
|
static examples: string[];
|
|
19
37
|
static args: {
|