@actuate-media/cli 0.4.1 → 0.4.2
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 +21 -10
- package/CHANGELOG.md +28 -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 +2 -2
- package/dist/commands/db-init.d.ts.map +1 -1
- package/dist/commands/db-init.js +32 -32
- 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/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 +41 -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 +15 -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__/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 +59 -59
- package/src/commands/db-status.ts +70 -68
- 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 +92 -85
- package/src/deployment/diagnostics.ts +86 -72
- package/src/index.ts +30 -30
- package/src/utils/logger.ts +10 -10
|
@@ -1,31 +1,33 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
buildDeploymentManifest,
|
|
5
5
|
createDiagnosticReport,
|
|
6
6
|
REQUIRED_CMS_MODELS,
|
|
7
7
|
REQUIRED_ENV_VARS,
|
|
8
|
-
} from '../deployment/diagnostics.js'
|
|
8
|
+
} from '../deployment/diagnostics.js'
|
|
9
9
|
|
|
10
10
|
describe('deployment diagnostics', () => {
|
|
11
11
|
it('tracks deploy-critical models used by first-run admin features', () => {
|
|
12
|
-
expect(REQUIRED_CMS_MODELS).toEqual(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
12
|
+
expect(REQUIRED_CMS_MODELS).toEqual(
|
|
13
|
+
expect.arrayContaining([
|
|
14
|
+
'User',
|
|
15
|
+
'Session',
|
|
16
|
+
'Document',
|
|
17
|
+
'Media',
|
|
18
|
+
'Version',
|
|
19
|
+
'Folder',
|
|
20
|
+
'Redirect',
|
|
21
|
+
'FormSubmission',
|
|
22
|
+
'AuditLog',
|
|
23
|
+
'PasswordResetToken',
|
|
24
|
+
'MediaUsage',
|
|
25
|
+
'ScriptTag',
|
|
26
|
+
'PageTemplate',
|
|
27
|
+
'SavedSection',
|
|
28
|
+
]),
|
|
29
|
+
)
|
|
30
|
+
})
|
|
29
31
|
|
|
30
32
|
it('reports missing models and env vars with exact fix commands', () => {
|
|
31
33
|
const report = createDiagnosticReport({
|
|
@@ -36,22 +38,24 @@ describe('deployment diagnostics', () => {
|
|
|
36
38
|
},
|
|
37
39
|
packageManager: 'pnpm',
|
|
38
40
|
schemaPath: 'prisma/schema.prisma',
|
|
39
|
-
})
|
|
41
|
+
})
|
|
40
42
|
|
|
41
|
-
expect(report.status).toBe('fail')
|
|
42
|
-
expect(report.checks).toEqual(
|
|
43
|
-
expect.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
43
|
+
expect(report.status).toBe('fail')
|
|
44
|
+
expect(report.checks).toEqual(
|
|
45
|
+
expect.arrayContaining([
|
|
46
|
+
expect.objectContaining({
|
|
47
|
+
id: 'schema-models',
|
|
48
|
+
status: 'fail',
|
|
49
|
+
fix: 'Run `actuate db:init --schema prisma/schema.prisma` for new schemas, or update the existing Actuate block from the database setup docs, then create and apply a Prisma migration.',
|
|
50
|
+
}),
|
|
51
|
+
expect.objectContaining({
|
|
52
|
+
id: 'environment',
|
|
53
|
+
status: 'fail',
|
|
54
|
+
fix: 'Set missing environment variables before deploying: CMS_SECRET, CMS_ENCRYPTION_KEY, NEXT_PUBLIC_SITE_URL.',
|
|
55
|
+
}),
|
|
56
|
+
]),
|
|
57
|
+
)
|
|
58
|
+
})
|
|
55
59
|
|
|
56
60
|
it('requires migration connection details for deploy checks', () => {
|
|
57
61
|
const report = createDiagnosticReport({
|
|
@@ -66,16 +70,18 @@ describe('deployment diagnostics', () => {
|
|
|
66
70
|
packageManager: 'pnpm',
|
|
67
71
|
schemaPath: 'prisma/schema.prisma',
|
|
68
72
|
mode: 'deploy',
|
|
69
|
-
})
|
|
73
|
+
})
|
|
70
74
|
|
|
71
|
-
expect(report.status).toBe('fail')
|
|
72
|
-
expect(report.checks).toEqual(
|
|
73
|
-
expect.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
expect(report.status).toBe('fail')
|
|
76
|
+
expect(report.checks).toEqual(
|
|
77
|
+
expect.arrayContaining([
|
|
78
|
+
expect.objectContaining({
|
|
79
|
+
id: 'environment',
|
|
80
|
+
message: expect.stringContaining('DIRECT_DATABASE_URL'),
|
|
81
|
+
}),
|
|
82
|
+
]),
|
|
83
|
+
)
|
|
84
|
+
})
|
|
79
85
|
|
|
80
86
|
it('warns when designed marketing pages appear to use flat fields instead of page-builder content', () => {
|
|
81
87
|
const report = createDiagnosticReport({
|
|
@@ -107,24 +113,26 @@ describe('deployment diagnostics', () => {
|
|
|
107
113
|
},
|
|
108
114
|
packageManager: 'pnpm',
|
|
109
115
|
schemaPath: 'prisma/schema.prisma',
|
|
110
|
-
})
|
|
116
|
+
})
|
|
111
117
|
|
|
112
|
-
expect(report.status).toBe('warn')
|
|
113
|
-
expect(report.checks).toEqual(
|
|
114
|
-
expect.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
expect(report.status).toBe('warn')
|
|
119
|
+
expect(report.checks).toEqual(
|
|
120
|
+
expect.arrayContaining([
|
|
121
|
+
expect.objectContaining({
|
|
122
|
+
id: 'design-first-page-builder',
|
|
123
|
+
status: 'warn',
|
|
124
|
+
docs: 'https://actuatecms.dev/docs/design-first-page-builder',
|
|
125
|
+
}),
|
|
126
|
+
]),
|
|
127
|
+
)
|
|
128
|
+
})
|
|
121
129
|
|
|
122
130
|
it('exposes machine-readable deployment metadata', () => {
|
|
123
|
-
const manifest = buildDeploymentManifest()
|
|
131
|
+
const manifest = buildDeploymentManifest()
|
|
124
132
|
|
|
125
|
-
expect(manifest.requiredModels).toEqual(REQUIRED_CMS_MODELS)
|
|
126
|
-
expect(manifest.requiredEnv).toEqual(REQUIRED_ENV_VARS)
|
|
127
|
-
expect(manifest.routes.apiHealth).toBe('/api/cms/health')
|
|
128
|
-
expect(manifest.crons.scheduledPublish).toBe('/api/cms/cron/publish')
|
|
129
|
-
})
|
|
130
|
-
})
|
|
133
|
+
expect(manifest.requiredModels).toEqual(REQUIRED_CMS_MODELS)
|
|
134
|
+
expect(manifest.requiredEnv).toEqual(REQUIRED_ENV_VARS)
|
|
135
|
+
expect(manifest.routes.apiHealth).toBe('/api/cms/health')
|
|
136
|
+
expect(manifest.crons.scheduledPublish).toBe('/api/cms/cron/publish')
|
|
137
|
+
})
|
|
138
|
+
})
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises'
|
|
2
|
-
import { Command } from 'commander'
|
|
3
|
-
import { describe, expect, it } from 'vitest'
|
|
1
|
+
import { readFile } from 'node:fs/promises'
|
|
2
|
+
import { Command } from 'commander'
|
|
3
|
+
import { describe, expect, it } from 'vitest'
|
|
4
4
|
|
|
5
|
-
import { buildCreateActuateArgs, registerInitCommand } from '../commands/init.js'
|
|
5
|
+
import { buildCreateActuateArgs, registerInitCommand } from '../commands/init.js'
|
|
6
6
|
|
|
7
7
|
describe('init command', () => {
|
|
8
8
|
it('registers an init command that delegates to create-actuate-cms', () => {
|
|
9
|
-
const program = new Command()
|
|
10
|
-
registerInitCommand(program)
|
|
9
|
+
const program = new Command()
|
|
10
|
+
registerInitCommand(program)
|
|
11
11
|
|
|
12
|
-
const command = program.commands.find((candidate) => candidate.name() === 'init')
|
|
12
|
+
const command = program.commands.find((candidate) => candidate.name() === 'init')
|
|
13
13
|
|
|
14
|
-
expect(command).toBeDefined()
|
|
15
|
-
expect(buildCreateActuateArgs()).toEqual(['create', 'actuate-cms@latest'])
|
|
14
|
+
expect(command).toBeDefined()
|
|
15
|
+
expect(buildCreateActuateArgs()).toEqual(['create', 'actuate-cms@latest'])
|
|
16
16
|
expect(buildCreateActuateArgs('maidpro-site')).toEqual([
|
|
17
17
|
'create',
|
|
18
18
|
'actuate-cms@latest',
|
|
19
19
|
'maidpro-site',
|
|
20
|
-
])
|
|
21
|
-
})
|
|
20
|
+
])
|
|
21
|
+
})
|
|
22
22
|
|
|
23
23
|
it('exposes an actuate-cms bin alias for installed CLI usage', async () => {
|
|
24
|
-
const raw = await readFile(new URL('../../package.json', import.meta.url), 'utf-8')
|
|
25
|
-
const pkg = JSON.parse(raw) as { bin?: Record<string, string> }
|
|
24
|
+
const raw = await readFile(new URL('../../package.json', import.meta.url), 'utf-8')
|
|
25
|
+
const pkg = JSON.parse(raw) as { bin?: Record<string, string> }
|
|
26
26
|
|
|
27
|
-
expect(pkg.bin?.actuate).toBe('dist/index.js')
|
|
28
|
-
expect(pkg.bin?.['actuate-cms']).toBe('dist/index.js')
|
|
29
|
-
})
|
|
30
|
-
})
|
|
27
|
+
expect(pkg.bin?.actuate).toBe('dist/index.js')
|
|
28
|
+
expect(pkg.bin?.['actuate-cms']).toBe('dist/index.js')
|
|
29
|
+
})
|
|
30
|
+
})
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import { Command } from 'commander'
|
|
2
|
-
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
|
3
|
-
import { tmpdir } from 'node:os'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
registerDbInitCommand,
|
|
9
|
+
resetDbInitCommandRunner,
|
|
10
|
+
setDbInitCommandRunner,
|
|
11
|
+
} from '../commands/db-init.js'
|
|
12
|
+
import { REQUIRED_CMS_MODELS } from '../deployment/diagnostics.js'
|
|
9
13
|
|
|
10
14
|
function baseSchema() {
|
|
11
15
|
return `generator client {
|
|
@@ -16,32 +20,32 @@ datasource db {
|
|
|
16
20
|
provider = "postgresql"
|
|
17
21
|
url = env("DATABASE_URL")
|
|
18
22
|
}
|
|
19
|
-
|
|
23
|
+
`
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
describe('db:init schema fragment', () => {
|
|
23
27
|
afterEach(() => {
|
|
24
|
-
resetDbInitCommandRunner()
|
|
25
|
-
vi.restoreAllMocks()
|
|
26
|
-
})
|
|
28
|
+
resetDbInitCommandRunner()
|
|
29
|
+
vi.restoreAllMocks()
|
|
30
|
+
})
|
|
27
31
|
|
|
28
32
|
it('injects all deploy-critical Actuate models', async () => {
|
|
29
|
-
const tempRoot = await mkdtemp(path.join(tmpdir(), 'actuate-db-init-'))
|
|
30
|
-
const schemaPath = path.join(tempRoot, 'schema.prisma')
|
|
31
|
-
await writeFile(schemaPath, baseSchema())
|
|
33
|
+
const tempRoot = await mkdtemp(path.join(tmpdir(), 'actuate-db-init-'))
|
|
34
|
+
const schemaPath = path.join(tempRoot, 'schema.prisma')
|
|
35
|
+
await writeFile(schemaPath, baseSchema())
|
|
32
36
|
|
|
33
|
-
vi.spyOn(process, 'cwd').mockReturnValue(tempRoot)
|
|
34
|
-
setDbInitCommandRunner(() => undefined)
|
|
35
|
-
const command = new Command()
|
|
36
|
-
registerDbInitCommand(command)
|
|
37
|
+
vi.spyOn(process, 'cwd').mockReturnValue(tempRoot)
|
|
38
|
+
setDbInitCommandRunner(() => undefined)
|
|
39
|
+
const command = new Command()
|
|
40
|
+
registerDbInitCommand(command)
|
|
37
41
|
|
|
38
|
-
await command.parseAsync(['node', 'actuate', 'db:init', '--schema', 'schema.prisma'])
|
|
42
|
+
await command.parseAsync(['node', 'actuate', 'db:init', '--schema', 'schema.prisma'])
|
|
39
43
|
|
|
40
|
-
const schema = await readFile(schemaPath, 'utf-8')
|
|
44
|
+
const schema = await readFile(schemaPath, 'utf-8')
|
|
41
45
|
for (const model of REQUIRED_CMS_MODELS) {
|
|
42
|
-
expect(schema).toContain(`model ${model} `)
|
|
46
|
+
expect(schema).toContain(`model ${model} `)
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
await rm(tempRoot, { recursive: true, force: true })
|
|
46
|
-
})
|
|
47
|
-
})
|
|
49
|
+
await rm(tempRoot, { recursive: true, force: true })
|
|
50
|
+
})
|
|
51
|
+
})
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
|
|
3
3
|
vi.mock('@actuate-media/cms-core', () => ({
|
|
4
4
|
extractPlainText: vi.fn((value: string) => value.replace(/<[^>]+>/g, ' ')),
|
|
5
5
|
hashContent: vi.fn(async () => 'hash-1'),
|
|
6
6
|
sanitizeHtml: vi.fn((value: string) => value.replace(/<script[\s\S]*?<\/script>/gi, '')),
|
|
7
7
|
createInitialAdmin: vi.fn(),
|
|
8
|
-
}))
|
|
8
|
+
}))
|
|
9
9
|
|
|
10
|
-
import { createSeedDocument, ensureSeedAdmin, normalizeSeedPayload } from '../commands/seed.js'
|
|
10
|
+
import { createSeedDocument, ensureSeedAdmin, normalizeSeedPayload } from '../commands/seed.js'
|
|
11
11
|
|
|
12
12
|
afterEach(() => {
|
|
13
|
-
vi.unstubAllEnvs()
|
|
14
|
-
})
|
|
13
|
+
vi.unstubAllEnvs()
|
|
14
|
+
})
|
|
15
15
|
|
|
16
16
|
describe('seed payload normalization', () => {
|
|
17
17
|
it('separates globals from collection documents', () => {
|
|
@@ -30,38 +30,38 @@ describe('seed payload normalization', () => {
|
|
|
30
30
|
},
|
|
31
31
|
],
|
|
32
32
|
},
|
|
33
|
-
})
|
|
33
|
+
})
|
|
34
34
|
|
|
35
35
|
expect(normalized.globals).toEqual([
|
|
36
36
|
{
|
|
37
37
|
slug: 'site-settings',
|
|
38
38
|
data: { siteName: 'MaidPro', phone: '555-0100' },
|
|
39
39
|
},
|
|
40
|
-
])
|
|
40
|
+
])
|
|
41
41
|
expect(normalized.documents).toEqual([
|
|
42
42
|
{
|
|
43
43
|
collection: 'pages',
|
|
44
44
|
data: { title: 'Home', slug: 'home' },
|
|
45
45
|
status: 'PUBLISHED',
|
|
46
46
|
},
|
|
47
|
-
])
|
|
48
|
-
})
|
|
47
|
+
])
|
|
48
|
+
})
|
|
49
49
|
|
|
50
50
|
it('keeps backwards-compatible collection-key seed files', () => {
|
|
51
51
|
const normalized = normalizeSeedPayload({
|
|
52
52
|
pages: [{ title: 'Home', slug: 'home' }],
|
|
53
|
-
})
|
|
53
|
+
})
|
|
54
54
|
|
|
55
|
-
expect(normalized.globals).toEqual([])
|
|
55
|
+
expect(normalized.globals).toEqual([])
|
|
56
56
|
expect(normalized.documents).toEqual([
|
|
57
57
|
{
|
|
58
58
|
collection: 'pages',
|
|
59
59
|
data: { title: 'Home', slug: 'home' },
|
|
60
60
|
status: 'DRAFT',
|
|
61
61
|
},
|
|
62
|
-
])
|
|
63
|
-
})
|
|
64
|
-
})
|
|
62
|
+
])
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
65
|
|
|
66
66
|
describe('seed admin handling', () => {
|
|
67
67
|
it('refuses to create a first admin without credentials', async () => {
|
|
@@ -69,11 +69,11 @@ describe('seed admin handling', () => {
|
|
|
69
69
|
user: {
|
|
70
70
|
findFirst: vi.fn().mockResolvedValue(null),
|
|
71
71
|
},
|
|
72
|
-
}
|
|
72
|
+
}
|
|
73
73
|
|
|
74
|
-
await expect(ensureSeedAdmin(db)).rejects.toThrow('CMS_ADMIN_EMAIL and CMS_ADMIN_PASSWORD')
|
|
75
|
-
})
|
|
76
|
-
})
|
|
74
|
+
await expect(ensureSeedAdmin(db)).rejects.toThrow('CMS_ADMIN_EMAIL and CMS_ADMIN_PASSWORD')
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
77
|
|
|
78
78
|
describe('seed document creation', () => {
|
|
79
79
|
it('creates documents with versions, content hashes, and sanitized HTML', async () => {
|
|
@@ -84,10 +84,10 @@ describe('seed document creation', () => {
|
|
|
84
84
|
version: {
|
|
85
85
|
create: vi.fn(),
|
|
86
86
|
},
|
|
87
|
-
}
|
|
87
|
+
}
|
|
88
88
|
const db = {
|
|
89
89
|
$transaction: vi.fn(async (fn) => fn(tx)),
|
|
90
|
-
}
|
|
90
|
+
}
|
|
91
91
|
|
|
92
92
|
await createSeedDocument(db, 'admin-1', {
|
|
93
93
|
collection: 'pages',
|
|
@@ -97,7 +97,7 @@ describe('seed document creation', () => {
|
|
|
97
97
|
slug: 'home',
|
|
98
98
|
content: '<p>Hello</p><script>alert(1)</script>',
|
|
99
99
|
},
|
|
100
|
-
})
|
|
100
|
+
})
|
|
101
101
|
|
|
102
102
|
expect(tx.document.create).toHaveBeenCalledWith({
|
|
103
103
|
data: expect.objectContaining({
|
|
@@ -114,13 +114,13 @@ describe('seed document creation', () => {
|
|
|
114
114
|
content: '<p>Hello</p>',
|
|
115
115
|
}),
|
|
116
116
|
}),
|
|
117
|
-
})
|
|
117
|
+
})
|
|
118
118
|
expect(tx.version.create).toHaveBeenCalledWith({
|
|
119
119
|
data: expect.objectContaining({
|
|
120
120
|
documentId: 'doc-1',
|
|
121
121
|
changedById: 'admin-1',
|
|
122
122
|
changeType: 'CREATE',
|
|
123
123
|
}),
|
|
124
|
-
})
|
|
125
|
-
})
|
|
126
|
-
})
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
})
|
package/src/commands/db-init.ts
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { Command } from
|
|
2
|
-
import { readFile, writeFile, access } from
|
|
3
|
-
import { resolve, join } from
|
|
4
|
-
import { execSync, type ExecSyncOptions } from
|
|
5
|
-
import ora from
|
|
6
|
-
import { logger } from
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { readFile, writeFile, access } from 'node:fs/promises'
|
|
3
|
+
import { resolve, join } from 'node:path'
|
|
4
|
+
import { execSync, type ExecSyncOptions } from 'node:child_process'
|
|
5
|
+
import ora from 'ora'
|
|
6
|
+
import { logger } from '../utils/logger.js'
|
|
7
7
|
|
|
8
|
-
const CMS_SCHEMA_MARKER =
|
|
8
|
+
const CMS_SCHEMA_MARKER = '// ── Actuate CMS models'
|
|
9
9
|
|
|
10
10
|
export let runDbInitCommand = (command: string, options: ExecSyncOptions): void => {
|
|
11
|
-
execSync(command, options)
|
|
12
|
-
}
|
|
11
|
+
execSync(command, options)
|
|
12
|
+
}
|
|
13
13
|
|
|
14
|
-
const defaultDbInitCommandRunner = runDbInitCommand
|
|
14
|
+
const defaultDbInitCommandRunner = runDbInitCommand
|
|
15
15
|
|
|
16
16
|
export function setDbInitCommandRunner(runner: typeof runDbInitCommand): void {
|
|
17
|
-
runDbInitCommand = runner
|
|
17
|
+
runDbInitCommand = runner
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export function resetDbInitCommandRunner(): void {
|
|
21
|
-
runDbInitCommand = defaultDbInitCommandRunner
|
|
21
|
+
runDbInitCommand = defaultDbInitCommandRunner
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
async function fileExists(filePath: string): Promise<boolean> {
|
|
25
25
|
try {
|
|
26
|
-
await access(filePath)
|
|
27
|
-
return true
|
|
26
|
+
await access(filePath)
|
|
27
|
+
return true
|
|
28
28
|
} catch {
|
|
29
|
-
return false
|
|
29
|
+
return false
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -302,83 +302,83 @@ model SavedSection {
|
|
|
302
302
|
|
|
303
303
|
@@index([category])
|
|
304
304
|
}
|
|
305
|
-
|
|
305
|
+
`
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
export function registerDbInitCommand(program: Command): void {
|
|
309
309
|
program
|
|
310
|
-
.command(
|
|
311
|
-
.description(
|
|
312
|
-
.option(
|
|
313
|
-
.option(
|
|
314
|
-
.option(
|
|
310
|
+
.command('db:init')
|
|
311
|
+
.description('Add Actuate CMS models to your Prisma schema and generate the client')
|
|
312
|
+
.option('--schema <path>', 'Path to schema.prisma', 'prisma/schema.prisma')
|
|
313
|
+
.option('--migrate', 'Run prisma migrate dev after adding models')
|
|
314
|
+
.option('--force', 'Overwrite existing CMS models if present')
|
|
315
315
|
.action(async (opts: { schema: string; migrate?: boolean; force?: boolean }) => {
|
|
316
|
-
const schemaPath = resolve(process.cwd(), opts.schema)
|
|
316
|
+
const schemaPath = resolve(process.cwd(), opts.schema)
|
|
317
317
|
|
|
318
318
|
if (!(await fileExists(schemaPath))) {
|
|
319
|
-
logger.error(`Schema file not found at ${schemaPath}`)
|
|
320
|
-
logger.info(
|
|
321
|
-
process.exitCode = 1
|
|
322
|
-
return
|
|
319
|
+
logger.error(`Schema file not found at ${schemaPath}`)
|
|
320
|
+
logger.info('Run `npx prisma init` first, or specify --schema <path>.')
|
|
321
|
+
process.exitCode = 1
|
|
322
|
+
return
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
const spinner = ora(
|
|
325
|
+
const spinner = ora('Reading Prisma schema...').start()
|
|
326
326
|
|
|
327
|
-
let content: string
|
|
327
|
+
let content: string
|
|
328
328
|
try {
|
|
329
|
-
content = await readFile(schemaPath,
|
|
329
|
+
content = await readFile(schemaPath, 'utf-8')
|
|
330
330
|
} catch (err) {
|
|
331
|
-
spinner.fail(
|
|
332
|
-
logger.error(err instanceof Error ? err.message : String(err))
|
|
333
|
-
process.exitCode = 1
|
|
334
|
-
return
|
|
331
|
+
spinner.fail('Failed to read schema file.')
|
|
332
|
+
logger.error(err instanceof Error ? err.message : String(err))
|
|
333
|
+
process.exitCode = 1
|
|
334
|
+
return
|
|
335
335
|
}
|
|
336
336
|
|
|
337
337
|
if (content.includes(CMS_SCHEMA_MARKER)) {
|
|
338
338
|
if (!opts.force) {
|
|
339
|
-
spinner.info(
|
|
340
|
-
return
|
|
339
|
+
spinner.info('Actuate CMS models already present in schema. Use --force to overwrite.')
|
|
340
|
+
return
|
|
341
341
|
}
|
|
342
|
-
spinner.text =
|
|
343
|
-
const markerIndex = content.indexOf(CMS_SCHEMA_MARKER)
|
|
344
|
-
content = content.substring(0, markerIndex).trimEnd() +
|
|
342
|
+
spinner.text = 'Removing existing CMS models...'
|
|
343
|
+
const markerIndex = content.indexOf(CMS_SCHEMA_MARKER)
|
|
344
|
+
content = content.substring(0, markerIndex).trimEnd() + '\n'
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
spinner.text =
|
|
348
|
-
const updatedContent = content.trimEnd() +
|
|
347
|
+
spinner.text = 'Adding Actuate CMS models...'
|
|
348
|
+
const updatedContent = content.trimEnd() + '\n' + getCmsSchemaFragment()
|
|
349
349
|
|
|
350
350
|
try {
|
|
351
|
-
await writeFile(schemaPath, updatedContent)
|
|
352
|
-
spinner.succeed(
|
|
351
|
+
await writeFile(schemaPath, updatedContent)
|
|
352
|
+
spinner.succeed('Actuate CMS models added to schema.')
|
|
353
353
|
} catch (err) {
|
|
354
|
-
spinner.fail(
|
|
355
|
-
logger.error(err instanceof Error ? err.message : String(err))
|
|
356
|
-
process.exitCode = 1
|
|
357
|
-
return
|
|
354
|
+
spinner.fail('Failed to write schema file.')
|
|
355
|
+
logger.error(err instanceof Error ? err.message : String(err))
|
|
356
|
+
process.exitCode = 1
|
|
357
|
+
return
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
-
const execOpts: ExecSyncOptions = { stdio:
|
|
360
|
+
const execOpts: ExecSyncOptions = { stdio: 'inherit', cwd: process.cwd() }
|
|
361
361
|
|
|
362
|
-
const genSpinner = ora(
|
|
362
|
+
const genSpinner = ora('Running prisma generate...').start()
|
|
363
363
|
try {
|
|
364
|
-
genSpinner.stop()
|
|
365
|
-
runDbInitCommand(
|
|
366
|
-
logger.success(
|
|
364
|
+
genSpinner.stop()
|
|
365
|
+
runDbInitCommand('npx prisma generate', execOpts)
|
|
366
|
+
logger.success('Prisma client generated.')
|
|
367
367
|
} catch {
|
|
368
|
-
logger.warn(
|
|
368
|
+
logger.warn('prisma generate failed. You may need to set DATABASE_URL first.')
|
|
369
369
|
}
|
|
370
370
|
|
|
371
371
|
if (opts.migrate) {
|
|
372
|
-
const migSpinner = ora(
|
|
372
|
+
const migSpinner = ora('Running prisma migrate dev...').start()
|
|
373
373
|
try {
|
|
374
|
-
migSpinner.stop()
|
|
375
|
-
runDbInitCommand(
|
|
376
|
-
logger.success(
|
|
374
|
+
migSpinner.stop()
|
|
375
|
+
runDbInitCommand('npx prisma migrate dev --name actuate-cms-init', execOpts)
|
|
376
|
+
logger.success('Migration created and applied.')
|
|
377
377
|
} catch {
|
|
378
|
-
logger.warn(
|
|
378
|
+
logger.warn('prisma migrate dev failed. Run it manually after setting DATABASE_URL.')
|
|
379
379
|
}
|
|
380
380
|
} else {
|
|
381
|
-
logger.info(
|
|
381
|
+
logger.info('Run `npx prisma migrate dev --name actuate-cms` to create the migration.')
|
|
382
382
|
}
|
|
383
|
-
})
|
|
383
|
+
})
|
|
384
384
|
}
|