Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b310b5c
feat: add drupal-contrib template for contrib module/theme developmen…
rfay May 6, 2026
da483d0
feat: add test-issue-branches.sh for drupal-contrib local testing
rfay May 6, 2026
8ee9986
fix: suppress ddev config global stdout in test-issue-branches.sh
rfay May 6, 2026
50a9728
fix: remove ddev config global from test-issue-branches.sh
rfay May 6, 2026
479aca9
feat: add beta unified picker, restore core-only drupal-issue.html
rfay May 6, 2026
bd24430
fix: coerce share_drupal_site through local to survive tftest mock
rfay May 6, 2026
9fb1951
fix: terraform fmt both templates; add pre-push checklist to CLAUDE.md
rfay May 6, 2026
1691cd3
ci: add concurrency cancel-in-progress to drupal integration test wor…
rfay May 6, 2026
026e55b
run tests
rfay May 6, 2026
ed8749b
fix: replace github.run_id with run_number in workspace names
rfay May 6, 2026
9a1c547
fix: strip project prefix from CONTRIB_TEST_ISSUE_FORK; fix job title
rfay May 6, 2026
fc1bc82
fix: pass share_drupal_site=owner to all CI coder create calls
rfay May 6, 2026
5757a2a
ci: skip integration-test on drupal-core/contrib-only PRs
rfay May 6, 2026
3ca668a
fix: clamp resolved Drupal version to supported range (10/11/12)
rfay May 6, 2026
36821d2
fix: re-add share_drupal_site=owner to both contrib coder create calls
rfay May 6, 2026
e3818ec
fix: re-add version clamping; precise module check; fix misleading en…
rfay May 6, 2026
96e8511
fix: remove broken Drupal version inference for contrib issue forks
rfay May 6, 2026
7ce30c5
fix: don't infer Drupal version from field_issue_version for contrib …
rfay May 6, 2026
c2a2bf5
fix: remove bash -c from CI steps, add SETUP_STATUS dump, fix branch …
rfay May 6, 2026
40eabc3
fix: use env -C instead of nonexistent ddev -p flag
rfay May 6, 2026
f7cda6a
fix: show setup log tail in CI, print drush output before grep, use -iw
rfay May 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
386 changes: 386 additions & 0 deletions .github/workflows/drupal-contrib-integration-test.yml

Large diffs are not rendered by default.

16 changes: 11 additions & 5 deletions .github/workflows/drupal-integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ name: Drupal Integration Tests
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true

on:
schedule:
- cron: '0 4 * * *'
Expand Down Expand Up @@ -60,7 +64,7 @@ jobs:
run:
shell: bash -euo pipefail {0}
env:
WORKSPACE_NAME: ci-drupal-${{ matrix.drupal_version }}-${{ github.run_id }}
WORKSPACE_NAME: cd-${{ matrix.drupal_version }}-${{ github.run_number }}-${{ github.run_attempt }}
CI: "true"
DDEV_NONINTERACTIVE: "true"
NO_COLOR: "1"
Expand Down Expand Up @@ -110,6 +114,7 @@ jobs:
--parameter issue_fork= \
--parameter issue_branch= \
--parameter install_profile=minimal \
--parameter share_drupal_site=owner \
--yes

- &verify-agent
Expand Down Expand Up @@ -200,7 +205,7 @@ jobs:
run:
shell: bash -euo pipefail {0}
env:
WORKSPACE_NAME: ci-drupal-fork-${{ github.run_id }}
WORKSPACE_NAME: cd-fork-${{ github.run_number }}-${{ github.run_attempt }}
CI: "true"
DDEV_NONINTERACTIVE: "true"
NO_COLOR: "1"
Expand Down Expand Up @@ -261,7 +266,7 @@ jobs:
coder templates push drupal-core \
--directory drupal-core \
--activate=false \
--name ci-${{ github.run_id }} \
--name cd-${{ github.run_number }}-${{ github.run_attempt }} \
--yes \
--variable workspace_image_registry=index.docker.io/ddev/coder-ddev \
--variable cache_path=/tmp/ci-no-cache
Expand All @@ -270,12 +275,13 @@ jobs:
run: |
coder create ${{ env.WORKSPACE_NAME }} \
--template drupal-core \
--template-version ci-${{ github.run_id }} \
--template-version cd-${{ github.run_number }}-${{ github.run_attempt }} \
--parameter "vscode_extensions=[]" \
--parameter drupal_version=${{ env.ISSUE_FORK_VERSION }} \
--parameter issue_fork=${{ env.ISSUE_FORK }} \
--parameter issue_branch=${{ env.ISSUE_BRANCH }} \
--parameter install_profile=minimal \
--parameter share_drupal_site=owner \
--yes

- name: Verify workspace — agent connected
Expand Down Expand Up @@ -350,4 +356,4 @@ jobs:

