chore(makefile): add worktree-new/worktree-rm targets for isolated dev servers
Encode the dual-repo worktree SOP as make targets so no step is skipped:
worktree-new creates the outer worktree off main, inits its own user/
submodule, branches both repos, and starts an isolated Grav dev server on an
auto-picked free port (8090+) whose identity is persisted in a git-ignored
.worktree-env; worktree-rm tears it all down including the submodule deinit
that, when skipped by hand, leaves orphaned .worktrees/ dirs.
docker-compose.yml container_name + ports are parametrized as ${VAR:-default}
so the main checkout is byte-identical, and the 11 hardcoded intotheeast_grav
refs in local targets now use $(GRAV_CONTAINER). CLAUDE.md points at the
commands instead of the manual steps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDS6t8wcpbwKvvrxykVQ5K
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
.env
|
||||
.env.prod
|
||||
.env.test
|
||||
# Per-worktree dev-server identity, written by `make worktree-new`
|
||||
.worktree-env
|
||||
|
||||
# Grav CMS
|
||||
/user/
|
||||
|
||||
@@ -155,7 +155,7 @@ Only these folders are tracked in the `user/` Git repo: `pages/`, `config/`, `ac
|
||||
- **Two repos, two cadences.** Outer repo = dev environment (tests/docs/scripts/Docker). `user/` = content + theme, with its own remote and `make content-push` cadence. The outer repo pins an exact `user/` commit via the `user` gitlink.
|
||||
- **Pointer-bump convention.** Routine content changes → **do not** bump the pin (leave it stale; harmless). At the **end of a cross-repo feature** → bump the pin once to the finished `user/` commit. Pin a commit reachable from `user/`'s published `main` (prefer the merge-to-main commit, not a squash-away branch tip), and **push `user/` before the outer repo** (superproject references a child SHA that must already exist upstream). The pin is dev-side coordination only — production pulls `user/` via the content webhook independently.
|
||||
- **`M user` / `m user` is normal.** `M` = pin differs from `user/` HEAD (bump pending/intentional). `m` = submodule working tree dirty (e.g. local-testing `config/site.yaml`). Neither is an error — do not "fix" them by committing the gitlink or the `site.yaml`.
|
||||
- **Worktrees for parallel work.** A worktree off `main` gets its own `user/` (`git submodule update --init user`) and can run its own dev server (`docker compose -p itte-<feature> up` — the `./user` mount is relative, so each worktree serves its own content). Tooling worktrees live under `.worktrees/` (excluded via `.git/info/exclude`). To add a commit to `main` while the main checkout is on another branch, use a throwaway `main` worktree rather than `git checkout main`.
|
||||
- **Worktrees for parallel work — use the make targets, don't do it by hand.** `make worktree-new NAME=<feature>` (from the main checkout) creates the outer worktree off `main`, initialises its own `user/` submodule, branches both, and starts an **isolated** dev server (own container name + auto-assigned port `8090+`, persisted in a git-ignored `.worktree-env` so every `make`/compose command in that worktree targets its own server). `make worktree-rm NAME=<feature>` tears it down cleanly (compose down → `submodule deinit` → `worktree remove` → `prune`) — skipping the deinit is what leaves orphaned `.worktrees/` dirs. Worktrees live under `.worktrees/` (excluded via `.git/info/exclude`). A fresh worktree's `user/` is empty until the submodule init runs, and `M user`/`m user` is normal (see above) — do not "fix" either. To add a commit to `main` while the main checkout is on another branch, use a throwaway `main` worktree rather than `git checkout main`.
|
||||
|
||||
## 1. Environment modes
|
||||
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
# for ${UID}/${GID} substitution and the travel-memories env_file.)
|
||||
-include .env
|
||||
|
||||
# Per-worktree dev-server identity, written by `make worktree-new` into the new
|
||||
# worktree only (git-ignored). Absent in the main checkout, so the defaults below
|
||||
# apply there. Loaded here so every local target + compose call in a worktree
|
||||
# targets that worktree's own container and ports.
|
||||
-include .worktree-env
|
||||
|
||||
# Remote config — loaded only when targeting an environment. ENV is set
|
||||
# automatically by the env-suffixed remote targets (e.g. `make remote-install-prod`);
|
||||
# each .env.<ENV> holds a full, self-contained set of remote vars.
|
||||
@@ -49,7 +55,7 @@ GRAV_TEST_USER ?= testrunner
|
||||
GRAV_TEST_PASS ?= Testpass1234
|
||||
|
||||
test-account:
|
||||
@docker exec intotheeast_grav sh -c 'test -f /var/www/html/user/accounts/$(GRAV_TEST_USER).yaml \
|
||||
@docker exec $(GRAV_CONTAINER) sh -c 'test -f /var/www/html/user/accounts/$(GRAV_TEST_USER).yaml \
|
||||
|| php bin/plugin login new-user -u $(GRAV_TEST_USER) -p "$(GRAV_TEST_PASS)" \
|
||||
-e $(GRAV_TEST_USER)@example.test -N "Test Runner" -P b --admin-type both -s enabled -n'
|
||||
|
||||
@@ -66,6 +72,13 @@ test: test-config test-post test-ui
|
||||
|
||||
# ── Local dev ──────────────────────────────────────────────────────────────────
|
||||
|
||||
# Dev-server identity. Defaults are the main checkout's canonical values; a
|
||||
# worktree's .worktree-env (above) overrides them so servers never collide.
|
||||
# Exported (via the top-of-file `export`) so `docker compose` picks them up.
|
||||
GRAV_CONTAINER ?= intotheeast_grav
|
||||
GRAV_PORT ?= 8081
|
||||
TM_PORT ?= 8082
|
||||
|
||||
build:
|
||||
docker compose build
|
||||
|
||||
@@ -78,19 +91,24 @@ build-assets:
|
||||
start:
|
||||
docker compose up -d
|
||||
|
||||
# Grav service only — used by `make worktree-new` (a worktree rarely needs the
|
||||
# travel-memories service, and this keeps its footprint minimal).
|
||||
start-grav:
|
||||
docker compose up -d grav
|
||||
|
||||
stop:
|
||||
docker compose down
|
||||
|
||||
setup: build start install-plugins fix-perms
|
||||
|
||||
fix-perms:
|
||||
docker exec intotheeast_grav bash -c "getent passwd 1000 > /dev/null || useradd -u 1000 -M hostuser"
|
||||
docker exec intotheeast_grav chown -R 1000:1000 /var/www/html
|
||||
docker exec intotheeast_grav apachectl graceful
|
||||
docker exec $(GRAV_CONTAINER) bash -c "getent passwd 1000 > /dev/null || useradd -u 1000 -M hostuser"
|
||||
docker exec $(GRAV_CONTAINER) chown -R 1000:1000 /var/www/html
|
||||
docker exec $(GRAV_CONTAINER) apachectl graceful
|
||||
|
||||
|
||||
install-plugins:
|
||||
docker exec -w /var/www/html intotheeast_grav php bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y
|
||||
docker exec -w /var/www/html $(GRAV_CONTAINER) php bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y
|
||||
$(MAKE) apply-plugin-patches
|
||||
|
||||
# Re-apply local fixes to git-ignored, GPM-managed third-party plugins. Run this
|
||||
@@ -105,13 +123,54 @@ apply-plugin-patches:
|
||||
fi; \
|
||||
done
|
||||
|
||||
# ── Worktrees ─────────────────────────────────────────────────────────────────
|
||||
# Isolated outer-repo worktree + its own user/ submodule checkout + its own dev
|
||||
# server (distinct container name & ports), for long-running feature work that
|
||||
# runs in parallel with the main checkout without collisions. Encodes the full
|
||||
# SOP from docs/solutions/architecture-patterns/dual-repo-submodule-workflow.md
|
||||
# so no step (submodule init, per-server isolation, clean teardown) is skipped.
|
||||
#
|
||||
# make worktree-new NAME=my-feature [PORT=8090] # create branch + start server
|
||||
# make worktree-rm NAME=my-feature # tear down cleanly
|
||||
#
|
||||
# Run both from the MAIN checkout. After worktree-new, `cd .worktrees/<name>`
|
||||
# and use make as normal — it targets that worktree's own server automatically.
|
||||
|
||||
WT_DIR = .worktrees/$(NAME)
|
||||
|
||||
guard-name:
|
||||
@test -n "$(NAME)" || { echo "ERROR: set NAME=, e.g. 'make worktree-new NAME=my-feature'."; exit 1; }
|
||||
|
||||
worktree-new: guard-name
|
||||
@test ! -e "$(WT_DIR)" || { echo "ERROR: $(WT_DIR) already exists."; exit 1; }
|
||||
git worktree add "$(WT_DIR)" -b feat/$(NAME) main
|
||||
git -C "$(WT_DIR)" submodule update --init user
|
||||
git -C "$(WT_DIR)/user" checkout -b feat/$(NAME)
|
||||
@port=$${PORT:-$$(for p in $$(seq 8090 8099); do \
|
||||
docker ps --format '{{.Ports}}' | grep -q ":$$p->" || { echo $$p; break; }; \
|
||||
done)}; \
|
||||
test -n "$$port" || { echo "ERROR: no free port in 8090-8099; pass PORT= explicitly."; exit 1; }; \
|
||||
printf 'COMPOSE_PROJECT_NAME=itte-%s\nGRAV_CONTAINER=itte_%s_grav\nGRAV_PORT=%s\nTM_PORT=%s\n' \
|
||||
"$(NAME)" "$(NAME)" "$$port" "$$((port + 100))" > "$(WT_DIR)/.worktree-env"; \
|
||||
echo "→ starting this worktree's Grav dev server on http://localhost:$$port"; \
|
||||
$(MAKE) -C "$(WT_DIR)" start-grav
|
||||
@echo "Worktree ready: $(WT_DIR) (outer + user/ on branch feat/$(NAME))"
|
||||
|
||||
worktree-rm: guard-name
|
||||
@test -e "$(WT_DIR)" || { echo "ERROR: $(WT_DIR) does not exist."; exit 1; }
|
||||
-$(MAKE) -C "$(WT_DIR)" stop
|
||||
-git -C "$(WT_DIR)" submodule deinit -f user
|
||||
git worktree remove --force "$(WT_DIR)"
|
||||
git worktree prune
|
||||
@echo "Removed $(WT_DIR). If feat/$(NAME) is merged, drop it: git branch -d feat/$(NAME)"
|
||||
|
||||
# ── Demo content ──────────────────────────────────────────────────────────────
|
||||
|
||||
demo-load:
|
||||
# Load every fixture trip under docs/demo/trips/ into the pages tree.
|
||||
# Source uses dailies/ + 04.stories/; dailies/ maps to 01.dailies/ on copy.
|
||||
# All copies are `|| true` so a fixture absent from an older user/ is skipped.
|
||||
docker exec intotheeast_grav bash -c 'for src in /var/www/html/user/docs/demo/trips/*/; do \
|
||||
docker exec $(GRAV_CONTAINER) bash -c 'for src in /var/www/html/user/docs/demo/trips/*/; do \
|
||||
slug=$$(basename "$$src"); dst=/var/www/html/user/pages/01.trips/$$slug; \
|
||||
mkdir -p "$$dst/01.dailies" "$$dst/04.stories"; \
|
||||
cp "$$src/trip.md" "$$dst/trip.md" 2>/dev/null || true; \
|
||||
@@ -123,15 +182,15 @@ demo-load:
|
||||
done; cd /var/www/html && php bin/grav clearcache'
|
||||
|
||||
demo-reset:
|
||||
docker exec intotheeast_grav bash -c 'for src in /var/www/html/user/docs/demo/trips/*/; do \
|
||||
docker exec $(GRAV_CONTAINER) bash -c 'for src in /var/www/html/user/docs/demo/trips/*/; do \
|
||||
rm -rf /var/www/html/user/pages/01.trips/$$(basename "$$src"); \
|
||||
done; cd /var/www/html && php bin/grav clearcache'
|
||||
|
||||
pixelfed-import:
|
||||
docker exec intotheeast_grav bash -c "which python3 || apt-get install -y python3 --no-install-recommends -q"
|
||||
docker cp /home/mischa/Nextcloud/Downloads/pixelfed/pixelfed-statuses.json intotheeast_grav:/tmp/pixelfed-statuses.json
|
||||
docker cp scripts/pixelfed-import.py intotheeast_grav:/tmp/pixelfed-import.py
|
||||
docker exec -w /var/www/html intotheeast_grav python3 /tmp/pixelfed-import.py
|
||||
docker exec $(GRAV_CONTAINER) bash -c "which python3 || apt-get install -y python3 --no-install-recommends -q"
|
||||
docker cp /home/mischa/Nextcloud/Downloads/pixelfed/pixelfed-statuses.json $(GRAV_CONTAINER):/tmp/pixelfed-statuses.json
|
||||
docker cp scripts/pixelfed-import.py $(GRAV_CONTAINER):/tmp/pixelfed-import.py
|
||||
docker exec -w /var/www/html $(GRAV_CONTAINER) python3 /tmp/pixelfed-import.py
|
||||
|
||||
# ── Content sync (user repo ↔ Gitea) ──────────────────────────────────────────
|
||||
|
||||
|
||||
+5
-3
@@ -1,13 +1,15 @@
|
||||
services:
|
||||
grav:
|
||||
build: .
|
||||
container_name: intotheeast_grav
|
||||
# Overridable so a git worktree can run its own isolated dev server (see
|
||||
# `make worktree-new`); unset → the canonical main-checkout values below.
|
||||
container_name: ${GRAV_CONTAINER:-intotheeast_grav}
|
||||
environment:
|
||||
- GRAV_CHANNEL=production
|
||||
- APACHE_RUN_USER=#1000
|
||||
- APACHE_RUN_GROUP=#1000
|
||||
ports:
|
||||
- "8081:80"
|
||||
- "${GRAV_PORT:-8081}:80"
|
||||
volumes:
|
||||
- ./user:/var/www/html/user
|
||||
- ./php/php-local.ini:/usr/local/etc/php/conf.d/php-local.ini
|
||||
@@ -16,7 +18,7 @@ services:
|
||||
travel-memories:
|
||||
build: ./services/travel-memories
|
||||
ports:
|
||||
- "8082:8082"
|
||||
- "${TM_PORT:-8082}:8082"
|
||||
volumes:
|
||||
- ./docs/immich-workflow:/app/state
|
||||
- ./user/pages:/app/pages
|
||||
|
||||
Reference in New Issue
Block a user