Skip to content

refactor: switch deployments to the canonical Remote Dev image - #60

Merged
eXPerience83 merged 11 commits into
mainfrom
refactor/canonical-deployment-image
Aug 1, 2026
Merged

refactor: switch deployments to the canonical Remote Dev image#60
eXPerience83 merged 11 commits into
mainfrom
refactor/canonical-deployment-image

Conversation

@eXPerience83

Copy link
Copy Markdown
Owner

What changed

  • changes generic and TrueNAS Compose defaults to ghcr.io/experience83/remote-dev:edge-amd64;
  • introduces canonical REMOTE_DEV_IMAGE interpolation in both Compose files;
  • retains CODEX_IMAGE as a lower-priority fallback throughout v0.1.x;
  • gives REMOTE_DEV_IMAGE precedence when both variables are set;
  • treats an empty canonical value as unset so an existing legacy value still works;
  • updates .env.example, English/Spanish usage, release guidance, project status and changelog;
  • adds deterministic Compose tests for canonical default, legacy fallback, canonical override, conflicting values and empty-value fallback in both Compose files.

Compatibility

The exact Compose expression is:

${REMOTE_DEV_IMAGE:-${CODEX_IMAGE:-ghcr.io/experience83/remote-dev:edge-amd64}}

Therefore existing .env files containing only CODEX_IMAGE continue to work without edits. The canonical variable wins only when it is non-empty. CODEX_IMAGE and the codex-remote-dev package remain supported through all v0.1.x releases and will not be removed before v0.2.0.

Scope

This PR changes only deployment image selection and related documentation/tests. It does not rename the codex service, container_name, data-root variables, volumes or persistent paths. It does not add the launcher/gateway, additional services or optional agents.

Validation

  • shell syntax for all scripts;
  • docker compose config --images against an empty env file;
  • canonical default for generic and TrueNAS Compose;
  • legacy-only fallback;
  • canonical-only override;
  • canonical precedence over a conflicting legacy value;
  • empty canonical value falling back to legacy;
  • full existing build, runtime smoke, notices, SBOM, Trivy and fixable-critical gate.

Part of #25. This completes the canonical image/package and deployment-variable naming slice; service topology and persistence migration remain separate later PRs.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Summary by CodeRabbit

  • New Features

    • Added support for configuring deployment images with REMOTE_DEV_IMAGE.
    • Added compatibility fallback to CODEX_IMAGE for existing deployments through v0.1.x.
    • Updated default images to use the remote-dev package and canonical edge image.
  • Documentation

    • Updated English and Spanish setup, migration, release, and project-status documentation.
  • Tests

    • Added validation for image defaults, overrides, precedence, and legacy compatibility.

Walkthrough

The Compose files now use REMOTE_DEV_IMAGE and the remote-dev image by default. CODEX_IMAGE remains a fallback for existing v0.1.x deployments. New compatibility tests run through local and CI validation. Documentation reflects the new names and precedence rules.

Changes

Remote-dev image compatibility

Layer / File(s) Summary
Compose image resolution and regression coverage
compose/*.yml, scripts/test-compose-image-compat.sh, CHANGELOG.md, .env.example
Compose prioritizes REMOTE_DEV_IMAGE, then CODEX_IMAGE, then the canonical edge image. Tests cover defaults, overrides, precedence, and empty values.
Validation workflow integration
Makefile, .github/workflows/build-amd64.yml
Local and AMD64 validation now run the Compose compatibility test.
Configuration and release documentation
README.md, README.es.md, docs/releases.md, PROJECT_STATUS.md
Documentation uses the remote-dev package and REMOTE_DEV_IMAGE, and records legacy compatibility through v0.1.x.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main deployment image change.
Description check ✅ Passed The description clearly covers the changes, compatibility, scope, validation, tests, and release boundary, but omits explicit template sections for safety checks and follow-up.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/canonical-deployment-image

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Owner Author

@codex review

Please review the current head 770b61a890dc27e6f6c55008f8f24535f7a2af31. Focus only on the bounded deployment-image switch: nested Compose interpolation, REMOTE_DEV_IMAGE precedence, CODEX_IMAGE fallback through v0.1.x, the new Compose regression test, and matching documentation. Do not broaden into service renaming, launcher topology, mounts or persistence migration.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 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 `@docs/releases.md`:
- Around line 23-25: Align all image-precedence documentation with the Compose
empty-value behavior: in docs/releases.md lines 23-25 state that
REMOTE_DEV_IMAGE takes precedence only when set and non-empty, otherwise falling
back to CODEX_IMAGE; update docs/releases.md line 77 to say CODEX_IMAGE is used
when REMOTE_DEV_IMAGE is unset or empty; replace “unset” with “unset or empty”
in .env.example line 3; and update README.md line 160 and README.es.md line 113
to limit precedence to a set or non-empty REMOTE_DEV_IMAGE.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d958b850-9fa8-4a81-9a7e-834455e1bcfd

📥 Commits

Reviewing files that changed from the base of the PR and between 39a56a5 and 770b61a.

📒 Files selected for processing (11)
  • .env.example
  • .github/workflows/build-amd64.yml
  • CHANGELOG.md
  • Makefile
  • PROJECT_STATUS.md
  • README.es.md
  • README.md
  • compose/docker-compose.yml
  • compose/truenas.yml
  • docs/releases.md
  • scripts/test-compose-image-compat.sh
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: build
🧰 Additional context used
📓 Path-based instructions (4)
.github/workflows/**

⚙️ CodeRabbit configuration file

.github/workflows/**: Review GitHub Actions for least-privilege permissions, safe event triggers, untrusted input handling,
accidental publication, tag-channel correctness, secret exposure, reproducible builds, SBOM/provenance
generation and appropriate validation before pushing images.

Files:

  • .github/workflows/build-amd64.yml
compose/**

⚙️ CodeRabbit configuration file

compose/**: Review Docker Compose files for TrueNAS and generic Docker compatibility.
Flag privileged mode, host networking, Docker socket mounts, overly broad host mounts,
missing authentication, unsafe public bindings and persistence paths that could lose credentials or workspaces.

Files:

  • compose/docker-compose.yml
  • compose/truenas.yml
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Check that documentation matches the implemented behavior and clearly distinguishes experimental edge builds
from stable releases. Flag outdated versions, unsafe deployment guidance and claims not proven by CI or testing.

Files:

  • docs/releases.md
  • CHANGELOG.md
  • PROJECT_STATUS.md
  • README.es.md
  • README.md
scripts/**/*.sh

