@sanity/workflow-cli 0.11.0 → 0.13.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 +66 -0
- package/README.md +75 -46
- package/dist/commands/{abort.d.ts → editorial-workflows/abort.d.ts} +3 -2
- package/dist/commands/{abort.js → editorial-workflows/abort.js} +7 -6
- package/dist/commands/{definition → editorial-workflows/definition}/delete.d.ts +3 -2
- package/dist/commands/{definition → editorial-workflows/definition}/delete.js +6 -5
- package/dist/commands/{definition → editorial-workflows/definition}/diff.d.ts +2 -1
- package/dist/commands/{definition → editorial-workflows/definition}/diff.js +7 -6
- package/dist/commands/{definition → editorial-workflows/definition}/list.d.ts +5 -1
- package/dist/commands/{definition → editorial-workflows/definition}/list.js +27 -24
- package/dist/commands/{definition → editorial-workflows/definition}/show.d.ts +3 -2
- package/dist/commands/{definition → editorial-workflows/definition}/show.js +9 -8
- package/dist/commands/{deploy.d.ts → editorial-workflows/deploy.d.ts} +2 -1
- package/dist/commands/{deploy.js → editorial-workflows/deploy.js} +14 -13
- package/dist/commands/{diagnose.d.ts → editorial-workflows/diagnose.d.ts} +2 -1
- package/dist/commands/{diagnose.js → editorial-workflows/diagnose.js} +7 -6
- package/dist/commands/{fire-action.d.ts → editorial-workflows/fire-action.d.ts} +3 -2
- package/dist/commands/{fire-action.js → editorial-workflows/fire-action.js} +8 -7
- package/dist/commands/{list.d.ts → editorial-workflows/list.d.ts} +4 -1
- package/dist/commands/{list.js → editorial-workflows/list.js} +33 -28
- package/dist/commands/editorial-workflows/nuke.d.ts +12 -0
- package/dist/commands/editorial-workflows/nuke.js +77 -0
- package/dist/commands/{reset-activity.d.ts → editorial-workflows/reset-activity.d.ts} +2 -1
- package/dist/commands/{reset-activity.js → editorial-workflows/reset-activity.js} +2 -1
- package/dist/commands/{set-stage.d.ts → editorial-workflows/set-stage.d.ts} +4 -3
- package/dist/commands/{set-stage.js → editorial-workflows/set-stage.js} +6 -5
- package/dist/commands/{show.d.ts → editorial-workflows/show.d.ts} +2 -1
- package/dist/commands/{show.js → editorial-workflows/show.js} +40 -26
- package/dist/commands/{start.d.ts → editorial-workflows/start.d.ts} +20 -4
- package/dist/commands/{start.js → editorial-workflows/start.js} +58 -16
- package/dist/commands/{tail.d.ts → editorial-workflows/tail.d.ts} +2 -1
- package/dist/commands/{tail.js → editorial-workflows/tail.js} +10 -8
- package/dist/hooks/finally/telemetry.js +2 -2
- package/dist/hooks/prerun/telemetry.d.ts +4 -3
- package/dist/lib/base-command.d.ts +4 -6
- package/dist/lib/base-command.js +6 -0
- package/dist/lib/client.d.ts +8 -0
- package/dist/lib/client.js +1 -1
- package/dist/lib/context.d.ts +6 -2
- package/dist/lib/context.js +9 -7
- package/dist/lib/definitions.js +2 -2
- package/dist/lib/flags.d.ts +4 -3
- package/dist/lib/nuke.d.ts +89 -0
- package/dist/lib/nuke.js +111 -0
- package/dist/lib/operation-args.d.ts +3 -1
- package/dist/lib/ops-report.d.ts +2 -1
- package/dist/lib/prompt.d.ts +11 -0
- package/dist/lib/prompt.js +4 -0
- package/dist/lib/select-deployment.d.ts +12 -7
- package/dist/lib/select-deployment.js +26 -1
- package/dist/lib/share-definitions.d.ts +23 -28
- package/dist/lib/share-definitions.js +30 -40
- package/dist/lib/telemetry-setup.d.ts +2 -2
- package/dist/lib/telemetry.d.ts +18 -10
- package/dist/lib/telemetry.js +5 -2
- package/dist/lib/ui.d.ts +12 -0
- package/dist/lib/ui.js +9 -0
- package/oclif.manifest.json +138 -46
- package/package.json +11 -9
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { styleText } from 'node:util';
|
|
2
|
-
import { getUserConfig
|
|
3
|
-
import {
|
|
4
|
-
import { definitionDeployedData, errorMessage, isTelemetryEnvDenied, } from '@sanity/workflow-engine';
|
|
2
|
+
import { getUserConfig } from '@sanity/cli-core';
|
|
3
|
+
import { assertReadableModel, definitionDeployedData, errorMessage, } from '@sanity/workflow-engine';
|
|
5
4
|
import { buildDefinitionShowQuery } from "./definitions.js";
|
|
5
|
+
import { canPromptOnStderr } from "./prompt.js";
|
|
6
6
|
import { cliTelemetry, WorkflowDefinitionShared, WorkflowDefinitionSharingDecided, } from "./telemetry.js";
|
|
7
7
|
export const SHARE_ENDPOINT_URI = '/workflow/definition-feedback';
|
|
8
8
|
const SHARE_TAG = 'definition.share';
|
|
9
9
|
const SHARE_DECISION_KEY = 'workflowCliDefinitionSharing';
|
|
10
|
-
export const
|
|
10
|
+
export const SHARE_FIRST_RUN_NOTICE = `${styleText('bold', 'Sharing new workflow definitions with Sanity')} to improve Editorial Workflows.\n` +
|
|
11
11
|
'They are sent verbatim (structure, names, filters, effect configuration,\n' +
|
|
12
|
-
'seeded values) with their deployment coordinates, and kept
|
|
13
|
-
'
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
'seeded values) with their deployment coordinates, and kept — never your\n' +
|
|
13
|
+
'content documents, workflow instances, or Sanity auth token. This notice is\n' +
|
|
14
|
+
`shown once on this machine; opt out of any run with ${styleText('cyan', '--no-share-defs')}.\n`;
|
|
15
|
+
export const SHARE_REMINDER = `${styleText('bold', 'Definition sharing: ON (default)')}\n` +
|
|
16
|
+
'New workflow definition versions are being shared with Sanity to improve Editorial Workflows.\n' +
|
|
17
|
+
'Examples:\n' +
|
|
18
|
+
` ${styleText('dim', '$')} sanity-workflows deploy ${styleText('dim', '# share by default')}\n` +
|
|
19
|
+
` ${styleText('dim', '$')} sanity-workflows deploy ${styleText('cyan', '--share-defs')} ${styleText('dim', '# share explicitly; hide this warning')}\n` +
|
|
20
|
+
` ${styleText('dim', '$')} sanity-workflows deploy ${styleText('cyan', '--no-share-defs')} ${styleText('dim', '# opt out')}\n`;
|
|
19
21
|
export function definitionShareMarker(args) {
|
|
20
22
|
const { status: _status, ...structural } = definitionDeployedData(args.definition, {
|
|
21
23
|
contentHash: args.contentHash,
|
|
@@ -26,7 +28,7 @@ export function definitionShareMarker(args) {
|
|
|
26
28
|
}
|
|
27
29
|
export function shouldForceShareTelemetry(args) {
|
|
28
30
|
const { commandId, argv } = args;
|
|
29
|
-
if (commandId !== 'deploy') {
|
|
31
|
+
if (commandId !== 'editorial-workflows:deploy') {
|
|
30
32
|
return false;
|
|
31
33
|
}
|
|
32
34
|
const terminator = argv.indexOf('--');
|
|
@@ -34,7 +36,8 @@ export function shouldForceShareTelemetry(args) {
|
|
|
34
36
|
if (tokens.includes('--dry-run') || tokens.includes('--check')) {
|
|
35
37
|
return false;
|
|
36
38
|
}
|
|
37
|
-
return tokens.lastIndexOf('--share-defs') > tokens.lastIndexOf('--no-share-defs')
|
|
39
|
+
return (tokens.lastIndexOf('--share-defs') > tokens.lastIndexOf('--no-share-defs') ||
|
|
40
|
+
!tokens.includes('--no-share-defs'));
|
|
38
41
|
}
|
|
39
42
|
async function loadShareEntry(args) {
|
|
40
43
|
const { candidate, result, warn } = args;
|
|
@@ -70,6 +73,7 @@ async function loadDeployedForShare(args) {
|
|
|
70
73
|
warn(`Could not read back ${result.name} v${result.version} — not shared.`);
|
|
71
74
|
return undefined;
|
|
72
75
|
}
|
|
76
|
+
assertReadableModel(deployed);
|
|
73
77
|
return deployed;
|
|
74
78
|
}
|
|
75
79
|
catch (error) {
|
|
@@ -82,43 +86,32 @@ function collectCreated(candidates) {
|
|
|
82
86
|
.filter((result) => result.status === 'created')
|
|
83
87
|
.map((result) => ({ candidate, result })));
|
|
84
88
|
}
|
|
85
|
-
function
|
|
89
|
+
function wasShareNoticeShown(userConfig) {
|
|
86
90
|
const value = userConfig.get(SHARE_DECISION_KEY);
|
|
87
|
-
return typeof value === 'boolean'
|
|
91
|
+
return typeof value === 'boolean';
|
|
88
92
|
}
|
|
89
|
-
|
|
90
|
-
let share;
|
|
93
|
+
function rememberShareNotice(userConfig) {
|
|
91
94
|
try {
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
try {
|
|
98
|
-
args.userConfig.set(SHARE_DECISION_KEY, share);
|
|
95
|
+
userConfig.set(SHARE_DECISION_KEY, true);
|
|
99
96
|
}
|
|
100
97
|
catch {
|
|
101
98
|
}
|
|
102
|
-
return share;
|
|
103
99
|
}
|
|
104
|
-
|
|
100
|
+
function resolveShareIntent(args) {
|
|
105
101
|
if (args.flag === true) {
|
|
106
102
|
return { share: true, decision: 'opt-in' };
|
|
107
103
|
}
|
|
108
104
|
if (args.flag === false) {
|
|
109
105
|
return { share: false, decision: 'opt-out' };
|
|
110
106
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
107
|
+
if (args.interactive && !wasShareNoticeShown(args.userConfig)) {
|
|
108
|
+
args.writeStderr(SHARE_FIRST_RUN_NOTICE);
|
|
109
|
+
rememberShareNotice(args.userConfig);
|
|
114
110
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
args.writeStderr(remembered ? SHARE_REMINDER_ON : SHARE_REMINDER_OFF);
|
|
118
|
-
return { share: remembered, decision: remembered ? 'remembered-share' : 'remembered-decline' };
|
|
111
|
+
else {
|
|
112
|
+
args.writeStderr(SHARE_REMINDER);
|
|
119
113
|
}
|
|
120
|
-
|
|
121
|
-
return { share: accepted, decision: accepted ? 'prompt-accepted' : 'prompt-declined' };
|
|
114
|
+
return { share: true, decision: 'default' };
|
|
122
115
|
}
|
|
123
116
|
async function donate(args) {
|
|
124
117
|
const shared = [];
|
|
@@ -169,14 +162,11 @@ export async function shareDefinitionsAfterDeploy(args) {
|
|
|
169
162
|
return;
|
|
170
163
|
}
|
|
171
164
|
try {
|
|
172
|
-
const { share, decision } =
|
|
165
|
+
const { share, decision } = resolveShareIntent({
|
|
173
166
|
flag: args.flag,
|
|
174
|
-
interactive: args.interactive ?? (
|
|
175
|
-
envDenied: args.envDenied ?? isTelemetryEnvDenied(process.env),
|
|
167
|
+
interactive: args.interactive ?? canPromptOnStderr(),
|
|
176
168
|
userConfig: args.userConfig ?? getUserConfig(),
|
|
177
169
|
writeStderr: args.writeStderr ?? ((message) => void process.stderr.write(message)),
|
|
178
|
-
confirmShare: args.confirmShare ??
|
|
179
|
-
(() => confirm({ message: SHARE_CONSENT_PROMPT, default: true }, { output: process.stderr })),
|
|
180
170
|
});
|
|
181
171
|
const shared = share ? await donate({ created, warn: args.warn }) : false;
|
|
182
172
|
emitSharingDecided({ decision, shared, definitionCount: created.length });
|
|
@@ -40,8 +40,8 @@ export declare function setupCliTelemetry(args?: {
|
|
|
40
40
|
/** The oclif config's version — the prerun hook supplies it. */
|
|
41
41
|
cliVersion?: string;
|
|
42
42
|
/** Override the environment denial (CI / `DO_NOT_TRACK`) — the prerun hook
|
|
43
|
-
* sets this for a deploy that
|
|
44
|
-
* `--share-defs`. */
|
|
43
|
+
* sets this for a deploy that may share definitions, either by default or
|
|
44
|
+
* through `--share-defs`. */
|
|
45
45
|
forceSend?: boolean;
|
|
46
46
|
/** Intake client override — tests inject a fake here, like the other
|
|
47
47
|
* hook dependencies above; omitted, the real project client applies. */
|
package/dist/lib/telemetry.d.ts
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* Definition Shared` is a content-free marker (hash + structural counts); the shared
|
|
25
25
|
* document itself rides the first-party feedback endpoint, never
|
|
26
26
|
* telemetry (see `share-definitions.ts`). `Editorial Workflows Definition
|
|
27
|
-
* Sharing Decided` records the per-invocation
|
|
27
|
+
* Sharing Decided` records the per-invocation sharing decision (default /
|
|
28
28
|
* opt-in / opt-out and whether anything was donated) — also content-free.
|
|
29
29
|
*/
|
|
30
30
|
import { type DefinedTelemetryTrace, type TelemetryStore } from '@sanity/telemetry';
|
|
@@ -47,14 +47,11 @@ export declare const WorkflowDefinitionShared: import("@sanity/telemetry").Defin
|
|
|
47
47
|
/**
|
|
48
48
|
* How the donation decision was reached, and what it was. Paired with `shared`
|
|
49
49
|
* (whether it actually reached the endpoint) this keeps every case distinct —
|
|
50
|
-
* an explicit
|
|
51
|
-
* failed POST:
|
|
50
|
+
* an explicit opt-out vs default sharing, and a chosen share vs a failed POST:
|
|
52
51
|
* - `opt-in` / `opt-out` — an explicit `--share-defs` / `--no-share-defs`.
|
|
53
|
-
* - `
|
|
54
|
-
* - `remembered-share` / `remembered-decline` — a persisted earlier answer.
|
|
55
|
-
* - `unattended` — CI / `DO_NOT_TRACK` / non-TTY: never asked, never shares.
|
|
52
|
+
* - `default` — no sharing flag; definitions are donated in every environment.
|
|
56
53
|
*/
|
|
57
|
-
export type DefinitionSharingDecision = 'opt-in' | 'opt-out' | '
|
|
54
|
+
export type DefinitionSharingDecision = 'opt-in' | 'opt-out' | 'default';
|
|
58
55
|
export interface WorkflowDefinitionSharingDecidedData {
|
|
59
56
|
decision: DefinitionSharingDecision;
|
|
60
57
|
/** Whether the donation actually reached the feedback endpoint — `false` for
|
|
@@ -93,9 +90,8 @@ export declare function setCliTelemetry(telemetry: CliTelemetry): void;
|
|
|
93
90
|
export declare function clearCliTelemetry(): void;
|
|
94
91
|
/** Build the built-in Sanity-intake shell over an authenticated,
|
|
95
92
|
* project-bound client. `forceSend` overrides the environment denial
|
|
96
|
-
* (CI / `DO_NOT_TRACK`)
|
|
97
|
-
*
|
|
98
|
-
* vars). Account-level consent is unaffected: the intake still fetches
|
|
93
|
+
* (CI / `DO_NOT_TRACK`) when a deploy may share definitions—flagless by
|
|
94
|
+
* default or explicitly enabled. Account-level consent is unaffected: the intake still fetches
|
|
99
95
|
* `/intake/telemetry-status`, so `sanity telemetry disable` still mutes. */
|
|
100
96
|
export declare function createBuiltinTelemetry(args: {
|
|
101
97
|
client: TelemetryIntakeClient;
|
|
@@ -119,6 +115,18 @@ export declare function raceWithDeadline<T>(work: Promise<T>, deadlineMs: number
|
|
|
119
115
|
* the same payload through its `log` seam and owns its own delivery.
|
|
120
116
|
*/
|
|
121
117
|
export declare function finishCliTelemetry(data: WorkflowCliCommandData): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* The command id a trace reports: the resolved class's canonical id when
|
|
120
|
+
* oclif supplies it, else the id as typed. An alias invocation must report
|
|
121
|
+
* under the same name as the canonical form — one command, one telemetry
|
|
122
|
+
* identity.
|
|
123
|
+
*/
|
|
124
|
+
export declare function traceCommandId(options: {
|
|
125
|
+
id: string;
|
|
126
|
+
Command?: {
|
|
127
|
+
id: string;
|
|
128
|
+
} | undefined;
|
|
129
|
+
}): string;
|
|
122
130
|
/**
|
|
123
131
|
* The names of declared flags present in `argv` — long form (`--name`,
|
|
124
132
|
* `--name=value`), negated booleans (`--no-name`), and short chars
|
package/dist/lib/telemetry.js
CHANGED
|
@@ -12,8 +12,8 @@ export const WorkflowDefinitionShared = defineEvent({
|
|
|
12
12
|
});
|
|
13
13
|
export const WorkflowDefinitionSharingDecided = defineEvent({
|
|
14
14
|
name: 'Editorial Workflows Definition Sharing Decided',
|
|
15
|
-
version:
|
|
16
|
-
description: 'One per deploy invocation that created new definition versions:
|
|
15
|
+
version: 2,
|
|
16
|
+
description: 'One per deploy invocation that created new definition versions: whether donation followed the default or an explicit flag, whether anything reached the endpoint, and how many versions were eligible. Content-free — measures the true opt-out rate without ever carrying the document.',
|
|
17
17
|
});
|
|
18
18
|
const OFF = { kind: 'off', logger: noopTelemetry };
|
|
19
19
|
let current = OFF;
|
|
@@ -77,6 +77,9 @@ export async function finishCliTelemetry(data) {
|
|
|
77
77
|
store.end();
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
export function traceCommandId(options) {
|
|
81
|
+
return options.Command?.id ?? options.id;
|
|
82
|
+
}
|
|
80
83
|
export function usedFlagNames(argv, flagDefs) {
|
|
81
84
|
const terminator = argv.indexOf('--');
|
|
82
85
|
const tokens = argv.slice(0, terminator === -1 ? argv.length : terminator);
|
package/dist/lib/ui.d.ts
CHANGED
|
@@ -32,6 +32,18 @@ export declare function clipToLimit<T>(rows: T[], limit: number): {
|
|
|
32
32
|
rows: T[];
|
|
33
33
|
note?: string;
|
|
34
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* The shared tail of a list command: {@link clipToLimit} the probe-fetched
|
|
37
|
+
* rows, render them through {@link formatTable}, print every line, and print
|
|
38
|
+
* the clip note (when present) beneath the table.
|
|
39
|
+
*/
|
|
40
|
+
export declare function logClippedTable<T>({ rows, limit, headers, toCells, log, }: {
|
|
41
|
+
rows: T[];
|
|
42
|
+
limit: number;
|
|
43
|
+
headers: string[];
|
|
44
|
+
toCells: (row: T) => string[];
|
|
45
|
+
log: (line: string) => void;
|
|
46
|
+
}): void;
|
|
35
47
|
/**
|
|
36
48
|
* The `▸ label` banner printed above a group's output when a run spans
|
|
37
49
|
* several groups (deployments for deploy, resources for fanned-out reads),
|
package/dist/lib/ui.js
CHANGED
|
@@ -31,6 +31,15 @@ export function clipToLimit(rows, limit) {
|
|
|
31
31
|
note: styleText('dim', `showing the first ${limit} — raise --limit to see more`),
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
+
export function logClippedTable({ rows, limit, headers, toCells, log, }) {
|
|
35
|
+
const { rows: clipped, note } = clipToLimit(rows, limit);
|
|
36
|
+
for (const line of formatTable(headers, clipped.map(toCells))) {
|
|
37
|
+
log(line);
|
|
38
|
+
}
|
|
39
|
+
if (note) {
|
|
40
|
+
log(note);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
34
43
|
export function groupBanner({ label, index, total, }) {
|
|
35
44
|
if (total <= 1) {
|
|
36
45
|
return undefined;
|