Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
79433e5
fix(ci): route release pipeline Linux jobs to the ECS runner pool
yiliang114 Aug 25, 2026
225fbf5
fix(ci): restore ECS workspace before release checkout
yiliang114 Aug 25, 2026
5c80aad
fix(ci): record release workflow growth
yiliang114 Aug 25, 2026
b276384
fix(ci): isolate release jobs on shared runners
yiliang114 Aug 26, 2026
324809e
fix(ci): bound shared release runner work
yiliang114 Aug 26, 2026
7ce76af
Merge branch 'main' into fix/release-ecs-runners
wenshao Aug 26, 2026
c823f67
Merge commit '7ce76afe6124ad5803e94c954bae38c87c0b079c' into HEAD
yiliang114 Aug 26, 2026
e29c23e
fix(ci): harden the release wipe and pin the docker preflight fail-cl…
yiliang114 Aug 26, 2026
2775857
test(ci): pin the canonical release wipe byte-identical at index 0
yiliang114 Aug 26, 2026
d770f22
Merge branch 'main' into fix/release-ecs-runners
yiliang114 Aug 26, 2026
ecbc499
fix(ci): deduplicate release runner cleanup
yiliang114 Aug 26, 2026
680e794
fix(ci): keep the release failure notifier on hosted runners
yiliang114 Aug 26, 2026
860f4c8
fix(ci): pin the release lanes' conditional ECS runs-on
yiliang114 Aug 26, 2026
d14fb9f
fix(ci): keep release publishing on hosted runners
yiliang114 Aug 26, 2026
132b7cb
fix(ci): fail closed on release git scrub
yiliang114 Aug 26, 2026
dcbaa9a
fix(ci): scrub ambient GIT_CONFIG_GLOBAL from the release wipe test
yiliang114 Aug 26, 2026
cf9c27d
test(ci): add behavioral tests for workspace wipe guard branches
yiliang114 Aug 26, 2026
d22c1a5
Merge branch 'main' into fix/release-ecs-runners
qwen-code-dev-bot Aug 26, 2026
c647666
fix(test): gate wipe behavioral test on GNU realpath, fix '..' path a…
yiliang114 Aug 26, 2026
84559ee
fix(ci): guard chmod -R against symlinked workspace before validation
yiliang114 Aug 26, 2026
909688a
fix(ci): bump release.yml size baseline to accommodate ECS runner cha…
yiliang114 Aug 26, 2026
c8459ae
Merge branch 'main' into fix/release-ecs-runners
qwen-code-dev-bot Aug 27, 2026
30cdb66
fix(test): create the sub directory in the wipe '..' sub-case setup
Aug 27, 2026
3284ce4
Merge remote-tracking branch 'origin/main' into fix/release-ecs-runners
yiliang114 Aug 27, 2026
5c5071e
fix(ci): skip ECS cleanup on hosted release jobs
yiliang114 Aug 27, 2026
da5b977
fix(ci): isolate persistent release runner state
yiliang114 Aug 27, 2026
d03d94f
fix(ci): accept the pool's sibling tool cache in the release wipe
yiliang114 Aug 27, 2026
3f9da8d
fix(ci): refuse a symlinked runner workspace before the release wipe
yiliang114 Aug 27, 2026
ec694c0
fix(ci): isolate gh config from the persistent pool HOME
yiliang114 Aug 27, 2026
b45ba27
fix(ci): digest-pin the sandbox base image
yiliang114 Aug 27, 2026
581c917
fix(ci): ratchet the release.yml size baseline for the pool hardening
yiliang114 Aug 27, 2026
9533c96
fix(ci): reap cross-job processes and keep the pool tool cache in the…
yiliang114 Aug 27, 2026
2cf1799
fix(ci): keep the release wipe suite green and kill-free on the pool
yiliang114 Aug 27, 2026
c806030
fix(ci): keep concurrent registrations' jobs out of the release reap
yiliang114 Aug 28, 2026
6a2dd4f
fix(ci): stop the R1-1 reap probe from enumerating the pool host
yiliang114 Aug 28, 2026
52d57d3
fix(ci): keep the release wipe suite off the hosted Windows lane
yiliang114 Aug 28, 2026
11a6bb6
Merge origin/main into fix/release-ecs-runners
yiliang114 Aug 28, 2026
3c0ca80
fix(ci): remove unsafe shared-runner process reaping
yiliang114 Aug 28, 2026
d391bec
Merge branch 'main' into fix/release-ecs-runners
yiliang114 Aug 28, 2026
5222836
Merge branch 'fix/release-ecs-runners' of https://github.com/QwenLM/q…
qwen-code-dev-bot Aug 28, 2026
b37e9a4
Merge branch 'main' into fix/release-ecs-runners
qwen-code-dev-bot Aug 28, 2026
3dd2329
chore(ci): drop prettier reflow noise from teamHelpers.test.ts
yiliang114 Aug 28, 2026
1ae3588
fix(ci): re-arm the release.yml size ratchet at its true floor (#10036)
qwen-code-ci-bot Aug 28, 2026
d9ef8be
Merge remote-tracking branch 'origin/main' into fix/release-ecs-runners
yiliang114 Aug 28, 2026
88eea3f
fix(ci): strip RUNNER_WORKSPACE trailing slashes before the symlink r…
qwen-code-ci-bot Aug 28, 2026
c6bea8a
fix(ci): refuse symlinked components anywhere in the wipe path (#10036)
qwen-code-ci-bot Aug 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/.size-baseline
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
22037 release-sdk-python.yml
19094 release-sdk.yml
14546 release-vscode-companion.yml
39291 release.yml
53022 release.yml
43717 repo-hygiene.yml
1079 scorecard-monthly.yml
10691 sdk-java.yml
Expand Down
257 changes: 248 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ on:
jobs:
prepare:
name: 'Prepare Release Metadata'
runs-on: 'ubuntu-latest'
# Process cleanup belongs to the runner service/cgroup boundary. This
# workflow only resets job-owned filesystem state on the shared pool.
# MAINTAINER_ECS_RUNNER_DISABLED restores the hosted fallback.
runs-on: '${{ (github.repository == ''QwenLM/qwen-code'' && vars.MAINTAINER_ECS_RUNNER_DISABLED != ''true'') && fromJSON(''["self-hosted", "linux", "x64", "ecs-qwen"]'') || fromJSON(''["ubuntu-latest"]'') }}'
Comment thread
yiliang114 marked this conversation as resolved.
Comment thread
yiliang114 marked this conversation as resolved.
timeout-minutes: 30
Comment thread
yiliang114 marked this conversation as resolved.
Comment thread
yiliang114 marked this conversation as resolved.
if: |-
${{ github.repository == 'QwenLM/qwen-code' }}
permissions:
Expand All @@ -56,6 +60,162 @@ jobs:
is_dry_run: '${{ steps.vars.outputs.is_dry_run }}'

steps:
# Shared ECS runners can retain root-owned files from an earlier
# containerized job. Restore the reusable workspace before checkout.
- &restore_release_workspace
name: 'Restore workspace ownership'
if: "${{ runner.environment == 'self-hosted' }}"
run: |-
set -uo pipefail
# Release jobs do not need cross-job workspace reuse: remove every
# persisted entry, including planted .git config/hooks/attributes,
# before actions/checkout runs with release credentials. The full
# wipe — rather than keeping and scrubbing .git like serve-ab.yml —
# is deliberate: these checkouts run with CI_BOT_PAT and the npm
# OIDC id-token, so no pre-existing repo state may survive into
# them; the accepted cost is re-fetching full history each run.
#
# Guards ported from serve-ab.yml's wipe (#9220, #9265): under a
# mangled env even `/home` or an empty string reached the rm. A
# wipe pointed at the wrong path is far worse than a skipped wipe,
# so canonicalize, strip trailing slashes, denylist the known
# roots, and require the target to sit inside the runner workspace
# before any rm.
#
# Validate the geometry BEFORE touching anything: the chown/chmod
# ladder and the wipe must never follow a runner workspace a previous
# pool job — which may have run contributor code — replaced with a
# symlink, so refuse one outright; and no ownership/permission change
# may run on a path the containment below has not accepted.
RWS="${RUNNER_WORKSPACE:?}"
Comment thread
yiliang114 marked this conversation as resolved.
while [ "${RWS%/}" != "$RWS" ]; do RWS="${RWS%/}"; done
if [ -L "$RWS" ]; then
echo "::error::refusing to wipe: runner workspace is a symlink: ${RWS}"
Comment thread
yiliang114 marked this conversation as resolved.
exit 1
fi
# `-L` only sees the LEAF: the kernel resolves intermediate
# components too, so compare the symlink-blind lexical form
# against the full canonicalization — any difference means some
# component was a symlink re-rooting the whole chain below
# (heal, allow-list, wipe) at the link's target.
RWS_LEX="$(realpath -m -s -- "$RWS" 2>/dev/null)" || { echo "::error::refusing to wipe: realpath unavailable, cannot canonicalize ${RUNNER_WORKSPACE}"; exit 1; }
RWS="$(realpath -m -- "$RWS" 2>/dev/null)" || { echo "::error::refusing to wipe: realpath unavailable, cannot canonicalize ${RUNNER_WORKSPACE}"; exit 1; }
Comment thread
yiliang114 marked this conversation as resolved.
if [ "$RWS" != "$RWS_LEX" ]; then
echo "::error::refusing to wipe: runner workspace resolves through a symlinked component: ${RWS_LEX} resolves to ${RWS}"
exit 1
fi
while [ "${RWS%/}" != "$RWS" ]; do RWS="${RWS%/}"; done
Comment thread
yiliang114 marked this conversation as resolved.
if [ -z "$RWS" ]; then echo "::error::refusing to wipe: runner workspace resolved to /"; exit 1; fi
case "$RWS" in
..|../*|*/..|*/../*) echo "::error::refusing runner workspace path containing '..': ${RWS}"; exit 1 ;;
esac
WS="${GITHUB_WORKSPACE:?}"
while [ "${WS%/}" != "$WS" ]; do WS="${WS%/}"; done
# Heal a workspace a previous job replaced with a symlink (or any
# non-directory) BEFORE canonicalizing it: afterwards the path
# resolves to the link's target, the containment below refuses it,
# and every later job on this runner would die here permanently on
# corruption that is itself inside the runner workspace and safe
# to unlink.
if [ -L "$WS" ] || [ ! -d "$WS" ]; then
# Judge the PARENT, canonicalized: the kernel resolves
# intermediate components too, so a raw containment match is not
# enough. Never resolve $WS itself — that would resolve through
# the very link being removed.
HEAL_PARENT="$(realpath -m -- "$(dirname -- "$WS")" 2>/dev/null)" || { echo "::error::refusing to heal: realpath unavailable, cannot canonicalize the parent of ${WS}"; exit 1; }
case "$HEAL_PARENT" in
"$RWS"|"$RWS"/*) ;;
*) echo "::error::refusing to heal workspace outside the runner workspace: ${WS} (parent: ${HEAL_PARENT}, runner workspace: ${RWS})"; exit 1 ;;
esac
if [ -L "$WS" ]; then
# The link target is bytes a PREVIOUS job chose — on this pool
# that job may have run contributor code — and the runner
# parses `::` at the start of any stdout line as a workflow
# command: keep untrusted bytes off the command line itself,
# strip the line breaks that could start a new one, and cap
# the length.
heal_target="$(readlink -- "$WS" 2>/dev/null || printf '%s' '<unreadable>')"
heal_target="$(printf '%s' "$heal_target" | tr -d '\r\n' | cut -c1-200)"
echo "::warning::healing workspace ${WS}: it was a symlink"
printf 'heal: %s pointed at %s\n' "$WS" "$heal_target"
else
echo "::warning::healing workspace ${WS}: it was not a directory"
fi
# `rm -f` on the RAW path removes the link itself and never
# follows it. Both legs fail closed: a swallowed failure here
# would leave the wipe running against a corrupt path.
rm -f -- "$WS" || { echo "::error::refusing to continue: could not remove ${WS}"; exit 1; }
mkdir -- "$WS" || { echo "::error::refusing to continue: could not recreate ${WS}"; exit 1; }
fi
# Heal only guarantees the LEAF is real; a symlinked component
# between the runner workspace and the leaf re-roots the
# containment below the same way, so apply the same comparison.
WS_LEX="$(realpath -m -s -- "$WS" 2>/dev/null)" || { echo "::error::refusing to wipe: realpath unavailable, cannot canonicalize ${GITHUB_WORKSPACE}"; exit 1; }
WS="$(realpath -m -- "$WS" 2>/dev/null)" || { echo "::error::refusing to wipe: realpath unavailable, cannot canonicalize ${GITHUB_WORKSPACE}"; exit 1; }
if [ "$WS" != "$WS_LEX" ]; then
echo "::error::refusing to wipe: workspace resolves through a symlinked component: ${WS_LEX} resolves to ${WS}"
exit 1
fi
while [ "${WS%/}" != "$WS" ]; do WS="${WS%/}"; done
case "$WS" in
..|../*|*/..|*/../*) echo "::error::refusing to wipe path containing '..': ${WS}"; exit 1 ;;
esac
case "$WS" in
/|/home|/root|/usr*|/etc*|/var|"") echo "::error::refusing to wipe suspicious workspace path: ${WS}"; exit 1 ;;
esac
# A denylist can only enumerate known roots — the allowlist closes
# every other one (/tmp, /opt, ...): only a directory inside the
# runner workspace may be wiped.
case "$WS" in
"$RWS"/*) ;;
*) echo "::error::refusing to wipe workspace outside the runner workspace: ${WS} (runner workspace: ${RWS})"; exit 1 ;;
esac
# Geometry validated — only now may ownership/permissions change.
# Shared ECS runners can retain root-owned files from an earlier
# containerized job; restore them so the wipe and checkout succeed.
RUNNER_UID="$(id -u)"
RUNNER_GID="$(id -g)"
if [ "$RUNNER_UID" != "0" ]; then
chown -R "$RUNNER_UID:$RUNNER_GID" "$GITHUB_WORKSPACE" 2>/dev/null || sudo -n chown -R "$RUNNER_UID:$RUNNER_GID" "$GITHUB_WORKSPACE" || echo "::warning::could not restore workspace ownership; checkout may fail on leftover root-owned files"
fi
# The validation above guarantees $GITHUB_WORKSPACE is a real directory
# inside the runner workspace (a symlinked leaf was healed, a symlinked
# runner workspace refused), so the recursive chmod cannot escape it.
chmod -R u+rwX "$GITHUB_WORKSPACE" 2>/dev/null || sudo -n chmod -R u+rwX "$GITHUB_WORKSPACE" || echo "::warning::could not restore workspace write permissions; checkout may fail on leftover read-only files"
find "$WS" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
# Later steps must not read pool-persistent Git, npm, Docker, or
# gh state. A fresh directory avoids an unbounded scrub denylist
# and stale lock files before checkout runs.
#
# The pool-wide RUNNER_TOOL_CACHE stays untouched ON PURPOSE:
# lanes in three other pool workflows (qwen-autofix.yml's
# issue-autofix/build-cli/review-address, serve-ab.yml's ab,
# repo-hygiene.yml's dedup lane) resolve Node from it through
# un-gated setup-node, while the pool-routed release jobs never
# read the tool cache — their pool path is PATH Node via
# .github/actions/self-hosted-node. Purging `_tool/node` here
# would strip Node out from under the next such job on this
# member, and nodejs.org may be unreachable through the pool's
# egress proxy.
release_state="$(mktemp -d "${RUNNER_TEMP:?}/release-state.XXXXXX")" || exit 1
: > "${release_state}/gitconfig" || exit 1
: > "${release_state}/npmrc" || exit 1
mkdir "${release_state}/docker" || exit 1
# gh reads $HOME/.config/gh across pool jobs: a prior job could
# plant a config.yml with http_unix_socket there and capture the
# token a later `gh` call sends — qwen-autofix.yml isolates
# GH_CONFIG_DIR the same way.
mkdir "${release_state}/gh" || exit 1
{
echo 'GIT_CONFIG_COUNT=0'
echo 'GIT_CONFIG_NOSYSTEM=1'
echo 'GIT_CONFIG_PARAMETERS='
echo "GIT_CONFIG_GLOBAL=${release_state}/gitconfig"
echo "NPM_CONFIG_USERCONFIG=${release_state}/npmrc"
echo "DOCKER_CONFIG=${release_state}/docker"
echo "GH_CONFIG_DIR=${release_state}/gh"
} >> "${GITHUB_ENV:?}"
Comment thread
yiliang114 marked this conversation as resolved.

- name: 'Checkout'
uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3
with:
Expand Down Expand Up @@ -88,13 +248,22 @@ jobs:
fi
echo "is_dry_run=${is_dry_run}" >> "${GITHUB_OUTPUT}"

- name: 'Setup Node.js'
- name: 'Set up Node.js (hosted)'
if: "${{ runner.environment != 'self-hosted' }}"
uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0
with:
node-version-file: '.nvmrc'
cache: 'npm'
package-manager-cache: false
cache-dependency-path: 'package-lock.json'

# Avoid setup-node downloads on ECS, where nodejs.org may be
# unreachable through the egress proxy; reuse the machine's Node
# instead.
- name: 'Use pre-installed Node.js (self-hosted)'
if: "${{ runner.environment == 'self-hosted' }}"
uses: './.github/actions/self-hosted-node'

- name: 'Install Dependencies'
env:
NPM_CONFIG_PREFER_OFFLINE: 'true'
Expand Down Expand Up @@ -145,7 +314,8 @@ jobs:

quality:
name: 'Quality Checks'
runs-on: 'ubuntu-latest'
runs-on: '${{ (github.repository == ''QwenLM/qwen-code'' && vars.MAINTAINER_ECS_RUNNER_DISABLED != ''true'') && fromJSON(''["self-hosted", "linux", "x64", "ecs-qwen"]'') || fromJSON(''["ubuntu-latest"]'') }}'
timeout-minutes: 120
needs: 'prepare'
if: |-
${{ github.event.inputs.force_skip_tests != 'true' }}
Expand All @@ -157,19 +327,32 @@ jobs:
OPENAI_MODEL: '${{ secrets.OPENAI_MODEL }}'

steps:
# Shared ECS runners can retain root-owned files from an earlier
# containerized job. Restore the reusable workspace before checkout.
- *restore_release_workspace

- name: 'Checkout'
uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3
with:
ref: '${{ github.event.inputs.ref || github.sha }}'
fetch-depth: 0

- name: 'Setup Node.js'
- name: 'Set up Node.js (hosted)'
if: "${{ runner.environment != 'self-hosted' }}"
uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0
with:
node-version-file: '.nvmrc'
cache: 'npm'
package-manager-cache: false
cache-dependency-path: 'package-lock.json'

# Avoid setup-node downloads on ECS, where nodejs.org may be
# unreachable through the egress proxy; reuse the machine's Node
# instead.
- name: 'Use pre-installed Node.js (self-hosted)'
if: "${{ runner.environment == 'self-hosted' }}"
uses: './.github/actions/self-hosted-node'

- name: 'Install Dependencies'
env:
NPM_CONFIG_PREFER_OFFLINE: 'true'
Expand Down Expand Up @@ -207,7 +390,8 @@ jobs:

integration_none:
name: 'Integration Tests (No Sandbox)'
runs-on: 'ubuntu-latest'
runs-on: '${{ (github.repository == ''QwenLM/qwen-code'' && vars.MAINTAINER_ECS_RUNNER_DISABLED != ''true'') && fromJSON(''["self-hosted", "linux", "x64", "ecs-qwen"]'') || fromJSON(''["ubuntu-latest"]'') }}'
timeout-minutes: 120
needs: 'prepare'
if: |-
${{ github.event.inputs.force_skip_tests != 'true' }}
Expand All @@ -219,19 +403,32 @@ jobs:
OPENAI_MODEL: '${{ secrets.OPENAI_MODEL }}'

steps:
# Shared ECS runners can retain root-owned files from an earlier
# containerized job. Restore the reusable workspace before checkout.
- *restore_release_workspace

- name: 'Checkout'
uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3
with:
ref: '${{ github.event.inputs.ref || github.sha }}'
fetch-depth: 0

- name: 'Setup Node.js'
- name: 'Set up Node.js (hosted)'
if: "${{ runner.environment != 'self-hosted' }}"
uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0
with:
node-version-file: '.nvmrc'
cache: 'npm'
package-manager-cache: false
cache-dependency-path: 'package-lock.json'

# Avoid setup-node downloads on ECS, where nodejs.org may be
# unreachable through the egress proxy; reuse the machine's Node
# instead.
- name: 'Use pre-installed Node.js (self-hosted)'
if: "${{ runner.environment == 'self-hosted' }}"
uses: './.github/actions/self-hosted-node'

- name: 'Install Dependencies'
env:
NPM_CONFIG_PREFER_OFFLINE: 'true'
Expand All @@ -258,7 +455,8 @@ jobs:

integration_docker:
name: 'Integration Tests (Docker)'
runs-on: 'ubuntu-latest'
runs-on: '${{ (github.repository == ''QwenLM/qwen-code'' && vars.MAINTAINER_ECS_RUNNER_DISABLED != ''true'') && fromJSON(''["self-hosted", "linux", "x64", "ecs-qwen"]'') || fromJSON(''["ubuntu-latest"]'') }}'
Comment thread
yiliang114 marked this conversation as resolved.
timeout-minutes: 120
needs: 'prepare'
if: |-
${{ github.event.inputs.force_skip_tests != 'true' }}
Expand All @@ -270,19 +468,40 @@ jobs:
OPENAI_MODEL: '${{ secrets.OPENAI_MODEL }}'

steps:
# Shared ECS runners can retain root-owned files from an earlier
# containerized job. Restore the reusable workspace before checkout.
- *restore_release_workspace

- name: 'Check docker daemon'
run: |-
if ! docker_info_output="$(docker info 2>&1)"; then
echo "::error::docker daemon is not reachable on this runner; docker integration tests cannot run."
printf '%s\n' "$docker_info_output"
exit 1
fi

- name: 'Checkout'
uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3
with:
ref: '${{ github.event.inputs.ref || github.sha }}'
fetch-depth: 0

- name: 'Setup Node.js'
- name: 'Set up Node.js (hosted)'
if: "${{ runner.environment != 'self-hosted' }}"
uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0
with:
node-version-file: '.nvmrc'
cache: 'npm'
package-manager-cache: false
cache-dependency-path: 'package-lock.json'

# Avoid setup-node downloads on ECS, where nodejs.org may be
# unreachable through the egress proxy; reuse the machine's Node
# instead.
- name: 'Use pre-installed Node.js (self-hosted)'
if: "${{ runner.environment == 'self-hosted' }}"
uses: './.github/actions/self-hosted-node'

- name: 'Install Dependencies'
env:
NPM_CONFIG_PREFER_OFFLINE: 'true'
Expand Down Expand Up @@ -327,6 +546,8 @@ jobs:

publish:
name: 'Publish Release'
# Publishing carries release credentials, so keep it off the shared pool
# that also executes pull-request code.
runs-on: 'ubuntu-latest'
needs:
- 'prepare'
Expand Down Expand Up @@ -384,6 +605,10 @@ jobs:
pull-requests: 'write'

steps:
# Shared ECS runners can retain root-owned files from an earlier
# containerized job. Restore the reusable workspace before checkout.
- *restore_release_workspace
Comment thread
yiliang114 marked this conversation as resolved.

- name: 'Checkout'
uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3
with:
Expand All @@ -396,7 +621,8 @@ jobs:
uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0
with:
node-version-file: '.nvmrc'
cache: 'npm'
cache: "${{ runner.environment != 'self-hosted' && 'npm' || '' }}"
package-manager-cache: false
cache-dependency-path: 'package-lock.json'
registry-url: 'https://registry.npmjs.org'
scope: '@qwen-code'
Expand Down Expand Up @@ -747,7 +973,20 @@ jobs:

notify_failure:
name: 'Notify Release Failure'
# Pinned to an ephemeral hosted runner instead of the ECS pool the other
# release jobs route to: this job exists to report failures OF that pool.
# In post-claim failure modes (a runner crash or host loss mid-job,
# pool-wide network/tooling loss) the upstream jobs are marked failed and
# the `if:` gate below opens while the pool is exactly what is broken —
# routing the notifier back onto it means a failed release produces no
# failure issue, no autofix dispatch, no alert. The job is gh/jq-only
# (both preinstalled on hosted images), so hosted capacity costs nothing
# here. The sibling failure notifiers stay hosted for the same reason
# (release-vscode-companion.yml, release-sdk.yml, release-sdk-python.yml;
# qwen-code-pr-review.yml's fallback comment: it "runs on an ephemeral
# hosted runner, so it survives whatever killed the review job").
runs-on: 'ubuntu-latest'
timeout-minutes: 10
Comment thread
yiliang114 marked this conversation as resolved.
needs:
- 'prepare'
- 'quality'
Expand Down
Loading
Loading