mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-01 04:02:35 +00:00
* 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>
149 lines
5.5 KiB
JavaScript
149 lines
5.5 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2026 Qwen Team
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { spawnSync } from 'node:child_process';
|
|
import {
|
|
copyFileSync,
|
|
mkdirSync,
|
|
mkdtempSync,
|
|
readFileSync,
|
|
rmSync,
|
|
symlinkSync,
|
|
writeFileSync,
|
|
} from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { dirname, join } from 'node:path';
|
|
import { expect, it } from 'vitest';
|
|
import semver from 'semver';
|
|
import { INDEPENDENT_PACKAGES } from '../release-packages.mjs';
|
|
import { getWorkspacePackageJsonPaths } from '../workspaces.js';
|
|
|
|
// A coupled manifest no later step rewrites: steps 6-8 of version.js only
|
|
// touch the root, packages/cli (config.sandboxImageUri) and packages/channels/*
|
|
// (the @qwen-code/channel-base pin), so any text change here is pnpm's.
|
|
const LAYOUT_CANARY = 'packages/vscode-ide-companion/package.json';
|
|
|
|
/**
|
|
* Put a manifest into a layout `pnpm version` rewrites: dependency entries in
|
|
* the reverse of the sorted order pnpm emits, plus an empty block pnpm drops.
|
|
* The tree as committed is already in pnpm's order, so without this the
|
|
* restore loop in version.js has nothing to undo and a raw-text assertion
|
|
* would stay green with the loop deleted.
|
|
*/
|
|
function perturbManifestLayout(text) {
|
|
const manifest = JSON.parse(text);
|
|
manifest.dependencies = Object.fromEntries(
|
|
Object.entries(manifest.dependencies).reverse(),
|
|
);
|
|
manifest.optionalDependencies = {};
|
|
return JSON.stringify(manifest, null, 2) + '\n';
|
|
}
|
|
|
|
it.each(['patch', '99.0.0-preview.1'])(
|
|
'aligns coupled versions (%s) without changing independent packages or the pnpm lockfile',
|
|
(version) => {
|
|
const root = process.cwd();
|
|
const directory = mkdtempSync(join(tmpdir(), 'release-versioning-'));
|
|
const read = (path) =>
|
|
JSON.parse(readFileSync(join(directory, path), 'utf8'));
|
|
const manifest = JSON.parse(readFileSync('package.json', 'utf8'));
|
|
const paths = getWorkspacePackageJsonPaths(root, manifest.workspaces);
|
|
const extensionPath =
|
|
'integrations/external-context-mem0/qwen-extension.json';
|
|
try {
|
|
for (const path of [
|
|
'package.json',
|
|
'pnpm-workspace.yaml',
|
|
'pnpm-lock.yaml',
|
|
'.pnpmfile.mjs',
|
|
'scripts/version.js',
|
|
'scripts/keep-manifest-layout.js',
|
|
'scripts/release-packages.mjs',
|
|
'scripts/workspaces.js',
|
|
extensionPath,
|
|
...paths,
|
|
]) {
|
|
mkdirSync(dirname(join(directory, path)), { recursive: true });
|
|
copyFileSync(join(root, path), join(directory, path));
|
|
}
|
|
symlinkSync(
|
|
join(root, 'node_modules'),
|
|
join(directory, 'node_modules'),
|
|
'junction',
|
|
);
|
|
// An internal workspace can lag behind; patch must resolve from the root,
|
|
// not bump each package's existing version independently.
|
|
const internalPath = 'packages/core/package.json';
|
|
writeFileSync(
|
|
join(directory, internalPath),
|
|
JSON.stringify({ ...read(internalPath), version: '0.0.1' }),
|
|
);
|
|
const layoutBefore = perturbManifestLayout(
|
|
readFileSync(join(directory, LAYOUT_CANARY), 'utf8'),
|
|
);
|
|
writeFileSync(join(directory, LAYOUT_CANARY), layoutBefore);
|
|
const result = spawnSync(
|
|
process.execPath,
|
|
['scripts/version.js', version],
|
|
{
|
|
cwd: directory,
|
|
encoding: 'utf8',
|
|
timeout: 60_000,
|
|
},
|
|
);
|
|
expect(result.status, result.stdout + result.stderr).toBe(0);
|
|
const expected =
|
|
version === 'patch' ? semver.inc(manifest.version, 'patch') : version;
|
|
expect(read('package.json').version).toBe(expected);
|
|
for (const path of paths) {
|
|
const before = JSON.parse(readFileSync(join(root, path), 'utf8'));
|
|
const after = read(path);
|
|
if (INDEPENDENT_PACKAGES.includes(before.name)) {
|
|
expect(after, path).toEqual(before);
|
|
} else {
|
|
expect(after.version, path).toBe(expected);
|
|
}
|
|
if (
|
|
before.dependencies?.['@qwen-code/channel-base'] &&
|
|
!before.dependencies['@qwen-code/channel-base'].startsWith('file:')
|
|
) {
|
|
expect(after.dependencies['@qwen-code/channel-base'], path).toBe(
|
|
expected,
|
|
);
|
|
}
|
|
expect(after.private, path).toBe(before.private);
|
|
}
|
|
expect(read(extensionPath).version).toBe(expected);
|
|
for (const path of ['package.json', 'packages/cli/package.json']) {
|
|
expect(read(path).config.sandboxImageUri).toBe(
|
|
`ghcr.io/qwenlm/qwen-code:${expected}`,
|
|
);
|
|
}
|
|
expect(readFileSync(join(directory, 'pnpm-lock.yaml'), 'utf8')).toBe(
|
|
readFileSync(join(root, 'pnpm-lock.yaml'), 'utf8'),
|
|
);
|
|
// Raw text, not parsed JSON: every manifest comparison above goes through
|
|
// JSON.parse, so it is key-order-insensitive and blind to a dropped empty
|
|
// dependency block. This is the assertion that pins version.js's
|
|
// snapshot/restore wiring — deleting that loop, moving the snapshot below
|
|
// the two `pnpm version` calls, or inverting its `updated !== original`
|
|
// guard all let pnpm's re-serialized layout through, which is what
|
|
// reordered the companion's generated NOTICES.txt in #12486.
|
|
expect(
|
|
readFileSync(join(directory, LAYOUT_CANARY), 'utf8'),
|
|
LAYOUT_CANARY,
|
|
).toBe(
|
|
layoutBefore.replace(
|
|
/^ {2}"version": "[^"]*"/m,
|
|
() => ` "version": ${JSON.stringify(expected)}`,
|
|
),
|
|
);
|
|
} finally {
|
|
rmSync(directory, { recursive: true, force: true });
|
|
}
|
|
},
|
|
60_000,
|
|
);
|