@sanity/runtime-cli 4.4.0 → 4.5.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.
Files changed (82) hide show
  1. package/README.md +49 -77
  2. package/dist/actions/blueprints/assets.d.ts +1 -0
  3. package/dist/actions/blueprints/assets.js +21 -4
  4. package/dist/actions/blueprints/blueprint.d.ts +6 -12
  5. package/dist/actions/blueprints/blueprint.js +38 -45
  6. package/dist/actions/blueprints/index.d.ts +33 -0
  7. package/dist/actions/blueprints/index.js +32 -0
  8. package/dist/actions/blueprints/projects.d.ts +9 -0
  9. package/dist/actions/blueprints/projects.js +12 -0
  10. package/dist/actions/blueprints/stacks.d.ts +0 -12
  11. package/dist/actions/blueprints/stacks.js +3 -30
  12. package/dist/actions/functions/test.d.ts +2 -2
  13. package/dist/actions/functions/test.js +2 -2
  14. package/dist/baseCommands.d.ts +24 -0
  15. package/dist/baseCommands.js +69 -0
  16. package/dist/commands/blueprints/add.d.ts +1 -1
  17. package/dist/commands/blueprints/add.js +7 -6
  18. package/dist/commands/blueprints/config.d.ts +1 -1
  19. package/dist/commands/blueprints/config.js +24 -11
  20. package/dist/commands/blueprints/deploy.d.ts +2 -2
  21. package/dist/commands/blueprints/deploy.js +18 -33
  22. package/dist/commands/blueprints/destroy.d.ts +4 -3
  23. package/dist/commands/blueprints/destroy.js +32 -35
  24. package/dist/commands/blueprints/info.d.ts +2 -2
  25. package/dist/commands/blueprints/info.js +16 -36
  26. package/dist/commands/blueprints/init.d.ts +10 -2
  27. package/dist/commands/blueprints/init.js +85 -26
  28. package/dist/commands/blueprints/logs.d.ts +2 -2
  29. package/dist/commands/blueprints/logs.js +18 -32
  30. package/dist/commands/blueprints/plan.d.ts +2 -2
  31. package/dist/commands/blueprints/plan.js +10 -16
  32. package/dist/commands/blueprints/stacks.d.ts +3 -2
  33. package/dist/commands/blueprints/stacks.js +10 -29
  34. package/dist/commands/functions/env/add.d.ts +2 -2
  35. package/dist/commands/functions/env/add.js +6 -17
  36. package/dist/commands/functions/env/list.d.ts +2 -2
  37. package/dist/commands/functions/env/list.js +10 -17
  38. package/dist/commands/functions/env/remove.d.ts +2 -2
  39. package/dist/commands/functions/env/remove.js +6 -17
  40. package/dist/commands/functions/invoke.d.ts +2 -2
  41. package/dist/commands/functions/invoke.js +7 -14
  42. package/dist/commands/functions/logs.d.ts +3 -7
  43. package/dist/commands/functions/logs.js +21 -37
  44. package/dist/commands/functions/test.d.ts +3 -3
  45. package/dist/commands/functions/test.js +13 -14
  46. package/dist/server/app.js +82 -14
  47. package/dist/server/static/api.js +24 -3
  48. package/dist/server/static/components/function-list.js +4 -4
  49. package/dist/server/static/components/response-panel.js +14 -3
  50. package/dist/server/static/index.html +1 -0
  51. package/dist/utils/build-payload.d.ts +1 -1
  52. package/dist/utils/build-payload.js +3 -3
  53. package/dist/utils/bundle/bundle-function.d.ts +8 -0
  54. package/dist/utils/bundle/bundle-function.js +125 -0
  55. package/dist/utils/bundle/cleanup-source-maps.d.ts +10 -0
  56. package/dist/utils/bundle/cleanup-source-maps.js +53 -0
  57. package/dist/utils/bundle/find-up.d.ts +16 -0
  58. package/dist/utils/bundle/find-up.js +39 -0
  59. package/dist/utils/bundle/verify-handler.d.ts +2 -0
  60. package/dist/utils/bundle/verify-handler.js +13 -0
  61. package/dist/utils/child-process-wrapper.js +8 -6
  62. package/dist/utils/display/blueprints-formatting.js +2 -2
  63. package/dist/utils/display/errors.d.ts +4 -0
  64. package/dist/utils/display/errors.js +27 -0
  65. package/dist/utils/display/index.d.ts +1 -0
  66. package/dist/utils/display/index.js +1 -0
  67. package/dist/utils/functions/find-entry-point.d.ts +11 -0
  68. package/dist/utils/functions/find-entry-point.js +75 -0
  69. package/dist/utils/functions/should-bundle.d.ts +2 -0
  70. package/dist/utils/functions/should-bundle.js +23 -0
  71. package/dist/utils/invoke-local.d.ts +2 -2
  72. package/dist/utils/invoke-local.js +49 -8
  73. package/dist/utils/is-record.d.ts +1 -0
  74. package/dist/utils/is-record.js +3 -0
  75. package/dist/utils/parse-json-object.d.ts +1 -0
  76. package/dist/utils/parse-json-object.js +10 -0
  77. package/dist/utils/types.d.ts +13 -4
  78. package/dist/utils/types.js +9 -3
  79. package/oclif.manifest.json +59 -37
  80. package/package.json +5 -1
  81. package/dist/utils/is-json.d.ts +0 -1
  82. package/dist/utils/is-json.js +0 -12
