qwen-code/scripts/version.js
易良 6b863adcac
fix(release): keep manifest layout when bumping versions (#12487)
* fix(vscode-ide-companion): make NOTICES.txt order-independent and retry release installs

Sort notice entries by package name and version so reordering the
companion's dependencies block (as the v0.24.4 release commit did) no
longer rewrites every section of NOTICES.txt and fails the main-branch
up-to-date check. NOTICES.txt is re-sorted to match; its line content
is unchanged.

Wrap both corepack pnpm install steps in the VS Code companion release
workflow in the bounded retry e2e.yml already uses, so a registry
connect timeout while corepack fetches pnpm no longer fails a release
leg that a plain re-run passes.

Fixes #12486
Fixes #12480

* fix(ci): run the release install retry under bash on windows-latest

The build matrix has no default shell, so windows-latest would run the
new bash retry loop under pwsh and fail every release leg there.

* fix(ci): prefetch pnpm and cache the pnpm store in the companion release

Fetch pnpm with corepack install under the same bounded retry before
anything else invokes it, then restore the pnpm store through the
repo's pnpm-store-cache action, as ci.yml and e2e.yml already do. The
cache takes most dependency downloads off the registry on a hit; the
install keeps its retry for cache misses.

* fix(release): keep manifest layout when bumping versions

pnpm version re-serializes every manifest it rewrites, sorting
dependency blocks and dropping empty ones. The v0.24.4 bump moved
@modelcontextprotocol/sdk inside the VS Code companion's dependencies,
which reordered its generated NOTICES.txt and failed main CI. Snapshot
the tracked manifests before the bump and write back the original text
with only the top-level version replaced.

This also reverts this branch's earlier NOTICES sorting and release
workflow retry/cache changes.

Fixes #12486

* fix(release): keep the version snapshot independent of git

scripts/version.js built its pre-bump manifest snapshot with
`git ls-files -- "*package.json"`, which exits 128 outside a git work tree.
scripts/tests/release-versioning.test.js spawns the real script with cwd set
to a fresh mkdtemp directory, so execSync threw before `pnpm version` ran and
both cases failed the `expect(result.status).toBe(0)` assertion.

Enumerate the root manifest plus the workspace globs through
getWorkspacePackageJsonPaths instead. That matches the git-independent design
the rest of the script already follows (both `pnpm version` calls pass
--no-git-checks) and covers exactly the manifests the bump rewrites. Also copy
the new scripts/keep-manifest-layout.js into the test fixture, since it is now
part of version.js's import closure.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-conflict/jmud0xfc6f0

* test(release): pin the manifest snapshot/restore wiring with raw text

Every manifest comparison in this suite went through JSON.parse, so key order
and a dropped empty dependency block were invisible to it: deleting the restore
loop in scripts/version.js, moving the snapshot below the two `pnpm version`
calls, or inverting its `updated !== original` guard all left it green.

Perturb one coupled manifest that steps 6-8 never rewrite
(packages/vscode-ide-companion) into the layout pnpm re-serializes -- the shape
that reordered its generated NOTICES.txt and broke main in #12486 -- then assert
its raw text afterwards equals that perturbed text with only the top-level
version value replaced. The tree as committed is already in pnpm's order, so
without the perturbation the assertion would be vacuous.

Mutation-verified: all three mutants above now fail, on both it.each cases.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmud8scl6ff

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-09-23 02:54:57 +00:00

122 lines
4.6 KiB
JavaScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { execSync } from 'node:child_process';
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { keepManifestLayout } from './keep-manifest-layout.js';
import { INDEPENDENT_PACKAGES } from './release-packages.mjs';
import { getWorkspacePackageJsonPaths } from './workspaces.js';
// A script to handle versioning and ensure all related changes are in a single, atomic commit.
function run(command) {
console.log(`> ${command}`);
execSync(command, { stdio: 'inherit' });
}
function readJson(filePath) {
return JSON.parse(readFileSync(filePath, 'utf-8'));
}
function writeJson(filePath, data) {
writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
}
// 1. Get the version from the command line arguments.
const versionType = process.argv[2];
if (!versionType) {
console.error('Error: No version specified.');
console.error(
'Usage: npm run version <version> (e.g., 1.2.3 or patch|minor|major|prerelease)',
);
process.exit(1);
}
// `pnpm version` re-sorts every manifest it rewrites; snapshot them so the
// bump below can be reduced to a version change (see keep-manifest-layout.js).
// Enumerate with the workspace globs rather than `git ls-files`: like the
// `--no-git-checks` flags below, this script must not require a git work tree.
const { workspaces } = readJson('package.json');
const snapshotPaths = [
'package.json',
...getWorkspacePackageJsonPaths(process.cwd(), workspaces),
];
const originalManifests = new Map(
snapshotPaths.map((file) => [file, readFileSync(file, 'utf-8')]),
);
// Resolve patch/minor/etc. once, then align all release workspaces to it.
run(
`corepack pnpm version ${versionType} --no-git-tag-version --allow-same-version --no-git-checks`,
);
const rootPackageJsonPath = resolve(process.cwd(), 'package.json');
const newVersion = readJson(rootPackageJsonPath).version;
run(
`corepack pnpm -r ${INDEPENDENT_PACKAGES.map((name) => `--filter="!${name}"`).join(' ')} version ${newVersion} --no-git-tag-version --allow-same-version --no-git-checks`,
);
for (const [file, original] of originalManifests) {
const updated = readFileSync(file, 'utf-8');
if (updated !== original) {
writeFileSync(file, keepManifestLayout(original, updated));
}
}
// 5. Keep the published Mem0 Extension manifest aligned with its package.
const mem0ManifestPath = resolve(
process.cwd(),
'integrations/external-context-mem0/qwen-extension.json',
);
const mem0Manifest = readJson(mem0ManifestPath);
mem0Manifest.version = newVersion;
writeJson(mem0ManifestPath, mem0Manifest);
run(
'npx prettier --experimental-cli --write integrations/external-context-mem0/qwen-extension.json',
);
// 6. Update the sandboxImageUri in the root package.json
const rootPackageJson = readJson(rootPackageJsonPath);
if (rootPackageJson.config?.sandboxImageUri) {
rootPackageJson.config.sandboxImageUri =
rootPackageJson.config.sandboxImageUri.replace(/:.*$/, `:${newVersion}`);
console.log(`Updated sandboxImageUri in root to use version ${newVersion}`);
writeJson(rootPackageJsonPath, rootPackageJson);
}
// 7. Update the sandboxImageUri in the cli package.json
const cliPackageJsonPath = resolve(process.cwd(), 'packages/cli/package.json');
const cliPackageJson = readJson(cliPackageJsonPath);
if (cliPackageJson.config?.sandboxImageUri) {
cliPackageJson.config.sandboxImageUri =
cliPackageJson.config.sandboxImageUri.replace(/:.*$/, `:${newVersion}`);
console.log(
`Updated sandboxImageUri in cli package to use version ${newVersion}`,
);
writeJson(cliPackageJsonPath, cliPackageJson);
}
// 8. Pin channel adapters' semver dependency on @qwen-code/channel-base to
// the exact new version. A caret range like ^0.21.0 does not match a
// prerelease bump (e.g. 0.21.1-preview.0), so npm would replace the workspace
// link with the stale registry package and the release build would compile
// against outdated types.
const channelsDir = resolve(process.cwd(), 'packages/channels');
for (const entry of readdirSync(channelsDir)) {
const pkgPath = join(channelsDir, entry, 'package.json');
if (!existsSync(pkgPath)) continue;
const pkg = readJson(pkgPath);
const dep = pkg.dependencies?.['@qwen-code/channel-base'];
if (dep && !dep.startsWith('file:')) {
pkg.dependencies['@qwen-code/channel-base'] = newVersion;
writeJson(pkgPath, pkg);
console.log(
`Pinned @qwen-code/channel-base to ${newVersion} in ${pkg.name}`,
);
}
}
console.log(`Successfully bumped versions to v${newVersion}.`);