A tagged push produces a GitHub Release with every built archive attached plus a combined checksums-sha256.txt. Optional surfaces layered on top: a curl/PowerShell installer, a Homebrew formula, a Scoop manifest. All configured in the :tinfoil keyword.

GitHub Release

Generated unconditionally. mix tinfoil.publish creates the release, attaches every archive, and adds checksums-sha256.txt. Tags matching prerelease_pattern (default: -rc, -beta, -alpha) are marked as prereleases.

Installer scripts

Set installer: [enabled: true] to ship two scripts with every release:

  • scripts/install.sh -- curl | sh for Unix.

  • scripts/install.ps1 -- iex (irm ...) for Windows.

Both resolve the latest release tag from the GitHub API, download the right asset for the detected OS/arch, verify it against the combined checksums-sha256.txt, and install to a sensible default directory (configurable via flags at install time).

Configure the default destination:

installer: [
  enabled: true,
  install_dir: "~/.local/bin"
]

Extra files in every archive

extra_artifacts: ships additional files alongside the binary inside each archive. Bare strings use the same relative path; a source/dest map places the file at a custom location:

extra_artifacts: [
  "LICENSE",
  %{source: "man/myapp.1", dest: "share/man/man1/myapp.1"}
]

Useful for LICENSE, man pages, shell completions, or anything else a user might expect in a distribution tarball.

Homebrew

The homebrew: job needs push access to your tap repo. Two auth modes are supported.

auth: :token (default)

The workflow expects a HOMEBREW_TAP_TOKEN repo secret -- a Personal Access Token (classic or fine-grained) with contents: write on the tap repo. The mix task clones over HTTPS with the token baked into the URL.

homebrew: [
  enabled: true,
  tap: "owner/homebrew-tap"
]

auth: :deploy_key

Generate an SSH key pair, add the public key to the tap repo's deploy keys (with write access), and set the private key as the HOMEBREW_TAP_DEPLOY_KEY secret on the CLI repo. The generated workflow installs webfactory/ssh-agent before running mix tinfoil.homebrew, which clones over SSH.

homebrew: [
  enabled: true,
  tap: "owner/homebrew-tap",
  auth: :deploy_key
]

Deploy keys are scoped to a single repo and never expire, which is the main reason to prefer them over PATs.

Secret name overrides

If your secret is named differently, override the name with homebrew: [token_secret: "YOUR_NAME"] or homebrew: [deploy_key_secret: "YOUR_NAME"]. The env var the mix task reads is fixed; only the secret reference in the workflow is configurable.

Linuxbrew

The generated formula's on_linux block makes it work under Linuxbrew too, no separate config needed. Linux users can run brew install owner/tap/myapp the same way macOS users do and will pull the matching linux_x86_64 or linux_arm64 tarball.

Scoop (Windows)

Symmetric counterpart to Homebrew for Windows users. When scoop: [enabled: true] and you have :windows_x86_64 in :targets, every release pushes a Scoop manifest to the configured bucket repo:

scoop: [
  enabled: true,
  bucket: "owner/scoop-bucket",
  auth: :token  # or :deploy_key
]

Create the bucket repo on GitHub (any name works; the convention is scoop-<something>), grant push access via a PAT named SCOOP_BUCKET_TOKEN or an SSH deploy key named SCOOP_BUCKET_DEPLOY_KEY, and downstream users install with:

scoop bucket add owner https://github.com/owner/scoop-bucket
scoop install owner/my_cli

The rendered manifest includes a checkver + autoupdate block so Scoop bucket maintainers (or automated bots) can pick up new versions without tinfoil re-pushing. If you don't want that, edit the manifest in the bucket after push.

Secret name overrides work the same way as Homebrew via scoop: [token_secret: "..."] / [deploy_key_secret: "..."].

Release channels and prerelease handling

prerelease_pattern controls two things:

  • GitHub Release creation (mix tinfoil.publish) -- the release is marked as a prerelease when the tag matches.
  • Homebrew / Scoop push-skip -- the generated workflow jobs skip the publish step when the tag looks like a prerelease, so tagged prereleases don't overwrite the stable formula/manifest.

The workflow's skip condition is hardcoded to match the default pattern (-rc, -beta, -alpha). If you override prerelease_pattern to use different tokens (-dev, -nightly, -snapshot, ...), mix tinfoil.publish will respect your pattern for the release flag, but the Homebrew and Scoop jobs will still only skip the default tokens. Workarounds:

  • Keep a superset pattern in prerelease_pattern that always includes the default tokens, or
  • Add a homebrew: [enabled: false] / scoop: [enabled: false] environment-gated override (the release still gets published; the package managers just won't auto-update), or
  • Hand-edit .github/workflows/release.yml after mix tinfoil.generate to extend the if: expression.

Unifying this into a single configurable skip list is tracked; open a PR if you need it before we get there.

Attestations

Every uploaded artifact is attested by GitHub Actions build provenance by default (attestations: true). End users can verify with:

gh attestation verify my_cli-0.1.0-aarch64-apple-darwin.tar.gz \
  --repo owner/my_cli

Opting out (attestations: false) drops the id-token: write and attestations: write permissions from the generated workflow.