fix(ci): Decouple managed runtime distribution from Maven Central (#15782)

Co-authored-by: Qwen-Coder <[email protected]>
This commit is contained in:
jinyeandQwen-Coder authored and GitHub committed 2026-10-28 11:07:56 +00:00
1 parent 11ef1988d8
commit db88f2da5e
7 files changed
+144 -161

No files matched your search

+1 -63
View File
@@ -10,7 +10,7 @@ on:
type: 'string'
required: true
dry_run:
description: 'Build and verify without publishing to Central, GitHub, or OSS.'
description: 'Build and verify without publishing to GitHub or OSS.'
type: 'boolean'
required: true
default: true
@@ -151,7 +151,6 @@ jobs:
# Older release tags shipped the executable as the main artifact.
if [[ ! -f "${runtime_jar}" ]]; then runtime_jar="packages/managed-agent-server/target/qwen-managed-agent-server-${CLI_VERSION}.jar"; fi
unzip -p "${runtime_jar}" META-INF/MANIFEST.MF | tr -d '\r' | grep -Fx 'Main-Class: org.springframework.boot.loader.launch.JarLauncher'
echo "MANAGED_RUNTIME_JAR=${runtime_jar}" >> "${GITHUB_ENV}"
node .qwen/managed-release-tools/scripts/managed-runtime-release.js --jar "${runtime_jar}" --version "${CLI_VERSION}" --out-dir dist/managed-runtime
- name: 'Retain verified JAR and checksum'
@@ -206,21 +205,6 @@ jobs:
fi
gh api "repos/QwenLM/qwen-code/releases/tags/${RELEASE_TAG}" | jq --exit-status '.draft == false'
jar="qwen-managed-agent-server-${CLI_VERSION}.jar"
central_files=("packages/managed-agent-server/target/${jar}")
if [[ "${MANAGED_RUNTIME_JAR}" != "${central_files[0]}" ]]; then central_files+=("${MANAGED_RUNTIME_JAR}"); fi
present=0
missing=0
for file in "${central_files[@]}"; do
central="https://repo.maven.apache.org/maven2/com/alibaba/qwen-managed-agent-server/${CLI_VERSION}/$(basename "${file}")"
state=$(node .qwen/managed-release-tools/scripts/managed-runtime-release.js --check-url "${central}" --file "${file}") || exit 1
case "${state}" in present) present=$((present+1));; missing) missing=$((missing+1));; *) exit 1;; esac
done
if (( present > 0 && missing > 0 )); then
echo 'Partial Central publication; refusing an immutable redeploy.' >&2
exit 1
fi
if (( missing > 0 )); then state=missing; else state=present; fi
echo "central=${state}" >> "${GITHUB_OUTPUT}"
: > "${RUNNER_TEMP}/managed-github-missing"
: > "${RUNNER_TEMP}/managed-oss-missing"
for asset in "${jar}" "${jar}.sha256"; do
@@ -236,52 +220,6 @@ jobs:
fi
done
- name: 'Configure signed Central publication'
if: "${{ (github.event_name == 'release' || !inputs.dry_run) && steps.publication.outputs.central == 'missing' }}"
uses: 'actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654' # v5.2.0
with:
distribution: 'temurin'
java-version: '21'
server-id: 'central'
server-username: 'MAVEN_USERNAME'
server-password: 'MAVEN_PASSWORD'
gpg-private-key: '${{ secrets.MAVEN_GPG_PRIVATE_KEY }}'
gpg-passphrase: 'MAVEN_GPG_PASSPHRASE'
- name: 'Publish the signed server artifact to Central'
if: "${{ (github.event_name == 'release' || !inputs.dry_run) && steps.publication.outputs.central == 'missing' }}"
env:
MAVEN_USERNAME: '${{ secrets.CENTRAL_USERNAME }}'
MAVEN_PASSWORD: '${{ secrets.CENTRAL_PASSWORD }}'
MAVEN_GPG_PRIVATE_KEY: '${{ secrets.MAVEN_GPG_PRIVATE_KEY }}'
MAVEN_GPG_PASSPHRASE: '${{ secrets.MAVEN_GPG_PASSPHRASE }}'
run: |-
set -euo pipefail
for name in MAVEN_USERNAME MAVEN_PASSWORD MAVEN_GPG_PRIVATE_KEY MAVEN_GPG_PASSPHRASE; do
[[ -n "${!name}" ]] || { echo "Missing production-release secret: ${name}" >&2; exit 1; }
done
mvn --batch-mode --no-transfer-progress -f packages/managed-agent-server/pom.xml deploy -Pcentral-release -DskipTests -Dproject.build.outputTimestamp="${BUILD_TIMESTAMP}"
cmp "${MANAGED_RUNTIME_JAR}" "dist/managed-runtime/qwen-managed-agent-server-${CLI_VERSION}.jar"
- name: 'Verify Central availability before distribution'
if: "${{ github.event_name == 'release' || !inputs.dry_run }}"
run: |-
set -euo pipefail
jar="qwen-managed-agent-server-${CLI_VERSION}.jar"
central_files=("packages/managed-agent-server/target/${jar}")
if [[ "${MANAGED_RUNTIME_JAR}" != "${central_files[0]}" ]]; then central_files+=("${MANAGED_RUNTIME_JAR}"); fi
for attempt in {1..40}; do
missing=0
for file in "${central_files[@]}"; do
state=$(node .qwen/managed-release-tools/scripts/managed-runtime-release.js --check-url "https://repo.maven.apache.org/maven2/com/alibaba/qwen-managed-agent-server/${CLI_VERSION}/$(basename "${file}")" --file "${file}")
case "${state}" in present) ;; missing) missing=$((missing+1));; *) exit 1;; esac
done
if (( missing == 0 )); then exit 0; fi
sleep 30
done
echo 'The exact server JAR is not yet available on Central.' >&2
exit 1
- name: 'Attach only missing GitHub release assets'
if: "${{ github.event_name == 'release' || !inputs.dry_run }}"
env:
+22 -7
View File
@@ -223,12 +223,27 @@ service children are created.
A dedicated protected release workflow builds from an existing CLI release tag,
checks its package version, and verifies the Java SDK and server using Java 21.
The server JAR is published as the independent Maven coordinate
`com.alibaba:qwen-managed-agent-server:<cli-version>` using the SDK's signed
Central publication conventions, including sources and Javadoc. Auxiliary
operator executable JARs are not attached to that publication. The exact same
primary JAR and checksum are attached to the CLI GitHub Release and copied to
the existing versioned OSS prefix, outside the standalone archive allowlist.
After full verification, the executable server JAR and checksum are attached to the
CLI GitHub Release and copied to the existing versioned OSS prefix, outside the
standalone archive allowlist. The workflow selects the `-exec.jar` build output
when present and falls back to the main JAR for older tags; the public versioned
JAR name stays unchanged. The workflow verifies both public channels against
the staged bytes. Runtime distribution does not check for the server coordinate
on Maven Central, publish it there, or wait for that publication. Central
publishing authorization and artifact synchronization no longer gate delivery
to the channels the installer actually uses. It needs OSS credentials and GitHub
release access, without loading Central publishing credentials or a GPG private key.
Java compilation and normal Maven dependency resolution, the complete SDK/server
verification, test-fork isolation and the existing `central-release` build profile
remain unchanged. GPG signing is
skipped during verification; the profile does not publish anything before the
Maven deploy phase. Independent signed publication of
`com.alibaba:qwen-managed-agent-server:<cli-version>` remains available through
that Maven profile when needed for Maven consumers, including sources and
Javadoc and excluding auxiliary operator JARs. It is a separate release
operation, with no dependency edge in the runtime distribution workflow.
Reproducible timestamps and checksum comparisons prevent a retry from replacing
an existing version with different bytes. Dry runs build and verify without
publishing. Production credentials and publication remain protected by the
@@ -320,7 +335,7 @@ they do not describe the current main branch. The results above belong to the
pre-split trial. Current split-head verification and limitations are recorded
in the PR test report.
The first-use download path passed fourteen built-bundle scenarios with the real Java 21 server and Hosted Harness: initial installation, offline restart, OSS-to-GitHub fallback, interrupted-download retry, and refusal of invalid downloads or selected payloads. Transport was intercepted at the exact production URLs to serve the locally built JAR; public production assets have not been published or validated. The release profile builds the primary executable JAR, sources, and Javadoc locally; signed Central publication and protected GitHub/OSS upload require a production release run.
The first-use download path passed fourteen built-bundle scenarios with the real Java 21 server and Hosted Harness: initial installation, offline restart, OSS-to-GitHub fallback, interrupted-download retry, and refusal of invalid downloads or selected payloads. Transport was intercepted at the exact production URLs to serve the locally built JAR; public production assets have not been published or validated. The release profile builds the primary executable JAR, sources, and Javadoc locally. The runtime-distribution change still requires a protected GitHub/OSS release run and clean first-install acceptance; signed Central publication is independent.
## Open questions
@@ -174,11 +174,21 @@ runtime,后续调用可以重试。已有显式、相邻或已安装的 payloa
应用凭据、数据或服务子进程之前。
独立的受保护发行 workflow 从已存在的 CLI release tag 构建,检查 package 版本,
并使用 Java 21 验证 Java SDK 和服务。服务 JAR 沿用 SDK 的 Central 签名发布约定,
以独立 Maven 坐标 `com.alibaba:qwen-managed-agent-server:<cli-version>` 发布,
同时提供源码和 Javadoc。辅助运维可执行 JAR 不附加到这次发布。完全相同的主 JAR
和校验文件附加到 CLI GitHub Release,再复制到现有版本化 OSS 前缀,位于
standalone archive 允许列表之外。可重现时间戳和校验和比较防止重试时用不同字节
并使用 Java 21 验证 Java SDK 和服务。完整验证通过后,可执行服务 JAR 和校验文件
附加到 CLI GitHub Release,再复制到现有版本化 OSS 前缀,位于 standalone archive
允许列表之外。workflow 优先选择 `-exec.jar` 构建产物,对旧 tag 回退到主 JAR;
公开的版本化 JAR 名称保持不变。workflow 将两个公开渠道的制品与暂存字节核对。runtime 分发不探测
服务坐标在 Maven Central 上是否存在,不向 Central 发布或等待该发布完成。Central
发布授权与制品同步状态不再阻塞安装器实际使用的渠道交付。分发需要 OSS 凭据和
GitHub release 权限,无需加载 Central 发布凭据或 GPG 私钥。
Java 编译与正常 Maven 依赖解析、完整 SDK/server 验证、测试 fork 隔离和现有
`central-release` 构建 profile 保持不变。验证时跳过 GPG 签名;该 profile 在 Maven deploy 阶段之前不会
执行发布。需要服务于 Maven 使用者时,仍可通过该 profile 独立签名发布
`com.alibaba:qwen-managed-agent-server:<cli-version>`,包含源码和 Javadoc,
排除辅助运维 JAR。该操作与 runtime 分发独立,分发 workflow 不依赖它。
可重现时间戳和校验和比较防止重试时用不同字节
替换已有版本。dry-run 只构建和验证,不发布。生产凭据与发布操作继续受现有
`production-release` environment 保护。
@@ -246,7 +256,7 @@ CLI 的生命周期验收。十五项选择检查通过,包括 JAVA_HOME/PATH
本地验收分别记录,不代表当前 main 分支状态。上述结果属于拆分前试验;当前
拆分 head 的验证和限制记录在 PR 测试报告中。
首次下载路径使用真实 Java 21 服务与 Hosted Harness,通过十四项 built-bundle 场景:首次安装、离线重启、OSS 转 GitHub 回退、中断下载后重试,以及无效下载和选中 payload 拒绝。测试在精确生产 URL 处拦截 transport,提供本地构建的 JAR;公开生产产物尚未发布或验证。发布 profile 已在本地构建主可执行 JAR、源码与 Javadoc;Central 签名发布及受保护的 GitHub/OSS 上传仍需执行生产发行。
首次下载路径使用真实 Java 21 服务与 Hosted Harness,通过十四项 built-bundle 场景:首次安装、离线重启、OSS 转 GitHub 回退、中断下载后重试,以及无效下载和选中 payload 拒绝。测试在精确生产 URL 处拦截 transport,提供本地构建的 JAR;公开生产产物尚未发布或验证。发布 profile 已在本地构建主可执行 JAR、源码与 Javadoc。runtime 分发改动仍需受保护的 GitHub/OSS 生产发行及空缓存首装验收;Central 签名发布独立进行。
## 未决问题
+15 -6
View File
@@ -239,16 +239,25 @@ CLI release tag, using the SDK and server from that exact source revision. A
manual run must be dispatched from `main` and defaults to a dry run. It verifies
the CLI and Java module, builds the `central-release` profile, and retains the
JAR and checksum as workflow artifacts without changing public repositories.
The workflow selects the `-exec.jar` build output when present and falls back to
the main JAR for older tags; the public versioned JAR name stays unchanged.
After verifying the dry run, release operators can run the same tag with
`dry_run=false` through the protected `production-release` environment. The
workflow sets the server artifact version to the CLI version, publishes
`com.alibaba:qwen-managed-agent-server:<cli-version>` to Maven Central with
sources, Javadoc and signatures, and distributes the same primary JAR plus its
`.sha256` through that GitHub Release and the existing versioned OSS mirror.
Auxiliary operator JARs are not attached to the Central publication. Existing
assets are compared byte-for-byte and never replaced with different content.
Published CLI release events also run this workflow automatically.
the verified executable JAR plus its `.sha256` through that GitHub Release and the
existing versioned OSS mirror, and checks both public download channels against
the staged bytes. Existing assets are compared byte-for-byte and never replaced
with different content. Published CLI release events also run this workflow
automatically.
Runtime distribution requires the existing OSS upload credentials and GitHub
release access. It does not need Central publishing credentials or a GPG private
key, and does not check, publish or wait for the server coordinate on Central.
Maven still builds and fully verifies the SDK and server, including normal
dependency resolution. The existing `central-release` profile can
be used separately for signed Maven publication when required; that operation
does not gate runtime delivery.
The CLI downloads from OSS/GitHub, not Maven Central. It does not resolve the
SDK as a separate user dependency: the executable server JAR includes its Java
+11 -5
View File
@@ -201,15 +201,21 @@ Qwen home 下的版本化安装。存在相邻 payload 时,它优先于 Qwen h
`Release Managed server runtime` workflow 从已公开的 CLI release tag 构建,使用
相同源码修订的 SDK 和服务。手动运行必须从 `main` 发起,默认是 dry-run。它验证
CLI 和 Java 模块,构建 `central-release` profile,并将 JAR 与校验文件保留为
workflow artifact,不修改公开仓库。
workflow artifact,不修改公开仓库。workflow 优先选择 `-exec.jar` 构建产物,
对旧 tag 回退到主 JAR;公开的版本化 JAR 名称保持不变。
验证 dry-run 后,发行运维可通过受保护的 `production-release` environment,用
相同 tag 和 `dry_run=false` 运行。workflow 将服务制品版本设为 CLI 版本,向
Maven Central 发布 `com.alibaba:qwen-managed-agent-server:<cli-version>` 及源码、
Javadoc 和签名,再将相同主 JAR 及其 `.sha256` 分发到该 GitHub Release 和现有的
版本化 OSS 镜像。辅助运维 JAR 不附加到 Central 发布。已有产物逐字节比较,绝不
相同 tag 和 `dry_run=false` 运行。workflow 将服务制品版本设为 CLI 版本,
将验证后的可执行 JAR 及其 `.sha256` 分发到该 GitHub Release 和现有的版本化 OSS
镜像,再将两个公开下载渠道的制品与暂存字节核对。已有产物逐字节比较,绝不
用不同内容替换。公开的 CLI release 事件也会自动运行该 workflow。
runtime 分发需要现有 OSS 上传凭据和 GitHub release 权限,无需 Central 发布凭据或
GPG 私钥,不探测服务坐标在 Central 上是否存在,不向 Central 发布或等待其完成。
Maven 仍负责构建和完整验证 SDK 与服务,正常依赖解析保持不变。
需要签名发布 Maven 制品时,可独立使用现有 `central-release`
profile;该操作不阻塞 runtime 交付。
CLI 从 OSS/GitHub 下载,不从 Maven Central 下载,也不要求用户单独解析 SDK:
可执行服务 JAR 包含其 Java 依赖。精确匹配 CLI release 的 runtime 产物必须已存在,
首次下载才能成功。本 PR 的本地验证不会发布这些产物,也不会绕过生产发行环境。
+7 -4
View File
@@ -28,12 +28,15 @@ existing MySQL defaults. Design:
[简体中文](../../docs/design/2026-10-20-managed-local-serve.zh-CN.md).
The `central-release` Maven profile attaches sources, Javadoc and GPG signatures
for the main executable server JAR, without attaching the auxiliary operator
JARs. Ordinary builds keep the existing attachments and do not activate Central
for server publication, without attaching the auxiliary operator JARs. The
executable server JAR uses the `exec` classifier. Ordinary builds keep the existing attachments and do not activate Central
publishing. The protected `Release Managed server runtime` workflow sets the
artifact version from an existing CLI release tag and publishes identical JAR
bytes to Central, GitHub Release assets and the official OSS mirror. The CLI's
first-use download uses OSS/GitHub and requires no Maven installation. See the
bytes and checksums to GitHub Release assets and the official OSS mirror after
full SDK/server verification. Distribution does not require Central credentials,
GPG signing or a successful Central publication. Signed Maven publication remains
an independent operation using the existing profile. The CLI's first-use download
uses OSS/GitHub and requires no Maven installation. See the
[release procedure](../../docs/users/managed-local-serve.md#publish-the-first-use-jar)
and its [Chinese version](../../docs/users/managed-local-serve.zh-CN.md#发布首次使用的-jar).
+72 -70
View File
@@ -364,7 +364,7 @@ setInterval(() => {}, 1000);
describe('Managed runtime publication gates', () => {
const steps = workflow.jobs.release.steps;
const step = (name) => steps.find((item) => item.name === name);
it('separates the Central dependency and executable bytes while retaining runtime download names', () => {
it('stages the executable classifier while retaining legacy fallback and runtime download names', () => {
const verification = step(
'Verify the executable JAR and publication profile',
).run;
@@ -374,23 +374,12 @@ describe('Managed runtime publication gates', () => {
expect(verification).toContain(
'Main-Class: org.springframework.boot.loader.launch.JarLauncher',
);
expect(verification).toContain('MANAGED_RUNTIME_JAR=${runtime_jar}');
const preflight = step(
'Recheck the immutable tag and existing publication bytes',
).run;
expect(preflight).toContain(
'central_files=("packages/managed-agent-server/target/${jar}")',
expect(verification).toContain(
'if [[ ! -f "${runtime_jar}" ]]; then runtime_jar="packages/managed-agent-server/target/qwen-managed-agent-server-${CLI_VERSION}.jar"; fi',
);
expect(preflight).toContain('central_files+=("${MANAGED_RUNTIME_JAR}")');
expect(preflight).toContain(
'Partial Central publication; refusing an immutable redeploy.',
expect(verification).toContain(
'--jar "${runtime_jar}" --version "${CLI_VERSION}" --out-dir dist/managed-runtime',
);
expect(step('Publish the signed server artifact to Central').run).toContain(
'cmp "${MANAGED_RUNTIME_JAR}"',
);
expect(
step('Verify Central availability before distribution').run,
).toContain('missing == 0');
expect(step('Verify both public download channels').run).toContain(
'jar="qwen-managed-agent-server-${CLI_VERSION}.jar"',
);
@@ -409,8 +398,6 @@ describe('Managed runtime publication gates', () => {
);
for (const name of [
'Recheck the immutable tag and existing publication bytes',
'Configure signed Central publication',
'Publish the signed server artifact to Central',
'Attach only missing GitHub release assets',
'Install the verified OSS uploader',
'Configure authenticated OSS access',
@@ -427,10 +414,31 @@ describe('Managed runtime publication gates', () => {
(item) =>
item.name === 'Verify the executable JAR and publication profile',
);
expect(build).toBeLessThan(
steps.findIndex(
(item) => item.name === 'Publish the signed server artifact to Central',
),
for (const name of [
'Attach only missing GitHub release assets',
'Upload only missing OSS release assets',
]) {
expect(build).toBeLessThan(steps.indexOf(step(name)));
}
});
it('distributes the verified runtime without Central publication access or signing credentials', () => {
const definition = JSON.stringify(workflow);
for (const dependency of [
'CENTRAL_USERNAME',
'CENTRAL_PASSWORD',
'MAVEN_GPG_PRIVATE_KEY',
'MAVEN_GPG_PASSPHRASE',
'repo.maven.apache.org',
'gpg-private-key',
]) {
expect(definition).not.toContain(dependency);
}
expect(steps.some((item) => /\bmvn\b.*\bdeploy\b/u.test(item.run))).toBe(
false,
);
expect(workflow.on.workflow_dispatch.inputs.dry_run.description).toBe(
'Build and verify without publishing to GitHub or OSS.',
);
});
@@ -444,8 +452,9 @@ describe('Managed runtime publication gates', () => {
expect(steps.indexOf(install)).toBeLessThan(steps.indexOf(configure));
expect(steps.indexOf(configure)).toBeLessThan(steps.indexOf(preflight));
expect(steps.indexOf(preflight)).toBeLessThan(
steps.indexOf(step('Publish the signed server artifact to Central')),
steps.indexOf(step('Attach only missing GitHub release assets')),
);
expect(steps.indexOf(preflight)).toBeLessThan(steps.indexOf(upload));
expect(install.env.OSSUTIL_URL).toContain('/1.7.19/');
expect(install.env.OSSUTIL_SHA256).toBe(
'dcc512e4a893e16bbee63bc769339d8e56b21744fd83c8212a9d8baf28767343',
@@ -513,9 +522,6 @@ describe('Managed runtime publication gates', () => {
expect(
step('Verify the executable JAR and publication profile').run,
).toContain('clean verify');
expect(step('Publish the signed server artifact to Central').run).toContain(
'cmp ',
);
});
it('isolates server test classes without filtering or bypassing release verification', () => {
@@ -580,8 +586,6 @@ describe.skipIf(process.platform === 'win32')('release gate execution', () => {
RELEASE_TAG: 'v0.25.4',
EVENT_NAME: 'workflow_dispatch',
WORKFLOW_REF: 'refs/heads/main',
MANAGED_RUNTIME_JAR:
'packages/managed-agent-server/target/qwen-managed-agent-server-0.25.4-exec.jar',
...env,
},
encoding: 'utf8',
@@ -648,58 +652,56 @@ describe.skipIf(process.platform === 'win32')('release gate execution', () => {
expect(fs.existsSync(path.join(temporary, 'gh-called'))).toBe(false);
});
it('refuses partial Central publication before attempting runtime channel writes', () => {
it('prepares both distribution channels when Central publication is unavailable and its credentials are absent', () => {
executable('git', "printf '%s\\tref\\n' original-sha");
executable('gh', "printf '%s\\n' '{\"draft\":false}'");
executable(
'node',
'case "$*" in *repo.maven.apache.org*exec.jar*) echo missing ;; *repo.maven.apache.org*) echo present ;; *) touch unexpected-channel; echo missing ;; esac',
);
const result = run(preflight, {
SOURCE_SHA: 'original-sha',
CLI_VERSION: '0.25.4',
});
expect(result.status).not.toBe(0);
expect(result.stderr).toContain('Partial Central publication');
expect(fs.existsSync(path.join(temporary, 'unexpected-channel'))).toBe(
false,
);
});
it('retains the single Central artifact check for legacy executable release tags', () => {
executable('git', "printf '%s\\tref\\n' original-sha");
executable('gh', "printf '%s\\n' '{\"draft\":false}'");
executable(
'node',
'case "$*" in *exec.jar*) echo unexpected-exec >&2; exit 1 ;; *) echo missing ;; esac',
'case "$*" in *repo.maven.apache.org*) echo unexpected-central >&2; exit 7 ;; *) echo missing ;; esac',
);
const result = run(preflight, {
SOURCE_SHA: 'original-sha',
CLI_VERSION: '0.25.4',
ALIYUN_OSS_BUCKET: 'release-bucket',
MANAGED_RUNTIME_JAR:
'packages/managed-agent-server/target/qwen-managed-agent-server-0.25.4.jar',
CENTRAL_USERNAME: '',
CENTRAL_PASSWORD: '',
MAVEN_USERNAME: '',
MAVEN_PASSWORD: '',
MAVEN_GPG_PRIVATE_KEY: '',
MAVEN_GPG_PASSPHRASE: '',
});
expect(result.status, result.stderr).toBe(0);
expect(fs.readFileSync(path.join(temporary, 'output'), 'utf8')).toContain(
'central=missing',
);
expect(result.status).toBe(0);
for (const channel of ['github', 'oss']) {
expect(
fs.readFileSync(
path.join(temporary, `managed-${channel}-missing`),
'utf8',
),
).toBe(
'dist/managed-runtime/qwen-managed-agent-server-0.25.4.jar\n' +
'dist/managed-runtime/qwen-managed-agent-server-0.25.4.jar.sha256\n',
);
}
});
it('aborts preflight on an artifact-verification error instead of treating it as present', () => {
executable('git', "printf '%s\\tref\\n' original-sha");
executable('gh', "printf '%s\\n' '{\"draft\":false}'");
executable(
'node',
'case "$*" in *github.com*".jar --file "*) echo mismatch >&2; exit 7 ;; *repo.maven.apache.org*) echo present ;; *) echo missing ;; esac',
);
const result = run(preflight, {
SOURCE_SHA: 'original-sha',
CLI_VERSION: '0.25.4',
OSS_PUBLIC_BASE_URL: 'https://example.com',
ALIYUN_OSS_BUCKET: 'release-bucket',
});
expect(result.status).not.toBe(0);
expect(result.stderr).toContain('mismatch');
});
it.each(['github.com', 'oss://'])(
'aborts preflight on a %s artifact-verification error instead of treating it as present',
(channel) => {
executable('git', "printf '%s\\tref\\n' original-sha");
executable('gh', "printf '%s\\n' '{\"draft\":false}'");
executable(
'node',
'case "$*" in *"$MISMATCH_HOST"*) echo mismatch >&2; exit 7 ;; *) echo missing ;; esac',
);
const result = run(preflight, {
SOURCE_SHA: 'original-sha',
CLI_VERSION: '0.25.4',
OSS_PUBLIC_BASE_URL: 'https://example.com',
ALIYUN_OSS_BUCKET: 'release-bucket',
MISMATCH_HOST: channel,
});
expect(result.status).not.toBe(0);
expect(result.stderr).toContain('mismatch');
},
);
});