UNPKG

sb-mig

Version:

CLI to rule the world. (and handle stuff related to Storyblok CMS)

237 lines (236 loc) 10.8 kB
import path from "path"; import { migrateAllComponentsDataInStories, migrateProvidedComponentsDataInStories, } from "../../api/data-migration/component-data-migration.js"; import { buildStoryBackupBaseName } from "../../api/data-migration/file-naming.js"; import { parseMigrationComponentAliasFlags, parseMigrationComponentOverrideFlags, } from "../../api/data-migration/migration-component-scope.js"; import { managementApi } from "../../api/managementApi.js"; import { backupStories } from "../../api/stories/backup.js"; import { parsePublishLanguagesOption } from "../../api/stories/stories.js"; import { createAndSaveToFile } from "../../utils/files.js"; import Logger from "../../utils/logger.js"; import { apiConfig } from "../api-config.js"; import { askForConfirmation } from "../helpers.js"; import { isItFactory, unpackElements, getFrom, getTo, } from "../utils/cli-utils.js"; const MIGRATE_COMMANDS = { content: "content", presets: "presets", }; const normalizeMigrationFlags = (migrationFlag) => { if (Array.isArray(migrationFlag)) { return migrationFlag.filter(Boolean); } if (typeof migrationFlag === "string" && migrationFlag.length > 0) { return [migrationFlag]; } return []; }; export const migrate = async (props) => { const { input, flags } = props; const command = input[1]; const rules = { empty: [], all: ["all", "migrateFrom"], }; const isIt = isItFactory(flags, rules, [ "to", "from", "fromFilePath", "pageId", "migration", "migrationComponentAlias", "migrationComponents", "yes", "withSlug", "startsWith", "dryRun", "publish", "publishLanguages", "fileName", ]); Logger.warning(`This feature is in BETA. Use it at your own risk. The API might change in the future. (Probably in a standard Prisma like migration way)`); switch (command) { case MIGRATE_COMMANDS.content: { Logger.log(`Migrating content with command: ${command}`); const fromFilePath = flags["fromFilePath"]; const migrateFromFlag = flags["migrateFrom"]; const fromFallback = migrateFromFlag === "file" && fromFilePath ? path.parse(fromFilePath).name : undefined; const from = flags["from"] || fromFallback || getFrom(flags, apiConfig); const to = getTo(flags, apiConfig); const migrationConfigs = normalizeMigrationFlags(flags["migration"]); const migrationComponentAliases = parseMigrationComponentAliasFlags(flags["migrationComponentAlias"]); const migrationComponentOverrides = parseMigrationComponentOverrideFlags(flags["migrationComponents"]); const dryRun = flags["dryRun"]; const publish = Boolean(flags["publish"]); const publishLanguagesFlag = flags["publishLanguages"]; const publishLanguages = publishLanguagesFlag ? parsePublishLanguagesOption(publishLanguagesFlag) : undefined; const fileName = flags["fileName"]; const withSlugFlag = flags["withSlug"]; const withSlug = Array.isArray(withSlugFlag) ? withSlugFlag : withSlugFlag ? [withSlugFlag] : undefined; const startsWith = flags["startsWith"] || undefined; if (migrationConfigs.length === 0) { throw new Error("Missing migration config. Pass at least one --migration value."); } if (!publish && publishLanguagesFlag) { throw new Error("--publishLanguages requires --publish for 'migrate content'."); } if (isIt("empty")) { const componentsToMigrate = unpackElements(input) || [""]; const migrateFrom = "space"; const runMigration = async () => { Logger.warning("Preparing to migrate..."); if (!dryRun) { await backupStories({ filename: buildStoryBackupBaseName({ from, fileName, }), suffix: ".sb.stories", spaceId: from, }, apiConfig); } await migrateProvidedComponentsDataInStories({ itemType: "story", from, to, migrateFrom, componentsToMigrate, migrationConfig: migrationConfigs, migrationComponentAliases, migrationComponentOverrides, filters: { withSlug, startsWith }, dryRun, publish, publishLanguages, fromFilePath, fileName, }, apiConfig); }; if (dryRun) { await runMigration(); } else { await askForConfirmation("Are you sure you want to MIGRATE content (stories) in your space ? (it will overwrite stories)", runMigration, () => { Logger.warning("Migration not started, exiting the program..."); }, flags["yes"]); } } else if (isIt("all")) { const migrateFrom = flags["migrateFrom"]; const runMigration = async () => { Logger.warning("Preparing to migrate..."); await migrateAllComponentsDataInStories({ itemType: "story", from, to, migrateFrom, migrationConfig: migrationConfigs, migrationComponentAliases, migrationComponentOverrides, filters: { withSlug, startsWith }, dryRun, publish, publishLanguages, fromFilePath, fileName, }, apiConfig); }; if (dryRun) { await runMigration(); } else { await askForConfirmation("Are you sure you want to MIGRATE content (stories) in your space ? (it will overwrite stories)", runMigration, () => { Logger.warning("Migration not started, exiting the program..."); }, flags["yes"]); } } else { Logger.error("Wrong combination of flags. check help for more info."); console.log("Passed flags: "); console.log(flags); } break; } case MIGRATE_COMMANDS.presets: { Logger.log(`Migrating content with command: ${command}`); const migrationConfigs = normalizeMigrationFlags(flags["migration"]); const migrationComponentAliases = parseMigrationComponentAliasFlags(flags["migrationComponentAlias"]); const migrationComponentOverrides = parseMigrationComponentOverrideFlags(flags["migrationComponents"]); const fromFilePath = flags["fromFilePath"]; const migrateFromFlag = flags["migrateFrom"]; const fromFallback = migrateFromFlag === "file" && fromFilePath ? path.parse(fromFilePath).name : undefined; const from = flags["from"] || fromFallback || getFrom(flags, apiConfig); const to = getTo(flags, apiConfig); const publish = Boolean(flags["publish"]); const publishLanguagesFlag = flags["publishLanguages"]; if (migrationConfigs.length === 0) { throw new Error("Missing migration config. Pass exactly one --migration value for presets."); } if (publish) { throw new Error("--publish is only supported for 'migrate content'. Presets cannot be published."); } if (publishLanguagesFlag) { throw new Error("--publishLanguages is only supported for 'migrate content'. Presets cannot be published."); } if (migrationConfigs.length > 1) { throw new Error("Multiple --migration values are currently supported only for 'migrate content'. Presets support a single migration config."); } console.log("Migrating with presets"); if (isIt("all")) { const migrateFrom = flags["migrateFrom"]; const dryRun = flags["dryRun"]; const fileName = flags["fileName"]; const runMigration = async () => { Logger.warning("Preparing to migrate..."); if (!dryRun) { const response = await managementApi.presets.getAllPresets(apiConfig); await createAndSaveToFile({ filename: "presets-backup", res: response, }, apiConfig); } await migrateAllComponentsDataInStories({ itemType: "preset", from, to, migrateFrom, migrationConfig: migrationConfigs[0], migrationComponentAliases, migrationComponentOverrides, dryRun, fromFilePath, fileName, }, apiConfig); }; if (dryRun) { await runMigration(); } else { await askForConfirmation("Are you sure you want to MIGRATE presets in your space ? (it will overwrite them)", runMigration, () => { Logger.warning("Migration not started, exiting the program..."); }, flags["yes"]); } } else { Logger.error("Wrong combination of flags. check help for more info."); console.log("Passed flags: "); console.log(flags); } break; } default: console.log(`no command like that: ${command}`); } };