Commit graph

233 commits

Author SHA1 Message Date
Jonathan Lebon
a0ff54a7bd tools/analyze-layer-reuse.py: overhaul CLI and output
These changes came out of a session with Opus to generalize this script
a bit more so it doesn't have the magic "auto-discovery based on tag"
behaviour anymore, but instead you just pass in all the images you want
in the order of "updates" directly on the CLI.

That makes it more intuitive to use and generally useful.

While we're here, simplify a bunch more things like dropping the
"compare originals" feature. That can just be a separate invocation
essentially.

Also support the fallback to image history for component info similar to
what we did for `inspect-layers.sh`.

And finally, rename `--show-components` to `--show-changed-components`
and add a `--show-unchanged-components`, which is also useful to know
sometimes. Sort those components by layer size.

Assisted-by: Claude Opus 4.6
2026-02-25 18:16:14 -05:00
Jonathan Lebon
72602754a0 deps: bump cap-std-ext to 5, rpm-qa to 0.2, ocidir to 0.7
Bump cap-std-ext from 4 to 5, rpm-qa from 0.1 to 0.2, and ocidir from
0.6 to 0.7. They all need to be bumped together because they all use
cap-std-ext.

No code changes were needed; all APIs remain compatible.

Assisted-by: Claude Opus 4.6
2026-02-25 16:40:56 -05:00
dependabot[bot]
3ef581e5e3 deps: bump the rust-deps group with 3 updates
Bumps the rust-deps group with 3 updates: [anyhow](https://github.com/dtolnay/anyhow), [chrono](https://github.com/chronotope/chrono) and [clap](https://github.com/clap-rs/clap).


Updates `anyhow` from 1.0.101 to 1.0.102
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.101...1.0.102)

Updates `chrono` from 0.4.43 to 0.4.44
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.43...v0.4.44)

Updates `clap` from 4.5.58 to 4.5.60
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.58...clap_complete-v4.5.60)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-version: 1.0.102
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-deps
- dependency-name: chrono
  dependency-version: 0.4.44
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-deps
- dependency-name: clap
  dependency-version: 4.5.60
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-23 14:45:53 +00:00
Jonathan Lebon
8b41af87ff Justfile: teach just diff recipe to also diff OCI metadata
We also want to make sure we retain these.

Assisted-by: Claude Opus 4.6
2026-02-19 16:29:11 -05:00
Jonathan Lebon
17d993b157 tools: delete split-and-diff.sh
This is obsolete now by the nicer `just split` and `just diff` recipes.
2026-02-19 16:29:11 -05:00
Jonathan Lebon
84959658eb scan: skip trusted.* xattrs
Noticed this while working on the previous commit. We were picking up
legacy XFS SGI_ACL_* xattr aliases for ACLs, which is just yucky. AFAICT
there's no use case for preserving some trusted xattrs, so just skip
them all for now until we find out otherwise.

Assisted-by: Claude Opus 4.6
2026-02-19 15:56:33 -05:00
Jonathan Lebon
9862279e24 scan: sort xattrs for reproducibility
Yet another source of reproducibility issues. If a file has multiple
xattrs, the order in which we read them in may change, but serializing
them to the tar layer needs to be deterministic.

Sort them at scan time.

