From 6697088ab704c798b65c87713a3856aac85b4e4e Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 27 Apr 2026 09:27:48 +1000 Subject: [PATCH 1/9] Allow Codecov to fail in CI without failing the pipeline. --- .github/workflows/build-test-deploy.yml | 1 + .github/workflows/vortex-test-common.yml | 2 ++ .github/workflows/vortex-test-docs.yml | 1 + .github/workflows/vortex-test-installer.yml | 1 + .../.github/workflows/build-test-deploy.yml | 3 ++- 5 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-test-deploy.yml b/.github/workflows/build-test-deploy.yml index 61cce049e..c7b564d69 100644 --- a/.github/workflows/build-test-deploy.yml +++ b/.github/workflows/build-test-deploy.yml @@ -488,6 +488,7 @@ jobs: - name: Upload coverage report to Codecov uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6 if: ${{ (matrix.instance == 0 || strategy.job-total == 1) && env.CODECOV_TOKEN != '' }} + continue-on-error: true with: directory: .logs/coverage fail_ci_if_error: true diff --git a/.github/workflows/vortex-test-common.yml b/.github/workflows/vortex-test-common.yml index ee7c54ae5..747012d78 100644 --- a/.github/workflows/vortex-test-common.yml +++ b/.github/workflows/vortex-test-common.yml @@ -110,6 +110,7 @@ jobs: - name: Upload coverage report to Codecov uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6 + continue-on-error: true with: directory: /tmp/.vortex-coverage-html fail_ci_if_error: false @@ -216,6 +217,7 @@ jobs: - name: Upload coverage report to Codecov uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6 + continue-on-error: true with: directory: /tmp/.vortex-coverage-html fail_ci_if_error: false diff --git a/.github/workflows/vortex-test-docs.yml b/.github/workflows/vortex-test-docs.yml index b8d4c7ff3..627f4a40c 100644 --- a/.github/workflows/vortex-test-docs.yml +++ b/.github/workflows/vortex-test-docs.yml @@ -108,6 +108,7 @@ jobs: - name: Upload coverage report to Codecov uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6 + continue-on-error: true with: files: .vortex/docs/.logs/cobertura.xml fail_ci_if_error: true diff --git a/.github/workflows/vortex-test-installer.yml b/.github/workflows/vortex-test-installer.yml index 5d9dd2c4b..118b217e1 100644 --- a/.github/workflows/vortex-test-installer.yml +++ b/.github/workflows/vortex-test-installer.yml @@ -80,6 +80,7 @@ jobs: - name: Upload coverage report to Codecov uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6 + continue-on-error: true with: files: .vortex/installer/.logs/cobertura.xml fail_ci_if_error: true diff --git a/.vortex/installer/tests/Fixtures/handler_process/code_coverage_provider_codecov/.github/workflows/build-test-deploy.yml b/.vortex/installer/tests/Fixtures/handler_process/code_coverage_provider_codecov/.github/workflows/build-test-deploy.yml index e6e1b1232..84ebf615d 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/code_coverage_provider_codecov/.github/workflows/build-test-deploy.yml +++ b/.vortex/installer/tests/Fixtures/handler_process/code_coverage_provider_codecov/.github/workflows/build-test-deploy.yml @@ -1,10 +1,11 @@ -@@ -432,6 +432,16 @@ +@@ -432,6 +432,17 @@ hide_and_recreate: true + - name: Upload coverage report to Codecov + uses: codecov/codecov-action@__HASH__ # __VERSION__ + if: ${{ (matrix.instance == 0 || strategy.job-total == 1) && env.CODECOV_TOKEN != '' }} ++ continue-on-error: true + with: + directory: .logs/coverage + fail_ci_if_error: true From 3aa6b019f47a7ca629d1cfdf73f9b0f7d501a124 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 27 Apr 2026 11:44:47 +1000 Subject: [PATCH 2/9] Added update script for test DB. --- .../contributing/maintenance/template.mdx | 202 ++-------- .vortex/tests/update-test-assets | 349 ++++++++++++++++++ 2 files changed, 385 insertions(+), 166 deletions(-) create mode 100755 .vortex/tests/update-test-assets diff --git a/.vortex/docs/content/contributing/maintenance/template.mdx b/.vortex/docs/content/contributing/maintenance/template.mdx index be25714e1..b2bf7b2c5 100644 --- a/.vortex/docs/content/contributing/maintenance/template.mdx +++ b/.vortex/docs/content/contributing/maintenance/template.mdx @@ -234,188 +234,58 @@ bats .vortex/tests/bats/deploy.bats ## Updating test assets -There are *demo* and *test* database dumps captured as *files* and *container images*. +There are *demo* and *test* database dumps captured as *files* and *container +images*. All flows are automated by `.vortex/tests/update-test-assets`, which +prints every shell command it runs. -### Updating *demo* database dump *file* - -
-Show instructions - -1. Run fresh build of **Vortex** locally: -```shell -rm .data/db.sql || true -VORTEX_PROVISION_TYPE=profile VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 AHOY_CONFIRM_RESPONSE=1 ahoy build -``` -2. Update content and config: -```shell -ahoy cli - -drush eval "Drupal::entityTypeManager()->getStorage('node')->create([ - 'type' => 'page', - 'title' => 'Welcome to the demo site!', - 'body' => [ - 'value' => '

This demo page is sourced from the Vortex database dump file to demonstrate database importing capabilities.

', - 'format' => 'basic_html', - ], -])->save();" - -drush config:set system.site page.front "/node/1" -y -drush sql:query "SHOW TABLES LIKE 'cache_%'" | xargs -I{} drush sql:query "TRUNCATE TABLE {}" && drush sql:query "TRUNCATE TABLE watchdog" - -exit - -``` -3. Export DB: -```shell -ahoy export-db db.demo.sql -``` -4. Upload `db.demo.sql` to the latest release as an asset and name it `db_d11.demo.sql`. - -
- -### Updating *demo* database *container image* - -
-Show instructions - -1. Run fresh build of **Vortex** locally: ```shell -rm .data/db.sql || true -VORTEX_PROVISION_TYPE=profile VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 AHOY_CONFIRM_RESPONSE=1 ahoy build +php .vortex/tests/update-test-assets [mode] [--tag ] ``` -2. Update content and config: -```shell -ahoy cli -drush eval "Drupal::entityTypeManager()->getStorage('node')->create([ - 'type' => 'page', - 'title' => 'Welcome to the demo site!', - 'body' => [ - 'value' => '

This demo page is sourced from the Vortex database container image to demonstrate database importing capabilities.

', - 'format' => 'basic_html', - ], -])->save();" +Without arguments, runs `all` for a full refresh. Default tag is `latest`. -drush config:set system.site page.front "/node/1" -y -drush sql:query "SHOW TABLES LIKE 'cache_%'" | xargs -I{} drush sql:query "TRUNCATE TABLE {}" && drush sql:query "TRUNCATE TABLE watchdog" +### Modes -exit +| Mode | What it does | Follow-up | +| --- | --- | --- | +| `demo-dump` | Builds the demo profile in-place; exports `.data/db.demo.sql`. | Upload as `db_d11.demo.sql` to the latest GitHub release. | +| `demo-image` | Builds the demo profile in-place; pushes `drevops/vortex-dev-mariadb-drupal-data-demo-11.x:`. | None. | +| `test-dump` | Installs Vortex into `/tmp/star-wars`; builds; exports `/tmp/star-wars/.data/db.test.sql`. | Upload as `db_d11.test.sql` to the latest GitHub release. | +| `test-image` | Installs Vortex into `/tmp/star-wars`; builds; pushes `drevops/vortex-dev-mariadb-drupal-data-test-11.x:`. | None. | +| `destination-images` | Tags and pushes the local demo image to the didi destination tags (`vortex-dev-database-ii`, `vortex-dev-didi-database-fi`). | None. | +| `all` | Runs every mode above in order. Default when no mode is given. | Upload both dump files as above. | -``` -3. Export DB: -```shell -ahoy export-db db.demo_image.sql +### Options -# Update the collation to avoid issues with MariaDB 10.5+: -sed -i '' 's/utf8mb4_0900_ai_ci/utf8mb4_general_ci/g' .data/db.demo_image.sql -``` -4. Seed the database container image: -```shell -curl -LO https://github.com/drevops/mariadb-drupal-data/releases/latest/download/seed.sh -chmod +x seed.sh -./seed.sh .data/db.demo_image.sql drevops/vortex-dev-mariadb-drupal-data-demo-11.x:latest -``` - -
- -### Updating *test* database dump *file* - -
-Show instructions +| Option | Description | +| --- | --- | +| `--tag ` | Image tag for image modes. Default: `latest`. Also accepts `--tag=`. | -1. Run a fresh install of **Vortex** into a new directory and name the project `Star Wars`: -```shell -mkdir /tmp/star-wars -VORTEX_INSTALLER_TEMPLATE_REPO="$(pwd)" .vortex/installer/installer.php /tmp/star-wars --no-interaction -cd /tmp/star-wars -``` -2. Run fresh build of **Vortex** locally: -```shell -rm .data/db.sql || true -VORTEX_PROVISION_TYPE=profile AHOY_CONFIRM_RESPONSE=1 ahoy build -``` -3. Update content and config: -```shell -ahoy cli - -drush eval "Drupal::entityTypeManager()->getStorage('node')->create([ - 'type' => 'page', - 'title' => 'Welcome to the test site!', - 'body' => [ - 'value' => '

This test page is sourced from the Vortex database dump file to demonstrate database importing capabilities.

', - 'format' => 'basic_html', - ], -])->save();" - -drush config:set system.site page.front "/node/1" -y -drush sql:query "SHOW TABLES LIKE 'cache_%'" | xargs -I{} drush sql:query "TRUNCATE TABLE {}" && drush sql:query "TRUNCATE TABLE watchdog" - -exit +### Requirements -``` -4. Export DB: -```shell -ahoy export-db db.test.sql -``` -5. Upload `db.test.sql` to the latest release as an asset and name it `db_d11.test.sql`. +- `docker`, `ahoy`, `curl`, `sed`, `php` available on `PATH`. +- A Docker Hub session with push permission to `drevops/` for any image mode. -
+### Caveats -### Updating *test* database *container image* +- Demo modes are **destructive** against the current repository: containers + are reset and `.data/db.sql` is removed before the rebuild. +- Test modes wipe and recreate `/tmp/star-wars` on each run. +- `destination-images` expects `drevops/vortex-dev-mariadb-drupal-data-demo-11.x:latest` + to already exist locally - run `demo-image` (or `all`) first. -
-Show instructions +### Examples -1. Run a fresh install of **Vortex** into a new directory and name the project `Star Wars`: ```shell -mkdir /tmp/star-wars -VORTEX_INSTALLER_TEMPLATE_REPO="$(pwd)" .vortex/installer/installer.php /tmp/star-wars --no-interaction -cd /tmp/star-wars -``` -2. Run fresh build of **Vortex** locally: -```shell -rm .data/db.sql || true -VORTEX_PROVISION_TYPE=profile AHOY_CONFIRM_RESPONSE=1 ahoy build -``` -3. Update content and config: -```shell -ahoy cli - -drush eval "Drupal::entityTypeManager()->getStorage('node')->create([ - 'type' => 'page', - 'title' => 'Welcome to the test site!', - 'body' => [ - 'value' => '

