Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
87a4de2
Update myoung34/github-runner Docker tag to v2.336.0
renovate[bot] Jul 21, 2026
ba347e8
Merge pull request #74 from compscidr/renovate/myoung34-github-runner…
compscidr Aug 4, 2026
d670374
Add design spec for PR build testing + auto-merge
compscidr Aug 5, 2026
946a0e2
Add implementation plan for PR build testing + auto-merge
compscidr Aug 5, 2026
84f9d06
Add PR build workflow with 'All builds passed' gate check
compscidr Aug 5, 2026
7397bfe
Use AUTOMERGE_PAT in SDK update workflow and enable auto-merge
compscidr Aug 5, 2026
38d4b1d
Clarify in spec why build.yml only extracts VERSION
compscidr Aug 5, 2026
73c7042
Harden build.yml with read-only permissions; fix spec concurrency wor…
compscidr Aug 5, 2026
1a364c0
Shrink SDK workflow token permissions; guard against expired PAT
compscidr Aug 5, 2026
53215a5
Merge pull request #76 from compscidr/feat/pr-build-automerge
compscidr Aug 5, 2026
8c6a75f
Update actions/checkout action to v7 (#75)
renovate[bot] Aug 5, 2026
a47de56
fix: validate persisted runner registration before reusing it
compscidr Aug 10, 2026
577c054
fix: bound the validation curl so network stalls can't block startup
compscidr Aug 10, 2026
8fa85f6
Merge pull request #77 from compscidr/preflight-validate-persisted-re…
compscidr Aug 10, 2026
2ea3834
fix: wipe migrated config files too when clearing dead registrations
compscidr Aug 11, 2026
e0494f7
Merge pull request #78 from compscidr/fix/preflight-wipe-migrated-config
compscidr Aug 11, 2026
bf4e5f4
chore: merge upstream compscidr/main and fix Renovate on this fork
azlekov Aug 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Build container (PR)
on:
pull_request:

permissions:
contents: read

concurrency:
group: build-pr-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
matrix:
name: Generate build matrix
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.generate.outputs.matrix }}
steps:
- uses: actions/checkout@v7

- name: Generate matrix from matrix.json
id: generate
run: |
set -euo pipefail
# PR builds deliberately cover less than deploy.yml: the full
# java x node x arch product is ~28 images, which is far more than a
# dependency bump needs. Instead we cover every JDK against the
# default Node, every Node against the default JDK, and add a single
# arm64 build of the default pair so arch-specific breakage (e.g. the
# TARGETARCH-derived JAVA_HOME) is still caught before merge.
MATRIX=$(jq -c '
.default_jdk as $dj | .default_node as $dn
| ( [ .java_versions[] | {java_version: ., node_version: $dn} ]
+ [ .node_versions[] | {java_version: $dj, node_version: .} ]
| unique ) as $combos
| { include:
( [ $combos[] | . + {platform: "linux/amd64", arch: "amd64", runner: "ubuntu-latest"} ]
+ [ {java_version: $dj, node_version: $dn,
platform: "linux/arm64", arch: "arm64", runner: "ubuntu-24.04-arm"} ] ) }' matrix.json)
echo "matrix=${MATRIX}" >> $GITHUB_OUTPUT

build:
name: Build ${{ matrix.arch }} JDK ${{ matrix.java_version }} Node ${{ matrix.node_version }}
needs: matrix
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.matrix.outputs.matrix) }}
steps:
- uses: actions/checkout@v7

- name: Extract versions from Dockerfile
id: versions
run: |
set -euo pipefail
# Only VERSION is extracted here: JDK and Node come from the matrix,
# and the SDK args are consumed straight from the Dockerfile defaults
# since nothing in a PR build needs them for tagging.
RUNNER_TAG=$(grep -m1 '^ARG VERSION=' Dockerfile | cut -d= -f2)

if [ -z "$RUNNER_TAG" ]; then
echo "::error::Failed to extract versions from Dockerfile"
exit 1
fi

echo "runner_tag=${RUNNER_TAG}" >> $GITHUB_OUTPUT

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4

- name: Build (no push)
uses: docker/build-push-action@v7
with:
context: .
platforms: ${{ matrix.platform }}
push: false
build-args: |
VERSION=${{ steps.versions.outputs.runner_tag }}
JAVA_VERSION=${{ matrix.java_version }}
NODE_VERSION=${{ matrix.node_version }}

gate:
name: All builds passed
needs: [matrix, build]
if: always()
runs-on: ubuntu-latest
steps:
- name: Check that all required jobs succeeded
run: |
if [ "${{ needs.matrix.result }}" != "success" ] || [ "${{ needs.build.result }}" != "success" ]; then
echo "::error::A required job failed or was cancelled (matrix: ${{ needs.matrix.result }}, build: ${{ needs.build.result }})"
exit 1
fi
echo "All builds succeeded"
6 changes: 6 additions & 0 deletions .github/workflows/check-sdk-updates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,9 @@ jobs:
else
echo "PR #${EXISTING} already exists, updated with force push"
fi