@@ -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',
@@ -56,6 +27,8 @@ export async function getStack({ stackId, auth, }) {
56
27
  };
57
28
  }
58
29
  export async function createStack({ stackPayload, auth, }) {
30
+ // LAUNCH LIMIT: 1 Stack per Project
31
+ stackPayload.useProjectBasedId = true;
59
32
  const response = await fetch(stacksUrl, {
60
33
  method: 'POST',
61
34
  headers: getHeaders(auth),
@@ -64,7 +37,7 @@ export async function createStack({ stackPayload, auth, }) {
64
37
  const stack = await response.json();
65
38
  return {
66
39
  ok: response.ok,
67
- error: response.ok ? null : stack.message,
40
+ error: response.ok ? null : stack.message || stack.error,
68
41
  stack,
69
42
  };
70
43
  }
@@ -1,2 +1,2 @@
1
- import type { InvocationResponse, InvokeContextOptions, InvokePayloadOptions } from '../../utils/types.js';
2
- export declare function testAction(srcPath: string, options: InvokePayloadOptions, context: InvokeContextOptions): Promise<InvocationResponse>;
1
+ import type { InvocationResponse, InvokeContextOptions, InvokePayloadOptions, LocalFunctionResource } from '../../utils/types.js';
2
+ export declare function testAction(resource: LocalFunctionResource, options: InvokePayloadOptions, context: InvokeContextOptions): Promise<InvocationResponse>;
@@ -1,10 +1,10 @@
1
1
  import buildPayload from '../../utils/build-payload.js';
2
2
  import invoke from '../../utils/invoke-local.js';
3
- export async function testAction(srcPath, options, context) {
3
+ export async function testAction(resource, options, context) {
4
4
  const payload = buildPayload(options);
5
5
  const { timeout } = options;
6
6
  try {
7
- const { json, logs } = await invoke(srcPath, payload, context, timeout);
7
+ const { json, logs } = await invoke(resource, payload, context, timeout);
8
8
  return { error: undefined, json, logs };
9
9
  }
10
10
  catch (error) {
@@ -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 Add extends Command {
2
+ export default class AddCommand extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
@@ -1,10 +1,11 @@
1
+ import { cwd } from 'node:process';
1
2
  import { Args, Command, Flags } from '@oclif/core';
2
3
  import highlight from 'color-json';
3
4
  import inquirer from 'inquirer';
4
5
  import { findBlueprintFile } from '../../actions/blueprints/blueprint.js';
5
6
  import { createFunctionResource } from '../../actions/blueprints/resources.js';
6
7
  import { validateFunctionName, validateFunctionType } from '../../utils/validate/resource.js';
7
- export default class Add extends Command {
8
+ export default class AddCommand extends Command {
8
9
  static description = 'Add a resource to a Blueprint';
9
10
  static examples = [
10
11
  '<%= config.bin %> <%= command.id %> function',
@@ -30,13 +31,13 @@ export default class Add extends Command {
30
31
  }),
31
32
  };
32
33
  async run() {
33
- const { args, flags } = await this.parse(Add);
34
+ const { args, flags } = await this.parse(AddCommand);
35
+ if (args.type !== 'function')
36
+ this.error(`Unsupported Resource type: ${args.type}`);
34
37
  const existingBlueprint = findBlueprintFile();
35
38
  if (!existingBlueprint) {
36
- this.error('No blueprint file found. Run `sanity blueprints init` first.');
39
+ this.error('No Blueprint file found. Run `sanity-run blueprints init` first.');
37
40
  }
38
- if (args.type !== 'function')
39
- this.error(`Unsupported resource type: ${args.type}`);
40
41
  const resourceName = flags.name;
41
42
  const functionType = flags['function-type'];
42
43
  await this.addFunction({ name: resourceName, type: functionType });
@@ -82,7 +83,7 @@ export default class Add extends Command {
82
83
  type: functionType,
83
84
  displayName: functionName,
84
85
  });
85
- this.log(`\nCreated function: ${filePath}`);
86
+ this.log(`\nCreated function: ${filePath.replace(cwd(), '')}`);
86
87
  if (!resourceAdded) {
87
88
  // print the resource JSON for manual addition
88
89
  this.log('\nAdd this Function resource to your blueprint:');
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- export default class Config extends Command {
2
+ export default class ConfigCommand extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
@@ -2,18 +2,21 @@ import { Command, Flags } from '@oclif/core';
2
2
  import highlight from 'color-json';
3
3
  import inquirer from 'inquirer';
4
4
  import Spinner from 'yocto-spinner';
5
- import { readBlueprintOnDisk, writeConfigFile } from '../../actions/blueprints/blueprint.js';
5
+ import { readLocalBlueprint, writeConfigFile } from '../../actions/blueprints/blueprint.js';
6
6
  import { listProjects } from '../../actions/blueprints/projects.js';
7
7
  import { getStack, listStacks } from '../../actions/blueprints/stacks.js';
8
8
  import { bold, dim, niceId } from '../../utils/display/colors.js';
9
+ import { presentBlueprintParserErrors } from '../../utils/display/errors.js';
9
10
  import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
10
- export default class Config extends Command {
11
+ export default class ConfigCommand extends Command {
11
12
  static description = 'View or edit Blueprint configuration';
12
13
  static examples = [
13
14
  '<%= config.bin %> <%= command.id %>',
14
15
  '<%= config.bin %> <%= command.id %> --test-config',
15
16
  '<%= config.bin %> <%= command.id %> --edit',
16
- '<%= config.bin %> <%= command.id %> --edit --project-id <projectId> --stack-id <stackId>',
17
+ '<%= config.bin %> <%= command.id %> --edit --project-id <projectId>',
18
+ // LAUNCH LIMIT: 1 Stack per Project - do not allow Stack ID to be set
19
+ // '<%= config.bin %> <%= command.id %> --edit --project-id <projectId> --stack-id <stackId>',
17
20
  ];
18
21
  static flags = {
19
22
  'test-config': Flags.boolean({
@@ -36,20 +39,25 @@ export default class Config extends Command {
36
39
  description: 'Update the Stack ID in the configuration. Requires --edit flag',
37
40
  aliases: ['stack', 'stackId'],
38
41
  dependsOn: ['edit'],
42
+ hidden: true, // LAUNCH LIMIT: 1 Stack per Project
39
43
  }),
40
44
  };
41
45
  sanityToken;
42
46
  async run() {
43
- const { flags } = await this.parse(Config);
47
+ const { flags } = await this.parse(ConfigCommand);
44
48
  const { edit: editConfig, 'project-id': editProjectId, 'stack-id': editStackId, 'test-config': testConfig, } = flags;
45
- const blueprint = await readBlueprintOnDisk();
46
- const { stackId: configStackId, projectId: configProjectId } = blueprint;
49
+ const blueprint = await readLocalBlueprint();
50
+ const { stackId: configStackId, projectId: configProjectId, errors } = blueprint;
51
+ if (errors.length > 0) {
52
+ this.log(presentBlueprintParserErrors(errors));
53
+ this.error('Blueprint parse errors.');
54
+ }
47
55
  if (!configStackId && !configProjectId) {
48
- this.error('Project and Stack configuration is missing! Use `blueprints init` to create a configuration.');
56
+ this.error('Project configuration is missing! Use `sanity-run blueprints init` to create a configuration.');
49
57
  }
50
58
  this.log(bold('Current configuration:'));
51
59
  this.log(` Sanity Project: ${niceId(configProjectId)}`);
52
- this.log(` Blueprint Stack: ${niceId(configStackId)}`);
60
+ this.log(` Blueprint deployment: ${niceId(configStackId)}`);
53
61
  if ((editProjectId || editStackId) && !editConfig) {
54
62
  this.log('To update the configuration, use the --edit flag.');
55
63
  return;
@@ -73,7 +81,10 @@ export default class Config extends Command {
73
81
  const projectId = editProjectId || (await this.promptForProjectId({ knownProjectId: configProjectId }));
74
82
  if (!projectId)
75
83
  this.error('Project ID is required.');
76
- const stackId = editStackId || (await this.promptForStackId({ projectId, knownStackId: configStackId }));
84
+ // LAUNCH LIMIT: 1 Stack per Project - do not allow Stack ID to be set
85
+ // const stackId =
86
+ // editStackId || (await this.promptForStackId({projectId, knownStackId: configStackId}))
87
+ const stackId = configStackId;
77
88
  if (testConfig) {
78
89
  if (projectId && stackId) {
79
90
  const { ok: newConfigOk } = await this.testConfigAndReport({ stackId, projectId });
@@ -87,12 +98,14 @@ export default class Config extends Command {
87
98
  }
88
99
  try {
89
100
  // update or create .blueprint/config.json
90
- writeConfigFile({ projectId, stackId });
101
+ // LAUNCH LIMIT: 1 Stack per Project - do not allow Stack ID to be set
102
+ writeConfigFile({ projectId /*, stackId*/ });
91
103
  this.log('Configuration updated successfully.');
92
104
  }
93
105
  catch (error) {
94
106
  this.log('Unable to dynamically update config. Use these values in your blueprint:');
95
- this.log(highlight(JSON.stringify({ metadata: { projectId, stackId } }, null, 2)));
107
+ // LAUNCH LIMIT: 1 Stack per Project - do not allow Stack ID to be set
108
+ this.log(highlight(JSON.stringify({ metadata: { projectId /*, stackId*/ } }, null, 2)));
96
109
  }
97
110
  }
98
111
  async promptForProjectId({ knownProjectId }) {
@@ -1,5 +1,5 @@
1
- import { Command } from '@oclif/core';
2
- export default class Deploy extends Command {
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: {
@@ -1,16 +1,17 @@
1
1
  import { setTimeout } from 'node:timers/promises';
2
- import { inspect } from 'node:util';
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
- import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
11
- export default class Deploy extends Command {
9
+ export default class DeployCommand extends DeployedBlueprintCommand {
12
10
  static description = 'Deploy a Blueprint';
13
- static examples = ['<%= config.bin %> <%= command.id %>'];
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 { token, error: tokenErr } = await validTokenOrErrorMessage();
22
- if (tokenErr)
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
- this.debug('BLUEPRINT DOCUMENT:', stackPayload);
62
- const spinner = Spinner({ text: 'Deploying stack...' }).start();
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(`Stack "${bold(stack.name)}" ${niceId(stack.id)} deployment started!`);
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 deployment status');
79
+ this.log('Use `sanity-run blueprints info` to check status');
94
80
  }
95
81
  }
96
82
  else {
97
- this.debug('STACK ERROR RESPONSE:', stack);
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 { Command } from '@oclif/core';
2
- export default class Destroy extends Command {
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 { Command, Flags } from '@oclif/core';
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
- import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
9
- export default class Destroy extends Command {
10
- static description = 'Destroy a deployed Blueprint Stack';
8
+ export default class DestroyCommand extends DeployedBlueprintCommand {
9
+ static description = 'Destroy a Blueprint deployment';
11
10
  static examples = [
12
11
  '<%= config.bin %> <%= command.id %>',
13
- '<%= config.bin %> <%= command.id %> --id ST-a1b2c3',
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 { token, error: tokenErr } = await validTokenOrErrorMessage();
27
- if (tokenErr)
28
- this.error(tokenErr.message);
29
- const { flags } = await this.parse(Destroy);
30
- const { errors, deployedStack, projectId } = await readBlueprintOnDisk({ getStack: true, token });
31
- if (errors.length > 0) {
32
- // printErrors(errors)
33
- this.warn('Blueprint parse errors:');
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
- if (!projectId)
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('Stack destruction cancelled');
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 stack in ${bold((i--).toString())} seconds...`;
69
+ destroySpinner.text = `Destroying deployment in ${bold((i--).toString())} seconds...`;
73
70
  await setTimeout(1000);
74
71
  }
75
- destroySpinner.text = 'Destroying stack 💥';
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 stack');
80
+ this.error(error || 'Failed to destroy deployment');
84
81
  // TODO: update local config
85
- destroySpinner.success(`Stack "${stack.name}" ${niceId(stack.id)} destroyed`);
82
+ destroySpinner.success(`Deployment "${stack.name}" ${niceId(stack.id)} destroyed`);
86
83
  }
87
84
  }
@@ -1,5 +1,5 @@
1
- import { Command } from '@oclif/core';
2
- export default class Info extends Command {
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 { Command, Flags } from '@oclif/core';
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
- import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
7
- export default class Info extends Command {
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
- '<%= config.bin %> <%= command.id %> --id ST-a1b2c3',
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
- required: false,
16
+ hidden: true, // LAUNCH LIMIT: 1 Stack per Project
17
17
  }),
18
18
  };
19
19
  async run() {
20
- const { token, error: tokenErr } = await validTokenOrErrorMessage();
21
- if (tokenErr)
22
- this.error(tokenErr.message);
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 { ok, stack: foundStack, error } = await getStack({ stackId: flags.id, auth });
42
- if (!ok)
43
- this.error(`Failed to get Stack ${niceId(flags.id)}: ${error}`);
44
- stack = foundStack;
45
- }
46
- else if (!stack) {
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('');