Skip to content

ci: move Nix workflows to the self-hosted ARM runner + warm cache for aarch64-linux#1793

Merged
kixelated merged 3 commits into
mainfrom
claude/upbeat-vaughan-2c4612
Jun 19, 2026
Merged

ci: move Nix workflows to the self-hosted ARM runner + warm cache for aarch64-linux#1793
kixelated merged 3 commits into
mainfrom
claude/upbeat-vaughan-2c4612

Conversation

@kixelated

@kixelated kixelated commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Moves the Nix-based, architecture-independent release workflows onto the moq-dev self-hosted A1 (aarch64) runner, following the pattern check.yml already established, and adds an aarch64-linux leg to the cachix binary cache so that platform is warmed too.

Workflow Job moved Trigger (all trusted)
release-rs.yml release-plz (crates.io) push to main
release-js.yml npm/JSR publish push to main (js paths)
release-py.yml build wheel/sdist push to main (py paths)
update-flake.yml flake.lock update monthly cron + dispatch
cachix.yml new aarch64-linux leg release tags

These all use Nix and produce arch-independent output (or, for cachix, the box natively is the target arch), so the aarch64 box is a valid runner. Unlike check.yml they trigger solely on trusted events (push/tag/schedule/dispatch), so no fork fallback is needed.

Per-workflow notes

For the moved jobs: runs-on[self-hosted, nix], dropped nix-installer-action (the box already has Nix), and ran Nix steps in a login shell (bash -leo pipefail {0}) so /etc/profile.d puts Nix on PATH.

  • release-rs: also reuses the persistent CARGO_TARGET_DIR=$HOME/cargo-target/moq-$RUNNER_NAME (same warm-cache trick as check.yml) since release-plz compiles.
  • release-py: only the build job moved; publish stays on ubuntu-latest (no Nix, just OIDC trusted-publish to PyPI).
  • update-flake: the update-flake-lock action invokes nix directly rather than through a shell, so a login shell wouldn't help. Added an Add Nix to PATH step that resolves the binary via a login shell and writes its dir to GITHUB_PATH. Also added an if: repository_owner == 'moq-dev' guard so a fork's manual dispatch doesn't queue forever on a runner that doesn't exist.
  • cachix: matrix moved to include form so each leg carries its own runs-on (string for hosted runners, [self-hosted, nix] for the box) while os stays the cache-pin label, keeping existing pins stable. The new aarch64-linux leg gates the installer on github-hosted, adds Nix to PATH for the cachix action + nix build, and sets skipAddingSubstituter on self-hosted: the box's runner user isn't a Nix trusted-user and it substitutes only from its warm local store, so cachix use must not rewrite nix.conf there. Push auth is unaffected; this leg only pushes.

Still left alone (arch-specific)

  • moq-cli / moq-relay / moq-token-cli / moq-gst, libmoq, release-py-ffi — per-arch release binaries / native wheels.

Caveats for the reviewer

  • No availability fallback: like check.yml, the moved jobs hard-target the single self-hosted box. If it's offline, a release queues until it's back (or hits the runner timeout). Matches the existing trade-off.
  • The cachix aarch64-linux leg adds load to the box on every release tag, but tags are infrequent and fail-fast: false keeps a failing arm build from killing the x86_64 / darwin legs.

🤖 Generated with Claude Code

(Written by Claude)

kixelated and others added 2 commits June 18, 2026 22:34
release-rs, release-js, release-py (build), and update-flake all use Nix
and produce architecture-independent output (crates.io publish, npm/JSR
publish, pure-python wheel, flake.lock update), so they can run on the
moq-dev self-hosted A1 (aarch64) runner with its warm /nix/store instead
of a GitHub-hosted runner.

These trigger only on trusted events (push to main, tag, schedule,
dispatch), so unlike check.yml they need no fork fallback. Per the
established self-hosted pattern: drop the nix-installer-action (the box
already has Nix) and run Nix steps in a login shell so /etc/profile.d
puts Nix on PATH. release-rs also reuses the persistent CARGO_TARGET_DIR.

update-flake's update-flake-lock action invokes nix directly rather than
through a shell, so it gets a step that resolves nix via a login shell
and writes its dir to GITHUB_PATH, plus an owner guard so a fork's manual
dispatch doesn't queue forever on a runner that doesn't exist.

Left arch-specific workflows alone: cachix (caches x86_64-linux), the
per-binary release builds, libmoq, and release-py-ffi.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an aarch64-linux matrix leg to the cachix release job, built on the
moq-dev self-hosted A1 (the cache previously covered only x86_64-linux
and aarch64-darwin). Tag pushes are trusted, so no fork concern.

The matrix moves to `include` so each leg carries its own `runs-on`
(string for the hosted runners, [self-hosted, nix] for the box) while
`os` stays the cache pin label, keeping existing pins stable.

