@sanity/runtime-cli 4.5.0 → 5.0.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/README.md +61 -84
- package/dist/actions/blueprints/blueprint.d.ts +6 -12
- package/dist/actions/blueprints/blueprint.js +55 -45
- package/dist/actions/blueprints/index.d.ts +33 -0
- package/dist/actions/blueprints/index.js +32 -0
- package/dist/actions/blueprints/projects.d.ts +9 -0
- package/dist/actions/blueprints/projects.js +12 -0
- package/dist/actions/blueprints/resources.d.ts +1 -0
- package/dist/actions/blueprints/resources.js +24 -11
- package/dist/actions/blueprints/stacks.d.ts +0 -12
- package/dist/actions/blueprints/stacks.js +1 -30
- package/dist/baseCommands.d.ts +24 -0
- package/dist/baseCommands.js +69 -0
- package/dist/commands/blueprints/add.d.ts +7 -3
- package/dist/commands/blueprints/add.js +51 -14
- package/dist/commands/blueprints/config.d.ts +9 -2
- package/dist/commands/blueprints/config.js +68 -18
- package/dist/commands/blueprints/deploy.d.ts +2 -2
- package/dist/commands/blueprints/deploy.js +18 -33
- package/dist/commands/blueprints/destroy.d.ts +4 -3
- package/dist/commands/blueprints/destroy.js +32 -35
- package/dist/commands/blueprints/info.d.ts +2 -2
- package/dist/commands/blueprints/info.js +16 -36
- package/dist/commands/blueprints/init.d.ts +15 -6
- package/dist/commands/blueprints/init.js +99 -47
- package/dist/commands/blueprints/logs.d.ts +2 -2
- package/dist/commands/blueprints/logs.js +18 -32
- package/dist/commands/blueprints/plan.d.ts +2 -2
- package/dist/commands/blueprints/plan.js +10 -16
- package/dist/commands/blueprints/stacks.d.ts +3 -2
- package/dist/commands/blueprints/stacks.js +10 -29
- package/dist/commands/functions/env/add.d.ts +2 -2
- package/dist/commands/functions/env/add.js +6 -17
- package/dist/commands/functions/env/list.d.ts +2 -2
- package/dist/commands/functions/env/list.js +10 -17
- package/dist/commands/functions/env/remove.d.ts +2 -2
- package/dist/commands/functions/env/remove.js +6 -17
- package/dist/commands/functions/invoke.d.ts +2 -2
- package/dist/commands/functions/invoke.js +7 -14
- package/dist/commands/functions/logs.d.ts +3 -7
- package/dist/commands/functions/logs.js +21 -37
- package/dist/commands/functions/test.d.ts +3 -3
- package/dist/commands/functions/test.js +10 -8
- package/dist/server/app.js +3 -3
- package/dist/server/static/vendor/vendor.bundle.d.ts +2 -2
- package/dist/utils/display/blueprints-formatting.js +2 -2
- package/dist/utils/display/colors.js +2 -0
- package/dist/utils/display/errors.d.ts +4 -0
- package/dist/utils/display/errors.js +27 -0
- package/dist/utils/display/index.d.ts +1 -0
- package/dist/utils/display/index.js +1 -0
- package/dist/utils/types.d.ts +15 -3
- package/dist/utils/types.js +9 -3
- package/oclif.manifest.json +93 -45
- package/package.json +2 -1
|
@@ -7,16 +7,16 @@ const DEFAULT_FUNCTION_TEMPLATE = /*js*/ `export async function handler({context
|
|
|
7
7
|
const time = new Date().toLocaleTimeString()
|
|
8
8
|
console.log(\`👋 Your Sanity Function was called at \${time}\`)
|
|
9
9
|
}`;
|
|
10
|
-
const DEFAULT_PACKAGE_JSON =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
10
|
+
const DEFAULT_PACKAGE_JSON = {
|
|
11
|
+
name: '',
|
|
12
|
+
type: 'module',
|
|
13
|
+
main: '',
|
|
14
|
+
};
|
|
15
15
|
/**
|
|
16
16
|
* Creates a new function resource file and adds it to the blueprint
|
|
17
17
|
*/
|
|
18
18
|
export function createFunctionResource(options) {
|
|
19
|
-
const { name, type, displayName = name, blueprintFilePath } = options;
|
|
19
|
+
const { name, type, lang, displayName = name, blueprintFilePath } = options;
|
|
20
20
|
let workingDir = cwd();
|
|
21
21
|
if (blueprintFilePath) {
|
|
22
22
|
if (!existsSync(blueprintFilePath)) {
|
|
@@ -34,19 +34,32 @@ export function createFunctionResource(options) {
|
|
|
34
34
|
if (!existsSync(functionDir)) {
|
|
35
35
|
mkdirSync(functionDir, { recursive: true });
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
if (!['ts', 'js'].includes(lang))
|
|
38
|
+
throw Error(`Unsupported language: ${lang}`);
|
|
39
|
+
// Create index.<lang> with default template
|
|
40
|
+
const indexPath = join(functionDir, `index.${lang}`);
|
|
39
41
|
writeFileSync(indexPath, DEFAULT_FUNCTION_TEMPLATE);
|
|
40
42
|
// Create package.json
|
|
41
43
|
const packagePath = join(functionDir, 'package.json');
|
|
42
|
-
const packageContent =
|
|
43
|
-
|
|
44
|
+
const packageContent = {
|
|
45
|
+
...DEFAULT_PACKAGE_JSON,
|
|
46
|
+
name,
|
|
47
|
+
main: `index.${lang}`,
|
|
48
|
+
};
|
|
49
|
+
writeFileSync(packagePath, JSON.stringify(packageContent, null, 2));
|
|
50
|
+
// type looks like 'document-publish'
|
|
51
|
+
const typeParts = type.split('-');
|
|
52
|
+
const typeName = typeParts[0];
|
|
53
|
+
const eventOn = typeParts[1];
|
|
44
54
|
// Create resource definition
|
|
45
55
|
const resourceJson = {
|
|
46
56
|
displayName,
|
|
47
57
|
name,
|
|
48
|
-
type: `sanity.function.${type}`,
|
|
49
58
|
src: `functions/${name}`,
|
|
59
|
+
type: `sanity.function.${typeName}`,
|
|
60
|
+
event: {
|
|
61
|
+
on: [eventOn],
|
|
62
|
+
},
|
|
50
63
|
};
|
|
51
64
|
// Add to blueprint or return for manual addition
|
|
52
65
|
const resource = addResourceToBlueprint({ blueprintFilePath, resource: resourceJson });
|
|
@@ -6,18 +6,6 @@ interface ListStacksResponse {
|
|
|
6
6
|
stacks: Stack[];
|
|
7
7
|
}
|
|
8
8
|
export declare function listStacks(auth: AuthParams): Promise<ListStacksResponse>;
|
|
9
|
-
interface GetStackByNameResponse {
|
|
10
|
-
ok: boolean;
|
|
11
|
-
error: string | null;
|
|
12
|
-
stack: Stack | null;
|
|
13
|
-
stackId: string | null;
|
|
14
|
-
availableStacks?: string[];
|
|
15
|
-
}
|
|
16
|
-
/** @deprecated Use getStack instead */
|
|
17
|
-
export declare function getStackByName({ name, auth, }: {
|
|
18
|
-
name: string;
|
|
19
|
-
auth: AuthParams;
|
|
20
|
-
}): Promise<GetStackByNameResponse>;
|
|
21
9
|
interface GetStackResponse {
|
|
22
10
|
ok: boolean;
|
|
23
11
|
error: string | null;
|
|
@@ -14,35 +14,6 @@ export async function listStacks(auth) {
|
|
|
14
14
|
stacks,
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
-
/** @deprecated Use getStack instead */
|
|
18
|
-
export async function getStackByName({ name, auth, }) {
|
|
19
|
-
const { ok, stacks, error } = await listStacks(auth);
|
|
20
|
-
if (!ok || !stacks) {
|
|
21
|
-
return {
|
|
22
|
-
ok: false,
|
|
23
|
-
error: error || 'Failed to retrieve stacks',
|
|
24
|
-
stack: null,
|
|
25
|
-
stackId: null,
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
const foundStack = stacks.find((stack) => stack.name === name);
|
|
29
|
-
if (!foundStack) {
|
|
30
|
-
return {
|
|
31
|
-
ok: true,
|
|
32
|
-
error: null,
|
|
33
|
-
stack: null,
|
|
34
|
-
stackId: null,
|
|
35
|
-
availableStacks: stacks.map((s) => s.name),
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
const stackResult = await getStack({ stackId: foundStack.id, auth });
|
|
39
|
-
return {
|
|
40
|
-
ok: stackResult.ok,
|
|
41
|
-
error: stackResult.error,
|
|
42
|
-
stack: stackResult.stack,
|
|
43
|
-
stackId: foundStack.id,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
17
|
export async function getStack({ stackId, auth, }) {
|
|
47
18
|
const response = await fetch(`${stacksUrl}/${stackId}`, {
|
|
48
19
|
method: 'GET',
|
|
@@ -64,7 +35,7 @@ export async function createStack({ stackPayload, auth, }) {
|
|
|
64
35
|
const stack = await response.json();
|
|
65
36
|
return {
|
|
66
37
|
ok: response.ok,
|
|
67
|
-
error: response.ok ? null : stack.message,
|
|
38
|
+
error: response.ok ? null : stack.message || stack.error,
|
|
68
39
|
stack,
|
|
69
40
|
};
|
|
70
41
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { Interfaces } from '@oclif/core';
|
|
3
|
+
import { readLocalBlueprint } from './actions/blueprints/blueprint.js';
|
|
4
|
+
import type { AuthParams, Stack } from './utils/types.js';
|
|
5
|
+
export type Flags<T extends typeof Command> = Interfaces.InferredFlags<(typeof BlueprintCommand)['baseFlags'] & T['flags']>;
|
|
6
|
+
export type Args<T extends typeof Command> = Interfaces.InferredArgs<T['args']>;
|
|
7
|
+
export declare abstract class BlueprintCommand<T extends typeof Command> extends Command {
|
|
8
|
+
protected sanityToken: string;
|
|
9
|
+
protected blueprint: Awaited<ReturnType<typeof readLocalBlueprint>>;
|
|
10
|
+
protected flags: Flags<T>;
|
|
11
|
+
protected args: Args<T>;
|
|
12
|
+
init(): Promise<void>;
|
|
13
|
+
protected catch(err: Error & {
|
|
14
|
+
exitCode?: number;
|
|
15
|
+
}): Promise<unknown>;
|
|
16
|
+
protected finally(_: Error | undefined): Promise<unknown>;
|
|
17
|
+
}
|
|
18
|
+
export declare abstract class DeployedBlueprintCommand<T extends typeof Command> extends BlueprintCommand<T> {
|
|
19
|
+
protected auth: AuthParams;
|
|
20
|
+
protected deployedStack: Stack;
|
|
21
|
+
protected projectId: string;
|
|
22
|
+
protected stackId: string;
|
|
23
|
+
init(): Promise<void>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// * https://oclif.io/docs/base_class
|
|
2
|
+
import { Command } from '@oclif/core';
|
|
3
|
+
import { readLocalBlueprint } from './actions/blueprints/blueprint.js';
|
|
4
|
+
import { getStack } from './actions/blueprints/stacks.js';
|
|
5
|
+
import { presentBlueprintParserErrors } from './utils/display/errors.js';
|
|
6
|
+
import { validTokenOrErrorMessage } from './utils/validated-token.js';
|
|
7
|
+
export class BlueprintCommand extends Command {
|
|
8
|
+
sanityToken;
|
|
9
|
+
blueprint;
|
|
10
|
+
flags;
|
|
11
|
+
args;
|
|
12
|
+
async init() {
|
|
13
|
+
const { args, flags } = await this.parse({
|
|
14
|
+
flags: this.ctor.flags,
|
|
15
|
+
baseFlags: super.ctor.baseFlags,
|
|
16
|
+
enableJsonFlag: this.ctor.enableJsonFlag,
|
|
17
|
+
args: this.ctor.args,
|
|
18
|
+
strict: this.ctor.strict,
|
|
19
|
+
});
|
|
20
|
+
this.flags = flags;
|
|
21
|
+
this.args = args;
|
|
22
|
+
await super.init();
|
|
23
|
+
const { token, error: tokenErr } = await validTokenOrErrorMessage();
|
|
24
|
+
if (tokenErr)
|
|
25
|
+
this.error(tokenErr.message);
|
|
26
|
+
this.sanityToken = token;
|
|
27
|
+
const blueprint = await readLocalBlueprint();
|
|
28
|
+
if (blueprint.errors.length > 0) {
|
|
29
|
+
this.log(presentBlueprintParserErrors(blueprint.errors));
|
|
30
|
+
this.error('Blueprint parse errors.');
|
|
31
|
+
}
|
|
32
|
+
this.blueprint = blueprint;
|
|
33
|
+
}
|
|
34
|
+
async catch(err) {
|
|
35
|
+
// add any custom logic to handle errors from the command
|
|
36
|
+
// or simply return the parent class error handling
|
|
37
|
+
return super.catch(err);
|
|
38
|
+
}
|
|
39
|
+
async finally(_) {
|
|
40
|
+
// called after run and catch regardless of whether or not the command errored
|
|
41
|
+
return super.finally(_);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class DeployedBlueprintCommand extends BlueprintCommand {
|
|
45
|
+
auth;
|
|
46
|
+
deployedStack;
|
|
47
|
+
projectId;
|
|
48
|
+
stackId;
|
|
49
|
+
async init() {
|
|
50
|
+
await super.init();
|
|
51
|
+
const { projectId, stackId } = this.blueprint;
|
|
52
|
+
if (!projectId)
|
|
53
|
+
this.error('Missing Project ID for Blueprint');
|
|
54
|
+
if (!stackId)
|
|
55
|
+
this.error('Missing Stack ID for Blueprint');
|
|
56
|
+
this.projectId = projectId;
|
|
57
|
+
this.stackId = stackId;
|
|
58
|
+
this.auth = { token: this.sanityToken, projectId };
|
|
59
|
+
const stackResponse = await getStack({ stackId, auth: this.auth });
|
|
60
|
+
if (!stackResponse.ok) {
|
|
61
|
+
this.error(stackResponse.error ?? 'Could not retrieve deployment info');
|
|
62
|
+
}
|
|
63
|
+
const { stack: deployedStack } = stackResponse;
|
|
64
|
+
if (!deployedStack) {
|
|
65
|
+
this.error('Unable to find deployed Stack. Run `sanity-run blueprints init`');
|
|
66
|
+
}
|
|
67
|
+
this.deployedStack = deployedStack;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
export default class
|
|
2
|
+
export default class AddCommand extends Command {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static args: {
|
|
@@ -7,13 +7,17 @@ export default class Add extends Command {
|
|
|
7
7
|
};
|
|
8
8
|
static flags: {
|
|
9
9
|
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
-
'
|
|
10
|
+
'fn-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
language: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
javascript: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
13
|
};
|
|
12
14
|
run(): Promise<void>;
|
|
13
15
|
promptForFunctionName(): Promise<string>;
|
|
14
16
|
promptForFunctionType(): Promise<string>;
|
|
15
|
-
|
|
17
|
+
promptForFunctionLang(): Promise<string>;
|
|
18
|
+
addFunction({ name, type, lang, }: {
|
|
16
19
|
name?: string;
|
|
17
20
|
type?: string;
|
|
21
|
+
lang?: string;
|
|
18
22
|
}): Promise<void>;
|
|
19
23
|
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
import { cwd } from 'node:process';
|
|
1
2
|
import { Args, Command, Flags } from '@oclif/core';
|
|
3
|
+
import chalk from 'chalk';
|
|
2
4
|
import highlight from 'color-json';
|
|
3
5
|
import inquirer from 'inquirer';
|
|
4
6
|
import { findBlueprintFile } from '../../actions/blueprints/blueprint.js';
|
|
5
7
|
import { createFunctionResource } from '../../actions/blueprints/resources.js';
|
|
6
8
|
import { validateFunctionName, validateFunctionType } from '../../utils/validate/resource.js';
|
|
7
|
-
export default class
|
|
8
|
-
static description = 'Add a resource to a Blueprint';
|
|
9
|
+
export default class AddCommand extends Command {
|
|
10
|
+
static description = 'Add a (function) resource to a Blueprint';
|
|
9
11
|
static examples = [
|
|
10
12
|
'<%= config.bin %> <%= command.id %> function',
|
|
11
13
|
'<%= config.bin %> <%= command.id %> function --name my-function',
|
|
12
|
-
'<%= config.bin %> <%= command.id %> function --name my-function --
|
|
14
|
+
'<%= config.bin %> <%= command.id %> function --name my-function --fn-type document-publish',
|
|
15
|
+
'<%= config.bin %> <%= command.id %> function --name my-function --fn-type document-publish --lang js',
|
|
13
16
|
];
|
|
14
17
|
static args = {
|
|
15
18
|
type: Args.string({
|
|
@@ -23,23 +26,37 @@ export default class Add extends Command {
|
|
|
23
26
|
description: 'Name of the resource to add',
|
|
24
27
|
char: 'n',
|
|
25
28
|
}),
|
|
26
|
-
'
|
|
27
|
-
description: 'Type of function
|
|
29
|
+
'fn-type': Flags.string({
|
|
30
|
+
description: 'Type of new function',
|
|
28
31
|
options: ['document-publish' /*, 'document-create', 'document-delete'*/],
|
|
32
|
+
aliases: ['function-type'],
|
|
33
|
+
// default: 'document-publish', // prompting informs user of default
|
|
29
34
|
dependsOn: ['name'],
|
|
30
35
|
}),
|
|
36
|
+
language: Flags.string({
|
|
37
|
+
description: 'Language of the new function',
|
|
38
|
+
aliases: ['function-language', 'lang'],
|
|
39
|
+
options: ['ts', 'js'],
|
|
40
|
+
default: 'ts',
|
|
41
|
+
}),
|
|
42
|
+
javascript: Flags.boolean({
|
|
43
|
+
description: 'Use JavaScript instead of TypeScript',
|
|
44
|
+
aliases: ['js'],
|
|
45
|
+
}),
|
|
31
46
|
};
|
|
32
47
|
async run() {
|
|
33
|
-
const { args, flags } = await this.parse(
|
|
48
|
+
const { args, flags } = await this.parse(AddCommand);
|
|
49
|
+
if (args.type !== 'function')
|
|
50
|
+
this.error(`Unsupported Resource type: ${args.type}`);
|
|
34
51
|
const existingBlueprint = findBlueprintFile();
|
|
35
52
|
if (!existingBlueprint) {
|
|
36
|
-
this.error('No
|
|
53
|
+
this.error('No Blueprint file found. Run `sanity-run blueprints init` first.');
|
|
37
54
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
await this.addFunction({ name: resourceName, type: functionType });
|
|
55
|
+
const { name: resourceName, 'fn-type': functionType, javascript } = flags;
|
|
56
|
+
let { lang: functionLang } = flags;
|
|
57
|
+
if (javascript)
|
|
58
|
+
functionLang = 'js';
|
|
59
|
+
await this.addFunction({ name: resourceName, type: functionType, lang: functionLang });
|
|
43
60
|
}
|
|
44
61
|
async promptForFunctionName() {
|
|
45
62
|
const { functionName } = await inquirer.prompt([
|
|
@@ -68,21 +85,41 @@ export default class Add extends Command {
|
|
|
68
85
|
]);
|
|
69
86
|
return functionType;
|
|
70
87
|
}
|
|
71
|
-
async
|
|
88
|
+
async promptForFunctionLang() {
|
|
89
|
+
const { functionLang } = await inquirer.prompt([
|
|
90
|
+
{
|
|
91
|
+
type: 'list',
|
|
92
|
+
name: 'functionLang',
|
|
93
|
+
message: 'Choose function language:',
|
|
94
|
+
choices: [
|
|
95
|
+
{ name: 'TypeScript', value: 'ts' },
|
|
96
|
+
{ name: 'JavaScript', value: 'js' },
|
|
97
|
+
],
|
|
98
|
+
default: 'ts',
|
|
99
|
+
},
|
|
100
|
+
]);
|
|
101
|
+
return functionLang;
|
|
102
|
+
}
|
|
103
|
+
async addFunction({ name, type, lang, }) {
|
|
72
104
|
const functionName = name || (await this.promptForFunctionName());
|
|
73
105
|
if (!validateFunctionName(functionName)) {
|
|
74
106
|
this.error('Invalid function name. Must be 6+ characters, no special characters, no spaces');
|
|
75
107
|
}
|
|
76
108
|
const functionType = type || (await this.promptForFunctionType());
|
|
109
|
+
const functionLang = lang || (await this.promptForFunctionLang());
|
|
77
110
|
if (!validateFunctionType(functionType)) {
|
|
78
111
|
this.error('Invalid function type. Must be one of: document-publish, document-create, document-delete');
|
|
79
112
|
}
|
|
80
113
|
const { filePath, resourceAdded, resource } = createFunctionResource({
|
|
81
114
|
name: functionName,
|
|
82
115
|
type: functionType,
|
|
116
|
+
lang: functionLang,
|
|
83
117
|
displayName: functionName,
|
|
84
118
|
});
|
|
85
|
-
this.log(`\nCreated function: ${filePath}`);
|
|
119
|
+
this.log(`\nCreated function: ${filePath.replace(cwd(), '')}`);
|
|
120
|
+
if (functionLang === 'ts') {
|
|
121
|
+
this.log(chalk.dim('To avoid committing build artifacts, it is helpful to .gitignore "functions/**/.build/**"'));
|
|
122
|
+
}
|
|
86
123
|
if (!resourceAdded) {
|
|
87
124
|
// print the resource JSON for manual addition
|
|
88
125
|
this.log('\nAdd this Function resource to your blueprint:');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
export default class
|
|
2
|
+
export default class ConfigCommand extends Command {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
@@ -9,6 +9,8 @@ export default class Config extends Command {
|
|
|
9
9
|
'stack-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
};
|
|
11
11
|
sanityToken: string | undefined;
|
|
12
|
+
projectId: string | undefined;
|
|
13
|
+
stackId: string | undefined;
|
|
12
14
|
run(): Promise<void>;
|
|
13
15
|
promptForProjectId({ knownProjectId }: {
|
|
14
16
|
knownProjectId?: string;
|
|
@@ -17,11 +19,16 @@ export default class Config extends Command {
|
|
|
17
19
|
projectId: string;
|
|
18
20
|
knownStackId?: string;
|
|
19
21
|
}): Promise<string | undefined>;
|
|
20
|
-
testConfigAndReport({ stackId, projectId }: {
|
|
22
|
+
testConfigAndReport({ stackId, projectId, reinit, }: {
|
|
21
23
|
stackId: string;
|
|
22
24
|
projectId: string;
|
|
25
|
+
reinit?: boolean;
|
|
23
26
|
}): Promise<{
|
|
24
27
|
ok: boolean;
|
|
25
28
|
error: string | null;
|
|
26
29
|
}>;
|
|
30
|
+
startReinitializeStack({ projectId, stackId }: {
|
|
31
|
+
projectId: string;
|
|
32
|
+
stackId: string;
|
|
33
|
+
}): Promise<void>;
|
|
27
34
|
}
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
import highlight from 'color-json';
|
|
3
4
|
import inquirer from 'inquirer';
|
|
4
5
|
import Spinner from 'yocto-spinner';
|
|
5
|
-
import {
|
|
6
|
-
import { listProjects } from '../../actions/blueprints/projects.js';
|
|
7
|
-
import { getStack, listStacks } from '../../actions/blueprints/stacks.js';
|
|
6
|
+
import { readLocalBlueprint, writeConfigFile } from '../../actions/blueprints/blueprint.js';
|
|
7
|
+
import { getProject, listProjects } from '../../actions/blueprints/projects.js';
|
|
8
|
+
import { createStack, getStack, listStacks } from '../../actions/blueprints/stacks.js';
|
|
8
9
|
import { bold, dim, niceId } from '../../utils/display/colors.js';
|
|
10
|
+
import { presentBlueprintParserErrors } from '../../utils/display/errors.js';
|
|
9
11
|
import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
|
|
10
|
-
export default class
|
|
12
|
+
export default class ConfigCommand extends Command {
|
|
11
13
|
static description = 'View or edit Blueprint configuration';
|
|
12
14
|
static examples = [
|
|
13
15
|
'<%= config.bin %> <%= command.id %>',
|
|
14
16
|
'<%= config.bin %> <%= command.id %> --test-config',
|
|
15
17
|
'<%= config.bin %> <%= command.id %> --edit',
|
|
16
|
-
'<%= config.bin %> <%= command.id %> --edit --project-id <projectId>
|
|
18
|
+
'<%= config.bin %> <%= command.id %> --edit --project-id <projectId>',
|
|
19
|
+
// LAUNCH LIMIT: 1 Stack per Project - do not allow Stack ID to be set
|
|
20
|
+
// '<%= config.bin %> <%= command.id %> --edit --project-id <projectId> --stack-id <stackId>',
|
|
17
21
|
];
|
|
18
22
|
static flags = {
|
|
19
23
|
'test-config': Flags.boolean({
|
|
@@ -36,20 +40,27 @@ export default class Config extends Command {
|
|
|
36
40
|
description: 'Update the Stack ID in the configuration. Requires --edit flag',
|
|
37
41
|
aliases: ['stack', 'stackId'],
|
|
38
42
|
dependsOn: ['edit'],
|
|
43
|
+
hidden: true, // LAUNCH LIMIT: 1 Stack per Project
|
|
39
44
|
}),
|
|
40
45
|
};
|
|
41
46
|
sanityToken;
|
|
47
|
+
projectId;
|
|
48
|
+
stackId;
|
|
42
49
|
async run() {
|
|
43
|
-
const { flags } = await this.parse(
|
|
50
|
+
const { flags } = await this.parse(ConfigCommand);
|
|
44
51
|
const { edit: editConfig, 'project-id': editProjectId, 'stack-id': editStackId, 'test-config': testConfig, } = flags;
|
|
45
|
-
const blueprint = await
|
|
46
|
-
const { stackId: configStackId, projectId: configProjectId } = blueprint;
|
|
52
|
+
const blueprint = await readLocalBlueprint();
|
|
53
|
+
const { stackId: configStackId, projectId: configProjectId, errors } = blueprint;
|
|
54
|
+
if (errors.length > 0) {
|
|
55
|
+
this.log(presentBlueprintParserErrors(errors));
|
|
56
|
+
this.error('Blueprint parse errors.');
|
|
57
|
+
}
|
|
47
58
|
if (!configStackId && !configProjectId) {
|
|
48
|
-
this.error('Project
|
|
59
|
+
this.error('Project configuration is missing! Use `sanity-run blueprints init` to create a configuration.');
|
|
49
60
|
}
|
|
50
61
|
this.log(bold('Current configuration:'));
|
|
51
62
|
this.log(` Sanity Project: ${niceId(configProjectId)}`);
|
|
52
|
-
this.log(` Blueprint
|
|
63
|
+
this.log(` Blueprint deployment: ${niceId(configStackId)}`);
|
|
53
64
|
if ((editProjectId || editStackId) && !editConfig) {
|
|
54
65
|
this.log('To update the configuration, use the --edit flag.');
|
|
55
66
|
return;
|
|
@@ -73,18 +84,27 @@ export default class Config extends Command {
|
|
|
73
84
|
const projectId = editProjectId || (await this.promptForProjectId({ knownProjectId: configProjectId }));
|
|
74
85
|
if (!projectId)
|
|
75
86
|
this.error('Project ID is required.');
|
|
76
|
-
|
|
87
|
+
// LAUNCH LIMIT: 1 Stack per Project - do not allow Stack ID to be set
|
|
88
|
+
// const stackId =
|
|
89
|
+
// editStackId || (await this.promptForStackId({projectId, knownStackId: configStackId}))
|
|
90
|
+
let stackId = configStackId;
|
|
77
91
|
if (testConfig) {
|
|
78
92
|
if (projectId && stackId) {
|
|
79
|
-
const { ok: newConfigOk } = await this.testConfigAndReport({
|
|
80
|
-
|
|
93
|
+
const { ok: newConfigOk } = await this.testConfigAndReport({
|
|
94
|
+
stackId,
|
|
95
|
+
projectId,
|
|
96
|
+
reinit: editConfig,
|
|
97
|
+
});
|
|
98
|
+
if (!newConfigOk)
|
|
81
99
|
this.error('Updated configuration has not been saved.');
|
|
82
|
-
}
|
|
83
100
|
}
|
|
84
101
|
else {
|
|
85
102
|
this.error('Unable to test the configuration. Both Project and Stack IDs must be set.');
|
|
86
103
|
}
|
|
87
104
|
}
|
|
105
|
+
// LAUNCH LIMIT: 1 Stack per Project - do not allow Stack ID to be set
|
|
106
|
+
if (stackId?.endsWith(projectId))
|
|
107
|
+
stackId = undefined;
|
|
88
108
|
try {
|
|
89
109
|
// update or create .blueprint/config.json
|
|
90
110
|
writeConfigFile({ projectId, stackId });
|
|
@@ -92,7 +112,8 @@ export default class Config extends Command {
|
|
|
92
112
|
}
|
|
93
113
|
catch (error) {
|
|
94
114
|
this.log('Unable to dynamically update config. Use these values in your blueprint:');
|
|
95
|
-
|
|
115
|
+
// LAUNCH LIMIT: 1 Stack per Project - do not allow Stack ID to be set
|
|
116
|
+
this.log(highlight(JSON.stringify({ metadata: { projectId /*, stackId*/ } }, null, 2)));
|
|
96
117
|
}
|
|
97
118
|
}
|
|
98
119
|
async promptForProjectId({ knownProjectId }) {
|
|
@@ -146,7 +167,7 @@ export default class Config extends Command {
|
|
|
146
167
|
}
|
|
147
168
|
return undefined;
|
|
148
169
|
}
|
|
149
|
-
async testConfigAndReport({ stackId, projectId }) {
|
|
170
|
+
async testConfigAndReport({ stackId, projectId, reinit = false, }) {
|
|
150
171
|
if (!this.sanityToken)
|
|
151
172
|
this.error('Unable to test the configuration. Missing API token.');
|
|
152
173
|
const spinner = Spinner({ text: 'Testing the configuration...' }).start();
|
|
@@ -155,12 +176,41 @@ export default class Config extends Command {
|
|
|
155
176
|
auth: { token: this.sanityToken, projectId },
|
|
156
177
|
});
|
|
157
178
|
if (!ok) {
|
|
158
|
-
spinner.error(
|
|
159
|
-
|
|
179
|
+
spinner.error('Blueprint deployment not found');
|
|
180
|
+
// check if configured project and stack can be reinitialized
|
|
181
|
+
if (reinit && stackId === `ST-${projectId}`) {
|
|
182
|
+
await this.startReinitializeStack({ projectId, stackId });
|
|
183
|
+
return { ok: true, error: null };
|
|
184
|
+
}
|
|
160
185
|
}
|
|
161
186
|
else {
|
|
162
187
|
spinner.success('Configuration is valid.');
|
|
163
188
|
}
|
|
164
189
|
return { ok, error };
|
|
165
190
|
}
|
|
191
|
+
async startReinitializeStack({ projectId, stackId }) {
|
|
192
|
+
if (!this.sanityToken)
|
|
193
|
+
this.error('Unable to reinitialize the Stack. Missing API token.');
|
|
194
|
+
const auth = { token: this.sanityToken, projectId };
|
|
195
|
+
// stack id IS ST-${projectId} – it has already been checked and doesn't exist
|
|
196
|
+
this.log(`A new Blueprint deployment can be created with the ${chalk.bold('existing')} configuration.`);
|
|
197
|
+
const { confirm } = await inquirer.prompt([
|
|
198
|
+
{
|
|
199
|
+
type: 'confirm',
|
|
200
|
+
name: 'confirm',
|
|
201
|
+
message: `Do you want to create a ${chalk.blue('new')}, empty Blueprint deployment with the ${chalk.blue('existing')} configuration?`,
|
|
202
|
+
},
|
|
203
|
+
]);
|
|
204
|
+
if (!confirm)
|
|
205
|
+
this.error('Reinitialization cancelled.');
|
|
206
|
+
const { ok: projectOk, project } = await getProject(auth);
|
|
207
|
+
if (!projectOk)
|
|
208
|
+
this.error('Failed to find Project while creating Stack');
|
|
209
|
+
const projectDisplayName = project.displayName;
|
|
210
|
+
const stackPayload = { name: projectDisplayName, projectId, document: { resources: [] } };
|
|
211
|
+
const response = await createStack({ stackPayload, auth });
|
|
212
|
+
if (!response.ok)
|
|
213
|
+
this.error(response.error || 'Failed to create new Stack');
|
|
214
|
+
this.log(`New Blueprint deployment created for "${projectDisplayName}"`);
|
|
215
|
+
}
|
|
166
216
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default class
|
|
1
|
+
import { DeployedBlueprintCommand } from '../../baseCommands.js';
|
|
2
|
+
export default class DeployCommand extends DeployedBlueprintCommand<typeof DeployCommand> {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|