ci: take CodeQL and E2E off the per-merge push path (#5859)

After #5842, CodeQL and the E2E suite were the only things running on every push to `main` — the per-commit, post-merge backstop. With merges landing back-to-back, those runs (CodeQL ~30min, three E2E jobs ~25/36/36min, the latter never cancel-superseded) stacked on the scarce hosted Linux pool and were a main driver of the recent runner-saturation incident.

- CodeQL moves to its own scheduled codeql.yml (nightly + workflow_dispatch). It was never a required check and findings still surface in the Security tab, so per-commit scanning bought little. ci.yml loses its now-empty `push` trigger as a result (every remaining job is gated to pull_request / merge_group).
- E2E gets event-scoped concurrency so back-to-back pushes to `main` cancel superseded runs (only the latest tree matters and it covers every merged change), plus a nightly full regression as the guaranteed signal in case a busy merge window keeps cancelling the push run. Manual workflow_dispatch added.

The merge_group gate (ubuntu + integration + mac + win) still validates every PR before it lands; this only changes the non-gating post-merge work.

Follow-up (separate PR): alert on E2E/CodeQL failure (a deduped ci-failure issue) now that they run unattended.
This commit is contained in:
易良 2026-06-25 18:50:12 +08:00 committed by GitHub
parent 7632301b64
commit d851f49135
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 61 additions and 47 deletions

View file

@ -3,12 +3,10 @@
name: 'Qwen Code CI'
on:
push:
branches:
- 'main'
# No 'release/**' here: release.yml's commits to the release branch would
# fire a redundant full CI run that blocks the release PR's merge queue.
# Kept under pull_request below for backport PRs.
# No `push` trigger: every job here is gated to pull_request / merge_group, so
# a push to `main` ran nothing (CodeQL was the last push job and moved to its
# own scheduled codeql.yml). The merge queue validates the merged tree before
# it lands, so there is nothing left to run on the post-merge push.
pull_request:
branches:
- 'main'
@ -430,36 +428,6 @@ jobs:
os: '${{ matrix.os }}'
github_token: '${{ secrets.GITHUB_TOKEN }}'
codeql:
name: 'CodeQL'
needs: 'classify_pr'
# Security findings rarely change push-to-push, and a slow scan is poor value
# as a hard merge gate. Run CodeQL only on push to `main` (post-merge
# backstop) — not on PR pushes, and not in the merge queue where, being
# non-required, it would just burn a runner and lengthen the serial queue
# without blocking anything.
if: "${{ !cancelled() && needs.classify_pr.outputs.skip_ci != 'true' && github.event_name != 'pull_request' && github.event_name != 'merge_group' }}"
runs-on: 'ubuntu-latest'
# Analysis normally finishes in ~7-9 min (22 min worst case observed). Without
# an explicit cap, a hosted runner that drops its heartbeat mid-analysis leaves
# the job "in_progress" for the default 6h, holding a scarce hosted Linux slot
# the whole time. 30 min reaps such an orphan fast while clearing real runs.
timeout-minutes: 30
permissions:
actions: 'read'
contents: 'read'
security-events: 'write'
steps:
- name: 'Checkout'
uses: 'actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd' # v6.0.2
- name: 'Initialize CodeQL'
uses: 'github/codeql-action/init@df559355d593797519d70b90fc8edd5db049e7a2' # ratchet:github/codeql-action/init@v3
with:
languages: 'javascript'
- name: 'Perform CodeQL Analysis'
uses: 'github/codeql-action/analyze@df559355d593797519d70b90fc8edd5db049e7a2' # ratchet:github/codeql-action/analyze@v3
# Integration tests run only in the merge queue, not on every PR push.
# They are the suite that previously ran *only* in the nightly Release

41
.github/workflows/codeql.yml vendored Normal file
View file

@ -0,0 +1,41 @@
name: 'CodeQL'
# CodeQL used to be a per-push job in ci.yml. Security findings rarely change
# commit-to-commit and the scan is non-blocking (never a required check), so
# running it on every merge to `main` just piled onto the scarce hosted Linux
# pool. Run it on a nightly schedule (plus manual dispatch) instead; findings
# still surface in the Security tab. If fresher results are wanted, add a
# path-filtered `push` trigger for security-relevant sources.
on:
schedule:
- cron: '0 3 * * *' # nightly (~03:00 UTC), staggered from the E2E nightly
workflow_dispatch:
permissions:
actions: 'read'
contents: 'read'
security-events: 'write'
concurrency:
group: 'codeql'
cancel-in-progress: false
jobs:
codeql:
name: 'CodeQL'
runs-on: 'ubuntu-latest'
# Analysis normally finishes in ~7-9 min (22 min worst case observed). Cap it
# so a runner that drops its heartbeat mid-analysis can't hold a slot for the
# default 6h.
timeout-minutes: 30
steps:
- name: 'Checkout'
uses: 'actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd' # v6.0.2
- name: 'Initialize CodeQL'
uses: 'github/codeql-action/init@df559355d593797519d70b90fc8edd5db049e7a2' # ratchet:github/codeql-action/init@v3
with:
languages: 'javascript'
- name: 'Perform CodeQL Analysis'
uses: 'github/codeql-action/analyze@df559355d593797519d70b90fc8edd5db049e7a2' # ratchet:github/codeql-action/analyze@v3

View file

@ -1,25 +1,30 @@
name: 'E2E Tests'
on:
# E2E runs post-merge on `main` (per-commit signal) only. It is intentionally
# NOT in the merge queue yet: it is slow and currently flaky, so gating the
# serial queue on it would stall every merge. Promote it to `merge_group` +
# required once it is reliable.
# E2E is slow and currently flaky, so it is NOT in the merge queue (gating the
# serial queue on it would stall every merge). It runs post-merge on `main`,
# plus a nightly full regression and on-demand. Promote it to `merge_group` +
# required once a stable subset is carved out.
push:
branches:
- 'main'
- 'feat/e2e/**'
schedule:
- cron: '0 4 * * *' # nightly full regression (~04:00 UTC), guaranteed signal
workflow_dispatch:
concurrency:
# Key on the head ref so a `push` to a `feat/e2e/**` branch and a
# `pull_request` from that same branch share one group and cancel each
# other instead of running the matrix twice for the same change.
# Scope the group by event so back-to-back pushes to `main` cancel superseded
# runs (no pile-up when PRs merge in quick succession), without ever cancelling
# the nightly schedule or a manual dispatch — they share the ref but not the
# event. feat/e2e/** pushes still cancel their own superseded runs.
group: |-
${{ github.workflow }}-${{ github.head_ref || github.ref_name }}
# Cancel superseded feature-branch runs, but let every `main` commit finish
# (complete per-commit e2e signal, matching ci.yml).
${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref_name }}
# Cancel superseded pushes (main included now — only the latest tree matters,
# and it covers every merged change). The nightly run always finishes, so a
# busy merge window that keeps cancelling the push run still gets a signal.
cancel-in-progress: |-
${{ github.event_name == 'push' && github.ref != 'refs/heads/main' }}
${{ github.event_name == 'push' }}
jobs:
e2e-test-linux: