|
| 1 | +--- |
| 2 | +sidebar_position: 3 |
| 3 | +--- |
| 4 | + |
| 5 | +import AsciinemaPlayer from '@site/src/components/AsciinemaPlayer'; |
| 6 | + |
| 7 | +# Installer |
| 8 | + |
| 9 | +The **Vortex** installer is a self-contained Symfony Console application that |
| 10 | +customizes the template based on user selections. It lives inside the same |
| 11 | +repository as the template at `.vortex/installer/`, which allows changes to the |
| 12 | +template and installer to be combined within a single pull request — ensuring |
| 13 | +that template modifications and the corresponding installer logic stay in sync. |
| 14 | + |
| 15 | +<AsciinemaPlayer |
| 16 | + src="/img/installer.json" |
| 17 | + poster="npt:0:1" |
| 18 | + autoPlay={false} |
| 19 | + loop={false} |
| 20 | + preload={true} |
| 21 | + controls={true} |
| 22 | + theme="asciinema" |
| 23 | + terminalLineHeight={1.0} |
| 24 | +/> |
| 25 | + |
| 26 | +## Architecture |
| 27 | + |
| 28 | +The installer is a PHP application rather than a shell script because it |
| 29 | +touches many different parts of the template and requires complex management of |
| 30 | +different permutations of the template variants. Using PHP allows the installer |
| 31 | +to provide a **terminal UI experience** via |
| 32 | +[Laravel Prompts](https://github.com/laravel/prompts), where users are guided |
| 33 | +through a series of questions to customize their project. At the same time, |
| 34 | +the installer supports **non-interactive installs** (via the `--no-interaction` |
| 35 | +flag) for both new and existing projects, as well as updates — making it |
| 36 | +suitable for automated pipelines and CI environments. |
| 37 | + |
| 38 | +Prompt answers can be provided from multiple sources, resolved in the following |
| 39 | +priority order: |
| 40 | + |
| 41 | +1. **Configuration file** — a JSON file passed via the `--config` option. |
| 42 | +2. **Environment variables** — using the `VORTEX_INSTALLER_PROMPT_<handler_id>` |
| 43 | + naming convention. |
| 44 | +3. **Discovery** — auto-detection from existing project files (e.g., |
| 45 | + `composer.json`, `.env`, hosting-specific files like `.lagoon.yml`). |
| 46 | +4. **Handler defaults** — fallback values defined by each handler. |
| 47 | + |
| 48 | +This means the installer can be driven entirely by a config file or environment |
| 49 | +variables without any user interaction, which is essential for reproducible |
| 50 | +installations and automated testing. |
| 51 | + |
| 52 | +The core flow is: |
| 53 | + |
| 54 | +1. User downloads the installer PHAR from https://www.vortextemplate.com/install |
| 55 | + and runs it with `php installer.php`. |
| 56 | +2. `InstallCommand` orchestrates the installation process. |
| 57 | +3. `PromptManager` collects user choices via interactive prompts. |
| 58 | +4. Handlers queue file operations based on selections — each handler is |
| 59 | + responsible for a specific aspect of the template (CI provider, hosting, |
| 60 | + services, theme, deployment strategy, etc.). |
| 61 | +5. File operations execute: conditional tokens are processed to include or |
| 62 | + exclude content based on the selected features. |
| 63 | +6. The output is a fully customized Drupal project. |
| 64 | + |
| 65 | +### Conditional token system |
| 66 | + |
| 67 | +In addition to string substitution for handling simple replacements and |
| 68 | +additions, the installer uses a token system to conditionally exclude entire |
| 69 | +blocks of content from template files. This simplifies the management of |
| 70 | +optional features — rather than requiring complex logic to surgically remove |
| 71 | +lines from configuration files, scripts, or documentation, maintainers simply |
| 72 | +wrap the relevant content in token markers. When a feature is not selected |
| 73 | +during installation, the installer removes everything between the markers. |
| 74 | + |
| 75 | +**Markdown**: |
| 76 | + |
| 77 | +```markdown |
| 78 | +[//]: # (#;< TOKEN_NAME) |
| 79 | +Content removed if feature not selected |
| 80 | +[//]: # (#;> TOKEN_NAME) |
| 81 | +``` |
| 82 | + |
| 83 | +**Shell/YAML**: |
| 84 | + |
| 85 | +```bash |
| 86 | +#;< TOKEN_NAME |
| 87 | +Content removed if feature not selected |
| 88 | +#;> TOKEN_NAME |
| 89 | +``` |
| 90 | +
|
| 91 | +## Testing |
| 92 | +
|
| 93 | +The installer uses a multi-layered testing approach with unit tests for |
| 94 | +individual components and functional tests for full installation scenarios. |
| 95 | +
|
| 96 | +### Unit testing |
| 97 | +
|
| 98 | +Unit tests cover core utilities (`Yaml`, `File`, `Git`, `Validator`, |
| 99 | +`Strings`), downloaders, runners, and handler discovery logic. Each component |
| 100 | +is tested in isolation with mocks provided by |
| 101 | +[Mockery](https://github.com/mockery/mockery). |
| 102 | +
|
| 103 | +### Functional testing with snapshots |
| 104 | +
|
| 105 | +For every test permutation, the installer *initiates a fresh project* from the |
| 106 | +Vortex template with a specific combination of user selections and runs |
| 107 | +assertions against the resulting files. Because a single template change can |
| 108 | +affect a hundred plus installation permutations, snapshot testing makes it easy to |
| 109 | +**review the impact of a change across all scenarios as diffs** — ensuring that |
| 110 | +regressions are caught before they reach consumers. |
| 111 | +
|
| 112 | +Each handler has a dedicated functional test class extending |
| 113 | +`AbstractHandlerProcessTestCase`, covering every aspect of the installation |
| 114 | +process: CI providers, hosting providers, services, themes, deployment |
| 115 | +strategies, database sources, notification channels, and more. |
| 116 | +
|
| 117 | +These tests use the [`alexskrypnyk/snapshot`](https://github.com/AlexSkrypnyk/snapshot) |
| 118 | +library for snapshot-based testing. The snapshot system works on a |
| 119 | +**baseline + diff** pattern: |
| 120 | +
|
| 121 | +- `_baseline/` contains the *complete* reference installation — a full set of |
| 122 | + template files as they would appear after a default installation. |
| 123 | +- Each scenario directory (e.g., `services_no_clamav/`, `hosting_acquia/`, |
| 124 | + `deploy_types_all_circleci/`) contains only the *delta* changes from the |
| 125 | + baseline. |
| 126 | +
|
| 127 | +When a test runs, the snapshot library applies the scenario's diffs on top of |
| 128 | +the baseline and compares the result against the actual installer output. This |
| 129 | +approach keeps the fixture files maintainable — instead of duplicating the |
| 130 | +entire template for each scenario, only the differences are stored. |
| 131 | +
|
| 132 | +The fixture directories in `.vortex/installer/tests/Fixtures/handler_process/` |
| 133 | +cover scenarios like: |
| 134 | +
|
| 135 | +- **CI providers**: GitHub Actions, CircleCI |
| 136 | +- **Hosting**: Acquia, Lagoon |
| 137 | +- **Services**: Solr, Redis, ClamAV (enabled/disabled combinations) |
| 138 | +- **Deployment**: artifact, container image, webhook, Lagoon, all combined |
| 139 | +- **Database sources**: Acquia, Lagoon, FTP, S3, URL, container registry |
| 140 | +- And many more permutations |
| 141 | +
|
| 142 | +### Running tests |
| 143 | +
|
| 144 | +```shell |
| 145 | +cd .vortex/installer |
| 146 | +
|
| 147 | +# Install Composer dependencies. |
| 148 | +composer install |
| 149 | +
|
| 150 | +# Run all tests. |
| 151 | +composer test |
| 152 | +
|
| 153 | +# Run only handler functional tests. |
| 154 | +./vendor/bin/phpunit --filter "Handlers\\\\" |
| 155 | +
|
| 156 | +# Run a specific handler test. |
| 157 | +./vendor/bin/phpunit --filter "ServicesHandlerProcessTest" |
| 158 | +``` |
| 159 | +
|
| 160 | +### Updating snapshots |
| 161 | +
|
| 162 | +Fixture files should never be modified directly. When the template or installer |
| 163 | +logic changes, update the snapshots: |
| 164 | +
|
| 165 | +```shell |
| 166 | +# From .vortex/ directory (recommended). |
| 167 | +ahoy update-snapshots |
| 168 | +
|
| 169 | +# Manual (for debugging specific scenarios). |
| 170 | +cd .vortex/installer |
| 171 | +UPDATE_SNAPSHOTS=1 ./vendor/bin/phpunit --filter "testHandlerProcess.*baseline" |
| 172 | +``` |
| 173 | +
|
| 174 | +When `UPDATE_SNAPSHOTS` is set, the installer *runs for every permutation*, |
| 175 | +initiates a fresh project for each scenario, and automatically updates the |
| 176 | +fixture files to match the current output. The resulting changes appear as |
| 177 | +diffs in version control, making it straightforward to review exactly how a |
| 178 | +template or installer change affects each scenario. |
| 179 | +
|
| 180 | +## Releasing |
| 181 | +
|
| 182 | +The installer is packaged as a PHAR archive using |
| 183 | +[Box](https://github.com/box-project/box). It is released on each **GitHub |
| 184 | +release** of the Vortex template and deployed to |
| 185 | +https://www.vortextemplate.com/install. Additionally, the installer is |
| 186 | +published for every branch containing `release-docs` or `release-installer` |
| 187 | +in its name, which allows testing installer changes before a formal release. |
| 188 | +
|
| 189 | +## Installer video |
| 190 | +
|
| 191 | +The documentation includes an interactive recording of the installer captured |
| 192 | +with [asciinema](https://asciinema.org/). The recording is produced by a script |
| 193 | +at `.vortex/docs/.utils/update-installer-video.sh` which: |
| 194 | +
|
| 195 | +1. Builds the installer PHAR from source. |
| 196 | +2. Generates an [Expect](https://en.wikipedia.org/wiki/Expect) script that |
| 197 | + automates the installer interaction, simulating human-like typing with |
| 198 | + delays. |
| 199 | +3. Records the session with `asciinema rec` into a JSON cast file. |
| 200 | +4. Post-processes the recording (sanitizes paths, removes spawn lines). |
| 201 | +5. Converts the cast file to SVG and PNG using a custom |
| 202 | + `svg-term-render.js` script, and optionally to GIF using `agg`. |
| 203 | +
|
| 204 | +The output files are stored in `.vortex/docs/static/img/` and embedded in the |
| 205 | +documentation using a custom `AsciinemaPlayer` React component. |
| 206 | +
|
| 207 | +To update the video: |
| 208 | +
|
| 209 | +```shell |
| 210 | +cd .vortex |
| 211 | +ahoy update-installer-video |
| 212 | +``` |
0 commit comments