Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
-
Layers build much faster cold. A layer of files and packages is tarred and gzipped in one step, without capturing an uncompressed tar, and every layer is gzipped on up to 16 cores in fixed-size blocks, like
pigz. On a 1.5 GB layer, tar and gzip went from 79 s to 12 s. Layer blobs come out about 0.4% larger, and every layer and image digest changes once; layerdiff_ids don’t change. The new[oci].layer_compression_level(default 6) sets the gzip level. -
oci_image_buildstackslayers=in the order they are listed. They were stacked in address order, so where two layers wrote the same path the wrong one could win. Images whoselayersare not listed in address order get new digests. -
pants runand OCI repl work on macOS throughpodman machine, and on any platform through a named podman connection with the new[oci].podman_connectionoption. The image is loaded into the connection’s store aslocalhost/pants-oci-run:<digest>and kept for later runs; a missing or stopped machine is reported with the command to fix it. Scripted build steps still need local Linux podman. See “Running through podman machine” in the README. -
[oci].runner = "docker"runspants run, OCI repl and scripted build steps with Docker instead of podman, for teams without podman. It needs Docker 23.0 or newer and thepants.backend.dockerandpants_backend_oci.dockerbackends; Docker is found and configured by the Docker backend’s[docker]options. podman stays the default. See “Running with Docker” in the README. -
Scripted build steps rerun when the podman binary changes, instead of reusing layers cached with the old podman. They also read podman’s configuration from your home directory, like
pants run, instead of from an empty private one.pants runno longer rejectsCONTAINER_HOST/CONTAINER_CONNECTION: both it and build steps ignore them and run podman locally. Podman’s runtime state moves from private directories under/tmpto your runtime directory ($XDG_RUNTIME_DIR, else/run/user/<uid>), which build steps now see (it is part of their cache key); the store moves tooci_podman/v2to go with it, so bases are imported again once. Remove the oldoci_podman/v1store and/tmp/oci-podman-*directories withpodman unshare rm -rf <named-caches-dir>/oci_podman/v1 /tmp/oci-podman-$(id -u)-*. The README shows how to clear the store, including what an interrupted build leaves behind. -
The sandboxed builders run as one
oci-builderexecutable, a scie built in the rules with pex from the plugin’s builder scripts and a pinned python-build-standalone Python (3.11.17), instead of as scripts run by Pants’ own Python at its absolute path under the user’s home. Builder processes now name nothing on the host, so their cache keys are the same on every machine and builds share remote-cache hits; only buildingoci-builderitself, once per machine, does not. Building it downloads that Python and pex’ssciencetool from GitHub the first time. It unpacks once per machine into the newoci_buildernamed cache, so builders need no home directory. Layer compression no longer depends on the Python Pants runs on. The plugin registers Pants’ pex rules for this. Image digests are unchanged.[oci].builder_python_versionand[oci].builder_pbs_releaseselect that Python. -
New
deb-layer-overwriteslinter:pants lintfails when anoci_deb_layerin anoci_image_buildreplaces a regular file from a lower layer, such as the base image, with different content, naming the packages and versions involved. Identical files aren’t reported. Each layer is indexed once, in its own cached process. Linting builds or pulls the base of every image with a deb layer. -
oci_passwd_layerandoci_group_layerare built in a sandboxed process instead of in the rule. Their layer digests are unchanged. -
commands=onoci_image_buildandoci_build_layeralso takes a list of strings, run as that argv without[oci].command_shell, so scripted steps work on bases with no shell, such as distroless ones. A string still runs through the shell.examples/oci/multistageuses it and packages again. -
Write the packaged-files layer (
layers/image_bundle.tar) and the archivepants runhands to podman with the plugin’s Python instead of the hosttar. GNU tar before 1.35 exits 1 with “file changed as we read it” when Pants hardlinks the same large file into another sandbox while it is read, which failed cold builds of large images intermittently. macOS no longer needsgtarfor this. Files in the layer are 0755 or 0644 by their executable bit; before, files of 512KB or more came out 0555 from the Pants store. Hardlinked paths are stored as copies. The packaged-files layer’s digest changes once.[oci].unsafe_tar_ignore_file_changedis deprecated and has no effect. -
deb_layerwithmerged_usr=True(the default) writes the/bin -> usr/binstyle links for the directories it moves under/usr, so it works on an empty base. Before, an image built only from deb layers had no/lib64and its dynamic loader couldn’t be found. -
Compress each layer once, in its own cached process, so assembly only links existing blobs. Editing one layer no longer re-compresses the unchanged ones, such as a large dependencies layer.
-
Add
oci_locale_layer, which builds a reproducible layer with only the selected compiled locales, such asC.utf8fromlibc-bin, without runninglocale-genorlocaledef. -
Breaking:
pants packagewrites anoci_image_buildas a deterministic OCI archive,<name>.tar, instead of an<name>.dOCI layout directory. The archive holds only the image’s blobs, names the imagerepository:tagand includes a Dockermanifest.json, sopodman loadanddocker loadkeep the tag. It is written by the plugin’s Python, so skopeo is no longer downloaded and the[skopeo]options are removed. -
Breaking: Remove the unused
[umoci]and[runc]tools and the[oci].rootless,uid_mapandgid_mapoptions; setting them inpants.tomlor on the command line is now an error. -
Run scripted build steps (
commands=onoci_image_buildandoci_build_layer) andoci_extractthroughpodman build(podman 4.0 or newer) with--timestamp 0instead of umoci and runc. A failing command or missing output now fails the build instead of producing an empty artifact, and a scripted image step adds one layer on top of the unchanged base layers. Layer and extract outputs keep the owners and modes they have in the image, globs inoutputskeep their paths, and emptyoutputsis an error. -
Run images and OCI repl through user-installed rootless podman on Linux, using OCI archives and separate Pants-managed storage. Non-root ownership and groups are preserved. Remote podman and macOS execution are not supported yet.
-
Align the Python OCI examples with a pinned Debian 12 / Python 3.11 base.
-
Assemble images in Python instead of with umoci: adding layers, editing the config, creating the empty base image and tagging pulled images now run a small Python script in a sandboxed process. Each image is assembled in one process instead of several umoci calls per layer. Built image digests change once: layers are gzipped differently, history entries no longer carry stray quotes, and empty
User/WorkingDirfields from the base config are kept. -
oci_build_layeradds a layer for each of itsdependencies, not only the first. -
oci_deb_layerandoci_cacerts_layerread zstd-compressed packages, such as Ubuntu’s, by piping them through a new[zstd]tool: a static zstd binary of about 1 MB downloaded from aspect-build/zstd-prebuilt, not a Python dependency.oci_cacerts_layeris now built in a process instead of in the rule. -
Add
oci_passwd_layer,oci_group_layerandoci_cacerts_layer, which build/etc/passwd(with home directories),/etc/groupand a CA bundle from theca-certificatespackage as reproducible layers, without runninguseraddorupdate-ca-certificates. -
Add
usertooci_image_build. -
Add
oci_deb_layer, which builds a layer from.debpackages without running dpkg or maintainer scripts, recording each package in/var/lib/dpkg/status.d/. Package contents are streamed, files shipped identically by several packages are accepted, and extended attributes are rejected. -
oci_deb_layerincludes the transitive closure of itsdebs, so it accepts the targetsdeb_packagesfrompants-backend-distrolessgenerates from a Debian lockfile. Afiletarget indebsthat has its owndependenciesnow brings them into the layer too, and they must be.debfiles as well. Packages for more than one architecture are rejected. -
Stop setting a layer target’s entrypoint to a nested layer tarball.
-
Pull, publish and mirror images with crane instead of skopeo. Credentials come from the docker config and its credential helpers when one exists, and only otherwise from
REGISTRY_AUTH_FILEor podman/skopeo’scontainers/auth.json; apodman loginalongside a docker config is no longer used. -
Pulled images are converted from Docker v2 to OCI in the rule; an omitted
osnow defaults tolinux. -
oci_mirror_imagenow copies the whole manifest list and keeps the source digest, instead of a single re-encoded platform image. -
The README explains how rebuilding large images grows the Pants store, and suggests a lower
local_store_files_max_size_bytes.
0.10.0 - 2026-10-17
-
Target Pants
2.31.0. -
Require
pants-backend-kustomize>=0.7.0. -
Replace remaining union-dispatched
Getcalls with polymorphic call-by-name rules. -
Default to umoci
v0.6.0, using official Linux x86-64/ARM64 binaries and refreshed macOS x86-64/ARM64 builds fromtgolsson/umoci-binary. Retain the existing pinned nightly andv0.4.7entries; CI examples now use the stable default. -
Default to Skopeo
v1.24.1fromlework/skopeo-binaryfor all four platforms; remove the old version entries and provider-routing branches. -
Default to runc
v1.5.2; includev1.4.3,v1.3.6, andv1.2.9for Linux x86-64/ARM64, replacing the oldv1.1.4entries. -
Fix Kustomize OCI injection to supply the image layout/reference when publishing local bundles and default an omitted registry tag to
latest. -
Check every recorded binary download and cover local/remote Kustomize injection with regression tests that mock registry operations.
0.9.0 - 2026-10-16
- Now targets
pantsversion2.30. - Migrate rules to the Pants call-by-name API, including the OCI mirror plugin.
0.8.1 - 2025-05-15
- Add
pants_backend_oci.utility.mirrorplugin to mirror images between repositories.
0.8.0 - 2025-05-14
- New target:
oci_extract. Allows pulling out individual files from another image; without executing the image itself. - Now targets
pantsversion2.24.
0.7.0 - 2024-09-19
- Now targets
pantsversion2.22.
0.6.2 - 2024-09-19
- Adds
[oci].unsafe_tar_ignore_file_changedflag that will set--warning=no-file-changedwhen invoking tar
0.6.1 - 2024-03-28
- Fix a bug where the plugin would try to publish OCI images without a repository.
- Will now default to “latest” as a tag if none is specified.
0.6.0 - 2024-03-21
- Adding layers to an image is changing. As a preparatory step, there’s now an
oci_layertarget. Add these to thelayersfield of anoci_image_build, instead of dependencies. These will process a bit better, and gives more control over what goes into each layer. - Mac support has improved. In order to provide determinism, the
gtar(GNU tar) binary has to be available. - Both umoci and skopeo are now supported on Mac M1 and x86_64. This plugin does not support image run steps on Mac still.
- When pulling multi-arch images you can now specify
osandarchitectureon theoci_pull_imagetarget - Fix a bug where OCI layer building would fail if no dependencies were specified
0.5.0 - 2023-11-19
- Fix a crash when image pulling fails
- Add
nightlyversion forumociwith experimental support for Mac - Add
v1.13.3version forskopeowith experimental support for Mac
0.4.0 - 2023-06-18
-
Improved support for very large layers > 2GB. A lot of layers will now be compressed in transit. This adds some overhead later when injecting them into image, but fixes some crashes inside Pants.
-
Improved support for multi-stage builds (
COPY --from)There is now support for building artifacts in one container and copying them into a new container. To do this, use
oci_build_layer, and configure the output files and directories. The files and data will be inserted verbatim into the downstream container. -
Adds support for empty base images (
FROM scratch)This change enables you to use
base=["//:empty"]to start from a completely empty container. This can be useful to produce containers with statically linked binaries that require no runtime environment at all.- To change the target name, set
[oci].empty_image_targetinpants.toml.
- To change the target name, set
0.3.1 - 2023-03-16
- Handle files when building layers
0.3.0 - 2023-03-14
- [breaking] Change output format when publishing
0.2.0 - 2023-02-10
- [bugfix] Ensure layer tars has no metadata or user information
- [bugfix] Properly pass args to runc
- [bugfix] Fix log output in package_oci_image
- [feature] Add proper metadata where possible
- [feature] Add support for anonymous image pulling (
anonymous=true) - [feature] Add support for passing image SHA along to Kustomize
- [feature] Add
python_image_targetwhich will set entrypoint appropriately - [feature] Forward PATH, HOME, XDG_RUNTIME_DIR env variables to publish step to make credHelpers work
[0.1.1] - 2022-11-17
- Move to new repo
- Compatibility fixes for 2.15.0a0
[0.1.0] - 2022-11-13
Initial release.