Skip to main content

Tooling package

The commands that assemble, start, provision and deploy your extension come from the drevops/eddy-tooling Composer package, so fixes reach your project through a version constraint. The make and Ahoy commands and CI call them, and you can run them yourself as vendor/bin/eddy-<command> from the project root. This page covers how the package gets installed, what each command does and how to change one.

The package is published from the .eddy/tooling directory of Eddy, and its own repository is a read-only mirror. Report bugs and propose changes in drevops/eddy.

How it's installed​

Your project's composer.json belongs to the extension, and the tooling assembles the Drupal site into build itself. So scripts/eddy-tooling installs the package into vendor/, with each command in vendor/bin/eddy-*. make and Ahoy run the installer before the commands that need it, and CI runs it as a step of its own. Without make or Ahoy, run it yourself first:

scripts/eddy-tooling

The version constraint lives in composer.dev.json:

"require-dev": {
"drevops/eddy-tooling": "~1.0.5"
}

Keep the ~ constraint. It accepts patch releases and holds the minor version, so CI picks up fixes on its own. A new minor version arrives only with the next scaffold update. A scaffold update also raises the constraint whenever the new scaffold needs a newer release. The next run of the installer then replaces a release that lacks what the scaffold calls.

The installer records what it installed in vendor/eddy-tooling.json, and runs Composer only in these cases:

  • The constraint or the patches for the package change.
  • A local patch file changes.
  • vendor/eddy-tooling.json is missing, such as after reset removes vendor/.
  • A command of the installed package is missing from vendor/bin.

Otherwise it exits without running Composer, so running it before each command adds almost no time. A local checkout keeps its patch release until vendor/ is removed. CI installs from scratch, so it takes the newest patch release on every run. To take the newest patch release locally and keep vendor/, delete vendor/eddy-tooling.json. The installer reinstalls the package the next time it runs.

The package requires PHP 8.3 or newer, so every CI job and local build that installs it needs PHP 8.3 or newer too.

Leave scripts/eddy-tooling as it is

Never modify scripts/eddy-tooling, by hand or with AI. It comes from Eddy as-is, and a scaffold update replaces it with the release's copy. To add a step of your own, use a lifecycle script. To change what a command does, patch the package.

Commands​

The package installs the following commands into vendor/bin:

CommandPurpose
eddy-assembleAssembles a Drupal codebase in build/, installs the dependencies and links the extension into it.
eddy-startStarts the PHP built-in webserver. When neither your shell nor .env sets WEBSERVER_PORT, discovers a free port from 8000 to 8099 and writes it to .env.
eddy-stopStops the webserver.
eddy-provisionInstalls Drupal on the assembled site and enables the extension.
eddy-loginPrints a one-time login link without opening it in a browser, and draws it as a QR code when LOGIN_QRCODE is set.
eddy-deployMirrors the extension to a remote git repository, such as Drupal.org. CI runs it.
eddy-browser-startStarts the WebDriver backend for FunctionalJavascript tests. When neither your shell nor .env sets WEBDRIVER_PORT, discovers a free port from 4444 to 4543 and writes it to .env.
eddy-browser-stopStops the WebDriver backend.
eddy-infoPrints a summary of the environment, or a single field such as site-url for the wrappers to read.
eddy-qrcodeDraws a URL as a QR code in the terminal.

These pages describe each command in detail:

eddy-info fields​

vendor/bin/eddy-info FIELD prints a single value. An unknown field prints - and exits with code 1. The following table lists the fields:

FieldValue
drupal-versionThe installed Drupal version.
php-versionThe PHP version on your PATH.
composerThe Composer version.
drushThe Drush version of the build.
nodeThe Node.js version.
npmThe npm version.
webserver-hostThe webserver host, from WEBSERVER_HOST.
webserver-portThe webserver port, from WEBSERVER_PORT.
site-urlThe site URL: the tunnel URL when a tunnel is up, the webserver URL otherwise.
xdebugenabled or disabled for the running webserver, or - when it isn't running.
webdriver-portThe WebDriver port, from WEBDRIVER_PORT.
webdriver-backendThe WebDriver backend, from WEBDRIVER_BACKEND: chromedriver or selenium.
webdriver-hostThe value of WEBDRIVER_HOST. An empty line when the variable isn't set, since the default depends on the backend.
build-dirThe absolute path of the build directory.
databaseThe path of the SQLite database.
drupal-profileThe install profile, from DRUPAL_PROFILE.

Apart from webdriver-host, a value that can't be resolved, such as the Drupal version before the first build, prints -.

Command output​

The commands print their own progress and hide the output of the tools they run, such as Composer, npm ci and Drush, unless one of them fails. The output of npm run build and of your lifecycle scripts always shows. Debug your extension explains how to see all of it, and where the processes that keep running write their logs.

Patch the package​

The package is installed into vendor/, so an edit made there is lost on the next install. To change what a command does, declare a patch for the package in composer.dev.json, next to its version constraint:

"extra": {
"patches": {
"drevops/eddy-tooling": {
"Describe the change": "patches/eddy-tooling-change.patch"
}
}
}

The installer reads patches for the package from composer.dev.json only, and applies them with cweagans/composer-patches. A patch for the package in your extension's composer.json never reaches the tooling. Local patch paths are relative to the project root. The site build leaves out the package and its patch declarations, so it never applies them. The patch files still reach build/patches with the rest of the patches directory.

Use a patch only when a lifecycle script can't do the job. A script adds a step to a phase, while a patch changes the command itself. A patch can also stop applying when a tooling release changes the lines it touches.

How the package is tested​

Eddy's own test suite covers every command. Unit tests run each one with its external calls mocked, and functional tests build, run and lint a real extension through the same commands. Scaffold tests describes the suite.