A detailed summary about the release and the reasons behind the changes can be found in the release blog. For the recommended setup, see agents, workspaces and CI.
Upgrade notes
- One full rebuild after upgrading. Cache keys are now computed from project-relative paths (see below) and include the CPU architecture and the definitions of a task's dependencies, so every existing cache entry misses once.
- More files count as sources. Globs now match what they say (see Fixed), so a task may see source changes it previously missed, and run.
- A
srcthat matches no file makes a task always run, like a task withoutsrc, and hammerkit warns about each such entry. - A task needs
srcof its own to be cached. A task withoutsrcwhose dependencies declaresrc(atesttask depending onbuild) used to be cached on its dependencies' sources alone, so changing only its own files (the tests) left it cached. It now always runs; declare the files it reads. - Tasks without
srcalways run. A task with nosrc— and every task depending on one — runs on every invocation, as documented. Earlier versions cached such a task after its first run. - Dependencies of cached tasks are skipped. A task pulled into a run only as a
dependency no longer runs when every task depending on it is a cache hit, so its
outputs (e.g. an exported
dist) aren't produced. Request the task explicitly or pass--no-skip-depsto run the whole graph as before. - Explicit
cache pull/pushfail on backend errors. Thes3backend now distinguishes a missing entry from a transport or credential error. Builds are unaffected: they still treat a backend error as a cache miss with a warning.
Added
registrycache backend: store cache entries in any OCI registry (GHCR, Docker Hub, ECR, GAR, Artifactory,registry:2) using your existingdocker logincredentials, including credential helpers. See caches.hammerkit cache pull/cache push --remote <name>: move the current commit's cache entries between the local cache and a remote cache without running tasks — warm a workspace before building, publish results after. See cache pull / push.- Read-only cache mode:
--cache-read-onlyonrun/up, orHAMMERKIT_CACHE_READ_ONLY=1, restores from cache backends but never writes to them;cache pushrefuses to run. - Cache retention: a
retentionblock on caches (maxAge,maxSize,keepPerTask),hammerkit cache lsandhammerkit cache prune(with--dry-run). Local caches with a policy are pruned automatically after every successful run; remote caches only with--remote. See retention. - Skipping dependencies of cached tasks, with
--no-skip-depsto opt out. See dependencies of cached tasks. - Task timeouts:
timeouton a task and--timeoutas a default onrun/up. A timed-out task fails, is cleaned up and writes no cache entry. See timeouts. hammerkit explain [task](with--json) reports whether each task is a cache hit or miss and which input caused a miss;run --explainprints the cause inline. See explain.run --dry-runprints the execution plan with predicted cache hits and misses.- End-of-run build summary with the cache hit ratio (
--no-summary,--summary-json). hammerkit graph [task]prints the build graph as mermaid or dot. See graph.- A guide for coding agents that rewrites an existing CI pipeline into a hammerkit build file. See migrate CI with an agent.
- The caching docs list what the cache can't see and how to check a build. See caching limitations.
Changed
- The npm package ships only the CLI (
dist) andbuild.schema.json: about half the size of 1.6.0, without test files or source maps. - Cache keys no longer depend on where the project is checked out. Paths in a task's or service's cache identity are relative to the project root (the git root, or the main build file's directory), so a laptop, a CI runner and an agent sandbox share cache entries. Machine-local runtime state (Docker containers, staging directory) stays scoped per checkout, so worktrees on one machine never share running containers. See ADR-0006.
- The built-in
defaultcache is now actually shared between checkouts on the same machine, as documented. - A service is only started when a task that needs it actually runs; cache hits don't start their services.
Fixed
- Cache hits after a change — several ways a task was reported as cached although
an input it reads had changed:
- Globs only looked at the top directory:
src/**/*.tsignored every file in a subdirectory, and**/*.tsmatched nothing at all. - Globs using
?,[...],{a,b}or extglobs, a*inside a file name (src/app*.ts) and globs with environment variables ($DIR/*.ts) matched nothing. - Binary sources were compared as text, so two different images or fonts could look identical.
- Changing a dependency's
cmds,envsorimagerebuilt the dependency but not the tasks depending on it; with skipped dependencies, not even the dependency. - Deleting a task's output (
rm -rf dist) left the task cached without the output. - A
srcentry with a typo kept its task cached forever.
- Globs only looked at the top directory:
- A cache hit of a container task now restores its exported directories and its file outputs on the host. Previously only its volumes were restored, and file outputs were not stored at all.
- A task whose
srcincludes what a dependency generates now has the same cache key before and after the dependency ran, so a clean checkout (CI) hits entries pushed from a built workspace (an agent). - Cache keys no longer depend on the order in which the filesystem lists files, which differs between machines.
- Cache entries (
description.json, pushed to remote caches) and the localexplainrecords no longer contain env values in plain text — only a digest. - Tasks running on Kubernetes now wait for their job to finish and fail when it fails. Previously a task was reported as completed as soon as its job was created. Cancelling a run (or a timeout) now deletes the running job.
- Restoring a container task from a cache backend or
store/restorenow writes the outputs into the task's volumes. Previously they were written into a throwaway container and lost, so the task reported a cache hit with empty outputs. - A task that needs a service no longer fails the run when it is a cache hit.
- A needed service that fails to start (missing image, port already in use) now fails the run immediately instead of hanging forever; service errors and crashes count as run failures.
cache pushuploads outputs that are current in the checkout even when they were never stored in the local cache.- Stopping a local task — on a timeout or Ctrl-C — now stops every process its
commands started. Previously only the shell was stopped: on Linux (where
shis dash) and for compound commands everywhere, the command kept running and the task waited for it to finish on its own. - A local task printing more than 1 MB of output is no longer killed.
- A Kubernetes task keeps the job of its last run, so an unchanged task is reused in place instead of being restored from the cache backend on every run.
- Running several commands in one process (
getCli, tests) no longer leaks log output of one run into the next command's output. - A container service's
imagesubstitutes environment variables (image: $POSTGRES_IMAGE) like a task's image does. build.schema.jsonis regenerated and matches the build-file schema again (continuous, serviceports,registrybackend).