LearnNewsExamplesServices
Frontmatter
id12150
titleCloud deploy must source neo from a pinned clone, not a local folder
stateClosed
labels
bugaiarchitecturebuild
assigneesneo-opus-ada
createdAtMay 28, 2026, 10:27 PM
updatedAtMay 28, 2026, 11:33 PM
githubUrlhttps://github.com/neomjs/neo/issues/12150
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 28, 2026, 11:33 PM

Cloud deploy must source neo from a pinned clone, not a local folder

Closed v13.0.0/archive-v13-0-0-chunk-14 bugaiarchitecturebuild
neo-opus-ada
neo-opus-ada commented on May 28, 2026, 10:27 PM

Context

Surfaced during a real-time dogfood of #12146 on a cloud Agent OS deployment. The deployment's images build neo from a co-located local checkout (NEO_SOURCE_DIR, default ../../../../github/neomjs/neo). Two failure modes fell out immediately:

  1. A second operator cannot deploy. Anyone without that exact co-located checkout has no build context — the relative path resolves to nothing, and docker compose build fails. The deployment is only buildable on the original author's machine.
  2. The deployed neo version is "whatever the local checkout happens to be." During the dogfood the co-located checkout was on an unrelated feature branch with uncommitted WIP — so a rebuild would have baked another lane's untested working tree into the deployment image, and still lacked the merged fix being dogfooded.

The Problem

The canonical ai/deploy build sources neo via Docker COPY . . of a local build context — there is no git clone, no pinned ref. Consequences:

  • Non-portable — requires a co-located neo checkout at a hardcoded relative path on the build host. The Day-0 docs list "clone + co-locate a neo checkout" as a manual prerequisite, which is the smell.
  • Non-reproducible — the image contents equal the local working tree at build time, including uncommitted changes and whatever branch is checked out. Two builders produce different images.
  • Version-coupled to local branch state — the deployment's neo version is implicit and mutable, not declared.

neo is MIT-licensed, so cloning + baking the source into the deploy image has no license barrier.

The Architectural Reality

  • ai/deploy/Dockerfile:15COPY . . from the build context (no source acquisition step).
  • ai/deploy/docker-compose.ymlcontext: ../.. (canonical); deployment overlays set context: ${NEO_SOURCE_DIR:-<local path>}.
  • git is available in alpine via apk, so a build-time clone is feasible without changing the base image family.

The Fix

For cloud / portable deployments, acquire neo from a pinned GitHub ref at build time instead of COPY . . from a local folder:

  • Add a source-acquisition step (build ARG NEO_REF + optional ARG NEO_REPO_URL=https://github.com/neomjs/neo) that git clones neo at the pinned ref into the build stage. A shallow clone (--depth 1 --branch <ref>) also sidesteps the #12017 local-context-bloat problem by construction.
  • Prefer a tag or commit SHA for reproducibility; a branch ref (e.g. dev) is acceptable only as an explicit interim for branch-tracking deployments, with the mutability tradeoff documented.
  • Keep a local-source mode for fast dev iteration (explicit NEO_SOURCE=git-ref|local), so the convenience of the co-located checkout is preserved without being the only path.
  • Update the Day-0 / deployment docs: the co-located checkout becomes a dev convenience, not a deployment prerequisite.

Acceptance Criteria

  • AC1: the deploy build produces a working image on a host with no co-located neo checkout (portability).
  • AC2: the build pins neo to an explicit ref (tag/SHA/branch ARG); the same ref yields the same source (reproducibility).
  • AC3: a local-source dev mode is still available (fast iteration preserved).
  • AC4: deployment docs updated — co-located checkout reframed as dev-only convenience.
  • AC5: capability-framing audit pass (no downstream-deployment-partner names in any artifact).

Out of Scope

  • Sourcing neo from a published npm package or a prebuilt neo base image — a heavier, longer-term evolution; this ticket is the immediate portability fix (pinned clone).
  • Choosing the specific release tag for any given deployment — operator config, not a code change.

Avoided Traps

  • ❌ Pinning to a mutable branch as the default — undermines reproducibility; default to tag/SHA, branch only as a documented interim.
  • ❌ Removing the local-COPY path entirely — it's the right tool for dev iteration; make source-mode explicit instead of replacing one coupling with another.

Decision Record impact

aligned-with ADR 0014 (cloud deployment topology) — refines the neo-sourcing model the deployment topology depends on. If ADR 0014 codified the co-located-checkout model explicitly, this amends that detail.

Related

  • #12017 (missing root .dockerignore → 90 GB build context) — same local-COPY . root mechanism, different facet (size); a shallow pinned clone addresses both.
  • ADR 0014 (cloud deployment topology + scheduler task taxonomy).
  • Surfaced during the #12146 (pull-mode tenantRepos tiered resolver) real-time dogfood.

Handoff Retrieval Hints

  • Retrieval Hint: "cloud deploy neo source local folder COPY vs pinned github clone reproducible portable"
tobiu referenced in commit e0be8b7 - "feat(deploy): source neo from a pinned git clone, not a local folder (#12150) (#12151) on May 28, 2026, 11:33 PM
tobiu closed this issue on May 28, 2026, 11:33 PM