@sanity/runtime-cli 1.2.1 → 1.3.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.
- package/README.md +34 -8
- package/dist/actions/blueprints/logs.d.ts +7 -0
- package/dist/actions/blueprints/logs.js +79 -0
- package/dist/actions/blueprints/operations.d.ts +18 -0
- package/dist/actions/blueprints/operations.js +52 -0
- package/dist/actions/blueprints/read-blueprint.d.ts +6 -1
- package/dist/actions/blueprints/read-blueprint.js +35 -9
- package/dist/actions/blueprints/stacks.d.ts +33 -5
- package/dist/actions/blueprints/stacks.js +53 -17
- package/dist/actions/blueprints/stash-asset.d.ts +4 -1
- package/dist/actions/blueprints/stash-asset.js +12 -7
- package/dist/actions/functions/invoke.js +8 -5
- package/dist/commands/blueprints/deploy.js +29 -35
- package/dist/commands/blueprints/info.js +45 -39
- package/dist/commands/blueprints/logs.d.ts +10 -0
- package/dist/commands/blueprints/logs.js +166 -0
- package/dist/commands/blueprints/plan.js +10 -23
- package/dist/config.js +2 -2
- package/dist/server/app.js +11 -6
- package/dist/server/static/api.js +4 -1
- package/dist/server/static/components/function-list.js +17 -12
- package/dist/utils/display/blueprints-formatting.d.ts +4 -0
- package/dist/utils/display/blueprints-formatting.js +42 -0
- package/dist/utils/display/colors.d.ts +6 -0
- package/dist/utils/display/colors.js +18 -0
- package/dist/utils/display/dates.d.ts +2 -0
- package/dist/utils/display/dates.js +26 -0
- package/dist/utils/types.d.ts +22 -3
- package/oclif.manifest.json +45 -1
- package/package.json +4 -2
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.3.1 linux-x64 node-v22.14.0
|
|
24
24
|
$ sanity --help [COMMAND]
|
|
25
25
|
USAGE
|
|
26
26
|
$ sanity COMMAND
|
|
@@ -31,6 +31,7 @@ USAGE
|
|
|
31
31
|
<!-- commands -->
|
|
32
32
|
* [`sanity blueprints deploy`](#sanity-blueprints-deploy)
|
|
33
33
|
* [`sanity blueprints info`](#sanity-blueprints-info)
|
|
34
|
+
* [`sanity blueprints logs`](#sanity-blueprints-logs)
|
|
34
35
|
* [`sanity blueprints plan`](#sanity-blueprints-plan)
|
|
35
36
|
* [`sanity functions dev`](#sanity-functions-dev)
|
|
36
37
|
* [`sanity functions invoke ID`](#sanity-functions-invoke-id)
|
|
@@ -63,7 +64,7 @@ EXAMPLES
|
|
|
63
64
|
$ sanity blueprints deploy
|
|
64
65
|
```
|
|
65
66
|
|
|
66
|
-
_See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
67
|
+
_See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v1.3.1/src/commands/blueprints/deploy.ts)_
|
|
67
68
|
|
|
68
69
|
## `sanity blueprints info`
|
|
69
70
|
|
|
@@ -80,7 +81,32 @@ EXAMPLES
|
|
|
80
81
|
$ sanity blueprints info
|
|
81
82
|
```
|
|
82
83
|
|
|
83
|
-
_See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
84
|
+
_See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v1.3.1/src/commands/blueprints/info.ts)_
|
|
85
|
+
|
|
86
|
+
## `sanity blueprints logs`
|
|
87
|
+
|
|
88
|
+
Display logs for a Blueprint stack
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
USAGE
|
|
92
|
+
$ sanity blueprints logs [-s <value>] [-w]
|
|
93
|
+
|
|
94
|
+
FLAGS
|
|
95
|
+
-s, --stack-id=<value> Stack ID to fetch logs for (optional if running in a blueprint directory)
|
|
96
|
+
-w, --watch Watch for new logs (streaming mode)
|
|
97
|
+
|
|
98
|
+
DESCRIPTION
|
|
99
|
+
Display logs for a Blueprint stack
|
|
100
|
+
|
|
101
|
+
EXAMPLES
|
|
102
|
+
$ sanity blueprints logs
|
|
103
|
+
|
|
104
|
+
$ sanity blueprints logs --stack-id <stack-id>
|
|
105
|
+
|
|
106
|
+
$ sanity blueprints logs --watch
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
_See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v1.3.1/src/commands/blueprints/logs.ts)_
|
|
84
110
|
|
|
85
111
|
## `sanity blueprints plan`
|
|
86
112
|
|
|
@@ -97,7 +123,7 @@ EXAMPLES
|
|
|
97
123
|
$ sanity blueprints plan
|
|
98
124
|
```
|
|
99
125
|
|
|
100
|
-
_See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
126
|
+
_See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v1.3.1/src/commands/blueprints/plan.ts)_
|
|
101
127
|
|
|
102
128
|
## `sanity functions dev`
|
|
103
129
|
|
|
@@ -117,7 +143,7 @@ EXAMPLES
|
|
|
117
143
|
$ sanity functions dev --port 8974
|
|
118
144
|
```
|
|
119
145
|
|
|
120
|
-
_See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
146
|
+
_See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v1.3.1/src/commands/functions/dev.ts)_
|
|
121
147
|
|
|
122
148
|
## `sanity functions invoke ID`
|
|
123
149
|
|
|
@@ -143,7 +169,7 @@ EXAMPLES
|
|
|
143
169
|
$ sanity functions invoke <ID> --file 'payload.json'
|
|
144
170
|
```
|
|
145
171
|
|
|
146
|
-
_See code: [src/commands/functions/invoke.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
172
|
+
_See code: [src/commands/functions/invoke.ts](https://github.com/sanity-io/runtime-cli/blob/v1.3.1/src/commands/functions/invoke.ts)_
|
|
147
173
|
|
|
148
174
|
## `sanity functions logs ID`
|
|
149
175
|
|
|
@@ -163,7 +189,7 @@ EXAMPLES
|
|
|
163
189
|
$ sanity functions logs <ID>
|
|
164
190
|
```
|
|
165
191
|
|
|
166
|
-
_See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
192
|
+
_See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v1.3.1/src/commands/functions/logs.ts)_
|
|
167
193
|
|
|
168
194
|
## `sanity functions test PATH`
|
|
169
195
|
|
|
@@ -192,7 +218,7 @@ EXAMPLES
|
|
|
192
218
|
$ sanity functions test ./test.ts --data '{ "id": 1 }' --timeout 60
|
|
193
219
|
```
|
|
194
220
|
|
|
195
|
-
_See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v1.
|
|
221
|
+
_See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v1.3.1/src/commands/functions/test.ts)_
|
|
196
222
|
|
|
197
223
|
## `sanity help [COMMAND]`
|
|
198
224
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BlueprintLog } from '../../utils/types.js';
|
|
2
|
+
export declare function getLogs(stackId: string, projectId: string, token: string): Promise<{
|
|
3
|
+
logs: BlueprintLog[];
|
|
4
|
+
ok: boolean;
|
|
5
|
+
error: string | null;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function streamLogs(stackId: string, projectId: string, token: string, onLog: (log: BlueprintLog) => void, onOpen: () => void, onError: (error: string) => void): () => void;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { EventSource } from 'eventsource';
|
|
2
|
+
import config from '../../config.js';
|
|
3
|
+
const { blueprints } = config.server;
|
|
4
|
+
export async function getLogs(stackId, projectId, token) {
|
|
5
|
+
const response = await fetch(`${blueprints}/vX/blueprints/logs?stackId=${stackId}`, {
|
|
6
|
+
headers: {
|
|
7
|
+
Accept: 'application/json',
|
|
8
|
+
'Content-Type': 'application/json',
|
|
9
|
+
Authorization: `Bearer ${token}`,
|
|
10
|
+
'X-Sanity-Scope-Type': 'project',
|
|
11
|
+
'X-Sanity-Scope-Id': projectId,
|
|
12
|
+
},
|
|
13
|
+
method: 'GET',
|
|
14
|
+
});
|
|
15
|
+
const result = await response.json();
|
|
16
|
+
return {
|
|
17
|
+
ok: response.ok,
|
|
18
|
+
error: response.ok ? null : result.message || 'Unknown error',
|
|
19
|
+
logs: response.ok ? result : [],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function streamLogs(stackId, projectId, token, onLog, onOpen, onError) {
|
|
23
|
+
const url = new URL(`${blueprints}/vX/blueprints/logs/stream`);
|
|
24
|
+
url.searchParams.append('stackId', stackId);
|
|
25
|
+
const headers = {
|
|
26
|
+
Authorization: `Bearer ${token}`,
|
|
27
|
+
'X-Sanity-Scope-Type': 'project',
|
|
28
|
+
'X-Sanity-Scope-Id': projectId,
|
|
29
|
+
};
|
|
30
|
+
const eventSource = new EventSource(url.toString(), {
|
|
31
|
+
fetch: (input, init) => fetch(input, {
|
|
32
|
+
...init,
|
|
33
|
+
headers: {
|
|
34
|
+
...init?.headers,
|
|
35
|
+
...headers,
|
|
36
|
+
},
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
eventSource.onopen = onOpen;
|
|
40
|
+
eventSource.onmessage = (event) => {
|
|
41
|
+
try {
|
|
42
|
+
const log = JSON.parse(event.data);
|
|
43
|
+
onLog(log);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
onError(`Failed to parse log data: ${err instanceof Error ? err.message : String(err)}`);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
eventSource.addEventListener('logs', (event) => {
|
|
50
|
+
try {
|
|
51
|
+
const logData = JSON.parse(event.data);
|
|
52
|
+
// usually an array
|
|
53
|
+
if (Array.isArray(logData)) {
|
|
54
|
+
for (const log of logData)
|
|
55
|
+
onLog(log);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
onLog(logData);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
console.error('Error parsing logs event:', err);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
eventSource.onerror = (err) => {
|
|
66
|
+
onError('Connection to log stream failed or was closed');
|
|
67
|
+
if (eventSource.readyState === eventSource.CLOSED) {
|
|
68
|
+
console.log('Connection is CLOSED');
|
|
69
|
+
}
|
|
70
|
+
else if (eventSource.readyState === eventSource.CONNECTING) {
|
|
71
|
+
console.log('Connection is attempting to reconnect...');
|
|
72
|
+
return; // Don't close if trying to reconnect
|
|
73
|
+
}
|
|
74
|
+
eventSource.close();
|
|
75
|
+
};
|
|
76
|
+
return () => {
|
|
77
|
+
eventSource.close();
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BlueprintOperation } from '../../utils/types.js';
|
|
2
|
+
export declare function getOperation({ stackId, operationId, projectId, }: {
|
|
3
|
+
stackId: string;
|
|
4
|
+
operationId: string;
|
|
5
|
+
projectId: string;
|
|
6
|
+
}): Promise<{
|
|
7
|
+
ok: boolean;
|
|
8
|
+
error: string | null;
|
|
9
|
+
operation: BlueprintOperation | null;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function listOperations({ stackId, projectId, }: {
|
|
12
|
+
stackId: string;
|
|
13
|
+
projectId: string;
|
|
14
|
+
}): Promise<{
|
|
15
|
+
ok: boolean;
|
|
16
|
+
error: string | null;
|
|
17
|
+
operations: BlueprintOperation[];
|
|
18
|
+
}>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import config from '../../config.js';
|
|
2
|
+
const { blueprints } = config.server;
|
|
3
|
+
function getHeaders(projectId) {
|
|
4
|
+
return {
|
|
5
|
+
Authorization: `Bearer ${config.token}`,
|
|
6
|
+
'Content-Type': 'application/json',
|
|
7
|
+
'X-Sanity-Scope-Type': 'project',
|
|
8
|
+
'X-Sanity-Scope-Id': projectId,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export async function getOperation({ stackId, operationId, projectId, }) {
|
|
12
|
+
const path = `${blueprints}/vX/blueprints/stacks/${stackId}/operations/${operationId}`;
|
|
13
|
+
const response = await fetch(path, {
|
|
14
|
+
method: 'GET',
|
|
15
|
+
headers: getHeaders(projectId),
|
|
16
|
+
});
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
const errorText = await response.text();
|
|
19
|
+
return {
|
|
20
|
+
ok: false,
|
|
21
|
+
error: errorText || 'Failed to fetch operation details',
|
|
22
|
+
operation: null,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const operation = await response.json();
|
|
26
|
+
return {
|
|
27
|
+
ok: true,
|
|
28
|
+
error: null,
|
|
29
|
+
operation,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export async function listOperations({ stackId, projectId, }) {
|
|
33
|
+
const path = `${blueprints}/vX/blueprints/stacks/${stackId}/operations`;
|
|
34
|
+
const response = await fetch(path, {
|
|
35
|
+
method: 'GET',
|
|
36
|
+
headers: getHeaders(projectId),
|
|
37
|
+
});
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
const errorText = await response.text();
|
|
40
|
+
return {
|
|
41
|
+
ok: false,
|
|
42
|
+
error: errorText || 'Failed to fetch operations',
|
|
43
|
+
operations: [],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const operations = await response.json();
|
|
47
|
+
return {
|
|
48
|
+
ok: true,
|
|
49
|
+
error: null,
|
|
50
|
+
operations,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -1,17 +1,43 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
2
|
+
import { extname, join } from 'node:path';
|
|
3
3
|
import { cwd } from 'node:process';
|
|
4
|
-
|
|
4
|
+
const SUPPORTED_FILE_NAMES_IN_PRIORITY_ORDER = [
|
|
5
|
+
'blueprint.json',
|
|
6
|
+
// 'blueprint.js',
|
|
7
|
+
// 'blueprint.mjs',
|
|
8
|
+
// 'blueprint.cjs',
|
|
9
|
+
// 'blueprint.ts',
|
|
10
|
+
];
|
|
11
|
+
function findBlueprintFile() {
|
|
12
|
+
for (const fileName of SUPPORTED_FILE_NAMES_IN_PRIORITY_ORDER) {
|
|
13
|
+
const filePath = join(cwd(), fileName);
|
|
14
|
+
if (existsSync(filePath)) {
|
|
15
|
+
return { path: filePath, fileName, extension: extname(filePath) };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
export default function readBlueprintOnDisk() {
|
|
5
21
|
try {
|
|
6
|
-
const blueprintPath =
|
|
7
|
-
if (!
|
|
8
|
-
throw Error('Could not find
|
|
22
|
+
const blueprintPath = findBlueprintFile();
|
|
23
|
+
if (!blueprintPath)
|
|
24
|
+
throw Error('Could not find Blueprint file');
|
|
25
|
+
const { path, fileName, extension } = blueprintPath;
|
|
26
|
+
let readBlueprint;
|
|
27
|
+
let blueprintJson;
|
|
28
|
+
if (extension === '.json') {
|
|
29
|
+
readBlueprint = readFileSync(path, 'utf8').toString();
|
|
30
|
+
blueprintJson = JSON.parse(readBlueprint);
|
|
31
|
+
// } else if (extension === '.js' || extension === '.mjs') {
|
|
32
|
+
// const blueprintModule = require(path)
|
|
33
|
+
// blueprintJson = blueprintModule.default
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
throw Error(`Unsupported blueprint file extension: ${extension}`);
|
|
9
37
|
}
|
|
10
|
-
|
|
11
|
-
const blueprintJson = JSON.parse(readBlueprint);
|
|
12
|
-
return blueprintJson;
|
|
38
|
+
return { document: blueprintJson, path, fileName, extension };
|
|
13
39
|
}
|
|
14
40
|
catch (err) {
|
|
15
|
-
throw Error(
|
|
41
|
+
throw Error(`Unable to parse Blueprint file: ${err}`);
|
|
16
42
|
}
|
|
17
43
|
}
|
|
@@ -1,22 +1,50 @@
|
|
|
1
1
|
import type { BlueprintJob } from '../../utils/types.js';
|
|
2
|
-
declare function listStacks(
|
|
2
|
+
declare function listStacks({ projectId }: {
|
|
3
|
+
projectId: string;
|
|
4
|
+
}): Promise<{
|
|
3
5
|
ok: boolean;
|
|
4
6
|
error: any;
|
|
5
7
|
stacks: any;
|
|
6
8
|
}>;
|
|
7
|
-
declare function
|
|
9
|
+
declare function getStackByName({ name, projectId }: {
|
|
10
|
+
name: string;
|
|
11
|
+
projectId: string;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
ok: boolean;
|
|
14
|
+
error: null;
|
|
15
|
+
stack: null;
|
|
16
|
+
stackId: null;
|
|
17
|
+
availableStacks: any;
|
|
18
|
+
} | {
|
|
19
|
+
ok: boolean;
|
|
20
|
+
error: any;
|
|
21
|
+
stack: any;
|
|
22
|
+
stackId: any;
|
|
23
|
+
availableStacks?: undefined;
|
|
24
|
+
}>;
|
|
25
|
+
declare function getStack({ stackId, projectId }: {
|
|
26
|
+
stackId: string;
|
|
27
|
+
projectId: string;
|
|
28
|
+
}): Promise<{
|
|
8
29
|
ok: boolean;
|
|
9
30
|
error: any;
|
|
10
31
|
stack: any;
|
|
11
32
|
}>;
|
|
12
|
-
declare function createStack(blueprint
|
|
33
|
+
declare function createStack({ blueprint, projectId }: {
|
|
34
|
+
blueprint: BlueprintJob;
|
|
35
|
+
projectId: string;
|
|
36
|
+
}): Promise<{
|
|
13
37
|
ok: boolean;
|
|
14
38
|
error: any;
|
|
15
39
|
stack: any;
|
|
16
40
|
}>;
|
|
17
|
-
declare function updateStack(stackId
|
|
41
|
+
declare function updateStack({ stackId, blueprint, projectId, }: {
|
|
42
|
+
stackId: string;
|
|
43
|
+
blueprint: BlueprintJob;
|
|
44
|
+
projectId: string;
|
|
45
|
+
}): Promise<{
|
|
18
46
|
ok: boolean;
|
|
19
47
|
error: any;
|
|
20
48
|
stack: any;
|
|
21
49
|
}>;
|
|
22
|
-
export { listStacks, getStack, createStack, updateStack };
|
|
50
|
+
export { listStacks, getStack, createStack, updateStack, getStackByName };
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import config from '../../config.js';
|
|
2
2
|
const { blueprints } = config.server;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
function getHeaders(projectId) {
|
|
4
|
+
return {
|
|
5
|
+
Authorization: `Bearer ${config.token}`,
|
|
6
|
+
'Content-Type': 'application/json',
|
|
7
|
+
'X-Sanity-Scope-Type': 'project',
|
|
8
|
+
'X-Sanity-Scope-Id': projectId,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
async function listStacks({ projectId }) {
|
|
12
|
+
const path = `${blueprints}/vX/blueprints/stacks`;
|
|
13
|
+
const response = await fetch(path, {
|
|
9
14
|
method: 'GET',
|
|
10
|
-
headers:
|
|
15
|
+
headers: getHeaders(projectId),
|
|
11
16
|
});
|
|
12
17
|
const stacks = await response.json();
|
|
13
18
|
return {
|
|
@@ -16,10 +21,39 @@ async function listStacks() {
|
|
|
16
21
|
stacks,
|
|
17
22
|
};
|
|
18
23
|
}
|
|
19
|
-
async function
|
|
20
|
-
const
|
|
24
|
+
async function getStackByName({ name, projectId }) {
|
|
25
|
+
const { ok, stacks, error } = await listStacks({ projectId });
|
|
26
|
+
if (!ok || !stacks) {
|
|
27
|
+
return {
|
|
28
|
+
ok: false,
|
|
29
|
+
error: error || 'Failed to retrieve stacks',
|
|
30
|
+
stack: null,
|
|
31
|
+
stackId: null,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const foundStack = stacks.find((stack) => stack.name === name);
|
|
35
|
+
if (!foundStack) {
|
|
36
|
+
return {
|
|
37
|
+
ok: true,
|
|
38
|
+
error: null,
|
|
39
|
+
stack: null,
|
|
40
|
+
stackId: null,
|
|
41
|
+
availableStacks: stacks.map((s) => s.name),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const stackResult = await getStack({ stackId: foundStack.id, projectId });
|
|
45
|
+
return {
|
|
46
|
+
ok: stackResult.ok,
|
|
47
|
+
error: stackResult.error,
|
|
48
|
+
stack: stackResult.stack,
|
|
49
|
+
stackId: foundStack.id,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
async function getStack({ stackId, projectId }) {
|
|
53
|
+
const path = `${blueprints}/vX/blueprints/stacks/${stackId}`;
|
|
54
|
+
const response = await fetch(path, {
|
|
21
55
|
method: 'GET',
|
|
22
|
-
headers:
|
|
56
|
+
headers: getHeaders(projectId),
|
|
23
57
|
});
|
|
24
58
|
const stack = await response.json();
|
|
25
59
|
return {
|
|
@@ -28,10 +62,11 @@ async function getStack(stackId) {
|
|
|
28
62
|
stack,
|
|
29
63
|
};
|
|
30
64
|
}
|
|
31
|
-
async function createStack(blueprint) {
|
|
32
|
-
const
|
|
65
|
+
async function createStack({ blueprint, projectId }) {
|
|
66
|
+
const path = `${blueprints}/vX/blueprints/stacks`;
|
|
67
|
+
const response = await fetch(path, {
|
|
33
68
|
method: 'POST',
|
|
34
|
-
headers:
|
|
69
|
+
headers: getHeaders(projectId),
|
|
35
70
|
body: JSON.stringify(blueprint),
|
|
36
71
|
});
|
|
37
72
|
const stack = await response.json();
|
|
@@ -41,10 +76,11 @@ async function createStack(blueprint) {
|
|
|
41
76
|
stack,
|
|
42
77
|
};
|
|
43
78
|
}
|
|
44
|
-
async function updateStack(stackId, blueprint) {
|
|
45
|
-
const
|
|
79
|
+
async function updateStack({ stackId, blueprint, projectId, }) {
|
|
80
|
+
const path = `${blueprints}/vX/blueprints/stacks/${stackId}`;
|
|
81
|
+
const response = await fetch(path, {
|
|
46
82
|
method: 'PUT',
|
|
47
|
-
headers:
|
|
83
|
+
headers: getHeaders(projectId),
|
|
48
84
|
body: JSON.stringify(blueprint),
|
|
49
85
|
});
|
|
50
86
|
const stack = await response.json();
|
|
@@ -54,4 +90,4 @@ async function updateStack(stackId, blueprint) {
|
|
|
54
90
|
stack,
|
|
55
91
|
};
|
|
56
92
|
}
|
|
57
|
-
export { listStacks, getStack, createStack, updateStack };
|
|
93
|
+
export { listStacks, getStack, createStack, updateStack, getStackByName };
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { BlueprintResource } from '../../utils/types.js';
|
|
2
|
-
export declare function stashAsset(resource
|
|
2
|
+
export declare function stashAsset({ resource, projectId, }: {
|
|
3
|
+
resource: BlueprintResource;
|
|
4
|
+
projectId: string;
|
|
5
|
+
}): Promise<{
|
|
3
6
|
success: boolean;
|
|
4
7
|
assetId: any;
|
|
5
8
|
error?: undefined;
|
|
@@ -4,7 +4,15 @@ import { cwd } from 'node:process';
|
|
|
4
4
|
import JSZip from 'jszip';
|
|
5
5
|
import config from '../../config.js';
|
|
6
6
|
const { blueprints } = config.server;
|
|
7
|
-
|
|
7
|
+
function getHeaders(projectId) {
|
|
8
|
+
return {
|
|
9
|
+
Authorization: `Bearer ${config.token}`,
|
|
10
|
+
'Content-Type': 'application/json',
|
|
11
|
+
'X-Sanity-Scope-Type': 'project',
|
|
12
|
+
'X-Sanity-Scope-Id': projectId,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export async function stashAsset({ resource, projectId, }) {
|
|
8
16
|
try {
|
|
9
17
|
// Read the function source
|
|
10
18
|
const source = await fs.readFileSync(path.join(cwd(), resource.src), 'utf8');
|
|
@@ -14,19 +22,16 @@ export async function stashAsset(resource) {
|
|
|
14
22
|
const zipBuffer = await zip.generateAsync({ type: 'nodebuffer' });
|
|
15
23
|
const base64Zip = zipBuffer.toString('base64');
|
|
16
24
|
// Stash the asset
|
|
17
|
-
const assetResponse = await fetch(`${blueprints}/assets/stash`, {
|
|
25
|
+
const assetResponse = await fetch(`${blueprints}/vX/blueprints/assets/stash`, {
|
|
18
26
|
method: 'POST',
|
|
19
|
-
headers:
|
|
20
|
-
Authorization: `Bearer ${config.token}`,
|
|
21
|
-
'Content-Type': 'application/json',
|
|
22
|
-
},
|
|
27
|
+
headers: getHeaders(projectId),
|
|
23
28
|
body: JSON.stringify({
|
|
24
29
|
file: base64Zip,
|
|
25
30
|
filename: `${resource.name}.zip`,
|
|
26
31
|
}),
|
|
27
32
|
});
|
|
28
33
|
const assetJson = await assetResponse.json();
|
|
29
|
-
console.debug('ASSET RESPONSE:', assetJson)
|
|
34
|
+
// console.debug('ASSET RESPONSE:', assetJson)
|
|
30
35
|
if (assetResponse.ok) {
|
|
31
36
|
return { success: true, assetId: assetJson.id };
|
|
32
37
|
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import config from '../../config.js';
|
|
2
2
|
import buildPayload from '../../utils/build-payload.js';
|
|
3
3
|
const { functions } = config.server;
|
|
4
|
+
function getHeaders() {
|
|
5
|
+
return {
|
|
6
|
+
Authorization: `Bearer ${config.token}`,
|
|
7
|
+
'Content-Type': 'application/json',
|
|
8
|
+
Accept: 'application/json',
|
|
9
|
+
};
|
|
10
|
+
}
|
|
4
11
|
export async function invoke(id, options) {
|
|
5
12
|
const payload = buildPayload(options);
|
|
6
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
7
13
|
const response = await fetch(`${functions}/vX/functions/${id}/invoke`, {
|
|
8
14
|
body: JSON.stringify({ data: payload }),
|
|
9
|
-
headers:
|
|
10
|
-
Accept: 'application/json',
|
|
11
|
-
'Content-Type': 'application/json',
|
|
12
|
-
},
|
|
15
|
+
headers: getHeaders(),
|
|
13
16
|
method: 'POST',
|
|
14
17
|
});
|
|
15
18
|
const json = await response.json();
|
|
@@ -1,70 +1,64 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
import
|
|
3
|
-
import { createStack,
|
|
2
|
+
import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
|
|
3
|
+
import { createStack, getStackByName, updateStack } from '../../actions/blueprints/stacks.js';
|
|
4
4
|
import { stashAsset } from '../../actions/blueprints/stash-asset.js';
|
|
5
|
+
import { green, red, yellow } from '../../utils/display/colors.js';
|
|
5
6
|
import Spinner from '../../utils/spinner.js';
|
|
6
7
|
export default class Deploy extends Command {
|
|
7
8
|
static description = 'Deploy a Blueprint';
|
|
8
9
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
9
10
|
async run() {
|
|
10
|
-
const {
|
|
11
|
+
const { document } = readBlueprintOnDisk();
|
|
12
|
+
const { displayName, name, projectId, resources } = document;
|
|
11
13
|
const s = new Spinner();
|
|
12
|
-
s.start('Deploying blueprint...');
|
|
13
14
|
const functionResources = resources.filter((r) => r.kind === 'function');
|
|
14
15
|
// First stash all function assets
|
|
15
16
|
if (functionResources.length > 0) {
|
|
16
|
-
s.stop('Preparing functions...');
|
|
17
17
|
for (const resource of functionResources) {
|
|
18
18
|
const fnSpinner = new Spinner();
|
|
19
19
|
fnSpinner.start(`Processing ${resource.name}...`);
|
|
20
|
-
const result = await stashAsset(resource);
|
|
20
|
+
const result = await stashAsset({ resource, projectId });
|
|
21
21
|
if (result.success) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const src = resource.src;
|
|
23
|
+
resource.src = result.assetId; // TODO: properly reference asset - for now, the API expects the assetId
|
|
24
|
+
fnSpinner.stop(`${green('✓')} ${resource.name} <${yellow(result.assetId)}>`);
|
|
25
|
+
this.log(` Source: ${src}`);
|
|
25
26
|
}
|
|
26
27
|
else {
|
|
27
|
-
fnSpinner.stop(
|
|
28
|
+
fnSpinner.stop(`${red('✗')} Failed to process ${resource.name}`);
|
|
28
29
|
this.log(` Error: ${result.error}`);
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
|
-
this.log('│');
|
|
33
|
-
s.start('Looking for existing stack...');
|
|
34
33
|
}
|
|
35
|
-
|
|
36
|
-
const { ok
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
s.start('Looking for existing stack...');
|
|
35
|
+
const { ok, error, stackId: existingStackId, } = await getStackByName({
|
|
36
|
+
name,
|
|
37
|
+
projectId,
|
|
38
|
+
});
|
|
39
|
+
if (!ok) {
|
|
40
|
+
s.stop(`${red('Failed')} to list stacks`);
|
|
41
|
+
this.log(`Error: ${error || 'Unknown error'}`);
|
|
40
42
|
return;
|
|
41
43
|
}
|
|
42
|
-
const existingStack = stacks.find((st) => st.name === name);
|
|
43
44
|
const blueprint = {
|
|
44
45
|
name,
|
|
45
46
|
projectId,
|
|
46
|
-
document: {
|
|
47
|
-
resources: resources.map((r) => {
|
|
48
|
-
if (r.kind === 'function') {
|
|
49
|
-
return { ...r, src: r.assetId };
|
|
50
|
-
}
|
|
51
|
-
return r;
|
|
52
|
-
}),
|
|
53
|
-
},
|
|
47
|
+
document: { resources },
|
|
54
48
|
};
|
|
55
49
|
this.debug('BLUEPRINT DOCUMENT:', blueprint);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
: await createStack(blueprint);
|
|
50
|
+
const { ok: deployOk, stack, error: deployError, } = existingStackId
|
|
51
|
+
? await updateStack({ stackId: existingStackId, blueprint, projectId })
|
|
52
|
+
: await createStack({ blueprint, projectId });
|
|
60
53
|
this.debug('STACK RESPONSE:', stack);
|
|
61
|
-
if (
|
|
62
|
-
s.stop(
|
|
63
|
-
this.log('
|
|
54
|
+
if (deployOk) {
|
|
55
|
+
s.stop(`${green('Success!')} Stack ${existingStackId ? 'updated' : 'created'} <${yellow(stack.id)}>`);
|
|
56
|
+
this.log('\nUse `sanity blueprints info` to check deployment status');
|
|
64
57
|
}
|
|
65
58
|
else {
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
this.debug('STACK ERROR RESPONSE:', stack);
|
|
60
|
+
s.stop(`${red('Failed')} to ${existingStackId ? 'update' : 'create'} stack`);
|
|
61
|
+
this.log(`Error: ${deployError || 'Unknown error'}`);
|
|
68
62
|
}
|
|
69
63
|
}
|
|
70
64
|
}
|