@mondaydotcomorg/z2h-cli 0.23.1 → 0.24.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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=migrate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/migrate.test.ts"],"names":[],"mappings":""}
@@ -1,3 +1,4 @@
1
+ export declare function baseVersion(version: string): string;
1
2
  export interface MigrateOptions {
2
3
  dryRun?: boolean;
3
4
  }
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AA6CA,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkGjG"}
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AA4BA,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMnD;AAqBD,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmGjG"}
@@ -16,6 +16,15 @@ const semver__default = /*#__PURE__*/_interopDefault(semver);
16
16
 
17
17
  const STATE_FILE = '.z2h-migrations-state.json';
18
18
  // ─── Helpers ───
19
+ // A prerelease sorts BELOW its own release under plain semver ordering, so
20
+ // comparing raw beta versions here would skip migrations the beta introduced.
21
+ function baseVersion(version) {
22
+ const parsed = semver__default.default.parse(version);
23
+ if (!parsed) {
24
+ throw new Error(`invalid semver version: ${version}`);
25
+ }
26
+ return `${parsed.major}.${parsed.minor}.${parsed.patch}`;
27
+ }
19
28
  async function readMigrationState(workspaceHome) {
20
29
  const statePath = path__default.default.join(workspaceHome, STATE_FILE);
21
30
  if (!(await fs.pathExists(statePath))) {
@@ -40,11 +49,12 @@ async function migrateCommand(cliVersion, opts = {}) {
40
49
  }
41
50
  const state = await readMigrationState(workspaceHome);
42
51
  const lastVersion = state?.lastMigratedVersion ?? '0.0.0';
43
- if (!semver__default.default.gt(cliVersion, lastVersion)) {
52
+ const targetVersion = baseVersion(cliVersion);
53
+ if (!semver__default.default.gt(targetVersion, lastVersion)) {
44
54
  util_logger.info('workspace is up to date — no migrations pending');
45
55
  return;
46
56
  }
47
- util_logger.info(`migrating workspace from v${lastVersion} → v${cliVersion}...`);
57
+ util_logger.info(`migrating workspace from v${lastVersion} → v${targetVersion}...`);
48
58
  // migrations.json is at the package root — two dirs up from dist/migrations/
49
59
  const thisDir = path__default.default.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('commands/migrate.js', document.baseURI).href))));
50
60
  const migrationsJsonPath = path__default.default.resolve(thisDir, '..', '..', 'migrations.json');
@@ -53,12 +63,12 @@ async function migrateCommand(cliVersion, opts = {}) {
53
63
  return;
54
64
  }
55
65
  const migrationsJson = (await fs.readJson(migrationsJsonPath));
56
- // Filter to pending migrations (version > lastVersion AND <= cliVersion).
66
+ // Filter to pending migrations (version > lastVersion AND <= targetVersion).
57
67
  // CONVENTION: a migration's `version` field must equal the CLI release it
58
68
  // ships with (not the feature branch version). This ensures the `gt` check
59
69
  // always catches a migration on the exact upgrade that introduces it.
60
70
  const pending = Object.entries(migrationsJson.migrations)
61
- .filter(([, entry]) => semver__default.default.gt(entry.version, lastVersion) && semver__default.default.lte(entry.version, cliVersion))
71
+ .filter(([, entry]) => semver__default.default.gt(entry.version, lastVersion) && semver__default.default.lte(entry.version, targetVersion))
62
72
  .sort(([, a], [, b]) => semver__default.default.compare(a.version, b.version));
