@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.
Files changed (92) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +69 -12
  3. package/CHANGELOG.md +58 -0
  4. package/dist/__tests__/db-init.test.d.ts +2 -0
  5. package/dist/__tests__/db-init.test.d.ts.map +1 -0
  6. package/dist/__tests__/db-init.test.js +127 -0
  7. package/dist/__tests__/db-init.test.js.map +1 -0
  8. package/dist/__tests__/db-sync.test.d.ts +2 -0
  9. package/dist/__tests__/db-sync.test.d.ts.map +1 -0
  10. package/dist/__tests__/db-sync.test.js +136 -0
  11. package/dist/__tests__/db-sync.test.js.map +1 -0
  12. package/dist/__tests__/deployment-diagnostics.test.js.map +1 -1
  13. package/dist/__tests__/init.test.js.map +1 -1
  14. package/dist/__tests__/schema-fragment.test.js +1 -1
  15. package/dist/__tests__/schema-fragment.test.js.map +1 -1
  16. package/dist/__tests__/seed.test.js.map +1 -1
  17. package/dist/commands/db-init.d.ts +19 -2
  18. package/dist/commands/db-init.d.ts.map +1 -1
  19. package/dist/commands/db-init.js +128 -306
  20. package/dist/commands/db-init.js.map +1 -1
  21. package/dist/commands/db-status.d.ts +1 -1
  22. package/dist/commands/db-status.d.ts.map +1 -1
  23. package/dist/commands/db-status.js +33 -33
  24. package/dist/commands/db-status.js.map +1 -1
  25. package/dist/commands/db-sync.d.ts +31 -0
  26. package/dist/commands/db-sync.d.ts.map +1 -0
  27. package/dist/commands/db-sync.js +195 -0
  28. package/dist/commands/db-sync.js.map +1 -0
  29. package/dist/commands/doctor.d.ts +1 -1
  30. package/dist/commands/doctor.d.ts.map +1 -1
  31. package/dist/commands/doctor.js +48 -41
  32. package/dist/commands/doctor.js.map +1 -1
  33. package/dist/commands/export.d.ts +1 -1
  34. package/dist/commands/export.d.ts.map +1 -1
  35. package/dist/commands/export.js +32 -32
  36. package/dist/commands/export.js.map +1 -1
  37. package/dist/commands/generate.d.ts +1 -1
  38. package/dist/commands/generate.d.ts.map +1 -1
  39. package/dist/commands/generate.js +8 -8
  40. package/dist/commands/generate.js.map +1 -1
  41. package/dist/commands/import.d.ts +1 -1
  42. package/dist/commands/import.d.ts.map +1 -1
  43. package/dist/commands/import.js +55 -58
  44. package/dist/commands/import.js.map +1 -1
  45. package/dist/commands/init.d.ts.map +1 -1
  46. package/dist/commands/init.js.map +1 -1
  47. package/dist/commands/migrate.d.ts +1 -1
  48. package/dist/commands/migrate.d.ts.map +1 -1
  49. package/dist/commands/migrate.js +18 -24
  50. package/dist/commands/migrate.js.map +1 -1
  51. package/dist/commands/seed.d.ts +1 -1
  52. package/dist/commands/seed.d.ts.map +1 -1
  53. package/dist/commands/seed.js +156 -157
  54. package/dist/commands/seed.js.map +1 -1
  55. package/dist/commands/update-check.d.ts +1 -1
  56. package/dist/commands/update-check.d.ts.map +1 -1
  57. package/dist/commands/update-check.js +34 -27
  58. package/dist/commands/update-check.js.map +1 -1
  59. package/dist/commands/upgrade.d.ts +1 -1
  60. package/dist/commands/upgrade.d.ts.map +1 -1
  61. package/dist/commands/upgrade.js +46 -34
  62. package/dist/commands/upgrade.js.map +1 -1
  63. package/dist/deployment/diagnostics.d.ts.map +1 -1
  64. package/dist/deployment/diagnostics.js +7 -2
  65. package/dist/deployment/diagnostics.js.map +1 -1
  66. package/dist/index.js +17 -15
  67. package/dist/index.js.map +1 -1
  68. package/dist/utils/logger.d.ts.map +1 -1
  69. package/dist/utils/logger.js +5 -5
  70. package/dist/utils/logger.js.map +1 -1
  71. package/package.json +3 -3
  72. package/src/__tests__/db-init.test.ts +155 -0
  73. package/src/__tests__/db-sync.test.ts +167 -0
  74. package/src/__tests__/deployment-diagnostics.test.ts +68 -60
  75. package/src/__tests__/init.test.ts +17 -17
  76. package/src/__tests__/schema-fragment.test.ts +29 -25
  77. package/src/__tests__/seed.test.ts +25 -25
  78. package/src/commands/db-init.ts +146 -319
  79. package/src/commands/db-status.ts +70 -68
  80. package/src/commands/db-sync.ts +227 -0
  81. package/src/commands/doctor.ts +102 -88
  82. package/src/commands/export.ts +65 -75
  83. package/src/commands/generate.ts +14 -16
  84. package/src/commands/import.ts +125 -140
  85. package/src/commands/init.ts +14 -14
  86. package/src/commands/migrate.ts +29 -35
  87. package/src/commands/seed.ts +294 -300
  88. package/src/commands/update-check.ts +77 -72
  89. package/src/commands/upgrade.ts +100 -85
  90. package/src/deployment/diagnostics.ts +86 -72
  91. package/src/index.ts +32 -30
  92. package/src/utils/logger.ts +10 -10
