mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-09-11 03:08:00 +00:00
* fix(ci): route release pipeline Linux jobs to the ECS runner pool
* fix(ci): restore ECS workspace before release checkout
Co-authored-by: Qwen Code <qwen-code@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): record release workflow growth
The ECS routing and workspace ownership guards intentionally grow release.yml beyond the workflow-size ratchet allowance.
Co-authored-by: Qwen Code <qwen-code@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): isolate release jobs on shared runners
Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): bound shared release runner work
Disable redundant setup-node cache transfers on the persistent ECS pool and add explicit timeouts for the remaining shared-pool jobs. Pin both contracts in the release workflow test.\n\nCo-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): harden the release wipe and pin the docker preflight fail-closed
Port the sibling wipe guards (realpath canonicalization, symlink heal,
root denylist, runner-workspace containment) onto all five release wipe
copies, remove planted user-level state the workspace wipe cannot see
(~/.npmrc script-shell, global git exec keys) with the qwen-autofix
pre-checkout denylist, and make the docker preflight print docker's own
error output before failing closed.
Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
* test(ci): pin the canonical release wipe byte-identical at index 0
Replace substring tripwires with full-string equality against the shared
canonical wipe constant and require the step to stay first, so a
commented-out find, an inserted early exit, or a uniformly dropped
ownership ladder fails review instead of shipping green. Pin the docker
preflight's full fail-closed form (captured daemon output printed,
exit 1) so deleting the exit or inverting the guard fails the suite.
Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
* fix(ci): deduplicate release runner cleanup
* fix(ci): keep the release failure notifier on hosted runners
notify_failure exists to report failures of the ECS pool, so routing it
onto that same pool wedges the alert chain in exactly the post-claim
failure modes it fires for (runner crash, host loss, pool-wide outage).
The job is gh/jq-only (both preinstalled on hosted images); pin it to an
ephemeral ubuntu-latest runner like the sibling failure notifiers
(release-sdk.yml, release-sdk-python.yml, release-vscode-companion.yml,
and qwen-code-pr-review.yml's fallback comment).
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): pin the release lanes' conditional ECS runs-on
No test pinned the migration itself: reverting any routed release job to
a hosted runner, or dropping the MAINTAINER_ECS_RUNNER_DISABLED clause,
shipped green. Pin each routed job's exact runs-on expression
(repository guard + kill switch + both branches) and the notifier's
hosted pin, matching qwen-autofix-workflow.test.js's tripwire style.
Verified by mutation: reverting prepare's runs-on to ubuntu-latest or
dropping the kill-switch clause now fails release-workflow.test.js.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): keep release publishing on hosted runners
* fix(ci): fail closed on release git scrub
* fix(ci): scrub ambient GIT_CONFIG_GLOBAL from the release wipe test
Mirror the sibling scrub harness in qwen-autofix-workflow.test.js: delete GIT_CONFIG_GLOBAL from the child env before spawning git, so setHook() and the post-scrub 'git config --global' assertion operate on the synthetic HOME instead of leaking through an ambient global config file.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(ci): add behavioral tests for workspace wipe guard branches
Drive the wipe half of canonicalWipe against fixtures so its guard
branches (symlink heal, realpath canonicalization, runner-workspace
allowlist) are executed, not just pinned by string equality:
- Happy path: leftover files are removed
- Symlink heal: symlinked workspace is removed and recreated
- Outside runner workspace: refused with error
- Path with '..': realpath resolves inside runner workspace, wipe proceeds
* fix(test): gate wipe behavioral test on GNU realpath, fix '..' path and heal assertions
- Add hasGnuRealpath capability probe and skip the behavioral wipe test
on non-GNU hosts (macOS BSD realpath lacks -m) and under root (uid 0
bypasses permission bits), matching the pattern used by every sibling
suite that executes this script lineage.
- Use string concatenation instead of path.join for the '..' sub-case so
the literal '..' segment reaches the script (path.join normalizes it
away, making the test equivalent to the happy path).
- Create a real decoy target file for the heal sub-case and assert it
survives the wipe, verifying that rm -f removes only the symlink
itself and does not follow/delete the target.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): guard chmod -R against symlinked workspace before validation
The recursive chmod ran on the raw $GITHUB_WORKSPACE before the
heal/canonicalize/guard block. GNU chmod -R follows a symlink given
as its starting operand, so a previous pool job that replaced the
workspace with a symlink would cause the chmod to descend into the
link target. Add a [-L] guard that skips chmod when the workspace
is not a real directory; the heal block below restores it before
the wipe runs.
Also plant a subdirectory in the behavioral wipe test's happy-path
fixture so recursive directory removal — the wipe's core property —
is actually exercised (R5-9).
Fixes R5-2, R5-9.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): bump release.yml size baseline to accommodate ECS runner changes
The check-workflow-size gate was 158 bytes short — release.yml at 52271
exceeded the old baseline (48017) + allowance (4096) = 52113 ceiling.
Raise the baseline to 48500 so the gate passes with headroom.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(test): create the sub directory in the wipe '..' sub-case setup
The '..' wipe sub-case drives the script at ${base}/sub/../workspace
without creating sub, so the heal block's mkdir fails ENOENT and the
payload exits 1 (fail-closed) while the test asserts 0. Create sub in
the setup so the path resolves and the suite goes green.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): skip ECS cleanup on hosted release jobs
* fix(ci): isolate persistent release runner state
* fix(ci): accept the pool's sibling tool cache in the release wipe
The wipe's tool-cache containment required RUNNER_TOOL_CACHE inside
RUNNER_WORKSPACE, but the standard self-hosted geometry the ecs-qwen
pool uses is a sibling (<root>/_work/_tool vs <root>/_work/qwen-code),
so every routed release job died at step 1. Anchor the containment to
the runner work root (allow the sibling _tool, keep refusing every
other outside path), and split Node setup the way ci.yml does: pool
runs reuse the machine's Node because the purge above empties the tool
cache and nodejs.org may be unreachable through the ECS egress proxy;
the hosted fallback keeps actions/setup-node.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): refuse a symlinked runner workspace before the release wipe
The wipe canonicalized RUNNER_WORKSPACE THROUGH symlinks, so a prior
pool job could plant _work/qwen-code -> /target and redirect the heal,
the containment allowlist, and the recursive rm to an attacker-chosen
location; the chown/chmod ladder additionally ran on the raw,
unvalidated path before any check. Refuse a symlinked runner workspace
outright and move the ownership/permission ladder after the geometry
validation so refused paths get no changes at all.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): isolate gh config from the persistent pool HOME
The isolation block redirected Git, npm, and Docker config away from
the attacker-writable persistent $HOME but not gh's, and prepare runs
gh with the job token on the pool. A prior pool job can plant
~/.config/gh with http_unix_socket and capture the token, the way
qwen-autofix.yml already defends against with a fresh GH_CONFIG_DIR.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): digest-pin the sandbox base image
Routing integration_docker onto the shared pool exposes the build to
the pool's persistent docker daemon store: a co-resident job can
pre-tag a poisoned image as node:22-slim, and an unpinned FROM
resolves against the local store with zero registry contact. Pin both
stages to the node:22-slim index digest (verified against two
independent registry mirrors) and pin the requirement in
release-workflow.test.js.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): ratchet the release.yml size baseline for the pool hardening
The pool security hardening (sibling tool-cache containment, symlinked-workspace refusal, GH_CONFIG_DIR isolation, and the hosted/self-hosted Node split across the four routed jobs) grew release.yml from 48500 to 54000 bytes — 5500 over the recorded baseline, past the 4096 allowance. The growth is real and deliberate: the attacker-model comments must stay next to the wipe steps they document, and the Node split repeats a small step block per job. Bump the ratchet per check-workflow-size.sh's own contract.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): reap cross-job processes and keep the pool tool cache in the release wipe
Round 9 R1-1: the pre-checkout isolation was a one-shot file-state sweep;
a detached postinstall child of a previous pool job (self-hosted runners do
not reap job processes) waits it out and tampers with the fresh tree while
secret-bearing steps run. Extend the restore step with qwen-triage.yml's
process-reap doctrine: kill every live process of the runner user outside
the runner agent's own tree (PPID-chain exclusion, zombies ignored), retry,
and fail closed naming any survivor.
Round 9 R7-4 (fix-induced): the round-8 geometry fix armed the
${RUNNER_TOOL_CACHE}/node purge on the pool, stripping the pool-wide Node
that five lanes in qwen-autofix.yml, serve-ab.yml and repo-hygiene.yml
resolve through un-gated setup-node, while the pool-routed release jobs
never read the tool cache. Drop the purge; document the pool-wide cache as
deliberately untouched.
Tests: canonicalWipe mirror regenerated byte-for-byte; the pool-geometry
sub-case now asserts _tool/node survives; new behavioral witnesses reap an
orphaned process and fail closed on an unkillable survivor (mutation-verified:
reap-condition inversion and purge re-addition both redden the suite; reap
leg exercised as a non-root user with orphan reap and stubbed unkillable
survivor probes).
Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
* fix(ci): keep the release wipe suite green and kill-free on the pool
The round-4 behavioral suite goes red in non-root CI for three reasons:
1. The happy-path sub-case still asserted the tool-cache purge the
round-4 commit deliberately removed (the pool geometry sub-case was
flipped to expect _tool/node survival, the happy path was not).
Flip it to expect survival: the sweep is scoped to the workspace.
2. Every runWipe guard-branch sub-case (happy path, symlink heal, pool
geometry, '..' canonicalization) executed the genuine reap, which
kills every live process of the runner user outside the step's
ancestor tree — on the shared ECS pool that reaches into whatever
job is co-resident on the same member. Give those cases a ps stub
that lists nothing so the reap branch executes end to end yet kills
nothing; R1-1 keeps the one genuine reap probe, clearly marked.
3. The R1-1 orphan spawn inherited the spawnSync stdio pipes, so the
spawn blocked until the 30s sleeper exited and the test timed out
before the wipe even ran. Redirect the sleeper's stdio to /dev/null.
Verified: root run 23 passed / 1 skipped (behavioral suite skips by
design under root); a dedicated uid-1002 run passes all 24 tests with
the behavioral suite executing in ~6s.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): keep concurrent registrations' jobs out of the release reap
The round-9 reap roots its keep set at this shell's ancestor chain
alone. One pool member hosts every registration under this same uid
(qwen-autofix.md af-014 documents ~27 registrations on one HOME), so a
concurrent job dispatched through another registration never reaches
that chain: the reap SIGKILLs its worker and processes mid-flight, and
a process (re)appearing during the retry window then fails the release
itself through the fail-closed survivor check.
Widen the keep set to every runner-agent tree of the user (runsvc.sh /
RunnerService / Runner.Listener / Runner.Worker roots). A process is
still killed when it is detached from ALL agent trees, which is the
documented shape of a cross-job leftover; concurrent jobs of other
registrations are spared. The canonicalWipe mirror carries the same
change byte-for-byte, the suite gains a disjoint-tree witness (a real
bystander parented under a second registration's tree must survive the
wipe while a genuine detached orphan dies), and the size ratchet moves
54000 -> 58255 per check-workflow-size.sh's same-PR contract.
Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
* fix(ci): stop the R1-1 reap probe from enumerating the pool host
The R1-1 case was the suite's one deliberate real-reap probe: it ran
the wipe against the real ps, so every pool run of the suite (the
quality job this PR routes to the ECS pool runs test:scripts) listed
every live process of the runner user and SIGKILLed whatever sat
outside the test's ancestor tree — co-resident jobs' processes under
the shared pool uid included. It was also flaky-red by measurement: a
transient same-uid process during the reap window failed the
fail-closed leg on a machine with no co-resident jobs at all.
Stub ps the way the sibling cases do, but keep the probe genuine: the
live-listing arm prints only the test's own orphan (tracking reality
via kill -0, the way the real ps does once the orphan dies), so the
real SIGKILL path still runs end to end on a real detached sleeper
while the host is never enumerated. Mutating the orphan line away
makes the kill assertion go red.
Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
* fix(ci): keep the release wipe suite off the hosted Windows lane
The behavioral wipe test gates on hasGnuRealpath and a non-root uid,
but that gate opens on the hosted Windows lane: Git for Windows ships
usr/bin/realpath.exe (GNU coreutils, -m works) and the installer adds
usr/bin to PATH (PathOption=CmdTools), so hasGnuRealpath is true on
windows-2022 — exactly the fallback lane ci.yml's test_windows runs
whenever MAINTAINER_ECS_RUNNER_DISABLED=true, a mode this PR
documents and pins. PortableGit ships no setsid, though, so the reap
fixture never writes its pid file there and readFileSync(pidFile)
throws ENOENT, turning the merge_group-gated lane red.
Add process.platform === 'win32' to the gate, the pattern this file
already uses for its other bash-driven case; the suite's YAML-parse
coverage keeps running on Windows.
Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
* fix(ci): remove unsafe shared-runner process reaping
Keep release validation on the conditional ECS lane while limiting workflow cleanup to job-owned filesystem state.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* chore(ci): drop prettier reflow noise from teamHelpers.test.ts
Restore the type-annotation formatting from origin/main so this PR
contains zero non-CI hunks. CI's prettier step is write-only
(prettier --write ., no drift check), same as main carries today,
so this revert is CI-neutral.
Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
* fix(ci): re-arm the release.yml size ratchet at its true floor (#10036)
The reap removal (3c0ca80501) shrank release.yml from 58255 to
53022 bytes after the baseline entry was written, banking 5233
bytes of unreviewed ratchet headroom inside the gate's 20000-byte
slack band. Lower the entry to the true floor so future growth
past the allowance is reviewed again. Gate and ratchet mirror
tests stay green; the probe value actual-4097 reddens the mirror.
Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
* fix(ci): strip RUNNER_WORKSPACE trailing slashes before the symlink refusal (#10036)
* fix(ci): refuse symlinked components anywhere in the wipe path (#10036)
---------
Co-authored-by: yiliang114 <yiliang114@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: root <root@vscode-sqlx011163220057.na131>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
82 lines
2.3 KiB
Docker
82 lines
2.3 KiB
Docker
# Build stage
|
|
# Digest-pinned: integration_docker builds on the shared ECS pool, whose
|
|
# docker daemon image store persists across jobs — a co-resident job can
|
|
# retag a mutable base tag with a poisoned image, but a digest cannot be
|
|
# moved by `docker tag`. Bump the digest together with the tag.
|
|
# ratchet:docker.io/library/node:22-slim
|
|
FROM docker.io/library/node:22-slim@sha256:83f487e0a63425e5b4d146fb5e5be574bcbe1b7b843d3ebafdd95eaf7767a7e5 AS builder
|
|
|
|
# Install build dependencies
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
python3 \
|
|
make \
|
|
g++ \
|
|
git \
|
|
&& apt-get clean \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Set up npm global package folder
|
|
RUN mkdir -p /usr/local/share/npm-global
|
|
ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
|
|
ENV PATH=$PATH:/usr/local/share/npm-global/bin
|
|
|
|
# Copy source code
|
|
COPY . /home/node/app
|
|
WORKDIR /home/node/app
|
|
|
|
# Install dependencies, build workspaces, bundle into a single distributable, and pack.
|
|
# QWEN_SKIP_PREPARE=1 stops npm ci's prepare script from building and bundling —
|
|
# the explicit build and bundle steps below already do that.
|
|
RUN QWEN_SKIP_PREPARE=1 npm ci \
|
|
&& npm run build \
|
|
&& npm run bundle \
|
|
&& npm run prepare:package \
|
|
&& cd dist && npm pack
|
|
|
|
# Runtime stage
|
|
# Digest-pinned for the same reason as the builder stage above.
|
|
# ratchet:docker.io/library/node:22-slim
|
|
FROM docker.io/library/node:22-slim@sha256:83f487e0a63425e5b4d146fb5e5be574bcbe1b7b843d3ebafdd95eaf7767a7e5
|
|
|
|
ARG SANDBOX_NAME="qwen-code-sandbox"
|
|
ARG CLI_VERSION_ARG
|
|
ENV SANDBOX="$SANDBOX_NAME"
|
|
ENV CLI_VERSION=$CLI_VERSION_ARG
|
|
|
|
# Install runtime dependencies
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
python3 \
|
|
man-db \
|
|
curl \
|
|
dnsutils \
|
|
less \
|
|
jq \
|
|
bc \
|
|
gh \
|
|
git \
|
|
unzip \
|
|
rsync \
|
|
ripgrep \
|
|
procps \
|
|
psmisc \
|
|
lsof \
|
|
socat \
|
|
ca-certificates \
|
|
&& apt-get clean \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Set up npm global package folder
|
|
RUN mkdir -p /usr/local/share/npm-global
|
|
ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
|
|
ENV PATH=$PATH:/usr/local/share/npm-global/bin
|
|
|
|
# Copy bundled package from builder stage
|
|
COPY --from=builder /home/node/app/dist/*.tgz /tmp/
|
|
|
|
# Install built packages globally
|
|
RUN npm install -g /tmp/*.tgz \
|
|
&& npm cache clean --force \
|
|
&& rm -rf /tmp/*.tgz
|
|
|
|
# Default entrypoint when none specified
|
|
CMD ["qwen"]
|