@sanity/runtime-cli 17.12.0 → 17.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -19
- package/dist/actions/blueprints/assets.d.ts +21 -20
- package/dist/actions/blueprints/assets.js +17 -87
- package/dist/actions/blueprints/logs-polling.d.ts +3 -5
- package/dist/actions/blueprints/logs-polling.js +4 -9
- package/dist/actions/blueprints/logs.d.ts +1 -2
- package/dist/actions/blueprints/logs.js +1 -3
- package/dist/actions/blueprints/operations-state.d.ts +10 -0
- package/dist/actions/blueprints/operations-state.js +42 -0
- package/dist/actions/blueprints/operations.d.ts +2 -9
- package/dist/actions/blueprints/operations.js +1 -15
- package/dist/actions/blueprints/pre-deploy.d.ts +14 -9
- package/dist/actions/blueprints/pre-deploy.js +83 -26
- package/dist/actions/blueprints/process-assets.d.ts +12 -11
- package/dist/actions/blueprints/process-assets.js +8 -7
- package/dist/actions/blueprints/stacks.d.ts +4 -4
- package/dist/actions/blueprints/stacks.js +2 -4
- package/dist/actions/blueprints/wait-for-operation.d.ts +1 -10
- package/dist/actions/blueprints/wait-for-operation.js +56 -101
- package/dist/baseCommands.js +6 -1
- package/dist/cores/blueprints/deploy.js +28 -6
- package/dist/cores/blueprints/destroy.js +4 -2
- package/dist/cores/blueprints/doctor.js +1 -1
- package/dist/cores/blueprints/init.js +3 -1
- package/dist/cores/blueprints/logs.js +11 -5
- package/dist/cores/functions/build.js +4 -5
- package/dist/cores/functions/test.js +3 -1
- package/dist/cores/index.js +3 -1
- package/dist/server/handlers/invoke.js +1 -1
- package/dist/utils/blueprints/resolve-deployed-resources.d.ts +2 -3
- package/dist/utils/blueprints/resolve-deployed-resources.js +2 -2
- package/dist/utils/blueprints/wait-for.d.ts +60 -0
- package/dist/utils/blueprints/wait-for.js +115 -0
- package/dist/utils/functions/child-runtime/durable.js +2 -2
- package/dist/utils/functions/invoke/assert-console.d.ts +5 -0
- package/dist/utils/functions/invoke/assert-console.js +60 -0
- package/dist/utils/functions/invoke/local.js +9 -0
- package/dist/utils/functions/is-folder-larger-than.d.ts +5 -0
- package/dist/utils/functions/is-folder-larger-than.js +38 -0
- package/dist/utils/functions/prepare-asset.d.ts +2 -0
- package/dist/utils/functions/prepare-asset.js +43 -6
- package/dist/utils/studios/prepare-asset.js +5 -3
- package/dist/utils/types.d.ts +18 -4
- package/dist/utils/types.js +0 -3
- package/oclif.manifest.json +1 -1
- package/package.json +2 -2
- package/dist/utils/functions/get-folder-size.d.ts +0 -5
- package/dist/utils/functions/get-folder-size.js +0 -24
|
@@ -1,15 +1,51 @@
|
|
|
1
|
+
import { SANITY_FUNCTION_PREFIX } from '../../constants.js';
|
|
1
2
|
import { resolveDeployedResources } from '../../utils/blueprints/resolve-deployed-resources.js';
|
|
2
3
|
import { CODE_OPERATION_UNCONFIRMED, EXIT_OPERATION_UNCONFIRMED } from '../../utils/exit-codes.js';
|
|
4
|
+
import { prepareAsset as prepareFunctionAsset } from '../../utils/functions/prepare-asset.js';
|
|
3
5
|
import { runCommand } from '../../utils/package-manager.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
+
import { prepareStudioAsset } from '../../utils/studios/prepare-asset.js';
|
|
7
|
+
import { setupLogPolling } from './logs-polling.js';
|
|
8
|
+
import { getOperationStateInfo } from './operations-state.js';
|
|
6
9
|
import { processAssets } from './process-assets.js';
|
|
7
10
|
import { updateStack } from './stacks.js';
|
|
8
|
-
|
|
11
|
+
/**
|
|
12
|
+
* A list of configurations for pre-deployment of resources. The first resourceType to match in the list will take precedence.
|
|
13
|
+
*/
|
|
14
|
+
const preDeployConfigs = [
|
|
15
|
+
{
|
|
16
|
+
resourceType: 'sanity.studio',
|
|
17
|
+
async prepareAsset(options) {
|
|
18
|
+
return prepareStudioAsset({
|
|
19
|
+
resource: options.resource,
|
|
20
|
+
blueprintFileDir: options.blueprintFileDir,
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
resourceType: (type) => type.startsWith(SANITY_FUNCTION_PREFIX),
|
|
26
|
+
async prepareAsset(options) {
|
|
27
|
+
return prepareFunctionAsset({
|
|
28
|
+
resource: options.resource,
|
|
29
|
+
blueprintFileDir: options.blueprintFileDir,
|
|
30
|
+
installer: options.installer,
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
function hasPreDeployResources(resources) {
|
|
36
|
+
return resources.some((r) => {
|
|
37
|
+
return !!getPredeployConfig(r);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function getPredeployConfig(r) {
|
|
41
|
+
return preDeployConfigs.find((c) => {
|
|
42
|
+
return ((typeof c.resourceType === 'function' && c.resourceType(r.type)) ||
|
|
43
|
+
(typeof c.resourceType !== 'function' && c.resourceType === r.type));
|
|
44
|
+
});
|
|
45
|
+
}
|
|
9
46
|
export async function preDeploy(options) {
|
|
10
|
-
const { resources, stackId, auth, blueprintFileDir, installer,
|
|
11
|
-
|
|
12
|
-
if (hasAssetResources) {
|
|
47
|
+
const { resources, stackId, auth, blueprintFileDir, installer, fetchFn } = options;
|
|
48
|
+
if (hasPreDeployResources(resources)) {
|
|
13
49
|
const preDeployStackResult = await preDeployStack(options);
|
|
14
50
|
if (!preDeployStackResult.success) {
|
|
15
51
|
return preDeployStackResult;
|
|
@@ -19,17 +55,23 @@ export async function preDeploy(options) {
|
|
|
19
55
|
resources,
|
|
20
56
|
stackId,
|
|
21
57
|
auth,
|
|
22
|
-
|
|
58
|
+
fetchFn,
|
|
23
59
|
});
|
|
24
|
-
const assetResources =
|
|
25
|
-
|
|
60
|
+
const assetResources = [];
|
|
61
|
+
for (const r of resolvedResources) {
|
|
62
|
+
const config = getPredeployConfig(r);
|
|
63
|
+
if (typeof config !== 'undefined' && typeof config.prepareAsset === 'function') {
|
|
64
|
+
assetResources.push({ resource: r, prepareAsset: config.prepareAsset });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
let processedAssets = [];
|
|
26
68
|
try {
|
|
27
69
|
processedAssets = await processAssets({
|
|
28
70
|
assetResources,
|
|
29
71
|
auth,
|
|
30
72
|
blueprintFileDir,
|
|
31
73
|
installer,
|
|
32
|
-
|
|
74
|
+
fetchFn,
|
|
33
75
|
onProgress: (progress) => options.onAssetEvent?.({ type: 'progress', progress }),
|
|
34
76
|
onResult: (result) => options.onAssetEvent?.({ type: 'result', result }),
|
|
35
77
|
});
|
|
@@ -52,8 +94,8 @@ export async function preDeploy(options) {
|
|
|
52
94
|
return { success: true };
|
|
53
95
|
}
|
|
54
96
|
async function preDeployStack(options) {
|
|
55
|
-
const { scopeType, scopeId, stackId, stackName, resources, auth,
|
|
56
|
-
|
|
97
|
+
const { scopeType, scopeId, stackId, stackName, resources, auth, bin, fetchFn, waitForFn } = options;
|
|
98
|
+
options.onPreDeployStackStart?.();
|
|
57
99
|
const stackMutation = {
|
|
58
100
|
scopeType,
|
|
59
101
|
scopeId,
|
|
@@ -61,9 +103,9 @@ async function preDeployStack(options) {
|
|
|
61
103
|
preDeploy: true,
|
|
62
104
|
document: { resources },
|
|
63
105
|
};
|
|
64
|
-
const { ok: deployOk, stack, error: deployError, } = await updateStack({ stackId, stackMutation, auth,
|
|
106
|
+
const { ok: deployOk, stack, error: deployError, } = await updateStack({ stackId, stackMutation, auth, fetchFn });
|
|
65
107
|
if (!deployOk) {
|
|
66
|
-
|
|
108
|
+
options.onPreDeployStackFail?.();
|
|
67
109
|
return {
|
|
68
110
|
success: false,
|
|
69
111
|
error: deployError || 'Failed to complete Stack pre-deployment',
|
|
@@ -73,22 +115,37 @@ async function preDeployStack(options) {
|
|
|
73
115
|
],
|
|
74
116
|
};
|
|
75
117
|
}
|
|
76
|
-
|
|
118
|
+
options.onPreDeployStackDone?.();
|
|
77
119
|
if (stack.changeCount === 0)
|
|
78
120
|
return { success: true };
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
121
|
+
options.onPreDeployOperationStart?.();
|
|
122
|
+
const outcome = await waitForFn({
|
|
123
|
+
getIdleMessage() {
|
|
124
|
+
return `The pre-deployment is still running on Sanity servers.`;
|
|
125
|
+
},
|
|
126
|
+
async onRequestLatestState() {
|
|
127
|
+
return await getOperationStateInfo({
|
|
128
|
+
stackId,
|
|
129
|
+
operationId: stack.operationId,
|
|
130
|
+
auth,
|
|
131
|
+
fetchFn,
|
|
132
|
+
includeDestroyed: false,
|
|
133
|
+
progressNoun: 'pre-deployment',
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
onStartLogPolling(options) {
|
|
137
|
+
return setupLogPolling({
|
|
138
|
+
auth,
|
|
139
|
+
fetchFn,
|
|
140
|
+
operationId: stack.operationId,
|
|
141
|
+
stackId,
|
|
142
|
+
onActivity: options.onActivity,
|
|
143
|
+
onLogEntry: options.onLogEntry,
|
|
144
|
+
});
|
|
145
|
+
},
|
|
89
146
|
});
|
|
90
147
|
if (outcome.type === 'completed') {
|
|
91
|
-
|
|
148
|
+
options.onPreDeployOperationDone?.();
|
|
92
149
|
}
|
|
93
150
|
else if (outcome.type === 'unconfirmed') {
|
|
94
151
|
return {
|
|
@@ -1,29 +1,30 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BlueprintResource } from '@sanity/blueprints';
|
|
2
2
|
import type { AssetResource, AuthParams, InstallerType } from '../../utils/types.js';
|
|
3
|
-
type ProcessAssetsOptions = {
|
|
4
|
-
assetResources: AssetResource[];
|
|
3
|
+
type ProcessAssetsOptions<R extends BlueprintResource> = {
|
|
4
|
+
assetResources: AssetResource<R>[];
|
|
5
5
|
auth: AuthParams;
|
|
6
6
|
blueprintFileDir: string;
|
|
7
7
|
installer: InstallerType;
|
|
8
|
-
|
|
8
|
+
fetchFn: typeof fetch;
|
|
9
9
|
onProgress?: (progress: ProcessAssetsProgress) => void;
|
|
10
|
-
onResult?: (result: ProcessedAsset) => void;
|
|
10
|
+
onResult?: (result: ProcessedAsset<R>) => void;
|
|
11
11
|
};
|
|
12
12
|
export type ProcessAssetsProgress = {
|
|
13
13
|
completed: number;
|
|
14
14
|
total: number;
|
|
15
15
|
slow: boolean;
|
|
16
16
|
};
|
|
17
|
-
export type ProcessedAsset = {
|
|
17
|
+
export type ProcessedAsset<R extends BlueprintResource> = {
|
|
18
18
|
success: false;
|
|
19
|
-
resource:
|
|
19
|
+
resource: R;
|
|
20
20
|
error: string;
|
|
21
21
|
failureKind: 'processing' | 'upload';
|
|
22
22
|
} | {
|
|
23
23
|
success: true;
|
|
24
|
-
resource:
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
resource: R;
|
|
25
|
+
assetParameter: string;
|
|
26
|
+
hash: string;
|
|
27
|
+
exists: boolean;
|
|
27
28
|
};
|
|
28
|
-
export declare function processAssets(options: ProcessAssetsOptions): Promise<ProcessedAsset[]>;
|
|
29
|
+
export declare function processAssets<R extends BlueprintResource>(options: ProcessAssetsOptions<R>): Promise<ProcessedAsset<R>[]>;
|
|
29
30
|
export {};
|
|
@@ -21,8 +21,8 @@ export async function processAssets(options) {
|
|
|
21
21
|
try {
|
|
22
22
|
for (let start = 0; start < total; start += concurrency) {
|
|
23
23
|
const batch = assetResources.slice(start, start + concurrency);
|
|
24
|
-
const batchResults = await Promise.all(batch.map(async (
|
|
25
|
-
const result = await processAsset(resource, options);
|
|
24
|
+
const batchResults = await Promise.all(batch.map(async (assetResource) => {
|
|
25
|
+
const result = await processAsset(assetResource.resource, assetResource.prepareAsset, options);
|
|
26
26
|
completed += 1;
|
|
27
27
|
reportProgress();
|
|
28
28
|
onResult?.(result);
|
|
@@ -42,13 +42,13 @@ function getAssetConcurrency() {
|
|
|
42
42
|
const configured = Number(env.SANITY_ASSET_CONCURRENCY);
|
|
43
43
|
return Number.isSafeInteger(configured) && configured > 0 ? configured : DEFAULT_ASSET_CONCURRENCY;
|
|
44
44
|
}
|
|
45
|
-
async function processAsset(resource, options) {
|
|
46
|
-
const { auth, blueprintFileDir, installer,
|
|
45
|
+
async function processAsset(resource, prepAsset, options) {
|
|
46
|
+
const { auth, blueprintFileDir, installer, fetchFn } = options;
|
|
47
47
|
let assetTimeoutTimer;
|
|
48
48
|
let result;
|
|
49
49
|
try {
|
|
50
50
|
result = await Promise.race([
|
|
51
|
-
stashAsset({ resource, blueprintFileDir, auth,
|
|
51
|
+
stashAsset({ resource, prepAsset, blueprintFileDir, auth, fetchFn, installer }),
|
|
52
52
|
new Promise((_, reject) => {
|
|
53
53
|
assetTimeoutTimer = setTimeout(() => {
|
|
54
54
|
reject(new Error(`Processing ${resource.name} timed out after ${assetTimeoutS}s`));
|
|
@@ -66,7 +66,8 @@ async function processAsset(resource, options) {
|
|
|
66
66
|
if (result.success && result.assetId) {
|
|
67
67
|
return {
|
|
68
68
|
success: true,
|
|
69
|
-
resource: { ...resource,
|
|
69
|
+
resource: { ...resource, [result.assetParameter]: result.assetId },
|
|
70
|
+
assetParameter: result.assetParameter,
|
|
70
71
|
hash: result.hash,
|
|
71
72
|
exists: result.exists,
|
|
72
73
|
};
|
|
@@ -74,7 +75,7 @@ async function processAsset(resource, options) {
|
|
|
74
75
|
return {
|
|
75
76
|
success: false,
|
|
76
77
|
resource,
|
|
77
|
-
error: result.error || 'Failed to process asset',
|
|
78
|
+
error: ('error' in result && result.error) || 'Failed to process asset',
|
|
78
79
|
failureKind: 'upload',
|
|
79
80
|
};
|
|
80
81
|
}
|
|
@@ -25,10 +25,10 @@ type GetStackResponse = {
|
|
|
25
25
|
stack?: unknown;
|
|
26
26
|
response?: Response;
|
|
27
27
|
};
|
|
28
|
-
export declare function getStack({ stackId, auth,
|
|
28
|
+
export declare function getStack({ stackId, auth, fetchFn, }: {
|
|
29
29
|
stackId: string;
|
|
30
30
|
auth: AuthParams;
|
|
31
|
-
|
|
31
|
+
fetchFn: typeof fetch;
|
|
32
32
|
}): Promise<GetStackResponse>;
|
|
33
33
|
type CreateStackResponse = GetStackResponse;
|
|
34
34
|
export declare function createStack({ stackMutation, auth, logger, }: {
|
|
@@ -55,11 +55,11 @@ type UpdateStackResponse = {
|
|
|
55
55
|
error: string;
|
|
56
56
|
stack?: unknown;
|
|
57
57
|
};
|
|
58
|
-
export declare function updateStack({ stackId, stackMutation, auth,
|
|
58
|
+
export declare function updateStack({ stackId, stackMutation, auth, fetchFn, }: {
|
|
59
59
|
stackId: string;
|
|
60
60
|
stackMutation: StackMutation;
|
|
61
61
|
auth: AuthParams;
|
|
62
|
-
|
|
62
|
+
fetchFn: typeof fetch;
|
|
63
63
|
}): Promise<UpdateStackResponse>;
|
|
64
64
|
interface PlanResourceSnapshot {
|
|
65
65
|
name: string;
|
|
@@ -24,8 +24,7 @@ export async function listStacks(auth, logger) {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
export async function getStack({ stackId, auth,
|
|
28
|
-
const fetchFn = createTracedFetch(logger);
|
|
27
|
+
export async function getStack({ stackId, auth, fetchFn, }) {
|
|
29
28
|
try {
|
|
30
29
|
const response = await fetchFn(`${stacksUrl}/${stackId}`, {
|
|
31
30
|
method: 'GET',
|
|
@@ -92,8 +91,7 @@ export async function createEmptyStack({ token, scopeType, scopeId, name, logger
|
|
|
92
91
|
}
|
|
93
92
|
return response.stack;
|
|
94
93
|
}
|
|
95
|
-
export async function updateStack({ stackId, stackMutation, auth,
|
|
96
|
-
const fetchFn = createTracedFetch(logger);
|
|
94
|
+
export async function updateStack({ stackId, stackMutation, auth, fetchFn, }) {
|
|
97
95
|
try {
|
|
98
96
|
const response = await fetchFn(`${stacksUrl}/${stackId}`, {
|
|
99
97
|
method: 'PUT',
|
|
@@ -1,15 +1,6 @@
|
|
|
1
|
+
import { type OperationOutcome } from '../../utils/blueprints/wait-for.js';
|
|
1
2
|
import type { Logger } from '../../utils/logger.js';
|
|
2
3
|
import type { AuthParams } from '../../utils/types.js';
|
|
3
|
-
/** Result of waiting for a deploy/destroy operation to settle. */
|
|
4
|
-
export type OperationOutcome = {
|
|
5
|
-
type: 'completed';
|
|
6
|
-
} | {
|
|
7
|
-
type: 'failed';
|
|
8
|
-
logHints: string[];
|
|
9
|
-
} | {
|
|
10
|
-
type: 'unconfirmed';
|
|
11
|
-
error?: string;
|
|
12
|
-
};
|
|
13
4
|
export interface WaitForOperationOptions {
|
|
14
5
|
stackId: string;
|
|
15
6
|
operationId: string;
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { runCommand } from '../../utils/package-manager.js';
|
|
1
|
+
import { createWaitFor } from '../../utils/blueprints/wait-for.js';
|
|
2
|
+
import { createTracedFetch } from '../../utils/traced-fetch.js';
|
|
4
3
|
import { setupLogPolling } from './logs-polling.js';
|
|
5
|
-
import { classifyOperationPoll, getOperation
|
|
6
|
-
const IDLE_SPINNER_DELAY_MS = 30_000;
|
|
7
|
-
const IDLE_SPINNER_UPDATE_INTERVAL_MS = 15_000;
|
|
4
|
+
import { classifyOperationPoll, getOperation } from './operations.js';
|
|
8
5
|
/**
|
|
9
6
|
* Stream logs and poll an operation until it settles.
|
|
10
7
|
* handles log-stream lifecycle, retry budget, and idle messaging;
|
|
@@ -12,107 +9,65 @@ const IDLE_SPINNER_UPDATE_INTERVAL_MS = 15_000;
|
|
|
12
9
|
*/
|
|
13
10
|
export async function waitForOperation(options) {
|
|
14
11
|
const { stackId, operationId, auth, log, bin, verbose = false, includeDestroyed, progressNoun, } = options;
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
log.showProgress();
|
|
25
|
-
};
|
|
26
|
-
try {
|
|
27
|
-
log.showProgress();
|
|
28
|
-
let lastLogAt = Date.now();
|
|
29
|
-
let nextIdleSpinnerUpdateAt = IDLE_SPINNER_DELAY_MS;
|
|
30
|
-
let consecutiveErrors = 0;
|
|
31
|
-
let lastStatus;
|
|
32
|
-
logStreamCleanup = setupLogPolling({
|
|
33
|
-
stackId,
|
|
34
|
-
operationId,
|
|
35
|
-
auth,
|
|
36
|
-
log,
|
|
37
|
-
verbose,
|
|
38
|
-
onActivity: () => {
|
|
39
|
-
lastLogAt = Date.now();
|
|
40
|
-
nextIdleSpinnerUpdateAt = IDLE_SPINNER_DELAY_MS;
|
|
41
|
-
stopIdleSpinner(true);
|
|
42
|
-
},
|
|
43
|
-
onLogEntry: (logEntry) => logHints.inspectLog(logEntry),
|
|
44
|
-
});
|
|
45
|
-
// balk in case read replica lags
|
|
46
|
-
await sleep(OPERATION_POLL_INITIAL_DELAY_MS);
|
|
47
|
-
while (true) {
|
|
48
|
-
const { ok, error: pollError, operation, response, } = await getOperation({
|
|
12
|
+
const fetchFn = createTracedFetch(log);
|
|
13
|
+
const waitFor = createWaitFor({
|
|
14
|
+
bin,
|
|
15
|
+
log,
|
|
16
|
+
verbose,
|
|
17
|
+
});
|
|
18
|
+
return await waitFor({
|
|
19
|
+
async onRequestLatestState() {
|
|
20
|
+
const { ok, error, operation, response } = await getOperation({
|
|
49
21
|
stackId,
|
|
50
22
|
operationId,
|
|
51
23
|
auth,
|
|
52
|
-
|
|
24
|
+
fetchFn,
|
|
53
25
|
includeDestroyed,
|
|
54
26
|
});
|
|
55
27
|
const httpStatus = response?.status;
|
|
56
28
|
const state = classifyOperationPoll({ ok, httpStatus, operation });
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
29
|
+
switch (state) {
|
|
30
|
+
case 'completed':
|
|
31
|
+
case 'failed':
|
|
32
|
+
return {
|
|
33
|
+
state,
|
|
34
|
+
};
|
|
35
|
+
case 'error':
|
|
36
|
+
return {
|
|
37
|
+
state,
|
|
38
|
+
error,
|
|
39
|
+
verboseError: `Could not read ${progressNoun} status (HTTP ${httpStatus ?? 'unknown'}${error ? `: ${error}` : ''})`,
|
|
40
|
+
};
|
|
41
|
+
case 'pending':
|
|
42
|
+
if (httpStatus === 404 || !operation) {
|
|
43
|
+
return {
|
|
44
|
+
state: 'not_found',
|
|
45
|
+
verboseError: `Waiting for ${progressNoun} status (HTTP 404); retrying.`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
return {
|
|
50
|
+
state,
|
|
51
|
+
latestStatus: operation.status,
|
|
52
|
+
verboseMessage: `${progressNoun} operation status: ${operation.status}.`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
default:
|
|
56
|
+
throw new Error(`Unexpected poll state: ${state}`);
|
|
63
57
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
log('');
|
|
80
|
-
return { type: 'unconfirmed', error: pollError ?? undefined };
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
consecutiveErrors = 0;
|
|
85
|
-
if (httpStatus === 404) {
|
|
86
|
-
log.verbose(`Waiting for ${progressNoun} status (HTTP 404); retrying.`);
|
|
87
|
-
}
|
|
88
|
-
else if (operation && operation.status !== lastStatus) {
|
|
89
|
-
log.verbose(`${progressNoun} operation status: ${operation.status}.`);
|
|
90
|
-
lastStatus = operation.status;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
const idleFor = Date.now() - lastLogAt;
|
|
94
|
-
if (idleFor >= nextIdleSpinnerUpdateAt) {
|
|
95
|
-
const elapsedIdleMs = IDLE_SPINNER_DELAY_MS +
|
|
96
|
-
Math.floor((idleFor - IDLE_SPINNER_DELAY_MS) / IDLE_SPINNER_UPDATE_INTERVAL_MS) *
|
|
97
|
-
IDLE_SPINNER_UPDATE_INTERVAL_MS;
|
|
98
|
-
const elapsedIdleSeconds = elapsedIdleMs / 1000;
|
|
99
|
-
const text = `No new activity for ${elapsedIdleSeconds} seconds. The ${progressNoun} is still running on Sanity servers. You can safely exit and check status later with \`${runCommand(bin)} blueprints info\`.`;
|
|
100
|
-
if (idleSpinner)
|
|
101
|
-
idleSpinner.start(text);
|
|
102
|
-
else
|
|
103
|
-
idleSpinner = log.ora(text).start();
|
|
104
|
-
nextIdleSpinnerUpdateAt = elapsedIdleMs + IDLE_SPINNER_UPDATE_INTERVAL_MS;
|
|
105
|
-
}
|
|
106
|
-
await sleep(OPERATION_POLL_INTERVAL_MS);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
if (logStreamCleanup)
|
|
111
|
-
logStreamCleanup();
|
|
112
|
-
throw error;
|
|
113
|
-
}
|
|
114
|
-
finally {
|
|
115
|
-
stopIdleSpinner();
|
|
116
|
-
log.clearProgress();
|
|
117
|
-
}
|
|
58
|
+
},
|
|
59
|
+
onStartLogPolling(options) {
|
|
60
|
+
return setupLogPolling({
|
|
61
|
+
stackId,
|
|
62
|
+
operationId,
|
|
63
|
+
auth,
|
|
64
|
+
fetchFn,
|
|
65
|
+
onActivity: options.onActivity,
|
|
66
|
+
onLogEntry: options.onLogEntry,
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
getIdleMessage() {
|
|
70
|
+
return `The ${progressNoun} is still running on Sanity servers.`;
|
|
71
|
+
},
|
|
72
|
+
});
|
|
118
73
|
}
|
package/dist/baseCommands.js
CHANGED
|
@@ -9,6 +9,7 @@ import { CI_SETUP_DOCS_URL } from './constants.js';
|
|
|
9
9
|
import { presentBlueprintParserErrors } from './utils/display/errors.js';
|
|
10
10
|
import { Logger } from './utils/logger.js';
|
|
11
11
|
import { styleText } from './utils/style-text.js';
|
|
12
|
+
import { createTracedFetch } from './utils/traced-fetch.js';
|
|
12
13
|
import { validTokenOrErrorMessage } from './utils/validated-token.js';
|
|
13
14
|
/**
|
|
14
15
|
* Fallback error-to-hint patterns for RuntimeCommand.catch().
|
|
@@ -378,7 +379,11 @@ export class ResolvedCommand extends RuntimeCommand {
|
|
|
378
379
|
// 8. Deployed Stack
|
|
379
380
|
if (needsDeployedStack) {
|
|
380
381
|
const spinner = log.ora('Loading Stack deployment...').start();
|
|
381
|
-
const response = await getStack({
|
|
382
|
+
const response = await getStack({
|
|
383
|
+
stackId: this.stackId,
|
|
384
|
+
auth: this.auth,
|
|
385
|
+
fetchFn: createTracedFetch(log),
|
|
386
|
+
});
|
|
382
387
|
if (!response.ok) {
|
|
383
388
|
spinner.fail('Could not load Stack deployment');
|
|
384
389
|
this.error('Missing Stack deployment', {
|
|
@@ -5,10 +5,12 @@ import { waitForOperation } from '../../actions/blueprints/wait-for-operation.js
|
|
|
5
5
|
import { checkUserPermission } from '../../actions/sanity/access.js';
|
|
6
6
|
import { extractDurableConfig } from '../../utils/blueprints/extract-durable-config.js';
|
|
7
7
|
import { createHintCollector } from '../../utils/blueprints/hints.js';
|
|
8
|
+
import { createWaitFor } from '../../utils/blueprints/wait-for.js';
|
|
8
9
|
import { check, niceId, severe } from '../../utils/display/presenters.js';
|
|
9
10
|
import { CODE_OPERATION_UNCONFIRMED, EXIT_OPERATION_UNCONFIRMED } from '../../utils/exit-codes.js';
|
|
10
11
|
import { runCommand } from '../../utils/package-manager.js';
|
|
11
12
|
import { styleText } from '../../utils/style-text.js';
|
|
13
|
+
import { createTracedFetch } from '../../utils/traced-fetch.js';
|
|
12
14
|
import { getWarnings } from '../../utils/warnings.js';
|
|
13
15
|
export async function blueprintDeployCore(options) {
|
|
14
16
|
const { bin = 'sanity', log: logger, auth, stackId, scopeType, scopeId, deployedStack, blueprint, flags, } = options;
|
|
@@ -20,7 +22,8 @@ export async function blueprintDeployCore(options) {
|
|
|
20
22
|
const workspaceWarnings = getWarnings({ dir: blueprintFileDir });
|
|
21
23
|
logger(`Deploying "${stackName}" ${niceId(deployedStack.id)}...`);
|
|
22
24
|
try {
|
|
23
|
-
const
|
|
25
|
+
const fetchFn = createTracedFetch(logger);
|
|
26
|
+
const { ok: checkOk, stack: currentStack } = await getStack({ stackId, auth, fetchFn });
|
|
24
27
|
if (!checkOk) {
|
|
25
28
|
return { success: false, error: 'Failed to check current Stack status' };
|
|
26
29
|
}
|
|
@@ -37,6 +40,7 @@ export async function blueprintDeployCore(options) {
|
|
|
37
40
|
}
|
|
38
41
|
const resources = [...blueprint.resources];
|
|
39
42
|
await extractDurableConfig(resources, blueprintFileDir);
|
|
43
|
+
let spinner;
|
|
40
44
|
const preDeployResult = await preDeploy({
|
|
41
45
|
scopeType,
|
|
42
46
|
scopeId,
|
|
@@ -46,10 +50,26 @@ export async function blueprintDeployCore(options) {
|
|
|
46
50
|
auth,
|
|
47
51
|
blueprintFileDir,
|
|
48
52
|
installer,
|
|
49
|
-
logger,
|
|
50
53
|
bin,
|
|
51
|
-
|
|
54
|
+
fetchFn,
|
|
55
|
+
waitForFn: createWaitFor({ bin, log: logger, verbose }),
|
|
52
56
|
onAssetEvent: createAssetEventHandler(logger),
|
|
57
|
+
onPreDeployStackStart() {
|
|
58
|
+
spinner = logger.ora('Starting pre-deploy...').start();
|
|
59
|
+
},
|
|
60
|
+
onPreDeployStackFail() {
|
|
61
|
+
spinner.fail(`${styleText('red', 'Failed')} to complete Stack pre-deployment`);
|
|
62
|
+
},
|
|
63
|
+
onPreDeployStackDone() {
|
|
64
|
+
spinner.stop().clear();
|
|
65
|
+
},
|
|
66
|
+
onPreDeployOperationStart() {
|
|
67
|
+
logger(styleText('dim', 'Stack pre-deployment progress:'));
|
|
68
|
+
logger('');
|
|
69
|
+
},
|
|
70
|
+
onPreDeployOperationDone() {
|
|
71
|
+
logger(styleText(['bold', 'green'], 'Stack pre-deployment completed!'));
|
|
72
|
+
},
|
|
53
73
|
});
|
|
54
74
|
if (!preDeployResult?.success)
|
|
55
75
|
return preDeployResult;
|
|
@@ -64,7 +84,7 @@ export async function blueprintDeployCore(options) {
|
|
|
64
84
|
userMessage: flags.message,
|
|
65
85
|
},
|
|
66
86
|
auth,
|
|
67
|
-
|
|
87
|
+
fetchFn,
|
|
68
88
|
});
|
|
69
89
|
if (!deployOk) {
|
|
70
90
|
deploySpinner.fail(`${styleText('red', 'Failed')} to update Stack deployment`);
|
|
@@ -192,8 +212,10 @@ function createAssetEventHandler(logger) {
|
|
|
192
212
|
logger.error(` ${result.error}`);
|
|
193
213
|
continue;
|
|
194
214
|
}
|
|
195
|
-
|
|
196
|
-
|
|
215
|
+
const r = resource;
|
|
216
|
+
const assetParameter = String(r[result.assetParameter]);
|
|
217
|
+
logger(` ${check(`${resource.name} ${niceId(assetParameter)}`)}`);
|
|
218
|
+
logger(` Source: ${assetParameter}`);
|
|
197
219
|
if (result.hash) {
|
|
198
220
|
const hash = result.hash;
|
|
199
221
|
const h = hash.length > 24 ? `${hash.slice(0, 8)}...${hash.slice(-12)}` : hash;
|
|
@@ -7,6 +7,7 @@ import { niceId } from '../../utils/display/presenters.js';
|
|
|
7
7
|
import { CODE_OPERATION_UNCONFIRMED, EXIT_OPERATION_UNCONFIRMED } from '../../utils/exit-codes.js';
|
|
8
8
|
import { runCommand } from '../../utils/package-manager.js';
|
|
9
9
|
import { styleText } from '../../utils/style-text.js';
|
|
10
|
+
import { createTracedFetch } from '../../utils/traced-fetch.js';
|
|
10
11
|
export async function blueprintDestroyCore(options) {
|
|
11
12
|
const { bin = 'sanity', log, token, scopeType, scopeId, localStackId, blueprintConfig, flags, } = options;
|
|
12
13
|
const { force = false, 'project-id': flagProjectId, 'organization-id': flagOrganizationId, stack: flagStack, 'no-wait': noWait = false, verbose = false, } = flags;
|
|
@@ -48,14 +49,15 @@ export async function blueprintDestroyCore(options) {
|
|
|
48
49
|
: undefined;
|
|
49
50
|
let stack;
|
|
50
51
|
try {
|
|
52
|
+
const fetchFn = createTracedFetch(log);
|
|
51
53
|
if (resolvedStackId) {
|
|
52
|
-
const overrideStack = await getStack({ stackId: resolvedStackId, auth,
|
|
54
|
+
const overrideStack = await getStack({ stackId: resolvedStackId, auth, fetchFn });
|
|
53
55
|
if (!overrideStack.ok)
|
|
54
56
|
return { success: false, error: overrideStack.error || 'Failed to get Stack' };
|
|
55
57
|
stack = overrideStack.stack;
|
|
56
58
|
}
|
|
57
59
|
else if (localStackId) {
|
|
58
|
-
const blueprintStack = await getStack({ stackId: localStackId, auth,
|
|
60
|
+
const blueprintStack = await getStack({ stackId: localStackId, auth, fetchFn });
|
|
59
61
|
if (!blueprintStack.ok)
|
|
60
62
|
return { success: false, error: blueprintStack.error || 'Failed to get Stack' };
|
|
61
63
|
stack = blueprintStack.stack;
|
|
@@ -172,7 +172,7 @@ export async function blueprintDoctorCore(options) {
|
|
|
172
172
|
const stackResponse = await getStack({
|
|
173
173
|
auth: { token, scopeType, scopeId },
|
|
174
174
|
stackId,
|
|
175
|
-
|
|
175
|
+
fetchFn,
|
|
176
176
|
});
|
|
177
177
|
if (stackResponse.ok) {
|
|
178
178
|
diagnostics.stackReady = { status: true };
|
|
@@ -15,6 +15,7 @@ import { CancelledError, promptForBlueprintType, promptForInitLocation, promptFo
|
|
|
15
15
|
import { isInteractive } from '../../utils/interactive.js';
|
|
16
16
|
import { installCommand, runCommand } from '../../utils/package-manager.js';
|
|
17
17
|
import { styleText } from '../../utils/style-text.js';
|
|
18
|
+
import { createTracedFetch } from '../../utils/traced-fetch.js';
|
|
18
19
|
import { blueprintConfigCore } from './config.js';
|
|
19
20
|
/** Derive scope type and ID from mutually exclusive flag values. */
|
|
20
21
|
function resolveScopeFromFlags(projectId, organizationId) {
|
|
@@ -288,11 +289,12 @@ export async function resolveScopeAndStack(params) {
|
|
|
288
289
|
}
|
|
289
290
|
// --stack-id: validate it exists
|
|
290
291
|
if (resolvedStackId && resolvedStackId === stackId) {
|
|
292
|
+
const fetchFn = createTracedFetch(log);
|
|
291
293
|
const spinner = log.ora('Validating Stack...').start();
|
|
292
294
|
const { ok, error } = await getStack({
|
|
293
295
|
stackId: resolvedStackId,
|
|
294
296
|
auth: { token, scopeType, scopeId },
|
|
295
|
-
|
|
297
|
+
fetchFn,
|
|
296
298
|
});
|
|
297
299
|
spinner.stop();
|
|
298
300
|
if (!ok) {
|