CLI
The Vortex CLI is a self-contained Symfony Console application that
customizes the template based on user selections. It lives inside the same
repository as the template at .vortex/cli/, which allows changes to the
template and the CLI to be combined within a single pull request — ensuring
that template modifications and the corresponding CLI logic stay in sync.
Architecture
The CLI is a PHP application rather than a shell script because it
touches many different parts of the template and requires complex management of
different permutations of the template variants. Using PHP allows the CLI
to provide a terminal UI experience via
Laravel Prompts, where users are guided
through a series of questions to customize their project. At the same time,
the CLI supports non-interactive installs (via the --no-interaction
flag) for both new and existing projects, as well as updates — making it
suitable for automated pipelines and CI environments.
The binary is vortex and it exposes three commands: install (the default),
check-requirements and build.
Prompt answers can be provided from multiple sources, resolved in the following priority order:
- Configuration file — a JSON file passed via the
--configoption. - Environment variables — using the
VORTEX_CLI_INSTALL_PROMPT_<handler_id>naming convention. - Discovery — auto-detection from existing project files (e.g.,
composer.json,.env, hosting-specific files like.lagoon.yml). - Handler defaults — fallback values defined by each handler.
This means the CLI can be driven entirely by a config file or environment variables without any user interaction, which is essential for reproducible installations and automated testing.
The core flow is:
- User downloads the PHAR from https://www.vortextemplate.com/install
and runs it with
php vortex.phar. InstallCommandorchestrates the installation process.PromptManagercollects user choices via interactive prompts.- Handlers queue file operations based on selections — each handler is responsible for a specific aspect of the template (CI provider, hosting, services, theme, deployment strategy, etc.).
- File operations execute: conditional tokens are processed to include or exclude content based on the selected features.
- The output is a fully customized Drupal project.
Conditional token system
In addition to string substitution for handling simple replacements and additions, the install command uses a token system to conditionally exclude entire blocks of content from template files. This simplifies the management of optional features — rather than requiring complex logic to surgically remove lines from configuration files, scripts, or documentation, maintainers simply wrap the relevant content in token markers. When a feature is not selected during installation, the install command removes everything between the markers.
Markdown:
[//]: # (#;< TOKEN_NAME)
Content removed if feature not selected
[//]: # (#;> TOKEN_NAME)
Shell/YAML:
#;< TOKEN_NAME
Content removed if feature not selected
#;> TOKEN_NAME
Distribution
The PHAR is published to three stable paths, each served with no file extension:
| Path | Contents |
|---|---|
https://www.vortextemplate.com/install | The current major's pin |
https://www.vortextemplate.com/v1/install | The 1.x line |
https://www.vortextemplate.com/v2/install | The 2.x line |
The bare /install path is a copy of the current major's pin, selected by the
VORTEX_CURRENT_MAJOR repository variable. Each per-major path is always built
from that major's own branch, so 1.x continues to publish the installer it
ships while 2.x publishes this CLI.
The documented download filename is vortex.phar:
curl -SsL https://www.vortextemplate.com/install > vortex.phar && php vortex.phar
Environment variables
Variables are scoped by what they configure. CLI-level settings use the
VORTEX_CLI_ prefix; settings that belong to the install command use
VORTEX_CLI_INSTALL_.
| Variable | Purpose |
|---|---|
VORTEX_CLI_VERSION | Overrides the stamped version |
VORTEX_CLI_URL | Where vortex-update downloads the PHAR |
VORTEX_CLI_PATH | Local PHAR path, overrides the URL |
VORTEX_CLI_URL_CACHE_BUST | Cache-busting parameter for the URL |
VORTEX_CLI_INSTALL_TMP_DIR | Working directory for the downloaded source |
VORTEX_CLI_INSTALL_TEMPLATE_REPO | Template repository to install from |
VORTEX_CLI_INSTALL_PROMPT_<ID> | Answer for a single prompt |
The superseded VORTEX_INSTALLER_* names still resolve: when a current name is
unset, the matching legacy name supplies the value and the run prints a
deprecation notice naming its replacement. The fallback is a migration aid and
will be removed in a future major.
Testing
The CLI uses a multi-layered testing approach with unit tests for individual components and functional tests for full installation scenarios.
Unit testing
Unit tests cover core utilities (Yaml, File, Git, Validator,
Strings), downloaders, runners, and handler discovery logic. Each component
is tested in isolation with mocks provided by
Mockery.
Functional testing with snapshots
For every test permutation, the install command initiates a fresh project from the Vortex template with a specific combination of user selections and runs assertions against the resulting files. Because a single template change can affect a hundred plus installation permutations, snapshot testing makes it easy to review the impact of a change across all scenarios as diffs — ensuring that regressions are caught before they reach consumers.
Each handler has a dedicated functional test class extending
AbstractHandlerProcessTestCase, covering every aspect of the installation
process: CI providers, hosting providers, services, themes, deployment
strategies, database sources, notification channels, and more.
These tests use the alexskrypnyk/snapshot
library for snapshot-based testing. The snapshot system works on a
baseline + diff pattern:
_baseline/contains the complete reference installation — a full set of template files as they would appear after a default installation.- Each scenario directory (e.g.,
services_no_clamav/,hosting_acquia/,deploy_types_all_circleci/) contains only the delta changes from the baseline.
When a test runs, the snapshot library applies the scenario's diffs on top of the baseline and compares the result against the actual output. This approach keeps the fixture files maintainable — instead of duplicating the entire template for each scenario, only the differences are stored.
The fixture directories in .vortex/cli/tests/Fixtures/handler_process/
cover scenarios like:
- CI providers: GitHub Actions, CircleCI
- Hosting: Acquia, Lagoon
- Services: Solr, Redis, ClamAV (enabled/disabled combinations)
- Deployment: artifact, container image, webhook, Lagoon, all combined
- Database sources: Acquia, Lagoon, FTP, S3, URL, container registry
- And many more permutations
Running tests
cd .vortex/cli
# Install Composer dependencies.
composer install
# Run all tests.
composer test
# Run only handler functional tests.
./vendor/bin/phpunit --filter "Handlers\\\\"
# Run a specific handler test.
./vendor/bin/phpunit --filter "ServicesHandlerProcessTest"
Updating snapshots
Fixture files should never be modified directly. When the template or CLI logic changes, update the snapshots:
# From .vortex/ directory (recommended).
ahoy update-snapshots
# Manual (for debugging specific scenarios).
cd .vortex/cli
UPDATE_SNAPSHOTS=1 ./vendor/bin/phpunit --filter "testHandlerProcess.*baseline"
When UPDATE_SNAPSHOTS is set, the install command runs for every permutation,
initiates a fresh project for each scenario, and automatically updates the
fixture files to match the current output. The resulting changes appear as
diffs in version control, making it straightforward to review exactly how a
template or CLI change affects each scenario.
Releasing
The CLI is packaged as a PHAR archive using
Box. It is released on each GitHub
release of the Vortex template and deployed to
https://www.vortextemplate.com/install. Additionally, it is
published for every branch containing release-docs or release-cli
in its name, which allows testing CLI changes before a formal release.
Install demo video
The documentation includes an interactive recording of the install flow captured
with asciinema. The recording is produced by
.vortex/docs/.utils/update-videos.php which:
- Builds the PHAR from source.
- Generates an Expect script that automates the interaction, simulating human-like typing with delays.
- Records the session with
asciinema recinto a JSON cast file. - Post-processes the recording (sanitizes paths, removes spawn lines).
- Converts the cast file to SVG and PNG using a custom
svg-term-render.jsscript, and optionally to GIF usingagg.
The output files are stored in .vortex/docs/static/img/ as cli-install.*
and embedded in the documentation using a custom AsciinemaPlayer React
component.
To update the video:
cd .vortex
ahoy update-videos cli-install
sharp-cli, which renders the PNG posters, is a declared dependency of
.vortex/docs, so it is pinned by yarn.lock and installed by the existing
yarn install --frozen-lockfile.
asciinema is downloaded rather than installed from a lockfile, so the
workflows that regenerate the video in CI pin it explicitly:
.github/workflows/vortex-release.yml and
.github/workflows/vortex-test-installer.yml each set ASCIINEMA_VERSION and
ASCIINEMA_SHA256. Bump both workflows together, so the release and the
installer test always generate assets with identical tooling.