This add-on installs a local code quality tool suite for Drupal projects and IDE usage, starting from Drupal.org GitLab CI template defaults. It provides DDEV commands and host shims so developers can run the same checks locally that GitLab CI runs on Drupal.org.
DDEV add-on that installs local code quality tooling based on Drupal.org GitLab CI template defaults (PHPStan, PHPCS, ESLint, Stylelint, Prettier, CSpell) for local CLI and IDE usage.
Tools covered:
# Install from GitHub
ddev add-on get UltraBob/ddev-drupal-code-quality
ddev restart
# Or, for local development
ddev add-on get /path/to/ddev-drupal-code-quality
ddev restart
There is overlap with other Drupal-focused DDEV add-ons. The key difference is the project type each one is designed for.
| Add-on | Best for | Typical project layout |
|---|---|---|
UltraBob/ddev-drupal-code-quality |
Full Drupal website projects where your site repo already contains Drupal code and custom code. | Existing site/project repo; installs code-quality configs and IDE shims in-place. |
ddev/ddev-drupal-contrib |
Drupal contrib module/theme development where the contrib project is the center of the repo. | Contrib project repo with Drupal scaffolded around it (symlink workflow). |
justafish/ddev-drupal-core-dev / joachim-n/ddev-drupal-core-dev |
Drupal core development. | Drupal core checkout or core-dev project template. |
ddev/ddev-drupal-contrib.ddev-drupal-core-dev (see above
forks).commands/: DDEV web commands copied into project .ddev/commands.drupal-code-quality/: project-root configs and .ddev shims copied by the installer.dcq-install.sh: conflict-aware installer invoked by install.yaml.install.yaml: DDEV add-on install definition.During installation, the add-on copies Drupal.org GitLab CI template default config files into the project root. If conflicts are detected, you can choose to back up and replace, skip, or abort. Skipping a config may diverge from the Drupal.org GitLab CI template defaults. The installer will prompt for:
drupal/core-dev or run ddev composer install).package.json exists..gitignore update for dcq-reports/ (default: yes).Recommended settings apply these defaults without further prompts:
replace conflicts (with backups), install PHP dev tools, install JS deps in the
project root, set PHPStan level 3, merge IDE settings, and add dcq-reports/
to .gitignore. Non-interactive runs with no overrides apply the recommended
settings automatically.
If PHPStan/PHPCS/PHPCBF binaries are missing, the installer prompts to add
drupal/core-dev (or to run ddev composer install if it is already required).
It uses ddev composer require --with-all-dependencies to avoid lockfile
conflicts.
For CLI usage, prefer the DDEV commands:
ddev phpstan
ddev phpcs
ddev phpcbf
ddev eslint
ddev stylelint
ddev prettier
ddev cspell
ddev composer-validate
ddev checks
Host shims are installed under .ddev/drupal-code-quality/tooling/bin. These are intended for
IDE tool paths or tools that require a local binary path:
./.ddev/drupal-code-quality/tooling/bin/phpstan
./.ddev/drupal-code-quality/tooling/bin/phpcs
./.ddev/drupal-code-quality/tooling/bin/eslint
./.ddev/drupal-code-quality/tooling/bin/stylelint
./.ddev/drupal-code-quality/tooling/bin/prettier
./.ddev/drupal-code-quality/tooling/bin/cspell
./.ddev/drupal-code-quality/tooling/bin/checks
ddev eslint-fix, ddev prettier-fix, and ddev stylelint-fix apply formatting changes directly, matching the behavior of their underlying tools (eslint --fix, prettier --write, stylelint --fix). All three commands support an optional --preview flag that builds a patch preview (saved to dcq-reports/), displays it, and prompts Apply these changes? [y/N] before applying. Use --preview when you want to review changes before committing them.
Starter settings live in .ddev/drupal-code-quality/ide-settings/vscode. During install, you can
choose to merge them into .vscode/settings.json and
.vscode/extensions.json, back up and overwrite, or skip and handle them
manually.
The template points PHP tooling at .ddev/drupal-code-quality/tooling/bin and JS tooling at local
node_modules. Override the paths if you prefer a different location.
web/).
The installer records the docroot in .ddev/.dcq-docroot for wrappers.ddev composer install).package.json from Drupal core when missing).
dcq-reports/ is created at the project root when running checks
or the *-fix commands (logs + patch previews).dcq-reports/ to .gitignore if you do not want to track it.ESLINT_TOOLCHAIN=auto (default) prefers root toolchain when root configs exist.ESLINT_TOOLCHAIN=core forces Drupal core JS toolchain.ESLINT_TOOLCHAIN=root forces project root toolchain.ESLINT_CONFIG_MODE=nearest (default) groups by nearest config file.ESLINT_CONFIG_MODE=fixed prefers .eslintrc.passing.json, then
.eslintrc.json in the project root.DCQ_ESLINT_QUIET=1 (default) adds --quiet to ddev eslint and
ddev eslint-fix, so warnings are suppressed.DCQ_ESLINT_QUIET=0 to include warnings in CLI output. Persist this in
.ddev/config.yaml (or .ddev/config.yml):
```yaml
web_environment:
"eslint.quiet": false in .vscode/settings.json to include warnings in
the IDE.overwrite regenerates IDE settings from template;
merge only adds missing keys and will not change an existing
eslint.quiet value.ddev exec php /mnt/ddev_config/drupal-code-quality/tooling/scripts/prepare-cspell.php -s .prepared once and
replace .cspell.json after reviewing the diff.ddev cspell defaults to scanning . when no paths are passed; scope is
controlled by .cspell.json (especially ignorePaths)..cspell-project-words.txt is created by the installer (empty) and updated
by ddev cspell-suggest when you accept suggested words..eslintignore, .stylelintignore, and .prettierignore..prettierignore so the file remains the
single source of truth for Prettier scope..phpcs.xml is installed by the add-on, ddev phpcs and
ddev phpcbf with no path default to scanning the configured docroot.__DOCROOT__/core/**, **/contrib/**,
**/node_modules/**, and __DOCROOT__/sites/*/files/**.ddev php-parallel-lint remains wrapper-scoped because the tool does not
provide an equivalent project config file for default target paths.ddev phpstan --generate-baseline.phpstan-baseline.neon at the project root and updates
phpstan.neon to include it.settings.php files), then work it down over
time. Avoid using it to hide new regressions.ddev phpstan requires project config (phpstan.neon*) unless you pass
--configuration <path>.DCQ_INSTALL_MODE: replace, skip, or abort for conflict handling.DCQ_NONINTERACTIVE=true: disable prompts; if no overrides are set, applies
the recommended settings automatically.DCQ_PHPSTAN_LEVEL: set phpstan.neon level (0-10) without prompting.DCQ_ESLINT_QUIET: 1/unset to suppress ESLint warnings by default
(GitLab CI parity), 0 to include warnings. This can be set in
.ddev/config.yaml under web_environment.DCQ_INSTALL_DEPS: install/true to auto-install missing drupal/core-dev,
skip/false to skip, or unset to prompt when interactive.DCQ_INSTALL_NODE_DEPS: root to install JS deps in the project root (creates
a root package.json from core if missing; name uses the DDEV project name,
and prompts to add missing Drupal deps when a root package.json already
exists), install/true to auto-install in the project root, skip/false
to skip, or unset to prompt (default: install in the project root). The
installer selects npm/yarn based on existing lockfiles.DCQ_INSTALL_GITIGNORE: add/true to add dcq-reports/ to .gitignore
without prompting, skip/false to skip, or unset to prompt when interactive
(default: yes).DCQ_INSTALL_IDE_SETTINGS: merge to add missing VS Code settings and
extension recommendations, overwrite to back up and replace, skip to
handle manually, or unset to prompt.The add-on ships config files sourced from Drupal core and the Drupal GitLab CI templates. A sync script checks for upstream changes:
# Check for upstream config changes (report only)
./scripts/sync-upstream-configs.sh
# Apply changes to local asset files
./scripts/sync-upstream-configs.sh --update
# Override upstream branches
./scripts/sync-upstream-configs.sh --branch-core=11.x --branch-templates=main
After applying updates, run the full test suite to validate configs still work:
DCQ_FULL_TESTS=1 bats --jobs 4 ./tests/test.bats
A weekly GitHub Actions workflow (upstream-config-check.yml) automatically
checks for upstream drift and opens an issue when changes are detected.
Removing the add-on cleans up the .ddev payload (commands, shims, assets,
manifest, and dcq-install.sh); project-root configs remain in place
intentionally. Remove them manually if desired.
Contributed and maintained by @UltraBob