⚙️ CodeRabbit configuration file

scripts/**/*.sh: Review as production Bash. Check quoting, set -euo pipefail behavior, cleanup traps,
bounded retries and timeouts, non-interactive execution, idempotency and useful failure messages.
Flag commands that can hang, leak credentials, silently ignore failures or mutate persistent data unexpectedly.

Files:

  • scripts/test-compose-image-compat.sh
🔇 Additional comments (9)
compose/docker-compose.yml (1)

3-3: LGTM!

compose/truenas.yml (1)

3-3: LGTM!

scripts/test-compose-image-compat.sh (1)

1-62: LGTM!

CHANGELOG.md (1)

34-43: LGTM!

Makefile (1)

15-15: LGTM!

.github/workflows/build-amd64.yml (1)

52-52: LGTM!

README.md (1)

28-28: LGTM!

Also applies to: 53-53, 130-130, 151-159, 162-171

README.es.md (1)

25-25: LGTM!

Also applies to: 50-50, 103-112, 114-126

PROJECT_STATUS.md (1)

19-21: LGTM!

Comment thread docs/releases.md
Comment on lines +23 to +25
The canonical `remote-dev` package is public and is the deployment default. Generic and TrueNAS Compose use `REMOTE_DEV_IMAGE`, defaulting to `ghcr.io/experience83/remote-dev:edge-amd64`.

### Canonical-package bootstrap

A newly created GHCR package starts with private visibility. After the first workflow run creates `remote-dev`, the maintainer must open that package's settings and change its visibility to **Public** before documentation or Compose defaults point anonymous users at it. Until that one-time action is confirmed, the checked-in Compose examples continue to use the public `codex-remote-dev` compatibility package.
The `codex-remote-dev` compatibility package and `CODEX_IMAGE` variable remain supported throughout `v0.1.x` and will not be removed before `v0.2.0`. `REMOTE_DEV_IMAGE` takes precedence when both variables are set. A deprecation notice must appear in release notes before removal.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files =="
git ls-files | rg '(^|/)(README(\.es)?\.md|\.env\.example|docs/releases\.md|.*compose.*|docker-compose\.ya?ml|compose\.ya?ml)$' || true

echo
echo "== relevant docs/env lines =="
for f in docs/releases.md README.md README.es.md .env.example; do
  if [ -f "$f" ]; then
    echo "--- $f ($(wc -l < "$f") lines) ---"
    nl -ba "$f" | sed -n '1,120p'
  fi
done

echo
echo "== compose/image variable occurrences =="
rg -n "REMOTE_DEV_IMAGE|CODEX_IMAGE|remote-dev|codex-remote-dev|image:" . -S --glob '!node_modules' --glob '!dist' --glob '!build' || true

echo
echo "== docker compose syntax references in repo =="
rg -n '(\$\{[^}]*:-|:-\$\{|-[^:}]+\$\{|\$\{[^}]*#|\$\{[^}]*:?\??[^}]*}\))' . --glob '*compose*.ya?ml' --glob '*.yaml' --glob '*.yml' -S || true