Self-hosted adjustments: gate the nix-installer on github-hosted (the box
has Nix), add Nix to PATH via a login shell for the cachix action and
`nix build`, and set skipAddingSubstituter on self-hosted. The box's
runner user isn't a Nix trusted-user and it substitutes only from its
warm local store, so `cachix use` must not rewrite nix.conf there; push
auth is unaffected and this leg only pushes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kixelated kixelated changed the title ci: move Nix release workflows to the self-hosted ARM runner ci: move Nix workflows to the self-hosted ARM runner + warm cache for aarch64-linux Jun 19, 2026
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 186c6723-4625-4902-85c2-a99b7c247718

📥 Commits

Reviewing files that changed from the base of the PR and between ee2ec7e and 3762f96.

📒 Files selected for processing (1)
  • .github/actionlint.yaml
✅ Files skipped from review due to trivial changes (1)
  • .github/actionlint.yaml

Walkthrough

All five GitHub Actions workflows (release-js, release-py, release-rs, update-flake, cachix) are updated to run on self-hosted Nix runners instead of ubuntu-latest. The first four workflows target a single self-hosted runner with a moq-dev owner guard, while cachix restructures its matrix to conditionally select runners per platform. The DeterminateSystems/nix-installer-action step is removed from all workflows. Build and install steps use an explicit bash -leo pipefail {0} shell so the login-shell sources Nix onto PATH. The update-flake workflow adds a step to append the nix binary directory to GITHUB_PATH. The release-rs workflow additionally persists CARGO_TARGET_DIR under $HOME via GITHUB_ENV. The cachix workflow conditionally applies Nix setup and Cachix action configuration based on runner type. The actionlint configuration is updated to register the nix label for self-hosted runners.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main changes: moving Nix workflows to self-hosted ARM runner and warming cache for aarch64-linux.
Description check ✅ Passed The description is well-structured and directly related to the changeset, providing comprehensive details about workflow migrations, technical implementation, and design decisions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/upbeat-vaughan-2c4612

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/release-js.yml (1)

24-24: ⚡ Quick win

Inconsistent if: syntax across workflows.

This file uses a bare if: condition without ${{ }} delimiters, while the other three workflows in this PR (release-py.yml, release-rs.yml, update-flake.yml) wrap the identical condition in ${{ }}. Both syntaxes are valid, but consistency improves maintainability.

