Imported from AlexSkrypnyk/drupal_extension_scaffold_destination_circleci (
AGENTS.md). Install upstream withnpx skills add AlexSkrypnyk/drupal_extension_scaffold_destination_circleci. Copyright stays with the author.
AGENTS.md
This file provides guidance to AI agents when working with code in this repository.
Overview
This is a Drupal extension scaffold template for creating contributed modules or themes. The project provides a complete development environment with CI configuration, testing setup, and deployment automation for publishing to Drupal.org.
Development Commands
HARD RULE - use the provided command wrappers, never the tool binaries directly. When make or ahoy exposes a command for a task, use that command; do not call the underlying binary directly. Each wrapper chdirs into build/ and runs the tool with the config, plugins, and environment that CI uses, so a raw invocation from the repository root silently diverges from CI - it can pass locally while CI fails (or vice versa), or crash outright when a relative path resolves against the wrong directory. If no wrapped command covers what you need, extend the make / ahoy target rather than making a one-off raw call; if that is not feasible, stop and ask.
Run each tool through its make wrapper, never the binary directly:
- PHPCS / PHPCBF:
make lint/make lint-fix- nevervendor/bin/phpcsorvendor/bin/phpcbf.
- PHPStan:
make lint- nevervendor/bin/phpstan.
- Rector:
make lint(dry-run) /make lint-fix- nevervendor/bin/rector.
- Twig CS Fixer:
make lint/make lint-fix- nevervendor/bin/twig-cs-fixer.
- ESLint / Stylelint:
make lint/make lint-fix- nevernpx eslintornpx stylelint.
- CSpell:
make lint- nevernpx cspell.
- PHPUnit:
make test/make test-unit/make test-kernel/make test-functional- nevervendor/bin/phpunit.
- Jest:
make test-javascript- nevernpx jest.
- Drush:
make drush <command>- neverbuild/vendor/bin/drushdirectly.
Run each tool through its ahoy wrapper, never the binary directly:
- PHPCS / PHPCBF:
ahoy lint/ahoy lint-fix- nevervendor/bin/phpcsorvendor/bin/phpcbf.
- PHPStan:
ahoy lint- nevervendor/bin/phpstan.
- Rector:
ahoy lint(dry-run) /ahoy lint-fix- nevervendor/bin/rector.
- Twig CS Fixer:
ahoy lint/ahoy lint-fix- nevervendor/bin/twig-cs-fixer.
- ESLint / Stylelint:
ahoy lint/ahoy lint-fix- nevernpx eslintornpx stylelint.
- CSpell:
ahoy lint- nevernpx cspell.
- PHPUnit:
ahoy test/ahoy test-unit/ahoy test-kernel/ahoy test-functional- nevervendor/bin/phpunit.
- Jest:
ahoy test-javascript- nevernpx jest.
- Drush:
ahoy drush <command>- neverbuild/vendor/bin/drushdirectly.
Build and Environment Management
Using Make (default):
make build- Complete build (stop → assemble → start → provision)make assemble- Assemble codebase with dependenciesmake start- Start PHP development servermake stop- Stop development servermake provision- Install/provision Drupal sitemake reset- Clean build directory and logs (aliases:make delete,make destroy)
Using Ahoy (alternative):
ahoy build- Complete build processahoy assemble- Assemble codebaseahoy start- Start development serverahoy provision- Provision Drupal site
Code Quality
Linting:
make lint- Run all linting toolsmake lint-fix- Auto-fix coding standards violations
ahoy lint- Run all linting toolsahoy lint-fix- Auto-fix coding standards violations
Testing:
make test- Run all tests
make test-unit- Run unit tests onlymake test-kernel- Run kernel tests onlymake test-functional- Run functional tests only
make test-functional-javascript- Run FunctionalJavascript tests (uses the local Chrome by default; setWEBDRIVER_BACKEND=seleniumfor Docker)
make test-javascript- Run JavaScript unit tests with Jest (alias:make test-js)
make browser-start- Start the browser for FunctionalJavascript tests (local Chrome by default; setWEBDRIVER_BACKEND=seleniumfor Docker)make browser-stop- Stop the browser
ahoy test- Run all tests
ahoy test-unit- Run unit tests onlyahoy test-kernel- Run kernel tests onlyahoy test-functional- Run functional tests only
ahoy test-functional-javascript- Run FunctionalJavascript tests (uses the local Chrome by default; setWEBDRIVER_BACKEND=seleniumfor Docker)
ahoy test-javascript- Run JavaScript unit tests with Jest (alias:ahoy test-js)
ahoy browser-start- Start the browser for FunctionalJavascript tests (local Chrome by default; setWEBDRIVER_BACKEND=seleniumfor Docker)ahoy browser-stop- Stop the browser
Drupal Commands
make drush <command>- Run Drush commandsmake login- Get one-time login link
ahoy drush <command>- Run Drush commandsahoy login- Get one-time login link
Diagnostics
make info- Print a read-only summary of PHP/Drupal/Composer/Drush/Node versions, webserver host/port (with source), XDebug state, build directory, database path, and active profile. (alias:make describe)
ahoy info- Print a read-only summary of PHP/Drupal/Composer/Drush/Node versions, webserver host/port (with source), XDebug state, build directory, database path, and active profile. (alias:ahoy describe)
Project Structure
Key Directories:
src/- Extension source code (services, forms, etc.)
tests/src/- PHPUnit tests (Unit/, Kernel/, Functional/)
config/schema/- Configuration schema definitionsbuild/- Assembled Drupal codebase (symlinked extension).devtools/- Build and deployment scripts used by CIscripts/- Custom lifecycle hooks: post-assemble (assemble-*.sh), post-provision (provision-*.sh), post-start (start-*.sh), and pre-stop (stop-*.sh). Run automatically during each phase in lexicographic order; non-zero exit aborts the parent. Excluded from distribution archives via.gitattributes
Template Files (before init):
your_extension.*- Template extension filesYourExtensionService.php- Main service class template
Architecture
- Service-based architecture: Main functionality in services registered via
*.services.yml - Configuration-driven: Uses Drupal configuration system with schema validation
- Test coverage: Unit, kernel, and functional test examples provided
- Form integration: Admin forms in
src/Form/for configuration
Environment Variables
DRUPAL_VERSION- Target Drupal version (e.g.,10,11,11@alpha)WEBSERVER_HOST- Development server host (default: localhost)WEBSERVER_PORT- Development server port. Auto-discovered from range 8000-8099 and written to.envif not already set
WEBDRIVER_BACKEND- FunctionalJavascript WebDriver backend:chromedriver(default, drives the locally installed Chrome with no Docker) orselenium(Docker container)WEBDRIVER_PORT- Port for the WebDriver endpoint (both backends). Auto-discovered from 4444 and written to.envif not already set, so several projects can run FunctionalJavascript tests simultaneously. The endpoint inphpunit.xmlis the default for port 4444; tests reach the resolved port because the FunctionalJavascript base class rewrites the port inMINK_DRIVER_ARGS_WEBDRIVERfrom this variable, so FunctionalJavascript tests must extend that base class (or export their ownMINK_DRIVER_ARGS_WEBDRIVER)
GITHUB_TOKEN- GitHub API token to avoid rate limitsDEBUG- Set to1to stream the full output of the underlying commands (Composer, npm, Drush). By default this output is suppressed and shown only when a command fails
Development Workflow
- Run
php init.phpto customize template for your extension - Build environment:
make buildorahoy build - Develop your extension code in
src/ - Check standards:
make lintorahoy lint - Run tests:
make testorahoy test - Access site at http://localhost:8000
Code Quality Tools
- CSpell: Spell checking across the codebase (config at
.cspell.json)
- PHPCS: Drupal and DrupalPractice standards
- PHPStan: Static analysis with Drupal extensions
- Rector: Automated refactoring and deprecation fixes
- Twig CS Fixer: Twig template formatting
CI/CD Support
- GitHub Actions:
.github/workflows/test.ymland deployment - CircleCI:
.circleci/config.ymlconfiguration - Matrix testing: PHP 8.3-8.5, Drupal 10-11
- Automated deployment: Mirror to Drupal.org on release
Important Notes
- The
build/directory contains the assembled Drupal site - Extension files are symlinked from root into
build/web/modules/custom/(module) orbuild/web/themes/custom/(theme) - SQLite database created in
/tmp/site_[EXTENSION_NAME].sqlite - All quality tools run from within
build/directory
Updating the scaffold
When the user asks to update this project's scaffold (e.g. "update scaffold"), fetch the update skill from GitHub into the local .claude/skills/ directory, then invoke it:
-
Create the target directory if it does not exist:
mkdir -p .claude/skills/update-consumer-drupal-extension-scaffold -
Download the skill:
curl -sSL https://raw.githubusercontent.com/AlexSkrypnyk/drupal_extension_scaffold/1.x/.scaffold/skills/update-consumer-drupal-extension-scaffold/SKILL.md -o .claude/skills/update-consumer-drupal-extension-scaffold/SKILL.md -
Invoke the
update-consumer-drupal-extension-scaffoldskill and follow its steps.
The skill directory is git-ignored - it is fetched on demand and not committed to the project.