Skip to content

feat(install): Homebrew formula + Nix flake (runner validation) - #285

Closed
schubydoo wants to merge 5 commits into
mainfrom
experiment/brew-nix
Closed

schubydoo wants to merge 5 commits into
mainfrom
experiment/brew-nix

Conversation

@schubydoo

Copy link
Copy Markdown
Owner

Draft to runner-validate the Homebrew formula + Nix flake (the dev host has neither). The brew-nix-smoke jobs run brew install on macOS and nix build/run on Linux against the 0.10.0 release. Promoting once green. Pairs with #283 (sh/ps1/Scoop install paths).

Add the two remaining prong-1 package paths, written to be validated on
GitHub runners (the dev host has neither brew nor nix):

- Formula/clauster.rb — installs the signed standalone binary on
  macOS arm64 + Linux x86_64.
- flake.nix — `nix run github:schubydoo/clauster`; wraps the binary with
  autoPatchelfHook on Linux. Covers x86_64-linux + aarch64-darwin.
- .github/workflows/brew-nix-smoke.yml — brew install on macos-latest +
  nix build/run on ubuntu-latest, asserting `clauster --version`.

Experimental: open as a draft so the pull_request trigger runs the smoke
jobs; promote once green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 15, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5113c6df-0656-48a8-842d-92ad2361831f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)

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

schubydoo and others added 4 commits June 15, 2026 10:31
Modern Homebrew rejects a bare local formula path (must be in a tap), so
stage the formula into a local tap and install from there — also a more
realistic test of how the formula is consumed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Temporary helper to capture a reproducible lockfile (no nix on the dev
host); the lock gets committed and this step removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Commit the runner-generated flake.lock (nixpkgs pinned) so
`nix run github:schubydoo/clauster` is reproducible, and switch the nix
smoke job to --locked so a stale lock fails CI. Drops the temporary
upload step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@schubydoo

Copy link
Copy Markdown
Owner Author

Validated on real runners ✅

brew-nix-smoke is green (run after committing the lock):

  • Homebrew (macos-latest/arm64): 🍺 Cellar/clauster/0.10.0, clauster --version → clauster 0.10.0.
  • Nix (ubuntu-latest/x86_64): nix build + nix run → clauster 0.10.0. autoPatchelfHook + zlib was sufficient for the PyInstaller ELF.

flake.lock is committed (nixpkgs pinned to 9eac87a…) and the nix job runs --no-update-lock-file, so a stale lock fails CI.

