refactor(box-images)!: adopt v0.1.0 line, relocate under apps/ - #1100
Conversation
Point every curated-image reference at the v0.1.0 tag and give the
images a home and a release pipeline matching the rest of the repo.
Images
- All 53 refs move from 20260605-p0-r3 to v0.1.0, across 41 files.
- v0.1.0 is multi-arch (linux/amd64 + linux/arm64); the old tag was
amd64-only.
- v0.1.0 runs as USER boxlite, so the six e2e files that wrote to /root
now use /workspace, which is on the container rootfs (not tmpfs, so
copy_out still sees it) and is owned by the box user.
Layout
- images/agent-runtime/ -> apps/box-images/, joining apps/{api,dex,
proxy,runner}, which already co-locate their Dockerfiles. Top-level
images/ is removed.
- scripts/images/build-agent-runtime.sh -> apps/box-images/build.sh, so
the Dockerfiles, VERSION and builder live together; the script anchors
on its own directory and keeps the repo root as the build context.
- "agent-runtime" was ambiguous next to src/guest (the guest agent) and
apps/runner; "box-images" says what they are.
Workflows
- build-box-images.yml (was publish-boxlite-cloud-images.yml) validates
only: both architectures with PUSH=0, contents:read, no registry
login, so it cannot write to GHCR.
- release-box-images.yml is the sole publisher, driven by an
apps/box-images/vMAJOR.MINOR.PATCH tag, matching the existing
sdks/go/v* convention. It rejects a tag disagreeing with
apps/box-images/VERSION, and refuses to republish a version already on
GHCR unless dispatched with allow-overwrite. Previously any edit under
the image directory republished the same tag, silently moving bytes a
running box pulls.
- The existence check reads the registry API and branches on the HTTP
status; only 404 counts as free, so a 5xx or an expired token stops
the release rather than reading as "not published".
- Publishing stays restricted to a release tag or main, preserving the
guard the replaced workflow carried.
BREAKING CHANGE: v0.1.0 images carry no in-box daemon and no
/boxlite/bin/start-agent-runtime entrypoint, and run as USER boxlite
rather than root. Boxes booted from the curated refs lose the daemon on
PID 1 port 2280. Nothing in-repo consumes it, and the e2e suite has not
been run against the new images.
📦 BoxLite review — looks good ·
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughIntroduces v0.1.0 Base, Python, and Node images with separate validation and release workflows. Updates application, infrastructure, quickstart, E2E, benchmark, and stress-test defaults to use versioned multi-architecture images and ChangesBox image release
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant GitHubActions
participant VERSION
participant GHCR
participant Buildx
GitHubActions->>VERSION: resolve and validate image version
GitHubActions->>GHCR: check existing manifests
GitHubActions->>GHCR: authenticate
GitHubActions->>Buildx: build and publish amd64 and arm64 images
Buildx->>GHCR: push image manifests
GitHubActions->>GHCR: inspect published digests
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| "dd if=/dev/urandom of=/workspace/big4m bs=4096 count=1024 2>/dev/null " | ||
| "&& sha256sum /workspace/big4m"], |
| "dd if=/dev/urandom of=/workspace/blob bs=4096 count=64 2>/dev/null " | ||
| "&& sha256sum /workspace/blob && sync"], None, |
| 'cat > /workspace/greet.sh << \'EOF\'\n' | ||
| '#!/bin/sh\n' | ||
| 'NAME="${1:-World}"\n' | ||
| 'echo "Hello, ${NAME}! Today is $(date +%A)."\n' | ||
| 'EOF\n' | ||
| 'chmod +x /root/greet.sh'], | ||
| 'chmod +x /workspace/greet.sh'], |
| "tr '[:lower:]' '[:upper:]' < /workspace/input.txt > /workspace/upper.txt && " | ||
| "wc -w < /workspace/input.txt | tr -d ' ' > /workspace/count.txt"], |
| "echo secret > /workspace/private.txt && chmod 600 /workspace/private.txt && " | ||
| "echo public > /workspace/public.txt && chmod 644 /workspace/public.txt && " | ||
| "stat -c '%a %n' /workspace/private.txt /workspace/public.txt"], |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/box-images/node.Dockerfile (1)
24-48: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winNode image has no C/C++ toolchain for native module builds.
python3/python3-pipare installed fornode-gyp, but there's nobuild-essential/make/g++. Many common npm packages with native bindings (e.g. bcrypt, sharp, sqlite3) will fail to install without a compiler.python.Dockerfileinstallsbuild-essentialfor the same reason on the Python side; consider parity here. Note thatboxlitehas passwordless sudo, so users can work around this themselves, which softens the impact.🔧 Proposed fix
bash \ + build-essential \ ca-certificates \🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/box-images/node.Dockerfile` around lines 24 - 48, Update the package list in the Node image build step to install the C/C++ compilation toolchain required by node-gyp and native npm modules, matching the existing build-tooling setup used by the Python image. Keep the change scoped to the apt-get install dependencies in the Dockerfile.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release-box-images.yml:
- Around line 32-37: Add a suitable timeout-minutes value to the release job
definition identified by release in the workflow, bounding stalled build or push
operations while preserving the existing ref restriction and concurrency
behavior.
---
Nitpick comments:
In `@apps/box-images/node.Dockerfile`:
- Around line 24-48: Update the package list in the Node image build step to
install the C/C++ compilation toolchain required by node-gyp and native npm
modules, matching the existing build-tooling setup used by the Python image.
Keep the change scoped to the apt-get install dependencies in the Dockerfile.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 1e3e890b-4c56-4326-bfee-b0cb0a18588a
📒 Files selected for processing (58)
.github/workflows/README.md.github/workflows/build-box-images.yml.github/workflows/publish-boxlite-cloud-images.yml.github/workflows/release-box-images.ymlapps/api/src/box/constants/curated-images.constant.spec.tsapps/api/src/box/constants/curated-images.constant.tsapps/box-images/VERSIONapps/box-images/base.Dockerfileapps/box-images/build.shapps/box-images/node.Dockerfileapps/box-images/python.Dockerfileapps/dashboard/src/components/Box/CreateBoxDialog.tsxapps/dashboard/src/lib/quickstart/interfaces.jsonapps/dashboard/src/lib/quickstart/templates/c.capps/dashboard/src/lib/quickstart/templates/cli.shapps/dashboard/src/lib/quickstart/templates/go.goapps/dashboard/src/lib/quickstart/templates/python.pyapps/dashboard/src/lib/quickstart/templates/rest.shapps/dashboard/src/lib/quickstart/templates/rust.rsapps/dashboard/src/lib/quickstart/templates/typescript.mtsapps/infra-local/compose/_local_arm64.pyapps/infra-local/compose/native.pyapps/infra/.env.exampleapps/infra/sst.config.tsapps/scripts/local-dex-env.mjsscripts/test/e2e/README.mdscripts/test/e2e/bench_rest_startup.pyscripts/test/e2e/cases/conftest.pyscripts/test/e2e/cases/test_c_coverage.pyscripts/test/e2e/cases/test_c_entry.pyscripts/test/e2e/cases/test_cli_comprehensive.pyscripts/test/e2e/cases/test_cli_detach_recovery.pyscripts/test/e2e/cases/test_cli_entry.pyscripts/test/e2e/cases/test_exec_comprehensive.pyscripts/test/e2e/cases/test_files_comprehensive.pyscripts/test/e2e/cases/test_files_io.pyscripts/test/e2e/cases/test_go_coverage.pyscripts/test/e2e/cases/test_go_entry.pyscripts/test/e2e/cases/test_lifecycle_comprehensive.pyscripts/test/e2e/cases/test_node_comprehensive.pyscripts/test/e2e/cases/test_node_coverage.pyscripts/test/e2e/cases/test_node_entry.pyscripts/test/e2e/cases/test_node_tunnel.pyscripts/test/e2e/cases/test_real_world.pyscripts/test/e2e/fixture_setup.pyscripts/test/e2e/sdks/c/e2e_basic.cscripts/test/e2e/sdks/c/e2e_exec.cscripts/test/e2e/sdks/go/e2e_basic.goscripts/test/e2e/sdks/go/e2e_copy.goscripts/test/e2e/sdks/go/e2e_exec_options.goscripts/test/e2e/sdks/node/e2e_basic.tsscripts/test/e2e/sdks/node/e2e_comprehensive.tsscripts/test/e2e/sdks/node/e2e_copy.tsscripts/test/e2e/sdks/node/e2e_exec.tsscripts/test/e2e/sdks/node/e2e_tunnel.tsscripts/test/stress/README.mdscripts/test/stress/api-create-box.k6.jsscripts/test/stress/api-vm-lifecycle.k6.js
💤 Files with no reviewable changes (1)
- .github/workflows/publish-boxlite-cloud-images.yml
| release: | ||
| name: Publish box images | ||
| runs-on: ubuntu-latest | ||
| # The workflow this replaced allowed publishing only from main; workflow_dispatch can target | ||
| # any ref, so keep that restriction. A release tag is the other legitimate source. | ||
| if: github.ref_type == 'tag' || github.ref == 'refs/heads/main' |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Add timeout-minutes to bound a stuck release.
This job has no explicit timeout, so it inherits GitHub's 360-minute default. Combined with concurrency: { group: release-box-images, cancel-in-progress: false } (repo-wide, serialized), a hung build/push step (e.g. a stalled registry push or retry loop) blocks every subsequent release for up to 6 hours with no automatic recovery.
🔧 Proposed fix
release:
name: Publish box images
runs-on: ubuntu-latest
+ timeout-minutes: 30📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| release: | |
| name: Publish box images | |
| runs-on: ubuntu-latest | |
| # The workflow this replaced allowed publishing only from main; workflow_dispatch can target | |
| # any ref, so keep that restriction. A release tag is the other legitimate source. | |
| if: github.ref_type == 'tag' || github.ref == 'refs/heads/main' | |
| release: | |
| name: Publish box images | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| # The workflow this replaced allowed publishing only from main; workflow_dispatch can target | |
| # any ref, so keep that restriction. A release tag is the other legitimate source. | |
| if: github.ref_type == 'tag' || github.ref == 'refs/heads/main' |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release-box-images.yml around lines 32 - 37, Add a
suitable timeout-minutes value to the release job definition identified by
release in the workflow, bounding stalled build or push operations while
preserving the existing ref restriction and concurrency behavior.
The curated base ref was written out 31 times under scripts/test/, so a version bump had to edit every driver. Most driver sites consulted BOXLITE_E2E_IMAGE first, but no test entrypoint exported it, which made the fallback the value normal runs used. run.sh now resolves the ref once from apps/box-images/VERSION and exports it, so pytest and the Go, C and Node subprocesses it spawns all agree. Fallbacks collapse to one place per executable ecosystem: - lib/images.py reads apps/box-images/VERSION for Python and run.sh. - image.js is shared by the Node and k6 JavaScript drivers. - sdks/go/e2e_image.go and sdks/c/e2e_image.h cover binaries compiled to /tmp with no reliable path back to the repo. Go drivers are each their own package main, so the shared constant has to be named on the build line; test_go_entry and test_go_coverage now pass it alongside the driver. Left alone deliberately: apps/api's curated-images constant is already the single definition; dashboard quickstart templates are sample code users copy; sst config entries are deploy-time env defaults.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/test/e2e/cases/test_go_coverage.py`:
- Around line 26-33: The _build_go function uses predictable shared output paths
that can collide across parallel runs or follow pre-existing symlinks. Create
the binary under a tempfile-created unique directory, ensure the directory is
cleaned up after the test completes, and preserve the existing Go build inputs
and returned Path behavior.
In `@scripts/test/e2e/fixture_setup.py`:
- Line 49: Update SNAPSHOTS_TO_REGISTER and the register_snapshot flow so the
environment-controlled result of default_image() cannot be interpolated into the
psql SQL statement. Validate image references against an appropriate safe format
before registration, or replace the raw psql command construction with a
parameterized database client while preserving cleanup of errored snapshots.
In `@scripts/test/image.js`:
- Around line 1-3: Update the DEFAULT_BOX_IMAGE export in scripts/test/image.js
to be compatible with the repository’s CommonJS package configuration, either by
using CommonJS syntax or by converting the file and its consumers to an
explicitly supported ES-module form. Ensure JavaScript test drivers can import
or require DEFAULT_BOX_IMAGE without needing an implicit module-context
override.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 59daeba7-21d8-4de9-b73c-45279d20d412
📒 Files selected for processing (33)
scripts/test/e2e/README.mdscripts/test/e2e/bench_rest_startup.pyscripts/test/e2e/cases/conftest.pyscripts/test/e2e/cases/test_c_coverage.pyscripts/test/e2e/cases/test_c_entry.pyscripts/test/e2e/cases/test_cli_comprehensive.pyscripts/test/e2e/cases/test_cli_detach_recovery.pyscripts/test/e2e/cases/test_cli_entry.pyscripts/test/e2e/cases/test_go_coverage.pyscripts/test/e2e/cases/test_go_entry.pyscripts/test/e2e/cases/test_node_comprehensive.pyscripts/test/e2e/cases/test_node_coverage.pyscripts/test/e2e/cases/test_node_entry.pyscripts/test/e2e/cases/test_node_tunnel.pyscripts/test/e2e/fixture_setup.pyscripts/test/e2e/lib/images.pyscripts/test/e2e/run.shscripts/test/e2e/sdks/c/e2e_basic.cscripts/test/e2e/sdks/c/e2e_exec.cscripts/test/e2e/sdks/c/e2e_image.hscripts/test/e2e/sdks/go/e2e_basic.goscripts/test/e2e/sdks/go/e2e_copy.goscripts/test/e2e/sdks/go/e2e_exec_options.goscripts/test/e2e/sdks/go/e2e_image.goscripts/test/e2e/sdks/node/e2e_basic.tsscripts/test/e2e/sdks/node/e2e_comprehensive.tsscripts/test/e2e/sdks/node/e2e_copy.tsscripts/test/e2e/sdks/node/e2e_exec.tsscripts/test/e2e/sdks/node/e2e_tunnel.tsscripts/test/image.jsscripts/test/stress/README.mdscripts/test/stress/api-create-box.k6.jsscripts/test/stress/api-vm-lifecycle.k6.js
🚧 Files skipped from review as they are similar to previous changes (9)
- scripts/test/e2e/sdks/c/e2e_basic.c
- scripts/test/e2e/sdks/node/e2e_exec.ts
- scripts/test/e2e/cases/conftest.py
- scripts/test/e2e/sdks/node/e2e_basic.ts
- scripts/test/stress/README.md
- scripts/test/e2e/README.md
- scripts/test/e2e/sdks/node/e2e_comprehensive.ts
- scripts/test/e2e/cases/test_node_entry.py
- scripts/test/e2e/sdks/node/e2e_tunnel.ts
| def _build_go(src_name: str) -> Path: | ||
| src = DRIVERS / src_name | ||
| assert src.exists(), f"{src} missing" | ||
| bin_path = Path(f"/tmp/boxlite_e2e_go_{src_name.replace('.go', '')}") | ||
| subprocess.run( | ||
| ["go", "build", "-o", str(bin_path), str(src)], | ||
| # Each driver is its own `package main`, so the shared image constant has | ||
| # to be named on the build line rather than picked up from the directory. | ||
| ["go", "build", "-o", str(bin_path), str(DRIVERS / "e2e_image.go"), str(src)], |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Use an isolated temporary output path.
_build_go() writes each binary to a predictable /tmp/boxlite_e2e_go_* path. Parallel E2E runs can overwrite another test’s binary. A pre-existing path or symlink can also redirect the build output on a shared runner. Use a tempfile-created directory and clean it up after the test.
🧰 Tools
🪛 ast-grep (0.45.0)
[info] 28-28: Do not hardcode temporary file or directory names
Context: f"/tmp/boxlite_e2e_go_{src_name.replace('.go', '')}"
Note: [CWE-377] Insecure Temporary File.
(hardcoded-tmp-file)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/test/e2e/cases/test_go_coverage.py` around lines 26 - 33, The
_build_go function uses predictable shared output paths that can collide across
parallel runs or follow pre-existing symlinks. Create the binary under a
tempfile-created unique directory, ensure the directory is cleaned up after the
test completes, and preserve the existing Go build inputs and returned Path
behavior.
Source: Linters/SAST tools
| or "devkey" # only used when bootstrap hasn't run yet | ||
| ) | ||
| SNAPSHOTS_TO_REGISTER = ["ghcr.io/boxlite-ai/boxlite-agent-base:20260605-p0-r3", "ubuntu:22.04", "ubuntu:24.04"] | ||
| SNAPSHOTS_TO_REGISTER = [default_image(), "ubuntu:22.04", "ubuntu:24.04"] |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Do not interpolate the environment-controlled image into SQL.
default_image() honors BOXLITE_E2E_IMAGE, so this snapshot name can contain arbitrary text. When the snapshot is in an error state, register_snapshot() inserts name into the psql -c statement at Lines 117-118. A quote or semicolon can change the statement and delete unintended data. Validate the image reference, or execute the delete with a parameterized database client.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/test/e2e/fixture_setup.py` at line 49, Update SNAPSHOTS_TO_REGISTER
and the register_snapshot flow so the environment-controlled result of
default_image() cannot be interpolated into the psql SQL statement. Validate
image references against an appropriate safe format before registration, or
replace the raw psql command construction with a parameterized database client
while preserving cleanup of errored snapshots.
| // Fallback for JavaScript test drivers run outside scripts/test/e2e/run.sh. | ||
| // Keep this in step with apps/box-images/VERSION. | ||
| export const DEFAULT_BOX_IMAGE = 'ghcr.io/boxlite-ai/boxlite-agent-base:v0.1.0' |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
fd -HI -t f 'package.json|biome.json|biome.jsonc' . | while IFS= read -r file; do
echo "== $file =="
rg -n '"type"\s*:\s*"module"|commonjs|module' "$file" || true
done
rg -n -C 3 'from .*image\.js|DEFAULT_BOX_IMAGE' scripts/test \
--glob '*.js' --glob '*.ts' || trueRepository: boxlite-ai/boxlite
Length of output: 11411
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== package.json type fields and scripts/test references =="
python3 - <<'PY'
import json, pathlib
for p in pathlib.Path('.').rglob('package.json'):
if 'node_modules' in p.parts:
continue
try:
data = json.loads(p.read_text())
except Exception:
continue
if p.name == 'package.json' and p.parts[:1] == ('.',):
print(f"== {p} ==")
print("type:", data.get("type"))
print("scripts:", {k:data["scripts"].get(k) for k in data.get("scripts", {}) if "test" in k.lower() or "e2e" in k.lower() or "run" in k.lower()})
PY
echo "== locate runner scripts =="
fd -HI -t f 'run\.sh|scripts|test' . | sed 's#^\./##' | head -200
echo "== e2e scripts/package/module related files =="
git ls-files | rg '(^scripts/test|package\.json|biome\.json|README|pnpm|tsconfig|vite|rollup|esbuild)' | sed -n '1,240p'
echo "== specific scripts/test files =="
for f in scripts/test/e2e/run.sh scripts/test/image.js package.json; do
if [ -f "$f" ]; then
echo "== $f =="
sed -n '1,160p' "$f"
fi
doneRepository: boxlite-ai/boxlite
Length of output: 17690
Make scripts/test/image.js ES-module compatible or CommonJS compatible.
The root package.json is "type": "commonjs", and scripts/test/e2e/run.sh runs only pytest rather than a Node runner that sets module context for scripts/test/image.js. If a JavaScript test driver imports DEFAULT_BOX_IMAGE, add ESMODULE=1 to the node command, use the .mjs extension, or change this export to CommonJS.
🧰 Tools
🪛 Biome (2.5.5)
[error] 3-3: Illegal use of an export declaration outside of a module
(parse)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/test/image.js` around lines 1 - 3, Update the DEFAULT_BOX_IMAGE
export in scripts/test/image.js to be compatible with the repository’s CommonJS
package configuration, either by using CommonJS syntax or by converting the file
and its consumers to an explicitly supported ES-module form. Ensure JavaScript
test drivers can import or require DEFAULT_BOX_IMAGE without needing an implicit
module-context override.
Source: Linters/SAST tools
Run fast image-resolution unit tests before bootstrap. The tests execute copies of the real run.sh and images.py with post-resolution commands stubbed, so they need neither the Linux VM nor running services. Cover a missing VERSION file, empty resolver output, a VERSION-derived default and an explicit override. Record whether the bootstrap check ran so resolver failures must stop at the intended boundary.
Signed-off-by: dorianzheng <8065637+DorianZheng@users.noreply.github.com>
Points every curated-image reference at the
v0.1.0tag, gives the box images ahome matching the rest of the repo, and splits their build from their release.
Images
All 53 references move from
20260605-p0-r3tov0.1.0across 41 files. The twotags are different image lineages, not two versions of one:
20260605-p0-r3v0.1.0COPY .../boxlite-daemon/boxlite/bin/start-agent-runtimenodekeeps stockdocker-entrypoint.sh)boxliteBecause
v0.1.0runs unprivileged, the six e2e files that wrote to/rootnow use/workspace— still on the container rootfs, socopy_outsees it, and owned bythe box user.
Layout
images/agent-runtime/→apps/box-images/, joiningapps/{api,dex,proxy,runner},which already co-locate their Dockerfiles; top-level
images/is gone.scripts/images/build-agent-runtime.sh→apps/box-images/build.shso theDockerfiles,
VERSIONand builder sit together. "agent-runtime" was ambiguous nextto
src/guest(the guest agent) andapps/runner.Workflows
build-box-images.yml(waspublish-boxlite-cloud-images.yml) validates only —both architectures at
PUSH=0,contents: read, no registry login, so it cannotwrite to GHCR.
release-box-images.ymlis the sole publisher, driven by anapps/box-images/vMAJOR.MINOR.PATCHtag (same convention as the existingsdks/go/v*tags) and restricted to a release tag ormain.Previously any edit under the image directory republished the same tag, silently
moving the bytes a running box pulls. The release path now rejects a tag that
disagrees with
VERSIONand refuses to republish an existing version unlessdispatched with
allow-overwrite. The existence check reads the registry API andbranches on HTTP status — only 404 counts as free, so a 5xx or an expired token
stops the release instead of reading as "not published".
Verification
actionlintclean on both workflows.build.shbuilds all three flavors from its new location; the resulting arm64image IDs match the pre-move builds, and the Dockerfiles are byte-identical
renames.
curated-images.constant.spec.tspasses 9/9.run:bodies from the YAMLand driving them directly: version resolution 10/10 (tag/VERSION disagreement,
missing or wrong tag prefix, prerelease, malformed input) and the overwrite guard
8/8 across 404 / 200 / 500 / 429 / 401 / token-exchange failure / empty token /
allow-overwrite.The e2e suite has not been run against the new images. The
/root→/workspacemove is derived from the image config —
images/config.rs:166reads the OCI user andportal/interfaces/container.rs:131passes it through — not from an observed run.Notes for review
mainand conflicts with2933c30bd fix(infra): harden SST deployments (#1093)inapps/infra/.env.example. Needs a rebase before merge;the resolution must keep
v0.1.0.Record published digestsrunsimagetools inspectunder
set -e, so a flaky inspect reddens the job after a successful publish. Nosilent-damage path — re-running the release hits the overwrite guard.
https://claude.ai/code/session_01T3B2RsTupPrn2gPtHymU5T
Summary by CodeRabbit
New Features
v0.1.0) for Base, Python, and Node.js environments.Bug Fixes
/workspace.Documentation