From 44aa9072256415f855941b78938e2541bef0af2d Mon Sep 17 00:00:00 2001 From: Anthony Ronning <101225832+AnthonyRonning@users.noreply.github.com> Date: Sat, 10 Oct 2026 05:03:01 +0000 Subject: [PATCH] ci: move heavy Linux builds to Blacksmith --- .github/actionlint.yaml | 1 + .github/workflows/agent-ci.yml | 4 ++-- .github/workflows/agent-desktop-build.yml | 2 +- .github/workflows/android-build.yml | 4 ++-- .github/workflows/android-pr-build.yml | 2 +- .github/workflows/desktop-build.yml | 4 ++-- .github/workflows/desktop-pr-build.yml | 2 +- .github/workflows/release.yml | 28 +++++++++++------------ docs/repository-workflows.md | 12 ++++++++++ scripts/ci/test_signing_workflows.py | 13 +++++++++++ 10 files changed, 49 insertions(+), 23 deletions(-) diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml index 82a2b6c3f..af31490dd 100644 --- a/.github/actionlint.yaml +++ b/.github/actionlint.yaml @@ -4,3 +4,4 @@ self-hosted-runner: - blacksmith-8vcpu-ubuntu-2404-arm - blacksmith-8vcpu-windows-2025 - blacksmith-6vcpu-macos-26 + - blacksmith-8vcpu-ubuntu-2404 diff --git a/.github/workflows/agent-ci.yml b/.github/workflows/agent-ci.yml index 2692a57d3..9d6cc8d27 100644 --- a/.github/workflows/agent-ci.yml +++ b/.github/workflows/agent-ci.yml @@ -7,7 +7,7 @@ on: # Run unprivileged validation for stacked PRs as well as master targets. workflow_dispatch: -# PR code, including forks, executes only on fresh hosted runners with a +# PR code, including forks, executes only on fresh managed runners with a # read-only token. No signing, release publication, environment, or deployment # credentials belong in this workflow. permissions: @@ -83,7 +83,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-26, windows-latest] + os: [blacksmith-8vcpu-ubuntu-2404, macos-26, windows-latest] steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: diff --git a/.github/workflows/agent-desktop-build.yml b/.github/workflows/agent-desktop-build.yml index 204e5fc05..9b97d9a3c 100644 --- a/.github/workflows/agent-desktop-build.yml +++ b/.github/workflows/agent-desktop-build.yml @@ -208,7 +208,7 @@ jobs: name: Linux AppImage (${{ matrix.variant }}) needs: changes if: ${{ always() && !cancelled() && ((github.event_name == 'pull_request' && (needs.changes.result != 'success' || needs.changes.outputs.agent != 'false')) || (github.repository == 'MaplePrivacyLabs/Maple' && github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch'))) }} - runs-on: ubuntu-latest + runs-on: blacksmith-8vcpu-ubuntu-2404 timeout-minutes: 120 strategy: fail-fast: false diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index c23034de9..7c6e579ff 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -18,7 +18,7 @@ jobs: environment: android-signing needs: changes if: ${{ always() && !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.frontend != 'false' || needs.changes.outputs.android != 'false') }} - runs-on: ubuntu-latest-8-cores + runs-on: blacksmith-8vcpu-ubuntu-2404 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: @@ -102,7 +102,7 @@ jobs: verify-android-artifacts: needs: build-android - runs-on: ubuntu-latest-8-cores + runs-on: ubuntu-latest permissions: contents: read steps: diff --git a/.github/workflows/android-pr-build.yml b/.github/workflows/android-pr-build.yml index ff9d24e55..0744e104a 100644 --- a/.github/workflows/android-pr-build.yml +++ b/.github/workflows/android-pr-build.yml @@ -14,7 +14,7 @@ jobs: build-android: needs: changes if: ${{ always() && !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.android != 'false') }} - runs-on: ubuntu-latest + runs-on: blacksmith-8vcpu-ubuntu-2404 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 19b523f0e..674ea8c06 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -90,7 +90,7 @@ jobs: environment: desktop-signing needs: changes if: ${{ always() && !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.frontend != 'false' || needs.changes.outputs.linux != 'false') }} - runs-on: ubuntu-latest-8-cores + runs-on: blacksmith-8vcpu-ubuntu-2404 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: @@ -167,7 +167,7 @@ jobs: verify-linux-desktop-artifacts: needs: - build-linux - runs-on: ubuntu-latest-8-cores + runs-on: ubuntu-latest permissions: contents: read steps: diff --git a/.github/workflows/desktop-pr-build.yml b/.github/workflows/desktop-pr-build.yml index c1f5f6d1a..b44b33a80 100644 --- a/.github/workflows/desktop-pr-build.yml +++ b/.github/workflows/desktop-pr-build.yml @@ -96,7 +96,7 @@ jobs: build-linux: needs: changes if: ${{ always() && !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.linux != 'false') }} - runs-on: ubuntu-latest + runs-on: blacksmith-8vcpu-ubuntu-2404 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ce7293af0..be24d468b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -140,7 +140,7 @@ jobs: - platform: macos-26-xlarge nix_shell: ci artifact_prefix: macos - - platform: ubuntu-latest-8-cores + - platform: blacksmith-8vcpu-ubuntu-2404 nix_shell: desktop-linux artifact_prefix: linux @@ -163,7 +163,7 @@ jobs: - name: Cache sccache uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # was v4 with: - path: ${{ startsWith(matrix.platform, 'macos') && '~/Library/Caches/Mozilla.sccache' || '~/.cache/sccache' }} + path: ${{ runner.os == 'macOS' && '~/Library/Caches/Mozilla.sccache' || '~/.cache/sccache' }} key: ${{ runner.os }}-sccache-release-${{ hashFiles('apps/maple-research/frontend/src-tauri/Cargo.lock') }} restore-keys: | ${{ runner.os }}-sccache-release- @@ -172,7 +172,7 @@ jobs: # Ubuntu 24.04 restricts unprivileged user namespaces by default; linuxdeploy/AppImage # packaging uses bubblewrap and otherwise fails with "bwrap: setting up uid map: Permission denied". - name: Allow bubblewrap user namespaces - if: matrix.platform == 'ubuntu-latest-8-cores' + if: runner.os == 'Linux' run: | set -euo pipefail if [ -e /proc/sys/kernel/apparmor_restrict_unprivileged_userns ]; then @@ -185,15 +185,15 @@ jobs: - name: Build Tauri desktop app run: nix develop --no-update-lock-file .#${{ matrix.nix_shell }} -c ./scripts/ci/desktop-release.sh env: - APPIMAGE_EXTRACT_AND_RUN: ${{ matrix.platform == 'ubuntu-latest-8-cores' && '1' || '' }} - NO_STRIP: ${{ matrix.platform == 'ubuntu-latest-8-cores' && 'true' || '' }} + APPIMAGE_EXTRACT_AND_RUN: ${{ runner.os == 'Linux' && '1' || '' }} + NO_STRIP: ${{ runner.os == 'Linux' && 'true' || '' }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} - APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} - APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} - APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + APPLE_CERTIFICATE: ${{ runner.os == 'macOS' && secrets.APPLE_CERTIFICATE || '' }} + APPLE_CERTIFICATE_PASSWORD: ${{ runner.os == 'macOS' && secrets.APPLE_CERTIFICATE_PASSWORD || '' }} + APPLE_ID: ${{ runner.os == 'macOS' && secrets.APPLE_ID || '' }} + APPLE_PASSWORD: ${{ runner.os == 'macOS' && secrets.APPLE_ID_PASSWORD || '' }} + APPLE_TEAM_ID: ${{ runner.os == 'macOS' && secrets.APPLE_TEAM_ID || '' }} - name: Show sccache stats if: always() @@ -382,7 +382,7 @@ jobs: build-android: environment: android-signing needs: classify-app-release - runs-on: ubuntu-latest-8-cores + runs-on: blacksmith-8vcpu-ubuntu-2404 permissions: contents: write id-token: write @@ -570,7 +570,7 @@ jobs: build-web: needs: classify-app-release - runs-on: ubuntu-latest-8-cores + runs-on: ubuntu-latest permissions: contents: write id-token: write @@ -764,7 +764,7 @@ jobs: needs: - classify-app-release - build-android - runs-on: ubuntu-latest-8-cores + runs-on: ubuntu-latest permissions: contents: read steps: @@ -795,7 +795,7 @@ jobs: needs: - classify-app-release - build-tauri - runs-on: ubuntu-latest-8-cores + runs-on: ubuntu-latest permissions: contents: read steps: diff --git a/docs/repository-workflows.md b/docs/repository-workflows.md index ae008148b..ced2361a9 100644 --- a/docs/repository-workflows.md +++ b/docs/repository-workflows.md @@ -60,6 +60,18 @@ artifacts to TestFlight. Creating a GitHub Release starts release builds and downstream publication. Use `$release-maple` only for explicitly requested release work, and report the tag and commit before publishing. +## Linux runners + +Research desktop/Android compilation and Agent Linux CI/AppImage builds use +`blacksmith-8vcpu-ubuntu-2404`. Lightweight selectors, web builds, artifact +verification and the separate Agent attestation job remain on GitHub's standard +runners. Signing jobs retain their protected environments. The desktop release +build command only receives Apple environment variables on macOS, but that +condition does not restrict the runner's job secret context: approving a signing +job trusts its runner with the selected environment's secrets. PR builds have +no signing credentials and retain their existing branch-scoped caches. No +persistent vendor disks or Docker builders are configured by these workflows. + ## macOS runners and Apple toolchain Unsigned Research desktop/iOS PR builds and Agent release-binary jobs use diff --git a/scripts/ci/test_signing_workflows.py b/scripts/ci/test_signing_workflows.py index 0163a0200..092f9af1d 100644 --- a/scripts/ci/test_signing_workflows.py +++ b/scripts/ci/test_signing_workflows.py @@ -147,6 +147,19 @@ def test_windows_oidc_keeps_its_federated_environment_identity(self): self.assertEqual(job["environment"], "windows-signing") self.assertEqual(job.get("permissions", workflows()[name]["permissions"])["id-token"], "write") + def test_desktop_release_linux_build_subprocess_has_no_apple_environment(self): + # Step env is evaluated on the runner. This checks subprocess hygiene, + # not isolation from the job's protected-environment secret context. + job = workflows()["release.yml"]["jobs"]["build-tauri"] + build = next(step for step in job["steps"] + if step.get("name") == "Build Tauri desktop app") + for name in ("APPLE_CERTIFICATE", "APPLE_CERTIFICATE_PASSWORD", "APPLE_ID", + "APPLE_PASSWORD", "APPLE_TEAM_ID"): + self.assertTrue(build["env"][name].startswith("${{ runner.os == 'macOS' && secrets.")) + self.assertTrue(build["env"][name].endswith(" || '' }}")) + for name in ("TAURI_SIGNING_PRIVATE_KEY", "TAURI_SIGNING_PRIVATE_KEY_PASSWORD"): + self.assertEqual(build["env"][name], "${{ secrets." + name + " }}") + def test_dev_testflight_runs_for_every_master_push_and_only_trusted_manual_runs(self): config = workflows()["ios-dev-testflight.yml"] self.assertEqual(config["on"], {"push": {"branches": ["master"]}, "workflow_dispatch": None})