Skip to content

feat: Homebrew tap and verified install script - #8

Open
RogerCecilpot wants to merge 1 commit into
feat/253-release-pipelinefrom
feat/258-homebrew-install-script
Open

RogerCecilpot wants to merge 1 commit into
feat/253-release-pipelinefrom
feat/258-homebrew-install-script

Conversation

@RogerCecilpot

Copy link
Copy Markdown

Closes devlyft/devlyft#258.

Stacked on #4 (release pipeline), which is stacked on #2 (scaffold). Retarget as those merge.

What

  • Homebrew cask via goreleaser homebrew_casks. brews formulae are deprecated in goreleaser v2.10+, so this uses casks. The release pushes Casks/devlyft.rb to devlyft/homebrew-tap, giving brew install devlyft/tap/devlyft on macOS and Linux.
    • The upload is skipped when HOMEBREW_TAP_GITHUB_TOKEN is unset, so releases keep working before the tap exists.
    • Completions come from generate_completions_from_executable, which runs devlyft completion <shell> at install time for bash, zsh and fish. No completion files go in the archive.
    • A macOS postflight step clears com.apple.quarantine, because the binary isn't notarized.
  • install.sh (POSIX sh, passes shellcheck), for Linux and macOS:
    • Detects OS and architecture, and installs the latest release or DEVLYFT_VERSION.
    • Downloads the archive and the checksum file, and verifies the sha256.
    • If cosign is installed, verifies the keyless signature bundle on the checksum file using the release workflow's identity regexp and the GitHub OIDC issuer. It fails closed if verification fails or the bundle is missing.
    • Without cosign it warns, or fails when DEVLYFT_REQUIRE_SIGNATURE=1 is set.
    • Installs into DEVLYFT_INSTALL_DIR, otherwise /usr/local/bin if writable, otherwise ~/.local/bin. It never uses sudo and replaces the binary with an atomic rename.
    • DEVLYFT_DOWNLOAD_BASE points the script at a mirror or file://…/dist for testing.
  • CI: the release-snapshot job now runs shellcheck, installs the snapshot with install.sh, and checks that a tampered archive is rejected.
  • Docs: docs/releasing.md (tap setup, how the install script works) and an Install section in the README.

Verified locally

  • goreleaser check passes with no deprecations. A snapshot build generates the cask with darwin and linux blocks for arm and intel, the postflight xattr step and the completions stanza.
  • install.sh against the snapshot dist/:
    • A good install works, and the installed binary reports the snapshot version.
    • A tampered archive fails with a checksum mismatch and installs nothing.
    • DEVLYFT_REQUIRE_SIGNATURE=1 without cosign fails.
    • With cosign v3.1.4 installed and no bundle present, the install fails closed.
    • DEVLYFT_DOWNLOAD_BASE without a version fails, and an unknown version fails cleanly.
  • shellcheck (koalaman/shellcheck:stable), actionlint, make lint and make test pass.

Not verified yet

  • A successful cosign verification against a real signed release, and the actual push to the tap. Both need the first tagged release after this merges.
  • brew install on a real machine.

Merge order

The cask runs devlyft completion, added in #3. Merge #3 before the first release that publishes the cask, or brew's completion step will fail. The snapshot build doesn't depend on it.

Needs creating before the first Homebrew release

  1. Tap repository: a public repo devlyft/homebrew-tap with a Casks/ directory.
  2. Tap credential: a fine-grained PAT owned by a bot account, or a GitHub App, with Contents: read and write on devlyft/homebrew-tap only.
  3. Secret: store that credential as HOMEBREW_TAP_GITHUB_TOKEN in devlyft/cli, ideally in a protected environment used only by release.yml.

🤖 Generated with Claude Code

@RogerCecilpot
RogerCecilpot force-pushed the feat/253-release-pipeline branch from 9bb3978 to 14c1dc3 Compare October 25, 2026 12:56
@RogerCecilpot
RogerCecilpot force-pushed the feat/258-homebrew-install-script branch from 2872a9c to 226397c Compare October 25, 2026 12:56
goreleaser now builds a Homebrew cask (casks replace the deprecated brews
formulae) and pushes it to devlyft/homebrew-tap when
HOMEBREW_TAP_GITHUB_TOKEN is set; otherwise the upload is skipped. The
cask installs completions via `devlyft completion` and clears the macOS
quarantine attribute.

install.sh installs on Linux and macOS: it verifies the archive's sha256
against the release checksum file and, when cosign is installed, the
keyless signature on that file, failing closed on any mismatch. It never
uses sudo. CI runs shellcheck and installs the snapshot build with it,
including a tampered-archive case.

Refs devlyft/devlyft#258

Co-Authored-By: Claude Opus 5.5 <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