@@ -1,65 +1,72 @@
1
- import { Command } from "commander";
2
- import { readFile } from "node:fs/promises";
3
- import { existsSync } from "node:fs";
4
- import { resolve } from "node:path";
5
- import ora from "ora";
6
- import { logger } from "../utils/logger.js";
7
-
8
- const UPDATE_SERVER_URL = "https://updates.actuatecms.com/api/versions";
1
+ import { Command } from 'commander'
2
+ import { readFile } from 'node:fs/promises'
3
+ import { existsSync } from 'node:fs'
4
+ import { resolve } from 'node:path'
5
+ import ora from 'ora'
6
+ import { logger } from '../utils/logger.js'
7
+
8
+ const UPDATE_SERVER_BASE = (
9
+ process.env.ACTUATE_UPDATE_SERVER_URL ?? 'https://updates.actuatecms.com'
10
+ ).replace(/\/+$/, '')
11
+ const UPDATE_SERVER_URL = `${UPDATE_SERVER_BASE}/api/versions`
9
12
 
10
13
  function compareVersions(a: string, b: string): number {
11
- const pa = a.replace(/^[^0-9]*/, "").split(".").map(Number);
12
- const pb = b.replace(/^[^0-9]*/, "").split(".").map(Number);
14
+ const pa = a
15
+ .replace(/^[^0-9]*/, '')
16
+ .split('.')
17
+ .map(Number)
18
+ const pb = b
19
+ .replace(/^[^0-9]*/, '')
20
+ .split('.')
21
+ .map(Number)
13
22
  for (let i = 0; i < 3; i++) {
14
- if ((pa[i] || 0) > (pb[i] || 0)) return 1;
15
- if ((pa[i] || 0) < (pb[i] || 0)) return -1;
23
+ if ((pa[i] || 0) > (pb[i] || 0)) return 1
24
+ if ((pa[i] || 0) < (pb[i] || 0)) return -1
16
25
  }
17
- return 0;
26
+ return 0
18
27
  }
19
28
 
20
29
  interface PackageVersions {
21
- [pkg: string]: string;
30
+ [pkg: string]: string
22
31
  }
23
32
 
