@sanity/runtime-cli 4.5.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 (52) hide show
  1. package/README.md +49 -77
  2. package/dist/actions/blueprints/blueprint.d.ts +6 -12
  3. package/dist/actions/blueprints/blueprint.js +38 -45
  4. package/dist/actions/blueprints/index.d.ts +33 -0
  5. package/dist/actions/blueprints/index.js +32 -0
  6. package/dist/actions/blueprints/projects.d.ts +9 -0
  7. package/dist/actions/blueprints/projects.js +12 -0
  8. package/dist/actions/blueprints/stacks.d.ts +0 -12
  9. package/dist/actions/blueprints/stacks.js +3 -30
  10. package/dist/baseCommands.d.ts +24 -0
  11. package/dist/baseCommands.js +69 -0
  12. package/dist/commands/blueprints/add.d.ts +1 -1
  13. package/dist/commands/blueprints/add.js +7 -6
  14. package/dist/commands/blueprints/config.d.ts +1 -1
  15. package/dist/commands/blueprints/config.js +24 -11
  16. package/dist/commands/blueprints/deploy.d.ts +2 -2
  17. package/dist/commands/blueprints/deploy.js +18 -33
  18. package/dist/commands/blueprints/destroy.d.ts +4 -3
  19. package/dist/commands/blueprints/destroy.js +32 -35
  20. package/dist/commands/blueprints/info.d.ts +2 -2
  21. package/dist/commands/blueprints/info.js +16 -36
  22. package/dist/commands/blueprints/init.d.ts +10 -2
  23. package/dist/commands/blueprints/init.js +85 -26
  24. package/dist/commands/blueprints/logs.d.ts +2 -2
  25. package/dist/commands/blueprints/logs.js +18 -32
  26. package/dist/commands/blueprints/plan.d.ts +2 -2
  27. package/dist/commands/blueprints/plan.js +10 -16
  28. package/dist/commands/blueprints/stacks.d.ts +3 -2
  29. package/dist/commands/blueprints/stacks.js +10 -29
  30. package/dist/commands/functions/env/add.d.ts +2 -2
  31. package/dist/commands/functions/env/add.js +6 -17
  32. package/dist/commands/functions/env/list.d.ts +2 -2
  33. package/dist/commands/functions/env/list.js +10 -17
  34. package/dist/commands/functions/env/remove.d.ts +2 -2
  35. package/dist/commands/functions/env/remove.js +6 -17
  36. package/dist/commands/functions/invoke.d.ts +2 -2
  37. package/dist/commands/functions/invoke.js +7 -14
  38. package/dist/commands/functions/logs.d.ts +3 -7
  39. package/dist/commands/functions/logs.js +21 -37
  40. package/dist/commands/functions/test.d.ts +3 -3
  41. package/dist/commands/functions/test.js +10 -8
  42. package/dist/server/app.js +3 -3
  43. package/dist/server/static/vendor/vendor.bundle.d.ts +2 -2
  44. package/dist/utils/display/blueprints-formatting.js +2 -2
  45. package/dist/utils/display/errors.d.ts +4 -0
  46. package/dist/utils/display/errors.js +27 -0
  47. package/dist/utils/display/index.d.ts +1 -0
  48. package/dist/utils/display/index.js +1 -0
  49. package/dist/utils/types.d.ts +10 -3
  50. package/dist/utils/types.js +9 -3
  51. package/oclif.manifest.json +59 -37
  52. package/package.json +2 -1
@@ -1,21 +1,33 @@
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
4
  import inquirer from 'inquirer';
5
5
  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';
6
+ import { getProject, listProjects } from '../../actions/blueprints/projects.js';
7
+ import { createStack, getStack, listStacks } from '../../actions/blueprints/stacks.js';
8
8
  import { bold, dim, niceId, underline } from '../../utils/display/colors.js';
9
9
  import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
