clade keeps your derived container images fresh with their upstreams.
You describe a set of images to build ("ports") that are based on upstream
images you care about. clade watches the upstream tags, figures out which of
your images are out of date, and rebuilds them on top of the latest upstream —
in dependency order, since one of your images can itself be the base of another.
- Each port is a directory with a
Dockerfile, build context, and aport.yamlthat declares the upstream source to track and how the built image is named. - A source is either a
containerregistry (track its tags) or anhttpendpoint that returns a version string — so even a tool shipped as a bare binary can drive rebuilds. clade outdateddiscovers upstream versions, resolves the corresponding target images, and emits a serializable graph of the targets that need building.clade graphprints that graph as a tree, so you can see the upstream → target dependencies (and what is stale) at a glance.clade buildwalks that graph in topological order, building each Dockerfile (acontainersource passes the resolved upstream as theBASEbuild argument), then pushes.- Your own images can be the base of other ports, so an upstream update cascades to every descendant.
Registry metadata is cached (a metadata lookup costs rate limit) and can be
inspected or cleared with clade cache; version discovery, selection, "is it
outdated?", and the build backend are all pluggable.
go install github.com/lesomnus/clade@latest
# or build from a checkout
go build -o clade .clade build shells out to docker buildx, so Docker with Buildx is required to
build (not to just compute the graph).
Create a port under ports/:
ports/
dev-golang/
Dockerfile
port.yaml
# ports/dev-golang/port.yaml
source:
kind: container
repo: docker.io/library/golang
select:
kind: semver
last-major: 1
last-minor: 2
pre-release: alpine
build:
kind: build
repo: ghcr.io/me/dev-golang
tags:
- "{{.Major}}.{{.Minor}}.{{.Patch}}-alpine"
- "{{.Major}}.{{.Minor}}-alpine"# ports/dev-golang/Dockerfile
ARG BASE
FROM ${BASE}
RUN go versionThen:
clade outdated # show which targets are stale
clade outdated --format json # the build graph, serialized
clade graph # print the dependency graph as a tree
clade build # build & push the stale targets, in order
clade build --dry-run # print the buildx commands insteadRunnable examples live in ports/dev-golang (a container
source) and ports/claude (an http source for a binary release).
The workflows in .github/workflows run clade on a
schedule: refresh.yaml computes the graph and dispatches a build.yaml run
per stale target, in topological order.
- docs/architecture.md — packages and data flow
- docs/cli.md — commands and configuration
- docs/port.md — the
port.yamlreference
The build graph schema is documented inline in proto/clade/v1/graph.proto.