@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
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { setTimeout } from 'node:timers/promises';
|
|
2
|
-
import {
|
|
3
|
-
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { Flags } from '@oclif/core';
|
|
4
3
|
import Spinner from 'yocto-spinner';
|
|
5
4
|
import { stashAsset } from '../../actions/blueprints/assets.js';
|
|
6
|
-
import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
|
|
7
5
|
import { getStack, updateStack } from '../../actions/blueprints/stacks.js';
|
|
6
|
+
import { DeployedBlueprintCommand } from '../../baseCommands.js';
|
|
8
7
|
import { bold, niceId, red } from '../../utils/display/colors.js';
|
|
9
8
|
import { isLocalFunctionResource } from '../../utils/types.js';
|
|
10
|
-
|
|
11
|
-
export default class Deploy extends Command {
|
|
9
|
+
export default class DeployCommand extends DeployedBlueprintCommand {
|
|
12
10
|
static description = 'Deploy a Blueprint';
|
|
13
|
-
static examples = [
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> <%= command.id %>',
|
|
13
|
+
'<%= config.bin %> <%= command.id %> --no-wait',
|
|
14
|
+
];
|
|
14
15
|
static flags = {
|
|
15
16
|
'no-wait': Flags.boolean({
|
|
16
17
|
description: 'Do not wait for deployment to complete',
|
|
@@ -18,28 +19,15 @@ export default class Deploy extends Command {
|
|
|
18
19
|
}),
|
|
19
20
|
};
|
|
20
21
|
async run() {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
this.error(tokenErr.message);
|
|
24
|
-
const { flags } = await this.parse(Deploy);
|
|
25
|
-
const { errors, projectId, stackId, parsedBlueprint: { resources }, deployedStack, } = await readBlueprintOnDisk({ getStack: true, token });
|
|
26
|
-
if (errors.length > 0) {
|
|
27
|
-
// printErrors(errors) // TODO: error printer in formatting
|
|
28
|
-
this.error(`Blueprint parse errors:\n${inspect(errors, { depth: null })}`);
|
|
29
|
-
}
|
|
30
|
-
if (!deployedStack || !stackId || !projectId) {
|
|
31
|
-
this.error('Before deploying, run `sanity blueprints init`');
|
|
32
|
-
}
|
|
33
|
-
if (stackId !== deployedStack.id)
|
|
34
|
-
this.error('Stack ID mismatch');
|
|
35
|
-
const auth = { token, projectId };
|
|
22
|
+
const flags = this.flags;
|
|
23
|
+
const { resources } = this.blueprint.parsedBlueprint;
|
|
36
24
|
const validResources = resources?.filter((r) => r.type);
|
|
37
25
|
const functionResources = validResources?.filter(isLocalFunctionResource);
|
|
38
26
|
// First stash all function assets
|
|
39
27
|
if (functionResources?.length) {
|
|
40
28
|
for (const resource of functionResources) {
|
|
41
29
|
const fnSpinner = Spinner({ text: `Processing ${resource.name}...` }).start();
|
|
42
|
-
const result = await stashAsset({ resource, auth });
|
|
30
|
+
const result = await stashAsset({ resource, auth: this.auth });
|
|
43
31
|
if (result.success && result.assetId) {
|
|
44
32
|
const src = resource.src;
|
|
45
33
|
resource.src = result.assetId; // TODO: properly reference asset - for now, the API expects the assetId
|
|
@@ -54,21 +42,19 @@ export default class Deploy extends Command {
|
|
|
54
42
|
}
|
|
55
43
|
}
|
|
56
44
|
const stackPayload = {
|
|
57
|
-
projectId,
|
|
58
|
-
name: deployedStack.name,
|
|
45
|
+
projectId: this.projectId,
|
|
46
|
+
name: this.deployedStack.name,
|
|
59
47
|
document: { resources: validResources },
|
|
60
48
|
};
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
const { ok: deployOk, stack, error: deployError, } = await updateStack({ stackId: deployedStack.id, stackPayload, auth });
|
|
64
|
-
this.debug('STACK RESPONSE:', stack);
|
|
49
|
+
const spinner = Spinner({ text: 'Deploying...' }).start();
|
|
50
|
+
const { ok: deployOk, stack, error: deployError, } = await updateStack({ stackId: this.stackId, stackPayload, auth: this.auth });
|
|
65
51
|
if (deployOk) {
|
|
66
|
-
spinner.success(`
|
|
52
|
+
spinner.success(`Deployment "${bold(stack.name)}" ${niceId(stack.id)} started!`);
|
|
67
53
|
if (!flags['no-wait']) {
|
|
68
54
|
const waitSpinner = Spinner({ text: 'Waiting for deployment to complete...' }).start();
|
|
69
55
|
while (true) {
|
|
70
56
|
// TODO: watch logs and print those while polling
|
|
71
|
-
const { ok, stack: currentStack } = await getStack({ stackId: stack.id, auth });
|
|
57
|
+
const { ok, stack: currentStack } = await getStack({ stackId: stack.id, auth: this.auth });
|
|
72
58
|
if (!ok) {
|
|
73
59
|
waitSpinner.error('Failed to check deployment status');
|
|
74
60
|
break;
|
|
@@ -90,12 +76,11 @@ export default class Deploy extends Command {
|
|
|
90
76
|
}
|
|
91
77
|
}
|
|
92
78
|
else {
|
|
93
|
-
this.log('Use `sanity blueprints info` to check
|
|
79
|
+
this.log('Use `sanity-run blueprints info` to check status');
|
|
94
80
|
}
|
|
95
81
|
}
|
|
96
82
|
else {
|
|
97
|
-
|
|
98
|
-
spinner.error(`${red('Failed')} to update stack`);
|
|
83
|
+
spinner.error(`${red('Failed')} to update deployment`);
|
|
99
84
|
this.log(`Error: ${deployError || JSON.stringify(stack, null, 2) || 'Unknown error'}`);
|
|
100
85
|
}
|
|
101
86
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default class
|
|
1
|
+
import { DeployedBlueprintCommand } from '../../baseCommands.js';
|
|
2
|
+
export default class DestroyCommand extends DeployedBlueprintCommand<typeof DestroyCommand> {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
|
-
id: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
6
|
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
projectId: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
id: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
};
|
|
9
10
|
run(): Promise<void>;
|
|
10
11
|
}
|
|
@@ -1,54 +1,51 @@
|
|
|
1
1
|
import { setTimeout } from 'node:timers/promises';
|
|
2
|
-
import {
|
|
2
|
+
import { Flags } from '@oclif/core';
|
|
3
3
|
import inquirer from 'inquirer';
|
|
4
4
|
import Spinner from 'yocto-spinner';
|
|
5
|
-
import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
|
|
6
5
|
import { destroyStack, getStack } from '../../actions/blueprints/stacks.js';
|
|
6
|
+
import { DeployedBlueprintCommand } from '../../baseCommands.js';
|
|
7
7
|
import { bold, niceId } from '../../utils/display/colors.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
static description = 'Destroy a deployed Blueprint Stack';
|
|
8
|
+
export default class DestroyCommand extends DeployedBlueprintCommand {
|
|
9
|
+
static description = 'Destroy a Blueprint deployment (will not delete local files)';
|
|
11
10
|
static examples = [
|
|
12
11
|
'<%= config.bin %> <%= command.id %>',
|
|
13
|
-
|
|
12
|
+
// LAUNCH LIMIT: 1 Stack per Project - do not allow Stack ID to be set
|
|
13
|
+
// '<%= config.bin %> <%= command.id %> --id ST-a1b2c3 --projectId a1b2c3 --force',
|
|
14
14
|
];
|
|
15
15
|
static flags = {
|
|
16
|
-
id: Flags.string({
|
|
17
|
-
description: 'Stack ID to destroy (defaults to current Stack)',
|
|
18
|
-
required: false,
|
|
19
|
-
}),
|
|
20
16
|
force: Flags.boolean({
|
|
21
17
|
description: 'Force destroy (skip confirmation)',
|
|
22
18
|
default: false,
|
|
23
19
|
}),
|
|
20
|
+
projectId: Flags.string({
|
|
21
|
+
description: 'Project associated with the Stack (defaults to current Project)',
|
|
22
|
+
dependsOn: ['id', 'force'],
|
|
23
|
+
hidden: true, // LAUNCH LIMIT: 1 Stack per Project
|
|
24
|
+
}),
|
|
25
|
+
id: Flags.string({
|
|
26
|
+
description: 'Stack ID to destroy (defaults to current Stack)',
|
|
27
|
+
dependsOn: ['projectId', 'force'],
|
|
28
|
+
hidden: true, // LAUNCH LIMIT: 1 Stack per Project
|
|
29
|
+
}),
|
|
24
30
|
};
|
|
25
31
|
async run() {
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
console.dir(errors, { depth: null });
|
|
32
|
+
const flags = this.flags;
|
|
33
|
+
if (flags.projectId && flags.id && flags.force) {
|
|
34
|
+
// just try to destroy it
|
|
35
|
+
const auth = { token: this.sanityToken, projectId: flags.projectId };
|
|
36
|
+
const { ok, error, stack } = await destroyStack({ stackId: flags.id, auth });
|
|
37
|
+
if (!ok)
|
|
38
|
+
this.error(error || 'Failed to destroy deployment');
|
|
39
|
+
this.log(`Deployment "${stack.name}" ${niceId(stack.id)} destroyed`);
|
|
35
40
|
return;
|
|
36
41
|
}
|
|
37
|
-
|
|
38
|
-
this.error('Project resource not found in blueprint');
|
|
39
|
-
const auth = { token, projectId };
|
|
40
|
-
let stack = deployedStack;
|
|
42
|
+
let stack = this.deployedStack;
|
|
41
43
|
if (flags.id) {
|
|
42
|
-
const { ok, stack: foundStack, error } = await getStack({ stackId: flags.id, auth });
|
|
44
|
+
const { ok, stack: foundStack, error } = await getStack({ stackId: flags.id, auth: this.auth });
|
|
43
45
|
if (!ok)
|
|
44
46
|
this.error(error || 'Failed to get stack');
|
|
45
47
|
stack = foundStack;
|
|
46
48
|
}
|
|
47
|
-
else if (!stack) {
|
|
48
|
-
this.error('No stack found');
|
|
49
|
-
}
|
|
50
|
-
if (!stack)
|
|
51
|
-
this.error('Stack not found. Is it deployed?');
|
|
52
49
|
const destroySpinner = Spinner({
|
|
53
50
|
text: `Destroying ${bold(stack.name)} ${niceId(stack.id)}...`,
|
|
54
51
|
color: 'red',
|
|
@@ -63,25 +60,25 @@ export default class Destroy extends Command {
|
|
|
63
60
|
},
|
|
64
61
|
]);
|
|
65
62
|
if (!confirm) {
|
|
66
|
-
this.log('
|
|
63
|
+
this.log('Deployment destruction cancelled');
|
|
67
64
|
return;
|
|
68
65
|
}
|
|
69
66
|
destroySpinner.start();
|
|
70
67
|
let i = 5;
|
|
71
68
|
while (i >= 0) {
|
|
72
|
-
destroySpinner.text = `Destroying
|
|
69
|
+
destroySpinner.text = `Destroying deployment in ${bold((i--).toString())} seconds...`;
|
|
73
70
|
await setTimeout(1000);
|
|
74
71
|
}
|
|
75
|
-
destroySpinner.text = 'Destroying
|
|
72
|
+
destroySpinner.text = 'Destroying deployment 💥';
|
|
76
73
|
await setTimeout(500);
|
|
77
74
|
}
|
|
78
75
|
else {
|
|
79
76
|
destroySpinner.start();
|
|
80
77
|
}
|
|
81
|
-
const { ok, error } = await destroyStack({ stackId: stack.id, auth });
|
|
78
|
+
const { ok, error } = await destroyStack({ stackId: stack.id, auth: this.auth });
|
|
82
79
|
if (!ok)
|
|
83
|
-
this.error(error || 'Failed to destroy
|
|
80
|
+
this.error(error || 'Failed to destroy deployment');
|
|
84
81
|
// TODO: update local config
|
|
85
|
-
destroySpinner.success(`
|
|
82
|
+
destroySpinner.success(`Deployment "${stack.name}" ${niceId(stack.id)} destroyed`);
|
|
86
83
|
}
|
|
87
84
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default class
|
|
1
|
+
import { DeployedBlueprintCommand } from '../../baseCommands.js';
|
|
2
|
+
export default class InfoCommand extends DeployedBlueprintCommand<typeof InfoCommand> {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
@@ -1,53 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
3
2
|
import { getStack } from '../../actions/blueprints/stacks.js';
|
|
3
|
+
import { DeployedBlueprintCommand } from '../../baseCommands.js';
|
|
4
4
|
import { formatResourceTree, formatStackInfo } from '../../utils/display/blueprints-formatting.js';
|
|
5
5
|
import { niceId } from '../../utils/display/colors.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
static description = 'Show information about a deployed Blueprint Stack';
|
|
6
|
+
export default class InfoCommand extends DeployedBlueprintCommand {
|
|
7
|
+
static description = 'Show information about a Blueprint deployment';
|
|
9
8
|
static examples = [
|
|
10
9
|
'<%= config.bin %> <%= command.id %>',
|
|
11
|
-
|
|
10
|
+
// LAUNCH LIMIT: 1 Stack per Project - do not allow Stack ID to be set
|
|
11
|
+
// '<%= config.bin %> <%= command.id %> --id ST-a1b2c3',
|
|
12
12
|
];
|
|
13
13
|
static flags = {
|
|
14
14
|
id: Flags.string({
|
|
15
15
|
description: 'Stack ID to show info for (defaults to current stack)',
|
|
16
|
-
|
|
16
|
+
hidden: true, // LAUNCH LIMIT: 1 Stack per Project
|
|
17
17
|
}),
|
|
18
18
|
};
|
|
19
19
|
async run() {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const { flags } = await this.parse(Info);
|
|
24
|
-
const { errors, deployedStack, projectId, stackId } = await readBlueprintOnDisk({
|
|
25
|
-
getStack: true,
|
|
26
|
-
token,
|
|
27
|
-
});
|
|
28
|
-
if (errors.length > 0) {
|
|
29
|
-
// printErrors(errors)
|
|
30
|
-
this.warn('Blueprint parse errors:');
|
|
31
|
-
console.dir(errors, { depth: null });
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
if (!stackId && !flags.id)
|
|
35
|
-
this.error('No Stack ID provided');
|
|
36
|
-
if (!projectId)
|
|
37
|
-
this.error('Missing Project ID for Blueprint');
|
|
38
|
-
const auth = { token, projectId };
|
|
39
|
-
let stack = deployedStack;
|
|
20
|
+
const flags = this.flags;
|
|
21
|
+
const stackId = flags.id || this.stackId;
|
|
22
|
+
let stack = this.deployedStack;
|
|
40
23
|
if (flags.id) {
|
|
41
|
-
const
|
|
42
|
-
if (!ok)
|
|
43
|
-
this.error(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
this.error(`Stack ${niceId(stackId)} not found`);
|
|
24
|
+
const existingStackResponse = await getStack({ stackId, auth: this.auth });
|
|
25
|
+
if (!existingStackResponse.ok) {
|
|
26
|
+
this.error(existingStackResponse.error ??
|
|
27
|
+
`Could not retrieve deployment info for ${niceId(stackId)}`);
|
|
28
|
+
}
|
|
29
|
+
stack = existingStackResponse.stack;
|
|
48
30
|
}
|
|
49
|
-
if (!stack)
|
|
50
|
-
this.error('Stack not found. Is it deployed?');
|
|
51
31
|
this.log(formatStackInfo(stack, true));
|
|
52
32
|
if (stack.resources) {
|
|
53
33
|
this.log('');
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
import type { AuthParams, Stack
|
|
3
|
-
export default class
|
|
2
|
+
import type { AuthParams, Stack } from '../../utils/types.js';
|
|
3
|
+
export default class InitCommand extends Command {
|
|
4
4
|
static description: string;
|
|
5
5
|
static examples: string[];
|
|
6
|
+
static args: {
|
|
7
|
+
dir: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
8
|
+
};
|
|
6
9
|
static flags: {
|
|
10
|
+
dir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
11
|
'blueprint-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
12
|
'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
13
|
'stack-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -12,12 +16,17 @@ export default class Init extends Command {
|
|
|
12
16
|
sanityToken: string | undefined;
|
|
13
17
|
run(): Promise<void>;
|
|
14
18
|
promptForBlueprintType(): Promise<string>;
|
|
15
|
-
|
|
19
|
+
promptForProject(): Promise<{
|
|
20
|
+
projectId: string;
|
|
21
|
+
displayName: string;
|
|
22
|
+
}>;
|
|
16
23
|
promptForStackId({ projectId }: {
|
|
17
24
|
projectId: string;
|
|
18
25
|
}): Promise<string>;
|
|
19
|
-
createEmptyStack({
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
createEmptyStack({ projectId, name, projectBased, }: {
|
|
27
|
+
projectId: string;
|
|
28
|
+
name: string;
|
|
29
|
+
projectBased?: boolean;
|
|
22
30
|
}): Promise<Stack>;
|
|
31
|
+
createProjectBasedStack(auth: AuthParams): Promise<Stack>;
|
|
23
32
|
}
|
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { cwd } from 'node:process';
|
|
3
|
-
import { Command, Flags } from '@oclif/core';
|
|
3
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
4
|
+
import chalk from 'chalk';
|
|
4
5
|
import inquirer from 'inquirer';
|
|
5
6
|
import { findBlueprintFile, writeBlueprintToDisk, writeConfigFile, } from '../../actions/blueprints/blueprint.js';
|
|
6
|
-
import { listProjects } from '../../actions/blueprints/projects.js';
|
|
7
|
-
import { createStack, listStacks } from '../../actions/blueprints/stacks.js';
|
|
8
|
-
import {
|
|
7
|
+
import { getProject, listProjects } from '../../actions/blueprints/projects.js';
|
|
8
|
+
import { createStack, getStack, listStacks } from '../../actions/blueprints/stacks.js';
|
|
9
|
+
import { niceId } from '../../utils/display/colors.js';
|
|
9
10
|
import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
|
|
10
|
-
|
|
11
|
+
const LAUNCH_LIMIT_STACK_PER_PROJECT = true;
|
|
12
|
+
export default class InitCommand extends Command {
|
|
11
13
|
static description = 'Initialize a new Blueprint';
|
|
12
14
|
static examples = [
|
|
13
15
|
'<%= config.bin %> <%= command.id %>',
|
|
16
|
+
'<%= config.bin %> <%= command.id %> [directory]',
|
|
14
17
|
'<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts>',
|
|
15
|
-
'<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId>
|
|
16
|
-
|
|
18
|
+
'<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId>',
|
|
19
|
+
// LAUNCH LIMIT: 1 Stack per Project - do not prompt for Stack, just create one
|
|
20
|
+
// '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId> --stack-id <stackId>',
|
|
21
|
+
// '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId> --stack-name <stackName>',
|
|
17
22
|
];
|
|
23
|
+
static args = {
|
|
24
|
+
dir: Args.string({
|
|
25
|
+
description: 'Directory to create the Blueprint in',
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
18
28
|
static flags = {
|
|
29
|
+
dir: Flags.string({
|
|
30
|
+
description: 'Directory to create the Blueprint in',
|
|
31
|
+
}),
|
|
19
32
|
'blueprint-type': Flags.string({
|
|
20
33
|
description: 'Blueprint manifest type to use for the Blueprint',
|
|
21
34
|
options: ['json', 'js', 'ts'],
|
|
@@ -30,52 +43,62 @@ export default class Init extends Command {
|
|
|
30
43
|
aliases: ['stack', 'stackId'],
|
|
31
44
|
dependsOn: ['project-id'],
|
|
32
45
|
exclusive: ['stack-name'],
|
|
46
|
+
hidden: true, // LAUNCH LIMIT: 1 Stack per Project
|
|
33
47
|
}),
|
|
34
48
|
'stack-name': Flags.string({
|
|
35
|
-
char: 'n',
|
|
36
49
|
description: 'Name to use for a NEW Stack',
|
|
37
50
|
aliases: ['name'],
|
|
38
|
-
dependsOn: ['project-id'],
|
|
39
51
|
exclusive: ['stack-id'],
|
|
52
|
+
hidden: true, // LAUNCH LIMIT: 1 Stack per Project
|
|
40
53
|
}),
|
|
41
54
|
};
|
|
42
55
|
sanityToken;
|
|
43
56
|
async run() {
|
|
44
|
-
const { flags } = await this.parse(
|
|
45
|
-
const { 'blueprint-type': flagBlueprintType, 'project-id': flagProjectId, 'stack-id': flagStackId, 'stack-name': flagStackName, } = flags;
|
|
46
|
-
const existingBlueprint = findBlueprintFile();
|
|
47
|
-
if (existingBlueprint) {
|
|
48
|
-
this.error(`A blueprint file already exists: ${existingBlueprint.fileName}`);
|
|
49
|
-
}
|
|
57
|
+
const { args, flags } = await this.parse(InitCommand);
|
|
50
58
|
const { token, error: tokenErr } = await validTokenOrErrorMessage();
|
|
51
59
|
if (tokenErr)
|
|
52
60
|
this.error(tokenErr.message);
|
|
53
61
|
this.sanityToken = token;
|
|
62
|
+
const { 'blueprint-type': flagBlueprintType, 'project-id': flagProjectId, 'stack-id': flagStackId, 'stack-name': flagStackName, dir: flagDir, } = flags;
|
|
63
|
+
const { dir: argDir } = args;
|
|
64
|
+
const dirProvided = argDir || flagDir;
|
|
65
|
+
const here = cwd();
|
|
66
|
+
const dir = argDir || flagDir || here;
|
|
67
|
+
const existingBlueprint = findBlueprintFile(dir);
|
|
68
|
+
if (existingBlueprint)
|
|
69
|
+
this.error('Existing Blueprint found.');
|
|
54
70
|
const blueprintExtension = flagBlueprintType || (await this.promptForBlueprintType());
|
|
55
71
|
if (!blueprintExtension)
|
|
56
72
|
this.error('Blueprint type is required.');
|
|
57
|
-
|
|
58
|
-
if (!projectId)
|
|
59
|
-
this.error('Project ID is required.');
|
|
73
|
+
let projectId = flagProjectId;
|
|
60
74
|
let stackId = flagStackId;
|
|
61
|
-
if (!
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
if (!projectId) {
|
|
76
|
+
const pickedProject = await this.promptForProject();
|
|
77
|
+
projectId = pickedProject.projectId;
|
|
78
|
+
}
|
|
79
|
+
const auth = { token: this.sanityToken, projectId };
|
|
80
|
+
if (flagStackName) {
|
|
81
|
+
// using --stack-name gets around "LAUNCH LIMIT: 1 Stack per Project"
|
|
82
|
+
const stack = await this.createEmptyStack({
|
|
83
|
+
projectId,
|
|
84
|
+
name: flagStackName,
|
|
85
|
+
projectBased: false,
|
|
86
|
+
});
|
|
87
|
+
stackId = stack.id;
|
|
88
|
+
}
|
|
89
|
+
// LAUNCH LIMIT: 1 Stack per Project - do not prompt for Stack, just create one
|
|
90
|
+
if (!stackId && LAUNCH_LIMIT_STACK_PER_PROJECT) {
|
|
91
|
+
const stack = await this.createProjectBasedStack(auth);
|
|
92
|
+
// stackId = stack.id
|
|
72
93
|
}
|
|
73
94
|
const fileName = `blueprint.${blueprintExtension}`;
|
|
74
|
-
const filePath = join(
|
|
95
|
+
const filePath = join(dir, fileName);
|
|
96
|
+
if (dirProvided)
|
|
97
|
+
this.log(`New Blueprint created: ${dirProvided}/`);
|
|
75
98
|
writeBlueprintToDisk({ blueprintFilePath: filePath });
|
|
76
|
-
this.log(`Created new blueprint:
|
|
99
|
+
this.log(`Created new blueprint: ${dirProvided ?? '.'}/${fileName}`);
|
|
77
100
|
writeConfigFile({ blueprintFilePath: filePath, projectId, stackId });
|
|
78
|
-
this.log(
|
|
101
|
+
this.log(`Created new config file: ${dirProvided ?? '.'}/.blueprint/config.json`);
|
|
79
102
|
if (blueprintExtension === 'ts') {
|
|
80
103
|
this.log('\nNote: TypeScript support requires "tsx" to be installed. Run: npm install -D tsx');
|
|
81
104
|
}
|
|
@@ -96,7 +119,7 @@ export default class Init extends Command {
|
|
|
96
119
|
]);
|
|
97
120
|
return pickedBlueprintsType;
|
|
98
121
|
}
|
|
99
|
-
async
|
|
122
|
+
async promptForProject() {
|
|
100
123
|
if (!this.sanityToken)
|
|
101
124
|
this.error('Unable to list projects. Missing API token.');
|
|
102
125
|
const { ok: projectsOk, error: projectsErr, projects, } = await listProjects({ token: this.sanityToken });
|
|
@@ -107,17 +130,17 @@ export default class Init extends Command {
|
|
|
107
130
|
}
|
|
108
131
|
const projectChoices = projects.map(({ displayName, id: projectId }) => ({
|
|
109
132
|
name: `"${displayName}" ${niceId(projectId)}`,
|
|
110
|
-
value: projectId,
|
|
133
|
+
value: { projectId, displayName },
|
|
111
134
|
}));
|
|
112
|
-
const {
|
|
135
|
+
const { pickedProject } = await inquirer.prompt([
|
|
113
136
|
{
|
|
114
137
|
type: 'list',
|
|
115
|
-
name: '
|
|
138
|
+
name: 'pickedProject',
|
|
116
139
|
message: 'Select your Sanity project:',
|
|
117
140
|
choices: projectChoices,
|
|
118
141
|
},
|
|
119
142
|
]);
|
|
120
|
-
return
|
|
143
|
+
return pickedProject;
|
|
121
144
|
}
|
|
122
145
|
async promptForStackId({ projectId }) {
|
|
123
146
|
if (!this.sanityToken)
|
|
@@ -125,11 +148,13 @@ export default class Init extends Command {
|
|
|
125
148
|
const { ok: stacksOk, error: stacksErr, stacks, } = await listStacks({ token: this.sanityToken, projectId });
|
|
126
149
|
if (!stacksOk)
|
|
127
150
|
this.error(stacksErr || 'Failed to list Stacks');
|
|
128
|
-
const stackChoices = [
|
|
151
|
+
const stackChoices = [
|
|
152
|
+
{ name: chalk.bold('✨ Create a new Stack'), value: 'new' },
|
|
153
|
+
];
|
|
129
154
|
if (stacks.length > 0) {
|
|
130
|
-
stackChoices.push(new inquirer.Separator(underline('Use an existing Stack:')));
|
|
155
|
+
stackChoices.push(new inquirer.Separator(chalk.underline('Use an existing Stack:')));
|
|
131
156
|
stackChoices.push(...stacks.map((s) => ({
|
|
132
|
-
name: `"${s.name}" ${niceId(s.id)} ${dim(`(${s.resources.length} res)`)}`,
|
|
157
|
+
name: `"${s.name}" ${niceId(s.id)} ${chalk.dim(`(${s.resources.length} res)`)}`,
|
|
133
158
|
value: s.id,
|
|
134
159
|
})));
|
|
135
160
|
}
|
|
@@ -137,7 +162,7 @@ export default class Init extends Command {
|
|
|
137
162
|
{
|
|
138
163
|
type: 'list',
|
|
139
164
|
name: 'pickedStackId',
|
|
140
|
-
message: 'Select
|
|
165
|
+
message: 'Select an existing deployment or create a new one:',
|
|
141
166
|
choices: stackChoices,
|
|
142
167
|
},
|
|
143
168
|
]);
|
|
@@ -150,18 +175,45 @@ export default class Init extends Command {
|
|
|
150
175
|
validate: (input) => input.length > 0 || 'Stack name is required',
|
|
151
176
|
},
|
|
152
177
|
]);
|
|
153
|
-
const stack = await this.createEmptyStack({
|
|
154
|
-
stackPayload: { name: stackName, projectId, document: { resources: [] } },
|
|
155
|
-
auth: { token: this.sanityToken, projectId },
|
|
156
|
-
});
|
|
178
|
+
const stack = await this.createEmptyStack({ projectId, name: stackName });
|
|
157
179
|
return stack.id;
|
|
158
180
|
}
|
|
159
181
|
return pickedStackId;
|
|
160
182
|
}
|
|
161
|
-
async createEmptyStack({
|
|
183
|
+
async createEmptyStack({ projectId, name, projectBased = true, }) {
|
|
184
|
+
if (!this.sanityToken)
|
|
185
|
+
this.error('Unable to create Stack. Missing API token.');
|
|
186
|
+
const stackPayload = {
|
|
187
|
+
name,
|
|
188
|
+
projectId,
|
|
189
|
+
useProjectBasedId: projectBased,
|
|
190
|
+
document: { resources: [] },
|
|
191
|
+
};
|
|
192
|
+
const auth = { token: this.sanityToken, projectId };
|
|
162
193
|
const response = await createStack({ stackPayload, auth });
|
|
163
194
|
if (!response.ok)
|
|
164
|
-
this.error(response.error || 'Failed to create Stack');
|
|
195
|
+
this.error(response.error || 'Failed to create new Stack');
|
|
165
196
|
return response.stack;
|
|
166
197
|
}
|
|
198
|
+
// LAUNCH LIMIT: 1 Stack per Project - create exclusive stack for project
|
|
199
|
+
async createProjectBasedStack(auth) {
|
|
200
|
+
const { projectId } = auth;
|
|
201
|
+
// get project
|
|
202
|
+
const { ok: projectOk, project } = await getProject(auth);
|
|
203
|
+
if (!projectOk)
|
|
204
|
+
this.error('Failed to find Project while creating Stack');
|
|
205
|
+
const projectDisplayName = project.displayName;
|
|
206
|
+
// check if project has a stack
|
|
207
|
+
const inferredStackId = `ST-${projectId}`;
|
|
208
|
+
const { stack: existingStack, ok: stackOk } = await getStack({ stackId: inferredStackId, auth });
|
|
209
|
+
// if existing stack, return stack
|
|
210
|
+
if (stackOk && existingStack) {
|
|
211
|
+
this.log(chalk.red(`Found existing deployment for "${projectDisplayName}" Blueprint`));
|
|
212
|
+
this.log(chalk.red('Deploying an empty Blueprint will override the existing deployment.'));
|
|
213
|
+
return existingStack;
|
|
214
|
+
}
|
|
215
|
+
// if not, create a stack
|
|
216
|
+
const stack = await this.createEmptyStack({ projectId, name: projectDisplayName });
|
|
217
|
+
return stack;
|
|
218
|
+
}
|
|
167
219
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default class
|
|
1
|
+
import { DeployedBlueprintCommand } from '../../baseCommands.js';
|
|
2
|
+
export default class LogsCommand extends DeployedBlueprintCommand<typeof LogsCommand> {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|