github jdx/hk v2.4.0
v2.4.0: More builtins apply their fixes as patches, via new format-diff and sarif-diff utils

4 hours ago

In hk fix, many more builtins now apply their check_diff patch instead of running the tool a second time as the fixer. hk now applies those patches itself instead of calling git apply, and read-only check_diff commands can run in parallel with other steps that read the same files.

Added

  • hk util format-diff (#1514, @jdx). Builds an applicable patch from any formatter that reads stdin and writes the formatted file to stdout. It runs the formatter once per file, in parallel, and replaces {} with the file's path. It exits 1 with a patch when a file would change, and 0 when nothing would. If the formatter fails for any file or prints nothing for a non-empty file, it prints no patch, so hk runs fix and that reports the error. Use --no-stdin for formatters that read the file from its path.

    check_diff = "hk util format-diff {{files}} -- stylua --stdin-filepath {} -"
  • hk util sarif-diff (#1515, @jdx). Runs a tool that reports fixes in a SARIF log and turns them into a patch. It prints a patch only when every result has a usable fix. If any result has no fix, or the tool fails, it prints no patch and hk runs fix. The findings exit code defaults to 1 and can be changed with --findings-exit-code.

    check_diff = "hk util sarif-diff -- pinact run --check --format sarif {{files}}"
  • run = "diff" step tests (#1518, @jdx). A diff test runs check_diff and applies its output the way fix mode does. If the patch doesn't apply, the test fails instead of falling back to fix. If the step sets check_after_diff, the test reruns check after the patch applies. TestMaker gains diffPass and diffFail. Every builtin with a check_diff now has a diff test.

  • Warning when a check_diff patch doesn't apply (#1517, #1520, @jdx). Before, hk ran the fixer without saying why. It now prints a warning that names the file and hunk, for example fmt: check_diff printed a patch that doesn't apply, so the fixer ran instead: test.txt: error applying hunk #1. Output that isn't a patch still only shows at debug level.

Changed

  • More builtins apply patches in fix mode instead of running the tool twice (@jdx):
    • stylua, yamlfmt, taplo_format, tombi_format, buildifier_format, terraform and tofu now use hk util format-diff (#1514). For stylua, yamlfmt, taplo_format and tombi_format, the old non-patch diff view is now their check, so hk check output doesn't change. The yamlfmt and taplo patch scripts need a POSIX shell. On Windows those two steps run the fixer instead.
    • ruff, oxfmt, shellharden and go_lines gain a check_diff (#1516). After ruff applies its patch, it reruns ruff check so issues it can't fix are still reported.
    • pinact and pinact_update apply pinact's SARIF fixes, which also avoids a second round of GitHub API calls (#1515).
    • isort gains a check_diff, and gomod_tidy patches now apply (#1513).
    • trailing_whitespace and newlines now apply their diffs instead of setting apply_check_diff = false (#1528).
    • selene and zizmor no longer run their tool twice in fix mode (#1511).
  • hk applies check_diff patches itself instead of running git apply (#1520, @jdx). Patches still apply all or nothing, with exact context matching. Existing files are written in place, so they keep their permissions and owner. If a write fails, hk restores the files it changed. Patches whose header labels one side with a temporary file or a label now apply, such as those from buildifier -mode=diff and phpcs --report=diff. hk refuses the following patches and runs the fixer instead:
    • paths outside the working directory
    • paths through symlinks
    • files with other hard links
    • renames, copies and mode changes
    • binary patches
  • Read-only check_diff steps run in parallel (#1519, @jdx). When a step's check_diff declares effect = "read", hk computes the patch under read locks. It takes write locks only on the files the patch names. If another step wrote any of the job's files in the meantime, hk recomputes the patch. On an already-formatted tree, two such steps over the same files now run at the same time instead of one after the other. 28 of the 30 builtin check_diff commands declare a read effect. A check_diff that declares effect = "read" must now really only read the step's files.
  • Steps with an argv prefix or custom shell no longer fail to load because of a shell-based check_diff or check_list_files (#1514, @jdx). hk skips that command, and anything that depends on it, and runs check and fix instead. As a result, yamlfmt, taplo_format, black and ruff now load with a prefix such as prefix = List("mise", "x", "--"). A check or fix that can't run with the prefix is still an error.
  • go_lines accepts an argv prefix (#1523, @jdx). Its shell check wrapper is gone. As a result, hk check now shows the patch golines would apply instead of a list of files.

Fixed

  • More check_diff patches apply (#1513, #1528, @jdx). Before, these patches were rejected and hk ran the fixer:
    • go mod tidy -diff's current//tidy/ headers
    • isort's :before/:after labels
    • hunk lines that look like ---/+++ headers
    • changes to lines with CRLF endings
    • file names containing tabs, which diff headers now quote the way git does
  • Files created by check_diff patches are staged (#1524, @jdx). For example, when go mod tidy -diff creates go.sum, hk no longer warns that the file is outside the job. stage = "<JOB_FILES>" now stages the new file in pre-commit.
  • No frozen spinner left after check_diff jobs (#1530, @jdx). In an interactive terminal, a read-locked check_diff step could leave a stale spinner, its command, and its last line of output under the finished step.
  • Concurrent hk install runs are reliable (#1533, @jdx). Local install and uninstall now hold a lock while they update the hook.hk-* git config entries, including across linked worktrees. Before, concurrent runs could fail on a missing key or git's config lock and leave hooks half-installed. Fixes #1531.

Full Changelog: v2.3.1...v2.4.0

💚 Sponsor hk

hk is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.

If hk speeds up your pre-commit loop or makes linting less painful, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep hk fast, free, and independent.

Don't miss a new hk release

NewReleases is sending notifications on new releases.