♻️ Standardize syntax
-    if: github.repository_owner == 'moq-dev'
+    if: ${{ github.repository_owner == 'moq-dev' }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release-js.yml at line 24, The `if:` condition on line 24
uses bare syntax without `${{ }}` delimiters, which is inconsistent with the
identical conditions in the other workflow files (release-py.yml,
release-rs.yml, update-flake.yml). Wrap the condition `github.repository_owner
== 'moq-dev'` with `${{ }}` delimiters to standardize the syntax across all
workflows and improve maintainability.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/update-flake.yml:
- Around line 21-22: The checkout action in the "Checkout" step does not
explicitly disable credential persistence, which means the GITHUB_TOKEN will be
available to all subsequent workflow steps by default. Add the
`persist-credentials: false` option to the `actions/checkout` configuration to
ensure credentials are not persisted in the git config, following the principle
of least privilege and matching the pattern used in the other workflows. This
ensures the `update-flake-lock` action uses only its own authentication
mechanism.

---

Nitpick comments:
In @.github/workflows/release-js.yml:
- Line 24: The `if:` condition on line 24 uses bare syntax without `${{ }}`
delimiters, which is inconsistent with the identical conditions in the other
workflow files (release-py.yml, release-rs.yml, update-flake.yml). Wrap the
condition `github.repository_owner == 'moq-dev'` with `${{ }}` delimiters to
standardize the syntax across all workflows and improve maintainability.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8f372179-f308-43a9-b1f2-ad7f98738a16

📥 Commits

Reviewing files that changed from the base of the PR and between ea94b7b and f89f24b.

📒 Files selected for processing (4)
  • .github/workflows/release-js.yml
  • .github/workflows/release-py.yml
  • .github/workflows/release-rs.yml
  • .github/workflows/update-flake.yml

Comment on lines 21 to 22
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Set persist-credentials: false on checkout.

The workflow does not explicitly disable credential persistence on checkout. By default, actions/checkout sets persist-credentials: true, making the GITHUB_TOKEN available to all subsequent steps. The update-flake-lock action creates pull requests and should use only its own authentication mechanism (via the default GITHUB_TOKEN passed to it), not rely on a persisted token in the workspace .git/config.

Explicitly setting persist-credentials: false follows the principle of least privilege and matches the pattern used in the other three workflows in this PR.

🛡️ Add persist-credentials setting
       - name: Checkout
         uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
+        with:
+          persist-credentials: false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 21-26: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/update-flake.yml around lines 21 - 22, The checkout action
in the "Checkout" step does not explicitly disable credential persistence, which
means the GITHUB_TOKEN will be available to all subsequent workflow steps by
default. Add the `persist-credentials: false` option to the `actions/checkout`
configuration to ensure credentials are not persisted in the git config,
following the principle of least privilege and matching the pattern used in the
other workflows. This ensures the `update-flake-lock` action uses only its own
authentication mechanism.

Source: Linters/SAST tools

@kixelated kixelated enabled auto-merge (squash) June 19, 2026 05:40

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/cachix.yml:
- Around line 16-34: The release job in cachix.yml lacks a repository owner
guard that other similar workflows (release-rs.yml, release-py.yml) have in
place. When a fork pushes a tag, the aarch64-linux matrix entry will queue
indefinitely waiting for the self-hosted nix runner that doesn't exist in the
fork. Add a job-level condition guard using `if: github.repository_owner ==
'moq-dev'` to the release job to ensure it only executes in the main repository
and prevent forks from triggering self-hosted runner requests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 38afc496-3ff9-41ab-92ec-4ffb227adfde

📥 Commits

Reviewing files that changed from the base of the PR and between f89f24b and ee2ec7e.

📒 Files selected for processing (1)
  • .github/workflows/cachix.yml

Comment on lines 16 to +34
release:
name: Release (${{ matrix.os }})
runs-on: ${{ matrix.os }}
runs-on: ${{ matrix.runs-on }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-latest
include:
# `os` is just the cache pin label; keep the existing values stable so
# old pins aren't orphaned.
- os: ubuntu-latest # x86_64-linux
runs-on: ubuntu-latest
- os: macos-latest # aarch64-darwin
runs-on: macos-latest
# aarch64-linux on the moq-dev self-hosted A1 (warm /nix/store). Tag
# pushes are trusted, so no fork concern.
- os: aarch64-linux
runs-on: [self-hosted, nix]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add repository owner guard to prevent fork CI failures.

Unlike the other workflows (release-rs.yml, release-py.yml, etc.), this workflow lacks an if: github.repository_owner == 'moq-dev' guard. If a fork pushes a tag, the aarch64-linux matrix entry will queue indefinitely waiting for the non-existent [self-hosted, nix] runner, while the other two matrix entries would succeed.

Consider either:

  1. Adding a job-level guard: if: github.repository_owner == 'moq-dev' (consistent with other workflows), or
  2. Making the aarch64-linux matrix entry conditional using ${{ github.repository_owner == 'moq-dev' && ...}} in a matrix exclude/include expression.

Option 1 is simpler and consistent with the pattern established in release-rs.yml (context snippet 1).

🛡️ Proposed fix: Add repository owner guard
   release:
     name: Release (${{ matrix.os }})
     runs-on: ${{ matrix.runs-on }}
+    if: ${{ github.repository_owner == 'moq-dev' }}
     permissions:
       contents: read
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
release:
name: Release (${{ matrix.os }})
runs-on: ${{ matrix.os }}
runs-on: ${{ matrix.runs-on }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-latest
include:
# `os` is just the cache pin label; keep the existing values stable so
# old pins aren't orphaned.
- os: ubuntu-latest # x86_64-linux
runs-on: ubuntu-latest
- os: macos-latest # aarch64-darwin
runs-on: macos-latest
# aarch64-linux on the moq-dev self-hosted A1 (warm /nix/store). Tag
# pushes are trusted, so no fork concern.
- os: aarch64-linux
runs-on: [self-hosted, nix]
release:
name: Release (${{ matrix.os }})
runs-on: ${{ matrix.runs-on }}
if: ${{ github.repository_owner == 'moq-dev' }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
# `os` is just the cache pin label; keep the existing values stable so
# old pins aren't orphaned.
- os: ubuntu-latest # x86_64-linux
runs-on: ubuntu-latest
- os: macos-latest # aarch64-darwin
runs-on: macos-latest
# aarch64-linux on the moq-dev self-hosted A1 (warm /nix/store). Tag
# pushes are trusted, so no fork concern.
- os: aarch64-linux
runs-on: [self-hosted, nix]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/cachix.yml around lines 16 - 34, The release job in
cachix.yml lacks a repository owner guard that other similar workflows
(release-rs.yml, release-py.yml) have in place. When a fork pushes a tag, the
aarch64-linux matrix entry will queue indefinitely waiting for the self-hosted
nix runner that doesn't exist in the fork. Add a job-level condition guard using
`if: github.repository_owner == 'moq-dev'` to the release job to ensure it only
executes in the main repository and prevent forks from triggering self-hosted
runner requests.

actionlint flagged `runs-on: [self-hosted, nix]` in the moved workflows
because `nix` is a custom label it doesn't know. check.yml slips past
only because its label is hidden inside a fromJSON() expression actionlint
can't statically read. Declare the label in .github/actionlint.yaml (the
fix the error itself recommends) so the literal form lints clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kixelated kixelated merged commit 230e34d into main Jun 19, 2026
1 of 2 checks passed
@kixelated kixelated deleted the claude/upbeat-vaughan-2c4612 branch June 19, 2026 06:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant