| 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. |
| # <https://github.com/servo/servo/settings/variables/actions> |
| 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 |