@sanity/runtime-cli 1.4.1 → 1.5.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 +33 -10
- package/dist/actions/blueprints/blueprint.d.ts +26 -0
- package/dist/actions/blueprints/blueprint.js +164 -0
- package/dist/actions/blueprints/logs.js +3 -1
- package/dist/commands/blueprints/deploy.js +43 -13
- package/dist/commands/blueprints/info.d.ts +3 -0
- package/dist/commands/blueprints/info.js +39 -16
- package/dist/commands/blueprints/logs.js +1 -1
- package/dist/commands/blueprints/plan.js +6 -8
- package/dist/commands/blueprints/stacks.d.ts +6 -0
- package/dist/commands/blueprints/stacks.js +40 -0
- package/dist/commands/functions/invoke.js +1 -1
- package/dist/commands/functions/logs.js +1 -1
- package/dist/server/app.js +1 -1
- package/dist/utils/display/blueprints-formatting.js +1 -1
- package/dist/utils/types.d.ts +2 -0
- package/oclif.manifest.json +37 -3
- package/package.json +2 -1
- package/dist/actions/blueprints/read-blueprint.d.ts +0 -16
- package/dist/actions/blueprints/read-blueprint.js +0 -89
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ $ npm install -g @sanity/runtime-cli
|
|
|
20
20
|
$ sanity COMMAND
|
|
21
21
|
running command...
|
|
22
22
|
$ sanity (--version)
|
|
23
|
-
@sanity/runtime-cli/1.
|
|
23
|
+
@sanity/runtime-cli/1.5.0 linux-x64 node-v22.14.0
|
|
24
24
|
$ sanity --help [COMMAND]
|
|
25
25
|
USAGE
|
|
26
26
|
$ sanity COMMAND
|
|
@@ -33,6 +33,7 @@ USAGE
|
|
|
33
33
|
* [`sanity blueprints info`](#sanity-blueprints-info)
|
|
34
34
|
* [`sanity blueprints logs`](#sanity-blueprints-logs)
|
|
35
35
|
* [`sanity blueprints plan`](#sanity-blueprints-plan)
|
|
36
|
+
* [`sanity blueprints stacks`](#sanity-blueprints-stacks)
|
|
36
37
|
* [`sanity functions dev`](#sanity-functions-dev)
|
|
37
38
|
* [`sanity functions invoke ID`](#sanity-functions-invoke-id)
|
|
38
39
|
* [`sanity functions logs ID`](#sanity-functions-logs-id)
|
|
@@ -64,7 +65,7 @@ EXAMPLES
|
|
|
64
65
|
$ sanity blueprints deploy
|
|
65
66
|
```
|
|
66
67
|
|
|
67
|
-
_See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
68
|
+
_See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v1.5.0/src/commands/blueprints/deploy.ts)_
|
|
68
69
|
|
|
69
70
|
## `sanity blueprints info`
|
|
70
71
|
|
|
@@ -72,16 +73,21 @@ Show information about a Blueprint
|
|
|
72
73
|
|
|
73
74
|
```
|
|
74
75
|
USAGE
|
|
75
|
-
$ sanity blueprints info
|
|
76
|
+
$ sanity blueprints info [--id <value>]
|
|
77
|
+
|
|
78
|
+
FLAGS
|
|
79
|
+
--id=<value> Stack ID to show info for (defaults to current stack)
|
|
76
80
|
|
|
77
81
|
DESCRIPTION
|
|
78
82
|
Show information about a Blueprint
|
|
79
83
|
|
|
80
84
|
EXAMPLES
|
|
81
85
|
$ sanity blueprints info
|
|
86
|
+
|
|
87
|
+
$ sanity blueprints info --id abc123
|
|
82
88
|
```
|
|
83
89
|
|
|
84
|
-
_See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
90
|
+
_See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v1.5.0/src/commands/blueprints/info.ts)_
|
|
85
91
|
|
|
86
92
|
## `sanity blueprints logs`
|
|
87
93
|
|
|
@@ -103,7 +109,7 @@ EXAMPLES
|
|
|
103
109
|
$ sanity blueprints logs --watch
|
|
104
110
|
```
|
|
105
111
|
|
|
106
|
-
_See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
112
|
+
_See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v1.5.0/src/commands/blueprints/logs.ts)_
|
|
107
113
|
|
|
108
114
|
## `sanity blueprints plan`
|
|
109
115
|
|
|
@@ -120,7 +126,24 @@ EXAMPLES
|
|
|
120
126
|
$ sanity blueprints plan
|
|
121
127
|
```
|
|
122
128
|
|
|
123
|
-
_See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
129
|
+
_See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v1.5.0/src/commands/blueprints/plan.ts)_
|
|
130
|
+
|
|
131
|
+
## `sanity blueprints stacks`
|
|
132
|
+
|
|
133
|
+
List all Blueprint stacks
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
USAGE
|
|
137
|
+
$ sanity blueprints stacks
|
|
138
|
+
|
|
139
|
+
DESCRIPTION
|
|
140
|
+
List all Blueprint stacks
|
|
141
|
+
|
|
142
|
+
EXAMPLES
|
|
143
|
+
$ sanity blueprints stacks
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
_See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v1.5.0/src/commands/blueprints/stacks.ts)_
|
|
124
147
|
|
|
125
148
|
## `sanity functions dev`
|
|
126
149
|
|
|
@@ -140,7 +163,7 @@ EXAMPLES
|
|
|
140
163
|
$ sanity functions dev --port 8974
|
|
141
164
|
```
|
|
142
165
|
|
|
143
|
-
_See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
166
|
+
_See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v1.5.0/src/commands/functions/dev.ts)_
|
|
144
167
|
|
|
145
168
|
## `sanity functions invoke ID`
|
|
146
169
|
|
|
@@ -166,7 +189,7 @@ EXAMPLES
|
|
|
166
189
|
$ sanity functions invoke <ID> --file 'payload.json'
|
|
167
190
|
```
|
|
168
191
|
|
|
169
|
-
_See code: [src/commands/functions/invoke.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
192
|
+
_See code: [src/commands/functions/invoke.ts](https://github.com/sanity-io/runtime-cli/blob/v1.5.0/src/commands/functions/invoke.ts)_
|
|
170
193
|
|
|
171
194
|
## `sanity functions logs ID`
|
|
172
195
|
|
|
@@ -186,7 +209,7 @@ EXAMPLES
|
|
|
186
209
|
$ sanity functions logs <ID>
|
|
187
210
|
```
|
|
188
211
|
|
|
189
|
-
_See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
212
|
+
_See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v1.5.0/src/commands/functions/logs.ts)_
|
|
190
213
|
|
|
191
214
|
## `sanity functions test PATH`
|
|
192
215
|
|
|
@@ -215,7 +238,7 @@ EXAMPLES
|
|
|
215
238
|
$ sanity functions test ./test.ts --data '{ "id": 1 }' --timeout 60
|
|
216
239
|
```
|
|
217
240
|
|
|
218
|
-
_See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
241
|
+
_See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v1.5.0/src/commands/functions/test.ts)_
|
|
219
242
|
|
|
220
243
|
## `sanity help [COMMAND]`
|
|
221
244
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Blueprint, BlueprintError, BlueprintStack } from '../../utils/types.js';
|
|
2
|
+
export declare function readBlueprintOnDisk({ blueprintPath, getStack, }?: {
|
|
3
|
+
blueprintPath?: string;
|
|
4
|
+
getStack?: boolean;
|
|
5
|
+
}): Promise<{
|
|
6
|
+
fileInfo: {
|
|
7
|
+
path: string;
|
|
8
|
+
fileName: string;
|
|
9
|
+
extension: string;
|
|
10
|
+
};
|
|
11
|
+
parsedBlueprint: Blueprint;
|
|
12
|
+
errors: BlueprintError[];
|
|
13
|
+
projectId?: string;
|
|
14
|
+
stackId?: string;
|
|
15
|
+
deployedStack?: BlueprintStack;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function updateBlueprintMetadata({ blueprintPath, projectId, stackId, }: {
|
|
18
|
+
blueprintPath?: string;
|
|
19
|
+
projectId: string;
|
|
20
|
+
stackId: string;
|
|
21
|
+
}): Promise<void>;
|
|
22
|
+
export declare function writeBlueprintConfig({ blueprintPath, projectId, stackId, }: {
|
|
23
|
+
blueprintPath?: string;
|
|
24
|
+
projectId: string;
|
|
25
|
+
stackId: string;
|
|
26
|
+
}): void;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, extname, join } from 'node:path';
|
|
3
|
+
import { cwd } from 'node:process';
|
|
4
|
+
import { BlueprintErrorType } from '../../utils/types.js';
|
|
5
|
+
// @ts-ignore - this is currently untyped
|
|
6
|
+
import blueprintParserValidator from '../../utils/vendor/parser-validator.js';
|
|
7
|
+
import { getStack as getStackById } from './stacks.js';
|
|
8
|
+
const SUPPORTED_FILE_NAMES_IN_PRIORITY_ORDER = [
|
|
9
|
+
'blueprint.json',
|
|
10
|
+
// 'blueprint.js',
|
|
11
|
+
// 'blueprint.mjs',
|
|
12
|
+
// 'blueprint.cjs',
|
|
13
|
+
// 'blueprint.ts',
|
|
14
|
+
];
|
|
15
|
+
function findBlueprintFile(blueprintPath) {
|
|
16
|
+
if (blueprintPath) {
|
|
17
|
+
if (existsSync(blueprintPath)) {
|
|
18
|
+
return { path: blueprintPath, fileName: blueprintPath, extension: extname(blueprintPath) };
|
|
19
|
+
}
|
|
20
|
+
throw Error(`Blueprint file not found: ${blueprintPath}`);
|
|
21
|
+
}
|
|
22
|
+
for (const fileName of SUPPORTED_FILE_NAMES_IN_PRIORITY_ORDER) {
|
|
23
|
+
const filePath = join(cwd(), fileName);
|
|
24
|
+
if (existsSync(filePath)) {
|
|
25
|
+
return { path: filePath, fileName, extension: extname(filePath) };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function readConfigFile(blueprintPath) {
|
|
31
|
+
if (blueprintPath) {
|
|
32
|
+
const blueprintDir = dirname(blueprintPath);
|
|
33
|
+
const configPath = join(blueprintDir, '.blueprint', 'config.json');
|
|
34
|
+
if (existsSync(configPath)) {
|
|
35
|
+
try {
|
|
36
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
37
|
+
return config || null;
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const metadataPath = join(cwd(), '.blueprint', 'config.json');
|
|
45
|
+
if (!existsSync(metadataPath))
|
|
46
|
+
return null;
|
|
47
|
+
try {
|
|
48
|
+
const metadata = JSON.parse(readFileSync(metadataPath, 'utf8'));
|
|
49
|
+
return metadata || null;
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export async function readBlueprintOnDisk({ blueprintPath, getStack, } = {}) {
|
|
56
|
+
try {
|
|
57
|
+
const blueprintFile = findBlueprintFile(blueprintPath);
|
|
58
|
+
if (!blueprintFile)
|
|
59
|
+
throw Error('Could not find Blueprint file! Use the init command.');
|
|
60
|
+
const { path, fileName, extension } = blueprintFile;
|
|
61
|
+
let blueprintString;
|
|
62
|
+
if (extension === '.json') {
|
|
63
|
+
blueprintString = readFileSync(path, 'utf8').toString();
|
|
64
|
+
// } else if (extension === '.js' || extension === '.mjs') {
|
|
65
|
+
// const blueprintModule = require(path)
|
|
66
|
+
// blueprintJson = blueprintModule.default
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
throw Error(`Unsupported blueprint file extension: ${extension}`);
|
|
70
|
+
}
|
|
71
|
+
const parsed = blueprintParserValidator(JSON.parse(blueprintString));
|
|
72
|
+
const { blueprint: parsedBlueprint } = parsed;
|
|
73
|
+
const errors = parsed.errors || [];
|
|
74
|
+
// find project and stack IDs from .blueprint/config.json first,
|
|
75
|
+
// then fallback to metadata in blueprint file,
|
|
76
|
+
// finally fallback to resources array...
|
|
77
|
+
let projectId;
|
|
78
|
+
let stackId;
|
|
79
|
+
const configIds = readConfigFile(blueprintPath);
|
|
80
|
+
const blueprintMetadata = extension === '.json' ? parsedBlueprint.metadata : null;
|
|
81
|
+
// find/create project resource
|
|
82
|
+
if (configIds?.projectId) {
|
|
83
|
+
projectId = configIds.projectId;
|
|
84
|
+
}
|
|
85
|
+
else if (blueprintMetadata?.projectId) {
|
|
86
|
+
projectId = blueprintMetadata.projectId;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
const projectResource = parsedBlueprint.resources.find((r) => r.type === 'sanity.project');
|
|
90
|
+
if (projectResource)
|
|
91
|
+
projectId = projectResource.id;
|
|
92
|
+
}
|
|
93
|
+
// find/create stack resource
|
|
94
|
+
if (configIds?.stackId) {
|
|
95
|
+
stackId = configIds.stackId;
|
|
96
|
+
}
|
|
97
|
+
else if (blueprintMetadata?.stackId) {
|
|
98
|
+
stackId = blueprintMetadata.stackId;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const stackResource = parsedBlueprint.resources.find((r) => r.type === 'sanity.blueprints.stack');
|
|
102
|
+
if (stackResource)
|
|
103
|
+
stackId = stackResource.id;
|
|
104
|
+
}
|
|
105
|
+
let deployedStack;
|
|
106
|
+
if (getStack && projectId && stackId) {
|
|
107
|
+
const { stack } = await getStackById({ stackId, projectId });
|
|
108
|
+
if (!stack) {
|
|
109
|
+
errors.push({
|
|
110
|
+
message: 'Stack not found',
|
|
111
|
+
type: BlueprintErrorType.InvalidStack,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
deployedStack = stack;
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
fileInfo: { path, fileName, extension },
|
|
118
|
+
errors,
|
|
119
|
+
projectId,
|
|
120
|
+
stackId,
|
|
121
|
+
deployedStack,
|
|
122
|
+
parsedBlueprint,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
throw Error(`Unable to parse Blueprint file: ${err}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export async function updateBlueprintMetadata({ blueprintPath, projectId, stackId, }) {
|
|
130
|
+
const blueprintFile = findBlueprintFile(blueprintPath);
|
|
131
|
+
if (!blueprintFile)
|
|
132
|
+
throw Error('Could not find Blueprint file');
|
|
133
|
+
const { path } = blueprintFile;
|
|
134
|
+
const blueprintString = readFileSync(path, 'utf8').toString();
|
|
135
|
+
const blueprint = JSON.parse(blueprintString);
|
|
136
|
+
blueprint.metadata = blueprint.metadata || {};
|
|
137
|
+
blueprint.metadata.projectId = projectId;
|
|
138
|
+
blueprint.metadata.stackId = stackId;
|
|
139
|
+
writeFileSync(path, JSON.stringify(blueprint, null, 2));
|
|
140
|
+
}
|
|
141
|
+
export function writeBlueprintConfig({ blueprintPath, projectId, stackId, }) {
|
|
142
|
+
const blueprintFile = findBlueprintFile(blueprintPath);
|
|
143
|
+
if (!blueprintFile)
|
|
144
|
+
throw Error('Could not find Blueprint file');
|
|
145
|
+
const { path } = blueprintFile;
|
|
146
|
+
const blueprintDir = dirname(path);
|
|
147
|
+
const configDir = join(blueprintDir, '.blueprint');
|
|
148
|
+
const configPath = join(configDir, 'config.json');
|
|
149
|
+
if (!existsSync(configDir)) {
|
|
150
|
+
mkdirSync(configDir, { recursive: true });
|
|
151
|
+
}
|
|
152
|
+
let config = {};
|
|
153
|
+
if (existsSync(configPath)) {
|
|
154
|
+
try {
|
|
155
|
+
config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
// config broken, start fresh
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
config.projectId = projectId;
|
|
162
|
+
config.stackId = stackId;
|
|
163
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
164
|
+
}
|
|
@@ -3,7 +3,9 @@ import config from '../../config.js';
|
|
|
3
3
|
const { blueprints } = config.server;
|
|
4
4
|
export const logsUrl = `${blueprints}vX/blueprints/logs`;
|
|
5
5
|
export async function getLogs(stackId, projectId, token) {
|
|
6
|
-
const
|
|
6
|
+
const url = new URL(logsUrl);
|
|
7
|
+
url.searchParams.append('stackId', stackId);
|
|
8
|
+
const response = await fetch(url.toString(), {
|
|
7
9
|
headers: {
|
|
8
10
|
Accept: 'application/json',
|
|
9
11
|
'Content-Type': 'application/json',
|
|
@@ -1,28 +1,54 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
import
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { readBlueprintOnDisk, writeBlueprintConfig } from '../../actions/blueprints/blueprint.js';
|
|
3
4
|
import { createStack, updateStack } from '../../actions/blueprints/stacks.js';
|
|
4
5
|
import { stashAsset } from '../../actions/blueprints/stash-asset.js';
|
|
5
|
-
import { green, red, yellow } from '../../utils/display/colors.js';
|
|
6
|
+
import { bold, green, red, yellow } from '../../utils/display/colors.js';
|
|
6
7
|
import Spinner from '../../utils/spinner.js';
|
|
7
8
|
export default class Deploy extends Command {
|
|
8
9
|
static description = 'Deploy a Blueprint';
|
|
9
10
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
10
11
|
async run() {
|
|
11
|
-
const { errors,
|
|
12
|
+
const { errors, projectId: configuredProjectId, stackId, parsedBlueprint: { resources }, deployedStack, } = await readBlueprintOnDisk({ getStack: true });
|
|
12
13
|
if (errors.length > 0) {
|
|
13
14
|
// printErrors(errors) // TODO: error printer in formatting
|
|
14
15
|
this.log('Blueprint parse errors:');
|
|
15
16
|
console.dir(errors, { depth: null });
|
|
16
17
|
return;
|
|
17
18
|
}
|
|
18
|
-
if (!
|
|
19
|
-
this.error('
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
if (stackId && !deployedStack)
|
|
20
|
+
this.error('Stack ID defined but deployed stack not found');
|
|
21
|
+
let projectId = configuredProjectId;
|
|
22
|
+
if (!projectId) {
|
|
23
|
+
this.log('No Sanity Project context found in Blueprint configuration.');
|
|
24
|
+
const { maybeProjectId } = await inquirer.prompt([
|
|
25
|
+
{
|
|
26
|
+
type: 'input',
|
|
27
|
+
name: 'maybeProjectId',
|
|
28
|
+
message: 'Enter Sanity Project ID:',
|
|
29
|
+
},
|
|
30
|
+
]);
|
|
31
|
+
projectId = maybeProjectId;
|
|
32
|
+
}
|
|
33
|
+
if (!projectId)
|
|
34
|
+
this.error('Sanity Project context is required');
|
|
35
|
+
let name = deployedStack?.name;
|
|
36
|
+
if (!name) {
|
|
37
|
+
const { stackName } = await inquirer.prompt([
|
|
38
|
+
{
|
|
39
|
+
type: 'input',
|
|
40
|
+
name: 'stackName',
|
|
41
|
+
message: 'Enter stack name:',
|
|
42
|
+
validate: (input) => input.length > 0 || 'Stack name is required',
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
name = stackName;
|
|
46
|
+
}
|
|
47
|
+
if (!name)
|
|
48
|
+
this.error('Stack name is required');
|
|
24
49
|
const s = new Spinner();
|
|
25
|
-
const
|
|
50
|
+
const validResources = resources.filter((r) => r.kind);
|
|
51
|
+
const functionResources = validResources.filter((r) => r.type?.startsWith('sanity.function.'));
|
|
26
52
|
// First stash all function assets
|
|
27
53
|
if (functionResources.length > 0) {
|
|
28
54
|
for (const resource of functionResources) {
|
|
@@ -45,7 +71,7 @@ export default class Deploy extends Command {
|
|
|
45
71
|
const blueprint = {
|
|
46
72
|
name,
|
|
47
73
|
projectId,
|
|
48
|
-
document: { resources },
|
|
74
|
+
document: { resources: validResources },
|
|
49
75
|
};
|
|
50
76
|
this.debug('BLUEPRINT DOCUMENT:', blueprint);
|
|
51
77
|
const { ok: deployOk, stack, error: deployError, } = deployedStack
|
|
@@ -53,13 +79,17 @@ export default class Deploy extends Command {
|
|
|
53
79
|
: await createStack({ blueprint, projectId });
|
|
54
80
|
this.debug('STACK RESPONSE:', stack);
|
|
55
81
|
if (deployOk) {
|
|
56
|
-
s.stop(`${green('Success!')} Stack ${deployedStack ? 'updated' : 'created'} <${yellow(stack.id)}>`);
|
|
82
|
+
s.stop(`${green('Success!')} Stack "${bold(stack.name)}" ${deployedStack ? 'updated' : 'created'} <${yellow(stack.id)}>`);
|
|
83
|
+
writeBlueprintConfig({
|
|
84
|
+
projectId,
|
|
85
|
+
stackId: stack.id,
|
|
86
|
+
});
|
|
57
87
|
this.log('\nUse `sanity blueprints info` to check deployment status');
|
|
58
88
|
}
|
|
59
89
|
else {
|
|
60
90
|
this.debug('STACK ERROR RESPONSE:', stack);
|
|
61
91
|
s.stop(`${red('Failed')} to ${deployedStack ? 'update' : 'create'} stack`);
|
|
62
|
-
this.log(`Error: ${deployError || 'Unknown error'}`);
|
|
92
|
+
this.log(`Error: ${deployError || JSON.stringify(stack, null, 2) || 'Unknown error'}`);
|
|
63
93
|
}
|
|
64
94
|
}
|
|
65
95
|
}
|
|
@@ -2,5 +2,8 @@ import { Command } from '@oclif/core';
|
|
|
2
2
|
export default class Info extends Command {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
id: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
};
|
|
5
8
|
run(): Promise<void>;
|
|
6
9
|
}
|
|
@@ -1,32 +1,55 @@
|
|
|
1
|
-
import { Command } from '@oclif/core';
|
|
2
|
-
import readBlueprintOnDisk from '../../actions/blueprints/
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
|
|
3
|
+
import { getStack } from '../../actions/blueprints/stacks.js';
|
|
3
4
|
import { formatResourceTree } from '../../utils/display/blueprints-formatting.js';
|
|
4
5
|
import { bold, green, red, yellow } from '../../utils/display/colors.js';
|
|
5
6
|
import { formatDate, formatDuration } from '../../utils/display/dates.js';
|
|
6
7
|
export default class Info extends Command {
|
|
7
8
|
static description = 'Show information about a Blueprint';
|
|
8
|
-
static examples = [
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> <%= command.id %>',
|
|
11
|
+
'<%= config.bin %> <%= command.id %> --id abc123',
|
|
12
|
+
];
|
|
13
|
+
static flags = {
|
|
14
|
+
id: Flags.string({
|
|
15
|
+
description: 'Stack ID to show info for (defaults to current stack)',
|
|
16
|
+
required: false,
|
|
17
|
+
}),
|
|
18
|
+
};
|
|
9
19
|
async run() {
|
|
10
|
-
const {
|
|
20
|
+
const { flags } = await this.parse(Info);
|
|
21
|
+
const { errors, deployedStack, projectId } = await readBlueprintOnDisk({ getStack: true });
|
|
11
22
|
if (errors.length > 0) {
|
|
12
23
|
// printErrors(errors)
|
|
13
24
|
this.log('Blueprint parse errors:');
|
|
14
25
|
console.dir(errors, { depth: null });
|
|
15
26
|
return;
|
|
16
27
|
}
|
|
17
|
-
if (!
|
|
18
|
-
this.error('
|
|
28
|
+
if (!projectId)
|
|
29
|
+
this.error('Project resource not found in blueprint');
|
|
30
|
+
let stack = deployedStack;
|
|
31
|
+
if (flags.id) {
|
|
32
|
+
const { ok, stack: foundStack, error } = await getStack({ stackId: flags.id, projectId });
|
|
33
|
+
if (!ok)
|
|
34
|
+
this.error(error || 'Failed to get stack');
|
|
35
|
+
stack = foundStack;
|
|
36
|
+
}
|
|
37
|
+
else if (!stack) {
|
|
38
|
+
this.error('No stack found');
|
|
39
|
+
}
|
|
40
|
+
if (!stack)
|
|
41
|
+
this.error('Stack not found. Is it deployed?');
|
|
19
42
|
try {
|
|
20
|
-
this.log(`Stack name: ${bold(
|
|
21
|
-
this.log(`Stack ID: ${yellow(
|
|
22
|
-
if (
|
|
23
|
-
this.log(`Created: ${formatDate(
|
|
43
|
+
this.log(`Stack name: ${bold(stack.name)}`);
|
|
44
|
+
this.log(`Stack ID: ${yellow(stack.id)}`);
|
|
45
|
+
if (stack.createdAt) {
|
|
46
|
+
this.log(`Created: ${formatDate(stack.createdAt)}`);
|
|
24
47
|
}
|
|
25
|
-
if (
|
|
26
|
-
this.log(`Updated: ${formatDate(
|
|
48
|
+
if (stack.updatedAt) {
|
|
49
|
+
this.log(`Updated: ${formatDate(stack.updatedAt)}`);
|
|
27
50
|
}
|
|
28
|
-
if (
|
|
29
|
-
const operation =
|
|
51
|
+
if (stack.recentOperation) {
|
|
52
|
+
const operation = stack.recentOperation;
|
|
30
53
|
if (operation.id) {
|
|
31
54
|
this.log(`Recent Operation <${yellow(operation.id)}>:`);
|
|
32
55
|
}
|
|
@@ -44,8 +67,8 @@ export default class Info extends Command {
|
|
|
44
67
|
}
|
|
45
68
|
}
|
|
46
69
|
this.log('');
|
|
47
|
-
if (
|
|
48
|
-
formatResourceTree(
|
|
70
|
+
if (stack.resources) {
|
|
71
|
+
formatResourceTree(stack.resources, this.log.bind(this));
|
|
49
72
|
}
|
|
50
73
|
}
|
|
51
74
|
catch (err) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
|
|
2
3
|
import { getLogs, streamLogs } from '../../actions/blueprints/logs.js';
|
|
3
|
-
import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
|
|
4
4
|
import config from '../../config.js';
|
|
5
5
|
import { formatTitle } from '../../utils/display/blueprints-formatting.js';
|
|
6
6
|
import { blue, bold, green, red, yellow } from '../../utils/display/colors.js';
|
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
import readBlueprintOnDisk from '../../actions/blueprints/
|
|
2
|
+
import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
|
|
3
3
|
import { formatResourceTree, formatTitle } from '../../utils/display/blueprints-formatting.js';
|
|
4
4
|
export default class Plan extends Command {
|
|
5
5
|
static description = 'Enumerate resources to be deployed - will not modify any resources';
|
|
6
6
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
7
7
|
async run() {
|
|
8
|
-
const { errors,
|
|
8
|
+
const { errors, projectId, stackId, parsedBlueprint: { resources }, fileInfo, } = await readBlueprintOnDisk();
|
|
9
9
|
if (errors.length > 0) {
|
|
10
10
|
// printErrors(errors)
|
|
11
11
|
this.log('Blueprint parse errors:');
|
|
12
12
|
console.dir(errors, { depth: null });
|
|
13
|
-
return
|
|
13
|
+
// return // don't return, show the plan
|
|
14
14
|
}
|
|
15
|
-
if (!
|
|
15
|
+
if (!projectId)
|
|
16
16
|
this.log('Blueprint must contain a project resource');
|
|
17
|
-
|
|
18
|
-
this.log('Blueprint must contain a stack resource');
|
|
19
|
-
const name = stackResource?.name || 'Unknown';
|
|
17
|
+
const name = stackId || 'Unknown'; // TODO: what is a name, really?
|
|
20
18
|
this.log(`${formatTitle('Blueprint', name)} Plan\n`);
|
|
21
|
-
this.log(`Blueprint document:
|
|
19
|
+
this.log(`Blueprint document: (${fileInfo.fileName})`);
|
|
22
20
|
this.log('');
|
|
23
21
|
formatResourceTree(resources, this.log.bind(this));
|
|
24
22
|
if (resources.length > 0) {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
|
|
3
|
+
import { listStacks } from '../../actions/blueprints/stacks.js';
|
|
4
|
+
import { bold, boldnblue, yellow } from '../../utils/display/colors.js';
|
|
5
|
+
import { formatDate } from '../../utils/display/dates.js';
|
|
6
|
+
export default class Stacks extends Command {
|
|
7
|
+
static description = 'List all Blueprint stacks';
|
|
8
|
+
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
9
|
+
async run() {
|
|
10
|
+
const { errors, projectId, stackId } = await readBlueprintOnDisk();
|
|
11
|
+
if (errors.length > 0) {
|
|
12
|
+
this.log('Blueprint parse errors:');
|
|
13
|
+
console.dir(errors, { depth: null });
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (!projectId)
|
|
17
|
+
this.error('Project resource not found in blueprint');
|
|
18
|
+
const { ok, stacks, error } = await listStacks({ projectId });
|
|
19
|
+
if (!ok)
|
|
20
|
+
this.error(error || 'Failed to list stacks');
|
|
21
|
+
if (!stacks || stacks.length === 0) {
|
|
22
|
+
this.log('No stacks found');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
this.log(`${bold('Project')} <${yellow(projectId)}> ${bold('Stacks')} :\n`);
|
|
26
|
+
for (const stack of stacks) {
|
|
27
|
+
const isCurrentStack = stackId === stack.id;
|
|
28
|
+
const stackName = isCurrentStack ? boldnblue(stack.name) : bold(stack.name);
|
|
29
|
+
this.log(`${stackName} <${yellow(stack.id)}>${isCurrentStack ? ' (current)' : ''}`);
|
|
30
|
+
if (stack.createdAt) {
|
|
31
|
+
this.log(` Created: ${formatDate(stack.createdAt)}`);
|
|
32
|
+
}
|
|
33
|
+
if (stack.updatedAt) {
|
|
34
|
+
this.log(` Updated: ${formatDate(stack.updatedAt)}`);
|
|
35
|
+
}
|
|
36
|
+
this.log(` ${stack.resources.length} resource${stack.resources.length === 1 ? '' : 's'}`);
|
|
37
|
+
this.log('');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
-
import readBlueprintOnDisk from '../../actions/blueprints/
|
|
2
|
+
import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
|
|
3
3
|
import { invoke } from '../../actions/functions/invoke.js';
|
|
4
4
|
import config from '../../config.js';
|
|
5
5
|
import { red } from '../../utils/display/colors.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Args, Command } from '@oclif/core';
|
|
2
|
-
import readBlueprintOnDisk from '../../actions/blueprints/
|
|
2
|
+
import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
|
|
3
3
|
import { logs } from '../../actions/functions/logs.js';
|
|
4
4
|
import config from '../../config.js';
|
|
5
5
|
import { bold, red, yellow } from '../../utils/display/colors.js';
|
package/dist/server/app.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { default as mime } from 'mime-types';
|
|
3
|
-
import readBlueprintOnDisk from '../actions/blueprints/
|
|
3
|
+
import { readBlueprintOnDisk } from '../actions/blueprints/blueprint.js';
|
|
4
4
|
import invoke from '../utils/invoke-local.js';
|
|
5
5
|
import * as http from 'node:http';
|
|
6
6
|
const host = 'localhost';
|
|
@@ -36,7 +36,7 @@ export function formatResourceTree(resources, logger) {
|
|
|
36
36
|
logger(` └─ ${bold('Other Resources')} [${otherResources.length}]`);
|
|
37
37
|
for (const [i, other] of otherResources.entries()) {
|
|
38
38
|
const isLast = i === otherResources.length - 1;
|
|
39
|
-
logger(`
|
|
39
|
+
logger(` ${isLast ? '└─' : '├─'} "${yellow(other.displayName || other.name || other.src)}"`);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
}
|
package/dist/utils/types.d.ts
CHANGED
package/oclif.manifest.json
CHANGED
|
@@ -29,9 +29,19 @@
|
|
|
29
29
|
"args": {},
|
|
30
30
|
"description": "Show information about a Blueprint",
|
|
31
31
|
"examples": [
|
|
32
|
-
"<%= config.bin %> <%= command.id %>"
|
|
32
|
+
"<%= config.bin %> <%= command.id %>",
|
|
33
|
+
"<%= config.bin %> <%= command.id %> --id abc123"
|
|
33
34
|
],
|
|
34
|
-
"flags": {
|
|
35
|
+
"flags": {
|
|
36
|
+
"id": {
|
|
37
|
+
"description": "Stack ID to show info for (defaults to current stack)",
|
|
38
|
+
"name": "id",
|
|
39
|
+
"required": false,
|
|
40
|
+
"hasDynamicHelp": false,
|
|
41
|
+
"multiple": false,
|
|
42
|
+
"type": "option"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
35
45
|
"hasDynamicHelp": false,
|
|
36
46
|
"hiddenAliases": [],
|
|
37
47
|
"id": "blueprints:info",
|
|
@@ -106,6 +116,30 @@
|
|
|
106
116
|
"plan.js"
|
|
107
117
|
]
|
|
108
118
|
},
|
|
119
|
+
"blueprints:stacks": {
|
|
120
|
+
"aliases": [],
|
|
121
|
+
"args": {},
|
|
122
|
+
"description": "List all Blueprint stacks",
|
|
123
|
+
"examples": [
|
|
124
|
+
"<%= config.bin %> <%= command.id %>"
|
|
125
|
+
],
|
|
126
|
+
"flags": {},
|
|
127
|
+
"hasDynamicHelp": false,
|
|
128
|
+
"hiddenAliases": [],
|
|
129
|
+
"id": "blueprints:stacks",
|
|
130
|
+
"pluginAlias": "@sanity/runtime-cli",
|
|
131
|
+
"pluginName": "@sanity/runtime-cli",
|
|
132
|
+
"pluginType": "core",
|
|
133
|
+
"strict": true,
|
|
134
|
+
"enableJsonFlag": false,
|
|
135
|
+
"isESM": true,
|
|
136
|
+
"relativePath": [
|
|
137
|
+
"dist",
|
|
138
|
+
"commands",
|
|
139
|
+
"blueprints",
|
|
140
|
+
"stacks.js"
|
|
141
|
+
]
|
|
142
|
+
},
|
|
109
143
|
"functions:dev": {
|
|
110
144
|
"aliases": [],
|
|
111
145
|
"args": {},
|
|
@@ -281,5 +315,5 @@
|
|
|
281
315
|
]
|
|
282
316
|
}
|
|
283
317
|
},
|
|
284
|
-
"version": "1.
|
|
318
|
+
"version": "1.5.0"
|
|
285
319
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/runtime-cli",
|
|
3
3
|
"description": "A new CLI generated with oclif",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.5.0",
|
|
5
5
|
"author": "Sanity Runtime Team",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"@oclif/plugin-help": "^6",
|
|
42
42
|
"@oclif/plugin-plugins": "^5",
|
|
43
43
|
"eventsource": "^3.0.5",
|
|
44
|
+
"inquirer": "^12.5.0",
|
|
44
45
|
"jszip": "^3.10.1",
|
|
45
46
|
"mime-types": "^2.1.35",
|
|
46
47
|
"xdg-basedir": "^5.1.0"
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { Blueprint, BlueprintError, BlueprintResource, BlueprintStack } from '../../utils/types.js';
|
|
2
|
-
export default function readBlueprintOnDisk({ blueprintPath, getStack, }?: {
|
|
3
|
-
blueprintPath?: string;
|
|
4
|
-
getStack?: boolean;
|
|
5
|
-
}): Promise<{
|
|
6
|
-
fileInfo: {
|
|
7
|
-
path: string;
|
|
8
|
-
fileName: string;
|
|
9
|
-
extension: string;
|
|
10
|
-
};
|
|
11
|
-
parsedBlueprint: Blueprint;
|
|
12
|
-
errors: BlueprintError[];
|
|
13
|
-
projectResource?: BlueprintResource;
|
|
14
|
-
stackResource?: BlueprintResource;
|
|
15
|
-
deployedStack?: BlueprintStack;
|
|
16
|
-
}>;
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { extname, join } from 'node:path';
|
|
3
|
-
import { cwd } from 'node:process';
|
|
4
|
-
import { getStackByName } from '../../actions/blueprints/stacks.js';
|
|
5
|
-
import { BlueprintErrorType } from '../../utils/types.js';
|
|
6
|
-
// @ts-ignore - this is currently untyped
|
|
7
|
-
import blueprintParserValidator from '../../utils/vendor/parser-validator.js';
|
|
8
|
-
const SUPPORTED_FILE_NAMES_IN_PRIORITY_ORDER = [
|
|
9
|
-
'blueprint.json',
|
|
10
|
-
// 'blueprint.js',
|
|
11
|
-
// 'blueprint.mjs',
|
|
12
|
-
// 'blueprint.cjs',
|
|
13
|
-
// 'blueprint.ts',
|
|
14
|
-
];
|
|
15
|
-
function findBlueprintFile(blueprintPath) {
|
|
16
|
-
if (blueprintPath) {
|
|
17
|
-
if (existsSync(blueprintPath)) {
|
|
18
|
-
return { path: blueprintPath, fileName: blueprintPath, extension: extname(blueprintPath) };
|
|
19
|
-
}
|
|
20
|
-
throw Error(`Blueprint file not found: ${blueprintPath}`);
|
|
21
|
-
}
|
|
22
|
-
for (const fileName of SUPPORTED_FILE_NAMES_IN_PRIORITY_ORDER) {
|
|
23
|
-
const filePath = join(cwd(), fileName);
|
|
24
|
-
if (existsSync(filePath)) {
|
|
25
|
-
return { path: filePath, fileName, extension: extname(filePath) };
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
export default async function readBlueprintOnDisk({ blueprintPath, getStack, } = {}) {
|
|
31
|
-
try {
|
|
32
|
-
const blueprintFile = findBlueprintFile(blueprintPath);
|
|
33
|
-
if (!blueprintFile)
|
|
34
|
-
throw Error('Could not find Blueprint file');
|
|
35
|
-
const { path, fileName, extension } = blueprintFile;
|
|
36
|
-
let blueprintString;
|
|
37
|
-
if (extension === '.json') {
|
|
38
|
-
blueprintString = readFileSync(path, 'utf8').toString();
|
|
39
|
-
// } else if (extension === '.js' || extension === '.mjs') {
|
|
40
|
-
// const blueprintModule = require(path)
|
|
41
|
-
// blueprintJson = blueprintModule.default
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
throw Error(`Unsupported blueprint file extension: ${extension}`);
|
|
45
|
-
}
|
|
46
|
-
const parsed = blueprintParserValidator(JSON.parse(blueprintString));
|
|
47
|
-
const { blueprint: parsedBlueprint } = parsed;
|
|
48
|
-
const errors = parsed.errors || [];
|
|
49
|
-
const projectResource = parsedBlueprint.resources.find((r) => r.type === 'sanity.project');
|
|
50
|
-
const stackResource = parsedBlueprint.resources.find((r) => r.type === 'sanity.blueprints.stack');
|
|
51
|
-
if (!projectResource) {
|
|
52
|
-
errors.push({
|
|
53
|
-
message: 'Blueprint is missing a project resource',
|
|
54
|
-
type: BlueprintErrorType.MissingProject,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
if (!stackResource) {
|
|
58
|
-
errors.push({
|
|
59
|
-
message: 'Blueprint is missing a stack resource',
|
|
60
|
-
type: BlueprintErrorType.MissingStack,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
let deployedStack;
|
|
64
|
-
if (getStack && projectResource && stackResource) {
|
|
65
|
-
const { stack } = await getStackByName({
|
|
66
|
-
name: stackResource.name,
|
|
67
|
-
projectId: projectResource.id,
|
|
68
|
-
});
|
|
69
|
-
if (!stack) {
|
|
70
|
-
errors.push({
|
|
71
|
-
message: 'Stack not found',
|
|
72
|
-
type: BlueprintErrorType.InvalidStack,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
deployedStack = stack;
|
|
76
|
-
}
|
|
77
|
-
return {
|
|
78
|
-
fileInfo: { path, fileName, extension },
|
|
79
|
-
errors,
|
|
80
|
-
projectResource,
|
|
81
|
-
stackResource,
|
|
82
|
-
deployedStack,
|
|
83
|
-
parsedBlueprint,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
catch (err) {
|
|
87
|
-
throw Error(`Unable to parse Blueprint file: ${err}`);
|
|
88
|
-
}
|
|
89
|
-
}
|