Skip to content

Commit 22c6b14

Browse files
authored
comment to release (#4095)
<!-- CURSOR_SUMMARY --> > [!NOTE] > **Medium Risk** > Touches release/publish automation and backward-compatibility gating; mistakes could block releases or publish incorrect dist-tags, but changes are localized to CI/scripts and test setup. > > **Overview** > Improves release robustness by making the CLI build workflow tolerate `sccache` setup failures (`continue-on-error`) instead of failing the job. > > Hardens npm publishing scripts (`release-bin.sh`, `release-cli.sh`) to be idempotent: they now detect already-published versions and either skip publishing or fail if the version exists but `latest` points elsewhere. > > Updates the backward-compat E2E test to install and pin the **latest stable version published for both** `@514labs/moose-cli` and `@514labs/moose-lib` (rather than `@latest`), avoiding mismatches when npm `latest` tags are temporarily skewed by partial releases. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 4afc459. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 38d277e commit 22c6b14

4 files changed

Lines changed: 100 additions & 15 deletions

File tree

.github/workflows/release-cli.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ jobs:
324324

325325
- name: Run sccache-cache
326326
if: ${{ matrix.build.OS != 'ubuntu-22-8-core' }}
327+
continue-on-error: true
327328
uses: mozilla-actions/sccache-action@v0.0.9
328329
with:
329330
# sccache-action points at the latest sccache version

apps/framework-cli-e2e/test/backward-compatibility.test.ts

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ const MOOSE_PY_LIB_PATH = path.resolve(
6060
// Path to npm-installed CLI (will be set by checkLatestPublishedCLI)
6161
let LATEST_CLI_PATH: string;
6262
let CLI_INSTALL_DIR: string;
63+
let LATEST_MATCHING_NPM_VERSION: string;
6364

6465
const testLogger = logger.scope("backward-compatibility-test");
6566
const 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
*/
79125
async 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
*/
194245
function 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
*/
288340
async 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

apps/moose-cli-npm/scripts/release-bin.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,28 @@ cp "../../target/${build_target}/release/${current_bin}" "../../target/${build_t
4040
cp "../../target/${build_target}/release/${current_bin}" "${node_pkg}/bin"
4141
# publish the package
4242
cd "${node_pkg}"
43+
package_name=$(node -p "require('./package.json').name")
44+
package_version=$(node -p "require('./package.json').version")
4345
# For CI builds (TAG_LATEST=false), publish with version-specific tag
4446
# For release builds (TAG_LATEST=true), publish and update the 'latest' tag
4547
if [ "${TAG_LATEST}" = "true" ]; then
4648
# Release build - publish and update 'latest' tag
49+
if npm view "${package_name}@${package_version}" version >/dev/null 2>&1; then
50+
current_latest=$(npm view "${package_name}" dist-tags.latest 2>/dev/null || true)
51+
if [ "${current_latest}" = "${package_version}" ]; then
52+
echo "${package_name}@${package_version} is already published and latest already points to it"
53+
exit 0
54+
fi
55+
56+
echo "${package_name}@${package_version} is already published, but latest points to ${current_latest}"
57+
exit 1
58+
fi
4759
npm publish --access public
4860
else
4961
# CI build - publish with dev tag (doesn't update 'latest')
62+
if npm view "${package_name}@${package_version}" version >/dev/null 2>&1; then
63+
echo "${package_name}@${package_version} is already published; skipping publish"
64+
exit 0
65+
fi
5066
npm publish --access public --tag dev
51-
fi
67+
fi

apps/moose-cli-npm/scripts/release-cli.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,28 @@ pnpm install --filter "@514labs/moose-cli" --no-frozen-lockfile # requires optio
2626
pnpm build --filter @514labs/moose-cli
2727

2828
cd apps/moose-cli-npm
29+
package_name=$(node -p "require('./package.json').name")
30+
package_version=$(node -p "require('./package.json').version")
2931
# For CI builds (TAG_LATEST=false), publish with version-specific tag
3032
# For release builds (TAG_LATEST=true), publish and update the 'latest' tag
3133
if [ "${TAG_LATEST}" = "true" ]; then
3234
# Release build - publish and update 'latest' tag
35+
if npm view "${package_name}@${package_version}" version >/dev/null 2>&1; then
36+
current_latest=$(npm view "${package_name}" dist-tags.latest 2>/dev/null || true)
37+
if [ "${current_latest}" = "${package_version}" ]; then
38+
echo "${package_name}@${package_version} is already published and latest already points to it"
39+
exit 0
40+
fi
41+
42+
echo "${package_name}@${package_version} is already published, but latest points to ${current_latest}"
43+
exit 1
44+
fi
3345
pnpm publish --access public --no-git-checks
3446
else
3547
# CI build - publish with dev tag (doesn't update 'latest')
48+
if npm view "${package_name}@${package_version}" version >/dev/null 2>&1; then
49+
echo "${package_name}@${package_version} is already published; skipping publish"
50+
exit 0
51+
fi
3652
pnpm publish --access public --no-git-checks --tag dev
37-
fi
53+
fi

0 commit comments

Comments
 (0)