Skip to content

Commit dd4dc16

Browse files
committed
chore(release): prep v1.0.1 homebrew tap flow
1 parent d17ef17 commit dd4dc16

File tree

10 files changed

+1075
-4
lines changed

10 files changed

+1075
-4
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
name: Homebrew Tap PR
2+
3+
on:
4+
release:
5+
types:
6+
- published
7+
workflow_dispatch:
8+
inputs:
9+
tag:
10+
description: "Existing release tag to sync to tap (e.g. v1.0.0)"
11+
required: true
12+
type: string
13+
14+
concurrency:
15+
group: homebrew-tap-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.release.tag_name || inputs.tag || github.run_id }}
16+
cancel-in-progress: false
17+
18+
jobs:
19+
update-tap:
20+
name: Update homebrew tap formula
21+
runs-on: ubuntu-latest
22+
permissions:
23+
contents: read
24+
env:
25+
ENVGEN_HINTS: "0"
26+
HOMEBREW_TAP_REPO: smorinlabs/homebrew-tap
27+
TAP_REPO_DIR: ${{ github.workspace }}/homebrew-tap
28+
HOMEBREW_SOURCE_JSON: ${{ github.workspace }}/.homebrew/source.json
29+
steps:
30+
- name: Resolve tag
31+
id: tag
32+
shell: bash
33+
run: |
34+
if [[ "${{ github.event_name }}" == "release" ]]; then
35+
TAG="${{ github.event.release.tag_name }}"
36+
else
37+
TAG="${{ inputs.tag }}"
38+
fi
39+
40+
if [[ -z "$TAG" ]]; then
41+
echo "ERROR: missing release tag"
42+
exit 1
43+
fi
44+
45+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
46+
47+
- name: Checkout envgen
48+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
49+
with:
50+
fetch-depth: 0
51+
ref: ${{ steps.tag.outputs.tag }}
52+
53+
- name: Validate tag matches Cargo.toml version
54+
id: version
55+
shell: bash
56+
run: |
57+
VERSION="$(python3 scripts/release/validate_tag.py --tag "${{ steps.tag.outputs.tag }}")"
58+
echo "Validated tag ${{ steps.tag.outputs.tag }} for Cargo.toml version $VERSION"
59+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
60+
61+
- name: Resolve source tarball metadata
62+
run: |
63+
make homebrew-source \
64+
TAG="${{ steps.tag.outputs.tag }}" \
65+
HOMEBREW_SOURCE_JSON="$HOMEBREW_SOURCE_JSON"
66+
67+
- name: Checkout tap repository
68+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
69+
with:
70+
repository: smorinlabs/homebrew-tap
71+
token: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
72+
path: homebrew-tap
73+
fetch-depth: 0
74+
75+
- name: Configure git author
76+
working-directory: ${{ env.TAP_REPO_DIR }}
77+
run: |
78+
git config user.name "github-actions[bot]"
79+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
80+
81+
- name: Sync formula
82+
run: |
83+
make homebrew-sync-formula \
84+
TAG="${{ steps.tag.outputs.tag }}" \
85+
TAP_REPO_DIR="$TAP_REPO_DIR" \
86+
HOMEBREW_SOURCE_JSON="$HOMEBREW_SOURCE_JSON"
87+
88+
- name: Verify formula
89+
run: |
90+
make homebrew-verify-formula \
91+
TAG="${{ steps.tag.outputs.tag }}" \
92+
TAP_REPO_DIR="$TAP_REPO_DIR"
93+
94+
- name: Open or update tap PR
95+
env:
96+
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
97+
run: |
98+
make homebrew-open-tap-pr \
99+
TAG="${{ steps.tag.outputs.tag }}" \
100+
TAP_REPO_DIR="$TAP_REPO_DIR" \
101+
HOMEBREW_TAP_REPO="$HOMEBREW_TAP_REPO"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ __pycache__/
66
.uv-cache
77
.uv-tools
88
.bin
9+
.homebrew
910
.DS_Store
1011
.env.local
1112
.env.dev

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919

2020
### Security
2121

22+
## [1.0.1] - 2026-02-16
23+
24+
### Added
25+
26+
### Changed
27+
28+
- Added a separate `homebrew-tap` release flow: `make homebrew-*` targets, `scripts/homebrew/tap_release.py`, and an automated tap PR workflow.
29+
30+
### Deprecated
31+
32+
### Removed
33+
34+
### Fixed
35+
36+
### Security
37+
2238
## [1.0.0] - 2026-02-16
2339

2440
### Added

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "envgen"
3-
version = "1.0.0"
3+
version = "1.0.1"
44
edition = "2021"
55
description = "Generate and validate .env files from one spec - self-documenting config, consistent across environments, secrets stay out of git."
66
readme = "README.md"

Makefile

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ YAML_FIXTURES := $(shell find tests/fixtures -type f \( -name '*.yaml' -o -name
2020
SCHEMA_ARTIFACT_VERSION := $(shell tr -d '\r\n' < SCHEMA_VERSION)
2121
SCHEMA_FILE := schemas/envgen.schema.v$(SCHEMA_ARTIFACT_VERSION).json
2222
VERSION_BUMP_SCRIPT := scripts/version_bump.py
23+
HOMEBREW_TAP_SCRIPT := scripts/homebrew/tap_release.py
24+
HOMEBREW_SOURCE_REPO ?= smorinlabs/envgen
25+
HOMEBREW_TAP_REPO ?= smorinlabs/homebrew-tap
26+
HOMEBREW_TAP_REPO_DIR ?= /Users/stevemorin/c/homebrew-tap
27+
HOMEBREW_TAP_FORMULA ?= Formula/envgen.rb
28+
HOMEBREW_SOURCE_JSON ?= $(CURDIR)/.homebrew/source-$(TAG).json
29+
TAP_REPO_DIR ?= $(HOMEBREW_TAP_REPO_DIR)
2330

2431
# ─── Build & Test ────────────────────────────────────────────────
2532

@@ -351,6 +358,97 @@ tag-schema: ## Create local schema tag schema-vA.B.C (SCHEMA_VERSION can overrid
351358
push-tag-schema: ## Push schema tag schema-vA.B.C to origin (SCHEMA_VERSION can override file-derived value)
352359
SCHEMA_VERSION="$(SCHEMA_VERSION)" python3 $(VERSION_BUMP_SCRIPT) push-tag-schema $(if $(DRY_RUN),--dry-run,)
353360

361+
# ─── Homebrew Tap Release ───────────────────────────────────────
362+
363+
.PHONY: homebrew-status
364+
homebrew-status: ## Show Homebrew release/tap status for TAG=vX.Y.Z
365+
@if [ -z "$(TAG)" ]; then \
366+
echo "ERROR: TAG is required (example: make homebrew-status TAG=v1.0.0)"; \
367+
exit 1; \
368+
fi
369+
python3 $(HOMEBREW_TAP_SCRIPT) status \
370+
--tag "$(TAG)" \
371+
--source-repo "$(HOMEBREW_SOURCE_REPO)" \
372+
--source-json "$(HOMEBREW_SOURCE_JSON)" \
373+
--tap-repo "$(HOMEBREW_TAP_REPO)" \
374+
--tap-repo-dir "$(HOMEBREW_TAP_REPO_DIR)" \
375+
--formula-path "$(HOMEBREW_TAP_FORMULA)"
376+
377+
.PHONY: homebrew-source
378+
homebrew-source: ## Download and hash source tarball for TAG=vX.Y.Z
379+
@if [ -z "$(TAG)" ]; then \
380+
echo "ERROR: TAG is required (example: make homebrew-source TAG=v1.0.0)"; \
381+
exit 1; \
382+
fi
383+
python3 $(HOMEBREW_TAP_SCRIPT) resolve-source \
384+
--tag "$(TAG)" \
385+
--source-repo "$(HOMEBREW_SOURCE_REPO)" \
386+
--out-json "$(HOMEBREW_SOURCE_JSON)"
387+
388+
.PHONY: homebrew-sync-formula
389+
homebrew-sync-formula: ## Sync tap formula from source metadata (TAG=vX.Y.Z TAP_REPO_DIR=/path/to/homebrew-tap)
390+
@if [ -z "$(TAG)" ]; then \
391+
echo "ERROR: TAG is required (example: make homebrew-sync-formula TAG=v1.0.0 TAP_REPO_DIR=/path/to/homebrew-tap)"; \
392+
exit 1; \
393+
fi
394+
@if [ -z "$(TAP_REPO_DIR)" ]; then \
395+
echo "ERROR: TAP_REPO_DIR is required"; \
396+
exit 1; \
397+
fi
398+
python3 $(HOMEBREW_TAP_SCRIPT) sync-formula \
399+
--tag "$(TAG)" \
400+
--formula-path "$(TAP_REPO_DIR)/$(HOMEBREW_TAP_FORMULA)" \
401+
--source-json "$(HOMEBREW_SOURCE_JSON)" \
402+
$(if $(DRY_RUN),--dry-run,)
403+
404+
.PHONY: homebrew-verify-formula
405+
homebrew-verify-formula: ## Run brew style/audit/install/test on tap formula (TAG=vX.Y.Z TAP_REPO_DIR=/path/to/homebrew-tap)
406+
@if [ -z "$(TAG)" ]; then \
407+
echo "ERROR: TAG is required (example: make homebrew-verify-formula TAG=v1.0.0 TAP_REPO_DIR=/path/to/homebrew-tap)"; \
408+
exit 1; \
409+
fi
410+
@if [ -z "$(TAP_REPO_DIR)" ]; then \
411+
echo "ERROR: TAP_REPO_DIR is required"; \
412+
exit 1; \
413+
fi
414+
python3 $(HOMEBREW_TAP_SCRIPT) verify-formula \
415+
--tag "$(TAG)" \
416+
--tap-repo-dir "$(TAP_REPO_DIR)" \
417+
--tap-repo "$(HOMEBREW_TAP_REPO)" \
418+
--formula-path "$(HOMEBREW_TAP_FORMULA)"
419+
420+
.PHONY: homebrew-open-tap-pr
421+
homebrew-open-tap-pr: ## Open/update tap PR for TAG=vX.Y.Z (requires GH auth; TAP_REPO_DIR=/path/to/homebrew-tap)
422+
@if [ -z "$(TAG)" ]; then \
423+
echo "ERROR: TAG is required (example: make homebrew-open-tap-pr TAG=v1.0.0 TAP_REPO_DIR=/path/to/homebrew-tap)"; \
424+
exit 1; \
425+
fi
426+
@if [ -z "$(TAP_REPO_DIR)" ]; then \
427+
echo "ERROR: TAP_REPO_DIR is required"; \
428+
exit 1; \
429+
fi
430+
python3 $(HOMEBREW_TAP_SCRIPT) open-pr \
431+
--tag "$(TAG)" \
432+
--tap-repo "$(HOMEBREW_TAP_REPO)" \
433+
--tap-repo-dir "$(TAP_REPO_DIR)" \
434+
--formula-path "$(HOMEBREW_TAP_FORMULA)" \
435+
$(if $(DRY_RUN),--dry-run,)
436+
437+
.PHONY: homebrew-release-tap
438+
homebrew-release-tap: ## End-to-end tap release flow (TAG=vX.Y.Z TAP_REPO_DIR=/path/to/homebrew-tap)
439+
@if [ -z "$(TAG)" ]; then \
440+
echo "ERROR: TAG is required (example: make homebrew-release-tap TAG=v1.0.0 TAP_REPO_DIR=/path/to/homebrew-tap)"; \
441+
exit 1; \
442+
fi
443+
@if [ -z "$(TAP_REPO_DIR)" ]; then \
444+
echo "ERROR: TAP_REPO_DIR is required"; \
445+
exit 1; \
446+
fi
447+
$(MAKE) homebrew-source TAG="$(TAG)" HOMEBREW_SOURCE_JSON="$(HOMEBREW_SOURCE_JSON)" HOMEBREW_SOURCE_REPO="$(HOMEBREW_SOURCE_REPO)"
448+
$(MAKE) homebrew-sync-formula TAG="$(TAG)" TAP_REPO_DIR="$(TAP_REPO_DIR)" HOMEBREW_SOURCE_JSON="$(HOMEBREW_SOURCE_JSON)" HOMEBREW_TAP_FORMULA="$(HOMEBREW_TAP_FORMULA)" $(if $(DRY_RUN),DRY_RUN=$(DRY_RUN),)
449+
$(MAKE) homebrew-verify-formula TAG="$(TAG)" TAP_REPO_DIR="$(TAP_REPO_DIR)" HOMEBREW_TAP_FORMULA="$(HOMEBREW_TAP_FORMULA)"
450+
$(MAKE) homebrew-open-tap-pr TAG="$(TAG)" TAP_REPO_DIR="$(TAP_REPO_DIR)" HOMEBREW_TAP_REPO="$(HOMEBREW_TAP_REPO)" HOMEBREW_TAP_FORMULA="$(HOMEBREW_TAP_FORMULA)" $(if $(DRY_RUN),DRY_RUN=$(DRY_RUN),)
451+
354452
# ─── Commit Message ──────────────────────────────────────────────
355453

356454
CLAUDE ?= claude

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,16 @@ Variables to resolve:
7777

7878
## Install
7979

80-
### Homebrew (homebrew-core)
80+
### Homebrew (personal tap)
8181

8282
```bash
83+
brew tap smorinlabs/tap
8384
brew install envgen
8485
```
8586

87+
This project currently ships through the `smorinlabs/tap` formula stream. `homebrew-core`
88+
support is a future track and is intentionally non-blocking for releases.
89+
8690
### Cargo
8791

8892
```bash

RELEASING.md

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Releasing `envgen`
22

3-
This repo has two independent release streams:
3+
This repo has three independent release streams:
44

55
- Crate release stream:
66
- Version source: `Cargo.toml` (`[package].version`)
@@ -13,6 +13,42 @@ This repo has two independent release streams:
1313
- Changelog: `SCHEMA_CHANGELOG.md`
1414
- Tags: `schema-vA.B.C`
1515
- Automation: no publish workflow is triggered by schema tags
16+
- Homebrew tap sync stream:
17+
- Source tag: crate tag `vX.Y.Z` from `smorinlabs/envgen`
18+
- Formula host repo: `smorinlabs/homebrew-tap`
19+
- Formula path: `Formula/envgen.rb`
20+
- Automation: `.github/workflows/homebrew-tap-pr.yml` (opens/updates PR in tap repo)
21+
22+
## Homebrew repo ownership
23+
24+
- `smorinlabs/envgen` owns:
25+
- release tag (`vX.Y.Z`) creation
26+
- source tarball URL + SHA256 resolution
27+
- automation code (`scripts/homebrew/tap_release.py`, `make homebrew-*`)
28+
- workflow that opens/updates tap PRs (`.github/workflows/homebrew-tap-pr.yml`)
29+
- `smorinlabs/homebrew-tap` owns:
30+
- formula files (`Formula/*.rb`)
31+
- PR review/merge policy for tap updates
32+
- optional tap-native CI
33+
- `Homebrew/homebrew-core` is future/optional:
34+
- no required release gate for `envgen`
35+
- existing `release.yml` `homebrew-core` job remains non-blocking until formula exists upstream
36+
37+
## GitHub source tarball contract (for Homebrew formulas)
38+
39+
For release tag `vX.Y.Z`, the formula source URL is:
40+
41+
- `https://github.com/smorinlabs/envgen/archive/refs/tags/vX.Y.Z.tar.gz`
42+
43+
Behavior:
44+
45+
- GitHub serves this as a redirect (`302`) to codeload, then `200` when the tag exists.
46+
- This archive is generated from the git tag; it is not uploaded by release assets.
47+
- Formula `sha256` must be computed from this tarball content.
48+
49+
Current verified example:
50+
51+
- `v1.0.0` (published `2026-02-16T20:11:01Z`) resolves successfully via the URL above.
1652

1753
## One-time setup (crates.io trusted publishing)
1854

@@ -55,6 +91,12 @@ This mapping must match the publish job in `.github/workflows/release.yml` exact
5591
- `make push-tag-crate`
5692
- `make tag-schema`
5793
- `make push-tag-schema`
94+
- `make homebrew-status TAG=vX.Y.Z`
95+
- `make homebrew-source TAG=vX.Y.Z`
96+
- `make homebrew-sync-formula TAG=vX.Y.Z TAP_REPO_DIR=/path/to/homebrew-tap`
97+
- `make homebrew-verify-formula TAG=vX.Y.Z TAP_REPO_DIR=/path/to/homebrew-tap`
98+
- `make homebrew-open-tap-pr TAG=vX.Y.Z TAP_REPO_DIR=/path/to/homebrew-tap`
99+
- `make homebrew-release-tap TAG=vX.Y.Z TAP_REPO_DIR=/path/to/homebrew-tap`
58100

59101
## Guided hints
60102

@@ -77,6 +119,10 @@ Commands with guided next-step output include:
77119
- `make check-schema`
78120
- `make tag-schema`
79121
- `make push-tag-schema`
122+
- `make homebrew-source`
123+
- `make homebrew-sync-formula`
124+
- `make homebrew-verify-formula`
125+
- `make homebrew-open-tap-pr`
80126

81127
Example (crate flow):
82128

@@ -230,6 +276,23 @@ The release workflow at `.github/workflows/release.yml` runs when:
230276

231277
Pushing `schema-v*.*.*` tags does not trigger crates.io publish.
232278

279+
## Homebrew tap sync flow (example)
280+
281+
1. Ensure a crate release tag exists in `smorinlabs/envgen`:
282+
- e.g. `v1.0.0`
283+
2. Prepare source metadata:
284+
- `make homebrew-source TAG=v1.0.0`
285+
3. Sync formula in tap clone:
286+
- `make homebrew-sync-formula TAG=v1.0.0 TAP_REPO_DIR=/Users/stevemorin/c/homebrew-tap`
287+
4. Verify formula:
288+
- `make homebrew-verify-formula TAG=v1.0.0 TAP_REPO_DIR=/Users/stevemorin/c/homebrew-tap`
289+
5. Open/update tap PR:
290+
- `make homebrew-open-tap-pr TAG=v1.0.0 TAP_REPO_DIR=/Users/stevemorin/c/homebrew-tap`
291+
292+
Automation equivalent:
293+
294+
- `.github/workflows/homebrew-tap-pr.yml` runs on `release.published` and can be retried with `workflow_dispatch`.
295+
233296
## Failure modes
234297

235298
- Missing release section for tagging:
@@ -265,5 +328,6 @@ For migration safety, token-based publish remains available through
265328
## Notes
266329

267330
- This setup assumes you use "Squash and merge" and "Default to PR title for squash merge commits" in GitHub settings so your main-branch commits stay Conventional.
331+
- Tap PR automation requires `HOMEBREW_TAP_GITHUB_TOKEN` secret with write access to `smorinlabs/homebrew-tap`.
268332
- homebrew-core bump PRs require the `HOMEBREW_GITHUB_API_TOKEN` secret (a GitHub token able to open PRs against `Homebrew/homebrew-core`).
269333
- The homebrew-core automation will no-op until the `envgen` formula exists in `Homebrew/homebrew-core`.

0 commit comments

Comments
 (0)