TL;DR

  • Merge the Release Please PR on main.
  • Wait for ci.yml to finish green on the exact release SHA.
  • Let the Release workflow run Run release preflight, Verify version alignment, and Check whether Hex.pm release already exists.
  • If the version is already live, recovery reruns skip publish and continue to public verification.
  • Use mix hex.publish --revert VERSION for in-window rollback; use retire plus a fix release after the window.

This maintainer runbook documents the workflow that shipped Rindle to Hex.pm on 2026-04-29. 0.1.0 through 0.1.3 were pipeline shakedown iterations during that first publish window. Treat 0.1.4 as the first recommended pin. When a release needs existing-adopter guidance, summarize the change here and deep-link to upgrading.md instead of duplicating the full procedure in the release runbook.

First Public Release History

Start from a reviewed Release Please PR on main, not from a manual tag push. The first publish flow converted @version "0.1.0-dev" into 0.1.0, created v0.1.0, and then continued through follow-up release fixes until 0.1.4 closed the publish window.

One-Time Publish Prerequisites

Run these checks outside CI:

mix hex.user whoami
mix hex.owner list rindle
  • Confirm the current maintainer identity with mix hex.user whoami.
  • Confirm package-name availability before the inaugural publish of a new package.
  • Configure the release GitHub Actions environment secret HEX_API_KEY.
  • Keep maintainer identity and package-name availability checks outside scripts/release_preflight.sh and outside secret-gated automation.
  • Confirm the initial owner after first publish, then add additional owners with mix hex.owner add rindle USERNAME.

Exact-SHA Release Proof

Local preflight is diagnostic preparation, not authoritative release proof. Authoritative signoff requires a green GitHub Actions run on the exact release-candidate SHA. Run bash scripts/release_preflight.sh and local mix hex.build --unpack while iterating, then rely on the exact-SHA ci.yml run selected by Release Please or workflow_dispatch recovery.

Do not substitute a green branch head, a rerun on a different commit, or a local-only transcript for this proof. The Package Consumer Proof Matrix + Release Preflight lane in ci.yml is part of the exact-SHA boundary.

Package Metadata Review

Build the package exactly as shipped before every release attempt:

bash scripts/release_preflight.sh

Check the unpacked hex_metadata.config and package contents for:

  • rindle
  • the intended release version
  • MIT
  • GitHub
  • CHANGELOG.md
  • guides/release_publish.md
  • mix docs --warnings-as-errors

Review shipped metadata, not just repo source. The packaged metadata review is still diagnostic until the same commit is green in GitHub Actions CI.

Routine Releases

Run this sequence on every release after the inaugural publish:

  1. Merge the Release Please PR on main.
  2. Wait for the Release workflow to complete these step names in order:
    • Release Please
    • Wait for CI to finish green on release SHA
    • Run release preflight
    • Verify version alignment
    • Check whether Hex.pm release already exists
    • Dry run Hex publish
    • Publish to Hex.pm (live)
    • Wait for Hex.pm index (post-publish)
    • Verify HexDocs reachability
    • Verify public Hex.pm artifact
  3. Use the recovery-only dispatch lane only when you must rerun the trusted path from an exact immutable ref.

Release Workflow Contract

The repository workflow runs these shipped commands:

bash scripts/release_preflight.sh
bash scripts/assert_version_match.sh
bash scripts/hex_release_exists.sh
mix hex.publish --dry-run --yes
mix hex.publish --yes
curl --fail --location --silent --show-error "https://hexdocs.pm/rindle/$VERSION"
bash scripts/public_smoke.sh "$VERSION"

The repo's package-consumer lane shifts the release contract left before publish time. The release workflow waits for ci.yml on the exact release SHA to finish green before entering the protected publish lane. After live publish, Wait for Hex.pm index (post-publish) and Verify HexDocs reachability poll for up to 5 minutes with 15-second retries. The HexDocs probe follows redirects to the final 2xx response for https://hexdocs.pm/rindle/$VERSION. Verify public Hex.pm artifact then proves the package from a fresh runner with HEX_API_KEY cleared.

