An opinionated development environment for brewing Drupal Canvas, built with DDEV. I maintain this project to use for my day-to-day development work, with workflows and tools I prefer.
- Clone the repository
- Run:
ddev start \ && ddev clone-repo \ && ddev clone-repo-mercury \ && ddev composer install \ && ddev site-install \ && ddev launch \ && ddev ui --install
When a coding tool creates worktrees from web/modules/contrib/canvas as a
standalone project, wrap the existing Canvas worktree with an environment
worktree.
Use this in the tool setup hook, replacing SOURCE_TREE_PATH and
WORKTREE_PATH with the environment variables provided by the tool:
set -euo pipefail
SOURCE_ENV="$(cd "$SOURCE_TREE_PATH/../../../.." && pwd)"
"$SOURCE_ENV/scripts/wrap-canvas-worktree.sh" \
--source-env="$SOURCE_ENV" \
--canvas-worktree="$WORKTREE_PATH"This creates an environment wrapper next to the Canvas worktree, writes a unique
ignored DDEV project name, mounts the Canvas worktree into DDEV at
web/modules/contrib/canvas, and creates .ddev-env inside the Canvas worktree
as a symlink back to the environment wrapper.
From the Canvas worktree, run DDEV commands through the wrapper:
cd .ddev-env
ddev start
ddev composer install
ddev site-install
ddev ui --installThe DDEV project name is derived from the checkout directory because
.ddev/config.yaml does not set name. This keeps Git worktrees independent: a
checkout in canvas-env uses https://canvas-env.ddev.site, and a worktree in
canvas-env-issue-123 uses https://canvas-env-issue-123.ddev.site.
Use ignored local overrides in .ddev/config.local.yaml only when a checkout
needs a specific project name.
This project uses two agent configuration scopes:
- Environment scope for root-level tooling and infrastructure tasks.
- Canvas scope for module code work in
web/modules/contrib/canvas.
For details on workspace entrypoints and skill locations, see
agents/README.md.
Running ddev clone-repo also wires the Canvas agent files in the nested
repository by creating symlinks for AGENTS.md and .agents/skills, and by
adding local-only nested-repo excludes for AGENTS.md and .agents/ in
.git/info/exclude.
| Command | Description |
|---|---|
n |
Run npm inside the UI directory.--canvas-dir=<directory>: runs npm inside specified directory( root, ui, astro, cli, or docs) |
ui |
Build the UI code and start the dev server.--install -i: runs npm install before--skip-build -s: skips the build step |
playwright |
Run Playwright in UI mode. (Accessible via VNC at https://<project-name>.ddev.site:6081/vnc.html)--spec <path-to-spec>: runs a spec in headless mode |
cy,cypress |
Run the Cypress UI in end-to-end testing mode (Accessible via VNC at https://<project-name>.ddev.site:6081/vnc.html)--spec <path-to-spec>: runs a spec in headless mode--component -c: use component testing mode |
phpunit [path] |
Run PHPUnit tests in the module's codebase[path]: narrows to the path, relative to the module directory |
phpcs [path] |
Run PHP Code Beautifier and Fixer in the module's codebase[path]: narrows to the path, relative to the module directory |
phpstan |
Run PHPStan in the module's codebase |
si,site-install |
Install Drupal site, install and configure modules: Canvas and Canvas OAuth.--ui: also installs Canvas Vite for UI development--mercury -m: applies the Mercury recipe instead of the Stark and Humanify recipes--stark: skips the Humanify recipe, and creates an empty page |
clone-repo |
Clone the Canvas module's repository, add local nested-repo excludes for AGENTS.md and .agents/, and set symlinks for AGENTS.md and .agents/skills. (Runs on the host.) |
clone-repo-mercury |
Clone the Mercury theme's repository. (Runs on the host.) |
See the list of commands provided by DDEV out-of-the box.
| Script | Description |
|---|---|
scripts/wrap-canvas-worktree.sh |
Wrap an existing Canvas worktree with a sibling environment worktree and DDEV bind mount. |
scripts/wire-canvas-agents.sh |
Wire local Canvas AGENTS.md and .agents/skills into a Canvas checkout or worktree. |
scripts/install-canvas-packages.sh |
Build and install local Canvas package tarballs into a target project. |
Use ddev drush dcer to export specific entities to recipe content folders.
Export a canvas_page entity:
ddev drush dcer canvas_page 1 --folder=../recipes/canvas_dev_humanify/contentExport a menu_link_content entity:
ddev drush dcer menu_link_content 1 --folder=../../recipes/canvas_dev_humanify/contentMy work on Drupal Canvas is made possible by Acquia.