Add a new `tools/release.py` script to help cut releases and hook it up
to `just release`.
Use `--no-push` to prepare the release locally without pushing, allowing
inspection of artifacts before committing to the release.
Release notes are saved to `.release-notes-{version}.md` after editing
so they can be recovered if a later step fails.
Assisted-by: Claude Opus 4.5
Migrate the extract-oci-archive skill to the native OpenCode skills
directory structure. Symlink .claude so it still works with Claude Code.
Assisted-by: Claude Opus 4.5
Add a new component repo that claims unclaimed files >= 1 MB into
separate standalone components. This solves a conceptual issue in the
unclaimed files logic: by grouping all unclaimed files together, they
can't ever be broken back out into separate layers since the packer
considers each component as one monolithic unit. By breaking out large
files, the packer can choose to merge them back in (or leave them
separate) as it sees fit.
Conceptually, every unclaimed file should be considered separately, but
it's overkill to be this granular so we just filter for >= 1 MB. Files
like initramfs.img and rpmdb.sqlite are good examples of large volatile
files that benefit from being in their own layers.
Hardlinked files (same inode) are grouped into the same component so
that tar can emit them properly as hardlinks.
On FCOS, this drops the unclaimed component from ~165 MB to ~9 MB by
extracting just 5 files totaling 156 MB.
Assisted-by: Claude Opus 4.5
Instead of using 0.0 for components with unknown stability, use half
the minimum stability of all claimed components with known values. This
ensures unclaimed and xattr content are always considered less stable
than any known component while still having a non-zero value so the
packing algorithm can make meaningful TEV loss calculations when them.
This is also prep for breaking down the unclaimed component into
multiple ones.
Assisted-by: Claude Opus 4.5
Add `tools/perf/` with Justfile and Containerfile to make it easier
to do basic performance analysis in a realistic setup. Includes two
targets:
- profile: generates `flamegraph.svg` using cargo-flamegraph
- benchmark: runs hyperfine for timing measurements
Both targets build a container with the necessary tools, pull the target
image if missing, and mount it for chunkah to process. I kept the bulk
of the logic in a separate Justfile to keep the main one clean, but did
add forwarding targets for convenience:
just profile [image]
just benchmark [image]
Assisted-by: Claude Opus 4.5
Having coverage for a bootable container is overdue at this point. Now
that we've fixed RPM path canonicalization, let's do that.
This test still shows there's work to be done in handling the unclaimed
bucket. More to come in that area.
Assisted-by: Claude Opus 4.5
RPM database stores paths as they were at package build time, which may
use compat symlinks (e.g., /lib/modules/... instead of
/usr/lib/modules/...). This caused ~316 MB of kernel content to go to
"unclaimed" instead of being claimed by rpm/kernel-* packages.
Add path canonicalization to RpmRepo::load() that resolves symlinks in
directory components only before building the path-to-component mapping.
The final component is intentionally not resolved, so that if a file
that is supposed to be a file/directory according to the rpmdb turns out
to be a symlink, the RPM does not claim it.
Assisted-by: Claude Opus 4.5
Remove the direct cap-std v3 dependency and instead use the cap_std
re-export from cap-std-ext v4. That way we guarantee it's always deduped
and also avoid having to maintain that dep separately.
Assisted-by: Claude Opus 4.5
Add dependabot.yml to enable weekly dependency updates for Cargo and
GitHub Actions. Minor and patch updates are grouped together to reduce
PR noise, while major updates remain separate for manual review.
Add dependabot-automerge.yml workflow that enables auto-merge (rebase)
for non-major updates after CI passes.
Assisted-by: Claude Opus 4.5
Add a --prune flag that allows excluding paths from the rootfs scan (and
thus, the resulting tar layers).
If a directory ends with /*, its contents are excluded but not the
directory itself. No other glob patterns are supported.
My main motivation for this right now is to prune `/sysroot/ostree` in
OSTree-based bootable container images, but it's obviously generally
useful.
Assisted-by: Claude Opus 4.5
Introduce a Scanner struct with builder pattern similar to
ocibuilder::Builder. The Scanner owns a cloned Dir and provides
builder methods for configuration before scanning.
This improves ergonomics as more scanning options are added.
Assisted-by: Claude Opus 4.5
Move the scan_for_components logic directly into cmd_build::run() as
a preparatory step for refactoring the scanner code to use a builder
pattern. That abstraction didn't seem worth it anyway; it wasn't used
in unit tests, and it feels cleaner to not have the scanner module know
about components.
Assisted-by: Claude Opus 4.5
When the source image has multiple layers, squash it to a single layer
before passing to chunkah. This makes it easier to compare against
chunkah's `--max-layers 1` output.
While we're here, use the `localhost/` prefix for image names we create
to avoid reference resolution issues between skopeo, buildah, and
podman.
Assisted-by: Claude Opus 4.5
The cap handling test requires building a derived image, which we're
already doing in `test-buildtime.sh`. So let's move it there.
Assisted-by: Claude Opus 4.5
We need to cover the build-time path, which is going to be very common.
And it allows us to verify support for the new --skip-special-files.
While we're here, rename existing tests to better reflect that they test
splitting existing images.
Assisted-by: Claude Opus 4.5
Currently, we error out when special files are encountered. This is by
design; chunkah aims to stay true to the input rootfs it's being fed.
Ideally unsupported content should be deleted upfront.
That said, it definitely makes sense to provide an easy escape hatch.
This patch adds --skip-special-files which silently skips them instead
of erroring.
Closes: https://github.com/jlebon/chunkah/issues/2
Assisted-by: Claude Opus 4.5
Symlinks can also be hardlinked, but the hardlink detection logic was
only applied to regular files. This extends the hardlink tracking to
handle symlinks as well.
Assisted-by: Claude Opus 4.5
This option skips removing the original and split images from
containers-storage on exit, which is useful for debugging or inspecting
the images after the script runs.
Assisted-by: Claude Opus 4.5