[object Object]

← back to Cli Printing Press

ci(ci): guard low-risk checks and setup docs (#347)

3f7410de27fc69f9aff6238059d3be81d8d24bb1 · 2026-04-27 11:36:42 -0700 · Trevin Chow

* docs(ci): document lefthook setup safeguards

* ci(ci): skip low-risk lint and golden work

Files touched

Diff

commit 3f7410de27fc69f9aff6238059d3be81d8d24bb1
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Mon Apr 27 11:36:42 2026 -0700

    ci(ci): guard low-risk checks and setup docs (#347)
    
    * docs(ci): document lefthook setup safeguards
    
    * ci(ci): skip low-risk lint and golden work
---
 .github/workflows/golden.yml | 33 +++++++++++++++++++++++++++++++++
 .github/workflows/lint.yml   | 33 +++++++++++++++++++++++++++++++++
 AGENTS.md                    |  4 ++--
 README.md                    | 13 ++++++++++++-
 lefthook.yml                 |  2 +-
 5 files changed, 81 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/golden.yml b/.github/workflows/golden.yml
index 4669d1e1..67f2adcb 100644
--- a/.github/workflows/golden.yml
+++ b/.github/workflows/golden.yml
@@ -16,12 +16,45 @@ jobs:
     timeout-minutes: 15
     steps:
       - uses: actions/checkout@v6
+        with:
+          fetch-depth: 0
+
+      - name: Check whether golden verification is needed
+        id: changes
+        shell: bash
+        run: |
+          set -euo pipefail
+
+          git fetch --no-tags --prune origin "${{ github.base_ref }}"
+          mapfile -t changed < <(git diff --name-only --diff-filter=ACMR "origin/${{ github.base_ref }}...HEAD")
+
+          echo "Changed files:"
+          printf '  %s\n' "${changed[@]}"
+
+          needs_golden=false
+          for file in "${changed[@]}"; do
+            case "$file" in
+              cmd/*|internal/*|catalog/*|testdata/golden/*|scripts/golden.sh|go.mod|go.sum|.github/workflows/golden.yml)
+                needs_golden=true
+                break
+                ;;
+            esac
+          done
+
+          echo "needs_golden=$needs_golden" >> "$GITHUB_OUTPUT"
+
+      - name: Skip golden verification
+        if: steps.changes.outputs.needs_golden != 'true'
+        run: |
+          echo "Skipping golden verification: no generator, CLI, catalog, golden fixture, module, or golden workflow files changed."
 
       - uses: actions/setup-go@v6
+        if: steps.changes.outputs.needs_golden == 'true'
         with:
           go-version-file: go.mod
           cache: true
           cache-dependency-path: go.sum
 
       - name: Verify golden outputs
+        if: steps.changes.outputs.needs_golden == 'true'
         run: scripts/golden.sh verify
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 5cfebac4..af73f666 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -16,14 +16,47 @@ jobs:
     timeout-minutes: 15
     steps:
       - uses: actions/checkout@v6
+        with:
+          fetch-depth: 0
+
+      - name: Check whether Go lint is needed
+        id: changes
+        shell: bash
+        run: |
+          set -euo pipefail
+
+          git fetch --no-tags --prune origin "${{ github.base_ref }}"
+          mapfile -t changed < <(git diff --name-only --diff-filter=ACMR "origin/${{ github.base_ref }}...HEAD")
+
+          echo "Changed files:"
+          printf '  %s\n' "${changed[@]}"
+
+          needs_lint=false
+          for file in "${changed[@]}"; do
+            case "$file" in
+              *.go|go.mod|go.sum|.golangci.yml|.github/workflows/lint.yml)
+                needs_lint=true
+                break
+                ;;
+            esac
+          done
+
+          echo "needs_lint=$needs_lint" >> "$GITHUB_OUTPUT"
+
+      - name: Skip Go lint
+        if: steps.changes.outputs.needs_lint != 'true'
+        run: |
+          echo "Skipping golangci-lint: no Go, module, lint config, or lint workflow files changed."
 
       - uses: actions/setup-go@v6
+        if: steps.changes.outputs.needs_lint == 'true'
         with:
           go-version-file: go.mod
           cache: true
           cache-dependency-path: go.sum
 
       - uses: golangci/golangci-lint-action@v9
+        if: steps.changes.outputs.needs_lint == 'true'
         with:
           version: v2.11.4
 
diff --git a/AGENTS.md b/AGENTS.md
index d7394cb4..39d227ee 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -45,9 +45,9 @@ go fmt ./...
 golangci-lint run ./...
 ```
 
-A pre-commit hook runs `gofmt -w` on staged Go files automatically. A pre-push hook runs `golangci-lint`. The same config (`.golangci.yml`: errcheck, govet, staticcheck, unused) runs in CI. To run lint manually: `golangci-lint run ./...`
+A pre-commit hook runs `gofmt -w` on staged Go files automatically. A pre-push hook runs `golangci-lint`. The same config (`.golangci.yml`: errcheck, govet, staticcheck, unused) runs in CI. Install hooks with `brew install lefthook && lefthook install --reset-hooks-path`; `--reset-hooks-path` clears stale local `core.hooksPath` settings that block hook sync. Avoid `lefthook install --force` unless intentionally overriding a custom hooks path. To run lint manually: `golangci-lint run ./...`
 
-**Always run `go fmt ./...` after writing Go code.** Use `go fmt`, not raw `gofmt` — `gofmt` doesn't accept Go package patterns, and `gofmt -w .` would walk into `testdata/golden/expected/` and rewrite frozen golden fixtures. `go fmt ./...` skips `testdata` and `vendor` by convention. Subagents and code generators often produce valid but unformatted Go. The pre-commit hook catches this for commits in the repo, but code written to external directories (e.g., `~/printing-press/library/`) must be formatted explicitly.
+**After writing Go code, format it with `go fmt ./...` before handing back work.** This is intentionally redundant with the pre-commit hook: `gofmt` is idempotent, and the hook is a safety net for commits while agents often stop before committing. Use `go fmt ./...` for repo-wide formatting and `gofmt -w path/to/file.go` only for explicit files. Do not run `gofmt -w ./...` — `gofmt` does not accept Go package patterns. Do not run `gofmt -w .` from the repo root — it can walk into `testdata/golden/expected/` and rewrite frozen golden fixtures. `go fmt ./...` formats package files and skips `testdata` and `vendor` by convention. Code written to external directories (e.g., `~/printing-press/library/`) must be formatted explicitly because repo hooks will not see it.
 
 **IMPORTANT: Always use relative paths for build output.** Never build to `/tmp` or any shared absolute path. Multiple worktrees run concurrently and will stomp on each other. Use `./printing-press` exactly as shown above.
 
diff --git a/README.md b/README.md
index 16431c82..4f83c119 100644
--- a/README.md
+++ b/README.md
@@ -454,7 +454,18 @@ go fmt ./...
 golangci-lint run ./...
 ```
 
-A pre-push lefthook hook runs `golangci-lint` on changed files; the same config (`.golangci.yml`) runs in CI. Install hooks with `brew install lefthook && lefthook install`. To test local skill changes, run `claude --plugin-dir .` so `/printing-press` loads from your working copy. See [AGENTS.md](AGENTS.md) for full conventions, glossary, and release flow.
+A pre-push lefthook hook runs `golangci-lint` on changed files; the same config (`.golangci.yml`) runs in CI.
+
+Install hooks with:
+
+```bash
+brew install lefthook
+lefthook install --reset-hooks-path
+```
+
+Use `--reset-hooks-path` so stale local `core.hooksPath` settings do not block hook sync. Avoid `lefthook install --force` unless intentionally overriding a custom hooks path.
+
+To test local skill changes, run `claude --plugin-dir .` so `/printing-press` loads from your working copy. See [AGENTS.md](AGENTS.md) for full conventions, glossary, and release flow.
 
 ### Golden Output Harness
 
diff --git a/lefthook.yml b/lefthook.yml
index 52ab241a..1e7ac169 100644
--- a/lefthook.yml
+++ b/lefthook.yml
@@ -45,7 +45,7 @@ pre-push:
       run: |
         unformatted="$(git ls-files '*.go' ':!:testdata/golden/expected/**' | xargs gofmt -l)"
         test -z "$unformatted" || { printf '%s\n' "$unformatted"; exit 1; }
-      fail_text: "gofmt failed. Run 'gofmt -w .' to fix."
+      fail_text: "gofmt failed. Run 'go fmt ./...' to fix. Do not run 'gofmt -w .' from the repo root; it can rewrite golden fixtures."
     lint:
       glob: "*.go"
       run: golangci-lint run ./...

← 4c64ffe6 ci(ci): improve workflow coverage and guards (#346)  ·  back to Cli Printing Press  ·  docs(cli): add MIT LICENSE matching README declaration (#349 f3c18d4c →