- name: Archive CI template version
if: always()
run: coder templates versions archive drupal-core ci-${{ github.run_id }} --yes || true
run: coder templates versions archive drupal-core cd-${{ github.run_number }}-${{ github.run_attempt }} --yes || true
10 changes: 8 additions & 2 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@ env:
on:
push:
branches: [main]
paths-ignore:
- 'drupal-core/**'
- 'drupal-contrib/**'
pull_request:
paths-ignore:
- 'drupal-core/**'
- 'drupal-contrib/**'
schedule:
- cron: '0 3 * * *'
workflow_dispatch:
Expand Down Expand Up @@ -82,8 +88,8 @@ jobs:
run:
shell: bash -euo pipefail {0}
env:
CI_TAG: ${{ github.run_id }}-${{ github.run_attempt }}
WORKSPACE_NAME: ci-${{ matrix.ws_name }}-${{ github.run_id }}-${{ github.run_attempt }}
CI_TAG: ${{ github.run_number }}-${{ github.run_attempt }}
WORKSPACE_NAME: ci-${{ matrix.ws_name }}-${{ github.run_number }}-${{ github.run_attempt }}
CI: "true"
DDEV_NONINTERACTIVE: "true"
NO_COLOR: "1"
Expand Down
23 changes: 23 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@ This project provides a Coder v2+ template for DDEV-based development environmen

- Use `jq` (not `python3 -m json.tool`) for JSON pretty-printing and querying

## Before Pushing / Pre-push Checklist

Run these before every push to avoid CI failures:

```bash
# Terraform formatting (CI runs terraform fmt -check -recursive)
terraform fmt -recursive

# Terraform validation for each template you touched
terraform -chdir=drupal-core init -backend=false && terraform -chdir=drupal-core validate
terraform -chdir=drupal-contrib init -backend=false && terraform -chdir=drupal-contrib validate

# Terraform tests (plan-level, no real infrastructure)
terraform -chdir=drupal-core test
terraform -chdir=drupal-contrib test
```

`terraform fmt -recursive` must be run from the repo root. It is non-destructive (rewrites in place) and the CI check fails with exit code 3 if any file is not formatted.

## Working with Coder Workspaces via SSH

After running `coder config-ssh --yes`, workspaces are available as SSH hosts named `<workspace>.coder`. Use `scp` to copy files in or out, then `ssh` to execute scripts non-interactively:
Expand All @@ -39,6 +58,10 @@ ssh mp1.coder ddev list

When running commands via `coder ssh -- ...` or piped heredocs, the PTY allocation causes interactive prompts and pipe-stall issues. Writing a script to `/tmp/` and executing it via `ssh workspace.coder bash /tmp/script.sh` is reliable for multi-step operations.

**CI scripting rule**: In GitHub Actions, for any `coder ssh` invocation that does more than a single trivial command, push a script file and run it — do not use `bash -c "..."`. Single commands with standard flags (e.g. `git -C /path branch --show-current`) are fine inline. Anything with `&&`, conditionals, or multiple statements belongs in a script file under the template's `scripts/` directory.

To run a command in a specific directory without a shell wrapper, use `env -C <dir> <cmd>` (available on Ubuntu 24.04 via GNU coreutils): `coder ssh ws -- env -C /home/coder/myproject ddev drush status`

## Essential Commands

### Template Management
Expand Down
15 changes: 13 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ DOCKERFILE_DIR := image
DOCKERFILE := $(DOCKERFILE_DIR)/Dockerfile

# Template directories (name == directory name == Coder template name)
TEMPLATES := user-defined-web drupal-core freeform
TEMPLATES := user-defined-web drupal-core drupal-contrib freeform

# Host path to the drupal-core seed cache (bind-mounted read-only into workspaces).
# This path is specific to the server where the template is deployed.
Expand All @@ -30,13 +30,16 @@ IMAGE_LATEST := $(IMAGE_NAME):latest
TEMPLATE_VARS_user-defined-web := --variable workspace_image_registry=index.docker.io/$(IMAGE_NAME)
TEMPLATE_VARS_drupal-core := --variable workspace_image_registry=index.docker.io/$(IMAGE_NAME) \
--variable cache_path=$(DRUPAL_CACHE_PATH)
TEMPLATE_VARS_drupal-contrib := --variable workspace_image_registry=index.docker.io/$(IMAGE_NAME)
TEMPLATE_VARS_freeform := --variable workspace_image_registry=index.docker.io/$(IMAGE_NAME)

# Per-template display metadata set via `coder templates edit` after push
# (coder templates push only supports --name, not --description)
TEMPLATE_EDIT_user-defined-web := --display-name "DDEV Web Workspace"
TEMPLATE_EDIT_drupal-core := --display-name "Drupal Core Development" \
--description "Drupal core dev environment: full DDEV stack, core clone, Umami demo site. Ready in about a minute."
TEMPLATE_EDIT_drupal-contrib := --display-name "Drupal Contrib Development" \
--description "Drupal contrib module/theme dev: clone any drupal.org project, optional issue branch checkout. Ready in 5-10 minutes."
TEMPLATE_EDIT_freeform := --display-name "DDEV Freeform (Traefik)" --default-ttl 24h

