From cbba9c0fc0d83204d11849a40aacb4c6c84687ff Mon Sep 17 00:00:00 2001 From: yiliang114 Date: Mon, 29 Jun 2026 15:40:34 +0800 Subject: [PATCH 1/3] fix(ci): cover release integration regressions --- .../cli/qwen-serve-client-mcp.test.ts | 9 +++++- packages/chrome-extension/package.json | 4 +-- .../tests/chrome-extension-package.test.js | 31 +++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 scripts/tests/chrome-extension-package.test.js diff --git a/integration-tests/cli/qwen-serve-client-mcp.test.ts b/integration-tests/cli/qwen-serve-client-mcp.test.ts index f87f51829c4..e2cc47de246 100644 --- a/integration-tests/cli/qwen-serve-client-mcp.test.ts +++ b/integration-tests/cli/qwen-serve-client-mcp.test.ts @@ -58,7 +58,12 @@ const REPO_ROOT = path.resolve(__dirname, '../..'); // platform-agnostic, but daemon SIGTERM teardown is cleaner on POSIX. Keep it // running everywhere `ws` works. const SKIP = process.platform === 'win32'; +const SANDBOX_MODE = process.env['QWEN_SANDBOX']?.toLowerCase(); +const SKIP_PROMPTED_MODEL_TEST = Boolean( + SANDBOX_MODE && SANDBOX_MODE !== 'false', +); const describeMaybe = SKIP ? describe.skip : describe; +const itPromptedModelMaybe = SKIP_PROMPTED_MODEL_TEST ? it.skip : it; let daemon: ChildProcess; let port = 0; @@ -417,7 +422,9 @@ describeMaybe('qwen serve — reverse tool channel (client-hosted MCP over WS)', // // This test does session/new THEN mcp_register (the "register after a session // already exists" timing), exercising the fan-out path specifically. - it('drives a model→agent tools/call of chrome_read_page over the reverse WS channel and consumes the result', async () => { + // Under container sandboxing, the ACP child cannot reach the host-loopback + // fake model server used below; keep the discovery-only test running there. + itPromptedModelMaybe('drives a model→agent tools/call of chrome_read_page over the reverse WS channel and consumes the result', async () => { const ws = new WebSocket(`ws://127.0.0.1:${port}/acp`, { headers: { Authorization: `Bearer ${TOKEN}` }, }); diff --git a/packages/chrome-extension/package.json b/packages/chrome-extension/package.json index d0228253f10..d4bfec7f694 100644 --- a/packages/chrome-extension/package.json +++ b/packages/chrome-extension/package.json @@ -24,12 +24,12 @@ "README.md" ], "scripts": { - "dev": "EXTENSION_OUT_DIR=dist/extension node scripts/dev-watch.js", + "dev": "node scripts/dev-watch.js", "debug:mac": "./scripts/debug.sh", "sync:extension": "node scripts/sync-extension.js", "build:bg": "node scripts/sync-extension.js && node config/esbuild.background.config.js", "build:bg:watch": "node scripts/sync-extension.js && node config/esbuild.background.config.js --watch", - "build": "EXTENSION_OUT_DIR=dist/extension npm run clean && EXTENSION_OUT_DIR=dist/extension node scripts/sync-extension.js && EXTENSION_OUT_DIR=dist/extension node config/esbuild.background.config.js --production", + "build": "node scripts/sync-extension.js && node config/esbuild.background.config.js --production", "test": "vitest run --config vitest.config.ts", "test:ci": "vitest run --config vitest.config.ts", "dev:chrome": "open -a 'Google Chrome' --args --load-extension=$PWD/dist/extension --auto-open-devtools-for-tabs", diff --git a/scripts/tests/chrome-extension-package.test.js b/scripts/tests/chrome-extension-package.test.js new file mode 100644 index 00000000000..dfc9f2350e9 --- /dev/null +++ b/scripts/tests/chrome-extension-package.test.js @@ -0,0 +1,31 @@ +/** + * @license + * Copyright 2026 Qwen Team + * SPDX-License-Identifier: Apache-2.0 + */ + +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(__dirname, '../..'); + +describe('chrome extension package scripts', () => { + it('keeps the build script portable for Windows npm lifecycle runs', () => { + const packageJson = JSON.parse( + readFileSync( + path.join(root, 'packages/chrome-extension/package.json'), + 'utf8', + ), + ); + + expect(packageJson.scripts.build).not.toMatch( + /(?:^|\s&&\s)[A-Za-z_][A-Za-z0-9_]*=/, + ); + expect(packageJson.scripts.build).not.toMatch( + /(?:^|\s&&\s)npm run clean(?:\s|$)/, + ); + }); +}); From 2d94520a7400755121be19acdb47a005aec18094 Mon Sep 17 00:00:00 2001 From: yiliang114 Date: Mon, 29 Jun 2026 18:40:42 +0800 Subject: [PATCH 2/3] fix(ci): retry linter archive downloads --- scripts/lint.js | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/scripts/lint.js b/scripts/lint.js index f02b845f765..f26e9ec7111 100644 --- a/scripts/lint.js +++ b/scripts/lint.js @@ -17,6 +17,22 @@ const YAMLLINT_VERSION = '1.35.1'; const TEMP_DIR = join(tmpdir(), 'qwen-code-linters'); +function archiveInstaller(url, archivePath, extractCommand) { + return ` + for attempt in 1 2 3 4 5; do + curl --fail --location --show-error --silent --retry 3 --retry-all-errors --retry-delay 2 --output "${archivePath}" "${url}" \\ + && tar -tf "${archivePath}" >/dev/null \\ + && break + rm -f "${archivePath}" + if [ "$attempt" -eq 5 ]; then + exit 1 + fi + sleep "$((attempt * 2))" + done + ${extractCommand} + `; +} + function getPlatformArch() { const platform = process.platform; const arch = process.arch; @@ -57,11 +73,11 @@ const platformArch = getPlatformArch(); const LINTERS = { actionlint: { check: 'command -v actionlint', - installer: ` - mkdir -p "${TEMP_DIR}/actionlint" - curl -sSLo "${TEMP_DIR}/.actionlint.tgz" "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_${platformArch.actionlint}.tar.gz" - tar -xzf "${TEMP_DIR}/.actionlint.tgz" -C "${TEMP_DIR}/actionlint" - `, + installer: archiveInstaller( + `https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_${platformArch.actionlint}.tar.gz`, + `${TEMP_DIR}/.actionlint.tgz`, + `mkdir -p "${TEMP_DIR}/actionlint" && tar -xzf "${TEMP_DIR}/.actionlint.tgz" -C "${TEMP_DIR}/actionlint"`, + ), run: ` actionlint \ -color \ @@ -73,11 +89,11 @@ const LINTERS = { }, shellcheck: { check: 'command -v shellcheck', - installer: ` - mkdir -p "${TEMP_DIR}/shellcheck" - curl -sSLo "${TEMP_DIR}/.shellcheck.txz" "https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/shellcheck-v${SHELLCHECK_VERSION}.${platformArch.shellcheck}.tar.xz" - tar -xf "${TEMP_DIR}/.shellcheck.txz" -C "${TEMP_DIR}/shellcheck" --strip-components=1 - `, + installer: archiveInstaller( + `https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/shellcheck-v${SHELLCHECK_VERSION}.${platformArch.shellcheck}.tar.xz`, + `${TEMP_DIR}/.shellcheck.txz`, + `mkdir -p "${TEMP_DIR}/shellcheck" && tar -xf "${TEMP_DIR}/.shellcheck.txz" -C "${TEMP_DIR}/shellcheck" --strip-components=1`, + ), run: ` git ls-files | grep -v '^integration-tests/terminal-bench/' | grep -E '^([^.]+|.*\\.(sh|zsh|bash))' | xargs file --mime-type \ | grep "text/x-shellscript" | awk '{ print substr($1, 1, length($1)-1) }' \ From f3f50ec0bcfb32583512f1ce74372d8acf319060 Mon Sep 17 00:00:00 2001 From: yiliang114 Date: Mon, 29 Jun 2026 19:07:18 +0800 Subject: [PATCH 3/3] fix(ci): keep release CI PR focused --- .../cli/qwen-serve-client-mcp.test.ts | 4 +-- scripts/lint.js | 36 ++++++------------- .../tests/chrome-extension-package.test.js | 3 -- 3 files changed, 12 insertions(+), 31 deletions(-) diff --git a/integration-tests/cli/qwen-serve-client-mcp.test.ts b/integration-tests/cli/qwen-serve-client-mcp.test.ts index e2cc47de246..0ac716eaf14 100644 --- a/integration-tests/cli/qwen-serve-client-mcp.test.ts +++ b/integration-tests/cli/qwen-serve-client-mcp.test.ts @@ -58,9 +58,9 @@ const REPO_ROOT = path.resolve(__dirname, '../..'); // platform-agnostic, but daemon SIGTERM teardown is cleaner on POSIX. Keep it // running everywhere `ws` works. const SKIP = process.platform === 'win32'; -const SANDBOX_MODE = process.env['QWEN_SANDBOX']?.toLowerCase(); +const SANDBOX_MODE = process.env['QWEN_SANDBOX']?.toLowerCase().trim(); const SKIP_PROMPTED_MODEL_TEST = Boolean( - SANDBOX_MODE && SANDBOX_MODE !== 'false', + SANDBOX_MODE && SANDBOX_MODE !== 'false' && SANDBOX_MODE !== '0', ); const describeMaybe = SKIP ? describe.skip : describe; const itPromptedModelMaybe = SKIP_PROMPTED_MODEL_TEST ? it.skip : it; diff --git a/scripts/lint.js b/scripts/lint.js index f26e9ec7111..f02b845f765 100644 --- a/scripts/lint.js +++ b/scripts/lint.js @@ -17,22 +17,6 @@ const YAMLLINT_VERSION = '1.35.1'; const TEMP_DIR = join(tmpdir(), 'qwen-code-linters'); -function archiveInstaller(url, archivePath, extractCommand) { - return ` - for attempt in 1 2 3 4 5; do - curl --fail --location --show-error --silent --retry 3 --retry-all-errors --retry-delay 2 --output "${archivePath}" "${url}" \\ - && tar -tf "${archivePath}" >/dev/null \\ - && break - rm -f "${archivePath}" - if [ "$attempt" -eq 5 ]; then - exit 1 - fi - sleep "$((attempt * 2))" - done - ${extractCommand} - `; -} - function getPlatformArch() { const platform = process.platform; const arch = process.arch; @@ -73,11 +57,11 @@ const platformArch = getPlatformArch(); const LINTERS = { actionlint: { check: 'command -v actionlint', - installer: archiveInstaller( - `https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_${platformArch.actionlint}.tar.gz`, - `${TEMP_DIR}/.actionlint.tgz`, - `mkdir -p "${TEMP_DIR}/actionlint" && tar -xzf "${TEMP_DIR}/.actionlint.tgz" -C "${TEMP_DIR}/actionlint"`, - ), + installer: ` + mkdir -p "${TEMP_DIR}/actionlint" + curl -sSLo "${TEMP_DIR}/.actionlint.tgz" "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_${platformArch.actionlint}.tar.gz" + tar -xzf "${TEMP_DIR}/.actionlint.tgz" -C "${TEMP_DIR}/actionlint" + `, run: ` actionlint \ -color \ @@ -89,11 +73,11 @@ const LINTERS = { }, shellcheck: { check: 'command -v shellcheck', - installer: archiveInstaller( - `https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/shellcheck-v${SHELLCHECK_VERSION}.${platformArch.shellcheck}.tar.xz`, - `${TEMP_DIR}/.shellcheck.txz`, - `mkdir -p "${TEMP_DIR}/shellcheck" && tar -xf "${TEMP_DIR}/.shellcheck.txz" -C "${TEMP_DIR}/shellcheck" --strip-components=1`, - ), + installer: ` + mkdir -p "${TEMP_DIR}/shellcheck" + curl -sSLo "${TEMP_DIR}/.shellcheck.txz" "https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/shellcheck-v${SHELLCHECK_VERSION}.${platformArch.shellcheck}.tar.xz" + tar -xf "${TEMP_DIR}/.shellcheck.txz" -C "${TEMP_DIR}/shellcheck" --strip-components=1 + `, run: ` git ls-files | grep -v '^integration-tests/terminal-bench/' | grep -E '^([^.]+|.*\\.(sh|zsh|bash))' | xargs file --mime-type \ | grep "text/x-shellscript" | awk '{ print substr($1, 1, length($1)-1) }' \ diff --git a/scripts/tests/chrome-extension-package.test.js b/scripts/tests/chrome-extension-package.test.js index dfc9f2350e9..b289078cb24 100644 --- a/scripts/tests/chrome-extension-package.test.js +++ b/scripts/tests/chrome-extension-package.test.js @@ -24,8 +24,5 @@ describe('chrome extension package scripts', () => { expect(packageJson.scripts.build).not.toMatch( /(?:^|\s&&\s)[A-Za-z_][A-Za-z0-9_]*=/, ); - expect(packageJson.scripts.build).not.toMatch( - /(?:^|\s&&\s)npm run clean(?:\s|$)/, - ); }); });