From d851f491351ae8da1c275c783cdcd9b584c5cbb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E8=89=AF?= <1204183885@qq.com> Date: Thu, 25 Jun 2026 18:50:12 +0800 Subject: [PATCH] ci: take CodeQL and E2E off the per-merge push path (#5859) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .github/workflows/ci.yml | 40 ++++------------------------------- .github/workflows/codeql.yml | 41 ++++++++++++++++++++++++++++++++++++ .github/workflows/e2e.yml | 27 ++++++++++++++---------- 3 files changed, 61 insertions(+), 47 deletions(-) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2badc2a60..496677b82a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..ded8856643 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -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 diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 9b166ba44c..202b44564c 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -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: