From 839e9be27614cacb3b1e1c9f11acd27cf346645a Mon Sep 17 00:00:00 2001 From: wenshao Date: Thu, 25 Jun 2026 16:05:44 +0800 Subject: [PATCH] ci: route merge-queue Linux jobs to the ECS runner pool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The merge queue (`merge_group`) skipped `classify_pr` (it was gated to `pull_request`), so the required `Test (ubuntu-latest)` gate and the `Integration Tests` job both fell back to hosted `ubuntu-latest`. With the queue live, every entry piled those onto the small hosted Linux pool while the self-hosted ECS runners sat idle — the required gate starved in `queued` for 30+ minutes and the queue stalled. Extend the existing ECS routing to `merge_group`: - `classify_pr` now also runs for `merge_group` and emits the ECS runner label set (the queue runs in the base-repo context, so it is trusted). The required `Test` gate picks this up automatically via its output. - `integration_cli` routes through `classify_pr` like the gate, with the same hosted/self-hosted Node handling (ECS has Node 22 pre-installed and cannot reach nodejs.org reliably). macOS/Windows stay hosted (ECS is Linux-only). The `MAINTAINER_ECS_RUNNER_DISABLED` kill-switch still reverts everything to hosted. --- .github/workflows/ci.yml | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d95b6a31d0c..70a134feff8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,9 +44,12 @@ env: jobs: classify_pr: name: 'Classify PR' - if: "${{ github.event_name == 'pull_request' }}" - # Gate runs on ECS for in-repo PRs too, else a busy hosted pool delays it and blocks the ECS-bound jobs. The kill-switch is read here, so flipping it reverts everything to hosted. - runs-on: '${{ (vars.MAINTAINER_ECS_RUNNER_DISABLED != ''true'' && github.event.pull_request.head.repo.full_name == github.repository) && fromJSON(''["self-hosted", "linux", "x64", "ecs-qwen"]'') || fromJSON(''["ubuntu-latest"]'') }}' + if: "${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }}" + # Gate runs on ECS for in-repo PRs and for the merge queue (which runs in the + # base-repo context), else a busy hosted pool delays it and blocks the + # ECS-bound jobs. The kill-switch is read here, so flipping it reverts + # everything to hosted. + runs-on: '${{ (vars.MAINTAINER_ECS_RUNNER_DISABLED != ''true'' && (github.event.pull_request.head.repo.full_name == github.repository || github.event_name == ''merge_group'')) && fromJSON(''["self-hosted", "linux", "x64", "ecs-qwen"]'') || fromJSON(''["ubuntu-latest"]'') }}' continue-on-error: true outputs: skip_ci: '${{ steps.release_sync.outputs.skip_ci }}' @@ -88,16 +91,18 @@ jobs: echo "skip_ci=${skip_ci}" >> "${GITHUB_OUTPUT}" echo "skip_ci=${skip_ci}" - # In-repo PR (head branch in this repo => author has write access) runs the - # Linux Test on ECS; forks stay hosted. Disable via repo var MAINTAINER_ECS_RUNNER_DISABLED=true. + # In-repo PR (head branch in this repo => author has write access) and the + # merge queue (base-repo context) run the Linux jobs on ECS; fork PRs stay + # hosted. Disable via repo var MAINTAINER_ECS_RUNNER_DISABLED=true. - name: 'Select Linux runner' id: 'pick_runner' env: SAME_REPO: '${{ github.event.pull_request.head.repo.full_name == github.repository }}' ECS_DISABLED: '${{ vars.MAINTAINER_ECS_RUNNER_DISABLED }}' + EVENT_NAME: '${{ github.event_name }}' run: |- ubuntu_runner='["ubuntu-latest"]' - if [[ "${ECS_DISABLED}" != "true" && "${SAME_REPO}" == "true" ]]; then + if [[ "${ECS_DISABLED}" != "true" && ( "${SAME_REPO}" == "true" || "${EVENT_NAME}" == "merge_group" ) ]]; then ubuntu_runner='["self-hosted", "linux", "x64", "ecs-qwen"]' fi echo "ubuntu_runner=${ubuntu_runner}" >> "${GITHUB_OUTPUT}" @@ -463,8 +468,13 @@ jobs: # `test:integration:cli:sandbox:none` script from `release.yml`. integration_cli: name: 'Integration Tests (CLI, No Sandbox)' - if: "${{ github.event_name == 'merge_group' }}" - runs-on: 'ubuntu-latest' + needs: 'classify_pr' + # Same ECS routing as the Ubuntu gate (via classify_pr): the merge queue runs + # in the base-repo context, so use the self-hosted ECS pool and keep the + # scarce hosted Linux runners free. Falls back to hosted if classify_pr is + # skipped or the ECS kill-switch is set. + if: "${{ !cancelled() && github.event_name == 'merge_group' }}" + runs-on: '${{ fromJSON(needs.classify_pr.outputs.ubuntu_runner || ''["ubuntu-latest"]'') }}' permissions: contents: 'read' env: @@ -475,13 +485,25 @@ jobs: - name: 'Checkout' uses: 'actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd' # v6.0.2 - - name: 'Setup Node.js' + # Hosted downloads Node; self-hosted ECS reuses its pre-installed Node 22 + # (it can't reach nodejs.org reliably). Mirrors the Ubuntu gate. + - name: 'Setup Node.js (hosted)' + if: "${{ runner.environment == 'github-hosted' }}" uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0 with: node-version-file: '.nvmrc' cache: 'npm' cache-dependency-path: 'package-lock.json' + - name: 'Use pre-installed Node.js (self-hosted)' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + if ! command -v node >/dev/null 2>&1; then + echo "::error::Node.js is not on PATH for this self-hosted runner. Provision Node 22.x or set the MAINTAINER_ECS_RUNNER_DISABLED repository variable to 'true' to route the merge queue back to hosted runners." + exit 1 + fi + echo "Using pre-installed Node $(node -v) / npm $(npm -v)" + - name: 'Install Dependencies' env: NPM_CONFIG_PREFER_OFFLINE: 'true'