63
73
  if (opts.dryRun) {
64
74
  util_logger.info(`${pending.length} pending migration(s) (dry-run — no changes will be applied):`);
@@ -68,7 +78,7 @@ async function migrateCommand(cliVersion, opts = {}) {
68
78
  return;
69
79
  }
70
80
  if (pending.length === 0) {
71
- await writeMigrationState(workspaceHome, { lastMigratedVersion: cliVersion });
81
+ await writeMigrationState(workspaceHome, { lastMigratedVersion: targetVersion });
72
82
  return;
73
83
  }
74
84
  const nxReady = await util_workspace.ensureWorkspaceNx(workspaceHome);
@@ -108,8 +118,9 @@ async function migrateCommand(cliVersion, opts = {}) {
108
118
  if (needsInstall) {
109
119
  await util_workspace.runYarnInstall(workspaceHome, 'package.json changed');
110
120
  }
111
- await writeMigrationState(workspaceHome, { lastMigratedVersion: cliVersion });
121
+ await writeMigrationState(workspaceHome, { lastMigratedVersion: targetVersion });
112
122
  util_logger.info('workspace migrations complete.');
113
123
  }
114
124
 
125
+ exports.baseVersion = baseVersion;
115
126
  exports.migrateCommand = migrateCommand;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=migrate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.test.d.ts","sourceRoot":"","sources":["../../../../src/commands/__tests__/migrate.test.ts"],"names":[],"mappings":""}
@@ -1,3 +1,4 @@
1
+ export declare function baseVersion(version: string): string;
1
2
  export interface MigrateOptions {
2
3
  dryRun?: boolean;
3
4
  }
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../src/commands/migrate.ts"],"names":[],"mappings":"AA6CA,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkGjG"}
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../src/commands/migrate.ts"],"names":[],"mappings":"AA4BA,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMnD;AAqBD,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmGjG"}
@@ -8,6 +8,15 @@ import { getWorkspaceHome, isZ2hWorkspace, ensureWorkspaceNx, runYarnInstall } f
8
8
 
9
9
  const STATE_FILE = '.z2h-migrations-state.json';
10
10
  // ─── Helpers ───
11
+ // A prerelease sorts BELOW its own release under plain semver ordering, so
12
+ // comparing raw beta versions here would skip migrations the beta introduced.
13
+ function baseVersion(version) {
14
+ const parsed = semver.parse(version);
15
+ if (!parsed) {
16
+ throw new Error(`invalid semver version: ${version}`);
17
+ }
18
+ return `${parsed.major}.${parsed.minor}.${parsed.patch}`;
19
+ }
11
20
  async function readMigrationState(workspaceHome) {
12
21
  const statePath = path.join(workspaceHome, STATE_FILE);
13
22
  if (!(await pathExists(statePath))) {
@@ -32,11 +41,12 @@ async function migrateCommand(cliVersion, opts = {}) {
32
41
  }
33
42
  const state = await readMigrationState(workspaceHome);
34
43
  const lastVersion = state?.lastMigratedVersion ?? '0.0.0';
35
- if (!semver.gt(cliVersion, lastVersion)) {
44
+ const targetVersion = baseVersion(cliVersion);
45
+ if (!semver.gt(targetVersion, lastVersion)) {
36
46
  info('workspace is up to date — no migrations pending');
37
47
  return;
38
48
  }
39
- info(`migrating workspace from v${lastVersion} → v${cliVersion}...`);
49
+ info(`migrating workspace from v${lastVersion} → v${targetVersion}...`);
40
50
  // migrations.json is at the package root — two dirs up from dist/migrations/
41
51
  const thisDir = path.dirname(fileURLToPath(import.meta.url));
42
52
  const migrationsJsonPath = path.resolve(thisDir, '..', '..', 'migrations.json');
@@ -45,12 +55,12 @@ async function migrateCommand(cliVersion, opts = {}) {
45
55
  return;
46
56
  }
47
57
  const migrationsJson = (await readJson(migrationsJsonPath));
48
- // Filter to pending migrations (version > lastVersion AND <= cliVersion).
58
+ // Filter to pending migrations (version > lastVersion AND <= targetVersion).
49
59
  // CONVENTION: a migration's `version` field must equal the CLI release it
50
60
  // ships with (not the feature branch version). This ensures the `gt` check
51
61
  // always catches a migration on the exact upgrade that introduces it.
52
62
  const pending = Object.entries(migrationsJson.migrations)
53
- .filter(([, entry]) => semver.gt(entry.version, lastVersion) && semver.lte(entry.version, cliVersion))
63
+ .filter(([, entry]) => semver.gt(entry.version, lastVersion) && semver.lte(entry.version, targetVersion))
54
64
  .sort(([, a], [, b]) => semver.compare(a.version, b.version));
55
65
  if (opts.dryRun) {
56
66
  info(`${pending.length} pending migration(s) (dry-run — no changes will be applied):`);
@@ -60,7 +70,7 @@ async function migrateCommand(cliVersion, opts = {}) {
60
70
  return;
61
71
  }
62
72
  if (pending.length === 0) {
63
- await writeMigrationState(workspaceHome, { lastMigratedVersion: cliVersion });
73
+ await writeMigrationState(workspaceHome, { lastMigratedVersion: targetVersion });
64
74
  return;
65
75
  }
66
76
  const nxReady = await ensureWorkspaceNx(workspaceHome);
@@ -100,8 +110,8 @@ async function migrateCommand(cliVersion, opts = {}) {
100
110
  if (needsInstall) {
101
111
  await runYarnInstall(workspaceHome, 'package.json changed');
102
112
  }
103
- await writeMigrationState(workspaceHome, { lastMigratedVersion: cliVersion });
113
+ await writeMigrationState(workspaceHome, { lastMigratedVersion: targetVersion });
104
114
  info('workspace migrations complete.');
105
115
  }
106
116
 
107
- export { migrateCommand };
117
+ export { baseVersion, migrateCommand };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mondaydotcomorg/z2h-cli",
3
- "version": "0.23.1",
3
+ "version": "0.24.0",
4
4
  "bin": "./bin/z2h-cli.js",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -0,0 +1,34 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import semver from 'semver';
3
+ import { baseVersion } from '../migrate';
4
+
5
+ describe('baseVersion', () => {
6
+ it('strips prerelease + build metadata down to major.minor.patch', () => {
7
+ expect(baseVersion('0.23.0-feat-faster-deploy-abc1234')).toBe('0.23.0');
8
+ });
9
+
10
+ it('is a no-op on an already-plain release version', () => {
11
+ expect(baseVersion('0.23.0')).toBe('0.23.0');
12
+ });
13
+
14
+ it('throws on an invalid version string rather than silently misbehaving', () => {
15
+ expect(() => baseVersion('not-a-version')).toThrow(/invalid semver/);
16
+ });
17
+ });
18
+
19
+ describe('prerelease migration gating (regression for the bug baseVersion fixes)', () => {
20
+ const MIGRATION_VERSION = '0.23.0';
21
+ const BETA_CLI_VERSION = '0.23.0-feat-faster-deploy-abc1234';
22
+
23
+ it('demonstrates the bug: a prerelease sorts BELOW its own release under plain semver.lte', () => {
24
+ expect(semver.lte(MIGRATION_VERSION, BETA_CLI_VERSION)).toBe(false);
25
+ });
26
+
27
+ it('the fix: comparing against the coerced base version includes it correctly', () => {
28
+ expect(semver.lte(MIGRATION_VERSION, baseVersion(BETA_CLI_VERSION))).toBe(true);
29
+ });
30
+
31
+ it('a migration registered at a later version than the beta build is still correctly excluded', () => {
32
+ expect(semver.lte('0.24.0', baseVersion(BETA_CLI_VERSION))).toBe(false);
33
+ });
34
+ });
@@ -24,6 +24,16 @@ interface MigrationsJson {
24
24
 
25
25
  // ─── Helpers ───
26
26
 
27
+ // A prerelease sorts BELOW its own release under plain semver ordering, so
28
+ // comparing raw beta versions here would skip migrations the beta introduced.
29
+ export function baseVersion(version: string): string {
30
+ const parsed = semver.parse(version);
31
+ if (!parsed) {
32
+ throw new Error(`invalid semver version: ${version}`);
33
+ }
34
+ return `${parsed.major}.${parsed.minor}.${parsed.patch}`;
35
+ }
36
+
27
37
  async function readMigrationState(workspaceHome: string): Promise<MigrationState | null> {
28
38
  const statePath = path.join(workspaceHome, STATE_FILE);
29
39
  if (!(await pathExists(statePath))) {
@@ -56,13 +66,14 @@ export async function migrateCommand(cliVersion: string, opts: MigrateOptions =
56
66
 
57
67
  const state = await readMigrationState(workspaceHome);
58
68
  const lastVersion = state?.lastMigratedVersion ?? '0.0.0';
69
+ const targetVersion = baseVersion(cliVersion);
59
70
 
60
- if (!semver.gt(cliVersion, lastVersion)) {
71
+ if (!semver.gt(targetVersion, lastVersion)) {
61
72
  info('workspace is up to date — no migrations pending');
62
73
  return;
63
74
  }
64
75
 
65
- info(`migrating workspace from v${lastVersion} → v${cliVersion}...`);
76
+ info(`migrating workspace from v${lastVersion} → v${targetVersion}...`);
66
77
 
67
78
  // migrations.json is at the package root — two dirs up from dist/migrations/
68
79
  const thisDir = path.dirname(fileURLToPath(import.meta.url));
@@ -75,12 +86,12 @@ export async function migrateCommand(cliVersion: string, opts: MigrateOptions =
75
86
 
76
87
  const migrationsJson = (await readJson(migrationsJsonPath)) as MigrationsJson;
77
88
 
78
- // Filter to pending migrations (version > lastVersion AND <= cliVersion).
89
+ // Filter to pending migrations (version > lastVersion AND <= targetVersion).
79
90
  // CONVENTION: a migration's `version` field must equal the CLI release it
80
91
  // ships with (not the feature branch version). This ensures the `gt` check
81
92
  // always catches a migration on the exact upgrade that introduces it.
82
93
  const pending = Object.entries(migrationsJson.migrations)
83
- .filter(([, entry]) => semver.gt(entry.version, lastVersion) && semver.lte(entry.version, cliVersion))
94
+ .filter(([, entry]) => semver.gt(entry.version, lastVersion) && semver.lte(entry.version, targetVersion))
84
95
  .sort(([, a], [, b]) => semver.compare(a.version, b.version));
85
96
 
86
97
  if (opts.dryRun) {
@@ -92,7 +103,7 @@ export async function migrateCommand(cliVersion: string, opts: MigrateOptions =
92
103
  }
93
104
 
94
105
  if (pending.length === 0) {
95
- await writeMigrationState(workspaceHome, { lastMigratedVersion: cliVersion });
106
+ await writeMigrationState(workspaceHome, { lastMigratedVersion: targetVersion });
96
107
  return;
97
108
  }
98
109
 
@@ -143,6 +154,6 @@ export async function migrateCommand(cliVersion: string, opts: MigrateOptions =
143
154
  await runYarnInstall(workspaceHome, 'package.json changed');
144
155
  }
145
156
 
146
- await writeMigrationState(workspaceHome, { lastMigratedVersion: cliVersion });
157
+ await writeMigrationState(workspaceHome, { lastMigratedVersion: targetVersion });
147
158
  info('workspace migrations complete.');
148
159
  }