From 1eebb767a31f1d1e9847dc58bb68000c9a51803e Mon Sep 17 00:00:00 2001 From: Hendrik Brombeer <20863417+hbrombeer@users.noreply.github.com> Date: Sat, 17 Oct 2026 18:34:27 +0200 Subject: [PATCH 1/3] ci: use RunsOn Magic Cache on runs-on runners When the runner input is a RunsOn label (runs-on=...), enable the Magic Cache via runs-on/action and turn the Actions cache back on: setup-java and setup-gradle caching, and the Buildx type=gha layer cache. hbr1 ARC and GitHub-hosted runners are unchanged. --- .github/workflows/docker-gradle-build-push.yml | 18 ++++++++++++++---- .github/workflows/gradle-ci.yml | 11 +++++++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker-gradle-build-push.yml b/.github/workflows/docker-gradle-build-push.yml index 8e59f1d..882c829 100644 --- a/.github/workflows/docker-gradle-build-push.yml +++ b/.github/workflows/docker-gradle-build-push.yml @@ -55,7 +55,15 @@ jobs: && 60 || 30 }} env: TAG_TYPE: ${{ github.ref_type == 'tag' && 'release' || 'edge' }} + RUNS_ON: ${{ startsWith(inputs.runner, 'runs-on=') }} steps: + # RunsOn (AWS) runners: routes the Actions cache, and with it the Buildx + # `type=gha` layer cache, to RunsOn's S3-backed Magic Cache. Needs + # `extras=s3-cache` in the runner label. + - name: Enable RunsOn Magic Cache + if: env.RUNS_ON == 'true' + uses: runs-on/action@v2 + - name: Checkout uses: actions/checkout@v7 @@ -69,8 +77,9 @@ jobs: # median in service-moderation, a third of the job, to read one # version string. Hosted runners only: from the self-hosted ARC runners # the GitHub Actions cache stalled at 0 bytes for minutes and crawled at - # ~1 MB/s (2026-09-30), slower than resolving from scratch. - cache: ${{ runner.environment == 'github-hosted' && 'gradle' || '' }} + # ~1 MB/s (2026-09-30), slower than resolving from scratch. RunsOn runners + # use it through the Magic Cache. + cache: ${{ (runner.environment == 'github-hosted' || env.RUNS_ON == 'true') && 'gradle' || '' }} - name: Configure GitHub Packages credentials run: | @@ -108,7 +117,8 @@ jobs: echo "DOCKER_CONFIG=${docker_config}" >> "$GITHUB_ENV" # Where BuildKit keeps its layer cache, decided on the runner: - # - GitHub-hosted: the repository-scoped GHA cache. + # - GitHub-hosted, or RunsOn with its Magic Cache: the repository-scoped + # GHA cache. # - self-hosted with BUILDKIT_CACHE_REGISTRY: that registry. The hbr1 ARC # runners advertise one next to them (plain HTTP, cluster-internal), # the same way they advertise GRADLE_BUILD_CACHE_URL. The GHA cache is @@ -123,7 +133,7 @@ jobs: set -euo pipefail repo="${GITHUB_REPOSITORY#*/}" from=""; to=""; config="" - if [ "${RUNNER_ENVIRONMENT:-}" = "github-hosted" ]; then + if [ "${RUNNER_ENVIRONMENT:-}" = "github-hosted" ] || [ "${RUNS_ON}" = "true" ]; then from="type=gha,scope=${GITHUB_REPOSITORY}-amd64" to="type=gha,scope=${GITHUB_REPOSITORY}-amd64,mode=max" elif [ -n "${BUILDKIT_CACHE_REGISTRY:-}" ]; then diff --git a/.github/workflows/gradle-ci.yml b/.github/workflows/gradle-ci.yml index b4fe9b3..17b62c4 100644 --- a/.github/workflows/gradle-ci.yml +++ b/.github/workflows/gradle-ci.yml @@ -49,6 +49,12 @@ jobs: || 'ubuntu-24.04' }} timeout-minutes: 30 steps: + # RunsOn (AWS) runners: routes the Actions cache to RunsOn's S3-backed + # Magic Cache. Needs `extras=s3-cache` in the runner label. + - name: Enable RunsOn Magic Cache + if: startsWith(inputs.runner, 'runs-on=') + uses: runs-on/action@v2 + - name: Check out uses: actions/checkout@v7 @@ -61,12 +67,13 @@ jobs: # runners on hbr1 it stalled at 0 bytes for minutes and crawled at ~1 MB/s # (2026-09-30), slower than resolving the dependencies from scratch. Hosted # runners keep it; self-hosted ones use the Gradle build cache node. - cache: ${{ runner.environment == 'github-hosted' && 'gradle' || '' }} + # RunsOn runners use the Actions cache through the Magic Cache. + cache: ${{ (runner.environment == 'github-hosted' || startsWith(inputs.runner, 'runs-on=')) && 'gradle' || '' }} - name: Set up Gradle uses: gradle/actions/setup-gradle@v6 with: - cache-disabled: ${{ runner.environment != 'github-hosted' }} + cache-disabled: ${{ runner.environment != 'github-hosted' && !startsWith(inputs.runner, 'runs-on=') }} - name: Use the Maven mirror uses: groundsgg/.github/.github/actions/maven-mirror@main From 8e87c3f40d76ce2d81d430596ca56024f59e8665 Mon Sep 17 00:00:00 2001 From: Hendrik Brombeer <20863417+hbrombeer@users.noreply.github.com> Date: Sat, 17 Oct 2026 19:06:12 +0200 Subject: [PATCH 2/3] ci: run private repos on RunsOn instead of hbr1 Private repositories now default to RunsOn spot instances in our AWS account instead of the grounds-runners ARC pool on hbr1: - builds (gradle-ci, gradle-publish, docker-gradle-build-push, preview build): m8a.xlarge, 4 vCPU / 16 GB, with Magic Cache - API-only jobs (release-please, label-sync, preview slashcmd, mint, evict): m8a.large, 2 vCPU / 8 GB The Magic Cache is now detected from the runner name, so it also applies to callers that pass their own RunsOn label. Public repos stay on GitHub-hosted runners. --- .../workflows/docker-gradle-build-push.yml | 14 ++++---- .github/workflows/gradle-ci.yml | 28 ++++++++-------- .github/workflows/gradle-publish.yml | 31 ++++++++++------- .github/workflows/label-sync.yml | 14 ++++---- .../platform-test-preview-slashcmd.yml | 12 +++---- .github/workflows/platform-test-preview.yml | 33 +++++++++++-------- .github/workflows/release-please.yml | 14 ++++---- 7 files changed, 79 insertions(+), 67 deletions(-) diff --git a/.github/workflows/docker-gradle-build-push.yml b/.github/workflows/docker-gradle-build-push.yml index 882c829..c3c7036 100644 --- a/.github/workflows/docker-gradle-build-push.yml +++ b/.github/workflows/docker-gradle-build-push.yml @@ -36,14 +36,15 @@ jobs: # happened to grounds-forge 4.3.0 in its migrate init container. Building # an amd64 image on an arm64 runner would mean QEMU for the whole build. # Public fork PRs remain on GitHub-hosted runners; private repositories - # use the shared self-hosted pool `grounds-runners` (org-level ARC; the - # org's Default runner group refuses public repositories). Missing + # use RunsOn, an ephemeral m8a.xlarge spot instance in our AWS account + # (eu-central-1). Missing # repository payloads safely fall back to GitHub-hosted. runs-on: >- ${{ inputs.runner != '' && inputs.runner || (github.event.repository.visibility == 'public' && 'ubuntu-24.04') - || (github.event.repository.name && 'grounds-runners') + || (github.event.repository.name + && format('runs-on={0}/cpu=4/ram=16/family=m8a/image=ubuntu24-full-x64/extras=s3-cache', github.run_id)) || 'ubuntu-24.04' }} # Cold ARC builds can exceed 30m downloading builder/runtime base images # even after Gradle succeeds. Keep the default repo-ARC path bounded at @@ -55,13 +56,12 @@ jobs: && 60 || 30 }} env: TAG_TYPE: ${{ github.ref_type == 'tag' && 'release' || 'edge' }} - RUNS_ON: ${{ startsWith(inputs.runner, 'runs-on=') }} steps: # RunsOn (AWS) runners: routes the Actions cache, and with it the Buildx # `type=gha` layer cache, to RunsOn's S3-backed Magic Cache. Needs # `extras=s3-cache` in the runner label. - name: Enable RunsOn Magic Cache - if: env.RUNS_ON == 'true' + if: startsWith(runner.name, 'runs-on--') uses: runs-on/action@v2 - name: Checkout @@ -79,7 +79,7 @@ jobs: # the GitHub Actions cache stalled at 0 bytes for minutes and crawled at # ~1 MB/s (2026-09-30), slower than resolving from scratch. RunsOn runners # use it through the Magic Cache. - cache: ${{ (runner.environment == 'github-hosted' || env.RUNS_ON == 'true') && 'gradle' || '' }} + cache: ${{ (runner.environment == 'github-hosted' || startsWith(runner.name, 'runs-on--')) && 'gradle' || '' }} - name: Configure GitHub Packages credentials run: | @@ -133,7 +133,7 @@ jobs: set -euo pipefail repo="${GITHUB_REPOSITORY#*/}" from=""; to=""; config="" - if [ "${RUNNER_ENVIRONMENT:-}" = "github-hosted" ] || [ "${RUNS_ON}" = "true" ]; then + if [ "${RUNNER_ENVIRONMENT:-}" = "github-hosted" ] || [[ "${RUNNER_NAME:-}" == runs-on--* ]]; then from="type=gha,scope=${GITHUB_REPOSITORY}-amd64" to="type=gha,scope=${GITHUB_REPOSITORY}-amd64,mode=max" elif [ -n "${BUILDKIT_CACHE_REGISTRY:-}" ]; then diff --git a/.github/workflows/gradle-ci.yml b/.github/workflows/gradle-ci.yml index 17b62c4..016cf04 100644 --- a/.github/workflows/gradle-ci.yml +++ b/.github/workflows/gradle-ci.yml @@ -7,10 +7,10 @@ on: description: > Runner label. Leave unset and the job picks one by repository visibility: GitHub's arm64 hosted runner in public repos, where it - is free and has 4 vCPUs, and the shared self-hosted `grounds-runners` - pool (amd64) in private repos, where GitHub-hosted arm64 is not - offered. Set it explicitly to override, e.g. `grounds-runners-large` - for a build that needs more memory than the shared pool gives. + is free and has 4 vCPUs, and a RunsOn m8a.xlarge (amd64, 4 vCPU, + 16 GB) in private repos, where GitHub-hosted arm64 is not offered. + Set it explicitly to override, e.g. a RunsOn label with + `cpu=8/ram=32` for a build that needs more memory. required: false type: string default: "" @@ -33,11 +33,10 @@ concurrency: jobs: build: # Read the three branches in order. A public repo keeps a GitHub-hosted - # runner: on a self-hosted pool a fork PR would run arbitrary code on - # hbr1, which also hosts production containers (the org's Default runner - # group refuses public repositories as a second line). A private repo goes - # to `grounds-runners`, the shared self-hosted ARC pool (org-level, on - # hbr1 today). And if `repository` is missing from the event payload, the + # runner: on a self-hosted runner a fork PR would run arbitrary code on + # our own infrastructure. A private repo goes to RunsOn, an ephemeral + # m8a spot instance in our AWS account (eu-central-1), sized per job in + # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and # a label no runner matches queues until someone cancels the job. @@ -45,14 +44,15 @@ jobs: ${{ inputs.runner != '' && inputs.runner || (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') - || (github.event.repository.name && 'grounds-runners') + || (github.event.repository.name + && format('runs-on={0}/cpu=4/ram=16/family=m8a/image=ubuntu24-full-x64/extras=s3-cache', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 30 steps: # RunsOn (AWS) runners: routes the Actions cache to RunsOn's S3-backed # Magic Cache. Needs `extras=s3-cache` in the runner label. - name: Enable RunsOn Magic Cache - if: startsWith(inputs.runner, 'runs-on=') + if: startsWith(runner.name, 'runs-on--') uses: runs-on/action@v2 - name: Check out @@ -66,14 +66,14 @@ jobs: # The GitHub Actions cache is a Azure-hosted download: from the self-hosted # runners on hbr1 it stalled at 0 bytes for minutes and crawled at ~1 MB/s # (2026-09-30), slower than resolving the dependencies from scratch. Hosted - # runners keep it; self-hosted ones use the Gradle build cache node. + # runners keep it; other self-hosted ones use the Gradle build cache node. # RunsOn runners use the Actions cache through the Magic Cache. - cache: ${{ (runner.environment == 'github-hosted' || startsWith(inputs.runner, 'runs-on=')) && 'gradle' || '' }} + cache: ${{ (runner.environment == 'github-hosted' || startsWith(runner.name, 'runs-on--')) && 'gradle' || '' }} - name: Set up Gradle uses: gradle/actions/setup-gradle@v6 with: - cache-disabled: ${{ runner.environment != 'github-hosted' && !startsWith(inputs.runner, 'runs-on=') }} + cache-disabled: ${{ runner.environment != 'github-hosted' && !startsWith(runner.name, 'runs-on--') }} - name: Use the Maven mirror uses: groundsgg/.github/.github/actions/maven-mirror@main diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml index 3740421..9f7752b 100644 --- a/.github/workflows/gradle-publish.yml +++ b/.github/workflows/gradle-publish.yml @@ -7,10 +7,10 @@ on: description: > Runner label. Leave unset and the job picks one by repository visibility: GitHub's arm64 hosted runner in public repos, where it - is free and has 4 vCPUs, and the shared self-hosted `grounds-runners` - pool (amd64) in private repos, where GitHub-hosted arm64 is not - offered. Set it explicitly to override, e.g. `grounds-runners-large` - for a build that needs more memory than the shared pool gives. + is free and has 4 vCPUs, and a RunsOn m8a.xlarge (amd64, 4 vCPU, + 16 GB) in private repos, where GitHub-hosted arm64 is not offered. + Set it explicitly to override, e.g. a RunsOn label with + `cpu=8/ram=32` for a build that needs more memory. required: false type: string default: "" @@ -33,11 +33,10 @@ concurrency: jobs: release: # Read the three branches in order. A public repo keeps a GitHub-hosted - # runner: on a self-hosted pool a fork PR would run arbitrary code on - # hbr1, which also hosts production containers (the org's Default runner - # group refuses public repositories as a second line). A private repo goes - # to `grounds-runners`, the shared self-hosted ARC pool (org-level, on - # hbr1 today). And if `repository` is missing from the event payload, the + # runner: on a self-hosted runner a fork PR would run arbitrary code on + # our own infrastructure. A private repo goes to RunsOn, an ephemeral + # m8a spot instance in our AWS account (eu-central-1), sized per job in + # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and # a label no runner matches queues until someone cancels the job. @@ -45,10 +44,17 @@ jobs: ${{ inputs.runner != '' && inputs.runner || (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') - || (github.event.repository.name && 'grounds-runners') + || (github.event.repository.name + && format('runs-on={0}/cpu=4/ram=16/family=m8a/image=ubuntu24-full-x64/extras=s3-cache', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 30 steps: + # RunsOn (AWS) runners: routes the Actions cache to RunsOn's S3-backed + # Magic Cache. Needs `extras=s3-cache` in the runner label. + - name: Enable RunsOn Magic Cache + if: startsWith(runner.name, 'runs-on--') + uses: runs-on/action@v2 + - name: Checkout uses: actions/checkout@v7 @@ -61,12 +67,13 @@ jobs: # runners on hbr1 it stalled at 0 bytes for minutes and crawled at ~1 MB/s # (2026-09-30), slower than resolving the dependencies from scratch. Hosted # runners keep it; self-hosted ones use the Gradle build cache node. - cache: ${{ runner.environment == 'github-hosted' && 'gradle' || '' }} + # RunsOn runners use the Actions cache through the Magic Cache. + cache: ${{ (runner.environment == 'github-hosted' || startsWith(runner.name, 'runs-on--')) && 'gradle' || '' }} - name: Set up Gradle uses: gradle/actions/setup-gradle@v6 with: - cache-disabled: ${{ runner.environment != 'github-hosted' }} + cache-disabled: ${{ runner.environment != 'github-hosted' && !startsWith(runner.name, 'runs-on--') }} - name: Use the Maven mirror uses: groundsgg/.github/.github/actions/maven-mirror@main diff --git a/.github/workflows/label-sync.yml b/.github/workflows/label-sync.yml index 18b1f05..e8cf976 100644 --- a/.github/workflows/label-sync.yml +++ b/.github/workflows/label-sync.yml @@ -9,24 +9,24 @@ concurrency: jobs: sync-labels: - # Free GitHub-hosted runner in public repos; `grounds-runners` in private ones, + # Free GitHub-hosted runner in public repos; a small RunsOn instance in private ones, # where GitHub-hosted arm64 is not offered. Tests `visibility` rather than # `repository.private` because GitHub coerces `==` operands to numbers, so # an absent `private` field would compare equal to false and hand a private # repo a label that does not exist there. # Read the three branches in order. A public repo keeps a GitHub-hosted - # runner: on a self-hosted pool a fork PR would run arbitrary code on - # hbr1, which also hosts production containers (the org's Default runner - # group refuses public repositories as a second line). A private repo goes - # to `grounds-runners`, the shared self-hosted ARC pool (org-level, on - # hbr1 today). And if `repository` is missing from the event payload, the + # runner: on a self-hosted runner a fork PR would run arbitrary code on + # our own infrastructure. A private repo goes to RunsOn, an ephemeral + # m8a spot instance in our AWS account (eu-central-1), sized per job in + # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and # a label no runner matches queues until someone cancels the job. runs-on: >- ${{ (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') - || (github.event.repository.name && 'grounds-runners') + || (github.event.repository.name + && format('runs-on={0}/cpu=2/ram=8/family=m8a/image=ubuntu24-full-x64', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 10 steps: diff --git a/.github/workflows/platform-test-preview-slashcmd.yml b/.github/workflows/platform-test-preview-slashcmd.yml index d935228..ca7c4ab 100644 --- a/.github/workflows/platform-test-preview-slashcmd.yml +++ b/.github/workflows/platform-test-preview-slashcmd.yml @@ -53,18 +53,18 @@ jobs: # here because reusable workflows shouldn't trust their callers. if: github.event.issue.pull_request != null # Read the three branches in order. A public repo keeps a GitHub-hosted - # runner: on a self-hosted pool a fork PR would run arbitrary code on - # hbr1, which also hosts production containers (the org's Default runner - # group refuses public repositories as a second line). A private repo goes - # to `grounds-runners`, the shared self-hosted ARC pool (org-level, on - # hbr1 today). And if `repository` is missing from the event payload, the + # runner: on a self-hosted runner a fork PR would run arbitrary code on + # our own infrastructure. A private repo goes to RunsOn, an ephemeral + # m8a spot instance in our AWS account (eu-central-1), sized per job in + # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and # a label no runner matches queues until someone cancels the job. runs-on: >- ${{ (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') - || (github.event.repository.name && 'grounds-runners') + || (github.event.repository.name + && format('runs-on={0}/cpu=2/ram=8/family=m8a/image=ubuntu24-full-x64', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 10 permissions: diff --git a/.github/workflows/platform-test-preview.yml b/.github/workflows/platform-test-preview.yml index f7d8d29..0304931 100644 --- a/.github/workflows/platform-test-preview.yml +++ b/.github/workflows/platform-test-preview.yml @@ -82,12 +82,13 @@ jobs: # amd64 on purpose: the preview image is deployed onto the amd64 UKS # node pools. An arm64-only image pulls fine and then dies at exec with # "exec format error". Building amd64 on an arm64 runner would mean QEMU. - # Keep public PRs on hosted runners and private repositories on ARC. + # Keep public PRs on hosted runners and private repositories on RunsOn. # Missing event payloads fall back to a hosted runner, not Blacksmith. runs-on: >- ${{ (github.event.repository.visibility == 'public' && 'ubuntu-24.04') - || (github.event.repository.name && 'grounds-runners') + || (github.event.repository.name + && format('runs-on={0}/cpu=4/ram=16/family=m8a/image=ubuntu24-full-x64/extras=s3-cache', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 30 outputs: @@ -97,6 +98,10 @@ jobs: packages: write id-token: write steps: + # RunsOn: routes the Buildx `type=gha` cache to the S3-backed Magic Cache. + - name: Enable RunsOn Magic Cache + if: startsWith(runner.name, 'runs-on--') + uses: runs-on/action@v2 - uses: actions/checkout@v7 with: ref: ${{ github.event.pull_request.head.sha }} @@ -185,18 +190,18 @@ jobs: if: github.event.action != 'closed' needs: build # Read the three branches in order. A public repo keeps a GitHub-hosted - # runner: on a self-hosted pool a fork PR would run arbitrary code on - # hbr1, which also hosts production containers (the org's Default runner - # group refuses public repositories as a second line). A private repo goes - # to `grounds-runners`, the shared self-hosted ARC pool (org-level, on - # hbr1 today). And if `repository` is missing from the event payload, the + # runner: on a self-hosted runner a fork PR would run arbitrary code on + # our own infrastructure. A private repo goes to RunsOn, an ephemeral + # m8a spot instance in our AWS account (eu-central-1), sized per job in + # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and # a label no runner matches queues until someone cancels the job. runs-on: >- ${{ (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') - || (github.event.repository.name && 'grounds-runners') + || (github.event.repository.name + && format('runs-on={0}/cpu=2/ram=8/family=m8a/image=ubuntu24-full-x64', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 20 permissions: @@ -398,18 +403,18 @@ jobs: evict: if: github.event.action == 'closed' # Read the three branches in order. A public repo keeps a GitHub-hosted - # runner: on a self-hosted pool a fork PR would run arbitrary code on - # hbr1, which also hosts production containers (the org's Default runner - # group refuses public repositories as a second line). A private repo goes - # to `grounds-runners`, the shared self-hosted ARC pool (org-level, on - # hbr1 today). And if `repository` is missing from the event payload, the + # runner: on a self-hosted runner a fork PR would run arbitrary code on + # our own infrastructure. A private repo goes to RunsOn, an ephemeral + # m8a spot instance in our AWS account (eu-central-1), sized per job in + # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and # a label no runner matches queues until someone cancels the job. runs-on: >- ${{ (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') - || (github.event.repository.name && 'grounds-runners') + || (github.event.repository.name + && format('runs-on={0}/cpu=2/ram=8/family=m8a/image=ubuntu24-full-x64', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 10 steps: diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 6579baf..57fc440 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -14,24 +14,24 @@ concurrency: jobs: release-please: name: Create Release - # Free GitHub-hosted runner in public repos; `grounds-runners` in private ones, + # Free GitHub-hosted runner in public repos; a small RunsOn instance in private ones, # where GitHub-hosted arm64 is not offered. Tests `visibility` rather than # `repository.private` because GitHub coerces `==` operands to numbers, so # an absent `private` field would compare equal to false and hand a private # repo a label that does not exist there. # Read the three branches in order. A public repo keeps a GitHub-hosted - # runner: on a self-hosted pool a fork PR would run arbitrary code on - # hbr1, which also hosts production containers (the org's Default runner - # group refuses public repositories as a second line). A private repo goes - # to `grounds-runners`, the shared self-hosted ARC pool (org-level, on - # hbr1 today). And if `repository` is missing from the event payload, the + # runner: on a self-hosted runner a fork PR would run arbitrary code on + # our own infrastructure. A private repo goes to RunsOn, an ephemeral + # m8a spot instance in our AWS account (eu-central-1), sized per job in + # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and # a label no runner matches queues until someone cancels the job. runs-on: >- ${{ (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') - || (github.event.repository.name && 'grounds-runners') + || (github.event.repository.name + && format('runs-on={0}/cpu=2/ram=8/family=m8a/image=ubuntu24-full-x64', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 10 steps: From 33ef5bb43c442403b9802f4039d93587687367a7 Mon Sep 17 00:00:00 2001 From: Hendrik Brombeer <20863417+hbrombeer@users.noreply.github.com> Date: Sat, 17 Oct 2026 19:10:00 +0200 Subject: [PATCH 3/3] ci: capacity-optimized spot across m8a, m7a and m8i Allow m7a and m8i next to m8a at the same size and pick spot pools by capacity, so jobs stay on spot instead of falling back to on-demand when one pool runs dry. --- .github/workflows/docker-gradle-build-push.yml | 4 ++-- .github/workflows/gradle-ci.yml | 6 +++--- .github/workflows/gradle-publish.yml | 6 +++--- .github/workflows/label-sync.yml | 4 ++-- .github/workflows/platform-test-preview-slashcmd.yml | 4 ++-- .github/workflows/platform-test-preview.yml | 10 +++++----- .github/workflows/release-please.yml | 4 ++-- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/docker-gradle-build-push.yml b/.github/workflows/docker-gradle-build-push.yml index c3c7036..1e639af 100644 --- a/.github/workflows/docker-gradle-build-push.yml +++ b/.github/workflows/docker-gradle-build-push.yml @@ -36,7 +36,7 @@ jobs: # happened to grounds-forge 4.3.0 in its migrate init container. Building # an amd64 image on an arm64 runner would mean QEMU for the whole build. # Public fork PRs remain on GitHub-hosted runners; private repositories - # use RunsOn, an ephemeral m8a.xlarge spot instance in our AWS account + # use RunsOn, an ephemeral 4 vCPU / 16 GB spot instance in our AWS account # (eu-central-1). Missing # repository payloads safely fall back to GitHub-hosted. runs-on: >- @@ -44,7 +44,7 @@ jobs: || (github.event.repository.visibility == 'public' && 'ubuntu-24.04') || (github.event.repository.name - && format('runs-on={0}/cpu=4/ram=16/family=m8a/image=ubuntu24-full-x64/extras=s3-cache', github.run_id)) + && format('runs-on={0}/cpu=4/ram=16/family=m8a+m7a+m8i/spot=capacity-optimized/image=ubuntu24-full-x64/extras=s3-cache', github.run_id)) || 'ubuntu-24.04' }} # Cold ARC builds can exceed 30m downloading builder/runtime base images # even after Gradle succeeds. Keep the default repo-ARC path bounded at diff --git a/.github/workflows/gradle-ci.yml b/.github/workflows/gradle-ci.yml index 016cf04..fa86571 100644 --- a/.github/workflows/gradle-ci.yml +++ b/.github/workflows/gradle-ci.yml @@ -7,7 +7,7 @@ on: description: > Runner label. Leave unset and the job picks one by repository visibility: GitHub's arm64 hosted runner in public repos, where it - is free and has 4 vCPUs, and a RunsOn m8a.xlarge (amd64, 4 vCPU, + is free and has 4 vCPUs, and a RunsOn spot instance (amd64, 4 vCPU, 16 GB) in private repos, where GitHub-hosted arm64 is not offered. Set it explicitly to override, e.g. a RunsOn label with `cpu=8/ram=32` for a build that needs more memory. @@ -35,7 +35,7 @@ jobs: # Read the three branches in order. A public repo keeps a GitHub-hosted # runner: on a self-hosted runner a fork PR would run arbitrary code on # our own infrastructure. A private repo goes to RunsOn, an ephemeral - # m8a spot instance in our AWS account (eu-central-1), sized per job in + # spot instance (m8a, m7a or m8i) in our AWS account (eu-central-1), sized per job in # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and @@ -45,7 +45,7 @@ jobs: || (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') || (github.event.repository.name - && format('runs-on={0}/cpu=4/ram=16/family=m8a/image=ubuntu24-full-x64/extras=s3-cache', github.run_id)) + && format('runs-on={0}/cpu=4/ram=16/family=m8a+m7a+m8i/spot=capacity-optimized/image=ubuntu24-full-x64/extras=s3-cache', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 30 steps: diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml index 9f7752b..aaf6fbf 100644 --- a/.github/workflows/gradle-publish.yml +++ b/.github/workflows/gradle-publish.yml @@ -7,7 +7,7 @@ on: description: > Runner label. Leave unset and the job picks one by repository visibility: GitHub's arm64 hosted runner in public repos, where it - is free and has 4 vCPUs, and a RunsOn m8a.xlarge (amd64, 4 vCPU, + is free and has 4 vCPUs, and a RunsOn spot instance (amd64, 4 vCPU, 16 GB) in private repos, where GitHub-hosted arm64 is not offered. Set it explicitly to override, e.g. a RunsOn label with `cpu=8/ram=32` for a build that needs more memory. @@ -35,7 +35,7 @@ jobs: # Read the three branches in order. A public repo keeps a GitHub-hosted # runner: on a self-hosted runner a fork PR would run arbitrary code on # our own infrastructure. A private repo goes to RunsOn, an ephemeral - # m8a spot instance in our AWS account (eu-central-1), sized per job in + # spot instance (m8a, m7a or m8i) in our AWS account (eu-central-1), sized per job in # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and @@ -45,7 +45,7 @@ jobs: || (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') || (github.event.repository.name - && format('runs-on={0}/cpu=4/ram=16/family=m8a/image=ubuntu24-full-x64/extras=s3-cache', github.run_id)) + && format('runs-on={0}/cpu=4/ram=16/family=m8a+m7a+m8i/spot=capacity-optimized/image=ubuntu24-full-x64/extras=s3-cache', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 30 steps: diff --git a/.github/workflows/label-sync.yml b/.github/workflows/label-sync.yml index e8cf976..553502c 100644 --- a/.github/workflows/label-sync.yml +++ b/.github/workflows/label-sync.yml @@ -17,7 +17,7 @@ jobs: # Read the three branches in order. A public repo keeps a GitHub-hosted # runner: on a self-hosted runner a fork PR would run arbitrary code on # our own infrastructure. A private repo goes to RunsOn, an ephemeral - # m8a spot instance in our AWS account (eu-central-1), sized per job in + # spot instance (m8a, m7a or m8i) in our AWS account (eu-central-1), sized per job in # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and @@ -26,7 +26,7 @@ jobs: ${{ (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') || (github.event.repository.name - && format('runs-on={0}/cpu=2/ram=8/family=m8a/image=ubuntu24-full-x64', github.run_id)) + && format('runs-on={0}/cpu=2/ram=8/family=m8a+m7a+m8i/spot=capacity-optimized/image=ubuntu24-full-x64', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 10 steps: diff --git a/.github/workflows/platform-test-preview-slashcmd.yml b/.github/workflows/platform-test-preview-slashcmd.yml index ca7c4ab..faa3984 100644 --- a/.github/workflows/platform-test-preview-slashcmd.yml +++ b/.github/workflows/platform-test-preview-slashcmd.yml @@ -55,7 +55,7 @@ jobs: # Read the three branches in order. A public repo keeps a GitHub-hosted # runner: on a self-hosted runner a fork PR would run arbitrary code on # our own infrastructure. A private repo goes to RunsOn, an ephemeral - # m8a spot instance in our AWS account (eu-central-1), sized per job in + # spot instance (m8a, m7a or m8i) in our AWS account (eu-central-1), sized per job in # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and @@ -64,7 +64,7 @@ jobs: ${{ (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') || (github.event.repository.name - && format('runs-on={0}/cpu=2/ram=8/family=m8a/image=ubuntu24-full-x64', github.run_id)) + && format('runs-on={0}/cpu=2/ram=8/family=m8a+m7a+m8i/spot=capacity-optimized/image=ubuntu24-full-x64', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 10 permissions: diff --git a/.github/workflows/platform-test-preview.yml b/.github/workflows/platform-test-preview.yml index 0304931..61b9ee8 100644 --- a/.github/workflows/platform-test-preview.yml +++ b/.github/workflows/platform-test-preview.yml @@ -88,7 +88,7 @@ jobs: ${{ (github.event.repository.visibility == 'public' && 'ubuntu-24.04') || (github.event.repository.name - && format('runs-on={0}/cpu=4/ram=16/family=m8a/image=ubuntu24-full-x64/extras=s3-cache', github.run_id)) + && format('runs-on={0}/cpu=4/ram=16/family=m8a+m7a+m8i/spot=capacity-optimized/image=ubuntu24-full-x64/extras=s3-cache', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 30 outputs: @@ -192,7 +192,7 @@ jobs: # Read the three branches in order. A public repo keeps a GitHub-hosted # runner: on a self-hosted runner a fork PR would run arbitrary code on # our own infrastructure. A private repo goes to RunsOn, an ephemeral - # m8a spot instance in our AWS account (eu-central-1), sized per job in + # spot instance (m8a, m7a or m8i) in our AWS account (eu-central-1), sized per job in # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and @@ -201,7 +201,7 @@ jobs: ${{ (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') || (github.event.repository.name - && format('runs-on={0}/cpu=2/ram=8/family=m8a/image=ubuntu24-full-x64', github.run_id)) + && format('runs-on={0}/cpu=2/ram=8/family=m8a+m7a+m8i/spot=capacity-optimized/image=ubuntu24-full-x64', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 20 permissions: @@ -405,7 +405,7 @@ jobs: # Read the three branches in order. A public repo keeps a GitHub-hosted # runner: on a self-hosted runner a fork PR would run arbitrary code on # our own infrastructure. A private repo goes to RunsOn, an ephemeral - # m8a spot instance in our AWS account (eu-central-1), sized per job in + # spot instance (m8a, m7a or m8i) in our AWS account (eu-central-1), sized per job in # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and @@ -414,7 +414,7 @@ jobs: ${{ (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') || (github.event.repository.name - && format('runs-on={0}/cpu=2/ram=8/family=m8a/image=ubuntu24-full-x64', github.run_id)) + && format('runs-on={0}/cpu=2/ram=8/family=m8a+m7a+m8i/spot=capacity-optimized/image=ubuntu24-full-x64', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 10 steps: diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 57fc440..7f10ef8 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -22,7 +22,7 @@ jobs: # Read the three branches in order. A public repo keeps a GitHub-hosted # runner: on a self-hosted runner a fork PR would run arbitrary code on # our own infrastructure. A private repo goes to RunsOn, an ephemeral - # m8a spot instance in our AWS account (eu-central-1), sized per job in + # spot instance (m8a, m7a or m8i) in our AWS account (eu-central-1), sized per job in # the label. And if `repository` is missing from the event payload, the # job lands on hosted amd64 `ubuntu-24.04`, which exists for public and # private repos alike -- hosted arm64 does not exist for private ones, and @@ -31,7 +31,7 @@ jobs: ${{ (github.event.repository.visibility == 'public' && 'ubuntu-24.04-arm') || (github.event.repository.name - && format('runs-on={0}/cpu=2/ram=8/family=m8a/image=ubuntu24-full-x64', github.run_id)) + && format('runs-on={0}/cpu=2/ram=8/family=m8a+m7a+m8i/spot=capacity-optimized/image=ubuntu24-full-x64', github.run_id)) || 'ubuntu-24.04' }} timeout-minutes: 10 steps: