Skip to content

feat(cli): build a remote sandbox base image from a Dockerfile and save it by digest - #750

Merged
ItamarZand88 merged 26 commits into
mainfrom
itamar/alien-1034-give-the-sandbox-capability-a-private-image-without-typing
Sep 26, 2026
Merged

ItamarZand88 merged 26 commits into
mainfrom
itamar/alien-1034-give-the-sandbox-capability-a-private-image-without-typing

Conversation

@ItamarZand88

Copy link
Copy Markdown
Contributor

Summary

alien projects capabilities enable remote-sandbox --src ./sandbox builds the sandbox base image from a Dockerfile, pushes it to the project's private repository and saves it by digest, so a private image no longer needs a hand-run docker login/tag/push. Re-running with an unchanged directory, on any machine, reuses the image and changes nothing.

When a developer runs the command with --src:

  1. The CLI hashes the source directory (relative paths and bytes, the Dockerfile, build args, target) and looks up remote-sandbox-src-<hash> in the project's repository.
  2. On a miss it builds with Docker, pushes, tags the manifest with that hash and reads back the digest; on a hit it skips both.
  3. It saves <registry>/<repository>@sha256:… as the capability's base image.

This PR changes a private sandbox image from four manual Docker commands to one CLI command.

What I did

  • --image (was --base-image, kept as a hidden alias) for a prebuilt image; --src with optional --dockerfile to build; --rebuild to build again even when the directory is unchanged, re-pulling FROM.
  • Pulled the manager proxy push settings and the cached stack push out of release.rs so release and this command share them.
  • ECR CreateRepository is now sent once. It was retried on the 400 "already exists" answer for ~7s before being treated as success, which made a repeat request time out. Repository lookups try the routable name first.
  • The configure request carries the project's saved Azure sandbox settings. The endpoint replaces the whole sandbox configuration, and the command used to send the AWS half only, which turned a configured Azure sandbox off.
  • The Docker build cache key now includes the Dockerfile's contents, and a build whose source changed mid-build is not cached. Side effect: an existing Docker worker rebuilds once on its next alien release.

How I tested

  • Unit: alien-build 150, alien-aws-clients 76 (one asserts a 400 "already exists" makes exactly one HTTP request), alien-bindings 346, alien-cli 333.
  • End to end against a real API, manager and ECR, with a Dockerfile FROM alpine:3.20:
    • repository already exists: no timeout;
    • unchanged rerun: Unchanged …:remote-sandbox-src-<hash> in 3s, same digest, nothing pushed;
    • edited Dockerfile: new build and digest;
    • a copy of the directory at another path with no local cache: reused, same digest;
    • --rebuild: new build with --pull;
    • --base-image alias with a public image still configures;
    • a project with a saved Azure sandbox keeps it after both --src and --image.
  • Registry tests against a local registry: re-tagging a manifest and a multi-arch index keeps the digest; a missing tag reads as absent; a 403 is an error, not a miss.
  • What it checked on the auth path:
    • The push authenticates to the manager proxy with the same workspace and token as alien release; no new credential is created or stored.
    • The saved reference is renamed under the host and repository the API returned, and the CLI refuses a pushed reference outside that repository (test configure_refuses_an_image_outside_the_pushed_repository).
    • Registry errors are classified by status and never carry the raw registry response, which can contain signed URLs.
    • Nothing turned up.

`alien projects capabilities enable remote-sandbox --src <dir>` builds the
Dockerfile in <dir> as the sandbox base image, pushes it to the project's
private repository through the manager, and configures the capability
with the pushed reference. A rerun with an unchanged build context reuses
the earlier push through the release push cache, so the reference does
not change.
…rror

Adds SandboxImagePushFailed so the registry cause is the headline, resolves
the manager before creating the project repository, marks the two
pushed-reference invariants as GenericError, and drops Platform clones.
…sh cache atomically

A missing --src directory is a validation error, not a retryable file
failure. The push cache is written through a temp file and renamed, so an
interrupted write cannot empty it and force a re-push under a new tag.
A retried create answers "already exists" to its own first attempt, and
every HTTP error is retryable in the shared client, so an existing
repository cost three backoff retries before it was reported as a
conflict. CreateRepository now goes out once through sign_send_json_once
and callers decide whether to retry.
create_repository always creates the prefixed name, so describing the
logical name first cost a failed request on every lookup. The logical
name stays as the fallback.
…re-pulls bases

BuildSettings.rebuild builds every source resource again: the local
artifact cache is not consulted and Docker builds pass --pull, so a
change that only exists upstream in a FROM image is picked up. Off for
every existing caller.
docker_source_input_hash covers the files under src by relative path,
the Dockerfile, build args, stage and target platforms, and not the
absolute path, so two checkouts of the same tree agree on it.

registry::manifest_digest reads a tag's digest and reports a missing
manifest as absent; registry::tag_manifest points a second tag at a
pushed manifest by copying its bytes, so an image index keeps its
digest.
…y digest

`alien projects capabilities enable remote-sandbox --src` now hashes the
build inputs and looks for remote-sandbox-src-<hash> in the project
repository first. A hit skips the build and push, from any machine; a
miss builds, pushes, and tags the pushed manifest with that name. The
capability is configured with the manifest digest.

--rebuild skips that lookup and the local artifact cache and re-pulls
FROM images. --image replaces --base-image, which still parses as a
hidden alias. A missing Dockerfile is refused before any request. The
repository is now ensured before the build, since the lookup needs it.
…misses

A pull role scoped to the repository prefix answers 403 for the logical
name, which hid the in-scope 404. Also state why CreateRepository is
sent once.
…or out

A refused lookup or tag is IMAGE_LOOKUP_REJECTED / IMAGE_PUSH_REJECTED
(not retryable); a transport error, 5xx or 429 stays retryable. The OCI
error is never attached, as registry errors can carry signed URLs.
The source tag is shared by every machine with the same tree, so the
input hash is recomputed after the build and a mismatch fails the run.
The sandbox push skips the release push cache, so --rebuild always
pushes and a cached reference the registry dropped cannot break the
tag step. A failed push-cache save is logged as a warning.
…e local cache

The artifact is cached under the pre-edit tree's key, so reverting the
edit would have reused it and tagged it as that tree.
…sable

A token endpoint failure reads as a failed token request, not a refused
credential.
…tifact cache

The cache key is taken before the build reads the tree. It is taken
again before the cache entry is written, and on a mismatch the artifact
is left uncached, so an interrupted run or a reverted edit cannot reuse
it. This covers alien release as well; the sandbox CLI keeps only its
own check before tagging.
…ache key

A Dockerfile outside the source directory, or under a directory the
source walk skips, was keyed by path only, so an edit to it reused the
old artifact. A missing Dockerfile is left to the build to report. A
mid-build re-key that fails counts as a change: the artifact stays
uncached rather than failing a finished build.
…a failed read

The wrapper covers lookup, push and tag, so it is SANDBOX_IMAGE_PUBLISH_FAILED.
IMAGE_LOOKUP_REJECTED no longer claims a refusal for an unusable manifest.
An empty ECR repository id looks up the shared repository, as create does.
A re-key that fails is logged with its error rather than as a source
change.
…WS one

The remote sandbox configure endpoint rebuilds the whole configuration from
the request, so an omitted `azure` key turns Azure off. `alien projects
capabilities enable remote-sandbox` always omitted it, which deleted a
project's saved Azure sandbox whenever the AWS base image was set with
--image or --src.

Read the project's saved Azure settings right before the configure call
and send them with the new base image. A project with no Azure sandbox
still omits the key.
@ItamarZand88

Copy link
Copy Markdown
Contributor Author

@greptile review

@greptile-apps

greptile-apps Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[High risk] Adds container registry operations and changes build caching logic.

The PR appears safe to merge based on the reviewed changes.

Summary

The PR adds a CLI flow to build and publish a remote sandbox base image by source hash, then configure it by digest. Since the previous review, it separates forced source rebuilding from base-image pulling and carries saved Azure settings into the configure request.

Reviews (3) · Last reviewed commit: "fix(cli): pull base images only on --reb..."

Comment thread crates/alien-build/src/source_input.rs Outdated
Comment thread crates/alien-cli/src/commands/platform/projects.rs
Comment thread crates/alien-cli/src/commands/release.rs Outdated
…image

The source input hash reused the artifact cache's file walk, which skips
node_modules, target, .git and symlinks. Docker sends all of them unless
.dockerignore says otherwise, so an edit there left the hash unchanged and
reused a stale image.

The hash now walks the context with Docker's .dockerignore rules (the
Dockerfile-specific ignore file first), and covers file contents, the
executable bit, symlink targets and directories. It skips the build's own
output directory when that sits inside the source, since the build writes
there between the hashes taken before and after it.
…ith its hash

On a registry hit the command configured the tagged digest using the hash
taken before the lookup, so a tree edited in between got the older image.
Both paths now rehash before they use the tag and fail the same way when
the source moved.
Two releases in one directory shared push-cache.json.tmp, so one could
rename the other's file away and fail its write. Each write now goes
through a uniquely named temp file in the cache directory.
@ItamarZand88

Copy link
Copy Markdown
Contributor Author

@greptile review

Comment thread crates/alien-cli/src/commands/platform/projects.rs
@ItamarZand88

Copy link
Copy Markdown
Contributor Author

@greptile review

@ItamarZand88
ItamarZand88 merged commit f2909b7 into main Sep 26, 2026
29 checks passed
@ItamarZand88
ItamarZand88 deleted the itamar/alien-1034-give-the-sandbox-capability-a-private-image-without-typing branch September 26, 2026 20:19
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