@actuate-media/cli 0.4.1 → 0.5.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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +69 -12
- package/CHANGELOG.md +58 -0
- package/dist/__tests__/db-init.test.d.ts +2 -0
- package/dist/__tests__/db-init.test.d.ts.map +1 -0
- package/dist/__tests__/db-init.test.js +127 -0
- package/dist/__tests__/db-init.test.js.map +1 -0
- package/dist/__tests__/db-sync.test.d.ts +2 -0
- package/dist/__tests__/db-sync.test.d.ts.map +1 -0
- package/dist/__tests__/db-sync.test.js +136 -0
- package/dist/__tests__/db-sync.test.js.map +1 -0
- package/dist/__tests__/deployment-diagnostics.test.js.map +1 -1
- package/dist/__tests__/init.test.js.map +1 -1
- package/dist/__tests__/schema-fragment.test.js +1 -1
- package/dist/__tests__/schema-fragment.test.js.map +1 -1
- package/dist/__tests__/seed.test.js.map +1 -1
- package/dist/commands/db-init.d.ts +19 -2
- package/dist/commands/db-init.d.ts.map +1 -1
- package/dist/commands/db-init.js +128 -306
- package/dist/commands/db-init.js.map +1 -1
- package/dist/commands/db-status.d.ts +1 -1
- package/dist/commands/db-status.d.ts.map +1 -1
- package/dist/commands/db-status.js +33 -33
- package/dist/commands/db-status.js.map +1 -1
- package/dist/commands/db-sync.d.ts +31 -0
- package/dist/commands/db-sync.d.ts.map +1 -0
- package/dist/commands/db-sync.js +195 -0
- package/dist/commands/db-sync.js.map +1 -0
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +48 -41
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/export.d.ts +1 -1
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +32 -32
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/generate.d.ts +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +8 -8
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/import.d.ts +1 -1
- package/dist/commands/import.d.ts.map +1 -1
- package/dist/commands/import.js +55 -58
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate.d.ts +1 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +18 -24
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/seed.d.ts +1 -1
- package/dist/commands/seed.d.ts.map +1 -1
- package/dist/commands/seed.js +156 -157
- package/dist/commands/seed.js.map +1 -1
- package/dist/commands/update-check.d.ts +1 -1
- package/dist/commands/update-check.d.ts.map +1 -1
- package/dist/commands/update-check.js +34 -27
- package/dist/commands/update-check.js.map +1 -1
- package/dist/commands/upgrade.d.ts +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +46 -34
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/deployment/diagnostics.d.ts.map +1 -1
- package/dist/deployment/diagnostics.js +7 -2
- package/dist/deployment/diagnostics.js.map +1 -1
- package/dist/index.js +17 -15
- package/dist/index.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +5 -5
- package/dist/utils/logger.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/db-init.test.ts +155 -0
- package/src/__tests__/db-sync.test.ts +167 -0
- package/src/__tests__/deployment-diagnostics.test.ts +68 -60
- package/src/__tests__/init.test.ts +17 -17
- package/src/__tests__/schema-fragment.test.ts +29 -25
- package/src/__tests__/seed.test.ts +25 -25
- package/src/commands/db-init.ts +146 -319
- package/src/commands/db-status.ts +70 -68
- package/src/commands/db-sync.ts +227 -0
- package/src/commands/doctor.ts +102 -88
- package/src/commands/export.ts +65 -75
- package/src/commands/generate.ts +14 -16
- package/src/commands/import.ts +125 -140
- package/src/commands/init.ts +14 -14
- package/src/commands/migrate.ts +29 -35
- package/src/commands/seed.ts +294 -300
- package/src/commands/update-check.ts +77 -72
- package/src/commands/upgrade.ts +100 -85
- package/src/deployment/diagnostics.ts +86 -72
- package/src/index.ts +32 -30
- package/src/utils/logger.ts +10 -10
|
@@ -1,120 +1,122 @@
|
|
|
1
|
-
import { Command } from
|
|
2
|
-
import { execSync } from
|
|
3
|
-
import { readFile, access } from
|
|
4
|
-
import { resolve } from
|
|
5
|
-
import ora from
|
|
6
|
-
import chalk from
|
|
7
|
-
import { logger } from
|
|
8
|
-
import { REQUIRED_CMS_MODELS } from
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { execSync } from 'node:child_process'
|
|
3
|
+
import { readFile, access } from 'node:fs/promises'
|
|
4
|
+
import { resolve } from 'node:path'
|
|
5
|
+
import ora from 'ora'
|
|
6
|
+
import chalk from 'chalk'
|
|
7
|
+
import { logger } from '../utils/logger.js'
|
|
8
|
+
import { REQUIRED_CMS_MODELS } from '../deployment/diagnostics.js'
|
|
9
9
|
|
|
10
|
-
const CMS_SCHEMA_MARKER =
|
|
10
|
+
const CMS_SCHEMA_MARKER = '// ── Actuate CMS models'
|
|
11
11
|
|
|
12
12
|
async function fileExists(filePath: string): Promise<boolean> {
|
|
13
13
|
try {
|
|
14
|
-
await access(filePath)
|
|
15
|
-
return true
|
|
14
|
+
await access(filePath)
|
|
15
|
+
return true
|
|
16
16
|
} catch {
|
|
17
|
-
return false
|
|
17
|
+
return false
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function registerDbStatusCommand(program: Command): void {
|
|
22
22
|
program
|
|
23
|
-
.command(
|
|
24
|
-
.description(
|
|
25
|
-
.option(
|
|
23
|
+
.command('db:status')
|
|
24
|
+
.description('Check which Actuate CMS models are present in your Prisma schema')
|
|
25
|
+
.option('--schema <path>', 'Path to schema.prisma', 'prisma/schema.prisma')
|
|
26
26
|
.action(async (opts: { schema: string }) => {
|
|
27
|
-
const schemaPath = resolve(process.cwd(), opts.schema)
|
|
27
|
+
const schemaPath = resolve(process.cwd(), opts.schema)
|
|
28
28
|
|
|
29
29
|
if (!(await fileExists(schemaPath))) {
|
|
30
|
-
logger.error(`Schema file not found at ${schemaPath}`)
|
|
31
|
-
logger.info(
|
|
32
|
-
process.exitCode = 1
|
|
33
|
-
return
|
|
30
|
+
logger.error(`Schema file not found at ${schemaPath}`)
|
|
31
|
+
logger.info('Run `npx prisma init` first, or specify --schema <path>.')
|
|
32
|
+
process.exitCode = 1
|
|
33
|
+
return
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const spinner = ora(
|
|
36
|
+
const spinner = ora('Checking schema...').start()
|
|
37
37
|
|
|
38
|
-
let content: string
|
|
38
|
+
let content: string
|
|
39
39
|
try {
|
|
40
|
-
content = await readFile(schemaPath,
|
|
40
|
+
content = await readFile(schemaPath, 'utf-8')
|
|
41
41
|
} catch (err) {
|
|
42
|
-
spinner.fail(
|
|
43
|
-
logger.error(err instanceof Error ? err.message : String(err))
|
|
44
|
-
process.exitCode = 1
|
|
45
|
-
return
|
|
42
|
+
spinner.fail('Failed to read schema file.')
|
|
43
|
+
logger.error(err instanceof Error ? err.message : String(err))
|
|
44
|
+
process.exitCode = 1
|
|
45
|
+
return
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
spinner.stop()
|
|
48
|
+
spinner.stop()
|
|
49
49
|
|
|
50
|
-
const hasMarker = content.includes(CMS_SCHEMA_MARKER)
|
|
51
|
-
const modelRegex = /^model\s+(\w+)\s*\{/gm
|
|
52
|
-
const schemaModels = new Set<string>()
|
|
53
|
-
let match
|
|
50
|
+
const hasMarker = content.includes(CMS_SCHEMA_MARKER)
|
|
51
|
+
const modelRegex = /^model\s+(\w+)\s*\{/gm
|
|
52
|
+
const schemaModels = new Set<string>()
|
|
53
|
+
let match
|
|
54
54
|
while ((match = modelRegex.exec(content)) !== null) {
|
|
55
|
-
schemaModels.add(match[1]!)
|
|
55
|
+
schemaModels.add(match[1]!)
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
console.log()
|
|
59
|
-
console.log(chalk.bold(
|
|
60
|
-
console.log(chalk.dim(
|
|
61
|
-
console.log()
|
|
58
|
+
console.log()
|
|
59
|
+
console.log(chalk.bold(' Actuate CMS Model Status'))
|
|
60
|
+
console.log(chalk.dim(' ─────────────────────────────────'))
|
|
61
|
+
console.log()
|
|
62
62
|
|
|
63
|
-
let present = 0
|
|
64
|
-
let missing = 0
|
|
63
|
+
let present = 0
|
|
64
|
+
let missing = 0
|
|
65
65
|
|
|
66
66
|
for (const model of REQUIRED_CMS_MODELS) {
|
|
67
67
|
if (schemaModels.has(model)) {
|
|
68
|
-
console.log(` ${chalk.green(
|
|
69
|
-
present
|
|
68
|
+
console.log(` ${chalk.green('✓')} ${model}`)
|
|
69
|
+
present++
|
|
70
70
|
} else {
|
|
71
|
-
console.log(` ${chalk.red(
|
|
72
|
-
missing
|
|
71
|
+
console.log(` ${chalk.red('✗')} ${model} ${chalk.dim('— missing')}`)
|
|
72
|
+
missing++
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
console.log()
|
|
77
|
-
console.log(chalk.dim(
|
|
78
|
-
console.log(
|
|
76
|
+
console.log()
|
|
77
|
+
console.log(chalk.dim(' ─────────────────────────────────'))
|
|
78
|
+
console.log(
|
|
79
|
+
` ${chalk.green(present)} present, ${missing > 0 ? chalk.red(missing) : chalk.green(missing)} missing`,
|
|
80
|
+
)
|
|
79
81
|
|
|
80
82
|
if (!hasMarker) {
|
|
81
|
-
console.log()
|
|
82
|
-
logger.warn(
|
|
83
|
-
logger.info(
|
|
83
|
+
console.log()
|
|
84
|
+
logger.warn('CMS schema marker not found. Models may have been added manually.')
|
|
85
|
+
logger.info('Run `actuate db:init` to add CMS models with proper markers.')
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
if (missing > 0) {
|
|
87
|
-
console.log()
|
|
88
|
-
logger.info(
|
|
89
|
+
console.log()
|
|
90
|
+
logger.info('Run `actuate db:init` to add missing models.')
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
let dbConnected = false
|
|
93
|
+
let dbConnected = false
|
|
92
94
|
try {
|
|
93
|
-
spinner.start(
|
|
94
|
-
execSync(
|
|
95
|
-
input:
|
|
96
|
-
stdio: [
|
|
95
|
+
spinner.start('Checking database connection...')
|
|
96
|
+
execSync('npx prisma db execute --stdin --schema ' + JSON.stringify(schemaPath), {
|
|
97
|
+
input: 'SELECT 1;',
|
|
98
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
97
99
|
cwd: process.cwd(),
|
|
98
|
-
})
|
|
99
|
-
spinner.succeed(
|
|
100
|
-
dbConnected = true
|
|
100
|
+
})
|
|
101
|
+
spinner.succeed('Database connection OK.')
|
|
102
|
+
dbConnected = true
|
|
101
103
|
} catch {
|
|
102
|
-
spinner.warn(
|
|
104
|
+
spinner.warn('Could not connect to database. Check DATABASE_URL.')
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
if (dbConnected && missing === 0) {
|
|
106
108
|
try {
|
|
107
|
-
spinner.start(
|
|
108
|
-
spinner.stop()
|
|
109
|
-
execSync(
|
|
110
|
-
stdio:
|
|
109
|
+
spinner.start('Checking migration status...')
|
|
110
|
+
spinner.stop()
|
|
111
|
+
execSync('npx prisma migrate status', {
|
|
112
|
+
stdio: 'inherit',
|
|
111
113
|
cwd: process.cwd(),
|
|
112
|
-
})
|
|
114
|
+
})
|
|
113
115
|
} catch {
|
|
114
|
-
logger.warn(
|
|
116
|
+
logger.warn('Could not check migration status.')
|
|
115
117
|
}
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
console.log()
|
|
119
|
-
})
|
|
120
|
+
console.log()
|
|
121
|
+
})
|
|
120
122
|
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { access, cp, mkdir, readFile, readdir, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { existsSync } from 'node:fs'
|
|
4
|
+
import { dirname, join, resolve } from 'node:path'
|
|
5
|
+
import { createRequire } from 'node:module'
|
|
6
|
+
import ora from 'ora'
|
|
7
|
+
import { logger } from '../utils/logger.js'
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url)
|
|
10
|
+
|
|
11
|
+
// Marker that identifies a schema this CLI/scaffolder owns (auto-synced from
|
|
12
|
+
// cms-core). We refuse to overwrite a schema lacking it unless --force, so a
|
|
13
|
+
// hand-customized schema is never silently clobbered.
|
|
14
|
+
const AUTO_SYNCED_MARKER = 'AUTO-SYNCED from @actuate-media/cms-core'
|
|
15
|
+
|
|
16
|
+
// Must match create-actuate-cms/scripts/sync-prisma-assets.ts `SCAFFOLD_SCHEMA_HEADER`
|
|
17
|
+
// so re-syncing a scaffolded project is idempotent (no spurious diffs).
|
|
18
|
+
const SCHEMA_HEADER = `// ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
// Actuate CMS — Prisma schema
|
|
20
|
+
//
|
|
21
|
+
// AUTO-SYNCED from @actuate-media/cms-core. Do NOT edit the model definitions
|
|
22
|
+
// by hand — they must match the bundled migrations and cms-core's API layer.
|
|
23
|
+
// (Generated by \`actuate db:sync\`)
|
|
24
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
generator client {
|
|
27
|
+
provider = "prisma-client"
|
|
28
|
+
output = "../generated/prisma"
|
|
29
|
+
previewFeatures = ["fullTextSearchPostgres", "relationJoins"]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
datasource db {
|
|
33
|
+
provider = "postgresql"
|
|
34
|
+
}`
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Build the consumer's `schema.prisma` from cms-core's canonical schema: strip
|
|
38
|
+
* cms-core's own generator/datasource blocks and prepend the consumer header
|
|
39
|
+
* (client output `../generated/prisma`, no datasource `url` — supplied by
|
|
40
|
+
* `prisma.config.ts`). Pure for testing. Mirrors the scaffolder's builder.
|
|
41
|
+
*/
|
|
42
|
+
export function buildConsumerSchema(coreSchemaSource: string): string {
|
|
43
|
+
const datasourceMatch = coreSchemaSource.match(/datasource\s+\w+\s*\{[\s\S]*?\}/)
|
|
44
|
+
if (!datasourceMatch || datasourceMatch.index === undefined) {
|
|
45
|
+
throw new Error('Could not locate the `datasource` block in cms-core schema.prisma')
|
|
46
|
+
}
|
|
47
|
+
const body = coreSchemaSource
|
|
48
|
+
.slice(datasourceMatch.index + datasourceMatch[0].length)
|
|
49
|
+
.replace(/^\s+/, '')
|
|
50
|
+
|
|
51
|
+
if (!/@@map\("actuate_users"\)/.test(body)) {
|
|
52
|
+
throw new Error('cms-core schema body is missing `@@map("actuate_users")` — aborting sync')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return `${SCHEMA_HEADER}\n\n${body.trimEnd()}\n`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Resolve the installed `@actuate-media/cms-core` package's `prisma/` directory
|
|
60
|
+
* (the source of truth for schema + migrations). Overridable for tests.
|
|
61
|
+
*/
|
|
62
|
+
export let resolveCmsCorePrismaDir = (): string | null => {
|
|
63
|
+
try {
|
|
64
|
+
const exported = require.resolve('@actuate-media/cms-core/prisma/schema')
|
|
65
|
+
return dirname(exported)
|
|
66
|
+
} catch {
|
|
67
|
+
/* exports map missing the subpath — try the main entry */
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const mainEntry = require.resolve('@actuate-media/cms-core')
|
|
71
|
+
return join(dirname(mainEntry), '..', 'prisma')
|
|
72
|
+
} catch {
|
|
73
|
+
return null
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const defaultPrismaDirResolver = resolveCmsCorePrismaDir
|
|
78
|
+
|
|
79
|
+
export function setCmsCorePrismaDirResolver(resolver: typeof resolveCmsCorePrismaDir): void {
|
|
80
|
+
resolveCmsCorePrismaDir = resolver
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function resetCmsCorePrismaDirResolver(): void {
|
|
84
|
+
resolveCmsCorePrismaDir = defaultPrismaDirResolver
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function listMigrationDirs(dir: string): Promise<string[]> {
|
|
88
|
+
try {
|
|
89
|
+
const entries = await readdir(dir, { withFileTypes: true })
|
|
90
|
+
return entries.filter((e) => e.isDirectory()).map((e) => e.name)
|
|
91
|
+
} catch {
|
|
92
|
+
return []
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface DbSyncOptions {
|
|
97
|
+
schema: string
|
|
98
|
+
force?: boolean
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface DbSyncResult {
|
|
102
|
+
schemaWritten: boolean
|
|
103
|
+
migrationsAdded: string[]
|
|
104
|
+
skippedReason?: string
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Core sync logic, separated from CLI plumbing for testing. Additive for
|
|
109
|
+
* migrations (never deletes existing ones — they are immutable history), and
|
|
110
|
+
* guarded for the schema (won't overwrite a non-auto-synced schema without
|
|
111
|
+
* `force`).
|
|
112
|
+
*/
|
|
113
|
+
export async function syncPrismaAssets(
|
|
114
|
+
consumerSchemaPath: string,
|
|
115
|
+
corePrismaDir: string,
|
|
116
|
+
opts: { force?: boolean } = {},
|
|
117
|
+
): Promise<DbSyncResult> {
|
|
118
|
+
const coreSchemaPath = join(corePrismaDir, 'schema.prisma')
|
|
119
|
+
const coreSchema = await readFile(coreSchemaPath, 'utf-8')
|
|
120
|
+
const nextSchema = buildConsumerSchema(coreSchema)
|
|
121
|
+
|
|
122
|
+
const consumerDir = dirname(consumerSchemaPath)
|
|
123
|
+
const consumerMigrationsDir = join(consumerDir, 'migrations')
|
|
124
|
+
const coreMigrationsDir = join(corePrismaDir, 'migrations')
|
|
125
|
+
|
|
126
|
+
// Guard the schema overwrite.
|
|
127
|
+
let schemaWritten = false
|
|
128
|
+
let skippedReason: string | undefined
|
|
129
|
+
const existing = existsSync(consumerSchemaPath)
|
|
130
|
+
? await readFile(consumerSchemaPath, 'utf-8')
|
|
131
|
+
: null
|
|
132
|
+
|
|
133
|
+
if (existing && !existing.includes(AUTO_SYNCED_MARKER) && !opts.force) {
|
|
134
|
+
skippedReason =
|
|
135
|
+
'schema.prisma is not an auto-synced Actuate schema (no AUTO-SYNCED marker). Re-run with --force to overwrite it.'
|
|
136
|
+
} else {
|
|
137
|
+
await mkdir(consumerDir, { recursive: true })
|
|
138
|
+
if (existing !== nextSchema) {
|
|
139
|
+
await writeFile(consumerSchemaPath, nextSchema)
|
|
140
|
+
schemaWritten = true
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Additively copy any cms-core migrations the consumer doesn't already have.
|
|
145
|
+
const coreMigrations = await listMigrationDirs(coreMigrationsDir)
|
|
146
|
+
const existingMigrations = new Set(await listMigrationDirs(consumerMigrationsDir))
|
|
147
|
+
const migrationsAdded: string[] = []
|
|
148
|
+
|
|
149
|
+
if (coreMigrations.length > 0) {
|
|
150
|
+
await mkdir(consumerMigrationsDir, { recursive: true })
|
|
151
|
+
const lockSrc = join(coreMigrationsDir, 'migration_lock.toml')
|
|
152
|
+
const lockDest = join(consumerMigrationsDir, 'migration_lock.toml')
|
|
153
|
+
if (existsSync(lockSrc) && !existsSync(lockDest)) {
|
|
154
|
+
await cp(lockSrc, lockDest)
|
|
155
|
+
}
|
|
156
|
+
for (const name of coreMigrations) {
|
|
157
|
+
if (existingMigrations.has(name)) continue
|
|
158
|
+
await cp(join(coreMigrationsDir, name), join(consumerMigrationsDir, name), {
|
|
159
|
+
recursive: true,
|
|
160
|
+
})
|
|
161
|
+
migrationsAdded.push(name)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return { schemaWritten, migrationsAdded, skippedReason }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function runDbSync(options: DbSyncOptions): Promise<void> {
|
|
169
|
+
const consumerSchemaPath = resolve(process.cwd(), options.schema)
|
|
170
|
+
const spinner = ora('Locating @actuate-media/cms-core…').start()
|
|
171
|
+
|
|
172
|
+
const corePrismaDir = resolveCmsCorePrismaDir()
|
|
173
|
+
if (!corePrismaDir) {
|
|
174
|
+
spinner.fail('Could not locate @actuate-media/cms-core.')
|
|
175
|
+
logger.info('Install it first: `npm install @actuate-media/cms-core`.')
|
|
176
|
+
process.exitCode = 1
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
await access(join(corePrismaDir, 'schema.prisma'))
|
|
182
|
+
} catch {
|
|
183
|
+
spinner.fail(`cms-core does not ship a Prisma schema at ${corePrismaDir}.`)
|
|
184
|
+
process.exitCode = 1
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
spinner.text = 'Syncing schema + migrations…'
|
|
189
|
+
let result: DbSyncResult
|
|
190
|
+
try {
|
|
191
|
+
result = await syncPrismaAssets(consumerSchemaPath, corePrismaDir, { force: options.force })
|
|
192
|
+
} catch (err) {
|
|
193
|
+
spinner.fail('Failed to sync Prisma assets.')
|
|
194
|
+
logger.error(err instanceof Error ? err.message : String(err))
|
|
195
|
+
process.exitCode = 1
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
spinner.succeed('Prisma assets synced from cms-core.')
|
|
200
|
+
|
|
201
|
+
if (result.skippedReason) {
|
|
202
|
+
logger.warn(`Schema not updated: ${result.skippedReason}`)
|
|
203
|
+
} else if (result.schemaWritten) {
|
|
204
|
+
logger.success('schema.prisma refreshed.')
|
|
205
|
+
} else {
|
|
206
|
+
logger.info('schema.prisma already up to date.')
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (result.migrationsAdded.length > 0) {
|
|
210
|
+
logger.success(`Added ${result.migrationsAdded.length} new migration(s).`)
|
|
211
|
+
} else {
|
|
212
|
+
logger.info('No new migrations to add.')
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (result.schemaWritten || result.migrationsAdded.length > 0) {
|
|
216
|
+
logger.info('Next: run `npx prisma migrate deploy` then `npx prisma generate`.')
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function registerDbSyncCommand(program: Command): void {
|
|
221
|
+
program
|
|
222
|
+
.command('db:sync')
|
|
223
|
+
.description('Sync the canonical Prisma schema + migrations from the installed cms-core')
|
|
224
|
+
.option('--schema <path>', 'Path to schema.prisma', 'prisma/schema.prisma')
|
|
225
|
+
.option('--force', 'Overwrite schema.prisma even if it lacks the AUTO-SYNCED marker')
|
|
226
|
+
.action(runDbSync)
|
|
227
|
+
}
|