From c0932979ee0eda41cb3405ecbf0080489c66c5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Sch=C3=B6ldstr=C3=B6m?= Date: Fri, 21 Aug 2026 14:18:40 -0300 Subject: [PATCH] v0.2: ddev start in worktrees with forked DB and Caddy hooks. Replace sidecar web + shared DB with full DDEV per worktree: unique project name via config fragment, DB cloned from canonical on first start (no search-replace), Caddy :808x proxy to the worktree web container. Hooks: wt-prepare (pre-start), wt-post-start, wt-pre-stop. Add wt-sync-db; deprecate wt-up, wt-guard, wt-wp. Co-authored-by: Cursor --- README.md | 50 ++-- commands/host/wt-down | 4 +- commands/host/wt-guard | 22 -- commands/host/wt-list | 4 +- commands/host/wt-port | 4 +- commands/host/wt-post-start | 13 + commands/host/wt-pre-stop | 13 + commands/host/wt-prepare | 13 + commands/host/wt-sync-db | 17 ++ commands/host/wt-up | 51 +--- commands/host/wt-wp | 28 +- config.generoi-worktree.yaml | 8 +- generoi-worktree/README.md | 45 +-- generoi-worktree/lib.sh | 425 +++++++++++++++++----------- generoi-worktree/test-acceptance.sh | 197 +++++-------- install.yaml | 25 +- 16 files changed, 481 insertions(+), 438 deletions(-) delete mode 100755 commands/host/wt-guard create mode 100755 commands/host/wt-post-start create mode 100755 commands/host/wt-pre-stop create mode 100755 commands/host/wt-prepare create mode 100755 commands/host/wt-sync-db diff --git a/README.md b/README.md index a91c291..458d0dc 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,12 @@ DDEV add-on for parallel [git worktree](https://git-scm.com/docs/git-worktree) development on Genero Bedrock sites. -Shared MariaDB by default; Caddy (`replace-response`) rewrites browser URLs to `:808x` while PHP/DB keep canonical DDEV hostnames. +**v0.2:** `ddev start` in worktrees — own MariaDB (cloned from canonical on first start, no search-replace), shared uploads via symlinks, Caddy on `:808x` for canonical browser URLs. ## Install ```bash -ddev add-on get generoi/ddev-worktree --version v0.1.3 -``` - -If the add-on registry lookup fails, use the release tarball directly: - -```bash -ddev add-on get https://github.com/generoi/ddev-worktree/tarball/v0.1.3 +ddev add-on get generoi/ddev-worktree ``` Or from a local checkout while developing the add-on: @@ -24,39 +18,49 @@ ddev add-on get /path/to/ddev-worktree ## Project setup (Bedrock) -1. **`.gitignore`** — `.ddev/wt/` (generated Caddyfile + state). +Add to `.gitignore`: -2. **Multisite** — list subsite hostnames in `additional_hostnames` (e.g. `nat.herrfors`). +``` +.ddev/wt/ +.ddev/config.generoi-worktree.local.yaml +.ddev/generoi-worktree/ +.ddev/commands/host/wt-* +.ddev/addon-metadata/generoi-worktree/ +``` -The add-on installs `config.generoi-worktree.yaml` with the `wt-guard` pre-start hook — no manual `config.yaml` edits. +Multisite: list subsite hostnames in `additional_hostnames` (e.g. `nat.herrfors`). -No Bedrock changes required — Caddy sends `{http.request.host}` (hostname without `:port`) to PHP and rewrites `:808x` back into browser responses. +No Bedrock changes — Caddy sends `{http.request.host}` to PHP and rewrites `:808x` into browser responses. ## Usage -**Canonical checkout:** `ddev start` +**Canonical checkout:** + +```bash +ddev start +# https://herrfors.ddev.site +``` **Git worktree:** ```bash git worktree add ../myproject-feature feature-branch cd ../myproject-feature -ddev wt-up +ddev start +# https://herrfors.ddev.site:808x (Caddy proxy; own DB forked from canonical on first start) ddev wt-port -ddev wt-down +ddev stop ``` | Command | Description | |---------|-------------| -| `ddev wt-up` | Shared DB + Caddy on `:808x` | -| `ddev wt-up --port=8083` | Pin host port | -| `ddev wt-up --no-deps` | Skip composer/pnpm | +| `ddev start` | Worktree: full DDEV + Caddy `:808x` + DB seed (first time) | | `ddev wt-port` | Print `WT_URL` / `WT_PORT` | -| `ddev wt-wp …` | WP-CLI in sidecar | -| `ddev wt-list` | Running sidecars | -| `ddev wt-down` | Stop sidecar + Caddy | +| `ddev wt-sync-db` | Re-clone DB from canonical (manual) | +| `ddev wt-list` | Running Caddy proxies | +| `ddev wt-down` | Stop Caddy only (web/db keep running) | -See [generoi-worktree/README.md](generoi-worktree/README.md) for architecture details. +Removed in v0.2: `wt-up`, `wt-guard`, `wt-wp` — use `ddev start` and `ddev wp`. ## Acceptance tests @@ -65,3 +69,5 @@ From a git worktree checkout (canonical DDEV running): ```bash bash .ddev/generoi-worktree/test-acceptance.sh ``` + +See [generoi-worktree/README.md](generoi-worktree/README.md) for architecture details. diff --git a/commands/host/wt-down b/commands/host/wt-down index 87365f4..b652d4f 100755 --- a/commands/host/wt-down +++ b/commands/host/wt-down @@ -1,7 +1,7 @@ #!/usr/bin/env bash ## #ddev-generated: If you want to edit and own this file, remove this line. -## Description: Stop this worktree's sidecar container +## Description: Stop Caddy proxy (DDEV web/db keep running) ## Usage: wt-down ## Example: "ddev wt-down" ## OSTypes: linux,darwin @@ -14,4 +14,4 @@ source "$(git rev-parse --show-toplevel 2>/dev/null || pwd -P)/.ddev/generoi-wor wt_require docker wt_ensure_ddev_project -wt_sidecar_stop +wt_caddy_stop diff --git a/commands/host/wt-guard b/commands/host/wt-guard deleted file mode 100755 index cc272f7..0000000 --- a/commands/host/wt-guard +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -## #ddev-generated: If you want to edit and own this file, remove this line. -## Description: Block ddev start on git worktrees (use ddev wt-up instead) -## Usage: (internal) wt-guard -## OSTypes: linux,darwin - -set -euo pipefail - -[[ -f .ddev/config.yaml ]] || exit 0 - -# shellcheck disable=SC1091 -source .ddev/generoi-worktree/lib.sh - -if wt_is_canonical_checkout; then - exit 0 -fi - -echo "generoi-worktree: refusing 'ddev start' in a git worktree." >&2 -echo "generoi-worktree: use 'ddev wt-up' here (shared canonical DB, no DB rewrite)." >&2 -echo "generoi-worktree: canonical checkout: $(wt_canonical_approot)" >&2 -exit 1 diff --git a/commands/host/wt-list b/commands/host/wt-list index f9e3f62..3553bad 100755 --- a/commands/host/wt-list +++ b/commands/host/wt-list @@ -1,7 +1,7 @@ #!/usr/bin/env bash ## #ddev-generated: If you want to edit and own this file, remove this line. -## Description: List generoi worktree sidecars for this canonical project +## Description: List generoi worktree Caddy proxies for this canonical project ## Usage: wt-list ## Example: "ddev wt-list" ## OSTypes: linux,darwin @@ -17,6 +17,6 @@ wt_ensure_ddev_project name=$(wt_canonical_name) docker ps -a \ - --filter "label=com.generoi.worktree=true" \ + --filter "label=com.generoi.worktree-proxy=caddy" \ --filter "label=com.generoi.canonical=${name}" \ --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}\t{{.Label "com.generoi.approot"}}' diff --git a/commands/host/wt-port b/commands/host/wt-port index 7486e10..079a324 100755 --- a/commands/host/wt-port +++ b/commands/host/wt-port @@ -1,7 +1,7 @@ #!/usr/bin/env bash ## #ddev-generated: If you want to edit and own this file, remove this line. -## Description: Print this worktree sidecar URL (WT_URL) +## Description: Print worktree browse URL (WT_URL on :808x) ## Usage: wt-port ## Example: "ddev wt-port" ## OSTypes: linux,darwin @@ -15,7 +15,7 @@ source "$(git rev-parse --show-toplevel 2>/dev/null || pwd -P)/.ddev/generoi-wor wt_ensure_ddev_project if ! wt_read_state >/dev/null 2>&1; then - echo "generoi-worktree: no active sidecar (run ddev wt-up)" >&2 + echo "generoi-worktree: no Caddy proxy state (run ddev start in this worktree)" >&2 exit 1 fi diff --git a/commands/host/wt-post-start b/commands/host/wt-post-start new file mode 100755 index 0000000..91cb4e3 --- /dev/null +++ b/commands/host/wt-post-start @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +## #ddev-generated: If you want to edit and own this file, remove this line. +## Description: Start Caddy proxy and seed DB on first worktree start +## Usage: (internal) wt-post-start +## OSTypes: linux,darwin + +set -euo pipefail + +# shellcheck disable=SC1091 +source "$(git rev-parse --show-toplevel 2>/dev/null || pwd -P)/.ddev/generoi-worktree/lib.sh" + +wt_post_start diff --git a/commands/host/wt-pre-stop b/commands/host/wt-pre-stop new file mode 100755 index 0000000..808084f --- /dev/null +++ b/commands/host/wt-pre-stop @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +## #ddev-generated: If you want to edit and own this file, remove this line. +## Description: Stop Caddy proxy before DDEV stops +## Usage: (internal) wt-pre-stop +## OSTypes: linux,darwin + +set -euo pipefail + +# shellcheck disable=SC1091 +source "$(git rev-parse --show-toplevel 2>/dev/null || pwd -P)/.ddev/generoi-worktree/lib.sh" + +wt_pre_stop diff --git a/commands/host/wt-prepare b/commands/host/wt-prepare new file mode 100755 index 0000000..90ae405 --- /dev/null +++ b/commands/host/wt-prepare @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +## #ddev-generated: If you want to edit and own this file, remove this line. +## Description: Prepare git worktree DDEV project (unique name, bootstrap links) +## Usage: (internal) wt-prepare +## OSTypes: linux,darwin + +set -euo pipefail + +# shellcheck disable=SC1091 +source "$(git rev-parse --show-toplevel 2>/dev/null || pwd -P)/.ddev/generoi-worktree/lib.sh" + +wt_prepare diff --git a/commands/host/wt-sync-db b/commands/host/wt-sync-db new file mode 100755 index 0000000..2d8a71e --- /dev/null +++ b/commands/host/wt-sync-db @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +## #ddev-generated: If you want to edit and own this file, remove this line. +## Description: Re-clone database from canonical (worktrees only) +## Usage: wt-sync-db +## Example: "ddev wt-sync-db" +## OSTypes: linux,darwin +## ProjectTypes: php,wordpress + +set -euo pipefail + +# shellcheck disable=SC1091 +source "$(git rev-parse --show-toplevel 2>/dev/null || pwd -P)/.ddev/generoi-worktree/lib.sh" + +wt_require ddev +wt_ensure_ddev_project +wt_sync_db diff --git a/commands/host/wt-up b/commands/host/wt-up index a5688cd..28bfaa4 100755 --- a/commands/host/wt-up +++ b/commands/host/wt-up @@ -1,57 +1,14 @@ #!/usr/bin/env bash ## #ddev-generated: If you want to edit and own this file, remove this line. -## Description: Start a worktree sidecar (shared DB + Caddy URL rewrite) -## Usage: wt-up [--port=808x] [--no-deps] -## Example: "ddev wt-up" or "ddev wt-up --port=8083 --no-deps" +## Description: (removed) use ddev start in git worktrees +## Usage: wt-up ## OSTypes: linux,darwin ## ProjectTypes: php,wordpress set -euo pipefail -APPROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd -P) # shellcheck disable=SC1091 -source "$APPROOT/.ddev/generoi-worktree/lib.sh" +source "$(git rev-parse --show-toplevel 2>/dev/null || pwd -P)/.ddev/generoi-worktree/lib.sh" -SKIP_DEPS=false -WT_PORT="" - -for arg in "$@"; do - case "$arg" in - --port=*) WT_PORT="${arg#*=}" ;; - --no-deps) SKIP_DEPS=true ;; - --db=*) - echo "generoi-worktree: --db= is no longer supported (always shared DB + Caddy :port)" >&2 - exit 1 - ;; - -h|--help) - echo "Usage: ddev wt-up [--port=808x] [--no-deps]" - exit 0 - ;; - *) - echo "Unknown argument: $arg" >&2 - exit 1 - ;; - esac -done - -wt_require docker -wt_require jq -wt_ensure_ddev_project - -if wt_is_canonical_checkout; then - echo "generoi-worktree: you are on the canonical checkout; use 'ddev start' for daily dev." >&2 - echo "generoi-worktree: create a git worktree first, then run 'ddev wt-up' there." >&2 - exit 1 -fi - -wt_ensure_canonical_running -wt_bootstrap_links - -if [[ "$SKIP_DEPS" == false ]]; then - wt_install_deps -fi - -wt_sidecar_start "$WT_PORT" - -echo "generoi-worktree: ready" +wt_sidecar_start diff --git a/commands/host/wt-wp b/commands/host/wt-wp index 4b6b589..0b3f6a8 100755 --- a/commands/host/wt-wp +++ b/commands/host/wt-wp @@ -1,31 +1,13 @@ #!/usr/bin/env bash ## #ddev-generated: If you want to edit and own this file, remove this line. -## Description: Run wp-cli in this worktree's sidecar -## Usage: wt-wp [flags] [args] -## Example: "ddev wt-wp plugin list" or "ddev wt-wp --url=https://nat.herrfors.ddev.site option get home" +## Description: (removed) use ddev wp in worktrees +## Usage: wt-wp +## Example: "ddev wt-wp plugin list" ## OSTypes: linux,darwin ## ProjectTypes: php,wordpress set -euo pipefail -# shellcheck disable=SC1091 -source "$(git rev-parse --show-toplevel 2>/dev/null || pwd -P)/.ddev/generoi-worktree/lib.sh" - -wt_require docker -wt_ensure_ddev_project - -if ! wt_sidecar_running; then - echo "generoi-worktree: sidecar is not running (run ddev wt-up)" >&2 - exit 1 -fi - -container=$(wt_container_name) -quoted=$(printf '%q ' "$@") -exec_cmd=(bash -lc "cd /var/www/html && wp --path=web/wp ${quoted}") - -if [[ -t 0 ]]; then - docker exec -it "$container" "${exec_cmd[@]}" -else - docker exec -i "$container" "${exec_cmd[@]}" -fi +echo "generoi-worktree: wt-wp was removed in v0.2; use 'ddev wp' in this worktree." >&2 +exit 1 diff --git a/config.generoi-worktree.yaml b/config.generoi-worktree.yaml index fc76582..1c68c13 100644 --- a/config.generoi-worktree.yaml +++ b/config.generoi-worktree.yaml @@ -1,5 +1,9 @@ # #ddev-generated -# generoi-worktree: block ddev start in git worktrees (use ddev wt-up). +# generoi-worktree: git worktree support (unique name, Caddy :808x, forked DB). hooks: pre-start: - - exec-host: "bash .ddev/commands/host/wt-guard" + - exec-host: "bash .ddev/commands/host/wt-prepare" + post-start: + - exec-host: "bash .ddev/commands/host/wt-post-start" + pre-stop: + - exec-host: "bash .ddev/commands/host/wt-pre-stop" diff --git a/generoi-worktree/README.md b/generoi-worktree/README.md index 5115973..29c2f44 100644 --- a/generoi-worktree/README.md +++ b/generoi-worktree/README.md @@ -2,12 +2,31 @@ Install: `ddev add-on get generoi/ddev-worktree` -Worktree sidecars for Bedrock sites: reuse the canonical DDEV web image + **shared -MariaDB**, browse on `https://:808x` while PHP/DB keep canonical -hostnames. +## v0.2 architecture -**Caddy** strips `:port` from `Host` before PHP (`header_up Host {http.request.host}`), -then `replace-response` adds `:808x` back in browser URLs. No Bedrock changes. +**Canonical:** normal `ddev start` — one MariaDB, Traefik URL. + +**Git worktree:** `ddev start` — full DDEV project with a unique name (`herrfors-wt-`), own MariaDB, Caddy on `:808x`. + +``` +Browser ──► Caddy (:808x) ──► ddev--web:80 + │ + └── ddev--db (forked from canonical) +``` + +**Caddy** strips `:port` from `Host` before PHP (`header_up Host {http.request.host}`), then `replace-response` adds `:808x` back in browser URLs. + +**Database:** first `ddev start` in a worktree exports canonical `db` and imports into the worktree DB (no search-replace). Refresh manually with `ddev wt-sync-db`. + +**Uploads / .env:** symlinked from canonical checkout on `wt-prepare` (shared on host). + +## Hooks + +| Hook | Action | +|------|--------| +| `pre-start` | `wt-prepare` — unique project name, bootstrap symlinks | +| `post-start` | `wt-post-start` — seed DB if empty, start Caddy | +| `pre-stop` | `wt-pre-stop` — stop Caddy | ## Usage @@ -16,18 +35,8 @@ then `replace-response` adds `:808x` back in browser URLs. No Bedrock changes. **Worktree:** ```bash -ddev wt-up +ddev start ddev wt-port -ddev wt-down +ddev wt-sync-db # optional: refresh DB from canonical +ddev stop ``` - -| Flag | Description | -|------|-------------| -| `--port=808x` | Pin host port (default: first free 8081–8099) | -| `--no-deps` | Skip composer/pnpm install | - -## Bootstrap - -Symlinks from canonical: `.env`, uploads, theme `public/` dirs. - -Local per worktree: `composer install`, `pnpm install` (or npm/yarn from lockfile). diff --git a/generoi-worktree/lib.sh b/generoi-worktree/lib.sh index d7e728c..3a9fa6f 100755 --- a/generoi-worktree/lib.sh +++ b/generoi-worktree/lib.sh @@ -6,6 +6,7 @@ set -euo pipefail WT_STATE_DIR=".ddev/wt" WT_STATE_FILE="${WT_STATE_DIR}/state.json" +WT_LOCAL_CONFIG=".ddev/config.generoi-worktree.local.yaml" WT_PORT_MIN=8081 WT_PORT_MAX=8099 @@ -55,15 +56,40 @@ wt_suffix() { echo "$(wt_approot)" | cksum | awk '{print $1}' } -wt_container_name() { - local name suffix - name=$(wt_canonical_name) - suffix=$(wt_suffix) - echo "ddev-${name}-wt-${suffix}" +wt_project_name() { + echo "$(wt_canonical_name)-wt-$(wt_suffix)" +} + +wt_ddev_project_name() { + wt_require ddev + ddev describe -j 2>/dev/null | jq -r '.raw.name // empty' +} + +wt_web_container() { + local project=${1:-} + if [[ -z "$project" ]]; then + project=$(wt_ddev_project_name) + fi + echo "ddev-${project}-web" } wt_caddy_container_name() { - echo "$(wt_container_name)-caddy" + local project=${1:-} + if [[ -z "$project" ]]; then + if wt_read_state >/dev/null 2>&1; then + project=$(wt_state_value project 2>/dev/null || true) + fi + project=${project:-$(wt_ddev_project_name)} + fi + echo "ddev-${project}-wt-caddy" +} + +wt_network_name() { + local project=${1:-} + if [[ -z "$project" ]]; then + project=$(wt_ddev_project_name) + fi + echo "ddev-${project}_default" } wt_port_in_use() { @@ -87,6 +113,14 @@ wt_allocate_port() { echo "$requested" return fi + if wt_read_state >/dev/null 2>&1; then + local saved + saved=$(wt_state_value port 2>/dev/null || true) + if [[ -n "$saved" ]] && ! wt_port_in_use "$saved"; then + echo "$saved" + return + fi + fi local port for port in $(seq "$WT_PORT_MIN" "$WT_PORT_MAX"); do if ! wt_port_in_use "$port"; then @@ -109,14 +143,6 @@ wt_ensure_canonical_running() { exit 1 } fi - if ! docker ps --format '{{.Names}}' | grep -qx "ddev-${name}-web"; then - echo "generoi-worktree: canonical web container is not running; run 'ddev start' in ${canonical_approot}" >&2 - exit 1 - fi -} - -wt_network_name() { - echo "ddev-$(wt_canonical_name)_default" } wt_read_state() { @@ -130,12 +156,42 @@ wt_state_value() { jq -r --arg k "$key" '.[$k] // empty' "$WT_STATE_FILE" } -wt_canonical_hosts() { +wt_canonical_hosts_from_config() { + local canonical_approot=$1 + local name primary line host + name=$(awk '/^name:[[:space:]]*/ {print $2; exit}' "$canonical_approot/.ddev/config.yaml") + primary="${name}.ddev.site" + echo "$primary" + awk ' + /^additional_hostnames:/ { in_hosts=1; next } + in_hosts && /^ - / { + gsub(/^ - /, "", $0) + gsub(/'\''|"/, "", $0) + if ($0 ~ /\.ddev\.site$/) print $0; else print $0 ".ddev.site" + next + } + in_hosts && /^[^ #]/ { exit } + ' "$canonical_approot/.ddev/config.yaml" +} + +wt_canonical_hosts_from_web() { local canonical_web=$1 docker inspect "$canonical_web" --format '{{range .Config.Env}}{{println .}}{{end}}' \ | awk -F= '/^DDEV_HOSTNAME=/{print $2}' | tr ',' '\n' | sed '/^$/d' } +wt_canonical_hosts() { + local canonical_approot canonical_name canonical_web + canonical_approot=$(wt_canonical_approot) + canonical_name=$(wt_canonical_name) + canonical_web="ddev-${canonical_name}-web" + if docker ps --format '{{.Names}}' | grep -qx "$canonical_web"; then + wt_canonical_hosts_from_web "$canonical_web" + else + wt_canonical_hosts_from_config "$canonical_approot" + fi +} + wt_caddy_image() { echo "generoi-wt-caddy:2.11" } @@ -203,15 +259,14 @@ EOF } wt_caddy_start() { - local port=$1 network=$2 + local port=$1 network=$2 web_container=$3 project=$4 local approot caddy caddyfile image approot=$(wt_approot) - caddy=$(wt_caddy_container_name) + caddy=$(wt_caddy_container_name "$project") caddyfile="${approot}/.ddev/wt/Caddyfile" image=$(wt_caddy_image) wt_ensure_caddy_image - docker rm -f "$caddy" >/dev/null 2>&1 || true docker run -d \ @@ -219,13 +274,31 @@ wt_caddy_start() { --network "$network" \ --label "com.generoi.worktree=true" \ --label "com.generoi.worktree-proxy=caddy" \ + --label "com.generoi.canonical=$(wt_canonical_name)" \ + --label "com.generoi.approot=${approot}" \ + --label "com.generoi.project=${project}" \ -v "${caddyfile}:/etc/caddy/Caddyfile:ro" \ -v "ddev-global-cache:/mnt/ddev-global-cache:ro" \ -p "127.0.0.1:${port}:443" \ "$image" \ caddy run --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null - echo "generoi-worktree: Caddy proxy on 127.0.0.1:${port} -> $(wt_container_name)" + echo "generoi-worktree: Caddy proxy on 127.0.0.1:${port} -> ${web_container}" +} + +wt_caddy_stop() { + local caddy project + if wt_read_state >/dev/null 2>&1; then + caddy=$(wt_state_value caddy_container 2>/dev/null || true) + fi + if [[ -z "${caddy:-}" ]]; then + project=$(wt_ddev_project_name 2>/dev/null || wt_project_name) + caddy=$(wt_caddy_container_name "$project") + fi + if docker ps -a --format '{{.Names}}' | grep -qx "$caddy"; then + docker rm -f "$caddy" >/dev/null + echo "generoi-worktree: stopped ${caddy}" + fi } wt_bootstrap_links() { @@ -234,7 +307,6 @@ wt_bootstrap_links() { approot=$(wt_approot) if [[ "$canonical" == "$approot" ]]; then - echo "generoi-worktree: bootstrap skipped on canonical checkout" return 0 fi @@ -278,6 +350,163 @@ wt_bootstrap_links() { fi } +wt_write_local_config() { + local name + name=$(wt_project_name) + mkdir -p "$(dirname "$WT_LOCAL_CONFIG")" + cat >"$WT_LOCAL_CONFIG" </dev/null \ + | tail -1 +} + +wt_db_is_empty() { + local count + count=$(wt_db_table_count 2>/dev/null || echo 0) + [[ "${count:-0}" -eq 0 ]] +} + +wt_seed_db_from_canonical() { + local canonical canonical_name tmp + wt_ensure_canonical_running + canonical=$(wt_canonical_approot) + canonical_name=$(wt_canonical_name) + tmp=$(mktemp "${TMPDIR:-/tmp}/generoi-wt-db.XXXXXX.sql.gz") + echo "generoi-worktree: cloning database from canonical '${canonical_name}' (no search-replace)..." + (cd "$canonical" && ddev export-db --file="$tmp") + ddev import-db --file="$tmp" + rm -f "$tmp" + echo "generoi-worktree: database seeded from canonical" +} + +wt_seed_db_if_empty() { + if wt_is_canonical_checkout; then + return 0 + fi + if ! wt_db_is_empty; then + return 0 + fi + wt_seed_db_from_canonical +} + +wt_sync_db() { + if wt_is_canonical_checkout; then + echo "generoi-worktree: wt-sync-db is for worktrees only" >&2 + exit 1 + fi + wt_seed_db_from_canonical +} + +wt_post_start() { + local project web_container network port canonical_name suffix approot + local -a canonical_hosts=() + local primary_host host_list url + + wt_ensure_ddev_project + if wt_is_canonical_checkout; then + return 0 + fi + + wt_require ddev + wt_require jq + wt_require docker + + project=$(wt_ddev_project_name) + [[ -n "$project" ]] || { + echo "generoi-worktree: could not read DDEV project name" >&2 + exit 1 + } + + web_container=$(wt_web_container "$project") + network=$(wt_network_name "$project") + canonical_name=$(wt_canonical_name) + suffix=$(wt_suffix) + approot=$(wt_approot) + + if ! docker ps --format '{{.Names}}' | grep -qx "$web_container"; then + echo "generoi-worktree: web container ${web_container} is not running" >&2 + exit 1 + fi + + wt_seed_db_if_empty + + while IFS= read -r _host; do + [[ -n "$_host" ]] && canonical_hosts+=("$_host") + done < <(wt_canonical_hosts) + primary_host="${canonical_hosts[0]}" + + port=$(wt_allocate_port "") + wt_write_caddyfile "$port" "$web_container" "$canonical_name" "${canonical_hosts[@]}" + wt_caddy_start "$port" "$network" "$web_container" "$project" + + url="https://${primary_host}:${port}" + host_list=$(printf '%s,' "${canonical_hosts[@]}") + host_list=${host_list%,} + + mkdir -p "$WT_STATE_DIR" + jq -n \ + --arg caddy "$(wt_caddy_container_name "$project")" \ + --arg suffix "$suffix" \ + --arg canonical_name "$canonical_name" \ + --arg canonical_approot "$(wt_canonical_approot)" \ + --arg approot "$approot" \ + --arg project "$project" \ + --arg web_container "$web_container" \ + --arg primary "$primary_host" \ + --arg hostnames "$host_list" \ + --arg url "$url" \ + --argjson port "$port" \ + --argjson db_seeded true \ + '{ + caddy_container: $caddy, + suffix: $suffix, + canonical_name: $canonical_name, + canonical_approot: $canonical_approot, + approot: $approot, + project: $project, + web_container: $web_container, + db_mode: "fork", + db_seeded: $db_seeded, + port: $port, + primary_hostname: $primary, + hostnames: $hostnames, + url: $url + }' >"$WT_STATE_FILE" + + echo "generoi-worktree: browse ${url}" + if ((${#canonical_hosts[@]} > 1)); then + echo "generoi-worktree: subsite example: https://${canonical_hosts[1]}:${port}" + fi + echo "WT_URL=${url}" +} + +wt_pre_stop() { + wt_ensure_ddev_project + if wt_is_canonical_checkout; then + return 0 + fi + wt_caddy_stop +} + wt_install_js_deps() { local approot=$1 [[ -f "$approot/package.json" ]] || return 0 @@ -329,161 +558,17 @@ wt_install_deps() { wt_install_js_deps "$approot" } -wt_sidecar_running() { - docker ps --format '{{.Names}}' | grep -qx "$(wt_container_name)" +# Legacy sidecar API (v0.1) — removed in v0.2; keep stubs for clearer errors. +wt_container_name() { + wt_web_container } wt_sidecar_stop() { - local container caddy - container=$(wt_container_name) - caddy=$(wt_caddy_container_name) - docker rm -f "$caddy" >/dev/null 2>&1 || true - if docker ps -a --format '{{.Names}}' | grep -qx "$container"; then - docker rm -f "$container" >/dev/null - echo "generoi-worktree: stopped ${container}" - fi + wt_caddy_stop rm -f "$WT_STATE_FILE" } -wt_sidecar_volume_args() { - local approot canonical_approot theme_public theme - approot=$(wt_approot) - canonical_approot=$(cd "$(wt_canonical_approot)" && pwd -P) - - local -a volume_args=( - -v "${approot}:/var/www/html:cached" - -v "${approot}/.ddev:/mnt/ddev_config:ro" - -v "ddev-global-cache:/mnt/ddev-global-cache" - -v "ddev-ssh-agent_socket_dir:/home/.ssh-agent" - ) - - for rel in web/app/uploads .env .env.local auth.json; do - if [[ -e "${canonical_approot}/${rel}" ]]; then - volume_args+=(-v "${canonical_approot}/${rel}:/var/www/html/${rel}:ro") - fi - done - - for theme_public in "${canonical_approot}"/web/app/themes/*/public; do - [[ -d "$theme_public" ]] || continue - theme=$(basename "$(dirname "$theme_public")") - local_rel="web/app/themes/${theme}/public" - if [[ ! -e "${approot}/${local_rel}" ]] || [[ -L "${approot}/${local_rel}" ]]; then - volume_args+=(-v "${theme_public}:/var/www/html/${local_rel}:ro") - fi - done - - printf '%s\n' "${volume_args[@]}" -} - -wt_sidecar_wait_healthy() { - local container=$1 - echo "generoi-worktree: waiting for sidecar health..." - local ready=0 i - for i in $(seq 1 45); do - if docker ps --filter "name=${container}" --filter status=running --format '{{.Status}}' | grep -q healthy; then - ready=1 - break - fi - sleep 2 - done - if [[ $ready -ne 1 ]]; then - echo "generoi-worktree: sidecar not healthy within 90s" >&2 - exit 1 - fi -} - wt_sidecar_start() { - local requested_port=${1:-} - local suffix canonical_web image network container approot canonical_name primary_host - local env_file port - - suffix=$(wt_suffix) - canonical_name=$(wt_canonical_name) - canonical_web="ddev-${canonical_name}-web" - image=$(docker inspect "$canonical_web" --format '{{.Config.Image}}') - network=$(wt_network_name) - container=$(wt_container_name) - approot=$(wt_approot) - port=$(wt_allocate_port "$requested_port") - - local canonical_hosts=() - while IFS= read -r _host; do - [[ -n "$_host" ]] && canonical_hosts+=("$_host") - done < <(wt_canonical_hosts "$canonical_web") - primary_host="${canonical_hosts[0]}" - - wt_sidecar_stop >/dev/null 2>&1 || true - - env_file=$(mktemp "${TMPDIR:-/tmp}/generoi-wt-env.XXXXXX") - docker inspect "$canonical_web" --format '{{range .Config.Env}}{{println .}}{{end}}' >"$env_file" - { - echo "GENEROI_WT=1" - echo "GENEROI_WT_PORT=${port}" - echo "DDEV_APPROOT=/var/www/html" - echo "DDEV_COMPOSER_ROOT=/var/www/html" - echo "DDEV_PRIMARY_URL=https://${primary_host}:${port}" - echo "DDEV_SCHEME=https" - echo "DDEV_HOSTNAME=$(grep '^DDEV_HOSTNAME=' "$env_file" | head -1 | cut -d= -f2-)" - } >>"$env_file" - - wt_write_caddyfile "$port" "$container" "$canonical_name" "${canonical_hosts[@]}" - - local -a volume_args=() - while IFS= read -r vol; do - volume_args+=("$vol") - done < <(wt_sidecar_volume_args) - - docker run -d \ - --name "$container" \ - --network "$network" \ - --user "501:20" \ - --label "com.generoi.worktree=true" \ - --label "com.generoi.canonical=${canonical_name}" \ - --label "com.generoi.approot=${approot}" \ - --env-file "$env_file" \ - "${volume_args[@]}" \ - "$image" >/dev/null - - rm -f "$env_file" - - wt_sidecar_wait_healthy "$container" - wt_caddy_start "$port" "$network" - - local url="https://${primary_host}:${port}" - local host_list - host_list=$(docker inspect "$canonical_web" --format '{{range .Config.Env}}{{println .}}{{end}}' \ - | awk -F= '/^DDEV_HOSTNAME=/{print $2; exit}') - - mkdir -p "$WT_STATE_DIR" - jq -n \ - --arg container "$container" \ - --arg caddy "$(wt_caddy_container_name)" \ - --arg suffix "$suffix" \ - --arg canonical_name "$canonical_name" \ - --arg canonical_approot "$(wt_canonical_approot)" \ - --arg approot "$approot" \ - --arg primary "$primary_host" \ - --arg hostnames "$host_list" \ - --arg url "$url" \ - --argjson port "$port" \ - '{ - container: $container, - caddy_container: $caddy, - suffix: $suffix, - db_name: "db", - canonical_name: $canonical_name, - canonical_approot: $canonical_approot, - approot: $approot, - db_mode: "shared", - port: $port, - primary_hostname: $primary, - hostnames: $hostnames, - url: $url - }' >"$WT_STATE_FILE" - - echo "generoi-worktree: sidecar ${container} at ${url}" - if ((${#canonical_hosts[@]} > 1)); then - echo "generoi-worktree: subsite example: https://${canonical_hosts[1]}:${port}" - fi - echo "WT_URL=${url}" + echo "generoi-worktree: wt-up was removed in v0.2; use 'ddev start' in this worktree." >&2 + exit 1 } diff --git a/generoi-worktree/test-acceptance.sh b/generoi-worktree/test-acceptance.sh index b5401cb..c7d79fb 100755 --- a/generoi-worktree/test-acceptance.sh +++ b/generoi-worktree/test-acceptance.sh @@ -11,9 +11,11 @@ PASS=0 FAIL=0 MARKER="wt-acceptance-$(date +%s).txt" MARKER_BODY="generoi-worktree-acceptance-marker" +DB_MARKER="wt-db-isolation-$(date +%s)" WT_HOST="" WT_PORT="" WT_SUB="" +WT_PROJECT="" pass() { echo " PASS: $1"; PASS=$((PASS + 1)); } fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); } @@ -35,18 +37,13 @@ assert_https_port() { assert_eq "$desc" "$want_code" "$code" } -assert_not_contains() { - local desc=$1 needle=$2 haystack=$3 - if echo "$haystack" | grep -qF "$needle"; then fail "$desc (unexpected '$needle')"; else pass "$desc"; fi -} - cleanup() { rm -f "$APPROOT/web/$MARKER" 2>/dev/null || true - (cd "$APPROOT" && ddev wt-down >/dev/null 2>&1) || true + (cd "$APPROOT" && ddev stop >/dev/null 2>&1) || true } trap cleanup EXIT -echo "=== generoi-worktree acceptance ===" +echo "=== generoi-worktree acceptance (v0.2) ===" echo "approot: $APPROOT" echo "" @@ -59,18 +56,18 @@ fi assert_eq "canonical approot exists" "$(wt_canonical_approot)" "$(git worktree list --porcelain | awk '/^worktree / {print $2; exit}')" echo "" -echo "[2] wt-guard" -if bash "$APPROOT/.ddev/commands/host/wt-guard" >/dev/null 2>&1; then - fail "wt-guard should block worktree ddev start" -else - pass "wt-guard blocks ddev start in worktree" -fi - -echo "" -echo "[3] bootstrap + composer deps" +echo "[2] wt-prepare (unique DDEV name + bootstrap)" +rm -f "$APPROOT/.ddev/config.generoi-worktree.local.yaml" 2>/dev/null || true rm -f "$APPROOT/.env" "$APPROOT/web/app/uploads" 2>/dev/null || true if [[ -L "$APPROOT/vendor" ]]; then rm "$APPROOT/vendor"; fi -wt_bootstrap_links +bash "$APPROOT/.ddev/commands/host/wt-prepare" + +if [[ -f "$APPROOT/.ddev/config.generoi-worktree.local.yaml" ]] \ + && grep -q "^name: $(wt_project_name)" "$APPROOT/.ddev/config.generoi-worktree.local.yaml"; then + pass "local config sets unique project name" +else + fail "missing .ddev/config.generoi-worktree.local.yaml with expected name" +fi if [[ ! -d "$APPROOT/vendor" ]] || [[ ! -f "$APPROOT/web/wp/wp-blog-header.php" ]] || \ [[ ! -f "$APPROOT/web/app/plugins/wp-spot-prices/dist/manifest.json" ]]; then @@ -81,111 +78,75 @@ for rel in .env web/app/uploads; do if [[ -e "$APPROOT/$rel" ]]; then pass "$rel exists"; else fail "$rel missing after bootstrap"; fi done -if [[ -d "$APPROOT/vendor" && ! -L "$APPROOT/vendor" ]]; then - pass "vendor is local directory (composer install)" -else - fail "vendor missing or still symlinked" -fi - -if [[ -f "$APPROOT/web/wp/wp-blog-header.php" && ! -L "$APPROOT/web/wp" ]]; then - pass "web/wp installed locally (composer)" -else - fail "web/wp missing or symlinked" -fi - -if [[ ! -f "$APPROOT/web/app/mu-plugins/generoi-worktree-sidecar.php" ]]; then - pass "no legacy PHP URL shim" -else - fail "legacy mu-plugin should not exist" -fi - -if grep -q 'header_up Host {http.request.host}' "$APPROOT/.ddev/wt/Caddyfile" 2>/dev/null; then - pass "Caddy sends canonical Host to PHP (no :port)" -else - fail ".ddev/wt/Caddyfile missing header_up Host {http.request.host}" -fi - -http_host=$(ddev wt-wp eval 'echo $_SERVER["HTTP_HOST"];' 2>/dev/null | tail -1) -if [[ "$http_host" == "$WT_HOST" ]] && [[ "$http_host" != *:* ]]; then - pass "sidecar PHP HTTP_HOST is ${http_host} (no port)" -else - fail "sidecar PHP HTTP_HOST should be ${WT_HOST} without port (got: ${http_host})" -fi - echo "" -echo "[4] wt-up / sidecar lifecycle (shared DB + Caddy)" -ddev wt-down >/dev/null 2>&1 || true -out=$(ddev wt-up --no-deps 2>&1) -assert_contains "wt-up prints WT_URL" "WT_URL=https://" "$out" -assert_contains "wt-up starts Caddy proxy" "Caddy proxy on 127.0.0.1:" "$out" +echo "[3] ddev start (forked DB + Caddy on first start)" +(cd "$APPROOT" && ddev stop >/dev/null 2>&1) || true +out=$(cd "$APPROOT" && ddev start 2>&1) +assert_contains "ddev start prints WT_URL" "WT_URL=https://" "$out" +assert_contains "ddev start starts Caddy" "Caddy proxy on 127.0.0.1:" "$out" WT_URL=$(echo "$out" | awk -F= '/^WT_URL=/{print $2}') -[[ -n "$WT_URL" ]] || fail "could not parse WT_URL from wt-up" +[[ -n "$WT_URL" ]] || WT_URL=$(ddev wt-port 2>&1 | awk -F= '/^WT_URL=/{print $2}') +[[ -n "$WT_URL" ]] || fail "could not parse WT_URL" WT_HOST=$(echo "$WT_URL" | sed -E 's|https://([^:/]+).*|\1|') WT_PORT=$(echo "$WT_URL" | sed -E 's|https://[^:]+:([0-9]+).*|\1|') [[ -n "$WT_HOST" && -n "$WT_PORT" ]] || fail "could not parse host/port from WT_URL ($WT_URL)" pass "worktree URL ${WT_HOST}:${WT_PORT}" WT_SUB="nat.${WT_HOST}" -assert_contains "wt-up subsite example" "subsite example: https://${WT_SUB}:${WT_PORT}" "$out" +assert_contains "subsite example" "subsite example: https://${WT_SUB}:${WT_PORT}" "$out" -if docker ps --filter "name=$(wt_container_name)" --filter status=running --format '{{.Status}}' | grep -q healthy; then - pass "sidecar healthy" +WT_PROJECT=$(wt_ddev_project_name) +web_container=$(wt_web_container "$WT_PROJECT") +if docker ps --filter "name=${web_container}" --filter status=running --format '{{.Status}}' | grep -q healthy; then + pass "worktree web healthy (${web_container})" else - fail "sidecar not healthy" + fail "worktree web not healthy" fi -if docker ps --filter "name=$(wt_caddy_container_name)" --filter status=running --format '{{.Names}}' | grep -qx "$(wt_caddy_container_name)"; then +if docker ps --filter "name=$(wt_caddy_container_name "$WT_PROJECT")" --filter status=running --format '{{.Names}}' | grep -qx "$(wt_caddy_container_name "$WT_PROJECT")"; then pass "caddy proxy running" else fail "caddy proxy not running" fi +if grep -q 'header_up Host {http.request.host}' "$APPROOT/.ddev/wt/Caddyfile" 2>/dev/null; then + pass "Caddy sends canonical Host to PHP" +else + fail "Caddyfile missing header_up Host {http.request.host}" +fi + +http_host=$(ddev wp eval 'echo $_SERVER["HTTP_HOST"];' 2>/dev/null | tail -1) +if [[ "$http_host" == "$WT_HOST" ]] && [[ "$http_host" != *:* ]]; then + pass "PHP HTTP_HOST is ${http_host} (no port)" +else + fail "PHP HTTP_HOST should be ${WT_HOST} without port (got: ${http_host})" +fi + port_out=$(ddev wt-port 2>&1) assert_contains "wt-port WT_URL" "WT_URL=${WT_URL}" "$port_out" -assert_contains "wt-port WT_PORT" "WT_PORT=${WT_PORT}" "$port_out" list_out=$(ddev wt-list 2>&1) -assert_contains "wt-list shows container" "$(wt_container_name)" "$list_out" -assert_contains "wt-list shows approot" "$APPROOT" "$list_out" +assert_contains "wt-list shows caddy" "$(wt_caddy_container_name "$WT_PROJECT")" "$list_out" echo "" -echo "[5] HTTPS + multisite (Caddy :${WT_PORT})" +echo "[4] HTTPS + multisite (Caddy :${WT_PORT})" assert_https_port "main site HTTPS 200" "$WT_HOST" "$WT_PORT" "200" assert_https_port "nat subsite HTTPS 200" "$WT_SUB" "$WT_PORT" "200" html=$(curl -k -s "https://${WT_HOST}:${WT_PORT}/") if echo "$html" | grep -qE ":${WT_PORT}:${WT_PORT}"; then - fail "HTML contains double :${WT_PORT} port (Caddy rewrite bug)" + fail "HTML contains double :${WT_PORT} port" else pass "no double :${WT_PORT} in HTML URLs" fi -asset=$(echo "$html" | grep -oE "https://${WT_HOST}:${WT_PORT}/wp/wp-includes/[^\"'<> ]+" | head -1) -if [[ -n "$asset" ]]; then - asset_code=$(curl -k -s -o /dev/null -w '%{http_code}' "$asset") - if [[ "$asset_code" == "200" ]]; then - pass "asset URL loads (200): $(basename "$asset")" - else - fail "asset URL ${asset} returned ${asset_code}" - fi -else - fail "could not find a wp-includes asset URL in HTML" -fi - -canonical_code=$(curl -k -s -o /dev/null -w '%{http_code}' --resolve "herrfors.ddev.site:443:127.0.0.1" "https://herrfors.ddev.site/") -if [[ "$canonical_code" =~ ^(200|301|302)$ ]]; then - pass "canonical still responds via Traefik ($canonical_code)" -else - fail "canonical HTTPS ($canonical_code)" -fi - echo "" -echo "[6] code isolation" +echo "[5] code isolation" echo "$MARKER_BODY" >"$APPROOT/web/$MARKER" side=$(curl -k -s "https://${WT_HOST}:${WT_PORT}/${MARKER}") canon=$(curl -k -s -o /dev/null -w '%{http_code}' --resolve "herrfors.ddev.site:443:127.0.0.1" "https://herrfors.ddev.site/${MARKER}") -assert_eq "marker visible on sidecar" "$MARKER_BODY" "$side" +assert_eq "marker visible on worktree" "$MARKER_BODY" "$side" if [[ "$canon" == "404" ]] || [[ "$canon" =~ ^30[0-9]$ ]]; then pass "marker not on canonical ($canon)" else @@ -193,7 +154,21 @@ else fi echo "" -echo "[7] uploads (same nginx fallback as canonical DDEV)" +echo "[6] DB isolation (forked MariaDB)" +ddev wp option update "generoi_wt_${DB_MARKER}" "worktree-only" --quiet 2>/dev/null +wt_val=$(ddev wp option get "generoi_wt_${DB_MARKER}" 2>/dev/null | tail -1) +assert_eq "option set in worktree DB" "worktree-only" "$wt_val" + +canon_val=$(cd "$(wt_canonical_approot)" && ddev wp option get "generoi_wt_${DB_MARKER}" 2>/dev/null || true) +if [[ -z "$canon_val" ]] || echo "$canon_val" | grep -qiE 'error|not found|does not exist'; then + pass "option absent from canonical DB" +else + fail "option leaked to canonical DB: $canon_val" +fi +ddev wp option delete "generoi_wt_${DB_MARKER}" --quiet 2>/dev/null || true + +echo "" +echo "[7] uploads (shared from canonical host path)" missing="/app/uploads/generoi-wt-missing-$(date +%s).jpg" headers=$(curl -k -sI "https://${WT_HOST}:${WT_PORT}${missing}") if echo "$headers" | grep -qiE '^HTTP/.* (302|301|307|308) '; then @@ -201,53 +176,31 @@ if echo "$headers" | grep -qiE '^HTTP/.* (302|301|307|308) '; then else fail "missing upload should redirect, got: $(echo "$headers" | head -1)" fi -if echo "$headers" | grep -qi 'herrfors.fi'; then - pass "missing upload redirect targets production" -else - fail "missing upload redirect should target herrfors.fi" -fi - -echo "" -echo "[8] shared DB via wt-wp (canonical URLs in DB)" -wp_out=$(ddev wt-wp option get home --url="https://${WT_HOST}:${WT_PORT}" 2>&1) -if echo "$wp_out" | grep -qF "https://${WT_HOST}" && ! echo "$wp_out" | grep -qF ":${WT_PORT}"; then - pass "wt-wp home uses canonical hostname (no port in DB)" -else - fail "wt-wp home wrong: $wp_out" -fi - -nat_out=$(ddev wt-wp option get blogname --url="https://${WT_SUB}:${WT_PORT}" 2>&1) -if [[ -n "$nat_out" ]] && ! echo "$nat_out" | grep -qiE 'error|failed|does not seem'; then - pass "wt-wp nat subsite blogname ($nat_out)" -else - fail "wt-wp nat failed: $nat_out" -fi echo "" -echo "[9] wt-down cleanup" -ddev wt-down >/dev/null 2>&1 -if docker ps -a --format '{{.Names}}' | grep -qx "$(wt_container_name)"; then - fail "container still exists after wt-down" +echo "[8] ddev stop (Caddy cleanup, state kept for port)" +ddev stop >/dev/null 2>&1 +if docker ps -a --format '{{.Names}}' | grep -qx "$(wt_caddy_container_name "$WT_PROJECT")"; then + fail "caddy still exists after ddev stop" else - pass "container removed after wt-down" + pass "caddy removed after ddev stop" fi -if docker ps -a --format '{{.Names}}' | grep -qx "$(wt_caddy_container_name)"; then - fail "caddy still exists after wt-down" +if [[ -f "$WT_STATE_FILE" ]]; then + pass "state file kept (port reuse)" else - pass "caddy removed after wt-down" + fail "state file should remain after ddev stop" fi -if [[ -f "$WT_STATE_FILE" ]]; then fail "state file still exists"; else pass "state file removed"; fi echo "" -echo "[10] wt-up on canonical should refuse" +echo "[9] wt-up deprecated" set +e -canon_out=$(cd "$(wt_canonical_approot)" && bash .ddev/commands/host/wt-up --no-deps 2>&1) -canon_ec=$? +deprecated=$(ddev wt-up 2>&1) +dep_ec=$? set -e -if [[ $canon_ec -ne 0 ]] && echo "$canon_out" | grep -q 'canonical checkout'; then - pass "wt-up refused on canonical" +if [[ $dep_ec -ne 0 ]] && echo "$deprecated" | grep -qi 'ddev start'; then + pass "wt-up points to ddev start" else - fail "wt-up should refuse on canonical (exit=$canon_ec)" + fail "wt-up should refuse with ddev start hint (exit=$dep_ec)" fi echo "" diff --git a/install.yaml b/install.yaml index 9ca7c2b..777e1be 100644 --- a/install.yaml +++ b/install.yaml @@ -7,12 +7,15 @@ project_files: - generoi-worktree/lib.sh - generoi-worktree/Dockerfile.caddy - generoi-worktree/test-acceptance.sh - - commands/host/wt-up + - commands/host/wt-prepare + - commands/host/wt-post-start + - commands/host/wt-pre-stop + - commands/host/wt-sync-db - commands/host/wt-down - commands/host/wt-list - commands/host/wt-port + - commands/host/wt-up - commands/host/wt-wp - - commands/host/wt-guard global_files: [] @@ -21,15 +24,25 @@ pre_install_actions: [] post_install_actions: - | #ddev-description: Make generoi-worktree commands executable - chmod +x "$DDEV_APPROOT/.ddev/commands/host/wt-up" "$DDEV_APPROOT/.ddev/commands/host/wt-down" "$DDEV_APPROOT/.ddev/commands/host/wt-list" "$DDEV_APPROOT/.ddev/commands/host/wt-port" "$DDEV_APPROOT/.ddev/commands/host/wt-wp" "$DDEV_APPROOT/.ddev/commands/host/wt-guard" + chmod +x "$DDEV_APPROOT/.ddev/commands/host/wt-prepare" \ + "$DDEV_APPROOT/.ddev/commands/host/wt-post-start" \ + "$DDEV_APPROOT/.ddev/commands/host/wt-pre-stop" \ + "$DDEV_APPROOT/.ddev/commands/host/wt-sync-db" \ + "$DDEV_APPROOT/.ddev/commands/host/wt-down" \ + "$DDEV_APPROOT/.ddev/commands/host/wt-list" \ + "$DDEV_APPROOT/.ddev/commands/host/wt-port" \ + "$DDEV_APPROOT/.ddev/commands/host/wt-up" \ + "$DDEV_APPROOT/.ddev/commands/host/wt-wp" - | - #ddev-description: Remind to gitignore generated worktree state + #ddev-description: Remind to gitignore generated worktree files if ! grep -qE '^\.ddev/wt/' "$DDEV_APPROOT/.gitignore" 2>/dev/null; then echo "" - echo "generoi-worktree: add .ddev/wt/ to .gitignore (generated Caddyfile + state)." + echo "generoi-worktree: add these to .gitignore (see README):" + echo " .ddev/wt/" + echo " .ddev/config.generoi-worktree.local.yaml" fi removal_actions: - | - #ddev-description: Stop any running worktree sidecar + #ddev-description: Stop Caddy proxy if running bash "$DDEV_APPROOT/.ddev/commands/host/wt-down" >/dev/null 2>&1 || true