# No auto-merge on purpose: this PR is opened with GITHUB_TOKEN, and GitHub
# does not trigger workflows for PRs created that way. build.yml never runs,
# so the "All builds passed" check never reports and an auto-merge request
# would hang forever. Merge manually (which runs the builds), or add a
# fine-grained PAT and restore upstream's auto-merge step.
15 changes: 13 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG VERSION=2.335.1-ubuntu-noble
ARG VERSION=2.336.0-ubuntu-noble
ARG JAVA_VERSION=21
ARG NODE_VERSION=24
ARG COMPILE_SDK=36.1
Expand Down Expand Up @@ -83,6 +83,17 @@ COPY cleanup.sh /usr/local/bin/cleanup.sh
RUN chmod +x /usr/local/bin/cleanup.sh
ENV ACTIONS_RUNNER_HOOK_JOB_COMPLETED=/usr/local/bin/cleanup.sh

# NB: there is no CMD so it will work the same as the base image. See the
# Preflight entrypoint: validates the persisted runner registration against
# GitHub before handing off to the upstream entrypoint. Upstream reuses
# CONFIGURED_ACTIONS_RUNNER_FILES_DIR credentials unconditionally, which
# crash-loops the container when the runner was removed server-side; the
# preflight wipes provably-dead registrations so a fresh ACCESS_TOKEN
# registration happens instead. See preflight-entrypoint.sh for details.
COPY preflight-entrypoint.sh /usr/local/bin/preflight-entrypoint.sh
RUN chmod +x /usr/local/bin/preflight-entrypoint.sh
ENTRYPOINT ["/usr/local/bin/preflight-entrypoint.sh"]
# Declaring ENTRYPOINT resets the CMD inherited from the base image, so the
# base image's CMD is restated here. See
# https://github.com/myoung34/docker-github-actions-runner#environment-variables
# for how to use the image
CMD ["./bin/Runner.Listener", "run", "--startuptype", "service"]
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,41 @@ Versions are kept up to date automatically:
| Component | Mechanism |
|-----------|-----------|
| Base runner image | [Renovate](https://github.com/renovatebot/renovate) tracks `myoung34/github-runner` Docker tags |
| GitHub Actions versions | Renovate (`config:base`) |
| GitHub Actions versions | Renovate (`config:recommended`) |
| New JDK major versions | Weekly workflow queries the [Adoptium API](https://api.adoptium.net) and opens a PR |
| New Node LTS majors | Weekly workflow queries the [Node Release schedule](https://github.com/nodejs/Release/blob/main/schedule.json) and opens a PR |
| Android SDK platform level | Weekly workflow queries `sdkmanager` and opens a PR |
| Android build-tools | Weekly workflow queries `sdkmanager` and opens a PR |
| Android NDK | Weekly workflow queries `sdkmanager` and opens a PR |

Every pull request is validated by [`build.yml`](.github/workflows/build.yml),
which builds each JDK against the default Node, each Node against the default
JDK, and one `linux/arm64` build of the default pair. Its `All builds passed`
job is the single check to require in branch protection.

Because this repository is a fork, `renovate.json` sets
`"forkProcessing": "enabled"`. The Mend-hosted Renovate App leaves fork
processing disabled for org-wide ("All repositories") installations, so without
that flag Renovate silently skips the repository and the base image is never
bumped.

The weekly SDK/JDK/Node workflow opens its PR with `GITHUB_TOKEN`, which means
GitHub does not run `build.yml` on it. Upstream solves this with an
`AUTOMERGE_PAT` secret and `gh pr merge --auto`; this fork does not, so those
PRs are merged manually and the builds run on merge.

## Runner registration preflight

The image wraps the base entrypoint with
[`preflight-entrypoint.sh`](preflight-entrypoint.sh). When
`CONFIGURED_ACTIONS_RUNNER_FILES_DIR` is set, the upstream entrypoint reuses
persisted registration credentials without checking them, so a runner that was
removed on GitHub's side crash-loops forever instead of re-registering. The
preflight asks GitHub whether the persisted runner still exists and wipes the
stored registration only on a definitive `404`, letting a fresh `ACCESS_TOKEN`
registration take over. Timeouts, missing variables, and unparseable state all
leave the configuration untouched.

## Building locally

```
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:
context: .
# if you don't want to build and instead just use the pre-built image
# comment out the build block above and uncomment below
# image: compscidr/github-runner-android
# image: ghcr.io/dodi-smart/github-runner
container_name: gha
env_file:
- .env
Expand Down
Loading
Loading