Skip to content

Move the CLI into a ./cli leaf module and distribute via Homebrew - #95

Merged
harrydayexe merged 4 commits into
v3-changesfrom
93-refactorcli-move-cli-into-a-cli-leaf-module-distribute-via-homebrew-drop-go-install
Sep 16, 2026
Merged

harrydayexe merged 4 commits into
v3-changesfrom
93-refactorcli-move-cli-into-a-cli-leaf-module-distribute-via-homebrew-drop-go-install

Conversation

@harrydayexe

@harrydayexe harrydayexe commented Sep 16, 2026 •

Copy link
Copy Markdown
Owner

Closes #93. Targets v3-changes.

Moves the goblog CLI out of the root Go module into a leaf module at ./cli/, and switches its distribution to a Homebrew tap plus release archives and Docker. go install is no longer a supported install path.

v3-changes was 27 commits behind main, so main was merged into it first (commit a572885, pushed directly to v3-changes). This PR sits on top of that merge.

What changed

Module split

/go.mod              github.com/harrydayexe/GoBlog/v2        (library only)
/cli/go.mod          github.com/harrydayexe/GoBlog/v2/cli    (never published)
  replace github.com/harrydayexe/GoBlog/v2 => ../
  cmd/goblog/        moved from /cmd/goblog
  internal/          moved from /internal
/integration/go.mod  unchanged

internal/ was already CLI-only — nothing under pkg/ imported it — so the whole tree moved rather than being reached across the module boundary.

The root module sheds urfave/cli/v3, fatih/color, mattn/go-colorable and mattn/go-isatty. pkg/... is untouched: git diff v3-changes -- pkg/ is empty.

Build and release

  • .goreleaser.yaml builds with dir: cli, generates bash/zsh/fish completions in a before hook, ships them in every archive, and publishes a homebrew_casks block to harrydayexe/homebrew-tap. The issue suggested brews:, but that is fully deprecated upstream as of GoReleaser v2.16 and makes goreleaser check fail, so this uses homebrew_casks instead.
  • Dockerfile builds from ./cli/cmd/goblog. Both go.mod files are copied before go mod download so the replace ../ resolves in the cached layer.
  • justfile recipes target the CLI module, and test/test-race/vet/fmt/vulncheck/mod-tidy now iterate every module instead of stopping at the root module's ./.... Coverage is reported per module, because go tool cover resolves sources through the module it runs in.

Docs

README's ## CLI section now documents brew install harrydayexe/tap/goblog and release archives. CONTRIBUTING describes the three-module layout; CLAUDE.md records the module boundary and the new install channels.

Follow-up needed

  • CI workflow changes are not in this PR and must be applied separately — see the review comment below for the exact diff. Without them the CLI module is not vetted, tested or race-tested at all, and the release job has no HOMEBREW_TAP_GITHUB_TOKEN.
  • The harrydayexe/homebrew-tap repo and its release token still need creating, and HOMEBREW_TAP_GITHUB_TOKEN adding as a repository secret. Until then the homebrew_casks step will fail at release time.
  • A final go install-capable release with a deprecation notice, if you want one, as the issue suggests.

Verification

  • just test, just test-race, just vet, just fmt-check and addlicense -check pass across all three modules.
  • goreleaser check passes; goreleaser release --snapshot produces archives containing the binary, LICENSE, README and completions/, and a cask with correct binary and *_completion stanzas.
  • The built binary reports the injected version and generates correctly.
  • Docker was unavailable in this environment, so the image build is verified by simulation — building ./cli/cmd/goblog from a clean git archive of the tree, and running go mod download in cli/ with only the two go.mod/go.sum pairs present.

Note

README.md claims GPL-3.0 in its badge and License section, but LICENSE is MPL 2.0 and every source header says MPL 2.0. Left alone as out of scope, but worth fixing.

🤖 Generated with Claude Code

Changelog (#95)

📚 Documentation

  • (cli) document Homebrew and archive installs, drop go install

♻️ Refactoring

  • (cli) move CLI into a ./cli leaf module

🏗️ Build System

  • (cli) build the CLI module and publish a Homebrew cask

🤖 CI

  • add updates for split cli and homebrew tap

harrydayexe and others added 3 commits September 16, 2026 23:02
Split the goblog CLI out of the root module into a leaf module at ./cli
so CLI dependencies are no longer inherited by library consumers.

- cmd/goblog -> cli/cmd/goblog
- internal/  -> cli/internal/ (already CLI-only; no pkg/ importers)
- cli/go.mod replaces github.com/harrydayexe/GoBlog/v2 => ../, so the
  CLI always builds against the library at the same commit.

The root module sheds urfave/cli/v3, fatih/color, mattn/go-colorable and
mattn/go-isatty. pkg/... is untouched.

The CLI generator tests loaded templates through a relative os.DirFS path
into pkg/templates/default; they now use the embedded templates.Default,
which does not depend on the tree layout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Dockerfile: download deps and build from ./cli; both go.mod files are
  copied up front so the `replace ../` resolves in the cached layer.
- justfile: build/install/run recipes target the CLI module; test, vet,
  fmt, vulncheck and mod-tidy now iterate every module instead of
  stopping at the root module's `./...`. Coverage is reported per module
  because `go tool cover` resolves sources through its own module.
- .goreleaser.yaml: `dir: cli`, shell completions generated in a before
  hook and shipped in every archive, and a `homebrew_casks` block that
  pushes to harrydayexe/homebrew-tap (`brews` is deprecated upstream).

The matching CI workflow changes are held back in a separate commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- README: the `## CLI` section now covers `brew install
  harrydayexe/tap/goblog` and release archives; `go install` no longer
  works because the CLI module is not published. Adds fish completion
  and notes that Homebrew and the archives ship completions.
- CONTRIBUTING: describe the three-module layout and why per-module
  commands are needed.
- CLAUDE.md: record the module boundary and the new install channels.
- version.go: the go install fallback path no longer applies.
- justfile: `run-gen` invoked a `gen` subcommand that does not exist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@harrydayexe
harrydayexe force-pushed the 93-refactorcli-move-cli-into-a-cli-leaf-module-distribute-via-homebrew-drop-go-install branch from c47a8cc to 80edf99 Compare September 16, 2026 23:03
@harrydayexe
harrydayexe changed the base branch from main to v3-changes September 16, 2026 23:03
@harrydayexe

Copy link
Copy Markdown
Owner Author

CI workflow changes to apply separately

These could not be pushed from the sandbox (the token lacks workflow scope). The diff below applies cleanly onto this branch — verified with git apply --check --3way.

Without them the CLI module is not vetted, tested or race-tested at all, since go vet ./... and go test ./... from the repo root stop at the nested go.mod boundary.

.github/workflows/test.yml

  • Matrix the unit job over . and cli so each module is vetted, tested and race-tested.
  • Point setup-go caching at both go.sum files.
  • Add a go vet step to the integration job and race-test it (timeout raised 10m → 15m to absorb the race detector).

.github/workflows/release.yml

  • Add an explicit setup-go step. GoReleaser's before hooks now run go run ./cmd/goblog completion ... to generate the shell completions, so the job needs a pinned Go rather than whatever the runner ships.
  • Pass HOMEBREW_TAP_GITHUB_TOKEN, which the homebrew_casks block needs to push to harrydayexe/homebrew-tap. The default GITHUB_TOKEN cannot write to another repository, so this has to be a PAT added as a repository secret.

Patch

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index be07784..9348f00 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -52,7 +52,14 @@ jobs:
         uses: actions/checkout@v6
         with:
           fetch-depth: 0
-      
+
+      - uses: actions/setup-go@v6
+        with:
+          go-version: '1.26'
+          cache-dependency-path: |
+            go.sum
+            cli/go.sum
+
       - name: Create a new release
         uses: goreleaser/goreleaser-action@v7
         with:
@@ -60,6 +67,9 @@ jobs:
           args: release --clean
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          # A PAT with write access to harrydayexe/homebrew-tap; the default
+          # GITHUB_TOKEN cannot push to another repository.
+          HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
           # Use the tag that triggered this run; otherwise goreleaser may pick
           # a pre-release tag (e.g. v2.6.0-beta5) pointing at the same commit.
           GORELEASER_CURRENT_TAG: ${{ github.ref_name }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index c7da401..3ee37d0 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -12,24 +12,34 @@ on:
 
 jobs:
   test:
+    name: Unit tests (${{ matrix.module }})
     runs-on: ubuntu-latest
     permissions:
       contents: read
+    strategy:
+      fail-fast: false
+      matrix:
+        # The library and the CLI are separate Go modules, so `./...` from the
+        # repo root does not reach the CLI. Each module is tested in its own job.
+        module: ['.', 'cli']
     steps:
       - uses: actions/checkout@v6
 
       - uses: actions/setup-go@v6
         with:
           go-version: '1.26'
+          cache-dependency-path: |
+            go.sum
+            cli/go.sum
 
       - name: Run go vet
-        run: go vet ./...
+        run: go -C ${{ matrix.module }} vet ./...
 
       - name: Run tests
-        run: go test -v ./...
+        run: go -C ${{ matrix.module }} test -v ./...
 
       - name: Run tests with race detector
-        run: go test -race ./...
+        run: go -C ${{ matrix.module }} test -race ./...
 
   integration:
     name: Integration tests
@@ -42,9 +52,13 @@ jobs:
       - uses: actions/setup-go@v6
         with:
           go-version: '1.26'
+          cache-dependency-path: integration/go.sum
+
+      - name: Run go vet
+        run: go -C integration vet ./...
 
       - name: Run integration tests
-        run: cd integration && go test -v -timeout 10m ./...
+        run: go -C integration test -race -v -timeout 15m ./...
 
   build-docker-image:
     name: Build Docker image

Also needed before the next release

  1. Create the harrydayexe/homebrew-tap repository (public, with a main branch).
  2. Create a PAT with contents: write on that repo and add it to harrydayexe/GoBlog as the HOMEBREW_TAP_GITHUB_TOKEN secret.

.goreleaser.yaml has skip_upload: auto, so prereleases will not push a cask.

@harrydayexe
harrydayexe merged commit 0ed77d3 into v3-changes Sep 16, 2026
6 checks passed
@harrydayexe
harrydayexe deleted the 93-refactorcli-move-cli-into-a-cli-leaf-module-distribute-via-homebrew-drop-go-install branch September 16, 2026 23:29
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.

refactor(cli): move CLI into a ./cli leaf module, distribute via Homebrew, drop go install

1 participant