Skip to main content
Version: v2

Tooling

The PHP scripts that consumer projects run for building, provisioning, database work, deployment, notifications, and helpers such as doctor and info live in .vortex/tooling/. This page is for contributors who change them: it covers the package layout, the script conventions, the test levels, and how the package gets published. To use the scripts in a project, see Architecture > Automation scripts.

The scripts are published as the standalone drevops/vortex-tooling Composer package. Consumer projects install the package and run the user-facing scripts, the ones listed under bin in the package composer.json, from vendor/bin/vortex-*. The other scripts, such as the per-channel vortex-notify-* ones, run through those entry points.

Package layout​

The following table lists the contents of .vortex/tooling/:

PathPurpose
src/The shipped scripts and their shared helpers.php
composer.jsonThe package definition, with the user-facing scripts under bin and no version field
README.mdThe package readme, which ships with the scripts
tests/Unit/PHPUnit unit tests covering the shipped scripts
tests/Self/, tests/Traits/, tests/Fixtures/The mocking infrastructure that the unit tests use, and its own tests
playground/Manual scripts that hit live services, such as the Acquia and Lagoon database fetches; not automated
phpcs.xml, phpstan.neon, rector.php, check-no-exit.phpThe lint configuration and checks
phpunit.xmlThe PHPUnit configuration

.gitattributes marks everything except src/, composer.json, and README.md as export-ignore, so those 3 make up the published archive.

While developing Vortex itself, the template's root composer.json resolves the package from the in-tree copy through a path repository with a pinned version. The CLI strips that entry so consumer sites resolve the published package from Packagist. The same version is also pinned in scripts/vortex-tooling.sh and .vortex/tests/phpunit/Traits/SutTrait.php; Release > Release process lists every spot to bump.

Script conventions​

The shipped scripts are PHP and use PHP internals wherever possible, so a consumer site needs no extra host binaries. Every script follows the same pattern:

  • A helpers.php include, which also loads .env and .env.local while letting real environment variables win
  • Variables read through getenv_default() and getenv_required(), with their fallback chains
  • The shared output helpers

The output helpers have a strict contract:

  • INFO() opens and closes an operation.
  • TASK() announces work that's starting and reports its outcome. It takes both the starting and the finishing message, so a task always closes.
  • PASS() reports success on its own, for a check that holds or a step that's skipped or finished.
  • NOTE() is a standalone remark that starts no task.
  • FAIL() reports the failure and aborts.
TASK('Disabling Search API Solr server.', 'Disabled Search API Solr server.', function (): void {
drush('search-api:server-disable solr');
});

When the body returns, TASK() prints [ OK ] and the finishing message. The body signals failure by throwing: TASK() reports the thrown message as [FAIL] and stops the script.

The scripts stop with quit() instead of exit, so the unit tests can intercept the exit code. composer lint runs check-no-exit.php, which fails on any exit call in src/.

Consumer projects that need to alter a shipped script do so with a cweagans/composer-patches patch, never by overriding it at runtime.

Add a script​

To add a user-facing script, follow these steps:

  1. Create it in src/ as vortex-<name>, following the script conventions.
  2. Add src/vortex-<name> to the bin list in .vortex/tooling/composer.json.

Composer creates a vendor/bin entry only for the scripts in that list. Leave helper scripts off it: vortex-notify, for example, runs each vortex-notify-* channel script by path from src/.

Testing​

Tests cover the scripts at 3 levels:

  • Unit - PHPUnit tests in tooling/tests/Unit/ cover the shipped scripts with external commands mocked. Most test classes are named after the script they cover, without its vortex- prefix, such as DeployLagoonTest for vortex-deploy-lagoon. Run them from .vortex/:

    cd .vortex
    ahoy test-tooling

    # Run a single test class.
    ahoy test-tooling -- --filter=DeployLagoonTest

    To check the code style and static analysis, run ahoy lint-tooling from .vortex/.

  • Integration - the template's PHPUnit functional tests in .vortex/tests/ run the scripts end-to-end. See Template > Testing.

  • Manual - tooling/playground/ holds scripts that hit live services. They aren't automated.

Publishing​

The Vortex - Publish tooling workflow (.github/workflows/vortex-publish-tooling.yml) mirrors .vortex/tooling/ to the drevops/vortex-tooling repository on every push to main, 2.x, and any branch whose name contains vortex-tooling, matching the source branch name.

Each push publishes 1 commit to the mirror, an empty one when nothing in .vortex/tooling/ changed. Its subject matches the subject of the pushed commit, and its body records provenance in 2 lines, Source: drevops/vortex@<sha> and Branch: <branch>. A branch that's new to the mirror starts there as an orphan with no shared history. Treat the mirror as read-only, because each publish replaces its files with the contents of .vortex/tooling/.

To confirm a publish, open the Vortex - Publish tooling run for your push. Its publish step ends with Published 'drevops/vortex@<sha>' to drevops/vortex-tooling#<branch>.

The workflow doesn't set a package version: Packagist reads it from the mirror's tags and branch names. So never hardcode a version in the package composer.json.

You tag tooling releases on the mirror repository. When the tooling has changed since the last release, tag it before the Vortex release, so the template can require the new tag. See Release > Tagging.