@@ -60,6 +60,7 @@ const MOOSE_PY_LIB_PATH = path.resolve(
6060// Path to npm-installed CLI (will be set by checkLatestPublishedCLI)
6161let LATEST_CLI_PATH : string ;
6262let CLI_INSTALL_DIR : string ;
63+ let LATEST_MATCHING_NPM_VERSION : string ;
6364
6465const testLogger = logger . scope ( "backward-compatibility-test" ) ;
6566const NPM_PROPAGATION_MAX_ATTEMPTS = 5 ;
@@ -72,12 +73,57 @@ system.forceSearchAttributesCacheRefreshOnRead:
7273 constraints: {}
7374` ;
7475
76+ function parseNpmVersions ( stdout : string ) : string [ ] {
77+ const parsed = JSON . parse ( stdout . trim ( ) ) ;
78+ return Array . isArray ( parsed ) ? parsed : [ parsed ] ;
79+ }
80+
81+ function isStableVersion ( version : string ) : boolean {
82+ return / ^ \d + \. \d + \. \d + $ / . test ( version ) ;
83+ }
84+
85+ function compareStableVersions ( a : string , b : string ) : number {
86+ const aParts = a . split ( "." ) . map ( Number ) ;
87+ const bParts = b . split ( "." ) . map ( Number ) ;
88+
89+ for ( let i = 0 ; i < 3 ; i += 1 ) {
90+ const diff = aParts [ i ] - bParts [ i ] ;
91+ if ( diff !== 0 ) return diff ;
92+ }
93+
94+ return 0 ;
95+ }
96+
97+ async function getLatestMatchingNpmVersion ( ) : Promise < string > {
98+ const [ cliVersionsResult , libVersionsResult ] = await Promise . all ( [
99+ execAsync ( "npm view @514labs/moose-cli versions --json" ) ,
100+ execAsync ( "npm view @514labs/moose-lib versions --json" ) ,
101+ ] ) ;
102+
103+ const cliVersions = new Set (
104+ parseNpmVersions ( cliVersionsResult . stdout ) . filter ( isStableVersion ) ,
105+ ) ;
106+ const matchingVersions = parseNpmVersions ( libVersionsResult . stdout )
107+ . filter ( isStableVersion )
108+ . filter ( ( version ) => cliVersions . has ( version ) )
109+ . sort ( compareStableVersions ) ;
110+
111+ const latestMatchingVersion = matchingVersions [ matchingVersions . length - 1 ] ;
112+ if ( ! latestMatchingVersion ) {
113+ throw new Error (
114+ "Cannot find a stable npm version published for both @514labs/moose-cli and @514labs/moose-lib" ,
115+ ) ;
116+ }
117+
118+ return latestMatchingVersion ;
119+ }
120+
75121/**
76- * Install and check the latest published version of moose-cli
122+ * Install and check the latest matching published version of moose-cli
77123 * Uses pnpm for consistency with the monorepo
78124 */
79125async function checkLatestPublishedCLI ( ) : Promise < void > {
80- testLogger . info ( "Installing latest published moose-cli from npm..." ) ;
126+ testLogger . info ( "Installing latest matching published moose-cli from npm..." ) ;
81127
82128 try {
83129 if ( CLI_INSTALL_DIR ) {
@@ -89,9 +135,14 @@ async function checkLatestPublishedCLI(): Promise<void> {
89135 CLI_INSTALL_DIR = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "moose-cli-" ) ) ;
90136 testLogger . info ( `Installing CLI to temp directory: ${ CLI_INSTALL_DIR } ` ) ;
91137
138+ LATEST_MATCHING_NPM_VERSION = await getLatestMatchingNpmVersion ( ) ;
139+ testLogger . info (
140+ `Latest stable version published for both CLI and lib: ${ LATEST_MATCHING_NPM_VERSION } ` ,
141+ ) ;
142+
92143 // Install CLI using pnpm (consistent with monorepo)
93144 const installResult = await execAsync (
94- " pnpm add @514labs/moose-cli@latest" ,
145+ ` pnpm add @514labs/moose-cli@${ LATEST_MATCHING_NPM_VERSION } ` ,
95146 { cwd : CLI_INSTALL_DIR } ,
96147 ) ;
97148 testLogger . info ( "pnpm install output:" , installResult . stdout ) ;
@@ -112,13 +163,13 @@ async function checkLatestPublishedCLI(): Promise<void> {
112163 const { stdout : version } = await execAsync (
113164 `"${ LATEST_CLI_PATH } " --version` ,
114165 ) ;
115- testLogger . info ( "Latest published CLI version:" , version . trim ( ) ) ;
166+ testLogger . info ( "Latest matching published CLI version:" , version . trim ( ) ) ;
116167 } catch ( error : any ) {
117168 testLogger . error ( "Failed to install latest CLI:" , error . message ) ;
118169 if ( error . stdout ) testLogger . error ( "stdout:" , error . stdout ) ;
119170 if ( error . stderr ) testLogger . error ( "stderr:" , error . stderr ) ;
120171 throw new Error (
121- "Cannot install latest published CLI for backward compatibility test" ,
172+ "Cannot install latest matching published CLI for backward compatibility test" ,
122173 ) ;
123174 }
124175}
@@ -189,7 +240,7 @@ async function verifyTypeScriptVersionsMatch(
189240}
190241
191242/**
192- * Ensure generated TypeScript project uses latest published Moose packages.
243+ * Ensure generated TypeScript project uses the latest matching published Moose packages.
193244 */
194245function enforceLatestTypeScriptDependencies ( projectDir : string ) : void {
195246 const packageJsonPath = path . join ( projectDir , "package.json" ) ;
@@ -198,9 +249,10 @@ function enforceLatestTypeScriptDependencies(projectDir: string): void {
198249 const dependencies = ( packageJson . dependencies ??= { } ) ;
199250 const devDependencies = ( packageJson . devDependencies ??= { } ) ;
200251
201- // Enforce latest Moose packages for backward compatibility initialization.
202- dependencies [ "@514labs/moose-lib" ] = "latest" ;
203- devDependencies [ "@514labs/moose-cli" ] = "latest" ;
252+ // Enforce the latest coherent release pair for backward compatibility
253+ // initialization. A partially failed release can skew npm latest tags.
254+ dependencies [ "@514labs/moose-lib" ] = LATEST_MATCHING_NPM_VERSION ;
255+ devDependencies [ "@514labs/moose-cli" ] = LATEST_MATCHING_NPM_VERSION ;
204256
205257 // Keep compatibility alias used across e2e tests.
206258 if ( dependencies [ "@confluentinc/kafka-javascript" ] ) {
@@ -283,7 +335,7 @@ function startLegacyTemporalConfigWriter(projectDir: string): () => void {
283335}
284336
285337/**
286- * Setup TypeScript project with latest npm moose-lib
338+ * Setup TypeScript project with latest matching npm moose-lib
287339 */
288340async function setupTypeScriptProjectWithLatestNpm (
289341 projectDir : string ,
@@ -292,7 +344,7 @@ async function setupTypeScriptProjectWithLatestNpm(
292344) : Promise < void > {
293345 for ( let attempt = 1 ; attempt <= NPM_PROPAGATION_MAX_ATTEMPTS ; attempt += 1 ) {
294346 testLogger . info (
295- `Initializing TypeScript project with latest npm moose-cli (attempt ${ attempt } /${ NPM_PROPAGATION_MAX_ATTEMPTS } )...` ,
347+ `Initializing TypeScript project with latest matching npm moose-cli (attempt ${ attempt } /${ NPM_PROPAGATION_MAX_ATTEMPTS } )...` ,
296348 ) ;
297349
298350 try {
@@ -312,7 +364,7 @@ async function setupTypeScriptProjectWithLatestNpm(
312364 }
313365
314366 testLogger . info (
315- "Installing dependencies with pnpm (using latest @514labs/moose-lib)..." ,
367+ "Installing dependencies with pnpm (using latest matching @514labs/moose-lib)..." ,
316368 ) ;
317369
318370 enforceLatestTypeScriptDependencies ( projectDir ) ;
@@ -457,7 +509,7 @@ describe("Backward Compatibility Tests", function () {
457509 before ( async function ( ) {
458510 this . timeout ( TIMEOUTS . TEST_SETUP_MS ) ;
459511
460- // Check latest published CLI is available
512+ // Check latest matching published CLI is available
461513 await checkLatestPublishedCLI ( ) ;
462514
463515 // Verify new CLI is built
0 commit comments