UNPKG

@lucidcms/core

Version:

The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.

1 lines 10.1 kB
{"version":3,"file":"migrate-status.mjs","names":["cliLogger"],"sources":["../../../../src/libs/cli/commands/migrate-status.ts"],"sourcesContent":["import { sql } from \"kysely\";\nimport type { Config, EnvironmentVariables } from \"../../../types.js\";\nimport createServiceContext from \"../../../utils/services/create-service-context.js\";\nimport assessMigrationPlans from \"../../collection/migration/assess-migration-plan.js\";\nimport type {\n\tMigrationAssessment,\n\tMigrationRisk,\n} from \"../../collection/migration/types.js\";\nimport planCollectionMigrations from \"../../collection/plan-collection-migrations.js\";\nimport loadConfigFile from \"../../config/load-config-file.js\";\nimport { prepareExternalMigrations } from \"../../db/load-external-migrations.js\";\nimport type { DatabaseConnection } from \"../../db/types.js\";\nimport prepareTranslations from \"../../i18n/prepare-translations.js\";\nimport {\n\tstartLoggerBuffering,\n\tstopLoggerBuffering,\n} from \"../../logger/index.js\";\nimport type { AdapterRuntimeContext } from \"../../runtime/types.js\";\nimport cliLogger from \"../logger.js\";\nimport { describeMigrationRiskReason } from \"../services/migration-report.js\";\nimport validateEnvVars from \"../services/validate-env-vars.js\";\n\n/**\n * A read-only preflight that reports what `migrate` would do and whether the\n * migration history is healthy. With `--check` it exits non-zero when\n * migrations are pending or executed migrations are missing from the\n * registered set, so it can gate CI and deploy pipelines.\n */\nconst migrateStatusCommand = async (options?: {\n\tcheck?: boolean;\n\tremote?: boolean;\n}) => {\n\tlet config: Config | undefined;\n\tlet env: EnvironmentVariables | undefined;\n\tlet runtimeContext: AdapterRuntimeContext | undefined;\n\tlet database: DatabaseConnection | undefined;\n\n\ttry {\n\t\tstartLoggerBuffering();\n\t\tconst startTime = cliLogger.startTimer();\n\n\t\tconst res = await loadConfigFile({\n\t\t\tprepareRuntime: true,\n\t\t});\n\t\tconfig = res.config;\n\t\tenv = res.env;\n\t\truntimeContext = res.runtimeContext;\n\t\tconst { translationStore } = await prepareTranslations({\n\t\t\tconfig,\n\t\t\tprojectRoot: res.projectRoot,\n\t\t});\n\n\t\tconst envValid = await validateEnvVars({\n\t\t\tenvSchema: res.envSchema,\n\t\t\tenv: res.env,\n\t\t});\n\t\tif (!envValid) {\n\t\t\tawait stopLoggerBuffering();\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\tcliLogger.info(\"Checking the migration status\");\n\n\t\tawait prepareExternalMigrations(config, res.projectRoot);\n\t\tdatabase = await config.db.connect(env);\n\n\t\t//* database migration status\n\t\tconst registeredMigrations = Object.keys(config.db.migrations).sort();\n\t\tlet executedMigrations: string[] = [];\n\t\ttry {\n\t\t\tconst executedRows = await sql<{ name: string }>`\n\t\t\t\tSELECT name FROM kysely_migration\n\t\t\t`.execute(database.client);\n\t\t\texecutedMigrations = executedRows.rows.map((row) => row.name);\n\t\t} catch (_) {\n\t\t\t//* the migration table doesnt exist yet - no migrations have run\n\t\t}\n\n\t\tconst pendingMigrations = registeredMigrations.filter(\n\t\t\t(name) => !executedMigrations.includes(name),\n\t\t);\n\t\tconst missingMigrations = executedMigrations.filter(\n\t\t\t(name) => !registeredMigrations.includes(name),\n\t\t);\n\n\t\t//* collection migration status\n\t\tconst serviceContext = createServiceContext({\n\t\t\tconfig,\n\t\t\tdatabase,\n\t\t\ttranslationStore,\n\t\t\tenv,\n\t\t\truntimeContext,\n\t\t});\n\t\tconst collectionResult = await planCollectionMigrations(serviceContext);\n\n\t\tlet pendingCollections: Array<{\n\t\t\tcollectionKey: string;\n\t\t\tassessment: MigrationAssessment;\n\t\t}> = [];\n\t\tlet collectionCheckError: string | undefined;\n\t\tif (collectionResult.error) {\n\t\t\tcollectionCheckError =\n\t\t\t\tserviceContext.translate.english(collectionResult.error.message) ||\n\t\t\t\t\"Unknown error\";\n\t\t} else {\n\t\t\tpendingCollections = collectionResult.data.collections\n\t\t\t\t.map(({ migrationPlan }) => ({\n\t\t\t\t\tcollectionKey: migrationPlan.collectionKey,\n\t\t\t\t\tassessment: assessMigrationPlans([migrationPlan]),\n\t\t\t\t}))\n\t\t\t\t.filter(({ assessment }) => assessment.reasons.length > 0);\n\t\t}\n\n\t\t//* report\n\t\tcliLogger.info(`Found ${executedMigrations.length} applied migration(s)`);\n\n\t\tif (pendingMigrations.length === 0) {\n\t\t\tcliLogger.success(\"No database schema migrations are pending\");\n\t\t} else {\n\t\t\tcliLogger.warn(\n\t\t\t\t`${pendingMigrations.length} database schema migration(s) are pending`,\n\t\t\t);\n\t\t\tfor (const name of pendingMigrations) {\n\t\t\t\tcliLogger.log(cliLogger.color.yellow(name), { indent: 2 });\n\t\t\t}\n\t\t}\n\n\t\tif (collectionCheckError) {\n\t\t\tcliLogger.warn(\n\t\t\t\t`Could not check collection migration status: ${collectionCheckError}`,\n\t\t\t);\n\t\t} else if (pendingCollections.length === 0) {\n\t\t\tcliLogger.success(\"No collection/brick table migrations are needed\");\n\t\t} else {\n\t\t\tcliLogger.warn(\n\t\t\t\t`${pendingCollections.length} collection(s) need table migrations`,\n\t\t\t);\n\t\t\tfor (const risk of [\n\t\t\t\t\"safe\",\n\t\t\t\t\"warning\",\n\t\t\t\t\"destructive\",\n\t\t\t] satisfies MigrationRisk[]) {\n\t\t\t\tconst grouped = pendingCollections.filter(\n\t\t\t\t\t(item) => item.assessment.risk === risk,\n\t\t\t\t);\n\t\t\t\tif (grouped.length === 0) continue;\n\t\t\t\tcliLogger.log(`${risk.toUpperCase()} (${grouped.length})`, {\n\t\t\t\t\tindent: 2,\n\t\t\t\t});\n\t\t\t\tfor (const item of grouped) {\n\t\t\t\t\tcliLogger.log(cliLogger.color.yellow(item.collectionKey), {\n\t\t\t\t\t\tindent: 4,\n\t\t\t\t\t});\n\t\t\t\t\tfor (const reason of item.assessment.reasons) {\n\t\t\t\t\t\tcliLogger.log(describeMigrationRiskReason(reason), { indent: 6 });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (missingMigrations.length > 0) {\n\t\t\tcliLogger.error(\n\t\t\t\t`${missingMigrations.length} previously executed migration(s) are no longer registered`,\n\t\t\t);\n\t\t\tfor (const name of missingMigrations) {\n\t\t\t\tcliLogger.log(cliLogger.color.red(name), { indent: 2 });\n\t\t\t}\n\t\t\tcliLogger.info(\n\t\t\t\t\"If you removed a plugin or migration file, restore it so its migrations can be run or rolled back.\",\n\t\t\t);\n\t\t}\n\n\t\tconst hasPendingWork =\n\t\t\tpendingMigrations.length > 0 ||\n\t\t\tpendingCollections.length > 0 ||\n\t\t\tcollectionCheckError !== undefined;\n\t\tconst unhealthy = missingMigrations.length > 0;\n\n\t\tconst endTime = startTime();\n\t\tcliLogger.log(\n\t\t\tcliLogger.createBadge(\"LUCID CMS\"),\n\t\t\thasPendingWork || unhealthy\n\t\t\t\t? \"Migration status checked with\"\n\t\t\t\t: \"Migration status checked\",\n\t\t\thasPendingWork || unhealthy\n\t\t\t\t? cliLogger.color.yellow(\"outstanding work\")\n\t\t\t\t: cliLogger.color.green(\"successfully\"),\n\t\t\t\"in\",\n\t\t\tcliLogger.color.green(cliLogger.formatMilliseconds(endTime)),\n\t\t\t{\n\t\t\t\tspaceAfter: true,\n\t\t\t\tspaceBefore: true,\n\t\t\t},\n\t\t);\n\n\t\tawait database.destroy();\n\t\tdatabase = undefined;\n\n\t\tawait stopLoggerBuffering();\n\t\tprocess.exit(options?.check && (hasPendingWork || unhealthy) ? 1 : 0);\n\t} catch (error) {\n\t\tawait database?.destroy();\n\t\tif (error instanceof Error) {\n\t\t\tcliLogger.errorInstance(error, \"Migration status failed\");\n\t\t} else {\n\t\t\tcliLogger.error(\"Migration status failed\", \"Unknown error\");\n\t\t}\n\t\tawait stopLoggerBuffering();\n\t\tprocess.exit(1);\n\t}\n};\n\nexport default migrateStatusCommand;\n"],"mappings":"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"}