Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "feat/issue-63-local-build-from-gitea-v2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #63 (Task #194).
Summary
When
MOLECULE_IMAGE_REGISTRYis unset, the workspace-server provisioner now switches to local-build mode: clone the workspace-template repo from Gitea,docker buildit locally, and use the SHA-pinned tag for ContainerCreate. OSS contributors no longer need authenticated GHCR/ECR access to provision a workspace.Production tenants are unaffected — every prod tenant sets
MOLECULE_IMAGE_REGISTRYto its private ECR mirror, so the SaaS pull path is byte-for-byte identical.Why now
Post-2026-05-06 the
Molecule-AIGitHub org was suspended; GHCR returns 403 for everymolecule-ai/workspace-template-*manifest. OSS-defaultRegistryPrefix() = ghcr.io/molecule-aiis now broken. Reproduced 2026-05-07:Design (full rationale in ADR-002)
Resolve()returningRegistrySource{Mode, Prefix}. Discriminated value, not a bare string, so call sites that branch on mode get a compile-time push instead of a string-equality footgun.MOLECULE_IMAGE_REGISTRY(Q2 design lock from Hongming, 2026-05-07). Set ⇒ SaaS pull (existing behavior). Unset ⇒ local-build path.~/.cache/molecule/workspace-template-build/<runtime>/<sha12>/. SHA-pinned image tag short-circuits clone+build on subsequent provisions.linux/amd64(matches the provisioner's existingdefaultImagePlatform()); honorsfeedback_local_must_mimic_production. 5–10 min first build on Apple Silicon via QEMU; cached afterwards.Files
workspace-server/internal/provisioner/registry_mode.go— Resolve(), RegistryMode, IsKnownRuntime (new SSOT)workspace-server/internal/provisioner/registry_mode_test.go— 8 tests pinning mode-decision contractworkspace-server/internal/provisioner/localbuild.go— clone+build pipeline (570 LOC, 22 unit tests)workspace-server/internal/provisioner/localbuild_test.go— happy/sad paths, fail-closed contractsworkspace-server/internal/provisioner/provisioner.go— Start() insertsensureLocalImageHookin local modedocs/adr/ADR-002-local-build-mode-via-registry-presence.md— design rationale + alternatives + security reviewdocs/development/local-development.md— local-build flow + env overridesCoverage
Resolve()mode detection — set / unset / empty / garbage URL → all four contracts pinnedEnsureLocalImage— happy path, cache hit, unknown runtime, Gitea unreachable, repo not found, auth failure, JSON-shaped HEAD response, build failure, missing Dockerfile, concurrent same-runtime, context cancellation, retag-after-cache-hit, HTTP body overflow, short-sha rejection, stale-cache cleanupmaskTokenInURL/maskTokenInString— token never echoed in logs (3 cases each)giteaBranchAPIURL/parseGiteaBranchHeadSha— URL composer + JSON parser pinned against the Gitea API shapeprovisioner_test.go/registry_test.gocases preserved unchanged.Security
IsKnownRuntime()gates the clone path. Defence-in-depth against future code paths that might let cfg.Runtime carry untrusted input.https://git.moleculesai.app/molecule-ai/molecule-ai-workspace-template-by default. Forks via opt-inMOLECULE_LOCAL_TEMPLATE_REPO_PREFIX.MOLECULE_GITEA_TOKEN, if set, is masked in every log line viamaskTokenInURL/maskTokenInString.docker buildinvoked with no--build-argfrom external input.Hostile self-review (3 weakest spots)
dockerBuildProdshells out todockerrather than using the SDK. If the host has the daemon but nodockerCLI on PATH (rare; mostly podman-only setups), the build fails withexec: docker not found. Mitigation: error message is clear; runbook lists docker CLI as prereq.main. A fork with amasterdefault branch would 404. Mitigation: error names the repo URL; future improvement could probe/branchesfor the default. Out of scope.Test plan
go test -race ./internal/provisioner/— all 60+ tests pass (existing + new)go test -race ./...— full workspace-server suite greengo vet ./...— cleangofmt -l— cleanMOLECULE_IMAGE_REGISTRY=+ workspace-create → confirm clone + build + boot end-to-endFollowups
feedback_workspace_template_sandbox_is_stale).crewai,deepagents,codex,gemini-cli,openclawworkspace-template repos to Gitea — currently 4 of 9 are mirrored; the others fail with an actionable error in local-build mode.Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
OSS contributors who clone molecule-core and `go run ./workspace-server/cmd/server` now get a working end-to-end provision without authenticating to GHCR or AWS ECR. Pre-fix: with MOLECULE_IMAGE_REGISTRY unset, the provisioner attempted to pull ghcr.io/molecule-ai/workspace-template-<runtime>:latest, which has been returning 403 since the 2026-05-06 GitHub-org suspension. Post-fix: when MOLECULE_IMAGE_REGISTRY is unset, the provisioner switches to local-build mode — looks up the workspace-template-<runtime> repo's HEAD sha on Gitea via a single API call, shallow-clones into ~/.cache/molecule/, and runs `docker build --platform=linux/amd64`. SHA-pinned cache key skips the clone+build entirely on subsequent provisions. Production tenants are unaffected: every prod tenant sets the var to its private ECR mirror, so the SaaS pull path is byte-for-byte identical. SSOT for mode detection lives in Resolve() (registry_mode.go) returning a discriminated RegistrySource{Mode, Prefix} so call sites that branch on mode get a compile-time push instead of a string-equality footgun. Coverage: * registry_mode.go — new SSOT (Resolve, RegistryMode, IsKnownRuntime) * registry_mode_test.go — 8 tests pinning mode-decision contract * localbuild.go — clone+build pipeline (570 LOC, fully unit-tested) * localbuild_test.go — 22 tests covering happy/sad paths, fail-closed * provisioner.go — Start() inserts ensureLocalImageHook in local mode * docs/adr/ADR-002 — design rationale + alternatives + security review * docs/development/local-development.md — local-build flow + env overrides Security: * Allowlist-only runtime names (knownRuntimes) gate the clone path. * Repo prefix hardcoded to git.moleculesai.app/molecule-ai/molecule-ai-workspace-template-; forks via opt-in MOLECULE_LOCAL_TEMPLATE_REPO_PREFIX. * MOLECULE_GITEA_TOKEN masked in every log line via maskTokenInURL/maskTokenInString. * Fail-closed: Gitea unreachable / runtime not mirrored → clear error, never silently fall back to GHCR/ECR. * docker build invocation passes no --build-arg from external input. * HTTP body cap 64KB on Gitea API responses (defence vs malicious upstream). Closes #63 / Task #194. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Local-dev provisioner SaaS/OSS split (#194). 1585 lines additive, 30 new tests + 64 existing preserved. Hongming-locked Option C: MOLECULE_IMAGE_REGISTRY presence as mode marker. ADR-002 captures rationale. Hostile-review weakest 3 filed as #204/#205/#206 follow-ups. Ready.