10
- export default class Init extends Command {
10
+ const LAUNCH_LIMIT_STACK_PER_PROJECT = true;
11
+ export default class InitCommand extends Command {
11
12
  static description = 'Initialize a new Blueprint';
12
13
  static examples = [
13
14
  '<%= config.bin %> <%= command.id %>',
15
+ '<%= config.bin %> <%= command.id %> [directory]',
14
16
  '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts>',
15
- '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId> --stack-id <stackId>',
16
- '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId> --stack-name <stackName>',
17
+ '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId>',
18
+ // LAUNCH LIMIT: 1 Stack per Project - do not prompt for Stack, just create one
19
+ // '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId> --stack-id <stackId>',
20
+ // '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId> --stack-name <stackName>',
17
21
  ];
22
+ static args = {
23
+ dir: Args.string({
24
+ description: 'Directory to create the Blueprint in',
25
+ }),
26
+ };
18
27
  static flags = {
28
+ dir: Flags.string({
29
+ description: 'Directory to create the Blueprint in',
30
+ }),
19
31
  'blueprint-type': Flags.string({
20
32
  description: 'Blueprint manifest type to use for the Blueprint',
21
33
  options: ['json', 'js', 'ts'],
@@ -30,22 +42,29 @@ export default class Init extends Command {
30
42
  aliases: ['stack', 'stackId'],
31
43
  dependsOn: ['project-id'],
32
44
  exclusive: ['stack-name'],
45
+ hidden: true, // LAUNCH LIMIT: 1 Stack per Project
33
46
  }),
34
47
  'stack-name': Flags.string({
35
- char: 'n',
36
48
  description: 'Name to use for a NEW Stack',
37
49
  aliases: ['name'],
38
50
  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(Init);
45
- const { 'blueprint-type': flagBlueprintType, 'project-id': flagProjectId, 'stack-id': flagStackId, 'stack-name': flagStackName, } = flags;
46
- const existingBlueprint = findBlueprintFile();
57
+ const { args, flags } = await this.parse(InitCommand);
58
+ const { 'blueprint-type': flagBlueprintType, 'project-id': flagProjectId, 'stack-id': flagStackId, 'stack-name': flagStackName, dir: flagDir, } = flags;
59
+ const { dir: argDir } = args;
60
+ const dirProvided = argDir || flagDir;
61
+ const here = cwd();
62
+ const dir = argDir || flagDir || here;
63
+ const existingBlueprint = findBlueprintFile(dir);
47
64
  if (existingBlueprint) {
48
- this.error(`A blueprint file already exists: ${existingBlueprint.fileName}`);
65
+ this.log(`A Blueprint file already exists: ${existingBlueprint.blueprintFilePath.replace(here, '')}`);
66
+ this.log('Use `sanity-run blueprints config --edit` to edit the configuration.');
67
+ this.error('Existing Blueprint found.');
49
68
  }
50
69
  const { token, error: tokenErr } = await validTokenOrErrorMessage();
51
70
  if (tokenErr)
@@ -54,15 +73,24 @@ export default class Init extends Command {
54
73
  const blueprintExtension = flagBlueprintType || (await this.promptForBlueprintType());
55
74
  if (!blueprintExtension)
56
75
  this.error('Blueprint type is required.');
57
- const projectId = flagProjectId || (await this.promptForProjectId());
58
- if (!projectId)
59
- this.error('Project ID is required.');
76
+ let projectId = flagProjectId;
60
77
  let stackId = flagStackId;
78
+ if (!projectId) {
79
+ const pickedProject = await this.promptForProject();
80
+ projectId = pickedProject.projectId;
81
+ }
82
+ const auth = { token: this.sanityToken, projectId };
83
+ // LAUNCH LIMIT: 1 Stack per Project - do not prompt for Stack, just create one
84
+ if (LAUNCH_LIMIT_STACK_PER_PROJECT) {
85
+ const stack = await this.createProjectBasedStack(auth);
86
+ stackId = stack.id;
87
+ }
88
+ // while LAUNCH_LIMIT_STACK_PER_PROJECT is true, the stackId is already set
61
89
  if (!stackId) {
62
90
  if (flagStackName) {
63
91
  const stack = await this.createEmptyStack({
64
92
  stackPayload: { name: flagStackName, projectId, document: { resources: [] } },
65
- auth: { token: this.sanityToken, projectId },
93
+ auth,
66
94
  });
67
95
  stackId = stack.id;
68
96
  }
@@ -71,11 +99,14 @@ export default class Init extends Command {
71
99
  }
72
100
  }
73
101
  const fileName = `blueprint.${blueprintExtension}`;
74
- const filePath = join(cwd(), fileName);
102
+ const filePath = join(dir, fileName);
103
+ if (dirProvided)
104
+ this.log(`New Blueprint created: ${dirProvided}/`);
75
105
  writeBlueprintToDisk({ blueprintFilePath: filePath });
76
- this.log(`Created new blueprint: ./${fileName}`);
77
- writeConfigFile({ blueprintFilePath: filePath, projectId, stackId });
78
- this.log('Created new config file: ./.blueprint/config.json');
106
+ this.log(`Created new blueprint: ${dirProvided ?? '.'}/${fileName}`);
107
+ // LAUNCH LIMIT: 1 Stack per Project - do not persist stackId
108
+ writeConfigFile({ blueprintFilePath: filePath, projectId /*, stackId*/ });
109
+ this.log(`Created new config file: ${dirProvided ?? '.'}/.blueprint/config.json`);
79
110
  if (blueprintExtension === 'ts') {
80
111
  this.log('\nNote: TypeScript support requires "tsx" to be installed. Run: npm install -D tsx');
81
112
  }
@@ -96,7 +127,7 @@ export default class Init extends Command {
96
127
  ]);
97
128
  return pickedBlueprintsType;
98
129
  }
99
- async promptForProjectId() {
130
+ async promptForProject() {
100
131
  if (!this.sanityToken)
101
132
  this.error('Unable to list projects. Missing API token.');
102
133
  const { ok: projectsOk, error: projectsErr, projects, } = await listProjects({ token: this.sanityToken });
@@ -107,17 +138,17 @@ export default class Init extends Command {
107
138
  }
108
139
  const projectChoices = projects.map(({ displayName, id: projectId }) => ({
109
140
  name: `"${displayName}" ${niceId(projectId)}`,
110
- value: projectId,
141
+ value: { projectId, displayName },
111
142
  }));
112
- const { pickedProjectId } = await inquirer.prompt([
143
+ const { pickedProject } = await inquirer.prompt([
113
144
  {
114
145
  type: 'list',
115
- name: 'pickedProjectId',
146
+ name: 'pickedProject',
116
147
  message: 'Select your Sanity project:',
117
148
  choices: projectChoices,
118
149
  },
119
150
  ]);
120
- return pickedProjectId;
151
+ return pickedProject;
121
152
  }
122
153
  async promptForStackId({ projectId }) {
123
154
  if (!this.sanityToken)
@@ -137,7 +168,7 @@ export default class Init extends Command {
137
168
  {
138
169
  type: 'list',
139
170
  name: 'pickedStackId',
140
- message: 'Select your Blueprint Stack:',
171
+ message: 'Select an existing deployment or create a new one:',
141
172
  choices: stackChoices,
142
173
  },
143
174
  ]);
@@ -161,7 +192,35 @@ export default class Init extends Command {
161
192
  async createEmptyStack({ stackPayload, auth, }) {
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.warn(`Found existing deployment for "${projectDisplayName}" Blueprint`);
212
+ this.warn('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({
217
+ stackPayload: {
218
+ projectId,
219
+ name: projectDisplayName,
220
+ document: { resources: [] },
221
+ },
222
+ auth,
223
+ });
224
+ return stack;
225
+ }
167
226
  }
@@ -1,5 +1,5 @@
1
- import { Command } from '@oclif/core';
2
- export default class Logs extends Command {
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: {
@@ -1,13 +1,12 @@
1
- import { Command, Flags } from '@oclif/core';
1
+ import { Flags } from '@oclif/core';
2
2
  import Spinner from 'yocto-spinner';
3
- import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
4
3
  import { findNewestLogTimestamp, getLogs, getRecentLogs, isNewerLog, streamLogs, } from '../../actions/blueprints/logs.js';
4
+ import { DeployedBlueprintCommand } from '../../baseCommands.js';
5
5
  import { formatTitle } from '../../utils/display/blueprints-formatting.js';
6
6
  import { bold, niceId, red } from '../../utils/display/colors.js';
7
7
  import { formatLogEntry, formatLogsByDay, organizeLogsByDay, } from '../../utils/display/logs-formatting.js';
8
- import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
9
- export default class Logs extends Command {
10
- static description = 'Display logs for a Blueprint stack';
8
+ export default class LogsCommand extends DeployedBlueprintCommand {
9
+ static description = 'Display logs for a Blueprint deployment';
11
10
  static examples = [
12
11
  '<%= config.bin %> <%= command.id %>',
13
12
  '<%= config.bin %> <%= command.id %> --watch',
@@ -21,32 +20,20 @@ export default class Logs extends Command {
21
20
  }),
22
21
  };
23
22
  async run() {
24
- const { token, error: tokenErr } = await validTokenOrErrorMessage();
25
- if (tokenErr)
26
- this.error(tokenErr.message);
27
- const { flags } = await this.parse(Logs);
28
- const watchMode = flags.watch;
23
+ const flags = this.flags;
24
+ const spinner = Spinner({
25
+ text: `Fetching recent logs for deployment ${niceId(this.stackId)}`,
26
+ }).start();
29
27
  try {
30
- const { errors, deployedStack } = await readBlueprintOnDisk({ getStack: true, token });
31
- if (errors.length > 0) {
32
- this.log('Blueprint parse errors:');
33
- console.dir(errors, { depth: null });
34
- return;
35
- }
36
- if (!deployedStack)
37
- this.error('Stack not found'); // returns
38
- const { id: stackId, projectId, name } = deployedStack;
39
- const auth = { token, projectId };
40
- const spinner = Spinner({ text: `Fetching logs for stack ${niceId(stackId)}` }).start();
41
- if (watchMode) {
42
- const { ok, logs, error } = await getLogs(stackId, auth);
28
+ if (flags.watch) {
29
+ const { ok, logs, error } = await getLogs(this.stackId, this.auth);
43
30
  if (!ok) {
44
31
  spinner.error(`${red('Failed')} to retrieve logs`);
45
32
  this.log(`Error: ${error || 'Unknown error'}`);
46
33
  return;
47
34
  }
48
- spinner.stop().clear(); // stop and remove the spinner
49
- this.log(`${formatTitle('Blueprint', name)} ${niceId(stackId)} logs`);
35
+ spinner.stop().clear();
36
+ this.log(`${formatTitle('Blueprint', this.deployedStack.name)} ${niceId(this.stackId)} logs`);
50
37
  if (logs.length > 0) {
51
38
  this.log('\nMost recent logs:');
52
39
  const recentLogs = getRecentLogs(logs);
@@ -55,7 +42,7 @@ export default class Logs extends Command {
55
42
  }
56
43
  }
57
44
  else {
58
- this.log(`No recent logs found for stack ${niceId(stackId)}`);
45
+ this.log(`No recent logs found for deployment ${niceId(this.stackId)}`);
59
46
  }
60
47
  const onOpen = () => {
61
48
  this.log(`Watching for new logs... ${bold('ctrl+c')} to stop`);
@@ -67,24 +54,23 @@ export default class Logs extends Command {
67
54
  newestTimestamp = new Date(log.timestamp).getTime();
68
55
  this.log(formatLogEntry(log, true));
69
56
  };
70
- this.debug(`Connecting to streaming endpoint for Stack ${niceId(stackId)}...`);
71
- streamLogs(stackId, auth, renderLog, onOpen, (error) => this.log(`${red('Error:')} ${error}`));
57
+ streamLogs(this.stackId, this.auth, renderLog, onOpen, (error) => this.log(`${red('Error:')} ${error}`));
72
58
  return new Promise(() => {
73
59
  // hold the line until the user terminates with Ctrl+C
74
60
  });
75
61
  }
76
- const { ok, logs, error } = await getLogs(stackId, auth);
62
+ const { ok, logs, error } = await getLogs(this.stackId, this.auth);
77
63
  if (!ok) {
78
64
  spinner.error(`${red('Failed')} to retrieve logs`);
79
65
  this.log(`Error: ${error || 'Unknown error'}`);
80
66
  return;
81
67
  }
82
68
  if (logs.length === 0) {
83
- spinner.info(`No logs found for stack ${stackId}`);
69
+ spinner.info(`No logs found for deployment ${this.stackId}`);
84
70
  return;
85
71
  }
86
- spinner.success(`${formatTitle('Blueprint', name)} Logs`);
87
- this.log(`Found ${bold(logs.length.toString())} log entries for Stack ${niceId(stackId)}\n`);
72
+ spinner.success(`${formatTitle('Blueprint', this.deployedStack.name)} Logs`);
73
+ this.log(`Found ${bold(logs.length.toString())} log entries for deployment ${niceId(this.stackId)}\n`);
88
74
  // Organize and format logs by day
89
75
  const logsByDay = organizeLogsByDay(logs);
90
76
  this.log(formatLogsByDay(logsByDay));
@@ -1,5 +1,5 @@
1
- import { Command } from '@oclif/core';
2
- export default class Plan extends Command {
1
+ import { BlueprintCommand } from '../../baseCommands.js';
2
+ export default class PlanCommand extends BlueprintCommand<typeof PlanCommand> {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  run(): Promise<void>;
@@ -1,26 +1,20 @@
1
- import { Command } from '@oclif/core';
2
- import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
1
+ import { BlueprintCommand } from '../../baseCommands.js';
3
2
  import { formatResourceTree, formatTitle } from '../../utils/display/blueprints-formatting.js';
4
- import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
5
- export default class Plan extends Command {
3
+ import { presentBlueprintParserErrors } from '../../utils/display/errors.js';
4
+ export default class PlanCommand extends BlueprintCommand {
6
5
  static description = 'Enumerate resources to be deployed - will not modify any resources';
7
6
  static examples = ['<%= config.bin %> <%= command.id %>'];
8
7
  async run() {
9
- const { token, error: tokenErr } = await validTokenOrErrorMessage();
10
- if (tokenErr)
11
- this.error(tokenErr.message);
12
- const { errors, projectId, stackId, parsedBlueprint: { resources }, fileInfo, deployedStack, } = await readBlueprintOnDisk({ getStack: true, token });
8
+ const { parsedBlueprint, fileInfo, errors } = this.blueprint;
9
+ const { resources } = parsedBlueprint;
13
10
  if (errors.length > 0) {
14
- // printErrors(errors)
15
- this.log('Blueprint parse errors:');
16
- console.dir(errors, { depth: null });
11
+ this.log(presentBlueprintParserErrors(errors));
12
+ // continue to show plan
17
13
  }
18
- if (!projectId)
19
- this.log('Blueprint must contain a project resource');
20
- const name = deployedStack?.name ?? stackId ?? 'Unknown';
21
- this.log(`${formatTitle('Stack', name)} Plan`);
14
+ // TODO: compare local to existingStack
15
+ this.log(`${formatTitle('Deployment', 'Plan')}`);
22
16
  this.log(`(${fileInfo.fileName})\n`);
23
17
  this.log(formatResourceTree(resources));
24
- this.log('\nRun `sanity blueprints deploy` to deploy these changes');
18
+ this.log('\nRun `sanity-run blueprints deploy` to deploy these changes');
25
19
  }
26
20
  }
@@ -1,5 +1,6 @@
1
- import { Command } from '@oclif/core';
2
- export default class Stacks extends Command {
1
+ import { BlueprintCommand } from '../../baseCommands.js';
2
+ export default class StacksCommand extends BlueprintCommand<typeof StacksCommand> {
3
+ static hidden: boolean;
3
4
  static description: string;
4
5
  static examples: string[];
5
6
  static flags: {
@@ -1,10 +1,11 @@
1
- import { Command, Flags } from '@oclif/core';
2
- import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
1
+ import { Flags } from '@oclif/core';
3
2
  import { listStacks } from '../../actions/blueprints/stacks.js';
3
+ import { BlueprintCommand } from '../../baseCommands.js';
4
4
  import { formatStacksListing } from '../../utils/display/blueprints-formatting.js';
5
5
  import { bold, niceId } from '../../utils/display/colors.js';
6
- import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
7
- export default class Stacks extends Command {
6
+ export default class StacksCommand extends BlueprintCommand {
7
+ // LAUNCH LIMIT: 1 Stack per Project - hide stacks command
8
+ static hidden = true;
8
9
  static description = 'List all Blueprint stacks';
9
10
  static examples = [
10
11
  '<%= config.bin %> <%= command.id %>',
@@ -17,34 +18,14 @@ export default class Stacks extends Command {
17
18
  }),
18
19
  };
19
20
  async run() {
20
- const { token, error: tokenErr } = await validTokenOrErrorMessage();
21
- if (tokenErr)
22
- this.error(tokenErr.message);
23
- const { flags } = await this.parse(Stacks);
24
- let { projectId } = flags;
25
- let stackId;
26
- if (!projectId) {
27
- try {
28
- const { errors, projectId: projectIdFromBlueprint, stackId: stackIdFromBlueprint, } = await readBlueprintOnDisk();
29
- if (errors.length > 0) {
30
- this.log('Blueprint parse errors:');
31
- console.dir(errors, { depth: null });
32
- return;
33
- }
34
- projectId = projectIdFromBlueprint;
35
- stackId = stackIdFromBlueprint;
36
- }
37
- catch {
38
- // continue to print error
39
- }
40
- }
21
+ const flags = this.flags;
22
+ const projectId = flags.projectId || this.blueprint.projectId;
41
23
  if (!projectId) {
42
24
  this.log('Unable to determine Project ID. Provide one with --projectId');
43
- this.log('Or create a Blueprint with `sanity blueprints init`');
25
+ this.log('Or create a Blueprint with `sanity-run blueprints init`');
44
26
  return;
45
27
  }
46
- const auth = { token, projectId };
47
- const { ok, stacks, error } = await listStacks(auth);
28
+ const { ok, stacks, error } = await listStacks({ token: this.sanityToken, projectId });
48
29
  if (!ok)
49
30
  this.error(error || 'Failed to list stacks');
50
31
  if (!stacks || stacks.length === 0) {
@@ -52,6 +33,6 @@ export default class Stacks extends Command {
52
33
  return;
53
34
  }
54
35
  this.log(`${bold('Project')} ${niceId(projectId)} ${bold('Stacks')}:\n`);
55
- this.log(formatStacksListing(stacks, stackId));
36
+ this.log(formatStacksListing(stacks, this.blueprint.stackId));
56
37
  }
57
38
  }
@@ -1,5 +1,5 @@
1
- import { Command } from '@oclif/core';
2
- export default class Add extends Command {
1
+ import { DeployedBlueprintCommand } from '../../../baseCommands.js';
2
+ export default class EnvAddCommand extends DeployedBlueprintCommand<typeof EnvAddCommand> {
3
3
  static args: {
4
4
  name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
5
  key: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
@@ -1,11 +1,10 @@
1
- import { Args, Command } from '@oclif/core';
1
+ import { Args } from '@oclif/core';
2
2
  import Spinner from 'yocto-spinner';
3
- import { readBlueprintOnDisk } from '../../../actions/blueprints/blueprint.js';
4
3
  import { update } from '../../../actions/functions/env/update.js';
4
+ import { DeployedBlueprintCommand } from '../../../baseCommands.js';
5
5
  import { red } from '../../../utils/display/colors.js';
6
6
  import { findFunctionByName } from '../../../utils/find-function.js';
7
- import { validTokenOrErrorMessage } from '../../../utils/validated-token.js';
8
- export default class Add extends Command {
7
+ export default class EnvAddCommand extends DeployedBlueprintCommand {
9
8
  static args = {
10
9
  name: Args.string({ description: 'The name of the Sanity Function', required: true }),
11
10
  key: Args.string({ description: 'The name of the environment variable', required: true }),
@@ -16,22 +15,12 @@ export default class Add extends Command {
16
15
  '<%= config.bin %> <%= command.id %> MyFunction API_URL https://api.example.com/',
17
16
  ];
18
17
  async run() {
19
- const { token, error: tokenErr } = await validTokenOrErrorMessage();
20
- if (tokenErr)
21
- this.error(tokenErr.message);
22
- const { args } = await this.parse(Add);
18
+ const args = this.args;
23
19
  const spinner = Spinner({
24
20
  text: `Updating "${args.key}" environment variable in "${args.name}"`,
25
21
  }).start();
26
- const { deployedStack } = await readBlueprintOnDisk({ getStack: true, token });
27
- if (!deployedStack)
28
- this.error('Stack not found'); // returns
29
- const { projectId } = deployedStack;
30
- const { externalId } = findFunctionByName(deployedStack, args.name);
31
- const result = await update(externalId, args.key, args.value, {
32
- token,
33
- projectId,
34
- });
22
+ const { externalId } = findFunctionByName(this.deployedStack, args.name);
23
+ const result = await update(externalId, args.key, args.value, this.auth);
35
24
  if (result.ok) {
36
25
  spinner.success(`Update of ${args.key} succeeded`);
37
26
  }
@@ -1,5 +1,5 @@
1
- import { Command } from '@oclif/core';
2
- export default class List extends Command {
1
+ import { DeployedBlueprintCommand } from '../../../baseCommands.js';
2
+ export default class EnvListCommand extends DeployedBlueprintCommand<typeof EnvListCommand> {
3
3
  static args: {
4
4
  name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
5
  };
@@ -1,31 +1,24 @@
1
- import { Args, Command } from '@oclif/core';
2
- import { readBlueprintOnDisk } from '../../../actions/blueprints/blueprint.js';
1
+ import { Args } from '@oclif/core';
2
+ import Spinner from 'yocto-spinner';
3
3
  import { list } from '../../../actions/functions/env/list.js';
4
+ import { DeployedBlueprintCommand } from '../../../baseCommands.js';
4
5
  import { findFunctionByName } from '../../../utils/find-function.js';
5
- import { validTokenOrErrorMessage } from '../../../utils/validated-token.js';
6
- export default class List extends Command {
6
+ export default class EnvListCommand extends DeployedBlueprintCommand {
7
7
  static args = {
8
8
  name: Args.string({ description: 'The name of the Sanity Function', required: true }),
9
9
  };
10
10
  static description = 'List the environment variables for a Sanity function';
11
11
  static examples = ['<%= config.bin %> <%= command.id %> MyFunction'];
12
12
  async run() {
13
- const { token, error: tokenErr } = await validTokenOrErrorMessage();
14
- if (tokenErr)
15
- this.error(tokenErr.message);
16
- const { args } = await this.parse(List);
17
- const { deployedStack } = await readBlueprintOnDisk({ getStack: true, token });
18
- if (!deployedStack)
19
- this.error('Stack not found. Is it deployed?'); // returns
20
- const { projectId } = deployedStack;
21
- const { externalId } = findFunctionByName(deployedStack, args.name);
22
- const result = await list(externalId, {
23
- token,
24
- projectId,
25
- });
13
+ const args = this.args;
14
+ const spinner = Spinner({ text: `Listing environment variables for "${args.name}"` }).start();
15
+ const { externalId } = findFunctionByName(this.deployedStack, args.name);
16
+ const result = await list(externalId, this.auth);
26
17
  if (!result.ok) {
18
+ spinner.stop().clear();
27
19
  this.error(`Error: ${result.error || 'Unknown error'}`);
28
20
  }
21
+ spinner.success(`Environment variables for "${args.name}"`);
29
22
  for (const key of result.envvars) {
30
23
  this.log(key);
31
24
  }
@@ -1,5 +1,5 @@
1
- import { Command } from '@oclif/core';
2
- export default class Remove extends Command {
1
+ import { DeployedBlueprintCommand } from '../../../baseCommands.js';
2
+ export default class EnvRemoveCommand extends DeployedBlueprintCommand<typeof EnvRemoveCommand> {
3
3
  static args: {
4
4
  name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
5
  key: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
@@ -1,11 +1,10 @@
1
- import { Args, Command } from '@oclif/core';
1
+ import { Args } from '@oclif/core';
2
2
  import Spinner from 'yocto-spinner';
3
- import { readBlueprintOnDisk } from '../../../actions/blueprints/blueprint.js';
4
3
  import { remove } from '../../../actions/functions/env/remove.js';
4
+ import { DeployedBlueprintCommand } from '../../../baseCommands.js';
5
5
  import { red } from '../../../utils/display/colors.js';
6
6
  import { findFunctionByName } from '../../../utils/find-function.js';
7
- import { validTokenOrErrorMessage } from '../../../utils/validated-token.js';
8
- export default class Remove extends Command {
7
+ export default class EnvRemoveCommand extends DeployedBlueprintCommand {
9
8
  static args = {
10
9
  name: Args.string({ description: 'The name of the Sanity Function', required: true }),
11
10
  key: Args.string({ description: 'The name of the environment variable', required: true }),
@@ -13,22 +12,12 @@ export default class Remove extends Command {
13
12
  static description = 'Remove an environment variable for a Sanity function';
14
13
  static examples = ['<%= config.bin %> <%= command.id %> MyFunction API_URL'];
15
14
  async run() {
16
- const { token, error: tokenErr } = await validTokenOrErrorMessage();
17
- if (tokenErr)
18
- this.error(tokenErr.message);
19
- const { args } = await this.parse(Remove);
15
+ const args = this.args;
20
16
  const spinner = Spinner({
21
17
  text: `Removing "${args.key}" environment variable in "${args.name}"`,
22
18
  }).start();
23
- const { deployedStack } = await readBlueprintOnDisk({ getStack: true, token });
24
- if (!deployedStack)
25
- this.error('Stack not found'); // returns
26
- const { projectId } = deployedStack;
27
- const { externalId } = findFunctionByName(deployedStack, args.name);
28
- const result = await remove(externalId, args.key, {
29
- token,
30
- projectId,
31
- });
19
+ const { externalId } = findFunctionByName(this.deployedStack, args.name);
20
+ const result = await remove(externalId, args.key, this.auth);
32
21
  if (result.ok) {
33
22
  spinner.success(`Removal of ${args.key} succeeded`);
34
23
  }
@@ -1,5 +1,5 @@
1
- import { Command } from '@oclif/core';
2
- export default class Invoke extends Command {
1
+ import { DeployedBlueprintCommand } from '../../baseCommands.js';
2
+ export default class InvokeCommand extends DeployedBlueprintCommand<typeof InvokeCommand> {
3
3
  static args: {
4
4
  name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
5
  };
@@ -1,11 +1,10 @@
1
- import { Args, Command, Flags } from '@oclif/core';
1
+ import { Args, Flags } from '@oclif/core';
2
2
  import Spinner from 'yocto-spinner';
3
- import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
4
3
  import { invoke } from '../../actions/functions/invoke.js';
4
+ import { DeployedBlueprintCommand } from '../../baseCommands.js';
5
5
  import { red } from '../../utils/display/colors.js';
6
6
  import { findFunctionByName } from '../../utils/find-function.js';
7
- import { validTokenOrErrorMessage } from '../../utils/validated-token.js';
8
- export default class Invoke extends Command {
7
+ export default class InvokeCommand extends DeployedBlueprintCommand {
9
8
  static args = {
10
9
  name: Args.string({ description: 'The name of the Sanity Function', required: true }),
11
10
  };
@@ -23,17 +22,11 @@ export default class Invoke extends Command {
23
22
  }),
24
23
  };
25
24
  async run() {
26
- const { token, error: tokenErr } = await validTokenOrErrorMessage();
27
- if (tokenErr)
28
- this.error(tokenErr.message);
29
- const { args, flags } = await this.parse(Invoke);
25
+ const args = this.args;
26
+ const flags = this.flags;
30
27
  const spinner = Spinner({ text: `Invoking function "${args.name}"` }).start();
31
- const { deployedStack } = await readBlueprintOnDisk({ getStack: true, token });
32
- if (!deployedStack)
33
- this.error('Stack not found'); // returns
34
- const { projectId } = deployedStack;
35
- const { externalId } = findFunctionByName(deployedStack, args.name);
36
- const result = await invoke(externalId, { data: flags.data, file: flags.file }, { token, projectId });
28
+ const { externalId } = findFunctionByName(this.deployedStack, args.name);
29
+ const result = await invoke(externalId, { data: flags.data, file: flags.file }, this.auth);
37
30
  if (result.ok) {
38
31
  spinner.success(`Invocation of ${args.name} succeeded`);
39
32
  if (result.json?.data?.type === 'Buffer') {