# `Tinfoil.Homebrew`
[🔗](https://github.com/joshrotenberg/tinfoil/blob/v0.2.22/lib/tinfoil/homebrew.ex#L1)

Render a Homebrew formula from release artifacts and push it to a
tap repo. Earlier tinfoil versions shelled out to a generated
`scripts/update-homebrew.sh`; this module replaces it so the whole
release lifecycle stays in Elixir and is unit-testable.

Called from `mix tinfoil.homebrew`, which the generated workflow
invokes on a separate CI job after the GitHub Release publish step
succeeds. See `Tinfoil.Publish` for the release-assets counterpart.

## Inputs at CI time

  * `artifacts/` directory containing the release tarballs plus
    `.sha256` sidecar files (downloaded from the build matrix)
  * `GITHUB_REF_NAME` env var holding the tag that triggered the
    release (e.g. `v1.2.3`)
  * Auth material depending on `config.homebrew.auth`:
    * `:token`      — `HOMEBREW_TAP_TOKEN` env with PAT
    * `:deploy_key` — SSH agent configured with a deploy key for
      the tap repo (typically via `webfactory/ssh-agent`)

## What the module does

1. Reads `.sha256` sidecars for every target's archive and builds a
   map of `target => sha256`.
2. Renders the user's checked-in `.tinfoil/formula.rb.eex` (falling
   back to the default template) with real version + SHAs.
3. Clones the tap repo into a temp dir, writes the rendered formula
   at `Formula/<formula_name>.rb`, commits and pushes.

The git operations shell out to `git` — no extra deps. Authentication
is left to the environment: HTTPS with a token-bearing URL, or SSH
via an already-running `ssh-agent`.

# `error`

```elixir
@type error() ::
  :homebrew_not_enabled
  | :missing_tag
  | :missing_homebrew_tap_token
  | :malformed_sha_sidecar
  | {:missing_sha_sidecar, atom(), Path.t()}
  | {:missing_formula_template, Path.t()}
  | {:formula_template_read_error, Path.t(), term()}
  | {:git_failed, [String.t()], non_neg_integer(), String.t()}
```

Known error atoms returned by `publish/2`. Catch-all is
`{:error, term()}` for unexpected failures.

  * `:homebrew_not_enabled` -- config has `homebrew.enabled: false`
  * `:missing_tag` -- no tag given and `GITHUB_REF_NAME` is unset
  * `:missing_homebrew_tap_token` -- `auth: :token` but
    `HOMEBREW_TAP_TOKEN` env var is missing or empty
  * `{:missing_sha_sidecar, target, path}` -- a `.sha256` sidecar
    file couldn't be read from `input_dir`
  * `:malformed_sha_sidecar` -- a `.sha256` sidecar didn't contain a
    64-hex-character digest
  * `{:missing_formula_template, path}` -- the EEx formula template
    couldn't be found (default `.tinfoil/formula.rb.eex`)
  * `{:formula_template_read_error, path, reason}` -- template read
    failed for a reason other than `:enoent`
  * `{:git_failed, args, exit_status, output}` -- a `git` subprocess
    call returned non-zero

# `opts`

```elixir
@type opts() :: [
  input_dir: Path.t(),
  tag: String.t() | nil,
  formula_template: Path.t() | nil,
  tap_dir: Path.t() | nil,
  dry_run: boolean() | nil,
  git: module()
]
```

# `preview`

```elixir
@type preview() :: %{
  dry_run: true,
  tap: String.t(),
  auth: :token | :deploy_key,
  clone_url: String.t(),
  formula_name: String.t(),
  formula: String.t(),
  commit_message: String.t()
}
```

# `result`

```elixir
@type result() ::
  %{pushed: boolean(), formula_path: Path.t(), commit_sha: String.t() | nil}
  | %{pushed: false, skipped: :prerelease}
```

# `publish`

```elixir
@spec publish(Tinfoil.Config.t(), opts()) ::
  {:ok, result()} | {:ok, preview()} | {:error, error() | term()}
```

Render the formula and push it to the tap repo.

Options (all optional, sane defaults):

  * `:input_dir`        — directory with archives + sha256 sidecars
    (default `"artifacts"`)
  * `:tag`              — release tag, usually taken from
    `GITHUB_REF_NAME` env
  * `:formula_template` — path to the EEx formula template
    (default `".tinfoil/formula.rb.eex"`)
  * `:tap_dir`          — temp dir to clone into (default a new mktemp)
  * `:git`              — module implementing the `git` callbacks
    (see `Tinfoil.Homebrew.Git`); injected for testing

---

*Consult [api-reference.md](api-reference.md) for complete listing*