echo
echo "== if compose is present, show relevant image substitutions =="
python3 - <<'PY'
from pathlib import Path
matches=[]
for p in Path('.').rglob('*'):
    if p.is_file() and p.name.endswith(('.yml','.yaml','.md','.txt','.lock')):
        try:
            s=p.read_text(errors='ignore')
        except Exception:
            continue
        if 'REMOTE_DEV_IMAGE' in s or 'CODEX_IMAGE' in s:
            matches.append(str(p))
for p in matches:
    s=Path(p).read_text(errors='ignore')
    if 'images/' in p or 'compose' in p.lower() or p.endswith('.env.example'):
        print(f'--- {p} ---')
        for i,line in enumerate(s.splitlines(),1):
            if 'REMOTE_DEV_IMAGE'在s或'CODEX_IMAGE'在s:
                print(f'{i}: {line}')
PY

Repository: eXPerience83/remote-dev-containers

Length of output: 482


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relevant env/readme/release text =="
for f in .env.example README.md README.es.md docs/releases.md; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    awk '{printf "%-6d %s\n", NR, $0}' "$f"
  fi
done

echo
echo "== compose files =="
for f in compose/docker-compose.yml compose/truenas.yml; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    awk '{printf "%-6d %s\n", NR, $0}' "$f"
  fi
done

echo
echo "== exact variable usages =="
grep -RIn -- 'REMOTE_DEV_IMAGE\|CODEX_IMAGE' . || true

echo
echo "== compose image substitution semantics check =="
python3 - <<'PY'
# Deterministic check based on compose variable expansion semantics:
# ${VAR-DEFAULT} substitutes ${VAR} when set and non-empty, else DEFAULT;
# ${VAR:-DEFAULT} substitutes DEFAULT when unset and non-empty.
from pathlib import Path

cases = {
    "unset-default":  ("REMOTE_DEV_IMAGE", "canonical", "$ ${REMOTE_DEV_IMAGE-canonical}"),
    "empty-default":   ("", "canonical", "$ ${REMOTE_DEV_IMAGE-canonical}"),
    "blank-default":   (" x ", "canonical", "$ ${REMOTE_DEV_IMAGE-canonical}"),
    "unset-coalesce":  ("REMOTE_DEV_IMAGE", "canonical", "$ ${REMOTE_DEV_IMAGE:-canonical}"),
    "empty-coalesce":  ("", "canonical", "$ ${REMOTE_DEV_IMAGE:-canonical}"),
    "blank-coalesce":  (" x ", "canonical", "$ ${REMOTE_DEV_IMAGE:-canonical}"),
}

def expand(v, default, use_default_on_blank=True):
    if v is None:
        return default
    if use_default_on_blank and v == "":
        return default
    return v

for label, (var, default, line) in cases.items():
    res = expand(var, default, use_default_on_blank=(label.endswith("-coalesce") or "coalesce" in label))
    print(f"{label}: var={var!r}, default={default!r} => {res!r} (line {line})")
print("The ${VAR-default} form only falls back when REMOTE_DEV_IMAGE is unset. docker-compose does treat unset and empty as the same substitution result for this form.")
PY

Repository: eXPerience83/remote-dev-containers

Length of output: 41067


Use one empty-value compatibility rule for the image precedence.

The Compose files use ${REMOTE_DEV_IMAGE:-${CODEX_IMAGE:-...}}, so Docker Compose only chooses the canonical image when REMOTE_DEV_IMAGE is set. Unset or empty REMOTE_DEV_IMAGE lets CODEX_IMAGE win before the canonical default. Update the matching compatibility statements.

  • docs/releases.md#L23-L25: say REMOTE_DEV_IMAGE takes precedence when set or non-empty and falls back to CODEX_IMAGE when unset or empty.
  • docs/releases.md#L77: note that legacy CODEX_IMAGE rolls back only when REMOTE_DEV_IMAGE is unset or empty.
  • .env.example#L3: replace “unset” with “unset or empty”.
  • README.md#L160 and README.es.md#L113: state precedence only applies when REMOTE_DEV_IMAGE is set or non-empty.
📍 Affects 4 files
  • docs/releases.md#L23-L25 (this comment)
  • docs/releases.md#L77-L77
  • .env.example#L3-L3
  • README.md#L160-L160
  • README.es.md#L113-L113
🤖 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 `@docs/releases.md` around lines 23 - 25, Align all image-precedence
documentation with the Compose empty-value behavior: in docs/releases.md lines
23-25 state that REMOTE_DEV_IMAGE takes precedence only when set and non-empty,
otherwise falling back to CODEX_IMAGE; update docs/releases.md line 77 to say
CODEX_IMAGE is used when REMOTE_DEV_IMAGE is unset or empty; replace “unset”
with “unset or empty” in .env.example line 3; and update README.md line 160 and
README.es.md line 113 to limit precedence to a set or non-empty
REMOTE_DEV_IMAGE.

Source: Path instructions

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 770b61a890

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@eXPerience83
eXPerience83 merged commit 18b1d6d into main Aug 1, 2026
2 checks passed
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