@sanity/runtime-cli 13.4.1 → 14.0.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 +43 -35
- package/dist/actions/blueprints/config.d.ts +3 -7
- package/dist/actions/blueprints/config.js +1 -1
- package/dist/actions/blueprints/logs-streaming.d.ts +1 -0
- package/dist/actions/blueprints/logs-streaming.js +1 -0
- package/dist/actions/blueprints/stacks.d.ts +1 -0
- package/dist/actions/blueprints/stacks.js +11 -0
- package/dist/baseCommands.d.ts +2 -0
- package/dist/baseCommands.js +6 -1
- package/dist/commands/blueprints/config.d.ts +1 -1
- package/dist/commands/blueprints/config.js +5 -5
- package/dist/commands/blueprints/deploy.d.ts +1 -0
- package/dist/commands/blueprints/deploy.js +5 -2
- package/dist/commands/blueprints/destroy.d.ts +1 -1
- package/dist/commands/blueprints/destroy.js +6 -6
- package/dist/commands/blueprints/doctor.js +5 -1
- package/dist/commands/blueprints/info.d.ts +1 -1
- package/dist/commands/blueprints/info.js +4 -4
- package/dist/commands/blueprints/init.js +1 -1
- package/dist/commands/blueprints/logs.d.ts +1 -0
- package/dist/commands/blueprints/logs.js +2 -1
- package/dist/commands/blueprints/plan.d.ts +3 -0
- package/dist/commands/blueprints/plan.js +4 -1
- package/dist/commands/functions/logs.d.ts +1 -0
- package/dist/commands/functions/logs.js +2 -1
- package/dist/cores/blueprints/config.d.ts +1 -1
- package/dist/cores/blueprints/config.js +27 -6
- package/dist/cores/blueprints/deploy.js +36 -3
- package/dist/cores/blueprints/destroy.d.ts +1 -1
- package/dist/cores/blueprints/destroy.js +30 -15
- package/dist/cores/blueprints/doctor.js +184 -90
- package/dist/cores/blueprints/info.d.ts +1 -3
- package/dist/cores/blueprints/info.js +5 -20
- package/dist/cores/blueprints/init.js +1 -1
- package/dist/cores/blueprints/plan.d.ts +1 -0
- package/dist/cores/blueprints/plan.js +20 -15
- package/dist/cores/index.d.ts +1 -0
- package/dist/cores/index.js +12 -7
- package/oclif.manifest.json +81 -24
- package/package.json +1 -1
|
@@ -1,202 +1,295 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { arch, cwd, version as nodeVersion, platform } from 'node:process';
|
|
3
|
+
import * as resolve from 'empathic/resolve';
|
|
4
|
+
import ora from 'ora';
|
|
2
5
|
import { readLocalBlueprint, } from '../../actions/blueprints/blueprint.js';
|
|
3
6
|
import { getStack } from '../../actions/blueprints/stacks.js';
|
|
4
|
-
import config from '../../config.js';
|
|
5
|
-
import {
|
|
7
|
+
import config, { RUNTIME_CLI_VERSION } from '../../config.js';
|
|
8
|
+
import { check, filePathRelativeToCwd, niceId, severe, unsure, } from '../../utils/display/presenters.js';
|
|
6
9
|
import { styleText } from '../../utils/style-text.js';
|
|
7
10
|
import { createTracedFetch } from '../../utils/traced-fetch.js';
|
|
8
11
|
import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
|
|
9
12
|
import { blueprintConfigCore } from './config.js';
|
|
10
13
|
const diagLookup = {
|
|
11
|
-
online: '
|
|
14
|
+
online: 'Host online',
|
|
12
15
|
tokenValid: 'Authenticated',
|
|
13
16
|
blueprintValid: 'Blueprint valid',
|
|
14
17
|
stackReady: 'Stack ready',
|
|
15
18
|
userHasAccess: 'User has access',
|
|
16
19
|
};
|
|
20
|
+
function sourceLabel(source) {
|
|
21
|
+
switch (source) {
|
|
22
|
+
case 'env':
|
|
23
|
+
return 'environment';
|
|
24
|
+
case 'module':
|
|
25
|
+
return 'blueprint module';
|
|
26
|
+
case 'config':
|
|
27
|
+
return 'config file';
|
|
28
|
+
case 'inferred':
|
|
29
|
+
return 'inferred';
|
|
30
|
+
default:
|
|
31
|
+
return 'unknown';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function renderSection(emit, title, rows) {
|
|
35
|
+
const pad = Math.max(...rows.map(([l]) => l.length)) + 3;
|
|
36
|
+
emit(styleText('bold', title));
|
|
37
|
+
for (const [label, value] of rows) {
|
|
38
|
+
emit(` ${styleText('dim', label.padEnd(pad))}${value}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
17
41
|
export async function blueprintDoctorCore(options) {
|
|
18
42
|
const { bin, log, token, validateResources, flags: { verbose: v, path: p, fix }, } = options;
|
|
19
|
-
const
|
|
20
|
-
log.error(styleText(['bgRedBright', 'whiteBright', 'bold'], ` ${s} `));
|
|
21
|
-
};
|
|
22
|
-
const here = cwd();
|
|
23
|
-
const path = p || here;
|
|
43
|
+
const path = p || cwd();
|
|
24
44
|
let tokenOrError;
|
|
25
|
-
log.verbose(`Checking ${filePathRelativeToCwd(path)}`);
|
|
26
|
-
// 3 states: null == unknown, true == good, false == bad
|
|
27
45
|
const diagnostics = {};
|
|
28
46
|
for (const key in diagLookup) {
|
|
29
|
-
diagnostics[key] = null;
|
|
47
|
+
diagnostics[key] = { status: null };
|
|
30
48
|
}
|
|
31
|
-
|
|
49
|
+
const envRows = [['Directory', p ? filePathRelativeToCwd(path) : path]];
|
|
50
|
+
const configRows = [];
|
|
51
|
+
const stackRows = [];
|
|
52
|
+
const spinner = ora('Running diagnostics...').start();
|
|
53
|
+
// --- ONLINE ---
|
|
32
54
|
const fetchFn = createTracedFetch(log);
|
|
33
55
|
try {
|
|
34
56
|
const res = await fetchFn(config.apiUrl);
|
|
35
57
|
if (res.ok) {
|
|
36
|
-
|
|
37
|
-
diagnostics.online = res.ok;
|
|
58
|
+
diagnostics.online = { status: true };
|
|
38
59
|
}
|
|
39
60
|
else {
|
|
40
|
-
|
|
41
|
-
diagnostics.online = false;
|
|
61
|
+
diagnostics.online = { status: false, detail: `${res.status} ${res.statusText}` };
|
|
42
62
|
}
|
|
43
63
|
}
|
|
44
|
-
catch {
|
|
45
|
-
|
|
64
|
+
catch (err) {
|
|
65
|
+
const reason = err instanceof Error ? err.message : 'unknown error';
|
|
66
|
+
diagnostics.online = { status: false, detail: reason };
|
|
46
67
|
}
|
|
47
|
-
// TOKEN
|
|
68
|
+
// --- TOKEN ---
|
|
48
69
|
if (token) {
|
|
49
70
|
tokenOrError = await validTokenOrErrorMessage(log, token);
|
|
50
71
|
if (tokenOrError.ok) {
|
|
51
|
-
diagnostics.tokenValid = true;
|
|
72
|
+
diagnostics.tokenValid = { status: true };
|
|
52
73
|
}
|
|
53
74
|
else {
|
|
54
|
-
|
|
55
|
-
diagnostics.tokenValid = false;
|
|
75
|
+
diagnostics.tokenValid = { status: false, detail: tokenOrError.error.message };
|
|
56
76
|
}
|
|
57
77
|
}
|
|
58
78
|
else {
|
|
59
|
-
|
|
60
|
-
diagnostics.tokenValid = false;
|
|
79
|
+
diagnostics.tokenValid = { status: false, detail: 'no token found' };
|
|
61
80
|
}
|
|
62
|
-
// BLUEPRINT
|
|
81
|
+
// --- BLUEPRINT ---
|
|
63
82
|
let localBlueprint;
|
|
64
83
|
try {
|
|
65
84
|
localBlueprint = await readLocalBlueprint(log, { resources: options.validateResources || false }, path);
|
|
66
|
-
|
|
85
|
+
envRows.push(['Blueprint', filePathRelativeToCwd(localBlueprint.fileInfo.blueprintFilePath)]);
|
|
67
86
|
if (localBlueprint.errors.length === 0) {
|
|
68
|
-
|
|
69
|
-
diagnostics.blueprintValid = true;
|
|
87
|
+
diagnostics.blueprintValid = { status: true };
|
|
70
88
|
}
|
|
71
89
|
else {
|
|
72
|
-
|
|
73
|
-
|
|
90
|
+
diagnostics.blueprintValid = {
|
|
91
|
+
status: false,
|
|
92
|
+
detail: `${localBlueprint.errors.length} error(s)`,
|
|
93
|
+
};
|
|
74
94
|
}
|
|
75
95
|
}
|
|
76
96
|
catch {
|
|
77
|
-
|
|
78
|
-
diagnostics.blueprintValid = false;
|
|
97
|
+
diagnostics.blueprintValid = { status: false, detail: 'unable to read file' };
|
|
79
98
|
}
|
|
99
|
+
envRows.push(['API URL', config.apiUrl]);
|
|
100
|
+
envRows.push(['Runtime', `Node.js ${nodeVersion} (${platform}-${arch})`]);
|
|
101
|
+
if (RUNTIME_CLI_VERSION) {
|
|
102
|
+
envRows.push(['Internals', `v${RUNTIME_CLI_VERSION}`]);
|
|
103
|
+
}
|
|
104
|
+
const sanityCliPkgPath = resolve.from(path, '@sanity/cli/package.json', true);
|
|
105
|
+
if (sanityCliPkgPath) {
|
|
106
|
+
try {
|
|
107
|
+
const sanityCliPkg = JSON.parse(readFileSync(sanityCliPkgPath, 'utf8'));
|
|
108
|
+
envRows.push(['Sanity CLI', sanityCliPkg.version]);
|
|
109
|
+
}
|
|
110
|
+
catch { }
|
|
111
|
+
}
|
|
112
|
+
// --- CONFIGURATION ---
|
|
80
113
|
if (localBlueprint) {
|
|
81
|
-
const { scopeType, scopeId, stackId, sources } = localBlueprint;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return 'inferred';
|
|
92
|
-
default:
|
|
93
|
-
return 'unknown';
|
|
94
|
-
}
|
|
95
|
-
};
|
|
114
|
+
const { scopeType, scopeId, stackId, sources, blueprintConfig } = localBlueprint;
|
|
115
|
+
if (blueprintConfig && 'configPath' in blueprintConfig && blueprintConfig.configPath) {
|
|
116
|
+
configRows.push(['Source', filePathRelativeToCwd(blueprintConfig.configPath)]);
|
|
117
|
+
}
|
|
118
|
+
else if (!blueprintConfig) {
|
|
119
|
+
configRows.push(['Source', styleText('dim', 'no config file')]);
|
|
120
|
+
}
|
|
121
|
+
if (blueprintConfig?.updatedAt) {
|
|
122
|
+
configRows.push(['Updated', new Date(blueprintConfig.updatedAt).toLocaleString('sv-SE')]);
|
|
123
|
+
}
|
|
96
124
|
if (scopeType && scopeId) {
|
|
97
125
|
const scopeSourceKey = scopeType === 'project' ? 'projectId' : 'organizationId';
|
|
98
|
-
|
|
126
|
+
const label = scopeType === 'project' ? 'Project' : 'Organization';
|
|
127
|
+
configRows.push([
|
|
128
|
+
label,
|
|
129
|
+
`${niceId(scopeId)} ${styleText('dim', sourceLabel(sources?.[scopeSourceKey]))}`,
|
|
130
|
+
]);
|
|
99
131
|
}
|
|
100
132
|
if (stackId) {
|
|
101
|
-
|
|
133
|
+
configRows.push([
|
|
134
|
+
'Stack',
|
|
135
|
+
`${niceId(stackId)} ${styleText('dim', sourceLabel(sources?.stackId))}`,
|
|
136
|
+
]);
|
|
102
137
|
}
|
|
103
|
-
// STACK + ACCESS
|
|
104
|
-
if (diagnostics.online
|
|
138
|
+
// --- STACK + ACCESS ---
|
|
139
|
+
if (diagnostics.online.status &&
|
|
140
|
+
diagnostics.tokenValid.status &&
|
|
141
|
+
token &&
|
|
142
|
+
stackId &&
|
|
143
|
+
scopeType &&
|
|
144
|
+
scopeId) {
|
|
105
145
|
const stackResponse = await getStack({
|
|
106
146
|
auth: { token, scopeType, scopeId },
|
|
107
147
|
stackId,
|
|
108
148
|
logger: log,
|
|
109
149
|
});
|
|
110
150
|
if (stackResponse.ok) {
|
|
111
|
-
|
|
112
|
-
diagnostics.
|
|
113
|
-
|
|
151
|
+
diagnostics.stackReady = { status: true };
|
|
152
|
+
diagnostics.userHasAccess = { status: true };
|
|
153
|
+
const stack = stackResponse.stack;
|
|
154
|
+
if (stack) {
|
|
155
|
+
const label = stack.name ? `"${stack.name}" ${niceId(stackId)}` : niceId(stackId);
|
|
156
|
+
stackRows.push(['Stack', label]);
|
|
157
|
+
if (stack.recentOperation) {
|
|
158
|
+
const op = stack.recentOperation;
|
|
159
|
+
const time = op.completedAt || op.createdAt;
|
|
160
|
+
const timestamp = time
|
|
161
|
+
? ` ${styleText('dim', new Date(time).toLocaleString('sv-SE'))}`
|
|
162
|
+
: '';
|
|
163
|
+
stackRows.push(['Operation', `${op.status}${timestamp}`]);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
114
166
|
}
|
|
115
167
|
else if (stackResponse.response?.status === 404) {
|
|
116
|
-
|
|
117
|
-
|
|
168
|
+
diagnostics.stackReady = {
|
|
169
|
+
status: false,
|
|
170
|
+
detail: `Stack ${niceId(stackId)} not found (404)`,
|
|
171
|
+
};
|
|
118
172
|
}
|
|
119
173
|
else if (stackResponse.response?.status === 403 || stackResponse.response?.status === 401) {
|
|
120
|
-
|
|
121
|
-
|
|
174
|
+
diagnostics.userHasAccess = {
|
|
175
|
+
status: false,
|
|
176
|
+
detail: `no access to Stack ${niceId(stackId)} (${stackResponse.response.status})`,
|
|
177
|
+
};
|
|
122
178
|
}
|
|
123
179
|
else {
|
|
124
|
-
|
|
180
|
+
const statusSuffix = stackResponse.response?.status
|
|
181
|
+
? ` (${stackResponse.response.status})`
|
|
182
|
+
: '';
|
|
183
|
+
diagnostics.stackReady = {
|
|
184
|
+
status: null,
|
|
185
|
+
detail: (stackResponse.error || 'unknown error') + statusSuffix,
|
|
186
|
+
};
|
|
187
|
+
diagnostics.userHasAccess = { status: null, detail: `unknown error${statusSuffix}` };
|
|
125
188
|
}
|
|
126
189
|
}
|
|
190
|
+
else if (stackId && scopeType && scopeId) {
|
|
191
|
+
diagnostics.stackReady = { status: null, detail: 'requires online + authenticated' };
|
|
192
|
+
diagnostics.userHasAccess = { status: null, detail: 'requires online + authenticated' };
|
|
193
|
+
}
|
|
194
|
+
else if (!stackId && !scopeType && !scopeId) {
|
|
195
|
+
diagnostics.stackReady = { status: null, detail: 'missing configuration' };
|
|
196
|
+
diagnostics.userHasAccess = { status: null, detail: 'missing configuration' };
|
|
197
|
+
}
|
|
127
198
|
else {
|
|
199
|
+
const missing = [];
|
|
128
200
|
if (!stackId)
|
|
129
|
-
|
|
130
|
-
if (scopeType
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (!scopeType)
|
|
140
|
-
yikes(`Blueprints configuration is missing a Scope Type`);
|
|
141
|
-
if (!scopeId)
|
|
142
|
-
yikes(`Blueprints configuration is missing a Scope ID`);
|
|
201
|
+
missing.push('Stack ID');
|
|
202
|
+
if (!scopeType)
|
|
203
|
+
missing.push('Scope Type');
|
|
204
|
+
if (!scopeId) {
|
|
205
|
+
const scopeLabel = scopeType === 'project'
|
|
206
|
+
? 'Project ID'
|
|
207
|
+
: scopeType === 'organization'
|
|
208
|
+
? 'Organization ID'
|
|
209
|
+
: 'Scope ID';
|
|
210
|
+
missing.push(scopeLabel);
|
|
143
211
|
}
|
|
212
|
+
const detail = `missing ${missing.join(', ')}`;
|
|
213
|
+
diagnostics.stackReady = { status: null, detail };
|
|
214
|
+
diagnostics.userHasAccess = { status: null, detail };
|
|
144
215
|
}
|
|
145
216
|
}
|
|
146
|
-
|
|
217
|
+
// --- RENDER REPORT ---
|
|
218
|
+
spinner.stop();
|
|
219
|
+
// Environment (verbose)
|
|
220
|
+
renderSection((msg) => log.verbose(msg), 'Environment', envRows);
|
|
221
|
+
log.verbose('');
|
|
222
|
+
// Configuration (verbose)
|
|
223
|
+
if (configRows.length > 0) {
|
|
224
|
+
renderSection((msg) => log.verbose(msg), 'Configuration', configRows);
|
|
225
|
+
log.verbose('');
|
|
226
|
+
}
|
|
227
|
+
// Deployment (verbose)
|
|
228
|
+
if (stackRows.length > 0) {
|
|
229
|
+
renderSection((msg) => log.verbose(msg), 'Deployment', stackRows);
|
|
230
|
+
log.verbose('');
|
|
231
|
+
}
|
|
232
|
+
// Checks (always visible)
|
|
233
|
+
const maxLabel = Math.max(...Object.values(diagLookup).map((l) => l.length));
|
|
234
|
+
log(styleText('bold', 'Checks'));
|
|
147
235
|
let allGood = true;
|
|
148
|
-
for (const [key,
|
|
149
|
-
|
|
236
|
+
for (const [key, entry] of Object.entries(diagnostics)) {
|
|
237
|
+
const label = diagLookup[key].padEnd(maxLabel);
|
|
238
|
+
const detail = entry.detail ? ` ${styleText('dim', entry.detail)}` : '';
|
|
239
|
+
switch (entry.status) {
|
|
150
240
|
case true:
|
|
151
|
-
log(check(
|
|
241
|
+
log(` ${check(label)}${detail}`);
|
|
152
242
|
break;
|
|
153
243
|
case false:
|
|
154
244
|
allGood = false;
|
|
155
|
-
log(severe(
|
|
245
|
+
log(` ${severe(label)}${detail}`);
|
|
156
246
|
break;
|
|
157
247
|
case null:
|
|
158
248
|
allGood = false;
|
|
159
|
-
log(unsure(
|
|
249
|
+
log(` ${unsure(label)}${detail}`);
|
|
160
250
|
break;
|
|
161
|
-
default:
|
|
162
|
-
allGood = false;
|
|
163
|
-
log(severe(`${key} is ${value}`));
|
|
164
251
|
}
|
|
165
252
|
}
|
|
253
|
+
// Result
|
|
254
|
+
const flatDiagnostics = {};
|
|
255
|
+
for (const [key, entry] of Object.entries(diagnostics)) {
|
|
256
|
+
flatDiagnostics[key] = entry.status;
|
|
257
|
+
}
|
|
258
|
+
log('');
|
|
166
259
|
const errorMessage = 'One or more checks failed';
|
|
167
260
|
if (allGood) {
|
|
168
261
|
log(styleText(['bold', 'green'], 'All checks passed'));
|
|
169
262
|
if (fix)
|
|
170
263
|
log(styleText(['bold', 'yellow'], 'Nothing to fix; --fix flag is ignored'));
|
|
171
|
-
return { success: true, data: { diagnostics } };
|
|
264
|
+
return { success: true, data: { diagnostics: flatDiagnostics } };
|
|
172
265
|
}
|
|
173
|
-
|
|
266
|
+
if (fix) {
|
|
174
267
|
if (p) {
|
|
175
268
|
return {
|
|
176
269
|
success: false,
|
|
177
270
|
error: `${errorMessage}. --fix cannot be used with --path`,
|
|
178
|
-
data: { diagnostics },
|
|
271
|
+
data: { diagnostics: flatDiagnostics },
|
|
179
272
|
};
|
|
180
273
|
}
|
|
181
274
|
if (!tokenOrError) {
|
|
182
275
|
return {
|
|
183
276
|
success: false,
|
|
184
277
|
error: `${errorMessage}. Unable to fix: Missing authentication token`,
|
|
185
|
-
data: { diagnostics },
|
|
278
|
+
data: { diagnostics: flatDiagnostics },
|
|
186
279
|
};
|
|
187
280
|
}
|
|
188
281
|
if (tokenOrError?.ok === false) {
|
|
189
282
|
return {
|
|
190
283
|
success: false,
|
|
191
284
|
error: `${errorMessage}. Unable to fix: ${tokenOrError.error.message}`,
|
|
192
|
-
data: { diagnostics },
|
|
285
|
+
data: { diagnostics: flatDiagnostics },
|
|
193
286
|
};
|
|
194
287
|
}
|
|
195
288
|
if (!localBlueprint) {
|
|
196
289
|
return {
|
|
197
290
|
success: false,
|
|
198
291
|
error: `${errorMessage}. Unable to fix: Blueprint is missing or invalid`,
|
|
199
|
-
data: { diagnostics },
|
|
292
|
+
data: { diagnostics: flatDiagnostics },
|
|
200
293
|
};
|
|
201
294
|
}
|
|
202
295
|
return blueprintConfigCore({
|
|
@@ -208,5 +301,6 @@ export async function blueprintDoctorCore(options) {
|
|
|
208
301
|
flags: { edit: true, verbose: v },
|
|
209
302
|
});
|
|
210
303
|
}
|
|
211
|
-
|
|
304
|
+
log(styleText('dim', ` Run \`${bin} blueprints doctor --fix\` to resolve configuration issues.`));
|
|
305
|
+
return { success: false, error: errorMessage, data: { diagnostics: flatDiagnostics } };
|
|
212
306
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Stack } from '../../utils/types.js';
|
|
2
2
|
import type { CoreConfig, CoreResult } from '../index.js';
|
|
3
3
|
export interface BlueprintInfoOptions extends CoreConfig {
|
|
4
|
-
auth: AuthParams;
|
|
5
4
|
stackId: string;
|
|
6
5
|
deployedStack: Stack;
|
|
7
6
|
flags: {
|
|
8
|
-
id?: string;
|
|
9
7
|
verbose?: boolean;
|
|
10
8
|
};
|
|
11
9
|
}
|
|
@@ -1,26 +1,11 @@
|
|
|
1
|
-
import { getStack } from '../../actions/blueprints/stacks.js';
|
|
2
1
|
import { formatDeployedResourceTree, formatStackInfo, } from '../../utils/display/blueprints-formatting.js';
|
|
3
|
-
import { niceId } from '../../utils/display/presenters.js';
|
|
4
2
|
export async function blueprintInfoCore(options) {
|
|
5
|
-
const { log,
|
|
6
|
-
const {
|
|
3
|
+
const { log, deployedStack, flags } = options;
|
|
4
|
+
const { verbose = false } = flags;
|
|
7
5
|
try {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const existingStackResponse = await getStack({ stackId: targetStackId, auth, logger: log });
|
|
12
|
-
if (!existingStackResponse.ok) {
|
|
13
|
-
log.error(`Could not retrieve Stack deployment info for ${niceId(targetStackId)}`);
|
|
14
|
-
return {
|
|
15
|
-
success: false,
|
|
16
|
-
error: existingStackResponse.error || 'Failed to retrieve Stack deployment',
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
stack = existingStackResponse.stack;
|
|
20
|
-
}
|
|
21
|
-
log(formatStackInfo(stack, true));
|
|
22
|
-
if (stack.resources)
|
|
23
|
-
log(formatDeployedResourceTree(stack.resources, verbose));
|
|
6
|
+
log(formatStackInfo(deployedStack, true));
|
|
7
|
+
if (deployedStack.resources)
|
|
8
|
+
log(formatDeployedResourceTree(deployedStack.resources, verbose));
|
|
24
9
|
return { success: true };
|
|
25
10
|
}
|
|
26
11
|
catch (error) {
|
|
@@ -1,25 +1,30 @@
|
|
|
1
|
-
import { getStack } from '../../actions/blueprints/stacks.js';
|
|
1
|
+
import { getStack, resolveStackIdByNameOrId } from '../../actions/blueprints/stacks.js';
|
|
2
2
|
import { formatResourceTree, stackDeployDiff } from '../../utils/display/blueprints-formatting.js';
|
|
3
3
|
import { styleText } from '../../utils/style-text.js';
|
|
4
4
|
export async function blueprintPlanCore(options) {
|
|
5
5
|
const { bin = 'sanity', log, blueprint, token, flags } = options;
|
|
6
6
|
const { verbose: _verbose = false } = flags;
|
|
7
|
-
const { scopeType, scopeId, stackId, parsedBlueprint, fileInfo } = blueprint;
|
|
7
|
+
const { scopeType, scopeId, stackId: blueprintStackId, parsedBlueprint, fileInfo } = blueprint;
|
|
8
8
|
log(`${styleText(['bold', 'blueBright'], 'Blueprint Stack deployment plan')} ${styleText('dim', `(${fileInfo.fileName})`)}`);
|
|
9
9
|
log(formatResourceTree(parsedBlueprint.resources));
|
|
10
|
-
if (token && scopeType && scopeId
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
10
|
+
if (token && scopeType && scopeId) {
|
|
11
|
+
const stackId = flags.stack
|
|
12
|
+
? await resolveStackIdByNameOrId(flags.stack, { token, scopeType, scopeId }, log)
|
|
13
|
+
: blueprintStackId;
|
|
14
|
+
if (stackId) {
|
|
15
|
+
const stackResponse = await getStack({
|
|
16
|
+
auth: { token, scopeType, scopeId },
|
|
17
|
+
stackId,
|
|
18
|
+
logger: log,
|
|
19
|
+
});
|
|
20
|
+
if (!stackResponse.ok) {
|
|
21
|
+
log(styleText('dim', 'Unable to retrieve live Stack deployment for comparison'));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
const diff = stackDeployDiff(parsedBlueprint, stackResponse.stack);
|
|
25
|
+
if (diff)
|
|
26
|
+
log(diff);
|
|
27
|
+
}
|
|
23
28
|
}
|
|
24
29
|
}
|
|
25
30
|
log(`\n Run "${styleText(['bold', 'magenta'], `${bin} blueprints deploy`)}" to deploy these Stack changes`);
|
package/dist/cores/index.d.ts
CHANGED
|
@@ -50,4 +50,5 @@ type InitBlueprintConfigParams = CoreConfig & ({
|
|
|
50
50
|
export declare function initBlueprintConfig({ bin, log, token, validateResources, validateToken, blueprintPath, }: InitBlueprintConfigParams): Promise<Result<BlueprintConfig>>;
|
|
51
51
|
export declare function initDeployedBlueprintConfig(config: Partial<BlueprintConfig> & Pick<BlueprintConfig, 'bin' | 'log' | 'token' | 'validateResources'> & {
|
|
52
52
|
validateToken?: boolean;
|
|
53
|
+
stackOverride?: string;
|
|
53
54
|
}): Promise<Result<DeployedBlueprintConfig>>;
|
package/dist/cores/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readLocalBlueprint } from '../actions/blueprints/blueprint.js';
|
|
2
|
-
import { getStack } from '../actions/blueprints/stacks.js';
|
|
2
|
+
import { getStack, resolveStackIdByNameOrId } from '../actions/blueprints/stacks.js';
|
|
3
3
|
import { presentBlueprintParserErrors } from '../utils/display/errors.js';
|
|
4
4
|
import { niceId } from '../utils/display/presenters.js';
|
|
5
5
|
import { validTokenOrErrorMessage } from '../utils/validated-token.js';
|
|
@@ -41,15 +41,20 @@ export async function initDeployedBlueprintConfig(config) {
|
|
|
41
41
|
config.blueprint = blueprintResult.value.blueprint;
|
|
42
42
|
config.token = blueprintResult.value.token;
|
|
43
43
|
}
|
|
44
|
-
const { scopeType, scopeId, stackId } = config.blueprint;
|
|
45
|
-
if (!
|
|
44
|
+
const { scopeType, scopeId, stackId: blueprintStackId } = config.blueprint;
|
|
45
|
+
if (!scopeType || !scopeId) {
|
|
46
46
|
config.log(`Incomplete configuration. Run \`${config.bin} blueprints doctor\` for diagnostics.`);
|
|
47
|
-
|
|
48
|
-
return { ok: false, error: 'Missing scope configuration for Blueprint' };
|
|
49
|
-
if (!stackId)
|
|
50
|
-
return { ok: false, error: 'Missing Stack deployment configuration for Blueprint' };
|
|
47
|
+
return { ok: false, error: 'Missing scope configuration for Blueprint' };
|
|
51
48
|
}
|
|
52
49
|
const auth = { token: config.token, scopeType, scopeId };
|
|
50
|
+
let stackId = blueprintStackId;
|
|
51
|
+
if (config.stackOverride) {
|
|
52
|
+
stackId = await resolveStackIdByNameOrId(config.stackOverride, auth, config.log);
|
|
53
|
+
}
|
|
54
|
+
if (!stackId) {
|
|
55
|
+
config.log(`Incomplete configuration. Run \`${config.bin} blueprints doctor\` for diagnostics.`);
|
|
56
|
+
return { ok: false, error: 'Missing Stack deployment configuration for Blueprint' };
|
|
57
|
+
}
|
|
53
58
|
const stackResponse = await getStack({ stackId, auth, logger: config.log });
|
|
54
59
|
if (!stackResponse.ok) {
|
|
55
60
|
config.log(`Could not retrieve Stack deployment info for ${niceId(stackId)}.`);
|