servo/.github/workflows/windows.yml
Delan Azabani 658df79d88
CI: use self-hosted runners for Windows build jobs (#33081)
* CI: use self-hosted Windows runners in main workflow

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Fix a couple of robustness issues by generating a unique build id

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Work around needs-context expressions being busted in concurrency

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* CI: use self-hosted Windows runners in try and try-label workflows

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Rename windows workflow back for simplicity

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Clarify why the copy resources step is for GitHub-hosted jobs only

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Fix cancelled status problem by dispatching instead of calling

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Tweak retry strategy to avoid hitting REST API rate limits

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Update dispatch-workflow.yml accordingly

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Rework to use simpler approach with runner labels

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Use org-scoped self-hosted runners

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Don’t run runner-timeout job when GitHub-hosted runner is selected

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Downgrade to Python 3.10

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Avoid failing when RUNNER_API_TOKEN is missing (such as in forks)

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Fix undefined needs output when RUNNER_API_TOKEN is missing

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Reset working tree, in case it was dirty in the runner image

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Clearer runner assignment timeout jobs that fail and offer help

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Fix some other sources of incremental build breakage (but not PATH)

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Log reasons why we fall back to GitHub-hosted runners

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Allow self-hosted runners to be disabled via repository variable

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Always install crown, even on self-hosted runners

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Rename incremental build debugging step

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Clean up job friendly names

Signed-off-by: Delan Azabani <dazabani@igalia.com>

* Reduce fetch depth, now that this job no longer lints

Signed-off-by: Delan Azabani <dazabani@igalia.com>

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
2024-08-27 11:04:42 +00:00

277 lines
12 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: Windows
on:
workflow_call:
inputs:
profile:
required: false
default: "release"
type: string
unit-tests:
required: false
default: false
type: boolean
upload:
required: false
default: false
type: boolean
github-release-id:
required: false
type: string
workflow_dispatch:
inputs:
profile:
required: false
default: "release"
options: ["release", "debug", "production"]
type: choice
unit-tests:
required: false
default: false
type: boolean
upload:
required: false
default: false
type: boolean
env:
RUST_BACKTRACE: 1
SHELL: /bin/bash
CCACHE: "ccache"
CARGO_TARGET_DIR: C:\\a\\servo\\servo\\target
# clang_sys will search msys path before Program Files\LLVM
# so we need to override this behaviour until we update clang-sys
# https://github.com/KyleMayes/clang-sys/issues/150
LIBCLANG_PATH: C:\Program Files\LLVM\bin
RUSTUP_WINDOWS_PATH_ADD_BIN: 1
jobs:
# Automatic runner selection for job: build
# 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.
# Selects a self-hosted runner if available, or else a GitHub-hosted runner.
# We generate a unique id for the workload, find an idle self-hosted runner
# with the given `image:` label that wasnt already reserved by another
# `runner-select` job run, and reserve it with a `reserved-for:<id>` label.
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 }}
concurrency:
group: servo-reserve-self-hosted-runner
cancel-in-progress: false
permissions: write-all
steps:
- name: Select and reserve best available runner
id: select
# Set the variables below to your desired runner images, runner scope
# (org or repo), and a token with permission to write to that scope.
run: |
github_hosted_runner_label=windows-2022
self_hosted_image_label=self-hosted-image:windows10
self_hosted_runner_scope=/orgs/${{ github.repository_owner }}/actions/runners
export GH_TOKEN=${{ secrets.RUNNER_API_TOKEN }}
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 jobs 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
# RUNNER_API_TOKEN secret will be unavailable in forks.
if [ -z "$GH_TOKEN" ]; then
echo 'RUNNER_API_TOKEN not set!'
fall_back_to_github_hosted
fi
runners=$(mktemp)
gh api "$self_hosted_runner_scope" > $runners
# Find a runner that is online, not busy, and not already reserved for
# any job (label prefix “reserved-for:”).
runner_id=$(mktemp)
if ! < $runners > $runner_id jq \
--arg self_hosted_image_label "$self_hosted_image_label" -e '
.runners
| map(select(.status == "online" and .busy == false))
| map(select([.labels[].name] | index($self_hosted_image_label) | not | not))
| map(select([.labels[].name] | map(startswith("reserved-for:")) | index(true) | not))
| first | .id'; then
echo 'No self-hosted runners available!'
fall_back_to_github_hosted
fi
# Reserve that runner by adding a label containing the unique id.
# Job concurrency ensures that runners never get assigned twice.
reserved_since=$(date +\%s)
gh api "$self_hosted_runner_scope/$(cat $runner_id)/labels" \
-f "labels[]=reserved-for:$unique_id" \
-f "labels[]=reserved-since:$reserved_since" \
--method POST --silent
echo "selected_runner_label=reserved-for:$unique_id" | tee -a $GITHUB_OUTPUT
echo 'is_self_hosted=true' | tee -a $GITHUB_OUTPUT
# In the unlikely event a self-hosted runner was selected and reserved but it
# goes down before the workload starts, cancel the workflow run.
runner-timeout:
needs:
- runner-select
if: ${{ fromJSON(needs.runner-select.outputs.is-self-hosted) }}
name: Detect Runner Timeout
runs-on: ubuntu-latest
steps:
- name: Wait a bit
run: sleep 30
- name: Cancel if workload job is still queued
run: |
run_url=/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}
export GH_TOKEN=${{ secrets.GITHUB_TOKEN }}
if [ "$(gh api "$run_url/jobs" \
| jq -er --arg id '${{ needs.runner-select.outputs.unique-id }}' \
'.jobs[] | select(.name | contains("[" + $id + "]")) | .status'
)" = queued ]; then
echo 'Timeout waiting for runner assignment!'
echo 'Hint: does this repo have permission to access the runner group?'
echo 'Hint: https://github.com/organizations/servo/settings/actions/runner-groups'
echo
echo 'Cancelling workflow run'
gh api "$run_url/cancel" --method POST
exit 1
fi
build:
needs:
- runner-select
name: Windows Build [${{ needs.runner-select.outputs.unique-id }}]
runs-on: ${{ needs.runner-select.outputs.selected-runner-label }}
steps:
- if: ${{ ! fromJSON(needs.runner-select.outputs.is-self-hosted) && github.event_name != 'pull_request_target' }}
uses: actions/checkout@v4
- if: ${{ ! fromJSON(needs.runner-select.outputs.is-self-hosted) && github.event_name == 'pull_request_target' }}
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 1
# Faster checkout for self-hosted runner that uses prebaked repo.
- if: ${{ fromJSON(needs.runner-select.outputs.is-self-hosted) && github.event_name != 'pull_request_target' }}
run: git fetch --depth=1 origin $env:GITHUB_SHA
- if: ${{ fromJSON(needs.runner-select.outputs.is-self-hosted) && github.event_name == 'pull_request_target' }}
run: git fetch --depth=1 origin refs/pull/${{ github.event_number }}/head
- if: ${{ fromJSON(needs.runner-select.outputs.is-self-hosted) }}
# Same as `git switch --detach FETCH_HEAD`, but fixes up dirty working
# trees, in case the runner image was baked with a dirty working tree.
run: |
git switch --detach
git reset --hard FETCH_HEAD
- name: ccache
# FIXME: “Error: Restoring cache failed: Error: Unable to locate executable file: sh.”
if: ${{ ! fromJSON(needs.runner-select.outputs.is-self-hosted) }}
uses: hendrikmuhs/ccache-action@v1.2
# Install missing tools in a GitHub-hosted runner.
# Workaround for https://github.com/actions/runner-images/issues/10001:
- name: Upgrade llvm
if: ${{ ! fromJSON(needs.runner-select.outputs.is-self-hosted) }}
run: choco upgrade llvm
- name: Install wixtoolset
if: ${{ ! fromJSON(needs.runner-select.outputs.is-self-hosted) }}
run: |
choco install wixtoolset
echo "C:\\Program Files (x86)\\WiX Toolset v3.11\\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- uses: actions/setup-python@v5
if: ${{ ! fromJSON(needs.runner-select.outputs.is-self-hosted) }}
with:
python-version: "3.10"
- name: Bootstrap
if: ${{ ! fromJSON(needs.runner-select.outputs.is-self-hosted) }}
run: |
python -m pip install --upgrade pip
python mach fetch
python mach bootstrap-gstreamer
# For some reason WiX isn't currently on the GitHub runner path. This is a
# temporary workaround until that is fixed.
- name: Add WiX to Path
if: ${{ ! fromJSON(needs.runner-select.outputs.is-self-hosted) }}
run: |
"$env:WIX\bin" >> $env:GITHUB_PATH
# Always install crown, even on self-hosted runners, because it is tightly
# coupled to the rustc version, and we may have the wrong version if the
# commit we are building uses a different rustc version.
- name: Install crown
run: cargo install --path support/crown --force
- name: Debug logging for incremental builds
if: ${{ fromJSON(needs.runner-select.outputs.is-self-hosted) }}
run: |
cat C:\init\incremental_build_debug.txt
echo "`$env:LIBCLANG_PATH now = $env:LIBCLANG_PATH"
echo "`$env:PATH now = $env:PATH"
- name: Build (${{ inputs.profile }})
run: |
python mach build --use-crown --locked --${{ inputs.profile }}
cp C:\a\servo\servo\target\cargo-timings C:\a\servo\servo\target\cargo-timings-windows -Recurse
- name: Copy resources
if: ${{ ! fromJSON(needs.runner-select.outputs.is-self-hosted) }}
# GitHub-hosted runners check out the repo on D: drive.
run: cp D:\a\servo\servo\resources C:\a\servo\servo -Recurse
- name: Smoketest
run: python mach smoketest --${{ inputs.profile }}
- name: Unit tests
if: ${{ inputs.unit-tests }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3 # https://github.com/servo/servo/issues/30683
command: python mach test-unit --${{ inputs.profile }} -- -- --test-threads=1
- name: Archive build timing
uses: actions/upload-artifact@v4
with:
name: cargo-timings-windows
# Using a wildcard here ensures that the archive includes the path.
path: C:\\a\\servo\\servo\\target\\cargo-timings-*
- name: Build mach package
run: python mach package --${{ inputs.profile }}
- name: Upload artifact for mach package
uses: actions/upload-artifact@v4
with:
name: win
# These files are available
# MSI Installer: C:\a\servo\servo\target\${{ inputs.profile }}\msi\Installer.msi
# Bundle: C:\a\servo\servo\target\${{ inputs.profile }}\msi\Servo.exe
# Zip: C:\a\servo\servo\target\${{ inputs.profile }}\msi\Servo.zip
path: C:\\a\\servo\\servo\\target\\${{ inputs.profile }}\\msi\\Servo.exe
- name: Upload nightly
if: ${{ inputs.upload }}
run: |
python mach upload-nightly windows-msvc --secret-from-environment `
--github-release-id ${{ inputs.github-release-id }}
env:
S3_UPLOAD_CREDENTIALS: ${{ secrets.S3_UPLOAD_CREDENTIALS }}
NIGHTLY_REPO_TOKEN: ${{ secrets.NIGHTLY_REPO_TOKEN }}
NIGHTLY_REPO: ${{ github.repository_owner }}/servo-nightly-builds