Extend the buildtime e2e test to try to cover this by adding more
xattrs (including ACLs which we didn't have any coverage for before).
Though it's not easy to really test this without injecting our own
llistxattr(2).

Assisted-by: Claude Opus 4.6
2026-02-19 15:56:33 -05:00
Jonathan Lebon
786189e24f tree: delete OCI archive after builds with old buildah
When building with older buildah, we have to use the `-v $PWD:/run/src`
trick, but then we need to clean up the OCI archive. I don't like using
the `RUN` trick to remove the OCI archive for various reasons. But it's
trivial anyway to just clean it up ourselves.
2026-02-19 15:56:33 -05:00
Jonathan Lebon
e4377d0a78 tree: add CONTRIBUTING.md
This will be helpful for future contributors. I also wanted a place
to write down my thoughts on using AI coding agents responsibly and
respectfully.

Assisted-by: Claude Opus 4.6
2026-02-17 13:25:51 -05:00
Jonathan Lebon
e154c6549d .packit.yaml: enable fast-forward merges
See https://packit.dev/docs/fedora-releases-guide/non-divergent-dist-git-branches.
2026-02-17 12:27:44 -05:00
Jonathan Lebon
b6ab85c4a7 Cargo.toml: bump version to v0.2.0 2026-02-15 23:59:45 -05:00
Jonathan Lebon
08d0e776b9 ci: add arm64 build and publish multi-arch images
Add arm64 architecture support to the CI workflow using native GitHub
arm64 runners.

Closes: https://github.com/coreos/chunkah/issues/53
Assisted-by: Claude Opus 4.5
2026-02-15 23:49:07 -05:00
Jonathan Lebon
421e394c9f AGENTS.md: note that Justfile is capitalized
Very often when I ask the agent to change a recipe, it always looks for
`justfile` first. This should help.

Assisted-by: Claude Opus 4.5
2026-02-15 23:26:33 -05:00
Jonathan Lebon
e59cdcdaa1 tools/differ: enforce anonymous /var/tmp volume
We need a real filesystem to apply security labels because overlayfs
prevents that unprivileged.

As mentioned in the comment, we probably should rewrite that whole
script to !bash to do the pretty diff we're looking for by inspecting
the objects using ostree-native APIs.

Anyway, this works for now.
2026-02-15 23:26:33 -05:00
Jonathan Lebon
b87c03a57a Justfile: add split target for splitting existing images
Add a convenience target that wraps the Containerfile.splitter workflow
from the README. It takes an image and optional chunkah arguments.

Assisted-by: Claude Opus 4.5
2026-02-15 23:26:33 -05:00
Jonathan Lebon
7cf94ac2d9 tree: add logging using tracing crate
One thing sorely missing right now is any feedback of what chunkah is
doing.

This remedies the situation quite a bit by hooking up the tracing crate
and adding logging throughout the codebase. I might've went overboard
with the trace level, though it's really nice to be able to follow
exactly the data flow for any given path, from scanning, claiming,
packing, merging, and serializing to tar.

Change all our e2e tests to use the debug level.

Assisted-by: Claude Opus 4.5

Closes: https://github.com/coreos/chunkah/issues/17
2026-02-15 23:26:33 -05:00
Jonathan Lebon
093f6f71b6 rpm: calculate stability just once per SRPM
We were unnecessarily doing it for each binary RPM.
2026-02-12 22:22:01 -05:00
Jonathan Lebon
0074ae3e5b tree: fix various reproducibility issues and add e2e test
Fix three sources of non-determinism that prevented chunkah from
producing reproducible OCI images across runs:
1. the way we calculated the clamp time for RPM components wasn't
accounting for subpackages of a same SRPM having different build times
2. when calculating stability, we were using the current time to define
the lookback period; pass through the default clamp time (which is
derived from `SOURCE_DATE_EPOCH` if defined) and use that instead
3. when packing, we weren't providing deterministic input to the
algorithm, so results may vary very slightly
4. when reconstructing merged components, we need to ensure their names
are deterministic as well

Add an e2e test that builds the same image twice with SOURCE_DATE_EPOCH
and verifies the OCI archives are byte-identical.

Closes #18

Assisted-by: Claude Opus 4.6
2026-02-12 22:22:01 -05:00
dependabot[bot]
8989574a65 deps: bump the rust-deps group with 4 updates
Bumps the rust-deps group with 4 updates: [clap](https://github.com/clap-rs/clap), [ctrlc](https://github.com/Detegr/rust-ctrlc), [rpm-qa](https://github.com/jlebon/rpm-qa-rs) and [tempfile](https://github.com/Stebalien/tempfile).


Updates `clap` from 4.5.57 to 4.5.58
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.57...clap_complete-v4.5.58)

Updates `ctrlc` from 3.5.1 to 3.5.2
- [Release notes](https://github.com/Detegr/rust-ctrlc/releases)
- [Commits](https://github.com/Detegr/rust-ctrlc/compare/3.5.1...3.5.2)

Updates `rpm-qa` from 0.1.0 to 0.1.1
- [Commits](https://github.com/jlebon/rpm-qa-rs/compare/v0.1.0...v0.1.1)

Updates `tempfile` from 3.24.0 to 3.25.0
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/commits)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.58
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-deps
- dependency-name: ctrlc
  dependency-version: 3.5.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-deps
- dependency-name: rpm-qa
  dependency-version: 0.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-deps
- dependency-name: tempfile
  dependency-version: 3.25.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-12 04:38:28 +00:00
Jonathan Lebon
4c6a188f03 Add Packit configuration for automated Fedora releases
Set up Packit here and downstream so that everything happens
automagically when we cut a release upstream.

Move canonical spec file from downstream to upstream. Switch to
`%autochangelog`.
2026-02-11 12:25:48 -05:00
CoreOS Bot
4d75c62a87 Sync repo templates ⚙
Sync with coreos/repo-templates@8c5711ffdc.
2026-02-11 12:09:29 -05:00
Jonathan Lebon
6be3b92d3d Bump buildah version check to v1.44
Buildah v1.43 was released but doesn't include
https://github.com/containers/buildah/pull/5975.

Bump the build context dir workaround version checks to v1.44.
2026-02-09 16:05:09 -05:00
Jonathan Lebon
35964bbcda README.md: add section about zstd:chunked
I think it's natural to wonder this since they both try to make OCI
updates more efficient.

Assisted-by: Claude Opus 4.6
2026-02-09 16:05:09 -05:00
Jonathan Lebon
fcbb011391 README.md: add table of contents
The README is getting a bit long now so let's add a TOC!

Assisted-by: Claude Opus 4.6
2026-02-09 16:05:09 -05:00
dependabot[bot]
c9d0cfd6f9 deps: bump the rust-deps group with 3 updates
Bumps the rust-deps group with 3 updates: [anyhow](https://github.com/dtolnay/anyhow), [clap](https://github.com/clap-rs/clap) and [flate2](https://github.com/rust-lang/flate2-rs).


Updates `anyhow` from 1.0.100 to 1.0.101
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.100...1.0.101)

Updates `clap` from 4.5.56 to 4.5.57
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.56...clap_complete-v4.5.57)

Updates `flate2` from 1.1.8 to 1.1.9
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.1.8...1.1.9)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-version: 1.0.101
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-deps
- dependency-name: clap
  dependency-version: 4.5.57
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-deps
- dependency-name: flate2
  dependency-version: 1.1.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-09 15:26:29 +00:00
Jonathan Lebon
ef6cad194a tools: add differ and use it in e2e tests
A principle of chunkah is that the resulting rootfs should be exactly
equivalent to the input rootfs, except for mtimes. So a huge gap of our
CI was its lack of any verification that this invariant holds.

This patch plugs that hole:
- First, a new tool is added for making it easier to diff two images.
  This is as useful for humans/agents as it is for tests.
- Second, e2e tests which split existing images make use of this tool to
  assert equivalence.

Assisted-by: Claude Opus 4.6
2026-02-08 11:53:31 -05:00
Jonathan Lebon
a8d57304d1 AGENTS.md: regenerate
Some of it is stale/incomplete.

Assisted-by: Claude Opus 4.6
2026-02-06 17:45:49 -05:00
Jonathan Lebon
89255163df tools: add FCOS example usage to analysis scripts
Add example usage in the docstrings for chunk-image-series.py and
analyze-layer-reuse.py showing how to use them to analyze FCOS images.

Assisted-by: Claude Opus 4.5
2026-02-06 15:36:45 -05:00
Jonathan Lebon
678f75682a tools: add scripts to analyze layer reuse across image updates
This allows us to actually evaluate changes to the packing algorithm in
a methodical way.

Sample usage for FCOS:

```
tools/chunk-image-series.py quay.io/fedora/fedora-coreos \
    --tag-filter '*.*.3.*' --limit 5 --keep-originals \
    --prefix fedora-coreos-test \
    --chunkah-image quay.io/jlebon/chunkah:dev \
        -- --compressed --prune /sysroot/
```

And then to do the actual analysis:

```
tools/analyze-layer-reuse.py localhost/fedora-coreos-test
    --compare-originals

tools/analyze-layer-reuse.py localhost/fedora-coreos-test
    --compare-originals --uncompressed
```

Planning also to analyze Hummingbird images, but images got reset there
recently so we lost a lot of a history to make this analysis practical.

Assisted-by: Claude Opus 4.5
2026-02-05 23:29:28 -05:00
Jonathan Lebon
1feb76e4ed components: fix panic when no components have non-zero stability
We want to support images with no RPM database where users manually
bridge the package manager -> component gap using xattrs. But right
now this breaks because we always expect at least one component to have
non-zero stability, which is information only provided by the RPM repo
currently.

Ideally, users would be able to also signal stability somehow when using
the xattr model, but for now at least just default them to 0.5. The
important thing is that they not be 0.0 since it's not _guaranteed_ they
all change and so that there is still a real TEV loss to merging items.

Assisted-by: Claude Opus 4.5
2026-02-05 15:46:49 -05:00
Jonathan Lebon
fcd23b2e90 tools/inspect-layers: support OCI history as fallback
When layer annotations are not available (e.g. because of
https://github.com/containers/buildah/issues/6652), fall back to
extracting component names from OCI history entries. This leverages the
history entries added in commit f09d54c which store the component name
in the comment field.

Assisted-by: Claude Opus 4.5
2026-02-05 14:52:36 -05:00
Jonathan Lebon
ce94b373c1 tests/e2e: bump unclaimed layer size threshold to 100MB
The FCOS stable image was updated and now has a slightly larger
unclaimed layer size (~55MB vs the previous threshold of 50MB). Bump the
threshold to 100MB to give more headroom. I'll be looking at the split
algorithm more closely soon as part of #16.

Assisted-by: Claude Opus 4.5
2026-02-03 16:23:45 -05:00
Jonathan Lebon
f09d54cd4d ocibuilder: emit history entries with component info
Add OCI history entries for each layer we emit. AIUI, history is
not mandated by the OCI image spec and while most tools handle
images without it fine (buildah even has a `--omit-history` flag),
some currently break (e.g. bootc). We could fix issues where we
encounter them, but it's much easier to match the rest of the
ecosystem and have it.

As a bonus, this gives us a more reliable way to retain
component information than via layer annotations (see e.g.
https://github.com/containers/buildah/issues/6652). And there's tooling
like `podman history` built to display that info more easily.

Assisted-by: Claude Opus 4.5
2026-02-03 16:23:45 -05:00
dependabot[bot]
54b401bc14 deps: bump clap from 4.5.54 to 4.5.56 in the rust-deps group
Bumps the rust-deps group with 1 update: [clap](https://github.com/clap-rs/clap).


Updates `clap` from 4.5.54 to 4.5.56
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.54...clap_complete-v4.5.56)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.56
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-02 15:27:04 +00:00
Jonathan Lebon
fa9418d3cd build: add support for removing labels
Implement buildah-compatible label removal using `--label KEY-` to
remove a specific label and `--label -` to clear all labels. I came
upon this need when playing with "faking" a plain FCOS by taking the
OSTree-based production images and pruning `/sysroot/`: we also in that
case want to drop the `ostree.final-diffid` and `ostree.commit` labels.

(There's also another issue before that transformed FCOS image is fully
accepted by bootc, which is that today the latter mandates OCI history,
which we don't currently emit. Will send a patch to bootc soon.)

Assisted-by: Claude Opus 4.5
2026-02-01 23:39:43 -05:00
Jonathan Lebon
a6682fd649 README.md: bump Containerfile.splitter versioned URL
Let's just have it always match the latest release.

Generalize the `lockcheck` CI check to become `versioncheck`, which also
covers the README.md. The grep lines are a bit yucky but meh.

Assisted-by: Claude Opus 4.5
2026-02-01 23:12:28 -05:00
Jonathan Lebon
094e3e105b tools/release.py: fail if worktree is dirty
Assisted-by: Claude Opus 4.5
2026-01-30 17:09:07 -05:00
Jonathan Lebon
34b030c435 ci: add lockcheck to verify Cargo.lock is in sync
Missed this when I bumped to v0.1.1. Check it explicitly in CI before
build (to catch mismatches before cargo modifies Cargo.lock) and in
release.py.

Assisted-by: Claude Opus 4.5
2026-01-30 17:09:07 -05:00
Jonathan Lebon
a6ee66e916 release: use non-# commentchar for git tag
Set core.commentchar to ';' when creating the signed tag so that
release notes containing markdown headers (lines starting with '#')
are preserved in the tag message.

Assisted-by: Claude Opus 4.5
2026-01-30 17:09:07 -05:00
Jonathan Lebon
900e80cee0 Cargo.lock: fix chunkah version mismatch 2026-01-30 17:09:07 -05:00
Jonathan Lebon
777ee69d8d Cargo.toml: bump version to 0.1.1
To get the vendored tarballs with dev dependencies out.
2026-01-30 16:33:33 -05:00
Jonathan Lebon
7e966f9eba tools/release.py: include dev dependencies in vendor tarball
I want people/packagers to be able to run unit tests from the tarballs.
2026-01-30 16:33:33 -05:00
Jonathan Lebon
d7e05550c2 Containerfile: install zlib-devel to avoid bundled zlib
One annoying thing about Rust packaging is that it's very easy to miss
when you're bundling what is normally distributed as a shared library.
We almost always want to avoid that. We avoided it for openssl, but we
need the same treatment for zlib.
2026-01-30 16:33:33 -05:00
Jonathan Lebon
50ab64c80e README.md: fix block note rendering
Initially did this but markdownlint complained. We definitely do need
the newline though for it to render as a separate box.
2026-01-30 15:25:47 -05:00
Jonathan Lebon
3215bf9caa README.md: fix Containerfile.splitter URL
For some reason, I can't get the `latest` link to work. So let's just
use what we have for now.
2026-01-30 15:20:23 -05:00
Jonathan Lebon
060acc9da0 tar: handle unknown file types from fuse-overlayfs
On FUSE, the dirent type may be unknown so we have to fallback to a
`stat()` call.

Fixes: https://github.com/jlebon/chunkah/issues/20

Assisted-by: Claude Opus 4.5
2026-01-30 14:09:39 -05:00
Jonathan Lebon
de0026e7f0 tools/release: add Containerfile.splitter as release asset
Include the splitter Containerfile in GitHub releases. Still thinking
about this, this file is meant to be used directly via HTTP but because
it's an end-user API, I want it to be:
1. accessible through a not-too-ugly URL
2. versioned somehow so users can pin version and we can make changes

Assisted-by: Claude Opus 4.5
2026-01-30 13:39:06 -05:00
Jonathan Lebon
98d1a1be84 ci: push :dev on main, :tag and :latest on tag pushes
Update CI workflow to differentiate between pushes to main and tag
pushes. On main pushes, only push to quay.io/jlebon/chunkah:dev. On
tag pushes, rebuild and run tests, then push to both :tag and :latest.

Also add ignore-versions regex to the cleanup step to ensure tagged
releases are never deleted from ghcr.io.

Assisted-by: Claude Opus 4.5
2026-01-30 13:27:55 -05:00
Jonathan Lebon
e3997b9436 README.md: document annotations bug 2026-01-30 13:10:54 -05:00
Jonathan Lebon
dbb10752af README.md: add section about bootable images
Notably, that we don't support OSTree-based images.
2026-01-30 13:10:54 -05:00