Repository navigation
feat(registry): push to ghcr.io, pull through a cell's remote repository - #1145
Merged
Merged
Conversation
A mirror declared for the host of registry.url now splits push from pull: images are pushed (and probed, cached, checked) at registry.url and the rendered manifests and cell file name them under the mirror with the pushed digest. EnvironmentRegistry.PullRepository is url read through mirrors, with the one mirroredReference spelling now shared from pkg/environments. - registry.artifacts: where frontend bundles are published and read - auth: ghcr wired (GITHUB_TOKEN/GH_TOKEN/gh auth token, GITHUB_ACTOR/gh api user, docker login --password-stdin) and requires a ghcr.io url - a released version is only built when the registry answers not found; any other probe failure stops the render - ghcr.io pushes carry org.opencontainers.image.source, outside the identity - the legacy in-agent path tags and pushes the push reference No mirror for the registry.url host: unchanged naming, push and probe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
….auth Like registry.mirrors (cli#1140), both are contract-owned: a contract that declares registry without them withdraws the value an earlier import wrote. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Why
Owner decision 2026-10-24: obin-ai images are pushed only to ghcr.io (private packages, one canonical registry), and each cell pulls through a same-cloud Artifact Registry remote repository whose upstream is ghcr.io — workload-identity pulls, no registry credential in any cell. This is the CLI half: the push and the pull become two registries when the environment mirrors its own
registry.urlhost.Classification: fix at the owner of the behaviour (the environment registry declaration and the build/render path that names, pushes and probes images). No hack.
Design
EnvironmentRegistry.PullRepository()(pkg/environments):urlread throughmirrors, with the same normalization a base image is mirrored with.mirroredReference/referenceRegistrymoved from pkg/orchestration toenvironments.MirroredReference/ReferenceRegistryso there is one spelling; orchestration delegates. A repository prefix is parsed with one probe component somyorgisdocker.io/myorg, notdocker.io/library/myorg, and a barelocalhost:5001is a host.registry.artifacts(new, optional): where frontend bundles are published and read instead ofurl— AR remote repos are not verified to serve non-image OCI artifacts. Validated withvalidateRegistryReference.auth: ghcrnow requires aghcr.iourl. Both parse through the strict coordinate-contract decoder (KnownFields(true)), covered by a test.auth: ghcris wired (pkg/builder): token fromGITHUB_TOKEN, elseGH_TOKEN, elsegh auth token --hostname github.com; user fromGITHUB_ACTOR, elsegh api user --jq .login;docker login ghcr.io --password-stdin, same shape as the ecr/acr arms. The token goes on stdin only and is never logged. Commands run behind a seam for tests.prepareSnapshotRegistrysets the agent'sDockerRepositorytoPullRepository(), so manifests and the cell file name the remote-repository path with the digest. Login stays againsturl/auth.recipeAsBuilt: when pushing and pull≠push, clones the recipe and replaces the<pull>/prefix ofImagewith<url>/(foreign images untouched). Push tag, released-version check, reuse cache andimageStillExiststherefore all use the ghcr reference; the plan (and so manifests/cell file/SBOM subjects) keeps the pull name.classifyImageProbeErrorin image_cache.go maps buildx'snot found/manifest unknown/name unknowntoerrImageNotFound(confirmed against ghcr.io: a missing package and a missing tag both answerERROR: <ref>: not found).withSourceLabel: whenurlis on ghcr.io, a pushed build gets--label org.opencontainers.image.source=<workspace GitHub https URL>(+--annotation index:…multi-platform), which links a private package to its repository. Applied after the identity is computed, likewithBuildInputsLabel(both now sharewithBuildFlags), so it is not an identity input. The URL comes from the workspace'soriginremote; a non-GitHub remote adds nothing.docker tagto the push reference, push that, and inspect its digest.frontendBundleRepositoryOfusesregistry.artifactswhen set, elseurl.Reused: the existing
mirrorsmap and its normalization,validateRegistryReference, theRegistryLoginarms' stdin pattern, theresolveImageManifestDigestprobe seam, thewithBuildInputsLabelinsertion point. Deliberately not added: a separatepullfield (the mirror already says where a registry is read from), any credential in the cell.Compatibility
With no mirror for the
registry.urlhost,PullRepository() == url,pushReferenceis the identity and every image is named, pushed and probed exactly as before.artifactsabsent → bundles aturlas before. Two deliberate behaviour changes that apply regardless of mirrors:auth: ghcrwith a non-ghcr.io url is refused at validation (it used to fail at login with "not yet wired").registrywithoutauthnow removes a previously writtenregistry.auth(it used to keep it).An image pushed to ghcr.io gains the source label; it is not part of the identity, so no cached image is rebuilt for it.
Tests
pkg/environments:
PullRepository(no mirror, other-registry mirror, mirrored, trailing slash, bare host, Hub namespace);artifacts+auth: ghcrvalidation; coordinate-contract import carryingauth/mirrors/artifacts.pkg/builder: ghcr token sources (CI env,
GH_TOKEN, gh CLI) via the exec seam, token only on stdin, refusals name no token.pkg/orchestration:
pushReferenceswaps only images under the pull base;recipeAsBuiltrenames the pushed recipe and leaves the plan alone (verified to fail with the swap removed); a released version is probed and built-t ghcr.io/…(no remote path in the build argv); a non-not-found probe error stops the render with zero builds; probe classification over real exit errors; source label present on a ghcr build, absent elsewhere, and a changed source reuses the image (same identity, one build); legacy path tags and pushes the ghcr reference.pkg/gitops:
prepareSnapshotRegistryhands agents the remote-repository path asDockerRepository; bundle repository usesartifacts.go test ./pkg/environments ./pkg/builder ./pkg/orchestration ./pkg/gitops ./cmd/environment ./cmd/build,make lint(0 issues).Re-import (coordinator decision):
registry.artifactsandregistry.authjoinregistry.mirrors(cli#1140) as contract-owned keys, so a contract declaringregistrywithout them withdraws what an earlier import wrote. Tested like fix(environment): re-import withdraws a registry mirror the contract no longer carries #1140's mirror test (TestReimportWithdrawsArtifactsAndAuthTheContractNoLongerCarries, verified to fail without the change); docs/commands.md's contract-owned line updated.Docs
docs/commands.md: the mirror section now covers mirroring the push registry itself (push to
url, deploy the mirror path),auth: ghcr, the source label andregistry.artifacts; theregistry.authcoverage line no longer calls ghcr a stub; the released-version section states the not-found-only rule.Not verified
DockerRepositorythe agent names images with, not through a real agent render.--annotation index:flag and the ghcr package↔repository link were not exercised against ghcr.io.<remote>@digestfrom a dev machine needs read access to the remote repository. Not exercised.artifactsregistry other thanurlrelies on Docker credentials already present for it (no login is run forartifacts).🤖 Generated with Claude Code