diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 88a886f..4dd83ef 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -2,13 +2,14 @@ name: Release on: push: tags: ["v*"] -permissions: - contents: write +permissions: {} concurrency: group: release cancel-in-progress: false jobs: build: + permissions: + contents: read runs-on: ubuntu-26.04 timeout-minutes: 20 strategy: @@ -32,6 +33,8 @@ jobs: uses: jdx/mise-action@6612f0ef07ed2a736cd1e00a94a065522dd41195 # v5.2.5 with: version: 2026.10.25 + # A cache from a pull request could reach the published artifacts. + cache: false - name: Build env: GOOS: ${{ matrix.platform.os }} @@ -48,6 +51,8 @@ jobs: if-no-files-found: error publish: needs: build + permissions: + contents: write runs-on: ubuntu-26.04 timeout-minutes: 10 env: diff --git a/hk.pkl b/hk.pkl index 8f9cd96..961dd53 100644 --- a/hk.pkl +++ b/hk.pkl @@ -1,63 +1,20 @@ +// The hook templates from nnutter/hk-config, which amend hk's configuration. +// Both package paths name the same hk-config version, and that version must +// target the hk pin in mise.toml. The revision is the newest one released for +// that hk version. amends - "package://github.com/nnutter/hk-config/releases/download/v2.11.1-0/hk-config@2.11.1-0#/Config.pkl" + "package://github.com/nnutter/hk-config/releases/download/v2.11.1-2/hk-config@2.11.1-2#/Config.pkl" -import "package://github.com/jdx/hk/releases/download/v2.11.1/hk@2.11.1#/Builtins.pkl" as Hk import - "package://github.com/nnutter/hk-config/releases/download/v2.11.1-0/hk-config@2.11.1-0#/Builtins.pkl" - as Extra + "package://github.com/nnutter/hk-config/releases/download/v2.11.1-2/hk-config@2.11.1-2#/Hooks.pkl" + as Hooks /// Fixtures hold deliberately broken files, so every step skips them. exclude = List("**/testdata/**") -/// Shared by the implicit check, fix, and pre-commit hooks. -steps { - ["constable"] = Extra.constable - ["errcheck"] = Hk.err_check - ["gitleaks"] = Hk.gitleaks - ["go_check_sumtype"] = Extra.go_check_sumtype - ["go_fix"] = Hk.go_fix - ["go_fumpt"] = Hk.go_fumpt - ["go_imports"] = Extra.go_imports - ["go_sec"] = Extra.go_sec - ["go_vet"] = Hk.go_vet - ["go_vuln_check"] = Hk.go_vuln_check - ["gomod_tidy"] = Hk.gomod_tidy - ["mdlint"] = Extra.mdlint - ["newlines"] = Hk.newlines - ["nilaway"] = Extra.nilaway - ["pkl"] = Hk.pkl - ["pkl_format"] = Hk.pkl_format - ["skills_ref"] = Extra.skills_ref - ["staticcheck"] = Hk.staticcheck - ["testifylint"] = Extra.testifylint - ["tombi"] = Hk.tombi - ["tombi_format"] = Hk.tombi_format - ["trailing_whitespace"] = Hk.trailing_whitespace - ["yamlfmt"] = Hk.yamlfmt -} - -hooks { - // A hook that hk does not create needs an explicit declaration. - ["commit-msg"] { - fix = true - steps { - ["change-id"] = Extra.timber_commit_msg - } - } - ["pre-commit"] { - fix = true - stage = true - stash = "git" - steps { - // No file filter: scan every staged change, including arbitrary types, - // binary files, deletions, and commits without Go or Markdown changes. - // The git-mode scan only sees text diffs, so staged binary bytes are - // checked out to a temporary directory and scanned explicitly. - ["gitleaks"] = (Hk.gitleaks) { - scan = "staged" - check = - "gitleaks git --redact --pre-commit --staged --verbose --no-banner && tmp=$(mktemp -d) || exit $?; trap 'rm -rf \"$tmp\"' EXIT INT TERM; git diff --cached --name-only -z --diff-filter=ACMRT > \"$tmp/list\" || exit $?; git checkout-index -z --stdin --prefix=\"$tmp/tree/\" < \"$tmp/list\" || exit $?; mkdir -p \"$tmp/tree\" || exit $?; gitleaks dir --redact --verbose --no-banner \"$tmp/tree\" || exit $?" - } - } - } -} +// The templates build the check, fix, pre-commit, and commit-msg hooks, and +// they order the steps of each hook that fixes: the steps that rewrite files +// run in the order their edits must land, and every step that only reads waits +// for the last fixer, so the readers run together. `check` sets no order. +// `go`, `toml`, `yaml`, and `agentskills` are the groups this repository needs. +hooks = Hooks.all(List("go", "toml", "yaml", "agentskills")) diff --git a/internal/timber/review/maintenance.go b/internal/timber/review/maintenance.go index 7a8ff5c..6610d0c 100644 --- a/internal/timber/review/maintenance.go +++ b/internal/timber/review/maintenance.go @@ -47,10 +47,10 @@ func prunePatchCache(ctx context.Context, dir string) (err error) { return err } folder, err := os.Open(dir) - if errors.Is(err, os.ErrNotExist) { - return nil - } if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil + } return err } defer func() { err = errors.Join(err, folder.Close()) }() diff --git a/internal/timber/review/state.go b/internal/timber/review/state.go index a55da08..72b8d5a 100644 --- a/internal/timber/review/state.go +++ b/internal/timber/review/state.go @@ -263,10 +263,10 @@ func (s Store) Update(action func(*State) error) (err error) { } lockPath := s.Path + ".lock" lock, err := os.OpenFile(lockPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o600) - if errors.Is(err, os.ErrExist) { - return fmt.Errorf("review state is locked: %s; remove the lock only after confirming no writer is running", lockPath) - } if err != nil { + if errors.Is(err, os.ErrExist) { + return fmt.Errorf("review state is locked: %s; remove the lock only after confirming no writer is running", lockPath) + } return err } defer func() { err = errors.Join(err, lock.Close(), os.Remove(lockPath)) }() diff --git a/internal/timber/review/worktree.go b/internal/timber/review/worktree.go index ec187b1..5b67152 100644 --- a/internal/timber/review/worktree.go +++ b/internal/timber/review/worktree.go @@ -28,10 +28,10 @@ func WorktreePath(repo string) (string, error) { // reviewRunning probes an existing lock without creating it. func reviewRunning(ctx context.Context, path, branch string) (running bool, err error) { file, err := os.OpenFile(path+".lock", os.O_RDONLY, 0) - if errors.Is(err, os.ErrNotExist) { - return false, nil - } if err != nil { + if errors.Is(err, os.ErrNotExist) { + return false, nil + } return false, err } defer func() { err = errors.Join(err, file.Close()) }() diff --git a/mise.lock b/mise.lock index 51d2eb9..9319174 100644 --- a/mise.lock +++ b/mise.lock @@ -300,6 +300,10 @@ url = "https://dl.google.com/go/go1.27.3.windows-amd64.zip" version = "1.30.0" backend = "go:github.com/kisielk/errcheck" +[[tools."go:github.com/kojah/gohawk"]] +version = "0.7.1" +backend = "go:github.com/kojah/gohawk" + [[tools."go:github.com/nnutter/testifylint"]] version = "0.0.0-20261101203540-577bf922d916" backend = "go:github.com/nnutter/testifylint" @@ -396,6 +400,45 @@ checksum = "sha256:60b925d9734cd6bec5fab78b9c825b39b2ad9b1c9d9f03b685423c2d21ae9 url = "https://github.com/jdx/hk/releases/download/v2.11.1/hk-x86_64-pc-windows-msvc.zip" signer = "sigstore-oidc:https://github.com/jdx/hk/.github/workflows/release.yml" +[[tools.jactionlint]] +version = "2.4.0" +backend = "packslip:github.com/jdx/jactionlint" + +[tools.jactionlint."platforms.linux-arm64"] +checksum = "sha256:42413391c248f99ff4550d6c866ee717d5ea4ca2b966d9d54ad1503af955e22f" +url = "https://github.com/jdx/jactionlint/releases/download/v2.4.0/jactionlint_2.4.0_linux_arm64.tar.gz" +signer = "sigstore-oidc:https://github.com/jdx/jactionlint/.github/workflows/release.yaml" + +[tools.jactionlint."platforms.linux-arm64-musl"] +checksum = "sha256:42413391c248f99ff4550d6c866ee717d5ea4ca2b966d9d54ad1503af955e22f" +url = "https://github.com/jdx/jactionlint/releases/download/v2.4.0/jactionlint_2.4.0_linux_arm64.tar.gz" +signer = "sigstore-oidc:https://github.com/jdx/jactionlint/.github/workflows/release.yaml" + +[tools.jactionlint."platforms.linux-x64"] +checksum = "sha256:26ddba81db28fffc51163175c070ee1a9d1150260de4b5232f01d9b749f32d8e" +url = "https://github.com/jdx/jactionlint/releases/download/v2.4.0/jactionlint_2.4.0_linux_amd64.tar.gz" +signer = "sigstore-oidc:https://github.com/jdx/jactionlint/.github/workflows/release.yaml" + +[tools.jactionlint."platforms.linux-x64-musl"] +checksum = "sha256:26ddba81db28fffc51163175c070ee1a9d1150260de4b5232f01d9b749f32d8e" +url = "https://github.com/jdx/jactionlint/releases/download/v2.4.0/jactionlint_2.4.0_linux_amd64.tar.gz" +signer = "sigstore-oidc:https://github.com/jdx/jactionlint/.github/workflows/release.yaml" + +[tools.jactionlint."platforms.macos-arm64"] +checksum = "sha256:349a13dbffdd25376898a256d195806afda08ba97ccc6378cbf2dd11a77ef8e7" +url = "https://github.com/jdx/jactionlint/releases/download/v2.4.0/jactionlint_2.4.0_darwin_arm64.tar.gz" +signer = "sigstore-oidc:https://github.com/jdx/jactionlint/.github/workflows/release.yaml" + +[tools.jactionlint."platforms.macos-x64"] +checksum = "sha256:4c21fce834f8267b65a65b32a527aaa2ec1c321c8f066d44b42d7f038835aa90" +url = "https://github.com/jdx/jactionlint/releases/download/v2.4.0/jactionlint_2.4.0_darwin_amd64.tar.gz" +signer = "sigstore-oidc:https://github.com/jdx/jactionlint/.github/workflows/release.yaml" + +[tools.jactionlint."platforms.windows-x64"] +checksum = "sha256:4e0c7b428a4faa35bb3b86af6fa65a0ac9797edcd4c64e4f7e16ca886437e172" +url = "https://github.com/jdx/jactionlint/releases/download/v2.4.0/jactionlint_2.4.0_windows_amd64.zip" +signer = "sigstore-oidc:https://github.com/jdx/jactionlint/.github/workflows/release.yaml" + [[tools.pkl]] version = "0.32.2" backend = "aqua:apple/pkl" diff --git a/mise.toml b/mise.toml index c86d3ef..7632740 100644 --- a/mise.toml +++ b/mise.toml @@ -2,6 +2,7 @@ minimum_release_age = "0" [tools] +jactionlint = "latest" "github:alecthomas/go-check-sumtype" = "latest" "github:nnutter/constable" = "latest" "github:nnutter/mdlint" = "latest" @@ -10,6 +11,7 @@ minimum_release_age = "0" gitleaks = "latest" go = "latest" "go:github.com/kisielk/errcheck" = "latest" +"go:github.com/kojah/gohawk" = "latest" "go:github.com/nnutter/testifylint" = "latest" "go:go.uber.org/nilaway/cmd/nilaway" = "latest" "go:golang.org/x/tools/cmd/goimports" = "latest"