Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 8 additions & 8 deletions .vortex/docs/.utils/update-videos.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ function build_installer_expect_script(int|float $prompt_delay, string $uri): st
}

set env(VORTEX_INSTALLER_PROMPT_BUILD_NOW) 0
# The prompts below match Unicode symbols, so the installer output is decoded
# as UTF-8 whatever the locale.
encoding system utf-8
spawn php installer.php --destination=star_wars --uri=$installer_uri

expect {
Expand Down Expand Up @@ -240,22 +243,19 @@ function build_installer_expect_script(int|float $prompt_delay, string $uri): st
}
}

# A multiselect that scrolls prints its selection count in the bottom border,
# so the corner follows the count instead of a line.
# A prompt waiting for input starts with the active symbol, U+25C7, and an
# answered one with the completed symbol, so only a waiting prompt matches.
# A top-level prompt is numbered "(5/36)" and a nested one "(5.1)".
while {1} {
expect {
"Proceed with installing Vortex?" {
after 2000
safe_send "\r"
}
"Vortex will be installed into your project" {
-re {◇[^\r\n]*Proceed with installing Vortex} {
after 2000
safe_send "\r"
}
"Finished installing Vortex" {
break
}
-re {─┘| selected ┘} {
-re {◇[^\r\n]*\([0-9]+(\.[0-9]+)*(/[0-9]+)?\)} {
wait_and_enter
}
timeout {
Expand Down
1 change: 1 addition & 0 deletions .vortex/docs/.vale/config/vocabularies/Vortex/accept.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ PHPCS
PHPStan
PHPUnit
PhpStorm
Prompty
Pygmy
Rector
Renovate
Expand Down
16 changes: 12 additions & 4 deletions .vortex/docs/content/contributing/maintenance/installer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ together with the installer logic that handles it, so the 2 stay in step.
The installer is written in PHP because it touches many parts of the
template and has to manage every permutation of the template's variants.
PHP also gives it a terminal UI through
[Laravel Prompts](https://github.com/laravel/prompts), which asks the
[Prompty](https://github.com/AlexSkrypnyk/prompty), which asks the
questions 1 at a time. With `--no-interaction`, it asks nothing at all: it
installs and updates projects unattended, which suits automated pipelines
and CI environments.
Expand All @@ -52,10 +52,18 @@ value:
`composer.json`, `.env`, hosting-specific files like `.lagoon.yml`).
3. **Handler defaults** - fallback values defined by each handler.

An environment variable named `VORTEX_INSTALLER_PROMPT_` followed by the
prompt ID in capitals answers that prompt, so the installer doesn't ask it.
For example, `VORTEX_INSTALLER_PROMPT_HOSTING_PROVIDER=lagoon` answers the
hosting prompt. The variable wins over the 3 preceding sources. A prompt
with several choices reads a comma-separated list. A yes or no prompt reads
`1`, `true` and `yes` as yes, and `0`, `false` and `no` as no. An invalid
value stops the installer at that prompt.

The profile, theme and web root prompts go a step further. When discovery
finds a value, or an earlier answer settles it, the installer uses that
value without asking. For these 3 prompts, that value wins over
`--prompts`. Choosing Acquia
`--prompts` and the environment variable. Choosing Acquia
hosting, for example, settles the web root.

`--prompts` with `--no-interaction` runs a whole installation without a
Expand All @@ -77,8 +85,8 @@ keyed by their `VORTEX_INSTALLER_*` names, such as
`VORTEX_INSTALLER_TEMPLATE_REPO`. An environment variable with the same name
wins over the value in the JSON.

The final **Run the site build now?** question is the only question with
an environment variable of its own:
The final **Run the site build now?** question comes after the summary, and
its environment variable sets the question's default:

- `VORTEX_INSTALLER_PROMPT_BUILD_NOW` sets its default. `0` or an empty
value defaults to no. Any other value, or an unset variable, defaults to
Expand Down
1 change: 1 addition & 0 deletions .vortex/docs/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"phpstorm",
"postbuild",
"preboot",
"prompty",
"pyrech",
"redis",
"renovatebot",
Expand Down
412 changes: 193 additions & 219 deletions .vortex/docs/static/img/installer.json

Large diffs are not rendered by default.

Binary file modified .vortex/docs/static/img/installer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion .vortex/docs/static/img/installer.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
109 changes: 10 additions & 99 deletions .vortex/installer/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ permission before running.
Triggers that require re-recording:
- New `Handlers/*.php` class or handler removal.
- Wording change to `label()` or `hint()` of any existing handler.
- Adding, removing or reordering prompts inside
`PromptManager::runPrompts()`, which also moves the progress denominator.
- Adding, removing or reordering prompts in the `PromptManager::runPrompts()`
chain, which also changes the step count in each label.

## Conditional Token System

Expand Down Expand Up @@ -133,9 +133,14 @@ Content removed if feature not selected

### Ordering

`PromptManager::runPrompts()` is the catalogue of prompts: the `form()`
chain lists every prompt, in order, under its section heading. Add a new
prompt there, and the progress denominator follows automatically.
`PromptManager::runPrompts()` declares the whole prompt flow as 1
`Prompty::flow()` chain: each `->section()` heading, then its handlers in
order with `->add()`. A prompt whose `shouldRun()` reads another prompt's
answer goes between `->children()` and `->end()` under that prompt, so it is
asked as a nested step. The chain holds structure only: each handler is a
Prompty step, so its `shouldRun()` decides whether it is asked and its
label, options and default decide what it shows. Add a new prompt to the
chain, and the step count follows automatically.

Processing runs in a different order, so each handler declares its own
`processWeight()` - lowest first, broadly the reverse of the prompt order so
Expand Down Expand Up @@ -174,97 +179,3 @@ Each handler has dedicated test class extending

Structure: each `dataProvider*` sits immediately after the test it feeds;
helper methods go last.

## Patches

The installer applies a single patch to `laravel/prompts` via
`cweagans/composer-patches` v2 to add three behaviors it depends on.

### Patch location

- File: `patches/laravel-prompts.patch`
- Registered in: `composer.json` under `extra.patches`
- Lockfile: `patches.lock.json` (regenerated by composer-patches; do not
hand-edit)

### What the patch adds

1. **`description` field on every Prompt class and renderer**. A new
`RendersDescription` trait under
`vendor/laravel/prompts/src/Themes/Default/Concerns/` renders a
description block inside the prompt frame. `PromptManager::runPrompts()`
threads `$handler->description($responses)` into `$args['description']`
for every handler that returns a non-null description.
2. **`Prompt::validateUsing(?Closure $callback)` accepts null**, so
`tests/Traits/TuiTrait.php::tuiTeardown()` can clear the validator
between tests.
3. **The static `validateUsing` callback runs before the instance
`validate` closure and receives `$value`**. `TuiTrait::tuiSetUp()` uses
this to intercept every prompt's validation and throw `RuntimeException`
on failure, so non-interactive tests fail fast instead of hanging
waiting for user input.

The patch also includes a small `Concerns/TypedValue.php` tweak
(`strlen($default) > 0` → truthy check); not exercised directly by the
installer.

### Re-roll procedure

Whenever the `laravel/prompts` version in `composer.json` is bumped
(Renovate PR or manual update), the patch must be re-rolled because the
description trait touches renderers that frequently change between
releases.

```bash
# 1. After bumping the version in .vortex/installer/composer.json, try the
# existing patch first.
composer --working-dir .vortex/installer update laravel/prompts --with-dependencies
rm -f ~/Library/Caches/composer/patches/*.patch # macOS cache path
rm -f ~/.cache/composer/patches/*.patch # Linux cache path
composer --working-dir .vortex/installer patches-relock
composer --working-dir .vortex/installer patches-repatch
```

If `patches-repatch` succeeds, jump to the verify step.

If it fails (`No available patcher was able to apply patch`), the patch
conflicts with the new upstream and needs editing:

```bash
# 2. Extract the new upstream source for editing.
git clone https://github.com/laravel/prompts.git /tmp/prompts-upstream
git -C /tmp/prompts-upstream checkout v<NEW_TAG>
cp -R /tmp/prompts-upstream/src /tmp/upstream-pristine-src

# 3. Apply the three changes to /tmp/prompts-upstream/src using
# .vortex/installer/patches/laravel-prompts.patch as reference:
# a) Re-introduce the RendersDescription trait and thread `description`
# through every Prompt constructor + matching renderer.
# b) Update Prompt::validateUsing() signature to `?Closure $callback`.
# c) Swap the match arms in Prompt::validate() so the static validator
# runs first and receives $value as the second argument.

# 4. Regenerate the unified diff against the pristine upstream copy.
diff -ruN /tmp/upstream-pristine-src /tmp/prompts-upstream/src > /tmp/raw.patch

# 5. Re-root paths to `a/src/...` / `b/src/...` and strip timestamps using
# the committed helper.
php .vortex/installer/patches/reroot-patch.php \
/tmp/raw.patch \
.vortex/installer/patches/laravel-prompts.patch \
/tmp/upstream-pristine-src/ \
/tmp/prompts-upstream/src/

# 6. Refresh the lockfile and re-apply.
rm -f ~/Library/Caches/composer/patches/*.patch
rm -f ~/.cache/composer/patches/*.patch
composer --working-dir .vortex/installer patches-relock
composer --working-dir .vortex/installer patches-repatch
```

Verify:

```bash
composer --working-dir .vortex/installer test
composer --working-dir .vortex/installer lint
```
19 changes: 8 additions & 11 deletions .vortex/installer/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@
"require": {
"php": ">=8.3",
"alexskrypnyk/file": "^1.3.0",
"alexskrypnyk/prompty": "dev-main",
"alexskrypnyk/str2name": "^1.7.0",
"composer/composer": "^2.11.1",
"cweagans/composer-patches": "^2",
"czproject/git-php": "^4.6",
"guzzlehttp/guzzle": "^8.3.1",
"laravel/prompts": "^0.3.27",
"nelexa/zip": "^4.1.0",
"nikic/iter": "^2.4.1",
"sebastian/diff": "^7.0.1",
Expand All @@ -41,12 +40,18 @@
"drupal/coder": "^9.0.1@alpha",
"ergebnis/composer-normalize": "^2.56.0",
"laravel/serializable-closure": "^2.1.0",
"mockery/mockery": "^1.6.15",
"phpstan/phpstan": "^2.3.5",
"phpunit/phpunit": "^12.5.43",
"rector/rector": "^2.8.4",
"symfony/finder": "^7.4.23"
},
"repositories": [
{
"name": "prompty",
"type": "vcs",
"url": "https://github.com/AlexSkrypnyk/prompty"
}
],
"autoload": {
"psr-4": {
"DrevOps\\VortexInstaller\\": "src/"
Expand All @@ -66,21 +71,13 @@
"config": {
"allow-plugins": {
"bamarni/composer-bin-plugin": true,
"cweagans/composer-patches": true,
"dealerdirect/phpcodesniffer-composer-installer": true,
"ergebnis/composer-normalize": true
},
"bump-after-update": true,
"process-timeout": 0,
"sort-packages": true
},
"extra": {
"patches": {
"laravel/prompts": {
"Description support, nullable validateUsing, validator precedence, TypedValue truthy check": "patches/laravel-prompts.patch"
}
}
},
"scripts": {
"build": [
"@composer bin box require --dev humbug/box",
Expand Down
Loading
Loading