claudecodeui Submodule → In‑Repo Migration (History Preserved)
This document describes how to replace the claudecodeui git submodule with a regular in‑repo directory while preserving its history, and how to update Docker, scripts, and GitHub Actions accordingly. It includes step‑by‑step commands, validation, and rollback.
Decisions (Confirmed)
- History mode: Preserve full commit history (no
--squash). - Import source:
mainbranch fromhttps://github.com/wapdat/sasha-studio.git. - Path: Keep directory name
claudecodeui/as‑is. - Dockerfile: Use
claudecodeui/Dockerfile.sliplanefor all builds. - Node version: Keep Node v20 (from
claudecodeui/.nvmrc) for CI and Docker. - Registry: Continue publishing to GHCR using default
GITHUB_TOKEN. - Upstream sync: No need to keep a persistent subtree remote configured after migration.
- Build scope now: Minimal changes to simplify build; defer extra npm ci/lint/test enhancements to a second phase.
Objectives
- Remove submodule complexity and errors; make
claudecodeui/a normal folder. - Preserve
claudecodeuicommit history (viagit subtree). - Keep dev/Docker build parity and GHCR release flow.
- Update CI/CD and scripts to stop depending on submodules.
Scope
- Repo structure remains: UI + server in
claudecodeui/; docs underdocs/. - Docker images and tags remain the same (VERSION‑driven).
- No change to app behavior; this is an operational migration.
Summary Of Changes (already applied in this repo)
- GitHub Actions:
- Updated
.github/workflows/docker-build.ymlto remove submodule checkout. - Added
.github/workflows/ci.ymlto build UI and docs on push/PR.
- Updated
- Scripts:
docker-build-ghcr.sh: removed submodule push logic; now commits VERSION only.docker-dev.sh: removed submodule awareness; supports running from UI dir.
You can proceed with the history‑preserving import and cutover steps below.
Phase 1 — Discovery
- Locate submodule info and current SHA:
git config -f .gitmodules --get submodule.claudecodeui.url
git submodule status -- claudecodeui
- Scan for submodule usage in scripts/CI:
rg -n "submodule|recurse-submodules|git submodule" -S
- Confirm build flows still expected post‑migration:
- Local dev:
./dev.sh(server+UI at :3007) /cd claudecodeui && npm run dev. - Docker dev:
./docker-dev.sh(container at :3006). - GHCR:
./docker-build-ghcr.sh [patch|minor|major].
Phase 2 — History Strategy (Chosen: Preserve via Subtree)
We will import the external UI repository into ./claudecodeui/ using git subtree, preserving the full commit history under that path (no --squash). If you prefer a snapshot, see Appendix B.
Phase 3 — Remove Submodule Wiring (Precondition: Commit Local Changes)
Create a feature branch for the migration:
git checkout -b feat/inline-claudecodeui
Precondition — ensure all local changes (including inside the current submodule working tree) are committed to the main repo before proceeding. This avoids losing any untracked work.
Remove the submodule (gitlink + metadata):
git rm -f claudecodeui
rm -rf .git/modules/claudecodeui
sed -i.bak '/submodule "claudecodeui"/,+3d' .gitmodules || true
git add .gitmodules || true
git commit -m "chore(repo): remove claudecodeui submodule wiring"
Validate the worktree no longer has a submodule at that path.
Phase 4 — Import claudecodeui With git subtree
Add the UI repo as a remote:
git remote add claudecodeui-remote <CLAUDECODEUI_REMOTE_URL>
git fetch claudecodeui-remote
Import to ./claudecodeui/ with full history from main:
# Preserve full history (no --squash)
git subtree add --prefix=claudecodeui claudecodeui-remote main
If you later decide to re‑sync from upstream:
git subtree pull --prefix=claudecodeui claudecodeui-remote main
Resolve any conflicts, then commit.
Phase 5 — Fix Paths & Config (if needed)
Most imports already reference in‑repo paths (e.g., claudecodeui/server/...). After subtree import:
- Ensure
.gitignoredoes not ignoreclaudecodeui/. - Keep Node
v20viaclaudecodeui/.nvmrc(CI uses it). - Keep environment usage (
.env.localfor local only; no secrets committed).
Phase 6 — Dockerfiles & Scripts
- Dockerfile path remains
claudecodeui/Dockerfile.sliplane. - Ensure build context includes
claudecodeui/(not excluded in.dockerignore). - Scripts (already updated here):
docker-build-ghcr.sh— commits VERSION only; no submodule push.docker-dev.sh— no submodule checks; builds using local code.
Out‑of‑scope (Phase 2 later): npm ci/lint/test improvements will be added in a follow‑up to keep this migration minimal.
Phase 7 — GitHub Actions (applied)
- Build & Push Docker (updated):
.github/workflows/docker-build.yml
- Uses
actions/checkout@v4without submodules. - Builds with
claudecodeui/Dockerfile.sliplane. - Tags include
VERSION+ semver + latest.
- CI (new):
.github/workflows/ci.yml
- Job
ui-build: Node v20 from.nvmrc,npm ci,npm run build, uploaddist. - Job
docs-build: installs doc builder and runsnpx @knowcode/doc-builder -c doc-builder.config.js.
Triggers remain minimal: PRs build artifacts; pushes to main build and publish Docker images. No submodule tokens required.
Phase 8 — Validation Checklist
- Local:
./dev.sh→ app athttp://localhost:3007cd claudecodeui && npm ci && npm run buildpasses
- Docker:
./docker-dev.sh→ app athttp://localhost:3006- Verify file browser, markdown preview, binary content endpoint
- CI:
- PR triggers
ci.yml(UI & Docs) anddocker-build.ymlon main push
- PR triggers
- Versioning:
./docker-build-ghcr.sh patchbumpsVERSION, pushes, and triggers Docker build
Phase 9 — Cutover
- Open PR with:
- Submodule removal commit(s)
- Subtree import commit
- CI/scripts updates (already in this repo)
- Rationale & rollback section
- After merge:
- Validate Actions runs (UI build, docs build, Docker image publish)
- Optionally tag or use
./docker-build-ghcr.shto release a version - Remove submodule references from
CLAUDE.mdand related docs (included in PR)
Phase 10 — Rollback Plan
- If needed, revert the subtree import and submodule removal commits to restore the original state.
- Keep a branch with subtree history for future reattempt.
Appendix A — Command Cheat Sheet
# Inspect submodule
git config -f .gitmodules --get submodule.claudecodeui.url
git submodule status -- claudecodeui
# Remove submodule
git rm -f claudecodeui
rm -rf .git/modules/claudecodeui
sed -i.bak '/submodule "claudecodeui"/,+3d' .gitmodules || true
git add .gitmodules || true
git commit -m "chore(repo): remove claudecodeui submodule wiring"
# Import via subtree (history preserved, full history)
git remote add claudecodeui-remote <REMOTE_URL>
git fetch claudecodeui-remote
git subtree add --prefix=claudecodeui claudecodeui-remote main
# Optional future sync (if you choose to keep the remote)
git subtree pull --prefix=claudecodeui claudecodeui-remote main
Appendix B — Alternative: Vendor Snapshot (No History)
If you choose not to preserve history:
git rm -f claudecodeui
rm -rf .git/modules/claudecodeui .gitmodules
# Copy the submodule working tree contents into ./claudecodeui/
cp -R /path/to/checkedout/claudecodeui ./claudecodeui
git add claudecodeui
git commit -m "chore(repo): vendor claudecodeui snapshot into repo"
Pros: simplest. Cons: lose UI history inside this repo.
Notes
- Docker image and VERSION tagging remain unchanged.
- If any docs refer to “submodules”, update those references after cutover.
- The shared docs root detection (e.g.,
SHARED_DOCS_ROOT) remains unaffected by this migration.