# Shared recipe for pushing any template (call with template name as argument)
Expand Down Expand Up @@ -167,12 +170,16 @@ push-template-user-defined-web: ## Push user-defined-web template to Coder
push-template-drupal-core: ## Push drupal-core template to Coder
$(call push_template,drupal-core)

.PHONY: push-template-drupal-contrib
push-template-drupal-contrib: ## Push drupal-contrib template to Coder
$(call push_template,drupal-contrib)

.PHONY: push-template-freeform
push-template-freeform: ## Push freeform template to Coder
$(call push_template,freeform)

.PHONY: push-all-templates
push-all-templates: push-template-user-defined-web push-template-drupal-core push-template-freeform ## Push all templates to Coder (no image build)
push-all-templates: push-template-user-defined-web push-template-drupal-core push-template-drupal-contrib push-template-freeform ## Push all templates to Coder (no image build)
@echo "All templates pushed!"

# --- Deploy targets ---
Expand All @@ -189,6 +196,10 @@ deploy-user-defined-web-no-cache: build-and-push-no-cache push-template-user-def
deploy-drupal-core: push-template-drupal-core ## Deploy drupal-core template (uses existing image)
@echo "Deployment of drupal-core complete!"

.PHONY: deploy-drupal-contrib
deploy-drupal-contrib: push-template-drupal-contrib ## Deploy drupal-contrib template (uses existing image)
@echo "Deployment of drupal-contrib complete!"

.PHONY: deploy-freeform
deploy-freeform: push-template-freeform ## Deploy freeform template (uses existing image)
@echo "Deployment of freeform complete!"
Expand Down
37 changes: 32 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Fully automated Drupal core development environment, including issue fork suppor
- **Setup**: Automatic (Drupal core cloned and installed, ~30s with seed cache)
- **Use Case**: Drupal core development, contribution, patch testing
- **Template Directory**: `drupal-core/`
- **Issue Picker**: [start.coder.ddev.com/drupal-issue](https://start.coder.ddev.com/drupal-issue) — paste any drupal.org issue URL to launch a workspace with the issue branch pre-checked-out
- **Issue Picker**: [start.coder.ddev.com/drupal-issue](https://start.coder.ddev.com/drupal-issue) — paste any drupal.org issue URL; the picker auto-detects core vs. contrib and routes to the right template
- **Includes**:
- Pre-cloned Drupal core (main branch by default)
- Issue fork checkout with automatic Composer dependency resolution (Drupal 10, 11, and 12/main)
Expand All @@ -136,20 +136,43 @@ Fully automated Drupal core development environment, including issue fork suppor
coder create --template drupal-core my-drupal-dev
```

### drupal-contrib (Drupal Contrib Development)

Automated environment for developing Drupal contrib modules and themes, with optional issue branch support.

- **Setup**: Automatic (module/theme cloned, Drupal installed as dev dependency via `ddev-drupal-contrib`, ~5-10 min)
- **Use Case**: Contrib module/theme development, issue queue work on any drupal.org project
- **Template Directory**: `drupal-contrib/`
- **Issue Picker**: [start.coder.ddev.com/drupal-issue](https://start.coder.ddev.com/drupal-issue) — paste a drupal.org issue or project URL; auto-detects contrib
- **Includes**:
- Any drupal.org project cloned (modules and themes)
- Issue fork checkout via `issue_fork` + `issue_branch` parameters
- DDEV with [`ddev-drupal-contrib`](https://github.com/ddev/ddev-drupal-contrib) addon (adds `ddev phpunit`, `ddev phpcs`, `ddev phpstan`)
- Drupal installed as a dev dependency (module/theme auto-symlinked into web root)

**Create workspace:**
```bash
coder create --template drupal-contrib my-contrib-dev \
--parameter project_name=token \
--parameter drupal_version=11
```

### Choosing a Template

- Use **user-defined-web** for:
- Contrib module development
- Site building
- Site building and custom site projects
- General Drupal/PHP projects
- Maximum flexibility

- Use **drupal-core** for:
- Drupal core patches
- Core issue queue work
- Drupal core patches and issue queue work
- Testing Drupal core changes
- Learning Drupal internals

- Use **drupal-contrib** for:
- Contrib module or theme development
- Working on drupal.org contrib issue queues

## Usage

Create a new workspace using your chosen template:
Expand All @@ -160,6 +183,10 @@ coder create --template user-defined-web <workspace-name>

# Drupal core development environment
coder create --template drupal-core <workspace-name>

# Drupal contrib module/theme development
coder create --template drupal-contrib <workspace-name> \
--parameter project_name=token --parameter drupal_version=11
```

**Access your project:**
Expand Down
Loading