Do not use --replace in CI. If you need mix hex.publish --replace --yes, run it locally during the grace window with deliberate human review. For docs-only repair, prefer mix hex.docs publish.

Recovery Workflow Contract

workflow_dispatch in .github/workflows/release.yml is recovery-only. Supply:

  • recovery_reason
  • recovery_ref
  • an exact existing tag or a 40-character commit SHA

Recovery reruns the exact-SHA gate, preflight, version alignment, idempotency probe, publish lane, and public verification. If the target version is already live on Hex.pm, the workflow skips both publish steps, writes a skip summary, and still runs public verification.

Post-Publish Follow-Up

After the first publish:

  1. Run mix hex.owner list rindle.
  2. Add additional owners with mix hex.owner add rindle USERNAME.

After every publish:

  1. Confirm the Release workflow finished successfully.
  2. Confirm Verify HexDocs reachability passed for https://hexdocs.pm/rindle/$VERSION.
  3. Confirm Verify public Hex.pm artifact passed.
  4. Update this runbook when workflow behavior changes.

Rollback and Revert

Use this quick decision table first:

SituationCommandNotes
Bad release within revert windowmix hex.publish --revert VERSION24h for the first publish, 1h for subsequent releases
Runtime breakage after revert windowmix hex.retire rindle VERSION REASON --message "..."Reasons: renamed, deprecated, security, invalid, other
Docs broken, code finemix hex.docs publishRepublish docs without mutating package version
Window closed and code brokenretire bad version, ship fix patch releaseLockfiles still install the bad version; publish the fix immediately

Runbook rules:

  • mix hex.publish --revert VERSION is the canonical revert command.
  • mix hex.revert rindle VERSION is wrong legacy wording. Do not use it.
  • mix hex.retire messages are limited to 140 characters.
  • mix hex.retire --unretire removes a retirement marker.
  • Retirement warns new resolvers but lockfiles still install the bad version.

Window-closed fallback:

  1. Run mix hex.retire rindle VERSION REASON --message "...".
  2. Ship the fix release immediately.
  3. Update the GitHub Release note with the adopter advisory.

Adopter advisory template:

Adopter advisory: VERSION is retired due to REASON. Upgrade to FIX_VERSION immediately. Existing lockfiles can still install VERSION until you update your dependency resolution.

Use this commit title when retire-and-patch fires:

fix(release): retire BAD_VERSION, ship FIX_VERSION

Use this GitHub Release title format:

rindle FIX_VERSION - replacement for retired BAD_VERSION

Footguns & Gotchas

  • Hex.pm versions are immutable once the revert window closes.
  • Reverting the last release removes the package entry for that version.
  • mix hex.owner add is post-publish-only for the package owner set.
  • Hex tarballs have practical 8MB and hard 64MB size pressure.
  • Git dependencies do not prove a Hex.pm release path.
  • Conventional commits and Release Please drive the release train.
  • The autorelease: pending label is part of the release-please loop.
  • Manual tag pushes fight the trusted workflow contract.
  • mix docs --warnings-as-errors is a publish gate, not optional cleanup.
  • Owner key and API key are different concerns; do not confuse them.
  • Component tags and simple vX.Y.Z tags are different release-please shapes.
  • Trusted current tooling is not the same thing as the frozen release source tree.

Appendix A: Deviation Log

DateChangeEvidence
2026-04-30Added idempotent recovery reruns so workflow_dispatch skips publish when the target version is already live and still runs public verification.Phase 16 recovery fix on current branch
2026-04-29Hardened publish preflight after first live publish friction.d5c21ad, 65728e5
2026-04-29Locked current tooling against frozen source via git worktree recovery flow.71a0f99
2026-04-29Moved public verification to the public package path and refreshed smoke discipline.6dd0d54
2026-04-29Fixed release version parsing drift in the workflow.a7efefd

Appendix B: Architecture Note

The release flow uses current tooling and frozen source:

  • main HEAD supplies the trusted workflow and scripts.
  • recovery_ref selects the immutable source commit or tag.
  • git worktree materializes that frozen source tree under the current tooling.
  • The workflow runs preflight, version checks, idempotency probe, publish, and public verification against that split model.