This test page is sourced from the Vortex database container image to demonstrate database importing capabilities.

', - 'format' => 'basic_html', - ], -])->save();" +# Full refresh of every asset at :latest (default). +php .vortex/tests/update-test-assets -drush config:set system.site page.front "/node/1" -y -drush sql:query "SHOW TABLES LIKE 'cache_%'" | xargs -I{} drush sql:query "TRUNCATE TABLE {}" && drush sql:query "TRUNCATE TABLE watchdog" - -exit - -``` -4. Export DB: -```shell -ahoy export-db db.test_image.sql +# Full refresh, tagging images as :rc1. +php .vortex/tests/update-test-assets --tag rc1 -# Update the collation to avoid issues with MariaDB 10.5+: -sed -i '' 's/utf8mb4_0900_ai_ci/utf8mb4_general_ci/g' .data/db.test_image.sql -``` -5. Seed the database container image: -```shell -curl -LO https://github.com/drevops/mariadb-drupal-data/releases/latest/download/seed.sh -chmod +x seed.sh -./seed.sh .data/db.test_image.sql drevops/vortex-dev-mariadb-drupal-data-test-11.x:latest -``` -6. Update destination container images: -```shell -docker tag drevops/vortex-dev-mariadb-drupal-data-demo-11.x:latest drevops/vortex-dev-mariadb-drupal-data-demo-destination-11.x:vortex-dev-database-ii -docker push drevops/vortex-dev-mariadb-drupal-data-demo-destination-11.x:vortex-dev-database-ii +# Refresh only the test container image at :latest. +php .vortex/tests/update-test-assets test-image -docker tag drevops/vortex-dev-mariadb-drupal-data-demo-11.x:latest drevops/vortex-dev-mariadb-drupal-data-demo-destination-11.x:vortex-dev-didi-database-fi -docker push drevops/vortex-dev-mariadb-drupal-data-demo-destination-11.x:vortex-dev-didi-database-fi +# Refresh only the test container image at :rc1. +php .vortex/tests/update-test-assets test-image --tag rc1 ``` - -
diff --git a/.vortex/tests/update-test-assets b/.vortex/tests/update-test-assets new file mode 100755 index 000000000..b640122df --- /dev/null +++ b/.vortex/tests/update-test-assets @@ -0,0 +1,349 @@ +#!/usr/bin/env php +] + * + * Without arguments, runs the 'all' mode (full refresh). + * + * Modes: + * demo-dump Build demo in-place; export .data/db.demo.sql. + * demo-image Build demo in-place; push + * drevops/vortex-dev-mariadb-drupal-data-demo-11.x:. + * test-dump Install Vortex into /tmp/star-wars; export + * .data/db.test.sql there. + * test-image Install Vortex into /tmp/star-wars; push + * drevops/vortex-dev-mariadb-drupal-data-test-11.x:. + * destination-images Tag and push the local demo image to didi destination + * tags (vortex-dev-database-ii, vortex-dev-didi-database-fi). + * all Run all of the above in order (default). + * + * Options: + * --tag Image tag for image modes (default: 'latest'). + * Also accepts '--tag='. + * + * Demo modes run in-place against the current repository and are destructive: + * they wipe containers and remove '.data/db.sql' before rebuilding. Test modes + * nuke and recreate '/tmp/star-wars'. Image modes require a Docker Hub session + * with push permission to 'drevops/'. + */ + +const SUT_DIR = '/tmp/star-wars'; +const SEED_URL = 'https://github.com/drevops/mariadb-drupal-data/releases/latest/download/seed.sh'; +const IMAGE_DEMO = 'drevops/vortex-dev-mariadb-drupal-data-demo-11.x'; +const IMAGE_TEST = 'drevops/vortex-dev-mariadb-drupal-data-test-11.x'; +const IMAGE_DEST = 'drevops/vortex-dev-mariadb-drupal-data-demo-destination-11.x'; + +$valid_modes = ['demo-dump', 'demo-image', 'test-dump', 'test-image', 'destination-images', 'all']; +[$mode, $tag] = parse_args($argv ?? [], $valid_modes); + +$root_dir = dirname(__DIR__, 2); +$installer = $root_dir . '/.vortex/installer/installer.php'; + +step(sprintf('Mode: %s', $mode)); +step(sprintf('Tag: %s', $tag)); +step(sprintf('Source repo: %s', $root_dir)); + +step('Validate environment'); +require_command('docker'); +require_command('ahoy'); +require_command('curl'); +require_command('sed'); +require_command('php'); + +if ($mode === 'all') { + run_mode('demo-dump', $root_dir, $installer, $tag); + run_mode('demo-image', $root_dir, $installer, $tag); + run_mode('test-dump', $root_dir, $installer, $tag); + run_mode('test-image', $root_dir, $installer, $tag); + run_mode('destination-images', $root_dir, $installer, $tag); +} +else { + run_mode($mode, $root_dir, $installer, $tag); +} + +step('Done'); + +// ============================================================================= +// Mode dispatcher. +// ============================================================================= + +function run_mode(string $mode, string $root_dir, string $installer, string $tag): void { + step(sprintf('--- Running mode: %s ---', $mode)); + + match ($mode) { + 'demo-dump' => mode_demo_dump($root_dir), + 'demo-image' => mode_demo_image($root_dir, $tag), + 'test-dump' => mode_test_dump($root_dir, $installer), + 'test-image' => mode_test_image($root_dir, $installer, $tag), + 'destination-images' => mode_destination_images(), + }; +} + +// ============================================================================= +// Modes. +// ============================================================================= + +function mode_demo_dump(string $root_dir): void { + build_demo_in_place($root_dir); + seed_node($root_dir, 'Welcome to the demo site!', 'This demo page is sourced from the Vortex database dump file to demonstrate database importing capabilities.'); + finalise_content($root_dir); + export_db($root_dir, 'db.demo.sql'); + + step('Manual follow-up'); + println(sprintf(' Upload %s/.data/db.demo.sql to the latest GitHub release as asset "db_d11.demo.sql".', $root_dir)); +} + +function mode_demo_image(string $root_dir, string $tag): void { + $image = sprintf('%s:%s', IMAGE_DEMO, $tag); + + build_demo_in_place($root_dir); + seed_node($root_dir, 'Welcome to the demo site!', 'This demo page is sourced from the Vortex database container image to demonstrate database importing capabilities.'); + finalise_content($root_dir); + export_db($root_dir, 'db.demo_image.sql'); + fix_collation($root_dir, 'db.demo_image.sql'); + seed_image($root_dir, 'db.demo_image.sql', $image); +} + +function mode_test_dump(string $root_dir, string $installer): void { + reset_sut($root_dir, $installer); + build_in($root_dir, SUT_DIR, FALSE); + seed_node(SUT_DIR, 'Welcome to the test site!', 'This test page is sourced from the Vortex database dump file to demonstrate database importing capabilities.'); + finalise_content(SUT_DIR); + export_db(SUT_DIR, 'db.test.sql'); + + step('Manual follow-up'); + println(sprintf(' Upload %s/.data/db.test.sql to the latest GitHub release as asset "db_d11.test.sql".', SUT_DIR)); +} + +function mode_test_image(string $root_dir, string $installer, string $tag): void { + $image = sprintf('%s:%s', IMAGE_TEST, $tag); + + reset_sut($root_dir, $installer); + build_in($root_dir, SUT_DIR, FALSE); + seed_node(SUT_DIR, 'Welcome to the test site!', 'This test page is sourced from the Vortex database container image to demonstrate database importing capabilities.'); + finalise_content(SUT_DIR); + export_db(SUT_DIR, 'db.test_image.sql'); + fix_collation(SUT_DIR, 'db.test_image.sql'); + seed_image(SUT_DIR, 'db.test_image.sql', $image); +} + +function mode_destination_images(): void { + step('Tag and push destination images from local demo image'); + $source = sprintf('%s:latest', IMAGE_DEMO); + + foreach (['vortex-dev-database-ii', 'vortex-dev-didi-database-fi'] as $dest_tag) { + $dest = sprintf('%s:%s', IMAGE_DEST, $dest_tag); + run(sprintf('docker tag %s %s', escapeshellarg($source), escapeshellarg($dest))); + run(sprintf('docker push %s', escapeshellarg($dest))); + } +} + +// ============================================================================= +// Shared subroutines. +// ============================================================================= + +function reset_sut(string $root_dir, string $installer): void { + step(sprintf('Reset SUT directory %s', SUT_DIR)); + run(sprintf('rm -rf %s', escapeshellarg(SUT_DIR))); + run(sprintf('mkdir -p %s', escapeshellarg(SUT_DIR))); + + step('Install Vortex into SUT'); + run(sprintf( + 'VORTEX_INSTALLER_TEMPLATE_REPO=%s php %s %s --no-interaction', + escapeshellarg($root_dir), + escapeshellarg($installer), + escapeshellarg(SUT_DIR) + ), $root_dir); +} + +function build_demo_in_place(string $root_dir): void { + build_in($root_dir, $root_dir, TRUE); +} + +function build_in(string $root_dir, string $cwd, bool $skip_post_ops): void { + step('Remove any pre-existing DB dump'); + run('rm -f .data/db.sql', $cwd); + + step('Build stack from profile'); + $env = $skip_post_ops + ? 'VORTEX_PROVISION_TYPE=profile VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 AHOY_CONFIRM_RESPONSE=1' + : 'VORTEX_PROVISION_TYPE=profile AHOY_CONFIRM_RESPONSE=1'; + run(sprintf('%s ahoy build', $env), $cwd); +} + +function seed_node(string $cwd, string $title, string $body): void { + step(sprintf('Seed test page node ("%s")', $title)); + + $php = build_seed_php($title, $body); + $drush_cmd = sprintf('drush php:eval %s', escapeshellarg($php)); + $ahoy_cmd = sprintf('ahoy cli %s', escapeshellarg($drush_cmd)); + + run($ahoy_cmd, $cwd); +} + +function finalise_content(string $cwd): void { + step('Set page.front to /node/1'); + run('ahoy drush config:set system.site page.front /node/1 -y', $cwd); + + step('Truncate cache_* tables and watchdog'); + run("ahoy cli 'drush sql:query \"SHOW TABLES LIKE \\\"cache_%\\\"\" | xargs -I{} drush sql:query \"TRUNCATE TABLE {}\" && drush sql:query \"TRUNCATE TABLE watchdog\"'", $cwd); +} + +function export_db(string $cwd, string $filename): void { + step(sprintf('Export database to .data/%s', $filename)); + run(sprintf('ahoy export-db %s', escapeshellarg($filename)), $cwd); +} + +function fix_collation(string $cwd, string $filename): void { + step('Normalise collation (utf8mb4_0900_ai_ci -> utf8mb4_general_ci)'); + $path = sprintf('.data/%s', $filename); + run(sprintf("sed -i.bak 's/utf8mb4_0900_ai_ci/utf8mb4_general_ci/g' %s", escapeshellarg($path)), $cwd); + run(sprintf('rm -f %s.bak', escapeshellarg($path)), $cwd); +} + +function seed_image(string $cwd, string $filename, string $image): void { + step('Download seed.sh'); + run(sprintf('curl -fsSL -o seed.sh %s', escapeshellarg(SEED_URL)), $cwd); + run('chmod +x seed.sh', $cwd); + + step(sprintf('Seed and push image to %s', $image)); + run(sprintf('./seed.sh .data/%s %s', escapeshellarg($filename), escapeshellarg($image)), $cwd); +} + +// ============================================================================= +// Helpers. +// ============================================================================= + +function parse_args(array $argv, array $valid_modes): array { + $mode = NULL; + $tag = 'latest'; + $script = $argv[0] ?? __FILE__; + + $args = array_slice($argv, 1); + $count = count($args); + $i = 0; + + while ($i < $count) { + $arg = $args[$i]; + + if ($arg === '--tag') { + if (!isset($args[$i + 1])) { + usage_error($script, $valid_modes, "Option '--tag' requires a value."); + } + $tag = $args[$i + 1]; + $i += 2; + continue; + } + + if (str_starts_with($arg, '--tag=')) { + $tag = substr($arg, strlen('--tag=')); + if ($tag === '') { + usage_error($script, $valid_modes, "Option '--tag' requires a value."); + } + $i += 1; + continue; + } + + if ($arg === '-h' || $arg === '--help') { + usage_error($script, $valid_modes, NULL, 0); + } + + if (str_starts_with($arg, '-')) { + usage_error($script, $valid_modes, sprintf("Unknown option '%s'.", $arg)); + } + + if ($mode !== NULL) { + usage_error($script, $valid_modes, sprintf("Unexpected positional argument '%s'.", $arg)); + } + + $mode = $arg; + $i += 1; + } + + $mode = $mode ?? 'all'; + + if (!in_array($mode, $valid_modes, TRUE)) { + usage_error($script, $valid_modes, sprintf("Invalid mode '%s'.", $mode)); + } + + return [$mode, $tag]; +} + +function usage_error(string $script, array $valid_modes, ?string $message, int $code = 1): void { + $stream = $code === 0 ? STDOUT : STDERR; + + if ($message !== NULL) { + fwrite($stream, sprintf("Error: %s\n\n", $message)); + } + + fwrite($stream, sprintf("Usage:\n php %s [mode] [--tag ]\n\n", $script)); + fwrite($stream, sprintf("Modes: %s\n", implode(', ', $valid_modes))); + fwrite($stream, "Default mode: all. Default tag: latest.\n"); + + exit($code); +} + +function step(string $message): void { + fwrite(STDOUT, "\n"); + fwrite(STDOUT, sprintf("==> %s\n", $message)); +} + +function println(string $message): void { + fwrite(STDOUT, sprintf("%s\n", $message)); +} + +function require_command(string $cmd): void { + $output = []; + $code = 0; + exec(sprintf('command -v %s 2>/dev/null', escapeshellarg($cmd)), $output, $code); + + if ($code !== 0) { + fwrite(STDERR, sprintf("Error: required command '%s' not found in PATH.\n", $cmd)); + exit(1); + } + + println(sprintf(' - %s: %s', $cmd, $output[0] ?? '(found)')); +} + +function run(string $command, ?string $cwd = NULL): void { + if ($cwd !== NULL) { + println(sprintf(' $ (cd %s) %s', $cwd, $command)); + $command = sprintf('cd %s && %s', escapeshellarg($cwd), $command); + } + else { + println(sprintf(' $ %s', $command)); + } + + passthru($command, $code); + + if ($code !== 0) { + fwrite(STDERR, sprintf("\nError: command failed with exit code %d.\n", $code)); + exit($code); + } +} + +function build_seed_php(string $title, string $body): string { + // Escape characters that have special meaning inside PHP double-quoted + // strings ('"', '\', '$') so the inline 'drush php:eval' payload stays + // intact through host shell, ahoy and container bash. + $title_escaped = addcslashes($title, '"\\$'); + $body_escaped = addcslashes($body, '"\\$'); + + return implode(' ', [ + '$node = \Drupal::entityTypeManager()->getStorage("node")->create([', + sprintf('"type" => "page", "title" => "%s",', $title_escaped), + sprintf('"body" => ["value" => "

