Skip to content

Instantly share code, notes, and snippets.

@skippednote
Last active March 5, 2026 09:09
Show Gist options
  • Select an option

  • Save skippednote/01ab91eb2ac5a5ed50059a8b89af056f to your computer and use it in GitHub Desktop.

Select an option

Save skippednote/01ab91eb2ac5a5ed50059a8b89af056f to your computer and use it in GitHub Desktop.
name description user_invocable
ddev-worktree
Spin up an isolated DDEV site from a git worktree so multiple features can run side-by-side on the same Drupal codebase.
true

DDEV Worktree — Isolated Drupal Environments

Spin up a fresh, conflict-free DDEV site for a feature branch. Each environment is a git worktree with its own DDEV project, database, and URL — completely independent from the main site.

Discovering Project Context

Before any operation, detect the current project's DDEV name and directory:

PROJECT_NAME=$(grep '^name:' .ddev/config.yaml | awk '{print $2}')
PROJECT_DIR=$(basename "$PWD")

Use $PROJECT_NAME everywhere below instead of hardcoding any project name.

What This Does

Main site:  <project>.ddev.site              ← current directory
Feature A:  <project>-ams-720.ddev.site      ← ../<project>-ams-720  (worktree)
Feature B:  <project>-ams-721.ddev.site      ← ../<project>-ams-721  (worktree)

Each is a full Drupal site running independently. No branch switching, no conflicts.

Spinning Up a New Environment

When the user asks to spin up a new environment (e.g., /ddev-worktree ams-720):

1. Detect the project name

PROJECT_NAME=$(grep '^name:' .ddev/config.yaml | awk '{print $2}')

2. Export the current database (if not already exported)

ddev export-db --file=/tmp/${PROJECT_NAME}-db.sql.gz

3. Create the worktree

git worktree add ../${PROJECT_NAME}-<slug> -b <branch-name> main

<slug> is a short kebab-case identifier from the feature name or ticket number.

4. Give it a unique DDEV project name

The main project has a name: in .ddev/config.yaml. Each worktree needs a unique name to avoid port conflicts.

cd ../${PROJECT_NAME}-<slug>
sed -i '' "s/^name: ${PROJECT_NAME}$/name: ${PROJECT_NAME}-<slug>/" .ddev/config.yaml

5. Start DDEV and hydrate

ddev start
ddev import-db --file=/tmp/${PROJECT_NAME}-db.sql.gz
ddev composer install
ddev drush cr

6. Report the environment

Tell the user:

  • URL: https://${PROJECT_NAME}-<slug>.ddev.site
  • Path: ../${PROJECT_NAME}-<slug>
  • Branch: <branch-name>

Agent Mode — Parallel Features

When the user provides multiple features, spin up one environment per feature and dispatch agents in parallel.

For each feature, launch an Agent in a single message (all concurrent):

Agent(
  subagent_type: "general-purpose",
  isolation: "worktree",
  run_in_background: true,
  description: "<3-5 word summary>",
  prompt: <see Agent Prompt below>
)

Agent Prompt

Fill PROJECT_NAME with the detected value before dispatching:

You are working on a Drupal 10 feature in an isolated git worktree.

## Project
- Drupal 10, docroot: `web/`, PHP 8.2, MariaDB 10.11, nginx-fpm
- Composer root is the project root
- DDEV for local dev
- Main DDEV project name: <PROJECT_NAME>

## Setup (run first)
1. sed -i '' 's/^name: <PROJECT_NAME>$/name: <PROJECT_NAME>-<SLUG>/' .ddev/config.yaml
2. ddev start
3. ddev import-db --file=/tmp/<PROJECT_NAME>-db.sql.gz  (skip if file missing)
4. ddev composer install
5. ddev drush cr

## Task
Branch: <BRANCH>
<FEATURE_DESCRIPTION>

## Rules
- Write Drupal-standards-compliant code
- Run `ddev drush cr` after changes
- Commit with a clear message when done
- Do NOT push

Reporting

As agents finish, summarize:

Feature Branch Status URL Path
Ticket-1 feature-1 Done <project>-feature-1.ddev.site ../<project>-feature-1
Ticket-2 feature-2 Done <project>-feature-2.ddev.site ../<project>-feature-2

Listing Environments

git worktree list
ddev list

Tearing Down an Environment

cd ../${PROJECT_NAME}-<slug> && ddev stop --remove-data
cd - && git worktree remove ../${PROJECT_NAME}-<slug>
git branch -d <branch-name>  # only if merged

Cleanup All

PROJECT_NAME=$(grep '^name:' .ddev/config.yaml | awk '{print $2}')
for wt in $(git worktree list --porcelain | grep '^worktree' | grep -v "/${PROJECT_NAME}$" | awk '{print $2}'); do
  (cd "$wt" && ddev stop --remove-data 2>/dev/null)
done
git worktree prune
rm -f /tmp/${PROJECT_NAME}-db.sql.gz

Constraints

  • 3-4 concurrent environments max (~512MB RAM each DDEV instance)
  • Every worktree MUST have a unique name in .ddev/config.yaml
  • Agents commit but never push — user reviews and pushes manually
  • DB export is a point-in-time snapshot; worktree DBs diverge from there
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment