@sanity/runtime-cli 1.6.0 → 1.8.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 +184 -122
- package/dist/actions/blueprints/blueprint.d.ts +24 -7
- package/dist/actions/blueprints/blueprint.js +65 -44
- package/dist/actions/blueprints/logs.d.ts +3 -0
- package/dist/actions/blueprints/logs.js +24 -0
- package/dist/actions/blueprints/projects.d.ts +5 -0
- package/dist/actions/blueprints/projects.js +21 -0
- package/dist/actions/blueprints/resources.d.ts +13 -0
- package/dist/actions/blueprints/resources.js +37 -0
- package/dist/actions/blueprints/stacks.js +1 -1
- package/dist/commands/blueprints/add.d.ts +10 -0
- package/dist/commands/blueprints/add.js +67 -0
- package/dist/commands/blueprints/config.d.ts +9 -0
- package/dist/commands/blueprints/config.js +72 -0
- package/dist/commands/blueprints/deploy.js +13 -12
- package/dist/commands/blueprints/info.js +1 -1
- package/dist/commands/blueprints/init.d.ts +6 -0
- package/dist/commands/blueprints/init.js +56 -0
- package/dist/commands/blueprints/logs.js +24 -64
- package/dist/commands/blueprints/stacks.js +3 -15
- package/dist/commands/functions/invoke.d.ts +1 -1
- package/dist/commands/functions/invoke.js +9 -7
- package/dist/commands/functions/logs.d.ts +1 -1
- package/dist/commands/functions/logs.js +21 -12
- package/dist/commands/functions/test.d.ts +1 -1
- package/dist/commands/functions/test.js +25 -16
- package/dist/config.d.ts +1 -0
- package/dist/config.js +12 -5
- package/dist/server/app.js +2 -2
- package/dist/server/static/api.js +43 -38
- package/dist/server/static/components/api-base.js +7 -6
- package/dist/server/static/components/function-list.js +48 -44
- package/dist/server/static/components/network-spinner.js +7 -6
- package/dist/server/static/components/payload-panel.js +36 -32
- package/dist/server/static/components/response-panel.js +64 -50
- package/dist/server/static/vendor/vendor.bundle.js +1029 -913
- package/dist/utils/display/blueprints-formatting.d.ts +3 -1
- package/dist/utils/display/blueprints-formatting.js +27 -2
- package/dist/utils/display/logs-formatting.d.ts +5 -0
- package/dist/utils/display/logs-formatting.js +50 -0
- package/dist/utils/find-function.d.ts +2 -0
- package/dist/utils/find-function.js +6 -0
- package/dist/utils/types.d.ts +0 -1
- package/oclif.manifest.json +102 -13
- package/package.json +10 -10
- package/dist/server/static/static/api.js +0 -53
- package/dist/server/static/static/components/api-base.js +0 -10
- package/dist/server/static/static/components/function-list.js +0 -54
- package/dist/server/static/static/components/network-spinner.js +0 -71
- package/dist/server/static/static/components/payload-panel.js +0 -45
- package/dist/server/static/static/components/response-panel.js +0 -83
- package/dist/server/static/static/vendor/vendor.bundle.js +0 -26879
- package/dist/utils/spinner.d.ts +0 -9
- package/dist/utils/spinner.js +0 -25
- /package/dist/server/static/{static/components → components}/app.css +0 -0
- /package/dist/server/static/{static/index.html → index.html} +0 -0
- /package/dist/server/static/{static/sanity-logo-sm.svg → sanity-logo-sm.svg} +0 -0
|
@@ -13,7 +13,11 @@ const SUPPORTED_FILE_NAMES_IN_PRIORITY_ORDER = [
|
|
|
13
13
|
'blueprint.cjs',
|
|
14
14
|
'blueprint.ts',
|
|
15
15
|
];
|
|
16
|
-
|
|
16
|
+
export const DEFAULT_BLUEPRINT_CONTENT = {
|
|
17
|
+
blueprintVersion: '2024-10-01',
|
|
18
|
+
resources: [],
|
|
19
|
+
};
|
|
20
|
+
export function findBlueprintFile(blueprintPath) {
|
|
17
21
|
if (blueprintPath) {
|
|
18
22
|
if (existsSync(blueprintPath)) {
|
|
19
23
|
return { path: blueprintPath, fileName: blueprintPath, extension: extname(blueprintPath) };
|
|
@@ -28,31 +32,6 @@ function findBlueprintFile(blueprintPath) {
|
|
|
28
32
|
}
|
|
29
33
|
return null;
|
|
30
34
|
}
|
|
31
|
-
function readConfigFile(blueprintPath) {
|
|
32
|
-
if (blueprintPath) {
|
|
33
|
-
const blueprintDir = dirname(blueprintPath);
|
|
34
|
-
const configPath = join(blueprintDir, '.blueprint', 'config.json');
|
|
35
|
-
if (existsSync(configPath)) {
|
|
36
|
-
try {
|
|
37
|
-
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
38
|
-
return config || null;
|
|
39
|
-
}
|
|
40
|
-
catch (err) {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
const configFilePath = join(cwd(), '.blueprint', 'config.json');
|
|
46
|
-
if (!existsSync(configFilePath))
|
|
47
|
-
return null;
|
|
48
|
-
try {
|
|
49
|
-
const config = JSON.parse(readFileSync(configFilePath, 'utf8'));
|
|
50
|
-
return config || null;
|
|
51
|
-
}
|
|
52
|
-
catch (err) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
35
|
export async function readBlueprintOnDisk({ blueprintPath, getStack, } = {}) {
|
|
57
36
|
try {
|
|
58
37
|
const blueprintFile = findBlueprintFile(blueprintPath);
|
|
@@ -160,25 +139,51 @@ export async function readBlueprintOnDisk({ blueprintPath, getStack, } = {}) {
|
|
|
160
139
|
throw Error(`Unable to parse Blueprint file: ${err}`);
|
|
161
140
|
}
|
|
162
141
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
142
|
+
export function writeBlueprintToDisk({ path, fileType, content = DEFAULT_BLUEPRINT_CONTENT, }) {
|
|
143
|
+
let blueprintContent;
|
|
144
|
+
switch (fileType) {
|
|
145
|
+
case 'json': {
|
|
146
|
+
blueprintContent = JSON.stringify(content, null, 2);
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
case 'js':
|
|
150
|
+
case 'ts': {
|
|
151
|
+
blueprintContent = `export default function() {
|
|
152
|
+
return ${JSON.stringify(content)}
|
|
153
|
+
}`;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
writeFileSync(path, blueprintContent);
|
|
158
|
+
return blueprintContent;
|
|
175
159
|
}
|
|
176
|
-
export function
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
160
|
+
export function readConfigFile(blueprintPath) {
|
|
161
|
+
if (blueprintPath) {
|
|
162
|
+
const blueprintDir = dirname(blueprintPath);
|
|
163
|
+
const configPath = join(blueprintDir, '.blueprint', 'config.json');
|
|
164
|
+
if (existsSync(configPath)) {
|
|
165
|
+
try {
|
|
166
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
167
|
+
return config || null;
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const configFilePath = join(cwd(), '.blueprint', 'config.json');
|
|
175
|
+
if (!existsSync(configFilePath))
|
|
176
|
+
return null;
|
|
177
|
+
try {
|
|
178
|
+
const config = JSON.parse(readFileSync(configFilePath, 'utf8'));
|
|
179
|
+
return config || null;
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
export function writeConfigFile({ blueprintPath, projectId, stackId, }) {
|
|
186
|
+
const blueprintDir = blueprintPath ? dirname(blueprintPath) : cwd();
|
|
182
187
|
const configDir = join(blueprintDir, '.blueprint');
|
|
183
188
|
const configPath = join(configDir, 'config.json');
|
|
184
189
|
if (!existsSync(configDir)) {
|
|
@@ -197,3 +202,19 @@ export function writeBlueprintConfig({ blueprintPath, projectId, stackId, }) {
|
|
|
197
202
|
config.stackId = stackId;
|
|
198
203
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
199
204
|
}
|
|
205
|
+
export function addResourceToBlueprint({ blueprintPath, resource, }) {
|
|
206
|
+
const blueprintFile = findBlueprintFile(blueprintPath);
|
|
207
|
+
if (!blueprintFile)
|
|
208
|
+
throw Error('Could not find Blueprint file');
|
|
209
|
+
const { path, extension } = blueprintFile;
|
|
210
|
+
// modify .json files directly
|
|
211
|
+
if (extension === '.json') {
|
|
212
|
+
const blueprintString = readFileSync(path, 'utf8').toString();
|
|
213
|
+
const blueprint = JSON.parse(blueprintString);
|
|
214
|
+
blueprint.resources = blueprint.resources || [];
|
|
215
|
+
blueprint.resources.push(resource);
|
|
216
|
+
writeFileSync(path, JSON.stringify(blueprint, null, 2));
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
return resource;
|
|
220
|
+
}
|
|
@@ -5,4 +5,7 @@ export declare function getLogs(stackId: string, projectId: string, token: strin
|
|
|
5
5
|
ok: boolean;
|
|
6
6
|
error: string | null;
|
|
7
7
|
}>;
|
|
8
|
+
export declare function findNewestLogTimestamp(logs: BlueprintLog[]): number;
|
|
9
|
+
export declare function isNewerLog(log: BlueprintLog, timestamp: number): boolean;
|
|
10
|
+
export declare function getRecentLogs(logs: BlueprintLog[], limit?: number): BlueprintLog[];
|
|
8
11
|
export declare function streamLogs(stackId: string, projectId: string, token: string, onLog: (log: BlueprintLog) => void, onOpen: () => void, onError: (error: string) => void): () => void;
|
|
@@ -22,6 +22,30 @@ export async function getLogs(stackId, projectId, token) {
|
|
|
22
22
|
logs: response.ok ? result : [],
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
+
// Process logs to find the newest timestamp
|
|
26
|
+
export function findNewestLogTimestamp(logs) {
|
|
27
|
+
let newestTimestamp = 0;
|
|
28
|
+
if (logs.length > 0) {
|
|
29
|
+
for (const log of logs) {
|
|
30
|
+
const timestamp = new Date(log.timestamp).getTime();
|
|
31
|
+
if (timestamp > newestTimestamp) {
|
|
32
|
+
newestTimestamp = timestamp;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return newestTimestamp;
|
|
37
|
+
}
|
|
38
|
+
// Check if a log is newer than a given timestamp
|
|
39
|
+
export function isNewerLog(log, timestamp) {
|
|
40
|
+
const logTimestamp = new Date(log.timestamp).getTime();
|
|
41
|
+
return logTimestamp > timestamp;
|
|
42
|
+
}
|
|
43
|
+
// Get most recent logs, up to a limit
|
|
44
|
+
export function getRecentLogs(logs, limit = 10) {
|
|
45
|
+
const sortedLogs = [...logs].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
46
|
+
return sortedLogs.slice(-limit);
|
|
47
|
+
}
|
|
48
|
+
// Create streaming logs connection
|
|
25
49
|
export function streamLogs(stackId, projectId, token, onLog, onOpen, onError) {
|
|
26
50
|
const url = new URL(`${logsUrl}/stream`);
|
|
27
51
|
url.searchParams.append('stackId', stackId);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import config from '../../config.js';
|
|
2
|
+
const { apiUrl, token } = config;
|
|
3
|
+
function getHeaders() {
|
|
4
|
+
return {
|
|
5
|
+
Authorization: `Bearer ${token}`,
|
|
6
|
+
'Content-Type': 'application/json',
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
const projectsUrl = `${apiUrl.toString()}v2021-06-07/projects`;
|
|
10
|
+
export async function listProjects() {
|
|
11
|
+
const response = await fetch(projectsUrl, {
|
|
12
|
+
method: 'GET',
|
|
13
|
+
headers: getHeaders(),
|
|
14
|
+
});
|
|
15
|
+
const projects = await response.json();
|
|
16
|
+
return {
|
|
17
|
+
ok: response.ok,
|
|
18
|
+
error: response.ok ? null : projects.error?.message,
|
|
19
|
+
projects,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface FunctionResourceOptions {
|
|
2
|
+
name: string;
|
|
3
|
+
type: string;
|
|
4
|
+
displayName?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new function resource file and adds it to the blueprint
|
|
8
|
+
*/
|
|
9
|
+
export declare function createFunctionResource(options: FunctionResourceOptions): {
|
|
10
|
+
filePath: string;
|
|
11
|
+
resourceAdded: boolean;
|
|
12
|
+
resource: Record<string, unknown>;
|
|
13
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { cwd } from 'node:process';
|
|
5
|
+
import { addResourceToBlueprint } from './blueprint.js';
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new function resource file and adds it to the blueprint
|
|
8
|
+
*/
|
|
9
|
+
export function createFunctionResource(options) {
|
|
10
|
+
const { name, type, displayName = name } = options;
|
|
11
|
+
// Ensure functions directory exists
|
|
12
|
+
const functionsDir = join(cwd(), 'functions');
|
|
13
|
+
if (!existsSync(functionsDir)) {
|
|
14
|
+
mkdirSync(functionsDir, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
// Create function file with default template
|
|
17
|
+
const functionPath = join(functionsDir, `${name}.js`);
|
|
18
|
+
const functionContent = `export async function handler (event) {
|
|
19
|
+
console.log(event)
|
|
20
|
+
return {event}
|
|
21
|
+
}`;
|
|
22
|
+
writeFileSync(functionPath, functionContent);
|
|
23
|
+
// Create resource definition
|
|
24
|
+
const resourceJson = {
|
|
25
|
+
displayName,
|
|
26
|
+
name,
|
|
27
|
+
type: `sanity.function.${type}`,
|
|
28
|
+
src: `functions/${name}.js`,
|
|
29
|
+
};
|
|
30
|
+
// Add to blueprint or return for manual addition
|
|
31
|
+
const resource = addResourceToBlueprint({ resource: resourceJson });
|
|
32
|
+
return {
|
|
33
|
+
filePath: functionPath,
|
|
34
|
+
resourceAdded: !resource, // If resource is null, it was added to blueprint
|
|
35
|
+
resource: resource || resourceJson,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Add extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
type: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
private addFunction;
|
|
10
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Args, Command } from '@oclif/core';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { highlight } from 'tinyhighlight/picocolors';
|
|
4
|
+
import { findBlueprintFile } from '../../actions/blueprints/blueprint.js';
|
|
5
|
+
import { createFunctionResource } from '../../actions/blueprints/resources.js';
|
|
6
|
+
export default class Add extends Command {
|
|
7
|
+
static description = 'Add a resource to a Blueprint';
|
|
8
|
+
static examples = ['<%= config.bin %> <%= command.id %> function'];
|
|
9
|
+
static args = {
|
|
10
|
+
type: Args.string({
|
|
11
|
+
description: 'Type of resource to add (e.g. function)',
|
|
12
|
+
options: ['function'],
|
|
13
|
+
required: true,
|
|
14
|
+
}),
|
|
15
|
+
};
|
|
16
|
+
async run() {
|
|
17
|
+
const { args } = await this.parse(Add);
|
|
18
|
+
const existingBlueprint = findBlueprintFile(undefined);
|
|
19
|
+
if (!existingBlueprint) {
|
|
20
|
+
this.error('No blueprint file found. Run `sanity blueprints init` first.');
|
|
21
|
+
}
|
|
22
|
+
if (args.type === 'function') {
|
|
23
|
+
await this.addFunction();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
this.error(`Unsupported resource type: ${args.type}`);
|
|
27
|
+
}
|
|
28
|
+
async addFunction() {
|
|
29
|
+
const { functionName } = await inquirer.prompt([
|
|
30
|
+
{
|
|
31
|
+
type: 'input',
|
|
32
|
+
name: 'functionName',
|
|
33
|
+
message: 'Enter function name:',
|
|
34
|
+
validate: (input) => input.length > 0 || 'Function name is required',
|
|
35
|
+
},
|
|
36
|
+
]);
|
|
37
|
+
const { functionType } = await inquirer.prompt([
|
|
38
|
+
{
|
|
39
|
+
type: 'list',
|
|
40
|
+
name: 'functionType',
|
|
41
|
+
message: 'Choose function type:',
|
|
42
|
+
choices: [
|
|
43
|
+
{ name: 'Document Mutation', value: 'document-mutation' },
|
|
44
|
+
{ name: 'Document Publish', value: 'document-publish' },
|
|
45
|
+
{ name: 'Document Update', value: 'document-update' },
|
|
46
|
+
{ name: 'Document Delete', value: 'document-delete' },
|
|
47
|
+
],
|
|
48
|
+
default: 'document-mutation',
|
|
49
|
+
},
|
|
50
|
+
]);
|
|
51
|
+
const { filePath, resourceAdded, resource } = createFunctionResource({
|
|
52
|
+
name: functionName,
|
|
53
|
+
type: functionType,
|
|
54
|
+
displayName: functionName,
|
|
55
|
+
});
|
|
56
|
+
this.log(`\nCreated function: ${filePath}`);
|
|
57
|
+
if (!resourceAdded) {
|
|
58
|
+
// print the resource JSON for manual addition
|
|
59
|
+
this.log('\nAdd this Function resource to your blueprint:');
|
|
60
|
+
this.log(highlight(JSON.stringify(resource, null, 2)));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// added to blueprint.json
|
|
64
|
+
this.log('Function resource added to blueprint');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { highlight } from 'tinyhighlight/picocolors';
|
|
4
|
+
import { readConfigFile, writeConfigFile } from '../../actions/blueprints/blueprint.js';
|
|
5
|
+
import { listProjects } from '../../actions/blueprints/projects.js';
|
|
6
|
+
import { listStacks } from '../../actions/blueprints/stacks.js';
|
|
7
|
+
export default class Config extends Command {
|
|
8
|
+
static description = 'View or edit Blueprint configuration';
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> <%= command.id %>',
|
|
11
|
+
'<%= config.bin %> <%= command.id %> --edit',
|
|
12
|
+
];
|
|
13
|
+
static flags = {
|
|
14
|
+
edit: Flags.boolean({
|
|
15
|
+
description: 'Edit the configuration',
|
|
16
|
+
default: false,
|
|
17
|
+
}),
|
|
18
|
+
};
|
|
19
|
+
async run() {
|
|
20
|
+
const { flags } = await this.parse(Config);
|
|
21
|
+
const config = readConfigFile();
|
|
22
|
+
if (!config) {
|
|
23
|
+
this.error('No configuration found. Run `sanity blueprints init` first.');
|
|
24
|
+
}
|
|
25
|
+
this.log('\nCurrent configuration:');
|
|
26
|
+
this.log(highlight(JSON.stringify(config, null, 2)));
|
|
27
|
+
if (!flags.edit)
|
|
28
|
+
return;
|
|
29
|
+
const { ok, projects, error } = await listProjects();
|
|
30
|
+
if (!ok)
|
|
31
|
+
this.error(error);
|
|
32
|
+
if (projects.length === 0) {
|
|
33
|
+
this.error('No projects found. Please create a project in Sanity.io first.');
|
|
34
|
+
}
|
|
35
|
+
const projectChoices = projects.map(({ displayName, id }) => ({
|
|
36
|
+
name: `${displayName} <${id}>`,
|
|
37
|
+
value: id,
|
|
38
|
+
}));
|
|
39
|
+
const { projectId } = await inquirer.prompt([
|
|
40
|
+
{
|
|
41
|
+
type: 'list',
|
|
42
|
+
name: 'projectId',
|
|
43
|
+
message: 'Select your Sanity project:',
|
|
44
|
+
choices: projectChoices,
|
|
45
|
+
default: config.projectId,
|
|
46
|
+
},
|
|
47
|
+
]);
|
|
48
|
+
// get stacks for selected project
|
|
49
|
+
const { ok: stacksOk, stacks, error: stacksError } = await listStacks({ projectId });
|
|
50
|
+
if (!stacksOk)
|
|
51
|
+
this.error(stacksError);
|
|
52
|
+
let stackId;
|
|
53
|
+
if (stacks.length > 0) {
|
|
54
|
+
const stackChoices = stacks.map(({ name, id }) => ({
|
|
55
|
+
name: `${name} <${id}>`,
|
|
56
|
+
value: id,
|
|
57
|
+
}));
|
|
58
|
+
const { stackId: selectedStackId } = await inquirer.prompt([
|
|
59
|
+
{
|
|
60
|
+
type: 'list',
|
|
61
|
+
name: 'stackId',
|
|
62
|
+
message: 'Select a stack:',
|
|
63
|
+
choices: stackChoices,
|
|
64
|
+
default: config.stackId,
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
stackId = selectedStackId;
|
|
68
|
+
}
|
|
69
|
+
writeConfigFile({ projectId, stackId });
|
|
70
|
+
this.log('\nConfiguration updated successfully.');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
|
-
import {
|
|
3
|
+
import { Spinner } from 'picospinner';
|
|
4
|
+
import { readBlueprintOnDisk, writeConfigFile } from '../../actions/blueprints/blueprint.js';
|
|
4
5
|
import { createStack, updateStack } from '../../actions/blueprints/stacks.js';
|
|
5
6
|
import { stashAsset } from '../../actions/blueprints/stash-asset.js';
|
|
6
7
|
import { bold, green, red, yellow } from '../../utils/display/colors.js';
|
|
7
|
-
import Spinner from '../../utils/spinner.js';
|
|
8
8
|
export default class Deploy extends Command {
|
|
9
9
|
static description = 'Deploy a Blueprint';
|
|
10
10
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
@@ -46,23 +46,22 @@ export default class Deploy extends Command {
|
|
|
46
46
|
}
|
|
47
47
|
if (!name)
|
|
48
48
|
this.error('Stack name is required');
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const functionResources = validResources.filter((r) => r.type?.startsWith('sanity.function.'));
|
|
49
|
+
const validResources = resources.filter((r) => r.type);
|
|
50
|
+
const functionResources = validResources.filter((r) => r.type.startsWith('sanity.function.'));
|
|
52
51
|
// First stash all function assets
|
|
53
52
|
if (functionResources.length > 0) {
|
|
54
53
|
for (const resource of functionResources) {
|
|
55
|
-
const fnSpinner = new Spinner();
|
|
56
|
-
fnSpinner.start(
|
|
54
|
+
const fnSpinner = new Spinner(`Processing ${resource.name}...`);
|
|
55
|
+
fnSpinner.start();
|
|
57
56
|
const result = await stashAsset({ resource, projectId });
|
|
58
57
|
if (result.success) {
|
|
59
58
|
const src = resource.src;
|
|
60
59
|
resource.src = result.assetId; // TODO: properly reference asset - for now, the API expects the assetId
|
|
61
|
-
fnSpinner.
|
|
60
|
+
fnSpinner.succeed(`${resource.name} <${yellow(result.assetId)}>`);
|
|
62
61
|
this.log(` Source: ${src}`);
|
|
63
62
|
}
|
|
64
63
|
else {
|
|
65
|
-
fnSpinner.
|
|
64
|
+
fnSpinner.fail(`Failed to process ${resource.name}`);
|
|
66
65
|
this.log(` Error: ${result.error}`);
|
|
67
66
|
return;
|
|
68
67
|
}
|
|
@@ -74,13 +73,15 @@ export default class Deploy extends Command {
|
|
|
74
73
|
document: { resources: validResources },
|
|
75
74
|
};
|
|
76
75
|
this.debug('BLUEPRINT DOCUMENT:', blueprint);
|
|
76
|
+
const spinner = new Spinner('Deploying stack...');
|
|
77
|
+
spinner.start();
|
|
77
78
|
const { ok: deployOk, stack, error: deployError, } = deployedStack
|
|
78
79
|
? await updateStack({ stackId: deployedStack.id, blueprint, projectId })
|
|
79
80
|
: await createStack({ blueprint, projectId });
|
|
80
81
|
this.debug('STACK RESPONSE:', stack);
|
|
81
82
|
if (deployOk) {
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
spinner.succeed(`${green('Success!')} Stack "${bold(stack.name)}" ${deployedStack ? 'updated' : 'created'} <${yellow(stack.id)}>`);
|
|
84
|
+
writeConfigFile({
|
|
84
85
|
projectId,
|
|
85
86
|
stackId: stack.id,
|
|
86
87
|
});
|
|
@@ -88,7 +89,7 @@ export default class Deploy extends Command {
|
|
|
88
89
|
}
|
|
89
90
|
else {
|
|
90
91
|
this.debug('STACK ERROR RESPONSE:', stack);
|
|
91
|
-
|
|
92
|
+
spinner.fail(`${red('Failed')} to ${deployedStack ? 'update' : 'create'} stack`);
|
|
92
93
|
this.log(`Error: ${deployError || JSON.stringify(stack, null, 2) || 'Unknown error'}`);
|
|
93
94
|
}
|
|
94
95
|
}
|
|
@@ -59,7 +59,7 @@ export default class Info extends Command {
|
|
|
59
59
|
this.log(` Status: ${operationColor(status)}`);
|
|
60
60
|
}
|
|
61
61
|
if (operation.createdAt) {
|
|
62
|
-
this.log(` Started
|
|
62
|
+
this.log(` Started: ${formatDate(operation.createdAt)}`);
|
|
63
63
|
}
|
|
64
64
|
if (operation.status === 'COMPLETED' && operation.completedAt && operation.createdAt) {
|
|
65
65
|
this.log(` Completed: ${formatDate(operation.completedAt)}`);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { cwd } from 'node:process';
|
|
3
|
+
import { Command } from '@oclif/core';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import { findBlueprintFile, writeBlueprintToDisk, writeConfigFile, } from '../../actions/blueprints/blueprint.js';
|
|
6
|
+
import { listProjects } from '../../actions/blueprints/projects.js';
|
|
7
|
+
export default class Init extends Command {
|
|
8
|
+
static description = 'Initialize a new Blueprint';
|
|
9
|
+
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
10
|
+
async run() {
|
|
11
|
+
const existingBlueprint = findBlueprintFile(undefined);
|
|
12
|
+
if (existingBlueprint) {
|
|
13
|
+
this.error(`A blueprint file already exists: ${existingBlueprint.fileName}`);
|
|
14
|
+
}
|
|
15
|
+
const { blueprintExtension } = await inquirer.prompt([
|
|
16
|
+
{
|
|
17
|
+
type: 'list',
|
|
18
|
+
name: 'blueprintExtension',
|
|
19
|
+
message: 'Choose a blueprint type:',
|
|
20
|
+
choices: [
|
|
21
|
+
{ name: 'JSON (Recommended)', value: 'json' },
|
|
22
|
+
{ name: 'JavaScript (Beta)', value: 'js' },
|
|
23
|
+
{ name: 'TypeScript (Alpha)', value: 'ts' },
|
|
24
|
+
],
|
|
25
|
+
default: 'json',
|
|
26
|
+
},
|
|
27
|
+
]);
|
|
28
|
+
const { ok, projects, error } = await listProjects();
|
|
29
|
+
if (!ok) {
|
|
30
|
+
this.error(error);
|
|
31
|
+
}
|
|
32
|
+
if (projects.length === 0) {
|
|
33
|
+
this.error('No projects found. Please create a project in Sanity.io first.');
|
|
34
|
+
}
|
|
35
|
+
const projectChoices = projects.map(({ displayName, id }) => ({
|
|
36
|
+
name: `${displayName} <${id}>`,
|
|
37
|
+
value: id,
|
|
38
|
+
}));
|
|
39
|
+
const { projectId } = await inquirer.prompt([
|
|
40
|
+
{
|
|
41
|
+
type: 'list',
|
|
42
|
+
name: 'projectId',
|
|
43
|
+
message: 'Select your Sanity project:',
|
|
44
|
+
choices: projectChoices,
|
|
45
|
+
},
|
|
46
|
+
]);
|
|
47
|
+
const fileName = `blueprint.${blueprintExtension}`;
|
|
48
|
+
const filePath = join(cwd(), fileName);
|
|
49
|
+
writeBlueprintToDisk({ path: filePath, fileType: blueprintExtension });
|
|
50
|
+
writeConfigFile({ projectId });
|
|
51
|
+
this.log(`Created new blueprint: ./${fileName}`);
|
|
52
|
+
if (blueprintExtension === 'ts') {
|
|
53
|
+
this.log('\nNote: TypeScript support requires "tsx" to be installed. Run: npm install -D tsx');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|