feat(plugin-oci): run targets in containers with an oci_runner - #421
Draft
raphaelvigee wants to merge 1 commit into
Draft
feat(plugin-oci): run targets in containers with an oci_runner#421raphaelvigee wants to merge 1 commit into
oci_runner#421raphaelvigee wants to merge 1 commit into
Conversation
A runner target naming an image; every target under it is `docker exec`'d into one container started for that environment. ```python oci_runner(name = "ctr", image = "ubuntu@sha256:…") target(name = "build", driver = "bash", run = "…", runner = ":ctr") ``` The second real user of the `Wrap` lane, and the one it was designed for: `WrapEnv::Args` exists precisely because `docker exec` creates the process on the far side of the daemon socket, so the environment heph sets belongs to the CLI on this side and the container sees none of it. Each variable is rendered into argv as `-e K=V`. Building it surfaced three things `WrapSession` could not express, each added here as a builder rather than a fourth and fifth constructor argument: - `with_cwd_args` — `docker exec -w <cwd>`. `spec.cwd` is where the *wrapper* starts, which says nothing about the process inside the container. Without it every target runs in the image's `WORKDIR`, usually `/`, and a build reading a relative path fails a long way from the cause. - `with_trailing_args` — `docker exec` is `[OPTIONS] CONTAINER COMMAND`, so the container id is an operand: it cannot sit in `prefix_argv`, which precedes the options. The three knobs now map onto exactly that grammar. - `with_teardown` — a container outlives the build unless something removes it. `TeardownJob` was written for this case and `WrapSession` had no way to carry one. Hermeticity: a digest is content the cache key already covers, so the session reports `Pinned`; a tag reports `Asserted` and says why, rather than being refused — choosing a weakly-pinned environment is the user's call. The sandbox root is mounted at the *same path* inside the container, because targets address `$OUT`/`$SRC` absolutely. The artifact is local-cache only: an `oci_load` ref names an image in this machine's daemon and would not resolve on another. Teardown is `docker rm -f`, with `--rm` as the backstop for an abort that never reaches it and `--init` so a target's own children are reaped inside. **Scope, stated plainly:** this serves targets built by heph's built-in drivers (`exec`, `bash`). A target built by a plugin driver under this runner is refused, because only the environment crosses the plugin seam and a container is not one. Lifting that needs the live session on the run lane — the follow-up already named in the commit that added the refusal. Tests cover the argv grammar, the cwd, env-through-argv, the digest/tag identity split, and that a container session does not flatten to an environment. The one part needing a daemon — `docker run` producing a container id — is the one part they do not exercise. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RC5ykWRMPjRKGk1Tz1KTja
raphaelvigee
force-pushed
the
raphaelvigee/exec-runners-oci
branch
from
August 24, 2026 08:21
76f025f to
4111d6c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A runner target naming an image; every target under it is
docker exec'd intoone container started for that environment.
The second real user of the
Wraplane, and the one it was designed for:WrapEnv::Argsexists precisely becausedocker execcreates the process onthe far side of the daemon socket, so the environment heph sets belongs to the
CLI on this side and the container sees none of it. Each variable is rendered
into argv as
-e K=V.Building it surfaced three things
WrapSessioncould not express, each addedhere as a builder rather than a fourth and fifth constructor argument:
with_cwd_args—docker exec -w <cwd>.spec.cwdis where the wrapperstarts, which says nothing about the process inside the container. Without it
every target runs in the image's
WORKDIR, usually/, and a build reading arelative path fails a long way from the cause.
with_trailing_args—docker execis[OPTIONS] CONTAINER COMMAND, so thecontainer id is an operand: it cannot sit in
prefix_argv, which precedes theoptions. The three knobs now map onto exactly that grammar.
with_teardown— a container outlives the build unless something removes it.TeardownJobwas written for this case andWrapSessionhad no way to carryone.
Hermeticity: a digest is content the cache key already covers, so the session
reports
Pinned; a tag reportsAssertedand says why, rather than beingrefused — choosing a weakly-pinned environment is the user's call. The sandbox
root is mounted at the same path inside the container, because targets address
$OUT/$SRCabsolutely. The artifact is local-cache only: anoci_loadrefnames an image in this machine's daemon and would not resolve on another.
Teardown is
docker rm -f, with--rmas the backstop for an abort that neverreaches it and
--initso a target's own children are reaped inside.Scope: serves any driver. A plugin driver asks the host to create its
processes (#418), so
go_*targets build inside the container like anythingelse.
Tests cover the argv grammar, the cwd, env-through-argv, the digest/tag identity
split, and that a container session does not flatten to an environment. The one
part needing a daemon —
docker runproducing a container id — is the one partthey do not exercise.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01RC5ykWRMPjRKGk1Tz1KTja
Stack created with GitHub Stacks CLI • Give Feedback 💬