From 059a2fd86d4768535b8c6ff98a366942ce7d9276 Mon Sep 17 00:00:00 2001 From: shuppy Date: Mon, 15 Sep 2025 12:54:43 +0800 Subject: [PATCH] ci: Convert runner select to composite action (#39270) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit to run bencher builds on self-hosted runners (#39269), we will need to do a self-hosted runner select, but that would exceed the workflow call depth limit (try → dispatch-workflow → linux → bencher → self-hosted-runner-select). this patch converts the self-hosted runner select from a [reusable workflow](https://docs.github.com/en/actions/concepts/workflows-and-actions/reusable-workflows) to a [composite action](https://docs.github.com/en/actions/tutorials/create-actions/create-a-composite-action), much like #38503 did for the runner timeout. Testing: - self-hosted - GitHub-hosted Fixes: part of #39269 --------- Signed-off-by: Delan Azabani --- .github/actions/runner-select/action.yml | 100 ++++++++++++++++++ .github/workflows/linux.yml | 35 ++++-- .github/workflows/mac.yml | 29 +++-- .../workflows/self-hosted-runner-select.yml | 100 ------------------ .github/workflows/windows.yml | 29 +++-- 5 files changed, 172 insertions(+), 121 deletions(-) create mode 100644 .github/actions/runner-select/action.yml delete mode 100644 .github/workflows/self-hosted-runner-select.yml diff --git a/.github/actions/runner-select/action.yml b/.github/actions/runner-select/action.yml new file mode 100644 index 00000000000..975797a0f70 --- /dev/null +++ b/.github/actions/runner-select/action.yml @@ -0,0 +1,100 @@ +name: Select Self-hosted Runner +inputs: + monitor-api-token: + required: true + type: string + github-hosted-runner-label: + required: true + type: string + self-hosted-image-name: + required: true + type: string + self-hosted-runner-scope: + required: false + type: string + default: /orgs/${{ github.repository_owner }}/actions/runners + force-github-hosted-runner: + required: false + type: boolean + default: false + NO_SELF_HOSTED_RUNNERS: + required: false + type: string + default: "" +outputs: + unique-id: + value: ${{ steps.select.outputs.unique_id }} + selected-runner-label: + value: ${{ steps.select.outputs.selected_runner_label }} + is-self-hosted: + value: ${{ steps.select.outputs.is_self_hosted }} + +runs: + using: "composite" + # Selects a self-hosted runner if available, or else a GitHub-hosted runner. + # We generate a unique id for the workload, then ask our monitor API to + # reserve a self-hosted runner for us. + steps: + - name: Select and reserve best available runner + id: select + shell: bash + run: | + github_hosted_runner_label='${{ inputs.github-hosted-runner-label }}' + self_hosted_image_name='${{ inputs.self-hosted-image-name }}' + self_hosted_runner_scope='${{ inputs.self-hosted-runner-scope }}' + + set -euo pipefail + + fall_back_to_github_hosted() { + echo 'Falling back to GitHub-hosted runner' + echo "selected_runner_label=$github_hosted_runner_label" | tee -a $GITHUB_OUTPUT + echo 'is_self_hosted=false' | tee -a $GITHUB_OUTPUT + exit 0 + } + + # Generate a unique id that allows the workload job to find the runner + # we are reserving for it (via runner labels), and allows the timeout + # job to find the workload job run (via the job’s friendly name), even + # if there are multiple instances in the workflow call tree. + unique_id=$(uuidgen) + echo "unique_id=$unique_id" | tee -a $GITHUB_OUTPUT + + # Disable self-hosted runners by creating a repository variable named + # NO_SELF_HOSTED_RUNNERS with any non-empty value. + # + if [ -n '${{ inputs.NO_SELF_HOSTED_RUNNERS }}' ]; then + echo 'NO_SELF_HOSTED_RUNNERS is set!' + fall_back_to_github_hosted + fi + + if [ '${{ inputs.force-github-hosted-runner }}' = true ]; then + echo 'inputs.force-github-hosted-runner is set!' + fall_back_to_github_hosted + fi + + for monitor_api_base_url in $(printf \%s\\n \ + https://ci0.servo.org \ + https://ci1.servo.org \ + https://ci2.servo.org \ + | shuf); do + # Use the monitor API to reserve a runner. If we get an object with + # runner details, we succeeded. If we get null, we failed. + take_runner_url=$monitor_api_base_url/profile/$self_hosted_image_name/take\?unique_id=$unique_id\&qualified_repo=${{ github.repository }}\&run_id=${{ github.run_id }} + result=$(mktemp) + echo + echo POST "$take_runner_url" + if curl -sS --fail-with-body --connect-timeout 5 --max-time 30 -X POST "$take_runner_url" \ + -H 'Authorization: Bearer ${{ inputs.monitor-api-token }}' > $result \ + && jq -e . $result > /dev/null; then + echo + echo "selected_runner_label=reserved-for:$unique_id" | tee -a $GITHUB_OUTPUT + echo 'is_self_hosted=true' | tee -a $GITHUB_OUTPUT + exit 0 + fi + done + + cat $result + echo + echo + echo 'No self-hosted runners available!' + fall_back_to_github_hosted diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 74bebff2712..605377b2fcc 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -99,15 +99,32 @@ jobs: # Runs the underlying job (“workload”) on a self-hosted runner if available, # with the help of a `runner-select` job and a `runner-timeout` job. runner-select: - uses: ./.github/workflows/self-hosted-runner-select.yml - secrets: inherit - with: - # Before updating the GH action runner image for the nightly job, ensure - # that the system has a glibc version that is compatible with the one - # used by the wpt.fyi runners. - github-hosted-runner-label: ubuntu-22.04 - self-hosted-image-name: servo-ubuntu2204 - force-github-hosted-runner: ${{ inputs.upload || inputs.force-github-hosted-runner }} + runs-on: ubuntu-22.04 + outputs: + unique-id: ${{ steps.select.outputs.unique-id }} + selected-runner-label: ${{ steps.select.outputs.selected-runner-label }} + is-self-hosted: ${{ steps.select.outputs.is-self-hosted }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + sparse-checkout: '.github' + - name: Runner select + id: select + uses: ./.github/actions/runner-select + with: + monitor-api-token: ${{ secrets.SERVO_CI_MONITOR_API_TOKEN }} + # Before updating the GH action runner image for the nightly job, ensure + # that the system has a glibc version that is compatible with the one + # used by the wpt.fyi runners. + github-hosted-runner-label: ubuntu-22.04 + self-hosted-image-name: servo-ubuntu2204 + # You can disable self-hosted runners globally by creating a repository variable named + # NO_SELF_HOSTED_RUNNERS with any non-empty value. + # + NO_SELF_HOSTED_RUNNERS: ${{ vars.NO_SELF_HOSTED_RUNNERS }} + # Any other boolean conditions that disable self-hosted runners go here. + force-github-hosted-runner: ${{ inputs.upload || inputs.force-github-hosted-runner }} runner-timeout: needs: - runner-select diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 9d12acf8c7d..8dcdce82521 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -85,12 +85,29 @@ jobs: # Runs the underlying job (“workload”) on a self-hosted runner if available, # with the help of a `runner-select` job and a `runner-timeout` job. runner-select: - uses: ./.github/workflows/self-hosted-runner-select.yml - secrets: inherit - with: - github-hosted-runner-label: macos-13 - self-hosted-image-name: servo-macos13 - force-github-hosted-runner: ${{ inputs.force-github-hosted-runner }} + runs-on: ubuntu-22.04 + outputs: + unique-id: ${{ steps.select.outputs.unique-id }} + selected-runner-label: ${{ steps.select.outputs.selected-runner-label }} + is-self-hosted: ${{ steps.select.outputs.is-self-hosted }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + sparse-checkout: '.github' + - name: Runner select + id: select + uses: ./.github/actions/runner-select + with: + monitor-api-token: ${{ secrets.SERVO_CI_MONITOR_API_TOKEN }} + github-hosted-runner-label: macos-13 + self-hosted-image-name: servo-macos13 + # You can disable self-hosted runners globally by creating a repository variable named + # NO_SELF_HOSTED_RUNNERS with any non-empty value. + # + NO_SELF_HOSTED_RUNNERS: ${{ vars.NO_SELF_HOSTED_RUNNERS }} + # Any other boolean conditions that disable self-hosted runners go here. + force-github-hosted-runner: ${{ inputs.force-github-hosted-runner }} runner-timeout: needs: - runner-select diff --git a/.github/workflows/self-hosted-runner-select.yml b/.github/workflows/self-hosted-runner-select.yml deleted file mode 100644 index 1c638f660c7..00000000000 --- a/.github/workflows/self-hosted-runner-select.yml +++ /dev/null @@ -1,100 +0,0 @@ -name: Select Self-hosted Runner -on: - workflow_call: - inputs: - github-hosted-runner-label: - required: true - type: string - self-hosted-image-name: - required: true - type: string - self-hosted-runner-scope: - required: false - type: string - default: /orgs/${{ github.repository_owner }}/actions/runners - force-github-hosted-runner: - required: false - type: boolean - default: false - outputs: - unique-id: - value: ${{ jobs.runner-select.outputs.unique-id }} - selected-runner-label: - value: ${{ jobs.runner-select.outputs.selected-runner-label }} - is-self-hosted: - value: ${{ jobs.runner-select.outputs.is-self-hosted }} - -jobs: - # Selects a self-hosted runner if available, or else a GitHub-hosted runner. - # We generate a unique id for the workload, then ask our monitor API to - # reserve a self-hosted runner for us. - runner-select: - name: Select Runner - runs-on: ubuntu-latest - outputs: - unique-id: ${{ steps.select.outputs.unique_id }} - selected-runner-label: ${{ steps.select.outputs.selected_runner_label }} - is-self-hosted: ${{ steps.select.outputs.is_self_hosted }} - steps: - - name: Select and reserve best available runner - id: select - run: | - github_hosted_runner_label='${{ inputs.github-hosted-runner-label }}' - self_hosted_image_name='${{ inputs.self-hosted-image-name }}' - self_hosted_runner_scope='${{ inputs.self-hosted-runner-scope }}' - - set -euo pipefail - - fall_back_to_github_hosted() { - echo 'Falling back to GitHub-hosted runner' - echo "selected_runner_label=$github_hosted_runner_label" | tee -a $GITHUB_OUTPUT - echo 'is_self_hosted=false' | tee -a $GITHUB_OUTPUT - exit 0 - } - - # Generate a unique id that allows the workload job to find the runner - # we are reserving for it (via runner labels), and allows the timeout - # job to find the workload job run (via the job’s friendly name), even - # if there are multiple instances in the workflow call tree. - unique_id=$(uuidgen) - echo "unique_id=$unique_id" | tee -a $GITHUB_OUTPUT - - # Disable self-hosted runners by creating a repository variable named - # NO_SELF_HOSTED_RUNNERS with any non-empty value. - # - if [ -n '${{ vars.NO_SELF_HOSTED_RUNNERS }}' ]; then - echo 'NO_SELF_HOSTED_RUNNERS is set!' - fall_back_to_github_hosted - fi - - if [ '${{ inputs.force-github-hosted-runner }}' = true ]; then - echo 'inputs.force-github-hosted-runner is set!' - fall_back_to_github_hosted - fi - - for monitor_api_base_url in $(printf \%s\\n \ - https://ci0.servo.org \ - https://ci1.servo.org \ - https://ci2.servo.org \ - | shuf); do - # Use the monitor API to reserve a runner. If we get an object with - # runner details, we succeeded. If we get null, we failed. - take_runner_url=$monitor_api_base_url/profile/$self_hosted_image_name/take\?unique_id=$unique_id\&qualified_repo=${{ github.repository }}\&run_id=${{ github.run_id }} - result=$(mktemp) - echo - echo POST "$take_runner_url" - if curl -sS --fail-with-body --connect-timeout 5 --max-time 30 -X POST "$take_runner_url" \ - -H 'Authorization: Bearer ${{ secrets.SERVO_CI_MONITOR_API_TOKEN }}' > $result \ - && jq -e . $result > /dev/null; then - echo - echo "selected_runner_label=reserved-for:$unique_id" | tee -a $GITHUB_OUTPUT - echo 'is_self_hosted=true' | tee -a $GITHUB_OUTPUT - exit 0 - fi - done - - cat $result - echo - echo - echo 'No self-hosted runners available!' - fall_back_to_github_hosted diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b14a7b3be93..459455859ed 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -76,12 +76,29 @@ jobs: # Runs the underlying job (“workload”) on a self-hosted runner if available, # with the help of a `runner-select` job and a `runner-timeout` job. runner-select: - uses: ./.github/workflows/self-hosted-runner-select.yml - secrets: inherit - with: - github-hosted-runner-label: windows-2022 - self-hosted-image-name: servo-windows10 - force-github-hosted-runner: ${{ inputs.force-github-hosted-runner }} + runs-on: ubuntu-22.04 + outputs: + unique-id: ${{ steps.select.outputs.unique-id }} + selected-runner-label: ${{ steps.select.outputs.selected-runner-label }} + is-self-hosted: ${{ steps.select.outputs.is-self-hosted }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + sparse-checkout: '.github' + - name: Runner select + id: select + uses: ./.github/actions/runner-select + with: + monitor-api-token: ${{ secrets.SERVO_CI_MONITOR_API_TOKEN }} + github-hosted-runner-label: windows-2022 + self-hosted-image-name: servo-windows10 + # You can disable self-hosted runners globally by creating a repository variable named + # NO_SELF_HOSTED_RUNNERS with any non-empty value. + # + NO_SELF_HOSTED_RUNNERS: ${{ vars.NO_SELF_HOSTED_RUNNERS }} + # Any other boolean conditions that disable self-hosted runners go here. + force-github-hosted-runner: ${{ inputs.force-github-hosted-runner }} runner-timeout: needs: - runner-select