@sanity/cli 7.9.0 → 7.11.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 +163 -22
- package/dist/actions/auth/ensureAuthenticated.js +2 -0
- package/dist/actions/auth/ensureAuthenticated.js.map +1 -1
- package/dist/actions/auth/login/getProvider.js +7 -2
- package/dist/actions/auth/login/getProvider.js.map +1 -1
- package/dist/actions/auth/login/loginInstructions.js +19 -0
- package/dist/actions/auth/login/loginInstructions.js.map +1 -0
- package/dist/actions/dataset/create.js +2 -2
- package/dist/actions/dataset/create.js.map +1 -1
- package/dist/actions/dataset/resolveDataset.js +7 -1
- package/dist/actions/dataset/resolveDataset.js.map +1 -1
- package/dist/actions/deploy/createUserApplication.js +8 -5
- package/dist/actions/deploy/createUserApplication.js.map +1 -1
- package/dist/actions/deploy/deployApp.js +47 -24
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +2 -0
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/graphql/resolveApiGeneration.js +9 -6
- package/dist/actions/graphql/resolveApiGeneration.js.map +1 -1
- package/dist/actions/init/initAction.js +23 -15
- package/dist/actions/init/initAction.js.map +1 -1
- package/dist/actions/manifest/extractCoreAppManifest.js +11 -4
- package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
- package/dist/actions/migration/constants.js +10 -0
- package/dist/actions/migration/constants.js.map +1 -0
- package/dist/actions/migration/ensureApiVersionFormat.js +16 -0
- package/dist/actions/migration/ensureApiVersionFormat.js.map +1 -0
- package/dist/actions/migration/fileExists.js +13 -0
- package/dist/actions/migration/fileExists.js.map +1 -0
- package/dist/actions/migration/prettyMutationFormatter.js +155 -0
- package/dist/actions/migration/prettyMutationFormatter.js.map +1 -0
- package/dist/actions/migration/resolveMigrationScript.js +75 -0
- package/dist/actions/migration/resolveMigrationScript.js.map +1 -0
- package/dist/actions/migration/resolveMigrations.js +50 -0
- package/dist/actions/migration/resolveMigrations.js.map +1 -0
- package/dist/actions/migration/templates/index.js +7 -0
- package/dist/actions/migration/templates/index.js.map +1 -0
- package/dist/actions/migration/templates/minimalAdvanced.js +21 -0
- package/dist/actions/migration/templates/minimalAdvanced.js.map +1 -0
- package/dist/actions/migration/templates/minimalSimple.js +61 -0
- package/dist/actions/migration/templates/minimalSimple.js.map +1 -0
- package/dist/actions/migration/templates/renameField.js +20 -0
- package/dist/actions/migration/templates/renameField.js.map +1 -0
- package/dist/actions/migration/templates/renameType.js +19 -0
- package/dist/actions/migration/templates/renameType.js.map +1 -0
- package/dist/actions/migration/templates/stringToPTE.js +32 -0
- package/dist/actions/migration/templates/stringToPTE.js.map +1 -0
- package/dist/actions/migration/tree.js +111 -0
- package/dist/actions/migration/tree.js.map +1 -0
- package/dist/actions/organizations/getOrganization.js +7 -3
- package/dist/actions/organizations/getOrganization.js.map +1 -1
- package/dist/actions/organizations/validateOrganizationSlug.js +20 -0
- package/dist/actions/organizations/validateOrganizationSlug.js.map +1 -0
- package/dist/actions/preview/previewAction.js +33 -1
- package/dist/actions/preview/previewAction.js.map +1 -1
- package/dist/actions/schema/listSchemas.js +6 -4
- package/dist/actions/schema/listSchemas.js.map +1 -1
- package/dist/actions/tokens/validateRole.js +2 -1
- package/dist/actions/tokens/validateRole.js.map +1 -1
- package/dist/actions/undeploy/runUndeploy.js +13 -4
- package/dist/actions/undeploy/runUndeploy.js.map +1 -1
- package/dist/commands/backups/disable.js +5 -1
- package/dist/commands/backups/disable.js.map +1 -1
- package/dist/commands/backups/download.js +30 -6
- package/dist/commands/backups/download.js.map +1 -1
- package/dist/commands/backups/enable.js +5 -1
- package/dist/commands/backups/enable.js.map +1 -1
- package/dist/commands/backups/list.js +5 -1
- package/dist/commands/backups/list.js.map +1 -1
- package/dist/commands/cors/add.js +15 -7
- package/dist/commands/cors/add.js.map +1 -1
- package/dist/commands/cors/delete.js +6 -1
- package/dist/commands/cors/delete.js.map +1 -1
- package/dist/commands/datasets/alias/create.js +25 -18
- package/dist/commands/datasets/alias/create.js.map +1 -1
- package/dist/commands/datasets/alias/delete.js +15 -8
- package/dist/commands/datasets/alias/delete.js.map +1 -1
- package/dist/commands/datasets/alias/link.js +39 -17
- package/dist/commands/datasets/alias/link.js.map +1 -1
- package/dist/commands/datasets/alias/unlink.js +23 -2
- package/dist/commands/datasets/alias/unlink.js.map +1 -1
- package/dist/commands/datasets/copy.js +18 -2
- package/dist/commands/datasets/copy.js.map +1 -1
- package/dist/commands/datasets/create.js +8 -2
- package/dist/commands/datasets/create.js.map +1 -1
- package/dist/commands/datasets/delete.js +14 -17
- package/dist/commands/datasets/delete.js.map +1 -1
- package/dist/commands/datasets/embeddings/disable.js +3 -0
- package/dist/commands/datasets/embeddings/disable.js.map +1 -1
- package/dist/commands/datasets/embeddings/enable.js +5 -2
- package/dist/commands/datasets/embeddings/enable.js.map +1 -1
- package/dist/commands/datasets/embeddings/status.js +3 -0
- package/dist/commands/datasets/embeddings/status.js.map +1 -1
- package/dist/commands/datasets/export.js +27 -15
- package/dist/commands/datasets/export.js.map +1 -1
- package/dist/commands/datasets/import.js +3 -3
- package/dist/commands/datasets/import.js.map +1 -1
- package/dist/commands/datasets/visibility/get.js +2 -2
- package/dist/commands/datasets/visibility/get.js.map +1 -1
- package/dist/commands/datasets/visibility/set.js +2 -2
- package/dist/commands/datasets/visibility/set.js.map +1 -1
- package/dist/commands/docs/read.js +3 -3
- package/dist/commands/docs/read.js.map +1 -1
- package/dist/commands/documents/create.js +18 -13
- package/dist/commands/documents/create.js.map +1 -1
- package/dist/commands/documents/delete.js +3 -3
- package/dist/commands/documents/delete.js.map +1 -1
- package/dist/commands/documents/get.js +3 -3
- package/dist/commands/documents/get.js.map +1 -1
- package/dist/commands/documents/query.js +3 -3
- package/dist/commands/documents/query.js.map +1 -1
- package/dist/commands/documents/validate.js +29 -26
- package/dist/commands/documents/validate.js.map +1 -1
- package/dist/commands/graphql/deploy.js +20 -16
- package/dist/commands/graphql/deploy.js.map +1 -1
- package/dist/commands/graphql/undeploy.js +15 -18
- package/dist/commands/graphql/undeploy.js.map +1 -1
- package/dist/commands/hooks/create.js +4 -0
- package/dist/commands/hooks/create.js.map +1 -1
- package/dist/commands/hooks/delete.js +6 -1
- package/dist/commands/hooks/delete.js.map +1 -1
- package/dist/commands/hooks/logs.js +6 -1
- package/dist/commands/hooks/logs.js.map +1 -1
- package/dist/commands/install.js +1 -1
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/login.js +2 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/media/create-aspect.js +20 -8
- package/dist/commands/media/create-aspect.js.map +1 -1
- package/dist/commands/media/delete-aspect.js +31 -16
- package/dist/commands/media/delete-aspect.js.map +1 -1
- package/dist/commands/media/deploy-aspect.js +10 -5
- package/dist/commands/media/deploy-aspect.js.map +1 -1
- package/dist/commands/media/export.js +9 -4
- package/dist/commands/media/export.js.map +1 -1
- package/dist/commands/media/import.js +6 -1
- package/dist/commands/media/import.js.map +1 -1
- package/dist/commands/migrations/create.js +150 -0
- package/dist/commands/migrations/create.js.map +1 -0
- package/dist/commands/migrations/list.js +64 -0
- package/dist/commands/migrations/list.js.map +1 -0
- package/dist/commands/migrations/run.js +306 -0
- package/dist/commands/migrations/run.js.map +1 -0
- package/dist/commands/organizations/create.js +79 -0
- package/dist/commands/organizations/create.js.map +1 -0
- package/dist/commands/organizations/delete.js +100 -0
- package/dist/commands/organizations/delete.js.map +1 -0
- package/dist/commands/organizations/get.js +48 -0
- package/dist/commands/organizations/get.js.map +1 -0
- package/dist/commands/organizations/list.js +60 -0
- package/dist/commands/organizations/list.js.map +1 -0
- package/dist/commands/organizations/update.js +115 -0
- package/dist/commands/organizations/update.js.map +1 -0
- package/dist/commands/preview.js +1 -0
- package/dist/commands/preview.js.map +1 -1
- package/dist/commands/projects/create.js +3 -1
- package/dist/commands/projects/create.js.map +1 -1
- package/dist/commands/schemas/delete.js +23 -2
- package/dist/commands/schemas/delete.js.map +1 -1
- package/dist/commands/schemas/deploy.js +6 -4
- package/dist/commands/schemas/deploy.js.map +1 -1
- package/dist/commands/schemas/extract.js +22 -1
- package/dist/commands/schemas/extract.js.map +1 -1
- package/dist/commands/schemas/list.js +8 -8
- package/dist/commands/schemas/list.js.map +1 -1
- package/dist/commands/tokens/{add.js → create.js} +25 -18
- package/dist/commands/tokens/create.js.map +1 -0
- package/dist/commands/tokens/delete.js +41 -23
- package/dist/commands/tokens/delete.js.map +1 -1
- package/dist/commands/undeploy.js +2 -7
- package/dist/commands/undeploy.js.map +1 -1
- package/dist/commands/users/invite.js +25 -3
- package/dist/commands/users/invite.js.map +1 -1
- package/dist/prompts/promptForOrganizationName.js +2 -1
- package/dist/prompts/promptForOrganizationName.js.map +1 -1
- package/dist/services/organizations.js +45 -2
- package/dist/services/organizations.js.map +1 -1
- package/dist/services/userApplications.js.map +1 -1
- package/dist/topicAliases.js +10 -0
- package/dist/topicAliases.js.map +1 -1
- package/dist/util/formatCliErrorMessages.js +5 -0
- package/dist/util/formatCliErrorMessages.js.map +1 -0
- package/dist/util/organizationAliases.js +12 -0
- package/dist/util/organizationAliases.js.map +1 -0
- package/oclif.config.js +6 -1
- package/oclif.manifest.json +464 -11
- package/package.json +28 -28
- package/dist/commands/tokens/add.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/migrations/create.ts"],"sourcesContent":["import {access, mkdir, writeFile} from 'node:fs/promises'\nimport path from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {Args} from '@oclif/core'\nimport {SanityCommand} from '@sanity/cli-core'\nimport {confirm, input, select} from '@sanity/cli-core/ux'\n\nimport {MIGRATIONS_DIRECTORY} from '../../actions/migration/constants.js'\nimport {\n minimalAdvanced,\n minimalSimple,\n renameField,\n renameType,\n stringToPTE,\n} from '../../actions/migration/templates/index.js'\n\nconst TEMPLATES = [\n {name: 'Minimalistic migration to get you started', template: minimalSimple},\n {name: 'Rename an object type', template: renameType},\n {name: 'Rename a field', template: renameField},\n {name: 'Convert string field to Portable Text', template: stringToPTE},\n {\n name: 'Advanced template using async iterators providing more fine grained control',\n template: minimalAdvanced,\n },\n]\n\nexport class CreateMigrationCommand extends SanityCommand<typeof CreateMigrationCommand> {\n static override args = {\n title: Args.string({\n description: 'Title of migration',\n required: false,\n }),\n }\n\n static override description = 'Create a new migration within your project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Create a new migration, prompting for title and options',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"Rename field from location to address\"',\n description: 'Create a new migration with the provided title, prompting for options',\n },\n ]\n\n static override hiddenAliases = ['migration:create']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(CreateMigrationCommand)\n const {directory: workDir} = await this.getProjectRoot()\n\n const title = await this.promptForTitle(args.title)\n const types = await this.promptForDocumentTypes()\n const {template} = await this.promptForTemplate()\n\n const renderedTemplate = (template || minimalSimple)({\n documentTypes: types\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean),\n migrationName: title,\n })\n\n const sluggedName = title\n .toLowerCase()\n .normalize('NFD')\n .replaceAll(/\\p{Mn}/gu, '')\n .replaceAll(/\\s+/g, '-')\n .replaceAll(/[^a-z0-9-]/g, '')\n\n if (!sluggedName) {\n this.error(\n `Could not derive a valid migration name from title ${JSON.stringify(\n title,\n )}. Use a title that contains at least one letter or number.`,\n {exit: 1},\n )\n }\n\n const destDir = path.join(workDir, MIGRATIONS_DIRECTORY, sluggedName)\n const definitionFile = path.join(destDir, 'index.ts')\n\n const dirCreated = await this.createMigrationFile(destDir, definitionFile, renderedTemplate)\n\n if (dirCreated) {\n this.log()\n this.log(`${styleText('green', '✓')} Migration created!`)\n this.log()\n this.log('Next steps:')\n this.log(\n `Open ${styleText(\n 'bold',\n definitionFile,\n )} in your code editor and write the code for your migration.`,\n )\n this.log(\n `Dry run the migration with:\\n\\`${styleText(\n 'bold',\n `sanity migration run ${sluggedName} --project=<projectId> --dataset <dataset> `,\n )}\\``,\n )\n this.log(\n `Run the migration against a dataset with:\\n \\`${styleText(\n 'bold',\n `sanity migration run ${sluggedName} --project=<projectId> --dataset <dataset> --no-dry-run`,\n )}\\``,\n )\n this.log()\n this.log(\n `👉 Learn more about schema and content migrations at ${styleText(\n 'bold',\n 'https://www.sanity.io/docs/schema-and-content-migrations',\n )}`,\n )\n }\n }\n\n private async createMigrationFile(\n destDir: string,\n definitionFile: string,\n renderedTemplate: string,\n ): Promise<boolean> {\n const dirExists = await access(destDir)\n .then(() => true)\n .catch(() => false)\n\n if (dirExists) {\n const shouldOverwrite = await this.promptForOverwrite(destDir)\n if (!shouldOverwrite) return false\n }\n\n try {\n await mkdir(destDir, {recursive: true})\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n this.error(`Failed to create migration directory: ${message}`, {exit: 1})\n }\n\n try {\n await writeFile(definitionFile, renderedTemplate)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n this.error(`Failed to create migration file: ${message}`, {exit: 1})\n }\n\n return true\n }\n\n private async promptForDocumentTypes(): Promise<string> {\n return input({\n message:\n 'Type of documents to migrate. You can add multiple types separated by comma (optional)',\n })\n }\n\n private async promptForOverwrite(destDir: string): Promise<boolean> {\n return confirm({\n default: false,\n message: `Migration directory ${styleText('cyan', destDir)} already exists. Overwrite?`,\n })\n }\n\n private async promptForTemplate(): Promise<{\n name: string\n template: (params: {documentTypes: string[]; migrationName: string}) => string\n }> {\n const templatesByName = Object.fromEntries(TEMPLATES.map((t) => [t.name, t]))\n const templateName = await select({\n choices: TEMPLATES.map((definedTemplate) => ({\n name: definedTemplate.name,\n value: definedTemplate.name,\n })),\n message: 'Select a template',\n })\n\n return templatesByName[templateName]!\n }\n\n private async promptForTitle(providedTitle?: string): Promise<string> {\n if (providedTitle?.trim()) {\n return providedTitle\n }\n\n return input({\n message: 'Title of migration (e.g. \"Rename field from location to address\")',\n validate: (value) => {\n if (!value.trim()) {\n return 'Title cannot be empty'\n }\n\n return true\n },\n })\n }\n}\n"],"names":["access","mkdir","writeFile","path","styleText","Args","SanityCommand","confirm","input","select","MIGRATIONS_DIRECTORY","minimalAdvanced","minimalSimple","renameField","renameType","stringToPTE","TEMPLATES","name","template","CreateMigrationCommand","args","title","string","description","required","examples","command","hiddenAliases","run","parse","directory","workDir","getProjectRoot","promptForTitle","types","promptForDocumentTypes","promptForTemplate","renderedTemplate","documentTypes","split","map","t","trim","filter","Boolean","migrationName","sluggedName","toLowerCase","normalize","replaceAll","error","JSON","stringify","exit","destDir","join","definitionFile","dirCreated","createMigrationFile","log","dirExists","then","catch","shouldOverwrite","promptForOverwrite","recursive","message","Error","String","default","templatesByName","Object","fromEntries","templateName","choices","definedTemplate","value","providedTitle","validate"],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
|
+
import { SanityCommand, subdebug } from '@sanity/cli-core';
|
|
3
|
+
import { Table } from 'console-table-printer';
|
|
4
|
+
import { resolveMigrations } from '../../actions/migration/resolveMigrations.js';
|
|
5
|
+
const listMigrationDebug = subdebug('migration:list');
|
|
6
|
+
export class ListMigrationCommand extends SanityCommand {
|
|
7
|
+
static description = 'List available migrations';
|
|
8
|
+
static examples = [
|
|
9
|
+
{
|
|
10
|
+
command: '<%= config.bin %> <%= command.id %>',
|
|
11
|
+
description: 'List all available migrations in the project'
|
|
12
|
+
}
|
|
13
|
+
];
|
|
14
|
+
static hiddenAliases = [
|
|
15
|
+
'migration:list'
|
|
16
|
+
];
|
|
17
|
+
async run() {
|
|
18
|
+
const { directory: workDir } = await this.getProjectRoot();
|
|
19
|
+
try {
|
|
20
|
+
const migrations = await resolveMigrations(workDir);
|
|
21
|
+
if (migrations.length === 0) {
|
|
22
|
+
this.log('No migrations found in migrations folder of the project');
|
|
23
|
+
this.log(`\nRun ${styleText('green', '`sanity migration create <NAME>`')} to create a new migration`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const table = new Table({
|
|
27
|
+
columns: [
|
|
28
|
+
{
|
|
29
|
+
alignment: 'left',
|
|
30
|
+
name: 'id',
|
|
31
|
+
title: 'ID'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
alignment: 'left',
|
|
35
|
+
name: 'title',
|
|
36
|
+
title: 'Title'
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
title: `Found ${migrations.length} migrations in project`
|
|
40
|
+
});
|
|
41
|
+
for (const definedMigration of migrations){
|
|
42
|
+
table.addRow({
|
|
43
|
+
id: definedMigration.id,
|
|
44
|
+
title: definedMigration.migration.title
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
table.printTable();
|
|
48
|
+
this.log('\nRun `sanity migration run <ID>` to run a migration');
|
|
49
|
+
listMigrationDebug(`Successfully listed ${migrations.length} migrations`);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (error.code === 'ENOENT') {
|
|
52
|
+
this.log('No migrations folder found in the project');
|
|
53
|
+
this.log(`\nRun ${styleText('green', '`sanity migration create <NAME>`')} to create a new migration`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
listMigrationDebug('Failed to list migrations:', error);
|
|
57
|
+
this.error(`List migrations failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
58
|
+
exit: 1
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/migrations/list.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\nimport {Table} from 'console-table-printer'\n\nimport {resolveMigrations} from '../../actions/migration/resolveMigrations.js'\n\nconst listMigrationDebug = subdebug('migration:list')\n\nexport class ListMigrationCommand extends SanityCommand<typeof ListMigrationCommand> {\n static override description = 'List available migrations'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List all available migrations in the project',\n },\n ]\n\n static override hiddenAliases = ['migration:list']\n\n public async run(): Promise<void> {\n const {directory: workDir} = await this.getProjectRoot()\n\n try {\n const migrations = await resolveMigrations(workDir)\n\n if (migrations.length === 0) {\n this.log('No migrations found in migrations folder of the project')\n this.log(\n `\\nRun ${styleText('green', '`sanity migration create <NAME>`')} to create a new migration`,\n )\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'ID'},\n {alignment: 'left', name: 'title', title: 'Title'},\n ],\n title: `Found ${migrations.length} migrations in project`,\n })\n\n for (const definedMigration of migrations) {\n table.addRow({id: definedMigration.id, title: definedMigration.migration.title})\n }\n table.printTable()\n this.log('\\nRun `sanity migration run <ID>` to run a migration')\n listMigrationDebug(`Successfully listed ${migrations.length} migrations`)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n this.log('No migrations folder found in the project')\n this.log(\n `\\nRun ${styleText('green', '`sanity migration create <NAME>`')} to create a new migration`,\n )\n return\n }\n listMigrationDebug('Failed to list migrations:', error)\n this.error(\n `List migrations failed: ${error instanceof Error ? error.message : String(error)}`,\n {\n exit: 1,\n },\n )\n }\n }\n}\n"],"names":["styleText","SanityCommand","subdebug","Table","resolveMigrations","listMigrationDebug","ListMigrationCommand","description","examples","command","hiddenAliases","run","directory","workDir","getProjectRoot","migrations","length","log","table","columns","alignment","name","title","definedMigration","addRow","id","migration","printTable","error","code","Error","message","String","exit"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACxD,SAAQC,KAAK,QAAO,wBAAuB;AAE3C,SAAQC,iBAAiB,QAAO,+CAA8C;AAE9E,MAAMC,qBAAqBH,SAAS;AAEpC,OAAO,MAAMI,6BAA6BL;IACxC,OAAgBM,cAAc,4BAA2B;IAEzD,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IAED,OAAgBG,gBAAgB;QAAC;KAAiB,CAAA;IAElD,MAAaC,MAAqB;QAChC,MAAM,EAACC,WAAWC,OAAO,EAAC,GAAG,MAAM,IAAI,CAACC,cAAc;QAEtD,IAAI;YACF,MAAMC,aAAa,MAAMX,kBAAkBS;YAE3C,IAAIE,WAAWC,MAAM,KAAK,GAAG;gBAC3B,IAAI,CAACC,GAAG,CAAC;gBACT,IAAI,CAACA,GAAG,CACN,CAAC,MAAM,EAAEjB,UAAU,SAAS,oCAAoC,0BAA0B,CAAC;gBAE7F;YACF;YAEA,MAAMkB,QAAQ,IAAIf,MAAM;gBACtBgB,SAAS;oBACP;wBAACC,WAAW;wBAAQC,MAAM;wBAAMC,OAAO;oBAAI;oBAC3C;wBAACF,WAAW;wBAAQC,MAAM;wBAASC,OAAO;oBAAO;iBAClD;gBACDA,OAAO,CAAC,MAAM,EAAEP,WAAWC,MAAM,CAAC,sBAAsB,CAAC;YAC3D;YAEA,KAAK,MAAMO,oBAAoBR,WAAY;gBACzCG,MAAMM,MAAM,CAAC;oBAACC,IAAIF,iBAAiBE,EAAE;oBAAEH,OAAOC,iBAAiBG,SAAS,CAACJ,KAAK;gBAAA;YAChF;YACAJ,MAAMS,UAAU;YAChB,IAAI,CAACV,GAAG,CAAC;YACTZ,mBAAmB,CAAC,oBAAoB,EAAEU,WAAWC,MAAM,CAAC,WAAW,CAAC;QAC1E,EAAE,OAAOY,OAAO;YACd,IAAI,AAACA,MAAgCC,IAAI,KAAK,UAAU;gBACtD,IAAI,CAACZ,GAAG,CAAC;gBACT,IAAI,CAACA,GAAG,CACN,CAAC,MAAM,EAAEjB,UAAU,SAAS,oCAAoC,0BAA0B,CAAC;gBAE7F;YACF;YACAK,mBAAmB,8BAA8BuB;YACjD,IAAI,CAACA,KAAK,CACR,CAAC,wBAAwB,EAAEA,iBAAiBE,QAAQF,MAAMG,OAAO,GAAGC,OAAOJ,QAAQ,EACnF;gBACEK,MAAM;YACR;QAEJ;IACF;AACF"}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { styleText } from 'node:util';
|
|
3
|
+
import { Args, Flags } from '@oclif/core';
|
|
4
|
+
import { getProjectCliClient, SanityCommand, subdebug } from '@sanity/cli-core';
|
|
5
|
+
import { confirm, spinner } from '@sanity/cli-core/ux';
|
|
6
|
+
import { DEFAULT_MUTATION_CONCURRENCY, dryRun, MAX_MUTATION_CONCURRENCY, run } from '@sanity/migrate';
|
|
7
|
+
import { Table } from 'console-table-printer';
|
|
8
|
+
import { DEFAULT_API_VERSION, MIGRATIONS_DIRECTORY } from '../../actions/migration/constants.js';
|
|
9
|
+
import { ensureApiVersionFormat } from '../../actions/migration/ensureApiVersionFormat.js';
|
|
10
|
+
import { prettyFormat } from '../../actions/migration/prettyMutationFormatter.js';
|
|
11
|
+
import { resolveMigrations } from '../../actions/migration/resolveMigrations.js';
|
|
12
|
+
import { isLoadableMigrationScript, resolveMigrationScript } from '../../actions/migration/resolveMigrationScript.js';
|
|
13
|
+
const runMigrationDebug = subdebug('migration:run');
|
|
14
|
+
export class RunMigrationCommand extends SanityCommand {
|
|
15
|
+
static args = {
|
|
16
|
+
id: Args.string({
|
|
17
|
+
description: 'ID',
|
|
18
|
+
required: false
|
|
19
|
+
})
|
|
20
|
+
};
|
|
21
|
+
static description = 'Run a migration against a dataset';
|
|
22
|
+
static examples = [
|
|
23
|
+
{
|
|
24
|
+
command: '<%= config.bin %> <%= command.id %> <id>',
|
|
25
|
+
description: 'dry run the migration'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
command: '<%= config.bin %> <%= command.id %> <id> --no-dry-run --project xyz --dataset staging',
|
|
29
|
+
description: 'execute the migration against a dataset'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
command: '<%= config.bin %> <%= command.id %> <id> --from-export=production.tar.gz --no-dry-run --project xyz --dataset staging',
|
|
33
|
+
description: 'execute the migration using a dataset export as the source'
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
static flags = {
|
|
37
|
+
'api-version': Flags.string({
|
|
38
|
+
description: `API version to use when migrating. Defaults to ${DEFAULT_API_VERSION}.`
|
|
39
|
+
}),
|
|
40
|
+
concurrency: Flags.integer({
|
|
41
|
+
default: DEFAULT_MUTATION_CONCURRENCY,
|
|
42
|
+
description: `How many mutation requests to run in parallel. Must be between 1 and ${MAX_MUTATION_CONCURRENCY}. Default: ${DEFAULT_MUTATION_CONCURRENCY}.`
|
|
43
|
+
}),
|
|
44
|
+
confirm: Flags.boolean({
|
|
45
|
+
allowNo: true,
|
|
46
|
+
default: true,
|
|
47
|
+
description: 'Prompt for confirmation before running the migration (default: true). Use --no-confirm to skip.'
|
|
48
|
+
}),
|
|
49
|
+
dataset: Flags.string({
|
|
50
|
+
description: 'Dataset to migrate. Defaults to the dataset configured in your Sanity CLI config.'
|
|
51
|
+
}),
|
|
52
|
+
'dry-run': Flags.boolean({
|
|
53
|
+
allowNo: true,
|
|
54
|
+
default: true,
|
|
55
|
+
description: 'By default the migration runs in dry mode. Use --no-dry-run to migrate dataset.'
|
|
56
|
+
}),
|
|
57
|
+
'from-export': Flags.string({
|
|
58
|
+
description: 'Use a local dataset export as source for migration instead of calling the Sanity API. Note: this is only supported for dry runs.'
|
|
59
|
+
}),
|
|
60
|
+
progress: Flags.boolean({
|
|
61
|
+
allowNo: true,
|
|
62
|
+
default: true,
|
|
63
|
+
description: 'Display progress during migration (default: true). Use --no-progress to hide output.'
|
|
64
|
+
}),
|
|
65
|
+
project: Flags.string({
|
|
66
|
+
description: 'Project ID of the dataset to migrate. Defaults to the projectId configured in your Sanity CLI config.'
|
|
67
|
+
})
|
|
68
|
+
};
|
|
69
|
+
static hiddenAliases = [
|
|
70
|
+
'migration:run'
|
|
71
|
+
];
|
|
72
|
+
async run() {
|
|
73
|
+
const { args, flags } = await this.parse(RunMigrationCommand);
|
|
74
|
+
const { directory: workDir } = await this.getProjectRoot();
|
|
75
|
+
const id = args.id;
|
|
76
|
+
const migrationsDirectoryPath = path.join(workDir, MIGRATIONS_DIRECTORY);
|
|
77
|
+
// Listing migrations (no id given) only needs a project root, mirroring
|
|
78
|
+
// `sanity migrations list` — it must not require api.projectId/dataset.
|
|
79
|
+
if (!id) {
|
|
80
|
+
this.warn(styleText('red', 'Error: Migration ID must be provided'));
|
|
81
|
+
let migrations = [];
|
|
82
|
+
try {
|
|
83
|
+
migrations = await resolveMigrations(workDir);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
// A missing migrations folder is expected for a fresh project; surface
|
|
86
|
+
// any other failure (e.g. a syntax error in a migration file).
|
|
87
|
+
if (error.code !== 'ENOENT') {
|
|
88
|
+
this.error(`Failed to list migrations: ${error instanceof Error ? error.message : String(error)}`, {
|
|
89
|
+
exit: 1
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (migrations.length === 0) {
|
|
94
|
+
this.log('\nNo migrations found in the project');
|
|
95
|
+
this.log(`Run ${styleText('green', '`sanity migration create <NAME>`')} to create a new migration`);
|
|
96
|
+
this.exit(1);
|
|
97
|
+
}
|
|
98
|
+
const table = new Table({
|
|
99
|
+
columns: [
|
|
100
|
+
{
|
|
101
|
+
alignment: 'left',
|
|
102
|
+
name: 'id',
|
|
103
|
+
title: 'ID'
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
alignment: 'left',
|
|
107
|
+
name: 'title',
|
|
108
|
+
title: 'Title'
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
title: `Migrations found in project`
|
|
112
|
+
});
|
|
113
|
+
for (const definedMigration of migrations){
|
|
114
|
+
table.addRow({
|
|
115
|
+
id: definedMigration.id,
|
|
116
|
+
title: definedMigration.migration.title
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
table.printTable();
|
|
120
|
+
this.log('\nRun `sanity migration run <ID>` to run a migration');
|
|
121
|
+
this.exit(1);
|
|
122
|
+
}
|
|
123
|
+
const cliConfig = await this.getCliConfig();
|
|
124
|
+
const projectId = await this.getProjectId({
|
|
125
|
+
deprecatedFlagName: 'project'
|
|
126
|
+
});
|
|
127
|
+
const datasetFromConfig = cliConfig.api?.dataset;
|
|
128
|
+
const fromExport = flags['from-export'];
|
|
129
|
+
const dry = flags['dry-run'];
|
|
130
|
+
const dataset = flags.dataset;
|
|
131
|
+
const project = flags.project;
|
|
132
|
+
const apiVersion = ensureApiVersionFormat(flags['api-version'] ?? DEFAULT_API_VERSION);
|
|
133
|
+
if (dataset && !project || project && !dataset) {
|
|
134
|
+
this.error('If either --dataset or --project is provided, both must be provided', {
|
|
135
|
+
exit: 1
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (!project && !projectId) {
|
|
139
|
+
this.error('sanity.cli.js does not contain a project identifier ("api.projectId") and no --project option was provided.', {
|
|
140
|
+
exit: 1
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if (!dataset && !datasetFromConfig) {
|
|
144
|
+
this.error('sanity.cli.js does not contain a dataset identifier ("api.dataset") and no --dataset option was provided.', {
|
|
145
|
+
exit: 1
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const candidates = await resolveMigrationScript(workDir, id);
|
|
149
|
+
const resolvedScripts = candidates.filter((candidate)=>isLoadableMigrationScript(candidate));
|
|
150
|
+
if (resolvedScripts.length > 1) {
|
|
151
|
+
// todo: consider prompt user about which one to run? note: it's likely a mistake if multiple files resolve to the same name
|
|
152
|
+
this.error(`Found multiple migrations for "${id}" in ${styleText('cyan', migrationsDirectoryPath)}: \n - ${candidates.map((candidate)=>path.relative(migrationsDirectoryPath, candidate.absolutePath)).join('\n - ')}`, {
|
|
153
|
+
exit: 1
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
const script = resolvedScripts[0];
|
|
157
|
+
if (!script) {
|
|
158
|
+
this.error(`No migration found for "${id}" in ${styleText('cyan', migrationsDirectoryPath)}. Make sure that the migration file exists and exports a valid migration as its default export.\n
|
|
159
|
+
Tried the following files:\n - ${candidates.map((candidate)=>path.relative(migrationsDirectoryPath, candidate.absolutePath)).join('\n - ')}`, {
|
|
160
|
+
exit: 1
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
const mod = script.mod;
|
|
164
|
+
if ('up' in mod || 'down' in mod) {
|
|
165
|
+
// todo: consider adding support for up/down as separate named exports
|
|
166
|
+
// For now, make sure we reserve the names for future use
|
|
167
|
+
this.error('Named "up"/"down" migration exports are not supported at this time, please export your migration as the default export', {
|
|
168
|
+
exit: 1
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const migration = mod.default;
|
|
172
|
+
if (fromExport && !dry) {
|
|
173
|
+
this.error('Can only dry run migrations from a dataset export file', {
|
|
174
|
+
exit: 1
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const concurrency = flags.concurrency;
|
|
178
|
+
if (concurrency !== undefined) {
|
|
179
|
+
if (concurrency > MAX_MUTATION_CONCURRENCY) {
|
|
180
|
+
this.error(`Concurrency exceeds the maximum allowed value of ${MAX_MUTATION_CONCURRENCY}`, {
|
|
181
|
+
exit: 1
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if (concurrency < 1) {
|
|
185
|
+
this.error(`Concurrency must be a positive number, got ${concurrency}`, {
|
|
186
|
+
exit: 1
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const projectClient = await getProjectCliClient({
|
|
191
|
+
apiVersion,
|
|
192
|
+
projectId: project ?? projectId,
|
|
193
|
+
requireUser: true
|
|
194
|
+
});
|
|
195
|
+
const projectConfig = projectClient.config();
|
|
196
|
+
const apiConfig = {
|
|
197
|
+
apiHost: projectConfig.apiHost,
|
|
198
|
+
apiVersion,
|
|
199
|
+
dataset: dataset ?? datasetFromConfig,
|
|
200
|
+
projectId: project ?? projectId,
|
|
201
|
+
token: projectConfig.token
|
|
202
|
+
};
|
|
203
|
+
if (dry) {
|
|
204
|
+
await this.dryRunHandler(id, migration, apiConfig, fromExport);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
this.log(`\n${styleText([
|
|
208
|
+
'yellow',
|
|
209
|
+
'bold'
|
|
210
|
+
], 'Note: During migrations, your webhooks stay active.')}`);
|
|
211
|
+
this.log(`To adjust them, launch the management interface with ${styleText('cyan', 'sanity manage')}, navigate to the API settings, and toggle the webhooks before and after the migration as needed.\n`);
|
|
212
|
+
if (flags.confirm) {
|
|
213
|
+
await this.promptConfirmMigrate(apiConfig);
|
|
214
|
+
}
|
|
215
|
+
const spin = spinner(`Running migration "${id}"`).start();
|
|
216
|
+
try {
|
|
217
|
+
await run({
|
|
218
|
+
api: apiConfig,
|
|
219
|
+
concurrency,
|
|
220
|
+
onProgress: this.createProgress(spin, flags, id, dry, apiConfig, migration)
|
|
221
|
+
}, migration);
|
|
222
|
+
} finally{
|
|
223
|
+
spin.stop();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
createProgress(progressSpinner, flags, id, dry, apiConfig, migration) {
|
|
227
|
+
return function onProgress(progress) {
|
|
228
|
+
if (!flags.progress) {
|
|
229
|
+
progressSpinner.stop();
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (progress.done) {
|
|
233
|
+
progressSpinner.text = `Migration "${id}" completed.
|
|
234
|
+
|
|
235
|
+
Project id: ${styleText('bold', apiConfig.projectId)}
|
|
236
|
+
Dataset: ${styleText('bold', apiConfig.dataset)}
|
|
237
|
+
|
|
238
|
+
${progress.documents} documents processed.
|
|
239
|
+
${progress.mutations} mutations generated.
|
|
240
|
+
${styleText('green', String(progress.completedTransactions.length))} transactions committed.`;
|
|
241
|
+
progressSpinner.stopAndPersist({
|
|
242
|
+
symbol: styleText('green', '✔')
|
|
243
|
+
});
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
for (const transaction of [
|
|
247
|
+
null,
|
|
248
|
+
...progress.currentTransactions
|
|
249
|
+
]){
|
|
250
|
+
progressSpinner.text = `Running migration "${id}" ${dry ? 'in dry mode...' : '...'}
|
|
251
|
+
|
|
252
|
+
Project id: ${styleText('bold', apiConfig.projectId)}
|
|
253
|
+
Dataset: ${styleText('bold', apiConfig.dataset)}
|
|
254
|
+
Document type: ${styleText('bold', migration.documentTypes?.join(',') ?? '')}
|
|
255
|
+
|
|
256
|
+
${progress.documents} documents processed…
|
|
257
|
+
${progress.mutations} mutations generated…
|
|
258
|
+
${styleText('blue', String(progress.pending))} requests pending…
|
|
259
|
+
${styleText('green', String(progress.completedTransactions.length))} transactions committed.
|
|
260
|
+
|
|
261
|
+
${transaction && !progress.done ? `» ${prettyFormat({
|
|
262
|
+
indentSize: 2,
|
|
263
|
+
migration,
|
|
264
|
+
subject: transaction
|
|
265
|
+
})}` : ''}`;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
async dryRunHandler(id, migration, apiConfig, fromExport) {
|
|
270
|
+
this.log(`Running migration "${id}" in dry mode`);
|
|
271
|
+
if (fromExport) {
|
|
272
|
+
this.log(`Using export ${styleText('cyan', fromExport)}`);
|
|
273
|
+
}
|
|
274
|
+
this.log();
|
|
275
|
+
this.log(`Project id: ${styleText('bold', apiConfig.projectId)}`);
|
|
276
|
+
this.log(`Dataset: ${styleText('bold', apiConfig.dataset)}`);
|
|
277
|
+
for await (const mutation of dryRun({
|
|
278
|
+
api: apiConfig,
|
|
279
|
+
exportPath: fromExport
|
|
280
|
+
}, migration)){
|
|
281
|
+
if (!mutation) continue;
|
|
282
|
+
this.log();
|
|
283
|
+
this.log(prettyFormat({
|
|
284
|
+
migration,
|
|
285
|
+
subject: mutation
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async promptConfirmMigrate(apiConfig) {
|
|
290
|
+
const response = await confirm({
|
|
291
|
+
message: `This migration will run on the ${styleText([
|
|
292
|
+
'yellow',
|
|
293
|
+
'bold'
|
|
294
|
+
], apiConfig.dataset)} dataset in ${styleText([
|
|
295
|
+
'yellow',
|
|
296
|
+
'bold'
|
|
297
|
+
], apiConfig.projectId)} project. Are you sure?`
|
|
298
|
+
});
|
|
299
|
+
if (!response) {
|
|
300
|
+
runMigrationDebug('User aborted migration');
|
|
301
|
+
this.exit(1);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/migrations/run.ts"],"sourcesContent":["import path from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {getProjectCliClient, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {confirm, spinner} from '@sanity/cli-core/ux'\nimport {\n type APIConfig,\n DEFAULT_MUTATION_CONCURRENCY,\n dryRun,\n MAX_MUTATION_CONCURRENCY,\n type Migration,\n type MigrationProgress,\n run,\n} from '@sanity/migrate'\nimport {Table} from 'console-table-printer'\n\nimport {DEFAULT_API_VERSION, MIGRATIONS_DIRECTORY} from '../../actions/migration/constants.js'\nimport {ensureApiVersionFormat} from '../../actions/migration/ensureApiVersionFormat.js'\nimport {prettyFormat} from '../../actions/migration/prettyMutationFormatter.js'\nimport {resolveMigrations} from '../../actions/migration/resolveMigrations.js'\nimport {\n isLoadableMigrationScript,\n resolveMigrationScript,\n} from '../../actions/migration/resolveMigrationScript.js'\n\nconst runMigrationDebug = subdebug('migration:run')\n\nexport type RunMigrationFlags = RunMigrationCommand['flags']\n\nexport class RunMigrationCommand extends SanityCommand<typeof RunMigrationCommand> {\n static override args = {\n id: Args.string({\n description: 'ID',\n required: false,\n }),\n }\n\n static override description = 'Run a migration against a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> <id>',\n description: 'dry run the migration',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> <id> --no-dry-run --project xyz --dataset staging',\n description: 'execute the migration against a dataset',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> <id> --from-export=production.tar.gz --no-dry-run --project xyz --dataset staging',\n description: 'execute the migration using a dataset export as the source',\n },\n ]\n\n static override flags = {\n 'api-version': Flags.string({\n description: `API version to use when migrating. Defaults to ${DEFAULT_API_VERSION}.`,\n }),\n concurrency: Flags.integer({\n default: DEFAULT_MUTATION_CONCURRENCY,\n description: `How many mutation requests to run in parallel. Must be between 1 and ${MAX_MUTATION_CONCURRENCY}. Default: ${DEFAULT_MUTATION_CONCURRENCY}.`,\n }),\n confirm: Flags.boolean({\n allowNo: true,\n default: true,\n description:\n 'Prompt for confirmation before running the migration (default: true). Use --no-confirm to skip.',\n }),\n dataset: Flags.string({\n description:\n 'Dataset to migrate. Defaults to the dataset configured in your Sanity CLI config.',\n }),\n 'dry-run': Flags.boolean({\n allowNo: true,\n default: true,\n description:\n 'By default the migration runs in dry mode. Use --no-dry-run to migrate dataset.',\n }),\n 'from-export': Flags.string({\n description:\n 'Use a local dataset export as source for migration instead of calling the Sanity API. Note: this is only supported for dry runs.',\n }),\n progress: Flags.boolean({\n allowNo: true,\n default: true,\n description:\n 'Display progress during migration (default: true). Use --no-progress to hide output.',\n }),\n project: Flags.string({\n description:\n 'Project ID of the dataset to migrate. Defaults to the projectId configured in your Sanity CLI config.',\n }),\n }\n\n static override hiddenAliases = ['migration:run']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(RunMigrationCommand)\n const {directory: workDir} = await this.getProjectRoot()\n const id = args.id\n const migrationsDirectoryPath = path.join(workDir, MIGRATIONS_DIRECTORY)\n\n // Listing migrations (no id given) only needs a project root, mirroring\n // `sanity migrations list` — it must not require api.projectId/dataset.\n if (!id) {\n this.warn(styleText('red', 'Error: Migration ID must be provided'))\n\n let migrations: Awaited<ReturnType<typeof resolveMigrations>> = []\n try {\n migrations = await resolveMigrations(workDir)\n } catch (error) {\n // A missing migrations folder is expected for a fresh project; surface\n // any other failure (e.g. a syntax error in a migration file).\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n this.error(\n `Failed to list migrations: ${error instanceof Error ? error.message : String(error)}`,\n {exit: 1},\n )\n }\n }\n\n if (migrations.length === 0) {\n this.log('\\nNo migrations found in the project')\n this.log(\n `Run ${styleText('green', '`sanity migration create <NAME>`')} to create a new migration`,\n )\n this.exit(1)\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'ID'},\n {alignment: 'left', name: 'title', title: 'Title'},\n ],\n title: `Migrations found in project`,\n })\n\n for (const definedMigration of migrations) {\n table.addRow({id: definedMigration.id, title: definedMigration.migration.title})\n }\n table.printTable()\n this.log('\\nRun `sanity migration run <ID>` to run a migration')\n\n this.exit(1)\n }\n\n const cliConfig = await this.getCliConfig()\n const projectId = await this.getProjectId({deprecatedFlagName: 'project'})\n const datasetFromConfig = cliConfig.api?.dataset\n\n const fromExport = flags['from-export']\n const dry = flags['dry-run']\n const dataset = flags.dataset\n const project = flags.project\n const apiVersion = ensureApiVersionFormat(flags['api-version'] ?? DEFAULT_API_VERSION)\n\n if ((dataset && !project) || (project && !dataset)) {\n this.error('If either --dataset or --project is provided, both must be provided', {exit: 1})\n }\n\n if (!project && !projectId) {\n this.error(\n 'sanity.cli.js does not contain a project identifier (\"api.projectId\") and no --project option was provided.',\n {exit: 1},\n )\n }\n\n if (!dataset && !datasetFromConfig) {\n this.error(\n 'sanity.cli.js does not contain a dataset identifier (\"api.dataset\") and no --dataset option was provided.',\n {exit: 1},\n )\n }\n\n const candidates = await resolveMigrationScript(workDir, id)\n const resolvedScripts = candidates.filter((candidate) => isLoadableMigrationScript(candidate))\n\n if (resolvedScripts.length > 1) {\n // todo: consider prompt user about which one to run? note: it's likely a mistake if multiple files resolve to the same name\n this.error(\n `Found multiple migrations for \"${id}\" in ${styleText('cyan', migrationsDirectoryPath)}: \\n - ${candidates\n .map((candidate) => path.relative(migrationsDirectoryPath, candidate.absolutePath))\n .join('\\n - ')}`,\n {exit: 1},\n )\n }\n\n const script = resolvedScripts[0]\n if (!script) {\n this.error(\n `No migration found for \"${id}\" in ${styleText('cyan', migrationsDirectoryPath)}. Make sure that the migration file exists and exports a valid migration as its default export.\\n\n Tried the following files:\\n - ${candidates\n .map((candidate) => path.relative(migrationsDirectoryPath, candidate.absolutePath))\n .join('\\n - ')}`,\n {exit: 1},\n )\n }\n\n const mod = script.mod\n if ('up' in mod || 'down' in mod) {\n // todo: consider adding support for up/down as separate named exports\n // For now, make sure we reserve the names for future use\n this.error(\n 'Named \"up\"/\"down\" migration exports are not supported at this time, please export your migration as the default export',\n {\n exit: 1,\n },\n )\n }\n\n const migration: Migration = mod.default\n\n if (fromExport && !dry) {\n this.error('Can only dry run migrations from a dataset export file', {exit: 1})\n }\n\n const concurrency = flags.concurrency\n if (concurrency !== undefined) {\n if (concurrency > MAX_MUTATION_CONCURRENCY) {\n this.error(`Concurrency exceeds the maximum allowed value of ${MAX_MUTATION_CONCURRENCY}`, {\n exit: 1,\n })\n }\n\n if (concurrency < 1) {\n this.error(`Concurrency must be a positive number, got ${concurrency}`, {exit: 1})\n }\n }\n\n const projectClient = await getProjectCliClient({\n apiVersion,\n projectId: (project ?? projectId)!,\n requireUser: true,\n })\n const projectConfig = projectClient.config()\n\n const apiConfig: APIConfig = {\n apiHost: projectConfig.apiHost,\n apiVersion,\n dataset: (dataset ?? datasetFromConfig)!,\n projectId: (project ?? projectId)!,\n token: projectConfig.token!,\n } as const\n if (dry) {\n await this.dryRunHandler(id, migration, apiConfig, fromExport)\n return\n }\n\n this.log(\n `\\n${styleText(['yellow', 'bold'], 'Note: During migrations, your webhooks stay active.')}`,\n )\n this.log(\n `To adjust them, launch the management interface with ${styleText('cyan', 'sanity manage')}, navigate to the API settings, and toggle the webhooks before and after the migration as needed.\\n`,\n )\n\n if (flags.confirm) {\n await this.promptConfirmMigrate(apiConfig)\n }\n\n const spin = spinner(`Running migration \"${id}\"`).start()\n try {\n await run(\n {\n api: apiConfig,\n concurrency,\n onProgress: this.createProgress(spin, flags, id, dry, apiConfig, migration),\n },\n migration,\n )\n } finally {\n spin.stop()\n }\n }\n\n private createProgress(\n progressSpinner: ReturnType<typeof spinner>,\n flags: RunMigrationFlags,\n id: string,\n dry: boolean,\n apiConfig: APIConfig,\n migration: Migration,\n ) {\n return function onProgress(progress: MigrationProgress) {\n if (!flags.progress) {\n progressSpinner.stop()\n return\n }\n if (progress.done) {\n progressSpinner.text = `Migration \"${id}\" completed.\n\n Project id: ${styleText('bold', apiConfig.projectId)}\n Dataset: ${styleText('bold', apiConfig.dataset)}\n\n ${progress.documents} documents processed.\n ${progress.mutations} mutations generated.\n ${styleText('green', String(progress.completedTransactions.length))} transactions committed.`\n progressSpinner.stopAndPersist({symbol: styleText('green', '✔')})\n return\n }\n\n for (const transaction of [null, ...progress.currentTransactions]) {\n progressSpinner.text = `Running migration \"${id}\" ${dry ? 'in dry mode...' : '...'}\n\n Project id: ${styleText('bold', apiConfig.projectId)}\n Dataset: ${styleText('bold', apiConfig.dataset)}\n Document type: ${styleText('bold', migration.documentTypes?.join(',') ?? '')}\n\n ${progress.documents} documents processed…\n ${progress.mutations} mutations generated…\n ${styleText('blue', String(progress.pending))} requests pending…\n ${styleText('green', String(progress.completedTransactions.length))} transactions committed.\n\n ${\n transaction && !progress.done\n ? `» ${prettyFormat({indentSize: 2, migration, subject: transaction})}`\n : ''\n }`\n }\n }\n }\n\n private async dryRunHandler(\n id: string,\n migration: Migration,\n apiConfig: APIConfig,\n fromExport: string | undefined,\n ) {\n this.log(`Running migration \"${id}\" in dry mode`)\n\n if (fromExport) {\n this.log(`Using export ${styleText('cyan', fromExport)}`)\n }\n\n this.log()\n this.log(`Project id: ${styleText('bold', apiConfig.projectId)}`)\n this.log(`Dataset: ${styleText('bold', apiConfig.dataset)}`)\n\n for await (const mutation of dryRun({api: apiConfig, exportPath: fromExport}, migration)) {\n if (!mutation) continue\n this.log()\n this.log(\n prettyFormat({\n migration,\n subject: mutation,\n }),\n )\n }\n }\n\n private async promptConfirmMigrate(apiConfig: APIConfig) {\n const response = await confirm({\n message: `This migration will run on the ${styleText(\n ['yellow', 'bold'],\n apiConfig.dataset,\n )} dataset in ${styleText(['yellow', 'bold'], apiConfig.projectId)} project. Are you sure?`,\n })\n\n if (!response) {\n runMigrationDebug('User aborted migration')\n this.exit(1)\n }\n }\n}\n"],"names":["path","styleText","Args","Flags","getProjectCliClient","SanityCommand","subdebug","confirm","spinner","DEFAULT_MUTATION_CONCURRENCY","dryRun","MAX_MUTATION_CONCURRENCY","run","Table","DEFAULT_API_VERSION","MIGRATIONS_DIRECTORY","ensureApiVersionFormat","prettyFormat","resolveMigrations","isLoadableMigrationScript","resolveMigrationScript","runMigrationDebug","RunMigrationCommand","args","id","string","description","required","examples","command","flags","concurrency","integer","default","boolean","allowNo","dataset","progress","project","hiddenAliases","parse","directory","workDir","getProjectRoot","migrationsDirectoryPath","join","warn","migrations","error","code","Error","message","String","exit","length","log","table","columns","alignment","name","title","definedMigration","addRow","migration","printTable","cliConfig","getCliConfig","projectId","getProjectId","deprecatedFlagName","datasetFromConfig","api","fromExport","dry","apiVersion","candidates","resolvedScripts","filter","candidate","map","relative","absolutePath","script","mod","undefined","projectClient","requireUser","projectConfig","config","apiConfig","apiHost","token","dryRunHandler","promptConfirmMigrate","spin","start","onProgress","createProgress","stop","progressSpinner","done","text","documents","mutations","completedTransactions","stopAndPersist","symbol","transaction","currentTransactions","documentTypes","pending","indentSize","subject","mutation","exportPath","response"],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { SanityCommand, subdebug } from '@sanity/cli-core';
|
|
3
|
+
import { getErrorMessage } from '@sanity/cli-core/errors';
|
|
4
|
+
import { spinner } from '@sanity/cli-core/ux';
|
|
5
|
+
import { validateOrganizationName } from '../../actions/organizations/validateOrganizationName.js';
|
|
6
|
+
import { promptForOrganizationName } from '../../prompts/promptForOrganizationName.js';
|
|
7
|
+
import { createOrganization } from '../../services/organizations.js';
|
|
8
|
+
import { organizationAliases } from '../../util/organizationAliases.js';
|
|
9
|
+
const createOrgDebug = subdebug('organizations:create');
|
|
10
|
+
export class CreateOrganizationCommand extends SanityCommand {
|
|
11
|
+
static description = 'Create a new organization';
|
|
12
|
+
static examples = [
|
|
13
|
+
{
|
|
14
|
+
command: '<%= config.bin %> <%= command.id %>',
|
|
15
|
+
description: 'Interactively create an organization'
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
command: '<%= config.bin %> <%= command.id %> --name "Acme Corp"',
|
|
19
|
+
description: 'Create an organization named "Acme Corp"'
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
command: '<%= config.bin %> <%= command.id %> --name "Acme Corp" --default-role member',
|
|
23
|
+
description: 'Create an organization with a default member role'
|
|
24
|
+
}
|
|
25
|
+
];
|
|
26
|
+
static flags = {
|
|
27
|
+
'default-role': Flags.string({
|
|
28
|
+
description: 'Default role assigned to new members',
|
|
29
|
+
required: false
|
|
30
|
+
}),
|
|
31
|
+
name: Flags.string({
|
|
32
|
+
description: 'Organization name',
|
|
33
|
+
required: false
|
|
34
|
+
})
|
|
35
|
+
};
|
|
36
|
+
static hiddenAliases = organizationAliases('create');
|
|
37
|
+
async run() {
|
|
38
|
+
const { 'default-role': defaultRoleFlag, name: nameFlag } = this.flags;
|
|
39
|
+
const defaultRole = defaultRoleFlag?.trim();
|
|
40
|
+
if (defaultRole === '') {
|
|
41
|
+
this.error('Default role cannot be empty', {
|
|
42
|
+
exit: 1
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
let name;
|
|
46
|
+
if (nameFlag === undefined) {
|
|
47
|
+
if (this.isUnattended()) {
|
|
48
|
+
this.error('Organization name is required. Provide it with the --name flag.', {
|
|
49
|
+
exit: 1
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
name = await promptForOrganizationName();
|
|
53
|
+
} else {
|
|
54
|
+
const trimmedName = nameFlag.trim();
|
|
55
|
+
const validation = validateOrganizationName(trimmedName);
|
|
56
|
+
if (validation !== true) {
|
|
57
|
+
this.error(validation, {
|
|
58
|
+
exit: 1
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
name = trimmedName;
|
|
62
|
+
}
|
|
63
|
+
const spin = spinner('Creating organization').start();
|
|
64
|
+
try {
|
|
65
|
+
const org = await createOrganization(name, defaultRole);
|
|
66
|
+
spin.succeed('Organization created');
|
|
67
|
+
this.log(`ID: ${org.id}`);
|
|
68
|
+
this.log(`Name: ${org.name}`);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
spin.fail();
|
|
71
|
+
createOrgDebug('Error creating organization', error);
|
|
72
|
+
this.error(`Failed to create organization: ${getErrorMessage(error)}`, {
|
|
73
|
+
exit: 1
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/organizations/create.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {validateOrganizationName} from '../../actions/organizations/validateOrganizationName.js'\nimport {promptForOrganizationName} from '../../prompts/promptForOrganizationName.js'\nimport {createOrganization} from '../../services/organizations.js'\nimport {organizationAliases} from '../../util/organizationAliases.js'\n\nconst createOrgDebug = subdebug('organizations:create')\n\nexport class CreateOrganizationCommand extends SanityCommand<typeof CreateOrganizationCommand> {\n static override description = 'Create a new organization'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively create an organization',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --name \"Acme Corp\"',\n description: 'Create an organization named \"Acme Corp\"',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --name \"Acme Corp\" --default-role member',\n description: 'Create an organization with a default member role',\n },\n ]\n\n static override flags = {\n 'default-role': Flags.string({\n description: 'Default role assigned to new members',\n required: false,\n }),\n name: Flags.string({\n description: 'Organization name',\n required: false,\n }),\n } satisfies FlagInput\n\n static override hiddenAliases = organizationAliases('create')\n\n public async run(): Promise<void> {\n const {'default-role': defaultRoleFlag, name: nameFlag} = this.flags\n\n const defaultRole = defaultRoleFlag?.trim()\n if (defaultRole === '') {\n this.error('Default role cannot be empty', {exit: 1})\n }\n\n let name: string\n if (nameFlag === undefined) {\n if (this.isUnattended()) {\n this.error('Organization name is required. Provide it with the --name flag.', {exit: 1})\n }\n name = await promptForOrganizationName()\n } else {\n const trimmedName = nameFlag.trim()\n const validation = validateOrganizationName(trimmedName)\n if (validation !== true) {\n this.error(validation, {exit: 1})\n }\n name = trimmedName\n }\n\n const spin = spinner('Creating organization').start()\n try {\n const org = await createOrganization(name, defaultRole)\n spin.succeed('Organization created')\n this.log(`ID: ${org.id}`)\n this.log(`Name: ${org.name}`)\n } catch (error) {\n spin.fail()\n createOrgDebug('Error creating organization', error)\n this.error(`Failed to create organization: ${getErrorMessage(error)}`, {\n exit: 1,\n })\n }\n }\n}\n"],"names":["Flags","SanityCommand","subdebug","getErrorMessage","spinner","validateOrganizationName","promptForOrganizationName","createOrganization","organizationAliases","createOrgDebug","CreateOrganizationCommand","description","examples","command","flags","string","required","name","hiddenAliases","run","defaultRoleFlag","nameFlag","defaultRole","trim","error","exit","undefined","isUnattended","trimmedName","validation","spin","start","org","succeed","log","id","fail"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AAEjC,SAAQC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACxD,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,OAAO,QAAO,sBAAqB;AAE3C,SAAQC,wBAAwB,QAAO,0DAAyD;AAChG,SAAQC,yBAAyB,QAAO,6CAA4C;AACpF,SAAQC,kBAAkB,QAAO,kCAAiC;AAClE,SAAQC,mBAAmB,QAAO,oCAAmC;AAErE,MAAMC,iBAAiBP,SAAS;AAEhC,OAAO,MAAMQ,kCAAkCT;IAC7C,OAAgBU,cAAc,4BAA2B;IAEzD,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IAED,OAAgBG,QAAQ;QACtB,gBAAgBd,MAAMe,MAAM,CAAC;YAC3BJ,aAAa;YACbK,UAAU;QACZ;QACAC,MAAMjB,MAAMe,MAAM,CAAC;YACjBJ,aAAa;YACbK,UAAU;QACZ;IACF,EAAqB;IAErB,OAAgBE,gBAAgBV,oBAAoB,UAAS;IAE7D,MAAaW,MAAqB;QAChC,MAAM,EAAC,gBAAgBC,eAAe,EAAEH,MAAMI,QAAQ,EAAC,GAAG,IAAI,CAACP,KAAK;QAEpE,MAAMQ,cAAcF,iBAAiBG;QACrC,IAAID,gBAAgB,IAAI;YACtB,IAAI,CAACE,KAAK,CAAC,gCAAgC;gBAACC,MAAM;YAAC;QACrD;QAEA,IAAIR;QACJ,IAAII,aAAaK,WAAW;YAC1B,IAAI,IAAI,CAACC,YAAY,IAAI;gBACvB,IAAI,CAACH,KAAK,CAAC,mEAAmE;oBAACC,MAAM;gBAAC;YACxF;YACAR,OAAO,MAAMX;QACf,OAAO;YACL,MAAMsB,cAAcP,SAASE,IAAI;YACjC,MAAMM,aAAaxB,yBAAyBuB;YAC5C,IAAIC,eAAe,MAAM;gBACvB,IAAI,CAACL,KAAK,CAACK,YAAY;oBAACJ,MAAM;gBAAC;YACjC;YACAR,OAAOW;QACT;QAEA,MAAME,OAAO1B,QAAQ,yBAAyB2B,KAAK;QACnD,IAAI;YACF,MAAMC,MAAM,MAAMzB,mBAAmBU,MAAMK;YAC3CQ,KAAKG,OAAO,CAAC;YACb,IAAI,CAACC,GAAG,CAAC,CAAC,MAAM,EAAEF,IAAIG,EAAE,EAAE;YAC1B,IAAI,CAACD,GAAG,CAAC,CAAC,MAAM,EAAEF,IAAIf,IAAI,EAAE;QAC9B,EAAE,OAAOO,OAAO;YACdM,KAAKM,IAAI;YACT3B,eAAe,+BAA+Be;YAC9C,IAAI,CAACA,KAAK,CAAC,CAAC,+BAA+B,EAAErB,gBAAgBqB,QAAQ,EAAE;gBACrEC,MAAM;YACR;QACF;IACF;AACF"}
|