Skip to content

feat(plugin-oci): oci_load tags every image by content - #381

Open
raphaelvigee wants to merge 1 commit into
worktree-bridge-cse_01Eu3bwzWeKmzmR38LBBjtJkfrom
feat/oci-load-auto-tag
Open

feat(plugin-oci): oci_load tags every image by content#381
raphaelvigee wants to merge 1 commit into
worktree-bridge-cse_01Eu3bwzWeKmzmR38LBBjtJkfrom
feat/oci-load-auto-tag

Conversation

@raphaelvigee

Copy link
Copy Markdown
Member

Based on #159 (oci_load only exists there). Independent of #378 — a sibling, not a link in that stack.

What

tag was required. Now every load also applies a content-addressed tag — the image target's address as a repository, this load's input hash as the tag:

$ heph run //app:load
app/img:9f2c4e1b7a0d3856

tag becomes optional and is applied in addition, never instead.

Why

A tag written in a BUILD file is a moving target. It names whichever image was loaded last, so two branches, two people, or a rebuild after a change all collide on it and docker run app:dev silently runs the wrong thing. It is also unusable from a script, which has to read the log to find out what happened.

The derived tag can't do that: it changes when and only when the image changes, two people on the same commit get the same string, and it can be predicted from the graph instead of discovered after the fact.

The pinned tag is applied first, so an image is never briefly reachable only under a moving name.

The repository follows the image, not the load

//app:load loading //svc/api:img gives svc/api/img, not app/load. The repository names what you are about to run, and nobody names their load target after the image it loads.

Deriving it is not a formatting exercise

A docker reference is much narrower than a heph address — each path component is [a-z0-9]+([._]|__|[-]+[a-z0-9]+)*:

Address Repository Why
//cmd/server:img cmd/server/img
//Cmd/My_Server:Img cmd/my-server/img grammar is lowercase alphanumeric
//example.com/svc:img example-com/svc/img docker reads a first component containing a dot as a registry host — kept as-is, docker run becomes a network pull from a registry that does not exist
//localhost:img heph/localhost/img a host by name alone, same trap
//app:img@v=linux_amd64 app/img-v-linux-amd64 variants are different images; sharing a repository puts two rows in docker images with nothing to tell them apart
//app:___ error at parse names the way out (give an explicit tag) rather than a reference-format complaint from the daemon later

The repository is resolved in parse. hashin is only known at run time — the engine computes it after parse — so only the repository is in the def.

OCI_LOAD_FORMAT_VERSION 3 → 4.

Tests

9 unit tests covering the mapping table above, plus tag being genuinely optional and the repo tracking the image rather than the load. The daemon-gated test now asserts both tags land in the daemon and that the derived one is a hex hash — with a per-process package so its repository is unique, since the daemon is shared state. That assertion needs a real daemon: the archive carries RepoTags, but a containerd-backed daemon ignores them in favour of the OCI path, so the explicit tagging afterwards is invisible to a fake.

Drive-by: test_real_skopeo_loads_an_oci_archive_into_the_daemon had described skopeo since skopeo stopped being involved. Renamed and re-documented to what it actually covers.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WqQZxsTqanJRWLWdPSchBo

`tag` was required, and a tag written in a BUILD file is a moving target:
it names whichever image was loaded *last*, so two branches, two people,
or a rebuild after a change all collide on it and `docker run app:dev`
silently runs the wrong thing. It is also unusable from a script, which
has to read the log to learn what happened.

Every load now applies a content-addressed tag as well — the image
target's address as a repository, this load's input hash as the tag:

    $ heph run //app:load
    app/img:9f2c4e1b7a0d3856

It changes when and only when the image changes, two people on the same
commit get the same string, and it can be predicted from the graph rather
than discovered after the fact.

`tag` is now optional and is applied *in addition*, never instead, so the
pinned name is always there to fall back to. The pinned one is applied
first, so an image is never briefly reachable only under a moving name.

The repository follows the **image** target, not the load target: it names
what you are about to run, and nobody names their load target after the
image it loads.

Deriving it is not a formatting exercise. A docker reference is much
narrower than a heph address — each path component is
`[a-z0-9]+([._]|__|[-]+[a-z0-9]+)*` — so everything outside `[a-z0-9]`
becomes `-` and each component is trimmed. Dots are mapped away rather
than kept, because docker reads a first component containing one as a
*registry host*: a package called `example.com` would turn `docker run`
into a pull from a registry that does not exist. `localhost` is a host by
name alone and gets a prefix. Variants keep their args in the name, so
`//app:img@v=linux_amd64` and its arm64 sibling do not land on one
repository with nothing in `docker images` to tell them apart.

The name is resolved in `parse`, so an address with nothing a repository
can hold is a BUILD-file error naming the way out, rather than a reference
-format complaint from the daemon much later. `hashin` is only known at
run time — the engine computes it after `parse` — so only the repository
is in the def.

OCI_LOAD_FORMAT_VERSION 3 -> 4.

Drive-by: `test_real_skopeo_loads_an_oci_archive_into_the_daemon` had
described skopeo since skopeo stopped being involved. Renamed and
re-documented to say what it actually covers — that the OCI-to-docker
archive conversion is accepted by a real daemon.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WqQZxsTqanJRWLWdPSchBo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant