qwen-code/.github/scripts
qwen-code-dev-bot 253f8b8daf
fix(ci): harden self-hosted runner workspace ownership recovery (#8115)
* fix(ci): harden self-hosted runner workspace ownership recovery

Containerised jobs (qwen-triage verify/tmux) leave root- or node-owned
files in the runner workspace.  When the next job's actions/checkout
tries to remove them it fails with EACCES, permanently poisoning the
runner for all subsequent jobs.

Three-layer fix:

1. qwen-triage.yml: split the ownership-restore (chmod + chown back to
   the runner user) out of the conditional 'Clean up runner workspace'
   step into its own 'if: always()' step, so it runs even when the job
   is cancelled or skipped — while the container still has root.

2. qwen-code-pr-review.yml / ci.yml: after the existing chown + sudo
   attempts, probe each known problem dir (.qwen, .git) with touch;
   if unwriteable, rename it aside (mv only needs write on the parent
   directory, which the runner user owns).  ci.yml gains the full
   'Restore workspace ownership' step it previously lacked.

3. ci.yml 'Clean stale .qwen before checkout' now also removes any
   .qwen.stale.* directories renamed aside by the step above.

Refs: runs 30339720611 (actions-runner-8), 30480422410
(actions-runner-test-14) — both EACCES on .qwen/agents.

* fix(ci): address review feedback on workspace ownership recovery

Repoint the verify cleanup regression test at the new 'Restore workspace
ownership' step so the chmod-before-chown guard is active again, and remove
renamed-aside .git.stale.* / .qwen.stale.* dirs in ci.yml and
qwen-code-pr-review.yml so they no longer accumulate on self-hosted runners.

* test(ci): guard unconditional ownership restore for verify and tmux (#8115)

* test(ci): guard rename-aside ownership recovery in ci.yml and pr-review (#8115)

* fix(ci): address review feedback on workspace ownership recovery

- Make .stale.* cleanup failure visible with :⚠️: instead of
  silent || true; add sudo -n rm -rf fallback to ci.yml cleanup step
- Add .stale.* sweep to containerised verify/tmux-testing ownership-
  restore steps (run as root, the only actor that can delete them)
- Extend ownership recovery to web_shell_e2e_smoke and integration_cli
  jobs which share the same ecs-qwen self-hosted runner pool
- Probe-first optimization: skip expensive recursive chown/chmod on
  healthy runs; only pay for the full-tree walk when a probe fails
- Use mkdir/rmdir instead of touch/rm for writability probe (mkdir
  never follows symlinks, avoiding a planted-symlink vector)
- Use GITHUB_RUN_ID.GITHUB_RUN_ATTEMPT instead of $$ for unique
  suffix (PID recycles on long-lived runners)
- Add set -uo pipefail and $GITHUB_WORKSPACE/ absolute paths to
  ci.yml cleanup step
- Keep rename-aside blocks byte-identical across ci.yml and
  qwen-code-pr-review.yml with a NOTE comment explaining why
  extraction into .github/scripts/ is impossible
- Add :⚠️: on chmod failure in verify ownership-restore step
- Add tests: new job coverage, byte-identical block assertion,
  .stale.* sweep assertions, cleanup hardening assertions (#8115)

* test(ci): guard all four rename-aside copies in byte-identical assertion (#8115)

* fix(ci): drop inert rename-aside, restore unconditional recovery (#8115)

Review verification showed the pre-checkout rename-aside fallback never
unblocks actions/checkout: checkout deletes every workspace entry (or runs
git clean -ffdx), walking straight into the renamed dir, and the rename only
fires when the runner does not own the dir — exactly when rm -rf cannot empty
it either. Drop it from all four checkout jobs and the qwen-triage root
sweeps, restoring the simpler unconditional chown/chmod recovery.

The probe-first gating is removed for the same reason: poisoning is
workspace-wide (root-owned node_modules/dist with no .qwen/.git), so a probe
that only checks .qwen/.git reports "healthy" and skips the chown that main
did unconditionally — a regression on runners with passwordless sudo. The
layer-1 fix (qwen-triage ownership restore running as root under
if: always()) is the actual root-cause fix and is kept unchanged.

* fix(ci): address review feedback on ownership tests (#8115)

Hoist assertUnconditional helper to module scope and reuse it in the
pr-review describe block, add sudo-fallback and stat-based UID discovery
assertions to guard the recovery branches that are load-bearing on
non-root and containerised runners.

* fix(ci): cover triage job, add chown warnings, tmux chmod (#8115)

* fix(ci): assert restore-before-checkout ordering, surface sudo chown stderr (#8115)

* fix(ci): widen verify restore chmod, guard symlinked .qwen, surface sudo chmod stderr (#8115)

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: qwen-code-autofix[bot] <qwen-code-autofix[bot]@users.noreply.github.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
2026-08-01 07:35:12 +00:00
..
ci fix(ci): keep the post-merge E2E signal on main alive (#7795) 2026-07-28 11:54:52 +00:00
dsw-swe-verified ci: add isolated DSW SWE-bench release pipeline (#7656) 2026-07-29 06:35:58 +00:00
auto-minimize-spam.test.mjs ci: auto-minimize comments from org-blocked users (#7899) 2026-07-29 23:37:42 +00:00
cap-release-notes.mjs fix(release): keep notes anchored and cap the release body (#8199) 2026-07-31 09:55:38 +00:00
cap-release-notes.test.mjs fix(release): keep notes anchored and cap the release body (#8199) 2026-07-31 09:55:38 +00:00
check-autofix-contracts.sh ci(autofix): add cross-package contract verification (#7642) 2026-07-24 05:13:04 +00:00
check-settings-schema.sh ci(autofix): recover from generated-artifact CI gates and stop silent stalls (#6998) 2026-07-17 03:26:43 +00:00
ci-flaky-rerun.mjs feat(ci): auto-open a deflake fix issue for confirmed flaky tests (#7231) 2026-07-19 16:49:29 +00:00
classify-release-notes.mjs ci: move release-note classifier from per-PR workflow to release-time batch (#7339) 2026-07-20 23:52:09 +00:00
classify-release-notes.test.mjs ci: move release-note classifier from per-PR workflow to release-time batch (#7339) 2026-07-20 23:52:09 +00:00
pr-safety-precheck.mjs fix(ci): limit fork PR precheck to safety signals (#6178) 2026-07-02 20:56:41 +08:00
pr-safety-precheck.test.mjs fix(ci): limit fork PR precheck to safety signals (#6178) 2026-07-02 20:56:41 +08:00
pr-triage.sh Sync upstream Gemini-CLI v0.8.2 (#838) 2025-10-23 09:27:04 +08:00
qwen-triage-workflow.test.mjs fix(ci): harden self-hosted runner workspace ownership recovery (#8115) 2026-08-01 07:35:12 +00:00
resolve-owning-packages.sh fix(autofix): resolve owning package for nested paths; report verify-failed handoffs as not pushed (#7330) 2026-07-20 14:39:56 +00:00
resolve-sandbox-image.mjs ci(autofix): restore sandbox image flow (#6261) 2026-07-03 15:30:58 +00:00
resolve-sandbox-image.test.mjs ci(autofix): restore sandbox image flow (#6261) 2026-07-03 15:30:58 +00:00
run-autofix-review-verification.sh fix(autofix): guard review thread resolution (#8231) 2026-07-31 16:04:30 +00:00
serve-ab-diff.mjs ci(serve): daemon A/B before/after preview on response-surface PRs (#6975) 2026-07-16 00:58:52 +00:00
serve-ab-diff.test.mjs ci(serve): daemon A/B before/after preview on response-surface PRs (#6975) 2026-07-16 00:58:52 +00:00
serve-ab-drive.mjs ci(serve): daemon A/B before/after preview on response-surface PRs (#6975) 2026-07-16 00:58:52 +00:00
web-shell-visuals-compose.mjs ci(web-shell): denoise cross-job font-AA so visual previews stop false-flagging (#7210) 2026-07-19 11:27:08 +00:00
web-shell-visuals-compose.test.mjs ci(web-shell): denoise cross-job font-AA so visual previews stop false-flagging (#7210) 2026-07-19 11:27:08 +00:00
web-shell-visuals-publish.mjs fix(ci): don't let one failing scenario sink the whole visual preview (#7511) 2026-07-23 02:34:07 +00:00
web-shell-visuals-publish.test.mjs fix(ci): don't let one failing scenario sink the whole visual preview (#7511) 2026-07-23 02:34:07 +00:00