mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-10-20 05:11:33 +00:00
* refactor(release): Use pnpm for workspace publishing and versioning * fix(ci): revert desktop-release install to npm ci for Windows compatibility The pnpm migration added a bash conditional to check for pnpm-lock.yaml, but the step did not specify shell: 'bash'. Windows runners default to PowerShell, which cannot parse bash syntax, causing the build to fail with a ParserError. Since desktop-release always checks out the same commit and the root lockfile is now pnpm-lock.yaml, the conditional is unnecessary. Revert to the simple npm ci command that works on all platforms. Fixes the Windows build failure in Desktop release v0.24.2. * refactor(build): Schedule workspace builds with pnpm * fix(ci): Preserve Windows release installation and version filters --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
128 lines
4.2 KiB
JavaScript
128 lines
4.2 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2026 Qwen Team
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { execSync } from 'node:child_process';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { RELEASE_WORKSPACES } from './release-packages.mjs';
|
|
|
|
export const PUBLISHED_PACKAGES = [
|
|
'@qwen-code/qwen-code',
|
|
...RELEASE_WORKSPACES,
|
|
];
|
|
|
|
// Deliberately a copy of `scripts/lib/release-helpers.js`, not an import:
|
|
// the workflow's sparse checkout excludes `scripts/lib/`, so importing it
|
|
// resolves in every test lane and throws ERR_MODULE_NOT_FOUND only in the
|
|
// publish job. `scripts/tests/release-workflow.test.js` pins the two bodies
|
|
// identical — widen or harden them together, and never widen this copy alone:
|
|
// treating a rate-limited probe as "release absent" would let the force push
|
|
// proceed over a shipped version.
|
|
function isExpectedMissingGitHubRelease(error) {
|
|
const stderr = error.stderr?.toString() ?? '';
|
|
const stdout = error.stdout?.toString() ?? '';
|
|
const message = `${error.message}\n${stderr}\n${stdout}`;
|
|
return message.includes('release not found') || message.includes('Not Found');
|
|
}
|
|
|
|
export function assertVersionUnreleased(version) {
|
|
if (
|
|
typeof version !== 'string' ||
|
|
!/^\d+\.\d+\.\d+(?:-preview\.\d+|-nightly\.\d{8}\.[0-9a-f]+)?$/.test(
|
|
version,
|
|
)
|
|
) {
|
|
const error = new Error(
|
|
'assert-unreleased requires a version in release format, e.g. --assert-unreleased=1.2.3',
|
|
);
|
|
// Not a probe failure: a malformed version is refused identically on
|
|
// every attempt, so it must not land in the exit-2 bucket that
|
|
// run-release-step.sh retries three times while logging it as one
|
|
// transient probe error.
|
|
error.code = 'VERSION_FORMAT';
|
|
throw error;
|
|
}
|
|
|
|
const shippedTo = [];
|
|
for (const pkg of PUBLISHED_PACKAGES) {
|
|
try {
|
|
const output = execSync(`npm view ${pkg}@${version} version`)
|
|
.toString()
|
|
.trim();
|
|
if (output === version) shippedTo.push(pkg);
|
|
} catch (error) {
|
|
if (shippedTo.length === 0 && !error.message?.includes('E404')) {
|
|
throw new Error(
|
|
`Failed to verify ${pkg}@${version} on npm: ${error.message}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (shippedTo.length === 0) {
|
|
try {
|
|
execSync(`git ls-remote --exit-code origin "refs/tags/v${version}"`);
|
|
shippedTo.push(`origin tag v${version}`);
|
|
} catch (error) {
|
|
if (error.status !== 2) {
|
|
throw new Error(
|
|
`Failed to verify tag v${version} on origin: ${error.message}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (shippedTo.length === 0) {
|
|
try {
|
|
const output = execSync(
|
|
`gh release view "v${version}" --json tagName --jq .tagName`,
|
|
)
|
|
.toString()
|
|
.trim();
|
|
if (output === `v${version}`)
|
|
shippedTo.push(`GitHub release v${version}`);
|
|
} catch (error) {
|
|
if (!isExpectedMissingGitHubRelease(error)) {
|
|
throw new Error(
|
|
`Failed to verify release v${version} on GitHub: ${error.message}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (shippedTo.length > 0) {
|
|
const error = new Error(
|
|
`Version ${version} has already shipped; refusing to force-push the release branch over it. Found on: ${shippedTo.join(', ')}. If a previous attempt published only part of the release, complete the remaining artifacts manually — re-running this job will keep failing here while the version stays published.`,
|
|
);
|
|
error.code = 'VERSION_SHIPPED';
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
export function runAssertVersionCli(version) {
|
|
try {
|
|
assertVersionUnreleased(version);
|
|
} catch (error) {
|
|
console.log(`::error::${error.message}`);
|
|
// 3 = already shipped (decisive, marked so the workflow skips the
|
|
// release-failed notification); 4 = malformed version (decisive, not
|
|
// retryable, and not a benign refusal); 2 = probe failure, retried.
|
|
if (error.code === 'VERSION_SHIPPED') return 3;
|
|
if (error.code === 'VERSION_FORMAT') return 4;
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
const prefix = '--assert-unreleased=';
|
|
const arg = process.argv[2] ?? '';
|
|
const exitCode = runAssertVersionCli(
|
|
arg.startsWith(prefix) ? arg.slice(prefix.length) : '',
|
|
);
|
|
if (exitCode !== 0) process.exit(exitCode);
|
|
}
|