Run command with caching.
When cache exists, replay by cache. When cache doesn't exist, run command and cache it.
Cache key can be generated with these way:
- Command
- File content (relative path and content)
- Environment variable (name and value)
- Text
Repeated --file, --env, and --text inputs are treated as dependency
sets, so their order does not affect the cache key.
--file values must be relative paths inside the current working directory.
Absolute paths and paths that escape the current working directory are rejected.
By default, cmd_cache keeps the newest 1024 complete cache entries in the
cache directory and prunes older entries after each run. Set
--max-cache-entries=0 to disable pruning.
New cache status files include a cmd_cache status v1 header before the exit
status. Existing cache entries that contain only a numeric exit status are still
accepted for backward compatibility.
On Linux and macOS, cmd_cache takes an advisory file lock (flock) before
running the wrapped command, so a second process racing on the same cache
key waits for the first instead of re-running the command.
Windows does not support this lock. flock is not available there, so
cmd_cache prints a running without lock warning to stderr and runs the
wrapped command without serialization. If two processes on Windows start
concurrently with the same cache key before either has written a cache
entry, the wrapped command can run more than once.
If you rely on cmd_cache to prevent duplicate concurrent execution of an
expensive command on Windows, serialize the invoking step externally (e.g.
in your build system or CI job) rather than relying on cmd_cache's lock.
The cache key only includes the environment variables listed with --env.
However, the wrapped command inherits the entire process environment when
it runs — not just the --env variables.
This means variables like PATH, HOME, LANG, and TZ silently affect
which binary is resolved and how the command behaves, even though they are
not part of the cache key.
If any unlisted environment variable affects the command's output, add it
with --env to include it in the cache key:
cmd_cache --env PATH --env LANG -- my-commandShared cache directories across machines or CI environments with different
PATH or locale settings can produce stale replays from a cache hit.
The cache key does not include the current working directory (CWD). The
wrapped command inherits the CWD of the cmd_cache process, but this
CWD is not factored into the cache key.
This means that CWD-sensitive commands — such as ls, make,
go build, or git status — can produce a cache hit across different
directories. The first cached result will be replayed regardless of
which directory cmd_cache was invoked from.
If the wrapped command's output depends on the CWD, include it explicitly in the cache key:
cmd_cache --text "$PWD" -- lsThis mirrors the --env pattern
for environment variables: any implicit dependency on the runtime
environment must be declared explicitly to get a correct cache key.
cmd_cache does not expire entries by time. There is no TTL, --max-age, or
date-based cache invalidation option. A cache hit is replayed until the command,
file dependencies, selected environment variables, or --text inputs produce a
different cache key, or until cache pruning removes the entry.
If the wrapped command changes output over time, include the intended time window in the cache key explicitly:
cmd_cache --text "$(date -u +%Y-%m-%d)" -- curl https://example.com/daily.jsonPrefer UTC or another explicitly chosen timezone for date keys. Plain
date +%Y-%m-%d uses the local timezone, so machines in different timezones can
create different daily cache entries while sharing the same cache directory.
If the command itself depends on local time or TZ, include that environment
variable with --env TZ or encode the chosen timezone in --text.
$ cmd_cache --file depedingfile.go --env GOPATH -- go build
cmd_cache
Usage:
cmd_cache [--cache-directory=DIRECTORY] [--max-cache-entries=COUNT] [(--file FILE | --env ENV | --text TEXT)...] -- COMMAND...
cmd_cache (--help | --version)
Arguments:
FILE depending file under the current working directory. (e.g. prog.h)
ENV depending environment variable. (e.g. LD_LIBRARY_PATH)
TEXT text affecting command.
COMMAND real command.
Options:
-h --help Show this screen.
-V --version Show version.
--cache-directory=DIRECTORY Cache directory [default: .cmd_cache]
--max-cache-entries=COUNT Maximum complete cache entries to keep; 0 disables pruning [default: 1024]
$ go get -d ./...
$ go buildInstall lefthook and register the hooks once:
$ lefthook installAfter that, on every commit the following check runs automatically:
- pre-commit:
shellcheck bin/*.sh,go vet ./...
On every push the full local suite runs:
- pre-push:
shellcheck bin/*.sh,go vet ./...,go test ./...
These hooks bring CI feedback earlier. The CI workflow still runs the full suite (including -race -cover) on every PR and push to main.
The 3-Clause BSD License. See also LICENSE file.