Remaining to ship (sequenced after #283 + #284 merge, to avoid conflicts on the shared docs/installation.md + release-please.yml)

  1. Docs — Homebrew + Nix sections in docs/installation.md + README (layers cleanly on top of feat(install): standalone-binary, Scoop, and sh/ps1 one-line installers #283's install sections).
  2. Release version+hash bump — Formula/flake.nix pin 0.10.0 + sha256; add a release-time step to bump them (layers on top of feat(ci): build linux-arm64 + macos-x86_64 standalone binaries #284's release-please change). Until then they need a manual bump per release.

Maintainer action — Homebrew tap repo

Modern brew only installs a formula from a tap (a brew install --formula <path> is rejected — that's why the CI stages it into a local tap). For the real UX you'd create a small public repo:

# 1. Create public repo: schubydoo/homebrew-clauster   (the "homebrew-" prefix is required)
# 2. Add this repo's Formula/clauster.rb at Formula/clauster.rb in it
# 3. Users then:
brew tap schubydoo/clauster
brew install clauster

The release-bump automation (item 2) can push the updated formula into that tap on each release (needs a token). Formula/clauster.rb here is the source of truth to mirror.

@schubydoo

Copy link
Copy Markdown
Owner Author

📌 Staged: consolidate into one Installer smoke workflow (apply AFTER #283 merges)

Per decision, the brew + nix jobs fold into installer-smoke.yml (which #283 puts on main); brew-nix-smoke.yml is deleted. Drop-in steps once #283 is merged:

  1. git checkout experiment/brew-nix && git fetch origin && git rebase origin/main (picks up installer-smoke.yml from feat(install): standalone-binary, Scoop, and sh/ps1 one-line installers #283).
  2. In .github/workflows/installer-smoke.yml, add the two jobs verbatim from brew-nix-smoke.yml:
    • brew — macos-latest: brew tap-new schubydoo/local --no-git → stage Formula/clauster.rb → brew install --formula schubydoo/local/clauster → clauster --version.
    • nix — ubuntu-latest: install nix (single-user) → nix build/run .#clauster --no-update-lock-file → verify clauster --version.
  3. Extend installer-smoke.yml's pull_request.paths with Formula/clauster.rb, flake.nix, flake.lock.
  4. git rm .github/workflows/brew-nix-smoke.yml.
  5. Result — one Installer smoke workflow: sh (ubuntu+macos), ps1 (windows), brew (macos), nix (ubuntu). All four package paths in one place.

Both source jobs are already green; this is a pure relocation. The ps1 job on #283 already carries the $LASTEXITCODE guard, so nothing in the brew/nix jobs needs changing — they move as-is.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9d7ded70-4e9c-4fd1-9104-a65b0752ef77

📥 Commits

Reviewing files that changed from the base of the PR and between 51f48ab and e5eb586.

⛔ Files ignored due to path filters (1)
  • flake.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • .github/workflows/brew-nix-smoke.yml
  • Formula/clauster.rb
  • flake.nix

Comment on lines +24 to +26
name: Homebrew (macos-arm64)
runs-on: macos-latest
timeout-minutes: 20

Copy link
Copy Markdown

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

Pin an explicit ARM macOS runner to keep the brew job valid.

Line 25 uses macos-latest, but the formula under test is arm-only on macOS (on_arm in Formula/clauster.rb). If macos-latest ever resolves to Intel, this smoke test fails for infra reasons rather than formula correctness. Pin a known ARM label (for example, macos-14) for deterministic architecture.

Suggested change
-    runs-on: macos-latest
+    runs-on: macos-14
📝 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: Homebrew (macos-arm64)
runs-on: macos-latest
timeout-minutes: 20
name: Homebrew (macos-arm64)
runs-on: macos-14
timeout-minutes: 20

Comment on lines +42 to +44
clauster --version
clauster --version | grep -q '^clauster'

Copy link
Copy Markdown

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

Strengthen version assertions to catch wrong-binary regressions.

Line 43 and Line 64 only assert the output starts with clauster, which can pass even when the wrong version is installed. Since this workflow is validating 0.10.0 packaging, assert the exact expected version banner.

Suggested change
-          clauster --version | grep -q '^clauster'
+          clauster --version | grep -q '^clauster 0\.10\.0$'
...
-          ./result/bin/clauster --version | grep -q '^clauster'
+          ./result/bin/clauster --version | grep -q '^clauster 0\.10\.0$'

Also applies to: 63-65

run: |
set -euo pipefail
# Single-user install (no daemon/systemd needed in CI).
sh <(curl -L https://nixos.org/nix/install) --no-daemon

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the full context of the workflow file
cat -n .github/workflows/brew-nix-smoke.yml

Repository: schubydoo/clauster

Length of output: 3015


🏁 Script executed:

# Search for Nix-related GitHub Actions or official installers
rg -i "nix.*action|nixos.*action" --type yaml

Repository: schubydoo/clauster

Length of output: 44


🌐 Web query:

Nix GitHub Actions official installer pinned commit

💡 Result:

There is no single "official" installer for Nix on GitHub Actions, as multiple community-maintained actions exist [1][2][3][4]. Consequently, there is no single "pinned commit" that covers all users [5][3]. Instead, you should choose a specific action and pin it to a commit hash or release tag for security and reproducibility [3][6]. Commonly used Nix installers for GitHub Actions include: 1. cachix/install-nix-action: This is a widely used community action [4][7]. While it uses semantic versioning (e.g., v31, v31.9.1) for releases, you can pin it to a specific git commit hash in your workflow file to ensure absolute reproducibility [5][6]. Example usage: - uses: cachix/install-nix-action@2126ae7 2. DeterminateSystems/determinate-nix-action: This action is maintained by Determinate Systems and supports explicit version pinning for their installer [3][8]. It is automatically tagged for every release [3]. Example usage: - uses: DeterminateSystems/determinate-nix-action@v3.17.2 3. NixOS/nix-installer-action: An experimental action associated with the official NixOS organization [1]. It is currently in a testing phase and primarily tracks the main branch [1]. To pin any of these actions securely: - Find the desired tag or commit on the action's GitHub repository "Releases" or "Commits" page [5][9]. - Replace the version tag (e.g., @v31) in your workflow file with the specific commit hash (e.g., @2126ae7fc54c9df00dd18f7f18754393182c73cd) [6]. - Use tools like Dependabot to manage and update these pinned versions automatically [2][10].

Citations:


🌐 Web query:

curl sh pattern security GitHub Actions CI environment risks

💡 Result:

The curl | sh (or similar pipe-to-shell) pattern is considered a high-risk practice in CI/CD environments, including GitHub Actions, due to its susceptibility to supply chain attacks and lack of integrity verification [1][2][3]. Primary Risks: 1. Arbitrary Code Execution: The pattern downloads and executes remote code in a single, unverified step. If the host, CDN, DNS, or network path is compromised, an attacker can serve malicious code that executes with the full privileges of the CI runner [1][2][3]. 2. Secret Exfiltration: Because the script runs in the CI environment, it has access to workflow secrets, environment variables, and tokens. Compromised scripts are frequently used to exfiltrate these credentials [3]. 3. Man-in-the-Middle (MitM) Attacks: Piping directly to a shell bypasses the ability to inspect the script's contents or verify cryptographic signatures before execution [3]. 4. Unintended Content: Remote servers may intermittently fail or serve unexpected content (e.g., HTML error pages) that can cause unpredictable or dangerous behavior when piped into a shell [4]. Recommended Secure Practices: 1. Use Vetted Actions: Replace ad-hoc shell scripts with official, well-maintained GitHub Actions. When using third-party actions, pin them to a specific commit SHA rather than a tag or branch [1][2]. 2. Download and Verify: If a script must be fetched, split the process into distinct steps: - Download the file to a local path [1][2]. - Verify its integrity using cryptographic hashes (e.g., SHA-256) or GPG signatures provided by the distributor [1][5][3]. - Execute the verified file only after the integrity check passes [1][2][3]. 3. Version Control: Whenever possible, vendor the script into your repository under version control to ensure the code remains immutable and audit-trailed [5]. 4. Use Security Tools: Employ automated security scanners (e.g., Semgrep, Poutine, or Pipefort) to detect and block unsafe pipe-to-shell patterns in your pipeline definitions [6][2][4]. Runtime security tools (e.g., kntrl-action) can further monitor and block suspicious process chains or unauthorized network access during workflow execution [7]. The critical takeaway is to treat all remote, unverified content as untrusted code [3][4]. Always verify the provenance and integrity of external scripts before execution [1][3].

Citations:


Replace inline shell installer with pinned GitHub Action for Nix.

Line 57 executes curl ... | sh to install Nix directly in the workflow. Standard practice is to use a vetted, commit-SHA pinned GitHub Action instead:

  • cachix/install-nix-action (widely used, pinnable to commit hash)
  • DeterminateSystems/determinate-nix-action (official, supports version pinning)
  • NixOS/nix-installer-action (experimental, official NixOS action)

This eliminates supply-chain attack surface, prevents silent failures from transient fetch issues, and aligns with how this workflow already pins actions/checkout to a commit SHA.

schubydoo added a commit that referenced this pull request Jun 15, 2026
Generalize the Scoop-only manifest bump into a single post-release path
that keeps all three in-repo package-manager manifests in lockstep with
published releases: the Scoop bucket (bucket/clauster.json), the Homebrew
formula (Formula/clauster.rb), and the Nix flake (flake.nix). None of
them auto-update on their own for a self-hosted bucket / tap / flake.

- Rename scoop-bump.yml -> packaging-bump.yml; the regen step now calls a
  tested helper instead of inline jq, and opens a packaging-bump/<tag> PR.
- scripts/bump_packaging.py: regenerate version/url/hash for whichever
  manifests exist from the release's SHA256SUMS. Fails closed (missing
  asset, or a stale non-target version surviving) and is two-pass
  (validate all, then write) so an aborted run leaves no half-rewritten
  tree. Stdlib-only; absent manifests are skipped, so this can merge
  ahead of the Homebrew/Nix packaging (#285) and light them up later.
- tests/test_bump_packaging.py: happy path, idempotency, missing-file
  skip, both fail-closed paths, two-pass atomicity, bad-version arg.

Re-audited by clauster-ci-auditor (ref-gating, pinning, injection, token
scope, fail-closed, branch-push safety, helper path/regex safety — clean).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@schubydoo

Copy link
Copy Markdown
Owner Author

Superseded by #287, which consolidates all installation-methods work (one-line installers, Scoop/Homebrew/Nix, the extra arch binaries, and the release auto-bump) into a single PR to remove the inter-PR merge ordering. This branch's content is preserved there. Closing in favor of #287.

@schubydoo schubydoo closed this Jun 15, 2026
@schubydoo
schubydoo deleted the experiment/brew-nix branch June 15, 2026 20:39
schubydoo added a commit that referenced this pull request Jun 15, 2026
…-bump (#287)

Consolidates the installation-methods work — previously split across
#283, #284, #285, #286 — into a single PR. The split forced a confusing
merge order and inter-PR dependencies (the auto-bump "skips absent
manifests," "rebase after each," etc.); landing everything together
makes all the manifests present at once and the dependencies vanish.

**Supersedes and closes #283, #284, #285, #286.**

## What ships

**One-line installers** (#283)
- `install.sh` (`curl … | bash`) — resolves the latest release,
downloads the arch-matched standalone binary, verifies SHA-256, installs
to `~/.local/bin`. Degrades to a `pip`/`uvx` fallback on a platform with
no binary (gated on the release `SHA256SUMS`, not a hardcoded arch
list).
- `install.ps1` (`irm … | iex`) — Windows equivalent; signals failure
via `$global:LASTEXITCODE` (no `exit`, for iex safety).

**Package managers**
- **Scoop** (#283) — `bucket/clauster.json`; the repo doubles as a Scoop
bucket.
- **Homebrew** (#285) — `Formula/clauster.rb` (macOS arm64 + Linux
x86_64).
- **Nix** (#285) — `flake.nix` + `flake.lock` (`nix run .#clauster`).

**More release binaries** (#284)
- Adds `linux-arm64` (`ubuntu-24.04-arm`) and `macos-x86_64`
(`macos-15-intel`) build legs → 5 targets; matrix↔publish-gate parity
preserved.

**Release auto-bump** (#286)
- `.github/workflows/packaging-bump.yml` + `scripts/bump_packaging.py`:
on `release: published`, regenerate the version/url/hash of whichever of
the Scoop / Homebrew / Nix manifests exist, from the release's signed
`SHA256SUMS`, and open a PR. Never commits to `main`. release-please
can't do this (it runs pre-build, before checksums exist). Helper is
stdlib-only, fails closed, two-pass (no partial writes); 6 unit tests in
`tests/test_bump_packaging.py`.

**CI**
- `installer-smoke.yml` now smoke-tests **all** install methods on real
runners — install.sh (Linux/macOS), install.ps1 (Windows), Homebrew
(macOS), Nix (Linux). `brew-nix-smoke.yml` is folded in and removed.

## Trust model

Installers and the auto-bump trust the release `SHA256SUMS` over GitHub
TLS — the same root throughout. Binaries are Sigstore-signed +
SLSA-attested; the manual verify path (`cosign` / `gh attestation
verify`) is documented.

## Maintainer follow-ups (not blocking this PR)

1. **Create the `schubydoo/homebrew-clauster` tap repo** — Homebrew
rejects bare-path formula installs; real `brew install` needs the
formula in a tap. (CI validates via an ephemeral local tap in the
meantime.)
2. **Homebrew/Nix user-facing docs** in `docs/installation.md` —
deferred until the tap exists (Scoop docs are already included).

## Review provenance

Each piece was reviewed in its original PR: #283 CodeRabbit-approved +
`clauster-reviewer`; #284 CodeRabbit-approved + `clauster-ci-auditor`,
5/5 legs green; #285 runner-validated; #286 `clauster-ci-auditor`
(twice). The smoke consolidation is a verbatim job move. Full suite
green (1077 passed, 97.67% coverage). CodeRabbit will re-review the
combined diff.

**Release-path PR → HOLD for maintainer merge.** Draft until ready.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Schuby <12485317+schubydoo@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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