Skip to content

Commit 56b2a31

Browse files
authored
Merge pull request #100 from RandomDevelopment/feature/scheduled-runner-updater
feat(ci): automate GitHub Actions runner updates
2 parents b7a2b16 + 63eec3d commit 56b2a31

5 files changed

Lines changed: 767 additions & 1 deletion

File tree

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
name: Update GitHub Actions runner release
2+
3+
on:
4+
schedule:
5+
- cron: "23 7 * * 1"
6+
workflow_dispatch:
7+
8+
permissions:
9+
actions: write
10+
contents: write
11+
pull-requests: write
12+
13+
concurrency:
14+
group: update-actions-runner
15+
cancel-in-progress: false
16+
17+
env:
18+
BASE_BRANCH: ${{ github.event.repository.default_branch }}
19+
UPDATE_BRANCH: automation/update-actions-runner
20+
21+
jobs:
22+
update:
23+
name: Check for a stable runner release
24+
runs-on: ubuntu-latest
25+
timeout-minutes: 20
26+
steps:
27+
- name: Check out the default branch
28+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
29+
with:
30+
ref: ${{ github.event.repository.default_branch }}
31+
fetch-depth: 0
32+
persist-credentials: false
33+
34+
- name: Test updater
35+
run: python3 scripts/test_update_actions_runner.py
36+
37+
- name: Update runner pins
38+
id: update
39+
run: python3 scripts/update_actions_runner.py --github-output "$GITHUB_OUTPUT"
40+
41+
- name: Validate generated changes
42+
if: steps.update.outputs.changed == 'true'
43+
run: |
44+
set -Eeuo pipefail
45+
git diff --check
46+
changed_file_list=$(git diff --name-only)
47+
mapfile -t changed_files <<<"$changed_file_list"
48+
expected_files=(deploy/compose.yaml runner/Dockerfile)
49+
if [[ "${changed_files[*]}" != "${expected_files[*]}" ]]; then
50+
printf 'updater changed unexpected files: %s\n' "${changed_files[*]}" >&2
51+
exit 1
52+
fi
53+
python3 scripts/test_update_actions_runner.py
54+
55+
- name: Commit and push deterministic update branch
56+
if: steps.update.outputs.changed == 'true'
57+
id: push
58+
env:
59+
GH_TOKEN: ${{ github.token }}
60+
VERSION: ${{ steps.update.outputs.version }}
61+
run: |
62+
set -Eeuo pipefail
63+
gh auth setup-git
64+
git config user.name 'github-actions[bot]'
65+
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
66+
git add -- deploy/compose.yaml runner/Dockerfile
67+
git diff --cached --check
68+
git commit -m "chore(runner): update GitHub Actions runner to ${VERSION}"
69+
70+
remote_sha=
71+
if git fetch --no-tags origin "refs/heads/${UPDATE_BRANCH}:refs/remotes/origin/${UPDATE_BRANCH}"; then
72+
remote_sha=$(git rev-parse "refs/remotes/origin/${UPDATE_BRANCH}")
73+
fi
74+
if [[ -n "$remote_sha" ]] && git diff --quiet "$remote_sha" HEAD; then
75+
printf 'pushed=false\n' >>"$GITHUB_OUTPUT"
76+
exit 0
77+
fi
78+
git push \
79+
--force-with-lease="refs/heads/${UPDATE_BRANCH}:${remote_sha}" \
80+
origin "HEAD:refs/heads/${UPDATE_BRANCH}"
81+
printf 'pushed=true\n' >>"$GITHUB_OUTPUT"
82+
83+
- name: Create or update pull request
84+
if: steps.update.outputs.changed == 'true'
85+
id: pull_request
86+
env:
87+
GH_TOKEN: ${{ github.token }}
88+
VERSION: ${{ steps.update.outputs.version }}
89+
run: |
90+
set -Eeuo pipefail
91+
body_file=$(mktemp)
92+
trap 'rm -f "$body_file"' EXIT
93+
printf '%s\n' \
94+
"Updates all GitHub Actions runner pins to v${VERSION}." \
95+
'' \
96+
'The updater selected the latest stable actions/runner release, verified both official Linux archives, and calculated their SHA-256 checksums.' \
97+
'' \
98+
'Generated by .github/workflows/update-actions-runner.yml.' >"$body_file"
99+
100+
pull_request_numbers=$(
101+
gh pr list \
102+
--repo "$GITHUB_REPOSITORY" \
103+
--state open \
104+
--base "$BASE_BRANCH" \
105+
--head "$UPDATE_BRANCH" \
106+
--json number \
107+
--jq '.[].number'
108+
)
109+
pull_requests=()
110+
if [[ -n "$pull_request_numbers" ]]; then
111+
mapfile -t pull_requests <<<"$pull_request_numbers"
112+
fi
113+
if (( ${#pull_requests[@]} > 1 )); then
114+
echo "more than one open updater pull request exists" >&2
115+
exit 1
116+
fi
117+
if (( ${#pull_requests[@]} == 0 )); then
118+
gh pr create \
119+
--repo "$GITHUB_REPOSITORY" \
120+
--base "$BASE_BRANCH" \
121+
--head "$UPDATE_BRANCH" \
122+
--title "chore(runner): update GitHub Actions runner to ${VERSION}" \
123+
--body-file "$body_file"
124+
printf 'created=true\n' >>"$GITHUB_OUTPUT"
125+
else
126+
gh pr edit "${pull_requests[0]}" \
127+
--repo "$GITHUB_REPOSITORY" \
128+
--title "chore(runner): update GitHub Actions runner to ${VERSION}" \
129+
--body-file "$body_file"
130+
printf 'created=false\n' >>"$GITHUB_OUTPUT"
131+
fi
132+
133+
- name: Dispatch validation for the update head
134+
if: >-
135+
steps.update.outputs.changed == 'true' &&
136+
(steps.push.outputs.pushed == 'true' || steps.pull_request.outputs.created == 'true')
137+
env:
138+
GH_TOKEN: ${{ github.token }}
139+
run: gh workflow run validate.yml --repo "$GITHUB_REPOSITORY" --ref "$UPDATE_BRANCH"

‎docs/HOST-MAINTENANCE.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,8 @@ The manual cleanup command is intentionally a dry-run. Enable the applying servi
6161

6262
Dependabot proposes updates for GitHub Actions, Go modules, and both Dockerfiles. Those pull requests must pass inert validation and be reviewed before merge. This preserves unattended host security patching without silently changing the runner control plane.
6363

64+
The `Update GitHub Actions runner release` workflow checks the official `actions/runner` releases each Monday. It ignores drafts and prereleases, validates the stable tag and exact Linux x64 and arm64 assets, downloads both archives, and calculates their SHA-256 checksums. When an update exists, it updates the three pins in `runner/Dockerfile` and the `CI_FLEET_RUNNER_VERSION` default in `deploy/compose.yaml` on the machine-managed `automation/update-actions-runner` branch. It creates or refreshes one pull request for normal review and validation. If all four pins are current, it makes no branch or pull-request change.
65+
66+
Run the same check on demand from **Actions → Update GitHub Actions runner release → Run workflow**. The workflow only proposes a repository change. It does not build or deploy a host update.
67+
6468
Controller and runner engine updates are also pinned. A merged private configuration change is applied with `install-worker-controller.sh --upgrade`; the host never follows a moving engine or configuration branch automatically. See [Git-authored controller desired state](DESIRED-STATE.md).

0 commit comments

Comments
 (0)