%s

", "format" => "basic_html"]', $body_escaped), + ']);', + '$node->save();', + 'echo "Created node " . $node->id() . PHP_EOL;', + ]); +} From 64a7da4b0db4414169d954b45fb018d2b2159dc8 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 27 Apr 2026 15:43:46 +1000 Subject: [PATCH 3/9] Updated test DB assests. --- .env | 4 +- .../contributing/maintenance/template.mdx | 6 +- .../Fixtures/handler_process/_baseline/.env | 2 +- .../db_download_source_acquia/.env | 2 +- .../.env | 2 +- .../db_download_source_ftp/.env | 2 +- .../db_download_source_lagoon/.env | 2 +- .../db_download_source_s3/.env | 2 +- .../handler_process/hosting_acquia/.env | 2 +- .../handler_process/hosting_lagoon/.env | 2 +- .../hosting_project_name___acquia/.env | 2 +- .../hosting_project_name___lagoon/.env | 2 +- .../migration_disabled_lagoon/.env | 2 +- .../migration_download_source_url/.env | 2 +- .../handler_process/migration_enabled/.env | 2 +- .../migration_enabled_circleci/.env | 2 +- .../provision_database_lagoon/.env | 2 +- .../handler_process/provision_profile/.env | 2 +- .../starter_drupal_cms_profile/.env | 2 +- .../starter_drupal_profile/.env | 2 +- .../theme_custom_non_vortex/.env | 2 +- .vortex/tests/bats/unit/provision.bats | 8 +- .../phpunit/Functional/AhoyWorkflowTest.php | 2 +- .vortex/tests/phpunit/Traits/SutTrait.php | 2 +- .vortex/tests/update-test-assets | 290 +++++++++++++++--- 25 files changed, 278 insertions(+), 72 deletions(-) diff --git a/.env b/.env index 559cad584..26d3f6eed 100644 --- a/.env +++ b/.env @@ -374,7 +374,7 @@ VORTEX_NOTIFY_WEBHOOK_URL= #;< DB_DOWNLOAD_SOURCE_URL # URL of the database used for demonstration with URL database download type. -VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/25.4.0/db_d11.demo.sql +VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/1.37.0/db.demo.sql #;> DB_DOWNLOAD_SOURCE_URL #;< MIGRATION @@ -390,6 +390,6 @@ VORTEX_DOWNLOAD_DB2_URL=https://github.com/drevops/vortex/releases/download/25.4 # The line below will be automatically uncommented for database-in-image # storage. It is commented out to allow running non-database-in-image # workflow by default. -##### VORTEX_DB_IMAGE=drevops/vortex-dev-mariadb-drupal-data-demo-11.x:latest +##### VORTEX_DB_IMAGE=drevops/vortex-dev-mariadb-drupal-data-demo-11.x:1.38.0-rc1 #;> DB_DOWNLOAD_SOURCE_CONTAINER_REGISTRY #;> DEMO_MODE diff --git a/.vortex/docs/content/contributing/maintenance/template.mdx b/.vortex/docs/content/contributing/maintenance/template.mdx index b2bf7b2c5..d0788e1f5 100644 --- a/.vortex/docs/content/contributing/maintenance/template.mdx +++ b/.vortex/docs/content/contributing/maintenance/template.mdx @@ -248,12 +248,12 @@ Without arguments, runs `all` for a full refresh. Default tag is `latest`. | Mode | What it does | Follow-up | | --- | --- | --- | -| `demo-dump` | Builds the demo profile in-place; exports `.data/db.demo.sql`. | Upload as `db_d11.demo.sql` to the latest GitHub release. | +| `demo-dump` | Builds the demo profile in-place; exports `.data/db.demo.sql`. | Upload as `db.demo.sql` to the latest GitHub release. | | `demo-image` | Builds the demo profile in-place; pushes `drevops/vortex-dev-mariadb-drupal-data-demo-11.x:`. | None. | | `test-dump` | Installs Vortex into `/tmp/star-wars`; builds; exports `/tmp/star-wars/.data/db.test.sql`. | Upload as `db_d11.test.sql` to the latest GitHub release. | | `test-image` | Installs Vortex into `/tmp/star-wars`; builds; pushes `drevops/vortex-dev-mariadb-drupal-data-test-11.x:`. | None. | | `destination-images` | Tags and pushes the local demo image to the didi destination tags (`vortex-dev-database-ii`, `vortex-dev-didi-database-fi`). | None. | -| `all` | Runs every mode above in order. Default when no mode is given. | Upload both dump files as above. | +| `all` | Optimised full refresh: builds the demo stack once for both demo modes and the test stack once for both test modes (2 stack builds instead of 4), then pushes images and tags destination images. Default when no mode is given. | Upload both dump files as above. | ### Options @@ -271,7 +271,7 @@ Without arguments, runs `all` for a full refresh. Default tag is `latest`. - Demo modes are **destructive** against the current repository: containers are reset and `.data/db.sql` is removed before the rebuild. - Test modes wipe and recreate `/tmp/star-wars` on each run. -- `destination-images` expects `drevops/vortex-dev-mariadb-drupal-data-demo-11.x:latest` +- `destination-images` expects `drevops/vortex-dev-mariadb-drupal-data-demo-11.x:1.38.0-rc1` to already exist locally - run `demo-image` (or `all`) first. ### Examples diff --git a/.vortex/installer/tests/Fixtures/handler_process/_baseline/.env b/.vortex/installer/tests/Fixtures/handler_process/_baseline/.env index 8e379075a..676f0d2db 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/_baseline/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/_baseline/.env @@ -225,4 +225,4 @@ VORTEX_NOTIFY_EMAIL_RECIPIENTS="webmaster@star-wars.com|Webmaster" ################################################################################ # URL of the database used for demonstration with URL database download type. -VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/db_download_source_acquia/.env b/.vortex/installer/tests/Fixtures/handler_process/db_download_source_acquia/.env index 59cd30f46..0ad8b1c11 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/db_download_source_acquia/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/db_download_source_acquia/.env @@ -30,4 +30,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/db_download_source_container_registry/.env b/.vortex/installer/tests/Fixtures/handler_process/db_download_source_container_registry/.env index a6e94b66b..952c1c0bb 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/db_download_source_container_registry/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/db_download_source_container_registry/.env @@ -32,4 +32,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/db_download_source_ftp/.env b/.vortex/installer/tests/Fixtures/handler_process/db_download_source_ftp/.env index ca9234051..8c94c9cd2 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/db_download_source_ftp/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/db_download_source_ftp/.env @@ -40,4 +40,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/db_download_source_lagoon/.env b/.vortex/installer/tests/Fixtures/handler_process/db_download_source_lagoon/.env index 997c9ca82..1f307068c 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/db_download_source_lagoon/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/db_download_source_lagoon/.env @@ -30,4 +30,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/db_download_source_s3/.env b/.vortex/installer/tests/Fixtures/handler_process/db_download_source_s3/.env index 8f477d4eb..ceabad24f 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/db_download_source_s3/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/db_download_source_s3/.env @@ -37,4 +37,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/.env b/.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/.env index 2b371977c..e7bcb4010 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/.env @@ -72,4 +72,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/hosting_lagoon/.env b/.vortex/installer/tests/Fixtures/handler_process/hosting_lagoon/.env index b03fffd2d..8565b1025 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/hosting_lagoon/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/hosting_lagoon/.env @@ -58,4 +58,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/.env b/.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/.env index d43b77eda..0229211a3 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/.env @@ -72,4 +72,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___lagoon/.env b/.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___lagoon/.env index 617791567..92128ac31 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___lagoon/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___lagoon/.env @@ -58,4 +58,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/migration_disabled_lagoon/.env b/.vortex/installer/tests/Fixtures/handler_process/migration_disabled_lagoon/.env index b03fffd2d..8565b1025 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/migration_disabled_lagoon/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/migration_disabled_lagoon/.env @@ -58,4 +58,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/migration_download_source_url/.env b/.vortex/installer/tests/Fixtures/handler_process/migration_download_source_url/.env index ac309d232..c695f08c8 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/migration_download_source_url/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/migration_download_source_url/.env @@ -31,7 +31,7 @@ @@ -226,3 +249,6 @@ # URL of the database used for demonstration with URL database download type. - VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql + VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql + +# URL of the migration source database used for demonstration. +VORTEX_DOWNLOAD_DB2_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo_source.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/migration_enabled/.env b/.vortex/installer/tests/Fixtures/handler_process/migration_enabled/.env index ac309d232..c695f08c8 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/migration_enabled/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/migration_enabled/.env @@ -31,7 +31,7 @@ @@ -226,3 +249,6 @@ # URL of the database used for demonstration with URL database download type. - VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql + VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql + +# URL of the migration source database used for demonstration. +VORTEX_DOWNLOAD_DB2_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo_source.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/migration_enabled_circleci/.env b/.vortex/installer/tests/Fixtures/handler_process/migration_enabled_circleci/.env index ac309d232..c695f08c8 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/migration_enabled_circleci/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/migration_enabled_circleci/.env @@ -31,7 +31,7 @@ @@ -226,3 +249,6 @@ # URL of the database used for demonstration with URL database download type. - VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql + VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql + +# URL of the migration source database used for demonstration. +VORTEX_DOWNLOAD_DB2_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo_source.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/provision_database_lagoon/.env b/.vortex/installer/tests/Fixtures/handler_process/provision_database_lagoon/.env index b03fffd2d..8565b1025 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/provision_database_lagoon/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/provision_database_lagoon/.env @@ -58,4 +58,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/provision_profile/.env b/.vortex/installer/tests/Fixtures/handler_process/provision_profile/.env index c43ff35c7..77f5f4583 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/provision_profile/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/provision_profile/.env @@ -46,4 +46,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/starter_drupal_cms_profile/.env b/.vortex/installer/tests/Fixtures/handler_process/starter_drupal_cms_profile/.env index 7acececa1..185cfcde2 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/starter_drupal_cms_profile/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/starter_drupal_cms_profile/.env @@ -24,4 +24,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/starter_drupal_profile/.env b/.vortex/installer/tests/Fixtures/handler_process/starter_drupal_profile/.env index a6741341d..4dc376352 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/starter_drupal_profile/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/starter_drupal_profile/.env @@ -15,4 +15,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql diff --git a/.vortex/installer/tests/Fixtures/handler_process/theme_custom_non_vortex/.env b/.vortex/installer/tests/Fixtures/handler_process/theme_custom_non_vortex/.env index 87210e92f..b6b1464cc 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/theme_custom_non_vortex/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/theme_custom_non_vortex/.env @@ -3,7 +3,7 @@ # # HTTP Basic Authentication credentials should be embedded into the value. -VORTEX_DOWNLOAD_DB_URL= -+VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql ++VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql # Environment to download the database from. # diff --git a/.vortex/tests/bats/unit/provision.bats b/.vortex/tests/bats/unit/provision.bats index 6656a3803..068ccc83b 100644 --- a/.vortex/tests/bats/unit/provision.bats +++ b/.vortex/tests/bats/unit/provision.bats @@ -1191,7 +1191,7 @@ assert_provision_info() { rm -f ./scripts/custom/provision-20-migration.sh export CI=1 - export VORTEX_DB_IMAGE="drevops/vortex-dev-mariadb-drupal-data-test-11.x:latest" + export VORTEX_DB_IMAGE="drevops/vortex-dev-mariadb-drupal-data-test-11.x:1.38.0-rc1" mkdir "./.data" touch "./.data/db.sql" @@ -1239,7 +1239,7 @@ assert_provision_info() { export CI=1 export VORTEX_PROVISION_SANITIZE_DB_PASSWORD="MOCK_DB_SANITIZE_PASSWORD" - export VORTEX_DB_IMAGE="drevops/vortex-dev-mariadb-drupal-data-test-11.x:latest" + export VORTEX_DB_IMAGE="drevops/vortex-dev-mariadb-drupal-data-test-11.x:1.38.0-rc1" mkdir "./.data" touch "./.data/db.sql" @@ -1380,7 +1380,7 @@ assert_provision_info() { export CI=1 export VORTEX_PROVISION_SANITIZE_DB_PASSWORD="MOCK_DB_SANITIZE_PASSWORD" - export VORTEX_DB_IMAGE="drevops/vortex-dev-mariadb-drupal-data-test-11.x:latest" + export VORTEX_DB_IMAGE="drevops/vortex-dev-mariadb-drupal-data-test-11.x:1.38.0-rc1" mkdir "./.data" touch "./.data/db.sql" @@ -1585,7 +1585,7 @@ assert_provision_info() { rm -f ./scripts/custom/provision-20-migration.sh export CI=1 - export VORTEX_DB_IMAGE="drevops/vortex-dev-mariadb-drupal-data-test-11.x:latest" + export VORTEX_DB_IMAGE="drevops/vortex-dev-mariadb-drupal-data-test-11.x:1.38.0-rc1" mkdir "./.data" touch "./.data/db.sql" diff --git a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php index 8c7c6d8d1..540aa93f4 100644 --- a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php +++ b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php @@ -150,7 +150,7 @@ public function testAhoyWorkflowDatabaseFromImageStorageInImage(): void { $this->assertFileContainsString('.env', 'VORTEX_DOWNLOAD_DB_SOURCE=container_registry', '.env should contain container registry source'); $this->assertFileContainsString('.env', 'VORTEX_DB_IMAGE=' . self::VORTEX_DB_IMAGE_TEST, '.env should contain correct database image'); // Assert that demo config was removed as a part of the installation. - $this->assertFileNotContainsString('.env', 'VORTEX_DB_IMAGE=drevops/vortex-dev-mariadb-drupal-data-demo-11.x:latest', '.env should not contain demo database image'); + $this->assertFileNotContainsString('.env', 'VORTEX_DB_IMAGE=drevops/vortex-dev-mariadb-drupal-data-demo-11.x:1.38.0-rc1', '.env should not contain demo database image'); $this->assertFileNotContainsString('.env', 'VORTEX_DOWNLOAD_DB_URL=', '.env should not contain database download URL'); // Do not use demo database - testing demo database discovery is diff --git a/.vortex/tests/phpunit/Traits/SutTrait.php b/.vortex/tests/phpunit/Traits/SutTrait.php index f1b387342..1dd073b87 100644 --- a/.vortex/tests/phpunit/Traits/SutTrait.php +++ b/.vortex/tests/phpunit/Traits/SutTrait.php @@ -34,7 +34,7 @@ trait SutTrait { /** * Image name for the test database. */ - const VORTEX_DB_IMAGE_TEST = 'drevops/vortex-dev-mariadb-drupal-data-test-11.x:latest'; + const VORTEX_DB_IMAGE_TEST = 'drevops/vortex-dev-mariadb-drupal-data-test-11.x:1.38.0-rc1'; /** * Environment variables to set when running the installer. diff --git a/.vortex/tests/update-test-assets b/.vortex/tests/update-test-assets index b640122df..a1c632635 100755 --- a/.vortex/tests/update-test-assets +++ b/.vortex/tests/update-test-assets @@ -24,7 +24,10 @@ declare(strict_types=1); * drevops/vortex-dev-mariadb-drupal-data-test-11.x:. * destination-images Tag and push the local demo image to didi destination * tags (vortex-dev-database-ii, vortex-dev-didi-database-fi). - * all Run all of the above in order (default). + * all Run all of the above (default). Optimised: builds + * the demo stack once for both demo modes and the + * test stack once for both test modes (2 stack builds + * instead of 4). * * Options: * --tag Image tag for image modes (default: 'latest'). @@ -59,16 +62,7 @@ require_command('curl'); require_command('sed'); require_command('php'); -if ($mode === 'all') { - run_mode('demo-dump', $root_dir, $installer, $tag); - run_mode('demo-image', $root_dir, $installer, $tag); - run_mode('test-dump', $root_dir, $installer, $tag); - run_mode('test-image', $root_dir, $installer, $tag); - run_mode('destination-images', $root_dir, $installer, $tag); -} -else { - run_mode($mode, $root_dir, $installer, $tag); -} +run_mode($mode, $root_dir, $installer, $tag); step('Done'); @@ -84,7 +78,8 @@ function run_mode(string $mode, string $root_dir, string $installer, string $tag 'demo-image' => mode_demo_image($root_dir, $tag), 'test-dump' => mode_test_dump($root_dir, $installer), 'test-image' => mode_test_image($root_dir, $installer, $tag), - 'destination-images' => mode_destination_images(), + 'destination-images' => mode_destination_images($tag), + 'all' => mode_all($root_dir, $installer, $tag), }; } @@ -93,53 +88,58 @@ function run_mode(string $mode, string $root_dir, string $installer, string $tag // ============================================================================= function mode_demo_dump(string $root_dir): void { + $body = body_demo_dump(); + build_demo_in_place($root_dir); - seed_node($root_dir, 'Welcome to the demo site!', 'This demo page is sourced from the Vortex database dump file to demonstrate database importing capabilities.'); + set_node_content($root_dir, 'Welcome to the demo site!', $body); finalise_content($root_dir); - export_db($root_dir, 'db.demo.sql'); - - step('Manual follow-up'); - println(sprintf(' Upload %s/.data/db.demo.sql to the latest GitHub release as asset "db_d11.demo.sql".', $root_dir)); + export_db($root_dir, 'db.demo.sql', $body); + print_dump_followup($root_dir, 'db.demo.sql', 'db.demo.sql'); } function mode_demo_image(string $root_dir, string $tag): void { $image = sprintf('%s:%s', IMAGE_DEMO, $tag); + $body = body_demo_image(); build_demo_in_place($root_dir); - seed_node($root_dir, 'Welcome to the demo site!', 'This demo page is sourced from the Vortex database container image to demonstrate database importing capabilities.'); + set_node_content($root_dir, 'Welcome to the demo site!', $body); finalise_content($root_dir); - export_db($root_dir, 'db.demo_image.sql'); + export_db($root_dir, 'db.demo_image.sql', $body); fix_collation($root_dir, 'db.demo_image.sql'); seed_image($root_dir, 'db.demo_image.sql', $image); } function mode_test_dump(string $root_dir, string $installer): void { + $body = body_test_dump(); + reset_sut($root_dir, $installer); build_in($root_dir, SUT_DIR, FALSE); - seed_node(SUT_DIR, 'Welcome to the test site!', 'This test page is sourced from the Vortex database dump file to demonstrate database importing capabilities.'); + set_node_content(SUT_DIR, 'Welcome to the test site!', $body); finalise_content(SUT_DIR); - export_db(SUT_DIR, 'db.test.sql'); - - step('Manual follow-up'); - println(sprintf(' Upload %s/.data/db.test.sql to the latest GitHub release as asset "db_d11.test.sql".', SUT_DIR)); + export_db(SUT_DIR, 'db.test.sql', $body); + print_dump_followup(SUT_DIR, 'db.test.sql', 'db_d11.test.sql'); } function mode_test_image(string $root_dir, string $installer, string $tag): void { $image = sprintf('%s:%s', IMAGE_TEST, $tag); + $body = body_test_image(); reset_sut($root_dir, $installer); build_in($root_dir, SUT_DIR, FALSE); - seed_node(SUT_DIR, 'Welcome to the test site!', 'This test page is sourced from the Vortex database container image to demonstrate database importing capabilities.'); + set_node_content(SUT_DIR, 'Welcome to the test site!', $body); finalise_content(SUT_DIR); - export_db(SUT_DIR, 'db.test_image.sql'); + export_db(SUT_DIR, 'db.test_image.sql', $body); fix_collation(SUT_DIR, 'db.test_image.sql'); seed_image(SUT_DIR, 'db.test_image.sql', $image); } -function mode_destination_images(): void { - step('Tag and push destination images from local demo image'); - $source = sprintf('%s:latest', IMAGE_DEMO); +function mode_destination_images(string $tag): void { + $source = sprintf('%s:%s', IMAGE_DEMO, $tag); + step(sprintf('Pull source demo image %s', $source)); + run(sprintf('docker pull %s', escapeshellarg($source))); + + step('Tag and push destination images from local demo image'); foreach (['vortex-dev-database-ii', 'vortex-dev-didi-database-fi'] as $dest_tag) { $dest = sprintf('%s:%s', IMAGE_DEST, $dest_tag); run(sprintf('docker tag %s %s', escapeshellarg($source), escapeshellarg($dest))); @@ -147,18 +147,101 @@ function mode_destination_images(): void { } } +/** + * Optimised full refresh: 2 stack builds (demo + test) instead of 4. + * + * Within each site context (demo and test) the stack is built once, content + * is exported as the dump variant, then the same node's body is updated to + * the image variant before exporting and pushing the image. This reuses the + * provisioned stack and skips two 'ahoy build' invocations. + */ +function mode_all(string $root_dir, string $installer, string $tag): void { + // Demo phase: one stack build, two exports. + step('=== Demo phase (single stack build) ==='); + build_demo_in_place($root_dir); + + $demo_dump_body = body_demo_dump(); + set_node_content($root_dir, 'Welcome to the demo site!', $demo_dump_body); + finalise_content($root_dir); + export_db($root_dir, 'db.demo.sql', $demo_dump_body); + print_dump_followup($root_dir, 'db.demo.sql', 'db.demo.sql'); + + $demo_image_body = body_demo_image(); + set_node_content($root_dir, 'Welcome to the demo site!', $demo_image_body); + truncate_caches($root_dir); + export_db($root_dir, 'db.demo_image.sql', $demo_image_body); + fix_collation($root_dir, 'db.demo_image.sql'); + seed_image($root_dir, 'db.demo_image.sql', sprintf('%s:%s', IMAGE_DEMO, $tag)); + + // Test phase: one stack build, two exports. + step('=== Test phase (single stack build) ==='); + reset_sut($root_dir, $installer); + build_in($root_dir, SUT_DIR, FALSE); + + $test_dump_body = body_test_dump(); + set_node_content(SUT_DIR, 'Welcome to the test site!', $test_dump_body); + finalise_content(SUT_DIR); + export_db(SUT_DIR, 'db.test.sql', $test_dump_body); + print_dump_followup(SUT_DIR, 'db.test.sql', 'db_d11.test.sql'); + + $test_image_body = body_test_image(); + set_node_content(SUT_DIR, 'Welcome to the test site!', $test_image_body); + truncate_caches(SUT_DIR); + export_db(SUT_DIR, 'db.test_image.sql', $test_image_body); + fix_collation(SUT_DIR, 'db.test_image.sql'); + seed_image(SUT_DIR, 'db.test_image.sql', sprintf('%s:%s', IMAGE_TEST, $tag)); + + // Destination images. + step('=== Destination images ==='); + mode_destination_images($tag); +} + +// ============================================================================= +// Body texts. +// ============================================================================= + +function body_demo_dump(): string { + return 'This demo page is sourced from the Vortex database dump file to demonstrate database importing capabilities.'; +} + +function body_demo_image(): string { + return 'This demo page is sourced from the Vortex database container image to demonstrate database importing capabilities.'; +} + +function body_test_dump(): string { + return 'This test page is sourced from the Vortex database dump file to demonstrate database importing capabilities.'; +} + +function body_test_image(): string { + return 'This test page is sourced from the Vortex database container image to demonstrate database importing capabilities.'; +} + +function print_dump_followup(string $cwd, string $filename, string $asset_name): void { + step('Manual follow-up'); + println(sprintf(' Upload %s/.data/%s to the latest GitHub release as asset "%s".', $cwd, $filename, $asset_name)); +} + // ============================================================================= // Shared subroutines. // ============================================================================= function reset_sut(string $root_dir, string $installer): void { step(sprintf('Reset SUT directory %s', SUT_DIR)); - run(sprintf('rm -rf %s', escapeshellarg(SUT_DIR))); + + // Drupal stages public files inside containers running as 'www-data' + // (uid 33). When those bind-mounts surface on the host, plain 'rm -rf' + // by the current user fails with "Permission denied". Clear the contents + // via an ephemeral root-privileged container so file ownership doesn't + // matter, then leave the dir itself in place for the installer. run(sprintf('mkdir -p %s', escapeshellarg(SUT_DIR))); + run(sprintf( + 'docker run --rm -v %s:/sut alpine find /sut -mindepth 1 -delete', + escapeshellarg(SUT_DIR) + )); step('Install Vortex into SUT'); run(sprintf( - 'VORTEX_INSTALLER_TEMPLATE_REPO=%s php %s %s --no-interaction', + 'VORTEX_INSTALLER_TEMPLATE_REPO=%s php %s --no-interaction --destination=%s', escapeshellarg($root_dir), escapeshellarg($installer), escapeshellarg(SUT_DIR) @@ -173,17 +256,39 @@ function build_in(string $root_dir, string $cwd, bool $skip_post_ops): void { step('Remove any pre-existing DB dump'); run('rm -f .data/db.sql', $cwd); - step('Build stack from profile'); + // Replicate 'ahoy build' step-by-step so COMPOSER_PROCESS_TIMEOUT=0 can be + // injected into the composer install step (extracting drupal/core can take + // longer than the default 300s on slower hosts). 'ahoy cli' does not forward + // COMPOSER_* env vars from the host, hence the direct 'docker compose exec'. + step('Reset stack'); + run('AHOY_CONFIRM_RESPONSE=1 AHOY_CONFIRM_WAIT_SKIP=1 ahoy reset', $cwd); + + step('Bring up containers'); + run('ahoy up --build --force-recreate', $cwd); + + step('Install Composer dependencies (COMPOSER_PROCESS_TIMEOUT=0)'); + run("docker compose exec -T -e COMPOSER_MEMORY_LIMIT=-1 -e COMPOSER_PROCESS_TIMEOUT=0 cli composer --ansi install", $cwd); + + step('Install front-end dependencies'); + run('ahoy fei', $cwd); + + step('Build front-end assets (skipped if theme not configured)'); + run('ahoy fe || true', $cwd); + + step('Provision'); $env = $skip_post_ops ? 'VORTEX_PROVISION_TYPE=profile VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 AHOY_CONFIRM_RESPONSE=1' : 'VORTEX_PROVISION_TYPE=profile AHOY_CONFIRM_RESPONSE=1'; - run(sprintf('%s ahoy build', $env), $cwd); + run(sprintf('%s ahoy provision', $env), $cwd); + + step('Info'); + run('ahoy info', $cwd); } -function seed_node(string $cwd, string $title, string $body): void { - step(sprintf('Seed test page node ("%s")', $title)); +function set_node_content(string $cwd, string $title, string $body): void { + step(sprintf('Set node 1 content ("%s")', $title)); - $php = build_seed_php($title, $body); + $php = build_set_node_content_php($title, $body); $drush_cmd = sprintf('drush php:eval %s', escapeshellarg($php)); $ahoy_cmd = sprintf('ahoy cli %s', escapeshellarg($drush_cmd)); @@ -191,16 +296,59 @@ function seed_node(string $cwd, string $title, string $body): void { } function finalise_content(string $cwd): void { - step('Set page.front to /node/1'); - run('ahoy drush config:set system.site page.front /node/1 -y', $cwd); + set_front_page($cwd, '/node/1'); + truncate_caches($cwd); +} + +function set_front_page(string $cwd, string $path): void { + step(sprintf('Set and verify page.front to %s', $path)); + + $path_escaped = addcslashes($path, '"\\$'); + $php = implode(' ', [ + sprintf('\Drupal::configFactory()->getEditable("system.site")->set("page.front", "%s")->save();', $path_escaped), + sprintf('$front = \Drupal::config("system.site")->get("page.front");'), + sprintf('if ($front !== "%s") { throw new \RuntimeException("page.front mismatch: " . $front); }', $path_escaped), + 'echo "Verified page.front=" . $front . PHP_EOL;', + ]); + $drush_cmd = sprintf('drush php:eval %s', escapeshellarg($php)); + $ahoy_cmd = sprintf('ahoy cli %s', escapeshellarg($drush_cmd)); + run($ahoy_cmd, $cwd); +} + +function truncate_caches(string $cwd): void { step('Truncate cache_* tables and watchdog'); run("ahoy cli 'drush sql:query \"SHOW TABLES LIKE \\\"cache_%\\\"\" | xargs -I{} drush sql:query \"TRUNCATE TABLE {}\" && drush sql:query \"TRUNCATE TABLE watchdog\"'", $cwd); } -function export_db(string $cwd, string $filename): void { +function export_db(string $cwd, string $filename, ?string $expected_substring = NULL): void { step(sprintf('Export database to .data/%s', $filename)); run(sprintf('ahoy export-db %s', escapeshellarg($filename)), $cwd); + + $path = sprintf('%s/.data/%s', $cwd, $filename); + step(sprintf('Verify export file %s', $path)); + + if (!is_file($path)) { + fwrite(STDERR, sprintf("Error: export file %s does not exist.\n", $path)); + exit(1); + } + + $size = filesize($path); + if ($size === FALSE || $size === 0) { + fwrite(STDERR, sprintf("Error: export file %s is empty.\n", $path)); + exit(1); + } + + println(sprintf(' - File exists, %d bytes.', $size)); + + if ($expected_substring !== NULL) { + $contents = file_get_contents($path); + if ($contents === FALSE || !str_contains($contents, $expected_substring)) { + fwrite(STDERR, sprintf("Error: export file %s does not contain expected substring: %s\n", $path, $expected_substring)); + exit(1); + } + println(sprintf(' - Contains expected body text: %s', truncate_for_log($expected_substring, 60))); + } } function fix_collation(string $cwd, string $filename): void { @@ -208,6 +356,27 @@ function fix_collation(string $cwd, string $filename): void { $path = sprintf('.data/%s', $filename); run(sprintf("sed -i.bak 's/utf8mb4_0900_ai_ci/utf8mb4_general_ci/g' %s", escapeshellarg($path)), $cwd); run(sprintf('rm -f %s.bak', escapeshellarg($path)), $cwd); + + step('Verify collation rewrite'); + $abs_path = sprintf('%s/.data/%s', $cwd, $filename); + $contents = file_get_contents($abs_path); + if ($contents === FALSE) { + fwrite(STDERR, sprintf("Error: cannot read %s for collation verification.\n", $abs_path)); + exit(1); + } + if (str_contains($contents, 'utf8mb4_0900_ai_ci')) { + fwrite(STDERR, sprintf("Error: %s still contains 'utf8mb4_0900_ai_ci' after rewrite.\n", $abs_path)); + exit(1); + } + println(sprintf(' - No utf8mb4_0900_ai_ci references remain in %s.', $abs_path)); +} + +function truncate_for_log(string $value, int $max): string { + if (strlen($value) <= $max) { + return $value; + } + + return substr($value, 0, $max) . '...'; } function seed_image(string $cwd, string $filename, string $image): void { @@ -331,19 +500,56 @@ function run(string $command, ?string $cwd = NULL): void { } } -function build_seed_php(string $title, string $body): string { +function build_set_node_content_php(string $title, string $body): string { // Escape characters that have special meaning inside PHP double-quoted // strings ('"', '\', '$') so the inline 'drush php:eval' payload stays // intact through host shell, ahoy and container bash. $title_escaped = addcslashes($title, '"\\$'); $body_escaped = addcslashes($body, '"\\$'); + // Idempotent: load node 1 and update it if present, otherwise create a new + // page node. The node MUST be published explicitly - bundle 'page' + // publication defaults are only applied via NodeForm and are NOT inherited + // by entity API create/save, so without an explicit status the node ends up + // unpublished and anonymous visitors get an "Access denied" page. + // If Content Moderation is attached to the bundle (test phase, after + // 'sw_search_deploy_add_editorial_workflow'), 'status' alone is not enough - + // 'moderation_state' must be set to 'published' or the node defaults to + // draft and isPublished() returns false even with status=1. + // Updates skip new revisions to keep exports diff-friendly. + // After save, the node is reloaded and asserted to verify state ended up + // as expected; any mismatch throws and exits non-zero. return implode(' ', [ - '$node = \Drupal::entityTypeManager()->getStorage("node")->create([', + '$storage = \Drupal::entityTypeManager()->getStorage("node");', + '$node = $storage->load(1);', + 'if ($node) {', + sprintf('$node->set("title", "%s");', $title_escaped), + sprintf('$node->set("body", ["value" => "

%s

", "format" => "basic_html"]);', $body_escaped), + '$node->setPublished();', + 'if ($node->hasField("moderation_state")) { $node->set("moderation_state", "published"); }', + '$node->setNewRevision(FALSE);', + '$node->save();', + 'echo "Updated node " . $node->id() . PHP_EOL;', + '} else {', + '$node = $storage->create([', sprintf('"type" => "page", "title" => "%s",', $title_escaped), - sprintf('"body" => ["value" => "

%s

", "format" => "basic_html"]', $body_escaped), + sprintf('"body" => ["value" => "

%s

", "format" => "basic_html"],', $body_escaped), + '"status" => 1,', + '"uid" => 1,', ']);', + 'if ($node->hasField("moderation_state")) { $node->set("moderation_state", "published"); }', '$node->save();', 'echo "Created node " . $node->id() . PHP_EOL;', + '}', + // Reload and verify post-save state. + '$storage->resetCache([1]);', + '$node = $storage->load(1);', + 'if (!$node) { throw new \RuntimeException("Node 1 missing after save."); }', + 'if (!$node->isPublished()) { throw new \RuntimeException("Node 1 is not published."); }', + sprintf('if ($node->getTitle() !== "%s") { throw new \RuntimeException("Title mismatch: " . $node->getTitle()); }', $title_escaped), + sprintf('$expected_body = "

%s

";', $body_escaped), + 'if ($node->get("body")->value !== $expected_body) { throw new \RuntimeException("Body mismatch: " . $node->get("body")->value); }', + '$mod_state = $node->hasField("moderation_state") ? $node->get("moderation_state")->value : "(no moderation)";', + 'echo "Verified node 1: published=1, title=\"" . $node->getTitle() . "\", body length=" . strlen($node->get("body")->value) . ", moderation_state=" . $mod_state . PHP_EOL;', ]); } From 70ddfabc71fdbfdcaf498c222e826fe648e78d23 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 27 Apr 2026 17:38:30 +1000 Subject: [PATCH 4/9] Updated snapshots. --- .../Fixtures/handler_process/migration_enabled_lagoon/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vortex/installer/tests/Fixtures/handler_process/migration_enabled_lagoon/.env b/.vortex/installer/tests/Fixtures/handler_process/migration_enabled_lagoon/.env index d7392e83b..b63aa8ab4 100644 --- a/.vortex/installer/tests/Fixtures/handler_process/migration_enabled_lagoon/.env +++ b/.vortex/installer/tests/Fixtures/handler_process/migration_enabled_lagoon/.env @@ -83,4 +83,4 @@ -################################################################################ - -# URL of the database used for demonstration with URL database download type. --VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db_d11.demo.sql +-VORTEX_DOWNLOAD_DB_URL=https://github.com/drevops/vortex/releases/download/__VERSION__/db.demo.sql From 02c9384830b34422bd18e1265917e601465081a8 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 27 Apr 2026 18:41:47 +1000 Subject: [PATCH 5/9] Added diagnostic probes to 'testAhoyWorkflowProvisionFallbackToProfile'. --- .../phpunit/Functional/AhoyWorkflowTest.php | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php index 540aa93f4..6a3e518ef 100644 --- a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php +++ b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php @@ -530,6 +530,53 @@ public function testAhoyWorkflowProvisionFallbackToProfile(): void { $this->logSubstep('Assert that Shield module is enabled'); $this->cmd('ahoy drush pm:list --status=enabled --type=module --format=list', '* shield', 'Shield module should be enabled after fallback provision'); + // Diagnostic probes to understand why the homepage hits the redirect + // module after a fallback profile install. Each command's full output is + // streamed to the test log; failures here are non-fatal so we still reach + // the original assertions below. + // @todo Remove once the underlying cause is understood and fixed. + $this->logSubstep('DEBUG: enabled modules per core.extension'); + $this->cmd('ahoy drush pm:list --status=enabled --type=module --format=list'); + + $this->logSubstep('DEBUG: core.extension config dump'); + $this->cmd('ahoy drush config:get core.extension'); + + $this->logSubstep('DEBUG: redirect module status (should be disabled after fallback)'); + $this->cmd('ahoy drush pm:list --filter=redirect --format=list'); + + $this->logSubstep('DEBUG: all DB tables'); + $this->cmd('ahoy drush sql:query "SHOW TABLES"'); + + $this->logSubstep('DEBUG: redirect-related DB tables (should be empty)'); + $this->cmd('ahoy drush sql:query "SHOW TABLES LIKE \'redirect%\'"'); + + $this->logSubstep('DEBUG: cache_container row count'); + $this->cmd('ahoy drush sql:query "SELECT COUNT(*) FROM cache_container"'); + + $this->logSubstep('DEBUG: PhpStorage layout under web/sites/default/files/php'); + $this->cmd('ahoy cli "ls -la web/sites/default/files/php/ 2>/dev/null || echo NO_PHP_DIR"'); + $this->cmd('ahoy cli "ls -la web/sites/default/files/php/container/ 2>/dev/null || echo NO_CONTAINER_DIR"'); + $this->cmd('ahoy cli "find web/sites/default/files/php -type f 2>/dev/null | head -30 || echo NO_FILES"'); + + $this->logSubstep('DEBUG: presence of redirect references in cached container files'); + $this->cmd('ahoy cli "grep -l \"Drupal..redirect\" web/sites/default/files/php/container/*.php 2>/dev/null || echo NO_REDIRECT_IN_CONTAINER_CACHE"'); + $this->cmd('ahoy cli "grep -c \"redirect.request_subscriber\\|RedirectRequestSubscriber\" web/sites/default/files/php/container/*.php 2>/dev/null || echo NO_REDIRECT_SUBSCRIBER_IN_CACHE"'); + + $this->logSubstep('DEBUG: hash_salt and deployment_identifier'); + $this->cmd('ahoy drush php:eval "print \"hash_salt=\" . substr(\Drupal\Core\Site\Settings::get(\"hash_salt\"), 0, 12) . PHP_EOL . \"deployment_identifier=\" . (\Drupal\Core\Site\Settings::get(\"deployment_identifier\") ?? \"(null)\") . PHP_EOL;"'); + + $this->logSubstep('DEBUG: runtime active modules per Drupal kernel'); + $this->cmd('ahoy drush php:eval "foreach (array_keys(\Drupal::moduleHandler()->getModuleList()) as \$m) { print \$m . PHP_EOL; }"'); + + $this->logSubstep('DEBUG: registered path_processor_inbound services'); + $this->cmd('ahoy drush php:eval "foreach (\Drupal::getContainer()->getServiceIds() as \$id) { if (str_contains(\$id, \"path_processor\") || str_contains(\$id, \"redirect\")) { print \$id . PHP_EOL; } }"'); + + $this->logSubstep('DEBUG: last 30 watchdog entries'); + $this->cmd('ahoy drush watchdog:show --count=30 || true'); + + $this->logSubstep('DEBUG: head of homepage response'); + $this->cmd('ahoy cli "curl -sS -o - -w \"\\nHTTP_STATUS=%{http_code}\\n\" http://nginx:8080/ | head -c 4000"'); + $this->logSubstep('Assert that homepage does not contain database dump content'); $this->assertWebpageNotContains('/', 'This demo page is sourced from the Vortex database dump file', 'Homepage should not show database dump content after fallback provision'); From ea77f2128ff89ec95965456ad26d3bc0e46fdab2 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 27 Apr 2026 19:02:35 +1000 Subject: [PATCH 6/9] Routed diagnostic probes through 'ahoy cli' to preserve quoting. --- .../phpunit/Functional/AhoyWorkflowTest.php | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php index 6a3e518ef..4c029ab69 100644 --- a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php +++ b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php @@ -531,51 +531,51 @@ public function testAhoyWorkflowProvisionFallbackToProfile(): void { $this->cmd('ahoy drush pm:list --status=enabled --type=module --format=list', '* shield', 'Shield module should be enabled after fallback provision'); // Diagnostic probes to understand why the homepage hits the redirect - // module after a fallback profile install. Each command's full output is - // streamed to the test log; failures here are non-fatal so we still reach - // the original assertions below. + // module after a fallback profile install. Each command is routed through + // 'ahoy cli' with a single-quoted outer arg so multi-word args (queries, + // eval expressions) survive ahoy's '$*' expansion intact. // @todo Remove once the underlying cause is understood and fixed. $this->logSubstep('DEBUG: enabled modules per core.extension'); - $this->cmd('ahoy drush pm:list --status=enabled --type=module --format=list'); + $this->cmd("ahoy cli 'vendor/bin/drush pm:list --status=enabled --type=module --format=list'"); $this->logSubstep('DEBUG: core.extension config dump'); - $this->cmd('ahoy drush config:get core.extension'); + $this->cmd("ahoy cli 'vendor/bin/drush config:get core.extension'"); $this->logSubstep('DEBUG: redirect module status (should be disabled after fallback)'); - $this->cmd('ahoy drush pm:list --filter=redirect --format=list'); + $this->cmd("ahoy cli 'vendor/bin/drush pm:list --filter=redirect --format=list || true'"); $this->logSubstep('DEBUG: all DB tables'); - $this->cmd('ahoy drush sql:query "SHOW TABLES"'); + $this->cmd("ahoy cli 'vendor/bin/drush sql:query \"SHOW TABLES\"'"); $this->logSubstep('DEBUG: redirect-related DB tables (should be empty)'); - $this->cmd('ahoy drush sql:query "SHOW TABLES LIKE \'redirect%\'"'); + $this->cmd("ahoy cli 'vendor/bin/drush sql:query \"SHOW TABLES LIKE \\\"redirect%\\\"\" || true'"); $this->logSubstep('DEBUG: cache_container row count'); - $this->cmd('ahoy drush sql:query "SELECT COUNT(*) FROM cache_container"'); + $this->cmd("ahoy cli 'vendor/bin/drush sql:query \"SELECT COUNT(*) FROM cache_container\" || true'"); $this->logSubstep('DEBUG: PhpStorage layout under web/sites/default/files/php'); - $this->cmd('ahoy cli "ls -la web/sites/default/files/php/ 2>/dev/null || echo NO_PHP_DIR"'); - $this->cmd('ahoy cli "ls -la web/sites/default/files/php/container/ 2>/dev/null || echo NO_CONTAINER_DIR"'); - $this->cmd('ahoy cli "find web/sites/default/files/php -type f 2>/dev/null | head -30 || echo NO_FILES"'); + $this->cmd("ahoy cli 'ls -la web/sites/default/files/php/ 2>/dev/null || echo NO_PHP_DIR'"); + $this->cmd("ahoy cli 'ls -la web/sites/default/files/php/container/ 2>/dev/null || echo NO_CONTAINER_DIR'"); + $this->cmd("ahoy cli 'find web/sites/default/files/php -type f 2>/dev/null | head -30 || echo NO_FILES'"); $this->logSubstep('DEBUG: presence of redirect references in cached container files'); - $this->cmd('ahoy cli "grep -l \"Drupal..redirect\" web/sites/default/files/php/container/*.php 2>/dev/null || echo NO_REDIRECT_IN_CONTAINER_CACHE"'); - $this->cmd('ahoy cli "grep -c \"redirect.request_subscriber\\|RedirectRequestSubscriber\" web/sites/default/files/php/container/*.php 2>/dev/null || echo NO_REDIRECT_SUBSCRIBER_IN_CACHE"'); + $this->cmd("ahoy cli 'grep -l \"Drupal..redirect\" web/sites/default/files/php/container/*.php 2>/dev/null || echo NO_REDIRECT_IN_CONTAINER_CACHE'"); + $this->cmd("ahoy cli 'grep -c \"redirect.request_subscriber\\|RedirectRequestSubscriber\" web/sites/default/files/php/container/*.php 2>/dev/null || echo NO_REDIRECT_SUBSCRIBER_IN_CACHE'"); $this->logSubstep('DEBUG: hash_salt and deployment_identifier'); - $this->cmd('ahoy drush php:eval "print \"hash_salt=\" . substr(\Drupal\Core\Site\Settings::get(\"hash_salt\"), 0, 12) . PHP_EOL . \"deployment_identifier=\" . (\Drupal\Core\Site\Settings::get(\"deployment_identifier\") ?? \"(null)\") . PHP_EOL;"'); + $this->cmd("ahoy cli 'vendor/bin/drush php:eval \"echo \\\"hash_salt=\\\" . substr(\\\\Drupal\\\\Core\\\\Site\\\\Settings::get(\\\"hash_salt\\\"), 0, 12) . PHP_EOL; echo \\\"deployment_identifier=\\\" . (\\\\Drupal\\\\Core\\\\Site\\\\Settings::get(\\\"deployment_identifier\\\") ?? \\\"(null)\\\") . PHP_EOL;\"'"); $this->logSubstep('DEBUG: runtime active modules per Drupal kernel'); - $this->cmd('ahoy drush php:eval "foreach (array_keys(\Drupal::moduleHandler()->getModuleList()) as \$m) { print \$m . PHP_EOL; }"'); + $this->cmd("ahoy cli 'vendor/bin/drush php:eval \"foreach (array_keys(\\\\Drupal::moduleHandler()->getModuleList()) as \\\$m) { echo \\\$m . PHP_EOL; }\"'"); - $this->logSubstep('DEBUG: registered path_processor_inbound services'); - $this->cmd('ahoy drush php:eval "foreach (\Drupal::getContainer()->getServiceIds() as \$id) { if (str_contains(\$id, \"path_processor\") || str_contains(\$id, \"redirect\")) { print \$id . PHP_EOL; } }"'); + $this->logSubstep('DEBUG: redirect/path_processor service ids in live container'); + $this->cmd("ahoy cli 'vendor/bin/drush php:eval \"foreach (\\\\Drupal::getContainer()->getServiceIds() as \\\$id) { if (str_contains(\\\$id, \\\"path_processor\\\") || str_contains(\\\$id, \\\"redirect\\\")) { echo \\\$id . PHP_EOL; } }\"'"); $this->logSubstep('DEBUG: last 30 watchdog entries'); - $this->cmd('ahoy drush watchdog:show --count=30 || true'); + $this->cmd("ahoy cli 'vendor/bin/drush watchdog:show --count=30 || true'"); $this->logSubstep('DEBUG: head of homepage response'); - $this->cmd('ahoy cli "curl -sS -o - -w \"\\nHTTP_STATUS=%{http_code}\\n\" http://nginx:8080/ | head -c 4000"'); + $this->cmd("ahoy cli 'curl -sS -o - -w \"\\nHTTP_STATUS=%{http_code}\\n\" http://nginx:8080/ | head -c 4000'"); $this->logSubstep('Assert that homepage does not contain database dump content'); $this->assertWebpageNotContains('/', 'This demo page is sourced from the Vortex database dump file', 'Homepage should not show database dump content after fallback provision'); From b61032f45235af29032ff7e1ab70b1281c6ee30d Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 27 Apr 2026 19:27:29 +1000 Subject: [PATCH 7/9] Wrote diagnostic probe output to '.logs/' so it ships in test artifacts. --- .../phpunit/Functional/AhoyWorkflowTest.php | 101 ++++++++++-------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php index 4c029ab69..a57809e5c 100644 --- a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php +++ b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php @@ -531,51 +531,28 @@ public function testAhoyWorkflowProvisionFallbackToProfile(): void { $this->cmd('ahoy drush pm:list --status=enabled --type=module --format=list', '* shield', 'Shield module should be enabled after fallback provision'); // Diagnostic probes to understand why the homepage hits the redirect - // module after a fallback profile install. Each command is routed through - // 'ahoy cli' with a single-quoted outer arg so multi-word args (queries, - // eval expressions) survive ahoy's '$*' expansion intact. + // module after a fallback profile install. Each probe writes its full + // output to '.logs/debug-.txt' so it ships out as a test artifact + // (cmd()'s streaming output is suppressed in CI). Multi-word args route + // through 'ahoy cli' with a single-quoted outer arg so they survive + // ahoy's '$*' expansion intact. // @todo Remove once the underlying cause is understood and fixed. - $this->logSubstep('DEBUG: enabled modules per core.extension'); - $this->cmd("ahoy cli 'vendor/bin/drush pm:list --status=enabled --type=module --format=list'"); - - $this->logSubstep('DEBUG: core.extension config dump'); - $this->cmd("ahoy cli 'vendor/bin/drush config:get core.extension'"); - - $this->logSubstep('DEBUG: redirect module status (should be disabled after fallback)'); - $this->cmd("ahoy cli 'vendor/bin/drush pm:list --filter=redirect --format=list || true'"); - - $this->logSubstep('DEBUG: all DB tables'); - $this->cmd("ahoy cli 'vendor/bin/drush sql:query \"SHOW TABLES\"'"); - - $this->logSubstep('DEBUG: redirect-related DB tables (should be empty)'); - $this->cmd("ahoy cli 'vendor/bin/drush sql:query \"SHOW TABLES LIKE \\\"redirect%\\\"\" || true'"); - - $this->logSubstep('DEBUG: cache_container row count'); - $this->cmd("ahoy cli 'vendor/bin/drush sql:query \"SELECT COUNT(*) FROM cache_container\" || true'"); - - $this->logSubstep('DEBUG: PhpStorage layout under web/sites/default/files/php'); - $this->cmd("ahoy cli 'ls -la web/sites/default/files/php/ 2>/dev/null || echo NO_PHP_DIR'"); - $this->cmd("ahoy cli 'ls -la web/sites/default/files/php/container/ 2>/dev/null || echo NO_CONTAINER_DIR'"); - $this->cmd("ahoy cli 'find web/sites/default/files/php -type f 2>/dev/null | head -30 || echo NO_FILES'"); - - $this->logSubstep('DEBUG: presence of redirect references in cached container files'); - $this->cmd("ahoy cli 'grep -l \"Drupal..redirect\" web/sites/default/files/php/container/*.php 2>/dev/null || echo NO_REDIRECT_IN_CONTAINER_CACHE'"); - $this->cmd("ahoy cli 'grep -c \"redirect.request_subscriber\\|RedirectRequestSubscriber\" web/sites/default/files/php/container/*.php 2>/dev/null || echo NO_REDIRECT_SUBSCRIBER_IN_CACHE'"); - - $this->logSubstep('DEBUG: hash_salt and deployment_identifier'); - $this->cmd("ahoy cli 'vendor/bin/drush php:eval \"echo \\\"hash_salt=\\\" . substr(\\\\Drupal\\\\Core\\\\Site\\\\Settings::get(\\\"hash_salt\\\"), 0, 12) . PHP_EOL; echo \\\"deployment_identifier=\\\" . (\\\\Drupal\\\\Core\\\\Site\\\\Settings::get(\\\"deployment_identifier\\\") ?? \\\"(null)\\\") . PHP_EOL;\"'"); - - $this->logSubstep('DEBUG: runtime active modules per Drupal kernel'); - $this->cmd("ahoy cli 'vendor/bin/drush php:eval \"foreach (array_keys(\\\\Drupal::moduleHandler()->getModuleList()) as \\\$m) { echo \\\$m . PHP_EOL; }\"'"); - - $this->logSubstep('DEBUG: redirect/path_processor service ids in live container'); - $this->cmd("ahoy cli 'vendor/bin/drush php:eval \"foreach (\\\\Drupal::getContainer()->getServiceIds() as \\\$id) { if (str_contains(\\\$id, \\\"path_processor\\\") || str_contains(\\\$id, \\\"redirect\\\")) { echo \\\$id . PHP_EOL; } }\"'"); - - $this->logSubstep('DEBUG: last 30 watchdog entries'); - $this->cmd("ahoy cli 'vendor/bin/drush watchdog:show --count=30 || true'"); - - $this->logSubstep('DEBUG: head of homepage response'); - $this->cmd("ahoy cli 'curl -sS -o - -w \"\\nHTTP_STATUS=%{http_code}\\n\" http://nginx:8080/ | head -c 4000'"); + $logs_dir = self::locationsRoot() . '/.vortex/tests/.logs'; + @mkdir($logs_dir, 0777, TRUE); + + $this->captureProbe($logs_dir, '01-enabled-modules.txt', "ahoy cli 'vendor/bin/drush pm:list --status=enabled --type=module --format=list'"); + $this->captureProbe($logs_dir, '02-core-extension.txt', "ahoy cli 'vendor/bin/drush config:get core.extension'"); + $this->captureProbe($logs_dir, '03-redirect-module-status.txt', "ahoy cli 'vendor/bin/drush pm:list --filter=redirect --format=list || true'"); + $this->captureProbe($logs_dir, '04-show-tables.txt', "ahoy cli 'vendor/bin/drush sql:query \"SHOW TABLES\" || true'"); + $this->captureProbe($logs_dir, '05-redirect-tables.txt', "ahoy cli 'vendor/bin/drush sql:query \"SHOW TABLES LIKE \\\"redirect%\\\"\" || true'"); + $this->captureProbe($logs_dir, '06-cache-container-count.txt', "ahoy cli 'vendor/bin/drush sql:query \"SELECT COUNT(*) FROM cache_container\" || true'"); + $this->captureProbe($logs_dir, '07-phpstorage-ls-php.txt', "ahoy cli 'ls -la web/sites/default/files/php/ 2>/dev/null || echo NO_PHP_DIR'"); + $this->captureProbe($logs_dir, '08-phpstorage-ls-container.txt', "ahoy cli 'ls -la web/sites/default/files/php/container/ 2>/dev/null || echo NO_CONTAINER_DIR'"); + $this->captureProbe($logs_dir, '09-phpstorage-find.txt', "ahoy cli 'find web/sites/default/files/php -type f 2>/dev/null | head -50 || echo NO_FILES'"); + $this->captureProbe($logs_dir, '10-grep-redirect-in-cache.txt', "ahoy cli 'grep -l Drupal..redirect web/sites/default/files/php/container/*.php 2>/dev/null || echo NO_REDIRECT_IN_CONTAINER_CACHE'"); + $this->captureProbe($logs_dir, '11-grep-subscriber-in-cache.txt', "ahoy cli 'grep -c redirect.request_subscriber web/sites/default/files/php/container/*.php 2>/dev/null || echo NO_REDIRECT_SUBSCRIBER_IN_CACHE'"); + $this->captureProbe($logs_dir, '12-watchdog.txt', "ahoy cli 'vendor/bin/drush watchdog:show --count=30 || true'"); + $this->captureProbe($logs_dir, '13-homepage.txt', "ahoy cli 'curl -sS -o /tmp/homepage.html -w HTTP_STATUS=%{http_code} http://nginx:8080/ ; echo ; head -c 4000 /tmp/homepage.html'"); $this->logSubstep('Assert that homepage does not contain database dump content'); $this->assertWebpageNotContains('/', 'This demo page is sourced from the Vortex database dump file', 'Homepage should not show database dump content after fallback provision'); @@ -584,4 +561,40 @@ public function testAhoyWorkflowProvisionFallbackToProfile(): void { $this->assertWebpageContains('/', 'logSubstep('DEBUG probe -> ' . $filename); + + try { + $process = $this->processRun($command); + $payload = sprintf( + "COMMAND:\n %s\n\nEXIT CODE: %d\n\n--- STDOUT ---\n%s\n--- STDERR ---\n%s\n", + $command, + $process->getExitCode() ?? -1, + $process->getOutput(), + $process->getErrorOutput() + ); + } + catch (\Throwable $exception) { + $payload = sprintf( + "COMMAND:\n %s\n\nEXCEPTION: %s\n", + $command, + $exception->getMessage() + ); + } + + file_put_contents($logs_dir . '/debug-' . $filename, $payload); + } + } From 537abaa9a6e3f08908706dcf195c19a01f20e1ca Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 27 Apr 2026 19:57:34 +1000 Subject: [PATCH 8/9] Captured fallback provision output and added config-default probes. --- .../phpunit/Functional/AhoyWorkflowTest.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php index a57809e5c..37fe3c640 100644 --- a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php +++ b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php @@ -511,6 +511,9 @@ public function testAhoyWorkflowProvisionFallbackToProfile(): void { 'Provision without fallback should fail when no database dump is available', ); + $logs_dir = self::locationsRoot() . '/.vortex/tests/.logs'; + @mkdir($logs_dir, 0777, TRUE); + $this->logSubstep('Provision with fallback should succeed'); $this->fileAddVar('.env', 'VORTEX_PROVISION_FALLBACK_TO_PROFILE', 1); $this->syncToContainer(['.env']); @@ -527,6 +530,13 @@ public function testAhoyWorkflowProvisionFallbackToProfile(): void { tio: 15 * 60, ); + // DEBUG: capture the full output of the fallback provision run before any + // further probes run drush and rebuild the container. + file_put_contents( + $logs_dir . '/debug-00-provision-output.txt', + sprintf("--- STDOUT ---\n%s\n--- STDERR ---\n%s\n", $this->processGet()->getOutput(), $this->processGet()->getErrorOutput()) + ); + $this->logSubstep('Assert that Shield module is enabled'); $this->cmd('ahoy drush pm:list --status=enabled --type=module --format=list', '* shield', 'Shield module should be enabled after fallback provision'); @@ -537,9 +547,6 @@ public function testAhoyWorkflowProvisionFallbackToProfile(): void { // through 'ahoy cli' with a single-quoted outer arg so they survive // ahoy's '$*' expansion intact. // @todo Remove once the underlying cause is understood and fixed. - $logs_dir = self::locationsRoot() . '/.vortex/tests/.logs'; - @mkdir($logs_dir, 0777, TRUE); - $this->captureProbe($logs_dir, '01-enabled-modules.txt', "ahoy cli 'vendor/bin/drush pm:list --status=enabled --type=module --format=list'"); $this->captureProbe($logs_dir, '02-core-extension.txt', "ahoy cli 'vendor/bin/drush config:get core.extension'"); $this->captureProbe($logs_dir, '03-redirect-module-status.txt', "ahoy cli 'vendor/bin/drush pm:list --filter=redirect --format=list || true'"); @@ -551,8 +558,11 @@ public function testAhoyWorkflowProvisionFallbackToProfile(): void { $this->captureProbe($logs_dir, '09-phpstorage-find.txt', "ahoy cli 'find web/sites/default/files/php -type f 2>/dev/null | head -50 || echo NO_FILES'"); $this->captureProbe($logs_dir, '10-grep-redirect-in-cache.txt', "ahoy cli 'grep -l Drupal..redirect web/sites/default/files/php/container/*.php 2>/dev/null || echo NO_REDIRECT_IN_CONTAINER_CACHE'"); $this->captureProbe($logs_dir, '11-grep-subscriber-in-cache.txt', "ahoy cli 'grep -c redirect.request_subscriber web/sites/default/files/php/container/*.php 2>/dev/null || echo NO_REDIRECT_SUBSCRIBER_IN_CACHE'"); - $this->captureProbe($logs_dir, '12-watchdog.txt', "ahoy cli 'vendor/bin/drush watchdog:show --count=30 || true'"); + $this->captureProbe($logs_dir, '12-watchdog.txt', "ahoy cli 'vendor/bin/drush watchdog:show --count=200 || true'"); $this->captureProbe($logs_dir, '13-homepage.txt', "ahoy cli 'curl -sS -o /tmp/homepage.html -w HTTP_STATUS=%{http_code} http://nginx:8080/ ; echo ; head -c 4000 /tmp/homepage.html'"); + $this->captureProbe($logs_dir, '14-config-default-listing.txt', "ahoy cli 'ls -la ../config/default/ | head -80'"); + $this->captureProbe($logs_dir, '15-core-extension-yml.txt', "ahoy cli 'cat ../config/default/core.extension.yml 2>/dev/null || echo NO_CORE_EXTENSION_YML'"); + $this->captureProbe($logs_dir, '16-shield-status.txt', "ahoy cli 'vendor/bin/drush pm:list --filter=shield --format=list || true'"); $this->logSubstep('Assert that homepage does not contain database dump content'); $this->assertWebpageNotContains('/', 'This demo page is sourced from the Vortex database dump file', 'Homepage should not show database dump content after fallback provision'); From eb9d5114d9a32360a694f9dd91fa4cd951a1be39 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 27 Apr 2026 20:03:34 +1000 Subject: [PATCH 9/9] Flushed Redis after 'sql:drop' so the kernel rebuilds its container. --- .vortex/tests/phpunit/Functional/AhoyWorkflowTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php index 37fe3c640..6880b99be 100644 --- a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php +++ b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php @@ -498,6 +498,12 @@ public function testAhoyWorkflowProvisionFallbackToProfile(): void { $this->logSubstep('Drop the database to simulate a fresh environment'); $this->cmd('ahoy drush sql:drop -y', txt: 'Database should be dropped successfully'); + // Vortex's Redis settings put 'cache.container' on Redis, so the Drupal + // kernel reuses the cached container (built from the pre-drop module set) + // even after the DB is wiped. Flush Redis to force the kernel to rebuild + // its container from the freshly-installed modules. + $this->cmd('ahoy flush-redis', txt: 'Redis cache flushed so the kernel rebuilds its container'); + $this->logSubstep('Provision without fallback should fail'); $this->fileAddVar('.env', 'VORTEX_PROVISION_FALLBACK_TO_PROFILE', 0); $this->syncToContainer(['.env']);