24
33
  async function getLocalVersions(): Promise<PackageVersions> {
25
- const pkgPath = resolve(process.cwd(), "package.json");
34
+ const pkgPath = resolve(process.cwd(), 'package.json')
26
35
  if (!existsSync(pkgPath)) {
27
- throw new Error("No package.json found in the current directory.");
36
+ throw new Error('No package.json found in the current directory.')
28
37
  }
29
38
 
30
- const raw = await readFile(pkgPath, "utf-8");
31
- const pkg = JSON.parse(raw);
32
- const versions: PackageVersions = {};
39
+ const raw = await readFile(pkgPath, 'utf-8')
40
+ const pkg = JSON.parse(raw)
41
+ const versions: PackageVersions = {}
33
42
 
34
- for (const section of ["dependencies", "devDependencies"] as const) {
35
- const deps = pkg[section];
36
- if (!deps) continue;
43
+ for (const section of ['dependencies', 'devDependencies'] as const) {
44
+ const deps = pkg[section]
45
+ if (!deps) continue
37
46
  for (const [name, version] of Object.entries(deps)) {
38
- if (name.startsWith("@actuate-media/") && typeof version === "string") {
39
- versions[name] = version.replace(/^[\^~]/, "").replace("workspace:", "");
47
+ if (name.startsWith('@actuate-media/') && typeof version === 'string') {
48
+ versions[name] = version.replace(/^[\^~]/, '').replace('workspace:', '')
40
49
  }
41
50
  }
42
51
  }
43
52
 
44
- return versions;
53
+ return versions
45
54
  }
46
55
 
47
- async function fetchLatestVersions(
48
- packages: string[],
49
- ): Promise<PackageVersions> {
50
- const versions: PackageVersions = {};
56
+ async function fetchLatestVersions(packages: string[]): Promise<PackageVersions> {
57
+ const versions: PackageVersions = {}
51
58
 
52
59
  try {
53
- const res = await fetch(UPDATE_SERVER_URL);
60
+ const res = await fetch(UPDATE_SERVER_URL)
54
61
  if (res.ok) {
55
- const data = await res.json();
62
+ const data = await res.json()
56
63
  for (const pkg of packages) {
57
64
  if (data[pkg]) {
58
- versions[pkg] = data[pkg];
65
+ versions[pkg] = data[pkg]
59
66
  }
60
67
  }
61
68
  if (Object.keys(versions).length === packages.length) {
62
- return versions;
69
+ return versions
63
70
  }
64
71
  }
65
72
  } catch {
@@ -67,81 +74,79 @@ async function fetchLatestVersions(
67
74
  }
68
75
 
69
76
  for (const pkg of packages) {
70
- if (versions[pkg]) continue;
77
+ if (versions[pkg]) continue
71
78
  try {
72
- const npmUrl = `https://registry.npmjs.org/${pkg}/latest`;
73
- const res = await fetch(npmUrl);
79
+ const npmUrl = `https://registry.npmjs.org/${pkg}/latest`
80
+ const res = await fetch(npmUrl)
74
81
  if (res.ok) {
75
- const data = await res.json();
76
- versions[pkg] = data.version;
82
+ const data = await res.json()
83
+ versions[pkg] = data.version
77
84
  }
78
85
  } catch {
79
86
  // Skip packages we can't look up
80
87
  }
81
88
  }
82
89
 
83
- return versions;
90
+ return versions
84
91
  }
85
92
 
86
93
  async function runUpdateCheck(): Promise<void> {
87
- const spinner = ora("Checking for updates…").start();
94
+ const spinner = ora('Checking for updates…').start()
88
95
 
89
96
  try {
90
- const local = await getLocalVersions();
91
- const packages = Object.keys(local);
97
+ const local = await getLocalVersions()
98
+ const packages = Object.keys(local)
92
99
 
93
100
  if (packages.length === 0) {
94
- spinner.warn("No @actuate-media/* packages found in package.json.");
95
- return;
101
+ spinner.warn('No @actuate-media/* packages found in package.json.')
102
+ return
96
103
  }
97
104
 
98
- const latest = await fetchLatestVersions(packages);
99
- spinner.stop();
105
+ const latest = await fetchLatestVersions(packages)
106
+ spinner.stop()
100
107
 
101
- let hasUpdates = false;
108
+ let hasUpdates = false
102
109
 
103
- console.log("\n " + "Package".padEnd(30) + "Current".padEnd(14) + "Latest".padEnd(14) + "Status");
104
- console.log(" " + "-".repeat(68));
110
+ console.log(
111
+ '\n ' + 'Package'.padEnd(30) + 'Current'.padEnd(14) + 'Latest'.padEnd(14) + 'Status',
112
+ )
113
+ console.log(' ' + '-'.repeat(68))
105
114
 
106
115
  for (const pkg of packages) {
107
- const current = local[pkg];
108
- const remote = latest[pkg] ?? "unknown";
109
- let status: string;
116
+ const current = local[pkg]
117
+ const remote = latest[pkg] ?? 'unknown'
118
+ let status: string
110
119
 
111
- if (remote === "unknown") {
112
- status = "?";
120
+ if (remote === 'unknown') {
121
+ status = '?'
113
122
  } else if (compareVersions(current!, remote) < 0) {
114
- status = "Update available";
115
- hasUpdates = true;
123
+ status = 'Update available'
124
+ hasUpdates = true
116
125
  } else {
117
- status = "Up to date";
126
+ status = 'Up to date'
118
127
  }
119
128
 
120
- console.log(
121
- ` ${pkg.padEnd(30)}${(current ?? '').padEnd(14)}${remote.padEnd(14)}${status}`,
122
- );
129
+ console.log(` ${pkg.padEnd(30)}${(current ?? '').padEnd(14)}${remote.padEnd(14)}${status}`)
123
130
  }
124
131
 
125
- console.log();
132
+ console.log()
126
133
 
127
134
  if (hasUpdates) {
128
- logger.warn('Updates available! Run "actuate upgrade" to update.');
135
+ logger.warn('Updates available! Run "actuate upgrade" to update.')
129
136
  } else {
130
- logger.success("All packages are up to date!");
137
+ logger.success('All packages are up to date!')
131
138
  }
132
139
  } catch (err) {
133
- spinner.fail("Update check failed.");
134
- const message = err instanceof Error ? err.message : String(err);
135
- logger.error(message);
136
- process.exit(1);
140
+ spinner.fail('Update check failed.')
141
+ const message = err instanceof Error ? err.message : String(err)
142
+ logger.error(message)
143
+ process.exit(1)
137
144
  }
138
145
  }
139
146
 
140
147
  export function registerUpdateCheckCommand(program: Command): void {
141
148
  program
142
- .command("update-check")
143
- .description(
144
- "Check for available Actuate CMS package updates",
145
- )
146
- .action(runUpdateCheck);
149
+ .command('update-check')
150
+ .description('Check for available Actuate CMS package updates')
151
+ .action(runUpdateCheck)
147
152
  }
@@ -1,173 +1,188 @@
1
- import { Command } from "commander";
2
- import { readFile, writeFile } from "node:fs/promises";
3
- import { existsSync } from "node:fs";
4
- import { resolve } from "node:path";
5
- import { createInterface } from "node:readline/promises";
6
- import ora from "ora";
7
- import { logger } from "../utils/logger.js";
8
-
9
- const UPDATE_SERVER_URL = "https://updates.actuatecms.com/api/versions";
1
+ import { Command } from 'commander'
2
+ import { readFile, writeFile } from 'node:fs/promises'
3
+ import { existsSync } from 'node:fs'
4
+ import { resolve } from 'node:path'
5
+ import { createInterface } from 'node:readline/promises'
6
+ import ora from 'ora'
7
+ import { logger } from '../utils/logger.js'
8
+
9
+ const UPDATE_SERVER_BASE = (
10
+ process.env.ACTUATE_UPDATE_SERVER_URL ?? 'https://updates.actuatecms.com'
11
+ ).replace(/\/+$/, '')
12
+ const UPDATE_SERVER_URL = `${UPDATE_SERVER_BASE}/api/versions`
10
13
 
11
14
  function compareVersions(a: string, b: string): number {
12
- const pa = a.replace(/^[^0-9]*/, "").split(".").map(Number);
13
- const pb = b.replace(/^[^0-9]*/, "").split(".").map(Number);
15
+ const pa = a
16
+ .replace(/^[^0-9]*/, '')
17
+ .split('.')
18
+ .map(Number)
19
+ const pb = b
20
+ .replace(/^[^0-9]*/, '')
21
+ .split('.')
22
+ .map(Number)
14
23
  for (let i = 0; i < 3; i++) {
15
- if ((pa[i] || 0) > (pb[i] || 0)) return 1;
16
- if ((pa[i] || 0) < (pb[i] || 0)) return -1;
24
+ if ((pa[i] || 0) > (pb[i] || 0)) return 1
25
+ if ((pa[i] || 0) < (pb[i] || 0)) return -1
17
26
  }
18
- return 0;
27
+ return 0
19
28
  }
20
29
 
21
30
  async function confirm(question: string): Promise<boolean> {
22
- const rl = createInterface({ input: process.stdin, output: process.stdout });
31
+ const rl = createInterface({ input: process.stdin, output: process.stdout })
23
32
  try {
24
- const answer = await rl.question(`${question} (y/N) `);
25
- return answer.trim().toLowerCase() === "y";
33
+ const answer = await rl.question(`${question} (y/N) `)
34
+ return answer.trim().toLowerCase() === 'y'
26
35
  } finally {
27
- rl.close();
36
+ rl.close()
28
37
  }
29
38
  }
30
39
 
31
- async function fetchLatestVersions(
32
- packages: string[],
33
- ): Promise<Record<string, string>> {
34
- const versions: Record<string, string> = {};
40
+ async function fetchLatestVersions(packages: string[]): Promise<Record<string, string>> {
41
+ const versions: Record<string, string> = {}
35
42
 
36
43
  try {
37
- const res = await fetch(UPDATE_SERVER_URL);
44
+ const res = await fetch(UPDATE_SERVER_URL)
38
45
  if (res.ok) {
39
- const data = await res.json();
46
+ const data = await res.json()
40
47
  for (const pkg of packages) {
41
- if (data[pkg]) versions[pkg] = data[pkg];
48
+ if (data[pkg]) versions[pkg] = data[pkg]
42
49
  }
43
- if (Object.keys(versions).length === packages.length) return versions;
50
+ if (Object.keys(versions).length === packages.length) return versions
44
51
  }
45
52
  } catch {
46
53
  // Fall through to npm registry
47
54
  }
48
55
 
49
56
  for (const pkg of packages) {
50
- if (versions[pkg]) continue;
57
+ if (versions[pkg]) continue
51
58
  try {
52
- const res = await fetch(`https://registry.npmjs.org/${pkg}/latest`);
59
+ const res = await fetch(`https://registry.npmjs.org/${pkg}/latest`)
53
60
  if (res.ok) {
54
- const data = await res.json();
55
- versions[pkg] = data.version;
61
+ const data = await res.json()
62
+ versions[pkg] = data.version
56
63
  }
57
64
  } catch {
58
65
  // Skip
59
66
  }
60
67
  }
61
68
 
62
- return versions;
69
+ return versions
63
70
  }
64
71
 
65
72
  interface UpgradeOptions {
66
- latest?: boolean;
67
- version?: string;
68
- dryRun?: boolean;
73
+ latest?: boolean
74
+ version?: string
75
+ dryRun?: boolean
69
76
  }
70
77
 
71
78
  async function runUpgrade(options: UpgradeOptions): Promise<void> {
72
- const pkgPath = resolve(process.cwd(), "package.json");
79
+ const pkgPath = resolve(process.cwd(), 'package.json')
73
80
  if (!existsSync(pkgPath)) {
74
- logger.error("No package.json found in the current directory.");
75
- process.exit(1);
81
+ logger.error('No package.json found in the current directory.')
82
+ process.exit(1)
76
83
  }
77
84
 
78
- const raw = await readFile(pkgPath, "utf-8");
79
- const pkg = JSON.parse(raw);
85
+ const raw = await readFile(pkgPath, 'utf-8')
86
+ const pkg = JSON.parse(raw)
80
87
 
81
- const actuatePackages: { name: string; current: string; section: string }[] = [];
88
+ const actuatePackages: { name: string; current: string; section: string }[] = []
82
89
 
83
- for (const section of ["dependencies", "devDependencies"] as const) {
84
- const deps = pkg[section];
85
- if (!deps) continue;
90
+ for (const section of ['dependencies', 'devDependencies'] as const) {
91
+ const deps = pkg[section]
92
+ if (!deps) continue
86
93
  for (const [name, version] of Object.entries(deps)) {
87
- if (name.startsWith("@actuate-media/") && typeof version === "string") {
88
- if (version.startsWith("workspace:")) continue;
94
+ if (name.startsWith('@actuate-media/') && typeof version === 'string') {
95
+ if (version.startsWith('workspace:')) continue
89
96
  actuatePackages.push({
90
97
  name,
91
- current: version.replace(/^[\^~]/, ""),
98
+ current: version.replace(/^[\^~]/, ''),
92
99
  section,
93
- });
100
+ })
94
101
  }
95
102
  }
96
103
  }
97
104
 
98
105
  if (actuatePackages.length === 0) {
99
- logger.warn("No @actuate-media/* packages found in package.json (excluding workspace: references).");
100
- return;
106
+ logger.warn(
107
+ 'No @actuate-media/* packages found in package.json (excluding workspace: references).',
108
+ )
109
+ return
101
110
  }
102
111
 
103
- const spinner = ora("Resolving target versions…").start();
112
+ const spinner = ora('Resolving target versions…').start()
104
113
 
105
- let targetVersions: Record<string, string>;
114
+ let targetVersions: Record<string, string>
106
115
 
107
116
  if (options.version) {
108
- targetVersions = {};
117
+ targetVersions = {}
109
118
  for (const p of actuatePackages) {
110
- targetVersions[p.name] = options.version;
119
+ targetVersions[p.name] = options.version
111
120
  }
112
- spinner.succeed(`Target version: ${options.version}`);
121
+ spinner.succeed(`Target version: ${options.version}`)
113
122
  } else {
114
- targetVersions = await fetchLatestVersions(
115
- actuatePackages.map((p) => p.name),
116
- );
117
- spinner.succeed("Resolved latest versions.");
123
+ targetVersions = await fetchLatestVersions(actuatePackages.map((p) => p.name))
124
+ spinner.succeed('Resolved latest versions.')
118
125
  }
119
126
 
120
- const upgrades: { name: string; from: string; to: string; section: string }[] = [];
127
+ const upgrades: { name: string; from: string; to: string; section: string }[] = []
121
128
 
122
129
  for (const p of actuatePackages) {
123
- const target = targetVersions[p.name];
124
- if (!target) continue;
130
+ const target = targetVersions[p.name]
131
+ if (!target) continue
125
132
  if (compareVersions(p.current, target) < 0) {
126
- upgrades.push({ name: p.name, from: p.current, to: target, section: p.section });
133
+ upgrades.push({ name: p.name, from: p.current, to: target, section: p.section })
127
134
  }
128
135
  }
129
136
 
130
137
  if (upgrades.length === 0) {
131
- logger.success("All @actuate-media/* packages are already up to date.");
132
- return;
138
+ logger.success('All @actuate-media/* packages are already up to date.')
139
+ return
133
140
  }
134
141
 
135
- console.log("\n Proposed upgrades:\n");
136
- console.log(" " + "Package".padEnd(30) + "From".padEnd(14) + "To");
137
- console.log(" " + "-".repeat(54));
142
+ console.log('\n Proposed upgrades:\n')
143
+ console.log(' ' + 'Package'.padEnd(30) + 'From'.padEnd(14) + 'To')
144
+ console.log(' ' + '-'.repeat(54))
138
145
  for (const u of upgrades) {
139
- console.log(` ${u.name.padEnd(30)}${u.from.padEnd(14)}${u.to}`);
146
+ console.log(` ${u.name.padEnd(30)}${u.from.padEnd(14)}${u.to}`)
140
147
  }
141
- console.log();
148
+ console.log()
142
149
 
143
150
  if (options.dryRun) {
144
- logger.info("Dry run — no changes were made.");
145
- return;
151
+ logger.info('Dry run — no changes were made.')
152
+ return
146
153
  }
147
154
 
148
- const proceed = await confirm("Apply these upgrades to package.json?");
155
+ const proceed = await confirm('Apply these upgrades to package.json?')
149
156
  if (!proceed) {
150
- logger.warn("Upgrade cancelled.");
151
- return;
157
+ logger.warn('Upgrade cancelled.')
158
+ return
152
159
  }
153
160
 
154
161
  for (const u of upgrades) {
155
- const prefix = pkg[u.section][u.name]?.match(/^[\^~]/)?.[0] ?? "^";
156
- pkg[u.section][u.name] = `${prefix}${u.to}`;
162
+ const prefix = pkg[u.section][u.name]?.match(/^[\^~]/)?.[0] ?? '^'
163
+ pkg[u.section][u.name] = `${prefix}${u.to}`
157
164
  }
158
165
 
159
- await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
166
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8')
167
+
168
+ logger.success(`Updated ${upgrades.length} package(s) in package.json.`)
169
+ logger.info('Run "pnpm install" to apply the upgrade.')
160
170
 
161
- logger.success(`Updated ${upgrades.length} package(s) in package.json.`);
162
- logger.info('Run "pnpm install" to apply the upgrade.');
171
+ // A cms-core bump can introduce new models/migrations. Bumping the version
172
+ // alone leaves the consumer's Prisma schema stale, so point them at db:sync.
173
+ if (upgrades.some((u) => u.name === '@actuate-media/cms-core')) {
174
+ logger.info(
175
+ 'cms-core changed — after installing, run "actuate db:sync" then "npx prisma migrate deploy" to update your Prisma schema.',
176
+ )
177
+ }
163
178
  }
164
179
 
165
180
  export function registerUpgradeCommand(program: Command): void {
166
181
  program
167
- .command("upgrade")
168
- .description("Upgrade @actuate-media/* packages to the latest or a specific version")
169
- .option("--latest", "Upgrade to the latest version (default)", true)
170
- .option("--version <ver>", "Upgrade to a specific version")
171
- .option("--dry-run", "Show proposed changes without modifying files")
172
- .action(runUpgrade);
182
+ .command('upgrade')
183
+ .description('Upgrade @actuate-media/* packages to the latest or a specific version')
184
+ .option('--latest', 'Upgrade to the latest version (default)', true)
185
+ .option('--version <ver>', 'Upgrade to a specific version')
186
+ .option('--dry-run', 'Show proposed changes without modifying files')
187
+ .action(runUpgrade)
173
188
  }