mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
* fix(ci): pin the fleet install to the /usr/local prefix
The fleet updater installs with `sudo env -u NPM_CONFIG_PREFIX npm
install -g`, which clears the environment variable but not npm's own
computed default prefix. On hk-4 and hk-5 that default is a custom Node
directory, `/usr/local/lib/nodejs/node-v22.23.2-linux-x64`, so the new
version installs to a bin directory nothing on the runner's PATH reads
while `/usr/local/bin/qwen` keeps pointing at the previous install.
The failure is silent in the only place it could be caught early: npm
reports "changed 16 packages" exactly as it does on the hosts where the
prefixes happen to agree, and only the verify step notices, by which
point the log offers no hint as to where the install went. hk-4 and hk-5
sat on 0.23.0 through 0.23.1, 0.23.2 and 0.23.3 that way, five
consecutive runs, each of them green through the install step.
`--prefix` is a command-line option and outranks the computed default,
root's npmrc and /etc/npmrc alike. Verified on hk-4: the same install
with the prefix pinned moved it from 0.23.0 to 0.23.3 immediately.
This restores c64936d6, which carried the same one-line change and was
dropped from #10921 to keep that PR's scope on the npm publish timeout.
* test(ci): pin the /usr/local prefix so it cannot be dropped again
Nothing asserted on this flag, which is how it left the tree the first
time: it was written, reviewed, and then scoped out of #10921 with no
test objecting, and four releases went to two pools that never received
them.
The existing case checks that NPM_CONFIG_PREFIX is cleared, and that
substring survives either way, so it passes with or without the flag.
This adds the assertion that actually fails on the version of the
workflow that shipped.
174 lines
7.2 KiB
YAML
174 lines
7.2 KiB
YAML
name: 'Update ECS Runner Qwen'
|
|
|
|
on:
|
|
push:
|
|
branches: ['main']
|
|
paths: ['.github/workflows/update-ecs-runner-qwen.yml']
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: 'Optional. Empty means latest from npm.'
|
|
required: false
|
|
default: ''
|
|
type: 'string'
|
|
repository_dispatch:
|
|
types: ['npm-published']
|
|
|
|
permissions:
|
|
contents: 'read'
|
|
|
|
jobs:
|
|
# Resolving on a hosted runner, once, keeps the registry wait below off the
|
|
# ECS pools (which queue behind real review/triage work) and guarantees every
|
|
# pool installs the SAME version even when their jobs start hours apart.
|
|
resolve:
|
|
name: 'Resolve version'
|
|
if: "${{ github.repository == 'QwenLM/qwen-code' }}"
|
|
runs-on: 'ubuntu-latest'
|
|
timeout-minutes: 100
|
|
outputs:
|
|
version: '${{ steps.version.outputs.version }}'
|
|
steps:
|
|
- name: 'Resolve version'
|
|
id: 'version'
|
|
env:
|
|
INPUT_VERSION: '${{ inputs.version || github.event.client_payload.version }}'
|
|
# npm publishes asynchronously: `npm publish --provenance` returns
|
|
# "Your package is being processed and may take a few minutes to
|
|
# become available", and release.yml dispatches this workflow as soon
|
|
# as it returns. v0.23.0 took 55m22s to become resolvable, so the
|
|
# previous 25-minute budget expired while the package was still in
|
|
# flight. Wait up to 90 minutes and treat anything longer as an
|
|
# anomalous publish.
|
|
RESOLVE_TIMEOUT_SECONDS: '5400'
|
|
RESOLVE_INTERVAL_SECONDS: '30'
|
|
run: |-
|
|
set -euo pipefail
|
|
specifier="@qwen-code/qwen-code@${INPUT_VERSION#v}"
|
|
if [[ "${specifier}" == '@qwen-code/qwen-code@' ]]; then
|
|
specifier='@qwen-code/qwen-code@latest'
|
|
fi
|
|
# Per-attempt stderr is held back so ~50 identical 404 blocks do not
|
|
# bury the log; the last one is replayed when the wait gives up.
|
|
err_log="$(mktemp)"
|
|
deadline=$(( SECONDS + RESOLVE_TIMEOUT_SECONDS ))
|
|
while :; do
|
|
version="$(npm view "${specifier}" version 2>"${err_log}" | tail -n 1)" || true
|
|
if [[ -n "${version}" ]]; then
|
|
break
|
|
fi
|
|
if (( SECONDS >= deadline )); then
|
|
cat "${err_log}" >&2
|
|
echo "::error::No published qwen version matches '${INPUT_VERSION:-latest}' after ${RESOLVE_TIMEOUT_SECONDS}s."
|
|
exit 1
|
|
fi
|
|
echo "'${specifier}' is not on the registry yet; retrying in ${RESOLVE_INTERVAL_SECONDS}s."
|
|
sleep "${RESOLVE_INTERVAL_SECONDS}"
|
|
done
|
|
echo "version=${version}" >> "${GITHUB_OUTPUT}"
|
|
echo "Resolved qwen version: ${version}"
|
|
|
|
update:
|
|
name: 'Update Qwen on ${{ matrix.runner }}'
|
|
needs: 'resolve'
|
|
if: "${{ github.repository == 'QwenLM/qwen-code' }}"
|
|
strategy:
|
|
matrix:
|
|
runner:
|
|
[
|
|
'ecs-update-hk-1',
|
|
'ecs-update-hk-2',
|
|
'ecs-update-hk-3',
|
|
'ecs-update-hk-4',
|
|
'ecs-update-hk-5',
|
|
]
|
|
fail-fast: false
|
|
runs-on: ['self-hosted', 'linux', 'x64', '${{ matrix.runner }}']
|
|
concurrency:
|
|
group: 'update-ecs-runner-qwen-${{ matrix.runner }}'
|
|
cancel-in-progress: false
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: 'Update qwen'
|
|
env:
|
|
VERSION: '${{ needs.resolve.outputs.version }}'
|
|
run: |-
|
|
set -euo pipefail
|
|
# The runner service resolves the system-wide qwen binary. Do not
|
|
# carry a runner user's custom npm prefix into the sudo install.
|
|
(
|
|
cd "${RUNNER_TEMP:?}"
|
|
# All runner processes of a region share one machine, so another
|
|
# job's concurrent `npm install -g` can race npm's rename of the
|
|
# package dir (ENOTEMPTY). Clear trash left by crashed installs
|
|
# and retry through the seconds-long race window.
|
|
PKG_DIR='/usr/local/lib/node_modules/@qwen-code'
|
|
for attempt in 1 2 3; do
|
|
sudo rm -rf "${PKG_DIR}"/.qwen-code-* 2>/dev/null || true
|
|
# --prefix pins the install to /usr/local: on the hk-4/hk-5
|
|
# machines root's npm global prefix is a custom Node directory
|
|
if sudo env -u NPM_CONFIG_PREFIX npm install -g --prefix /usr/local --registry=https://registry.npmjs.org "@qwen-code/qwen-code@${VERSION}"; then
|
|
exit 0
|
|
fi
|
|
if [[ "${attempt}" -lt 3 ]]; then
|
|
echo "::warning::npm install attempt ${attempt} failed; retrying"
|
|
sleep $((attempt * 10))
|
|
else
|
|
echo "::error::npm install of @qwen-code/qwen-code@${VERSION} failed after 3 attempts"
|
|
fi
|
|
done
|
|
exit 1
|
|
)
|
|
|
|
- name: 'Verify version'
|
|
env:
|
|
VERSION: '${{ needs.resolve.outputs.version }}'
|
|
run: |-
|
|
set -euo pipefail
|
|
qwen_path="$(command -v qwen)"
|
|
actual="$("${qwen_path}" --version)"
|
|
echo "qwen path: ${qwen_path}"
|
|
echo "qwen version: ${actual}"
|
|
test "${actual}" = "${VERSION}"
|
|
|
|
# A failed fleet update is otherwise invisible. `qwen-code-pr-review.yml` and
|
|
# `qwen-triage.yml` install the CLI only when it is absent, and on a
|
|
# self-hosted runner it never is, so a pool that misses an update keeps
|
|
# serving PRs on the old version indefinitely with nothing to distinguish it
|
|
# from a healthy one — on v0.22.3 that went unnoticed for a full day.
|
|
# `main-ci-failure-issue.yml` cannot cover this: it watches test suites on
|
|
# `main`, and this workflow runs off `repository_dispatch`.
|
|
report_failure:
|
|
name: 'Report a stale fleet'
|
|
needs: ['resolve', 'update']
|
|
# `cancelled` is deliberately not a failure: a newer dispatch supersedes an
|
|
# older one's still-pending legs through the per-pool concurrency group,
|
|
# which is routine and says nothing about the fleet's state.
|
|
if: "${{ always() && github.repository == 'QwenLM/qwen-code' && (needs.resolve.result == 'failure' || needs.update.result == 'failure') }}"
|
|
# Hosted, not the ECS pool: this job reports that the pools are broken, and
|
|
# queueing the report behind them would be the same trap it documents.
|
|
runs-on: 'ubuntu-latest'
|
|
timeout-minutes: 5
|
|
permissions:
|
|
# A job-level block replaces the workflow's, so checkout needs contents
|
|
# spelled out here even though the job only files an issue.
|
|
contents: 'read'
|
|
# Read this run's per-pool job conclusions.
|
|
actions: 'read'
|
|
issues: 'write'
|
|
steps:
|
|
- name: 'Checkout'
|
|
uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: 'File or update the stale-fleet issue'
|
|
env:
|
|
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
|
REPO: '${{ github.repository }}'
|
|
RUN_ID: '${{ github.run_id }}'
|
|
RUN_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
|
VERSION: '${{ needs.resolve.outputs.version }}'
|
|
DEDUP_LABEL: 'scope/ci-cd'
|
|
run: 'bash .github/scripts/ecs-fleet-update-failure-issue.sh'
|