From b2486e9f23534f669e801052b1e16465897da601 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 12:54:29 +1000 Subject: [PATCH 01/57] Restored the Lagoon database override flag to the state it was discovered in. Forward-ported from main 732619293de9e121e6d79cb21a9ce4f1612563b7. Re-implemented in the PHP tooling rewrite; BATS coverage replaced with PHPUnit. --- .vortex/tooling/src/vortex-deploy-lagoon | 173 ++++++++++++---- .../tooling/tests/Unit/DeployLagoonTest.php | 184 ++++++++++++++++-- 2 files changed, 294 insertions(+), 63 deletions(-) diff --git a/.vortex/tooling/src/vortex-deploy-lagoon b/.vortex/tooling/src/vortex-deploy-lagoon index 4d1a66aad..7759cd7f3 100755 --- a/.vortex/tooling/src/vortex-deploy-lagoon +++ b/.vortex/tooling/src/vortex-deploy-lagoon @@ -172,6 +172,119 @@ function lagoon_get_existing_environments(bool $is_branch): array { return is_array($names) ? $names : []; } +/** + * Discover the database import override flag on an environment. + * + * @param string $environment + * The environment name to read the variable from. + * + * @return array + * An array with the 'state' ('absent', 'present' or 'unknown'), and the + * 'value' and 'scope' of an existing flag. + */ +function lagoon_discover_override_db_flag(string $environment): array { + $flag = TASK( + 'Discovering a database import override flag.', + 'Completed database import override flag discovery.', + function () use ($environment): array { + $flag = ['state' => 'unknown', 'value' => '', 'scope' => 'global']; + + $exit_code = 0; + $json = lagoon_run_cli(sprintf('list variables --environment %s --reveal --output-json', escapeshellarg($environment)), $exit_code); + + if ($exit_code !== 0) { + // An unreadable variable list is not an absent flag. Adding one and + // deleting it afterwards would destroy a flag that already exists, so + // the flag is left alone. The build then runs against whatever value + // the environment already carries, which may not be the requested one. + NOTE('WARNING: Could not read environment variables. A database import override flag was left unchanged and may not match the requested deployment action.'); + + return $flag; + } + + $data = json_decode($json, TRUE); + if (!is_array($data) || !isset($data['data']) || !is_array($data['data'])) { + NOTE('WARNING: Could not read environment variables. A database import override flag was left unchanged and may not match the requested deployment action.'); + + return $flag; + } + + $flag['state'] = 'absent'; + + foreach ($data['data'] as $variable) { + if (!is_array($variable) || ($variable['name'] ?? NULL) !== 'VORTEX_PROVISION_OVERRIDE_DB') { + continue; + } + + $flag['state'] = 'present'; + $flag['value'] = (string) ($variable['value'] ?? ''); + $flag['scope'] = (string) ($variable['scope'] ?? 'global'); + break; + } + + if ($flag['state'] === 'absent') { + NOTE('No existing database import override flag found.'); + } + else { + NOTE('Found an existing database import override flag with value "%s" and scope "%s".', $flag['value'], $flag['scope']); + } + + return $flag; + }, + ); + + return is_array($flag) ? $flag : ['state' => 'unknown', 'value' => '', 'scope' => 'global']; +} + +/** + * Set the database import override flag for the duration of one build. + * + * @param string $environment + * The environment name to write the variable to. + * @param array $flag + * The flag as discovered by lagoon_discover_override_db_flag(). + * @param string $value + * The value this deployment needs while Lagoon queues the build. + */ +function lagoon_borrow_override_db_flag(string $environment, array $flag, string $value): void { + if ($flag['state'] === 'absent') { + TASK(sprintf('Adding a database import override flag with value %s.', $value), sprintf('Added a database import override flag with value %s.', $value), function () use ($environment, $value): void { + lagoon_run_cli(sprintf('add variable --environment %s --name VORTEX_PROVISION_OVERRIDE_DB --value %s --scope global', escapeshellarg($environment), escapeshellarg($value))); + }); + } + elseif ($flag['state'] === 'present') { + TASK(sprintf('Updating a database import override flag to %s.', $value), sprintf('Updated a database import override flag to %s.', $value), function () use ($environment, $flag, $value): void { + lagoon_run_cli(sprintf('update variable --environment %s --name VORTEX_PROVISION_OVERRIDE_DB --value %s --scope %s', escapeshellarg($environment), escapeshellarg($value), escapeshellarg($flag['scope']))); + }); + } +} + +/** + * Return the database import override flag to the state it was discovered in. + * + * @param string $environment + * The environment name to write the variable to. + * @param array $flag + * The flag as discovered by lagoon_discover_override_db_flag(). + */ +function lagoon_restore_override_db_flag(string $environment, array $flag): void { + // Lagoon reads the flag when it queues the build. + TASK('Waiting for deployment to be queued.', 'Waited for the deployment to be queued.', function (): void { + sleep_progress(10); + }); + + if ($flag['state'] === 'absent') { + TASK('Removing a database import override flag.', 'Removed a database import override flag.', function () use ($environment): void { + lagoon_run_cli(sprintf('delete variable --environment %s --name VORTEX_PROVISION_OVERRIDE_DB', escapeshellarg($environment))); + }); + } + elseif ($flag['state'] === 'present') { + TASK(sprintf('Restoring a database import override flag to %s.', $flag['value']), sprintf('Restored a database import override flag to %s.', $flag['value']), function () use ($environment, $flag): void { + lagoon_run_cli(sprintf('update variable --environment %s --name VORTEX_PROVISION_OVERRIDE_DB --value %s --scope %s', escapeshellarg($environment), escapeshellarg($flag['value']), escapeshellarg($flag['scope']))); + }); + } +} + /** * Handle Lagoon environment limit exceeded. * @@ -248,18 +361,14 @@ else { // Re-deployment of an existing environment. if ($is_redeploy) { - // Explicitly set DB overwrite flag to 0 due to a bug in Lagoon. - // @see https://github.com/uselagoon/lagoon/issues/1922 - // @todo Review and remove this workaround as the issue is fixed. - TASK('Setting a database overwrite flag to 0.', 'Set the database overwrite flag to 0.', function () use ($deploy_pr_full): void { - lagoon_run_cli(sprintf('update variable --environment %s --name VORTEX_PROVISION_OVERRIDE_DB --value 0 --scope global', escapeshellarg($deploy_pr_full))); - }); - - if ($deploy_action === 'deploy_override_db') { - TASK('Adding a database import override flag for the current deployment.', 'Added the database import override flag.', function () use ($deploy_pr_full): void { - lagoon_run_cli(sprintf('update variable --environment %s --name VORTEX_PROVISION_OVERRIDE_DB --value 1 --scope global', escapeshellarg($deploy_pr_full))); - }); - } + // The flag value this deployment needs while Lagoon queues the build. + $override_db_value = $deploy_action === 'deploy_override_db' ? '1' : '0'; + + // A deployment borrows the flag for a single build and then returns the + // environment to the state it was found in, so an environment that had no + // flag is not left with one. + $override_db_flag = lagoon_discover_override_db_flag($deploy_pr_full); + lagoon_borrow_override_db_flag($deploy_pr_full, $override_db_flag, $override_db_value); $deploy_cmd = sprintf('deploy pullrequest --number %s --base-branch-name %s --base-branch-ref %s --head-branch-name %s --head-branch-ref %s --title %s', escapeshellarg($deploy_pr), escapeshellarg($deploy_pr_base_branch), escapeshellarg('origin/' . $deploy_pr_base_branch), escapeshellarg($deploy_branch), escapeshellarg($deploy_pr_head), escapeshellarg($deploy_pr_full)); TASK(sprintf('Redeploying environment: project %s, PR: %s.', $lagoon_project, $deploy_pr), 'Requested the environment redeployment.', function () use ($deploy_cmd, &$exit_code, &$deploy_error): void { @@ -270,15 +379,7 @@ else { } }, fatal: FALSE); - if ($deploy_action === 'deploy_override_db') { - TASK('Waiting for deployment to be queued.', 'Waited for the deployment to be queued.', function (): void { - sleep_progress(10); - }); - - TASK('Removing a database import override flag for the current deployment.', 'Removed the database import override flag.', function () use ($deploy_pr_full): void { - lagoon_run_cli(sprintf('update variable --environment %s --name VORTEX_PROVISION_OVERRIDE_DB --value 0 --scope global', escapeshellarg($deploy_pr_full))); - }); - } + lagoon_restore_override_db_flag($deploy_pr_full, $override_db_flag); } // Fresh deployment. else { @@ -300,18 +401,14 @@ else { // Re-deployment of an existing environment. if ($is_redeploy) { - // Explicitly set DB overwrite flag to 0 due to a bug in Lagoon. - // @see https://github.com/uselagoon/lagoon/issues/1922 - // @todo Review and remove this workaround as the issue is fixed. - TASK('Setting a database overwrite flag to 0.', 'Set the database overwrite flag to 0.', function () use ($deploy_branch): void { - lagoon_run_cli(sprintf('update variable --environment %s --name VORTEX_PROVISION_OVERRIDE_DB --value 0 --scope global', escapeshellarg($deploy_branch))); - }); - - if ($deploy_action === 'deploy_override_db') { - TASK('Adding a database import override flag for the current deployment.', 'Added the database import override flag.', function () use ($deploy_branch): void { - lagoon_run_cli(sprintf('update variable --environment %s --name VORTEX_PROVISION_OVERRIDE_DB --value 1 --scope global', escapeshellarg($deploy_branch))); - }); - } + // The flag value this deployment needs while Lagoon queues the build. + $override_db_value = $deploy_action === 'deploy_override_db' ? '1' : '0'; + + // A deployment borrows the flag for a single build and then returns the + // environment to the state it was found in, so an environment that had no + // flag is not left with one. + $override_db_flag = lagoon_discover_override_db_flag($deploy_branch); + lagoon_borrow_override_db_flag($deploy_branch, $override_db_flag, $override_db_value); $deploy_cmd = sprintf('deploy latest --environment %s', escapeshellarg($deploy_branch)); TASK(sprintf('Redeploying environment: project %s, branch: %s.', $lagoon_project, $deploy_branch), 'Requested the environment redeployment.', function () use ($deploy_cmd, &$exit_code, &$deploy_error): void { @@ -322,15 +419,7 @@ else { } }, fatal: FALSE); - if ($deploy_action === 'deploy_override_db') { - TASK('Waiting for deployment to be queued.', 'Waited for the deployment to be queued.', function (): void { - sleep_progress(10); - }); - - TASK('Removing a database import override flag for the current deployment.', 'Removed the database import override flag.', function () use ($deploy_branch): void { - lagoon_run_cli(sprintf('update variable --environment %s --name VORTEX_PROVISION_OVERRIDE_DB --value 0 --scope global', escapeshellarg($deploy_branch))); - }); - } + lagoon_restore_override_db_flag($deploy_branch, $override_db_flag); } // Fresh deployment. else { diff --git a/.vortex/tooling/tests/Unit/DeployLagoonTest.php b/.vortex/tooling/tests/Unit/DeployLagoonTest.php index 2345ea454..1b6136f98 100644 --- a/.vortex/tooling/tests/Unit/DeployLagoonTest.php +++ b/.vortex/tooling/tests/Unit/DeployLagoonTest.php @@ -188,8 +188,14 @@ public function testBranchRedeployment(): void { ]); $this->mockPassthru([ - 'cmd' => $this->getLagoonCommand("update variable --environment 'develop' --name VORTEX_PROVISION_OVERRIDE_DB --value 0 --scope global"), - 'output' => 'Variable updated', + 'cmd' => $this->getLagoonCommand("list variables --environment 'develop' --reveal --output-json"), + 'output' => '{"data":[{"name":"OTHER_VARIABLE","value":"other","scope":"global"}]}', + 'result_code' => 0, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getLagoonCommand("add variable --environment 'develop' --name VORTEX_PROVISION_OVERRIDE_DB --value '0' --scope global"), + 'output' => 'Variable added', 'result_code' => 0, ]); @@ -199,11 +205,19 @@ public function testBranchRedeployment(): void { 'result_code' => 0, ]); + $this->mockPassthru([ + 'cmd' => $this->getLagoonCommand("delete variable --environment 'develop' --name VORTEX_PROVISION_OVERRIDE_DB"), + 'output' => 'Variable deleted', + 'result_code' => 0, + ]); + $output = $this->runScript('src/vortex-deploy-lagoon'); $this->assertStringContainsString('Found already deployed environment for branch "develop".', $output); - $this->assertStringContainsString('Setting a database overwrite flag to 0.', $output); + $this->assertStringContainsString('No existing database import override flag found.', $output); + $this->assertStringContainsString('Adding a database import override flag with value 0.', $output); $this->assertStringContainsString('Redeploying environment: project test-project, branch: develop.', $output); + $this->assertStringContainsString('Removing a database import override flag.', $output); $this->assertStringContainsString('Finished Lagoon deployment.', $output); } @@ -246,13 +260,13 @@ public function testBranchRedeploymentWithDbOverride(): void { ]); $this->mockPassthru([ - 'cmd' => $this->getLagoonCommand("update variable --environment 'develop' --name VORTEX_PROVISION_OVERRIDE_DB --value 0 --scope global"), - 'output' => 'Variable updated', + 'cmd' => $this->getLagoonCommand("list variables --environment 'develop' --reveal --output-json"), + 'output' => '{"data":[{"name":"VORTEX_PROVISION_OVERRIDE_DB","value":"0","scope":"build"}]}', 'result_code' => 0, ]); $this->mockPassthru([ - 'cmd' => $this->getLagoonCommand("update variable --environment 'develop' --name VORTEX_PROVISION_OVERRIDE_DB --value 1 --scope global"), + 'cmd' => $this->getLagoonCommand("update variable --environment 'develop' --name VORTEX_PROVISION_OVERRIDE_DB --value '1' --scope 'build'"), 'output' => 'Variable updated', 'result_code' => 0, ]); @@ -264,16 +278,17 @@ public function testBranchRedeploymentWithDbOverride(): void { ]); $this->mockPassthru([ - 'cmd' => $this->getLagoonCommand("update variable --environment 'develop' --name VORTEX_PROVISION_OVERRIDE_DB --value 0 --scope global"), + 'cmd' => $this->getLagoonCommand("update variable --environment 'develop' --name VORTEX_PROVISION_OVERRIDE_DB --value '0' --scope 'build'"), 'output' => 'Variable updated', 'result_code' => 0, ]); $output = $this->runScript('src/vortex-deploy-lagoon'); - $this->assertStringContainsString('Adding a database import override flag for the current deployment.', $output); + $this->assertStringContainsString('Found an existing database import override flag with value "0" and scope "build".', $output); + $this->assertStringContainsString('Updating a database import override flag to 1.', $output); $this->assertStringContainsString('Waiting for deployment to be queued.', $output); - $this->assertStringContainsString('Removing a database import override flag for the current deployment.', $output); + $this->assertStringContainsString('Restoring a database import override flag to 0.', $output); $this->assertStringContainsString('Finished Lagoon deployment.', $output); } @@ -376,8 +391,14 @@ public function testPrRedeployment(): void { ]); $this->mockPassthru([ - 'cmd' => $this->getLagoonCommand("update variable --environment 'pr-123' --name VORTEX_PROVISION_OVERRIDE_DB --value 0 --scope global"), - 'output' => 'Variable updated', + 'cmd' => $this->getLagoonCommand("list variables --environment 'pr-123' --reveal --output-json"), + 'output' => '{"data":[]}', + 'result_code' => 0, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getLagoonCommand("add variable --environment 'pr-123' --name VORTEX_PROVISION_OVERRIDE_DB --value '0' --scope global"), + 'output' => 'Variable added', 'result_code' => 0, ]); @@ -387,11 +408,19 @@ public function testPrRedeployment(): void { 'result_code' => 0, ]); + $this->mockPassthru([ + 'cmd' => $this->getLagoonCommand("delete variable --environment 'pr-123' --name VORTEX_PROVISION_OVERRIDE_DB"), + 'output' => 'Variable deleted', + 'result_code' => 0, + ]); + $output = $this->runScript('src/vortex-deploy-lagoon'); $this->assertStringContainsString('Found already deployed environment for PR "123".', $output); - $this->assertStringContainsString('Setting a database overwrite flag to 0.', $output); + $this->assertStringContainsString('No existing database import override flag found.', $output); + $this->assertStringContainsString('Adding a database import override flag with value 0.', $output); $this->assertStringContainsString('Redeploying environment: project test-project, PR: 123.', $output); + $this->assertStringContainsString('Removing a database import override flag.', $output); $this->assertStringContainsString('Finished Lagoon deployment.', $output); } @@ -680,16 +709,16 @@ public function testPrRedeploymentWithDbOverride(): void { 'result_code' => 0, ]); - // Set DB overwrite flag to 0. + // Read the flag the environment already carries. $this->mockPassthru([ - 'cmd' => $this->getLagoonCommand("update variable --environment 'pr-123' --name VORTEX_PROVISION_OVERRIDE_DB --value 0 --scope global"), - 'output' => 'Variable updated', + 'cmd' => $this->getLagoonCommand("list variables --environment 'pr-123' --reveal --output-json"), + 'output' => '{"data":[{"name":"VORTEX_PROVISION_OVERRIDE_DB","value":"1","scope":"global"}]}', 'result_code' => 0, ]); - // Set DB overwrite flag to 1 for this deployment. + // Borrow the flag for this deployment. $this->mockPassthru([ - 'cmd' => $this->getLagoonCommand("update variable --environment 'pr-123' --name VORTEX_PROVISION_OVERRIDE_DB --value 1 --scope global"), + 'cmd' => $this->getLagoonCommand("update variable --environment 'pr-123' --name VORTEX_PROVISION_OVERRIDE_DB --value '1' --scope 'global'"), 'output' => 'Variable updated', 'result_code' => 0, ]); @@ -701,9 +730,9 @@ public function testPrRedeploymentWithDbOverride(): void { 'result_code' => 0, ]); - // Reset DB overwrite flag after deployment. + // Return the flag to the value it was discovered with. $this->mockPassthru([ - 'cmd' => $this->getLagoonCommand("update variable --environment 'pr-123' --name VORTEX_PROVISION_OVERRIDE_DB --value 0 --scope global"), + 'cmd' => $this->getLagoonCommand("update variable --environment 'pr-123' --name VORTEX_PROVISION_OVERRIDE_DB --value '1' --scope 'global'"), 'output' => 'Variable updated', 'result_code' => 0, ]); @@ -711,10 +740,123 @@ public function testPrRedeploymentWithDbOverride(): void { $output = $this->runScript('src/vortex-deploy-lagoon'); $this->assertStringContainsString('Found already deployed environment for PR "123".', $output); - $this->assertStringContainsString('Adding a database import override flag for the current deployment.', $output); + $this->assertStringContainsString('Found an existing database import override flag with value "1" and scope "global".', $output); + $this->assertStringContainsString('Updating a database import override flag to 1.', $output); $this->assertStringContainsString('Redeploying environment: project test-project, PR: 123.', $output); $this->assertStringContainsString('Waiting for deployment to be queued.', $output); - $this->assertStringContainsString('Removing a database import override flag for the current deployment.', $output); + $this->assertStringContainsString('Restoring a database import override flag to 1.', $output); + $this->assertStringContainsString('Finished Lagoon deployment.', $output); + } + + public function testRedeploymentWithUnreadableVariables(): void { + $this->createFakeLagoonBinary(); + + $this->mockQuit(0); + + $this->expectException(QuitSuccessException::class); + + $this->mockPassthru([ + 'cmd' => $this->getSetupSshPath(), + 'output' => 'SSH setup complete', + 'result_code' => 0, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getLagoonConfigAddCommand(), + 'output' => 'Config added', + 'result_code' => 0, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getVersionCommand(), + 'output' => 'v0.32.0', + 'result_code' => 0, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getLagoonCommand('whoami'), + 'output' => 'tester', + 'result_code' => 0, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getLagoonCommand('list environments --output-json --pretty'), + 'output' => '{"data":[{"name":"develop","deploytype":"branch"}]}', + 'result_code' => 0, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getLagoonCommand("list variables --environment 'develop' --reveal --output-json"), + 'output' => 'Error: permission denied', + 'result_code' => 1, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getLagoonCommand("deploy latest --environment 'develop'"), + 'output' => 'Deploy queued', + 'result_code' => 0, + ]); + + $output = $this->runScript('src/vortex-deploy-lagoon'); + + $this->assertStringContainsString('WARNING: Could not read environment variables.', $output); + $this->assertStringNotContainsString('Adding a database import override flag', $output); + $this->assertStringNotContainsString('Removing a database import override flag', $output); + $this->assertStringContainsString('Finished Lagoon deployment.', $output); + } + + public function testRedeploymentWithMalformedVariables(): void { + $this->createFakeLagoonBinary(); + + $this->mockQuit(0); + + $this->expectException(QuitSuccessException::class); + + $this->mockPassthru([ + 'cmd' => $this->getSetupSshPath(), + 'output' => 'SSH setup complete', + 'result_code' => 0, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getLagoonConfigAddCommand(), + 'output' => 'Config added', + 'result_code' => 0, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getVersionCommand(), + 'output' => 'v0.32.0', + 'result_code' => 0, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getLagoonCommand('whoami'), + 'output' => 'tester', + 'result_code' => 0, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getLagoonCommand('list environments --output-json --pretty'), + 'output' => '{"data":[{"name":"develop","deploytype":"branch"}]}', + 'result_code' => 0, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getLagoonCommand("list variables --environment 'develop' --reveal --output-json"), + 'output' => 'not json', + 'result_code' => 0, + ]); + + $this->mockPassthru([ + 'cmd' => $this->getLagoonCommand("deploy latest --environment 'develop'"), + 'output' => 'Deploy queued', + 'result_code' => 0, + ]); + + $output = $this->runScript('src/vortex-deploy-lagoon'); + + $this->assertStringContainsString('WARNING: Could not read environment variables.', $output); $this->assertStringContainsString('Finished Lagoon deployment.', $output); } From d74842ab043a3c8f694352961d740463f81c5612 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2026 02:14:29 +0000 Subject: [PATCH 02/57] Update toshimaru/auto-author-assign action to v3.1.0 (#2976) | datasource | package | from | to | | ----------- | ---------------------------- | ------ | ------ | | github-tags | toshimaru/auto-author-assign | v3.0.3 | v3.1.0 | (cherry picked from commit 7b6cdaf782942d4ea94fea1290ee0db2816cbc81) --- .github/workflows/assign-author.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/assign-author.yml b/.github/workflows/assign-author.yml index e92231599..fdff9b652 100644 --- a/.github/workflows/assign-author.yml +++ b/.github/workflows/assign-author.yml @@ -15,4 +15,4 @@ jobs: steps: - name: Assign author - uses: toshimaru/auto-author-assign@3e19bfc990cb1cf0589dce95e9f75289bb1e22de # v3.0.3 + uses: toshimaru/auto-author-assign@a78a94b219445cece8ccf0d45fec60af449f212a # v3.1.0 From 27ba697c8e37a13841477faa38aed4be42a41d87 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 12:55:30 +1000 Subject: [PATCH 03/57] Raised the InnoDB redo log capacity so large database dumps import. (cherry picked from commit 1ffc2e85c6df5d0c4d519a266c6c02459eaf2cdc) Kept the 2.x 'subtestSolr()' name; fixtures regenerated separately. --- .docker/config/database/my.cnf | 10 +++++++++- .docker/database.dockerfile | 5 +++-- .../Functional/DockerComposeWorkflowTest.php | 2 ++ .../Traits/Subtests/SubtestDockerComposeTrait.php | 15 +++++++++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.docker/config/database/my.cnf b/.docker/config/database/my.cnf index 27b23b9b0..85f1bf9c3 100644 --- a/.docker/config/database/my.cnf +++ b/.docker/config/database/my.cnf @@ -1 +1,9 @@ -# Custom configuration for database clients. +# Custom configuration for the database service. + +[mysqld] +# The image default of 128MB is exhausted during a large dump import faster +# than the log checkpointer reclaims it, aborting the import with +# "ERROR 1114 ... table is full". +# MariaDB images abort at startup on this MySQL-only variable, so the +# "loose-" prefix downgrades that to a warning. +loose-innodb_redo_log_capacity = 1073741824 diff --git a/.docker/database.dockerfile b/.docker/database.dockerfile index a59f3a6da..7395267c7 100644 --- a/.docker/database.dockerfile +++ b/.docker/database.dockerfile @@ -10,8 +10,9 @@ FROM ${IMAGE} # hadolint ignore=DL3066 # named account provided by the base image USER root -COPY ./.docker/config/database/my.cnf /etc/my.cnf.d/server.cnf -RUN fix-permissions /etc/my.cnf.d/ +COPY ./.docker/config/database/my.cnf /etc/mysql/conf.d/server.cnf +# The entrypoint rewrites files in this directory before starting the server. +RUN fix-permissions /etc/mysql/conf.d/ # hadolint ignore=DL3064 # local development credentials only ENV MYSQL_DATABASE=drupal \ diff --git a/.vortex/tests/phpunit/Functional/DockerComposeWorkflowTest.php b/.vortex/tests/phpunit/Functional/DockerComposeWorkflowTest.php index c258a9c97..5f507adb0 100644 --- a/.vortex/tests/phpunit/Functional/DockerComposeWorkflowTest.php +++ b/.vortex/tests/phpunit/Functional/DockerComposeWorkflowTest.php @@ -40,6 +40,8 @@ public function testDockerComposeWorkflowFull(): void { $this->subtestDockerComposeDrushPhpIni(); + $this->subtestDockerComposeDatabaseConfig(); + $this->subtestSolr(); $this->logSubstep('Installing development dependencies'); diff --git a/.vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php b/.vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php index 6ebbb0c81..f20f68205 100644 --- a/.vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php +++ b/.vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php @@ -322,6 +322,21 @@ protected function subtestDockerComposeDrushPhpIni(): void { $this->logStepFinish(); } + protected function subtestDockerComposeDatabaseConfig(): void { + $this->logStepStart(); + + // Asserting the effective value rather than the file contents: the config + // file is copied to a path the database image reads only if the copy + // destination in the Dockerfile matches the engine's include directory. + $this->cmd( + 'docker compose exec -T database mysql -udrupal -pdrupal -e "SHOW VARIABLES LIKE \'innodb_redo_log_capacity\';"', + '1073741824', + 'Database applies the InnoDB redo log capacity from the shipped my.cnf' + ); + + $this->logStepFinish(); + } + protected function subtestSolr(): void { $this->logStepStart(); From 144b58bf69250bc9567215b39c9c5ba6314877de Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 12:56:17 +1000 Subject: [PATCH 04/57] Wired the remaining 'behat-steps' traits into 'FeatureContext'. (cherry picked from commit bee1d7e22bd768147cc6fa32179e9e4ff37d665d) Bumped 'drevops/behat-steps' to '^3.13.0' so the added traits resolve; fixtures regenerated separately. --- .../Handlers/ModulesHandlerProcessTest.php | 5 +- .../Traits/Subtests/SubtestAhoyTrait.php | 2 +- composer.json | 3 +- tests/behat/bootstrap/FeatureContext.php | 18 +++++++ tests/behat/features/behat.feature | 52 +++++++++++++++++++ tests/behat/features/redirect.feature | 36 +++++++++++++ tests/behat/features/xmlsitemap.feature | 5 +- tests/behat/fixtures/response.json | 13 +++++ tests/behat/fixtures/response.xml | 11 ++++ 9 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 tests/behat/features/redirect.feature create mode 100644 tests/behat/fixtures/response.json create mode 100644 tests/behat/fixtures/response.xml diff --git a/.vortex/cli/tests/Functional/Handlers/ModulesHandlerProcessTest.php b/.vortex/cli/tests/Functional/Handlers/ModulesHandlerProcessTest.php index 0a5bec3a5..cd84923af 100644 --- a/.vortex/cli/tests/Functional/Handlers/ModulesHandlerProcessTest.php +++ b/.vortex/cli/tests/Functional/Handlers/ModulesHandlerProcessTest.php @@ -70,7 +70,10 @@ public static function dataProviderHandlerProcess(): \Iterator { static::cw(function ($test): void { $test->prompts[Modules::id()] = static::getModulesExcept('redirect'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('drupal/redirect')), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'drupal/redirect', + 'RedirectTrait', + ])), ]; yield 'modules_no_reroute_email' => [ static::cw(function ($test): void { diff --git a/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php b/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php index 0a8601ac5..e01b8a9d4 100644 --- a/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php +++ b/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php @@ -613,7 +613,7 @@ protected function subtestAhoyTestBdd(string $webroot = 'web'): void { $this->logSubstep('Run all BDD tests'); - $this->cmd('ahoy test-bdd', tio: 120, ito: 90); + $this->cmd('ahoy test-bdd', tio: 240, ito: 90); $this->syncToHost('.logs'); $this->assertDirectoryExists('.logs/screenshots'); $this->assertFileExists('.logs/screenshots/behat-test-screenshot.html'); diff --git a/composer.json b/composer.json index 389e486c1..b262dcb35 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "dealerdirect/phpcodesniffer-composer-installer": "^1.2.1", "drevops/behat-format-progress-fail": "^1.5.1", "drevops/behat-screenshot": "^2.4.2", - "drevops/behat-steps": "^3.12.0", + "drevops/behat-steps": "^3.13.0", "drevops/phpcs-standard": "^0.7.0", "drupal/coder": "^9@alpha", "drupal/drupal-extension": "^6.1", @@ -62,6 +62,7 @@ "phpunit/phpunit": "^11.5.56", "pyrech/composer-changelogs": "^2.2", "rector/rector": "^2.6.1", + "softcreatr/jsonpath": "^0.10 || ^1.0", "vincentlanglet/twig-cs-fixer": "^4.0.2" }, "conflict": { diff --git a/tests/behat/bootstrap/FeatureContext.php b/tests/behat/bootstrap/FeatureContext.php index 873f743cc..77a4d17f0 100644 --- a/tests/behat/bootstrap/FeatureContext.php +++ b/tests/behat/bootstrap/FeatureContext.php @@ -11,6 +11,7 @@ use DrevOps\BehatSteps\CookieTrait; use DrevOps\BehatSteps\DateTrait; use DrevOps\BehatSteps\Drupal\BlockTrait; +use DrevOps\BehatSteps\Drupal\CacheTrait; use DrevOps\BehatSteps\Drupal\ContentBlockTrait; use DrevOps\BehatSteps\Drupal\ContentTrait; use DrevOps\BehatSteps\Drupal\DraggableviewsTrait; @@ -22,6 +23,9 @@ use DrevOps\BehatSteps\MetatagTrait; use DrevOps\BehatSteps\Drupal\OverrideTrait; use DrevOps\BehatSteps\Drupal\ParagraphsTrait; +// phpcs:ignore #;< MODULE_REDIRECT +use DrevOps\BehatSteps\Drupal\RedirectTrait; +// phpcs:ignore #;> MODULE_REDIRECT use DrevOps\BehatSteps\Drupal\SearchApiTrait; use DrevOps\BehatSteps\Drupal\TaxonomyTrait; use DrevOps\BehatSteps\Drupal\TestmodeTrait; @@ -30,12 +34,17 @@ use DrevOps\BehatSteps\ElementTrait; use DrevOps\BehatSteps\FieldTrait; use DrevOps\BehatSteps\FileDownloadTrait; +use DrevOps\BehatSteps\IframeTrait; use DrevOps\BehatSteps\JavascriptTrait; +use DrevOps\BehatSteps\JsonTrait; use DrevOps\BehatSteps\KeyboardTrait; use DrevOps\BehatSteps\LinkTrait; use DrevOps\BehatSteps\PathTrait; use DrevOps\BehatSteps\ResponseTrait; +use DrevOps\BehatSteps\ResponsiveTrait; +use DrevOps\BehatSteps\RestTrait; use DrevOps\BehatSteps\WaitTrait; +use DrevOps\BehatSteps\XmlTrait; use Drupal\DrupalExtension\Context\DrupalContext; /** @@ -45,6 +54,7 @@ class FeatureContext extends DrupalContext { use AccessibilityTrait; use BlockTrait; + use CacheTrait; use ContentBlockTrait; use ContentTrait; use CookieTrait; @@ -56,7 +66,9 @@ class FeatureContext extends DrupalContext { use FieldTrait; use FileDownloadTrait; use FileTrait; + use IframeTrait; use JavascriptTrait; + use JsonTrait; use KeyboardTrait; use LinkTrait; use MediaTrait; @@ -65,12 +77,18 @@ class FeatureContext extends DrupalContext { use OverrideTrait; use ParagraphsTrait; use PathTrait; + // phpcs:ignore #;< MODULE_REDIRECT + use RedirectTrait; + // phpcs:ignore #;> MODULE_REDIRECT use ResponseTrait; + use ResponsiveTrait; + use RestTrait; use SearchApiTrait; use TaxonomyTrait; use TestmodeTrait; use UserTrait; use WaitTrait; use WatchdogTrait; + use XmlTrait; } diff --git a/tests/behat/features/behat.feature b/tests/behat/features/behat.feature index 210ded2d5..839a47028 100644 --- a/tests/behat/features/behat.feature +++ b/tests/behat/features/behat.feature @@ -19,6 +19,58 @@ Feature: Behat configuration And I go to "/user/login" Then the path should be "/user/login" + @api @javascript @breakpoint:mobile_portrait + Scenario: Viewport is resized from a tag and from a step + Given I am an anonymous user + When I go to the homepage + And I set the viewport to the "tablet_landscape" breakpoint + And I set the viewport to "1920" by "1080" + And I go to "/user/login" + Then the path should be "/user/login" + + @api + Scenario: REST requests are sent and asserted + Given a REST header "Accept" with value "text/html" + When I send a REST "GET" request to "/user/login" + Then the REST response status code should be 200 + And the REST response should contain "user-login-form" + + @api + Scenario: XML responses are asserted + Given the response content from the file "response.xml" + Then the response should be in XML format + And the XML should use the namespace "https://example.com/meta" + And the XML element "//items" should have "2" elements + And the XML element "//item[@id='1']/title" should be equal to "First item" + And the XML element "//item[2]/title" should contain "Second" + And the XML attribute "id" on element "//item[2]" should be equal to "2" + And the XML element "//missing" should not exist + + @api + Scenario: JSON responses are asserted + Given the response JSON from the file "response.json" + Then the response should be in JSON format + And the JSON path "$.status" should be equal to "ok" + And the JSON path "$.items" should have "2" elements + And the JSON path "$.items[0].title" should be equal to "First item" + And the JSON path "$.items[*].id" should exist + And the JSON path "$.missing" should not exist + And the response should match the following JSON schema: + """ + { + "type": "object", + "required": ["status", "items"] + } + """ + + @api + Scenario: Caches are invalidated from within a scenario + Given the page cache for the path "/" has been cleared + And the render cache has been cleared + And I am an anonymous user + When I go to the homepage + Then the response status code should be 200 + @api Scenario: Drush integration works Given I run drush "status" diff --git a/tests/behat/features/redirect.feature b/tests/behat/features/redirect.feature new file mode 100644 index 000000000..b220441ef --- /dev/null +++ b/tests/behat/features/redirect.feature @@ -0,0 +1,36 @@ +@redirect @p1 +Feature: Redirects + + As a site owner + I want to ensure that redirects are created and followed + In order to keep old URLs working after content is moved + + @api + Scenario: Redirects are created with an explicit and a default status code + Given the following redirects exist: + | from | to | status_code | + | /old-login | /user/login | 301 | + | /legacy/sign-in | /user/login | | + Then the following redirects should exist: + | from | to | status_code | + | /old-login | /user/login | 301 | + | /legacy/sign-in | /user/login | 301 | + + @api + Scenario: A created redirect sends the visitor to the destination + Given the following redirects exist: + | from | to | status_code | + | /old-login | /user/login | 301 | + And I am an anonymous user + When I go to "/old-login" + Then the path should be "/user/login" + + @api + Scenario: A deleted redirect no longer exists + Given the following redirects exist: + | from | to | + | /temp-login | /user/login | + And the following redirects do not exist: + | /temp-login | + Then the following redirects should not exist: + | /temp-login | diff --git a/tests/behat/features/xmlsitemap.feature b/tests/behat/features/xmlsitemap.feature index d599935ce..c26b2745a 100644 --- a/tests/behat/features/xmlsitemap.feature +++ b/tests/behat/features/xmlsitemap.feature @@ -11,4 +11,7 @@ Feature: XML Sitemap And I am an anonymous user When I go to "/sitemap.xml" Then the response status code should be 200 - And the response should contain "urlset" + And the response should be in XML format + And the XML should use the namespace "http://www.sitemaps.org/schemas/sitemap/0.9" + And the XML element "//*[local-name()='urlset']" should exist + And the XML element "//*[local-name()='url']/*[local-name()='loc']" should exist diff --git a/tests/behat/fixtures/response.json b/tests/behat/fixtures/response.json new file mode 100644 index 000000000..adc158778 --- /dev/null +++ b/tests/behat/fixtures/response.json @@ -0,0 +1,13 @@ +{ + "status": "ok", + "items": [ + { + "id": 1, + "title": "First item" + }, + { + "id": 2, + "title": "Second item" + } + ] +} diff --git a/tests/behat/fixtures/response.xml b/tests/behat/fixtures/response.xml new file mode 100644 index 000000000..5985d78a2 --- /dev/null +++ b/tests/behat/fixtures/response.xml @@ -0,0 +1,11 @@ + + + + + First item + + + Second item + + + From f630d0728d8bea55d1add3f95f096c2138d027a6 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 12:56:33 +1000 Subject: [PATCH 05/57] Raised the default Drush 'memory_limit' to 1G. (cherry picked from commit f8f5cd5166e3b358eb01d8c5ff45d9209af374bc) Fixtures regenerated separately. --- .vortex/docs/content/tools/drush.mdx | 51 +++++++++++++++---- .../Subtests/SubtestDockerComposeTrait.php | 12 ++--- drush/php-ini/drush.ini | 6 ++- 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/.vortex/docs/content/tools/drush.mdx b/.vortex/docs/content/tools/drush.mdx index 33c0c18a3..a4a8285b4 100644 --- a/.vortex/docs/content/tools/drush.mdx +++ b/.vortex/docs/content/tools/drush.mdx @@ -43,22 +43,16 @@ import TabItem from '@theme/TabItem'; ## PHP runtime configuration -Vortex allows you to customize PHP runtime settings for Drush CLI commands -by editing the `drush/php-ini/drush.ini` file. +**Vortex** allows you to customize PHP runtime settings for PHP CLI processes, +including Drush commands, by editing the `drush/php-ini/drush.ini` file. This file is auto-discovered via the `PHP_INI_SCAN_DIR` environment variable, which is pre-configured in both the Docker CLI container and Acquia deployment -hooks. - -For example, to increase the memory limit for Drush operations: - -```ini -; drush/php-ini/drush.ini -memory_limit = 512M -``` +hooks. Every PHP CLI process that loads this scan path picks the file up, not +only Drush. Any valid PHP ini directive can be added to this file to adjust the runtime -behavior of Drush commands. +behavior of those processes. :::note @@ -68,6 +62,41 @@ overrides (such as `sendmail_path`) are not lost. ::: +### Memory limit + +The shipped configuration raises the PHP memory limit above the platform +default: + +```ini +; drush/php-ini/drush.ini +memory_limit = 1G +``` + +This value is sized for database imports and configuration synchronization on +large sites. It applies to PHP CLI processes that load this scan path, +including Drush, and does not change the memory limit used to serve web +requests. + +If a Drush command exhausts this limit, profile the command before raising the +value. An exhausted 1G limit usually points to a memory leak in custom code +rather than a limit set too low, and raising the ceiling hides the leak until a +larger dataset reaches the new limit. + +To grant more memory to a single command without changing the default: + + + + ```shell + ahoy cli php -d memory_limit=2G vendor/bin/drush + ``` + + + ```shell + docker compose exec cli php -d memory_limit=2G vendor/bin/drush + ``` + + + ## Aliases For Lagoon hosting, Drush site aliases are automatically generated for each diff --git a/.vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php b/.vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php index f20f68205..5f80267b0 100644 --- a/.vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php +++ b/.vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php @@ -282,11 +282,11 @@ protected function subtestDockerComposeDrushPhpIni(): void { $this->logSubstep('Assert default PHP ini values from drush.ini are applied.'); $this->assertFileExists($ini_file, 'Drush PHP ini file should exist'); - $this->assertFileContainsString($ini_file, 'memory_limit = 512M', 'Drush PHP ini file should contain default memory_limit'); + $this->assertFileContainsString($ini_file, 'memory_limit = 1G', 'Drush PHP ini file should contain default memory_limit'); $this->cmd( 'docker compose exec -T cli php -r "echo ini_get(\'memory_limit\');"', - '512M', - 'PHP memory_limit should be 512M from drush.ini' + '1G', + 'PHP memory_limit should be 1G from drush.ini' ); $this->logSubstep('Assert PHP ini values are updated when drush.ini is changed.'); @@ -300,12 +300,12 @@ protected function subtestDockerComposeDrushPhpIni(): void { ); $this->logSubstep('Assert multiple PHP ini directives are applied.'); - File::dump($ini_file, "memory_limit = 1024M\nerror_reporting = E_ALL\n"); + File::dump($ini_file, "memory_limit = 2048M\nerror_reporting = E_ALL\n"); $this->syncToContainer($ini_file); $this->cmd( 'docker compose exec -T cli php -r "echo ini_get(\'memory_limit\');"', - '1024M', - 'PHP memory_limit should be 1024M after second change' + '2048M', + 'PHP memory_limit should be 2048M after second change' ); $this->processRun('docker compose exec -T cli php -r "echo E_ALL;"'); $e_all = trim($this->processGet()->getOutput()); diff --git a/drush/php-ini/drush.ini b/drush/php-ini/drush.ini index 97b9aa9fc..bde1aec84 100644 --- a/drush/php-ini/drush.ini +++ b/drush/php-ini/drush.ini @@ -5,5 +5,7 @@ ; ; @see https://github.com/drevops/vortex/issues/1913 -; Increase memory limit for Drush operations (database imports, config sync). -memory_limit = 512M +; Sized for database imports and config sync on large sites. Exceeding it +; usually indicates a memory leak in custom code rather than a limit set too +; low. +memory_limit = 1G From 96e14a5677f5b21b70e922190ba515ffb339d59a Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 12:57:29 +1000 Subject: [PATCH 06/57] Raised the Prettier 'printWidth' default to 160 and pinned doc comments to 80. (cherry picked from commit 37954ee98336781031589ded0eb92fa9ea946603) Kept the 2.x counter data attributes and theme behavior; reflowed the affected test lines. Fixtures regenerated separately. --- .editorconfig | 4 ++++ .prettierrc.json | 3 ++- .vortex/docs/content/tools/eslint.mdx | 8 +++++++ .../custom/ys_demo/js/tests/ys_demo.test.js | 24 +++++-------------- .../custom/your_site_theme/.prettierrc.json | 3 ++- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/.editorconfig b/.editorconfig index 00607a458..673b9dc05 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,6 +13,10 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true +# Matches the Prettier 'printWidth' in .prettierrc.json. +[*.js] +max_line_length = 160 + [*.{json,lock}] indent_size = 4 diff --git a/.prettierrc.json b/.prettierrc.json index b5ed52786..b786f17f6 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,9 +1,10 @@ { - "printWidth": 80, + "printWidth": 160, "semi": true, "singleQuote": true, "trailingComma": "all", "plugins": ["@homer0/prettier-plugin-jsdoc"], + "jsdocPrintWidth": 80, "jsdocReplaceTagsSynonyms": false, "overrides": [ { diff --git a/.vortex/docs/content/tools/eslint.mdx b/.vortex/docs/content/tools/eslint.mdx index 64a6c680f..4a365643d 100644 --- a/.vortex/docs/content/tools/eslint.mdx +++ b/.vortex/docs/content/tools/eslint.mdx @@ -97,6 +97,14 @@ Prettier is integrated via `eslint-plugin-prettier` and provides automatic code When you run `ahoy lint-fe-fix`, both ESLint's `--fix` and Prettier's formatting are applied automatically. +#### Line width + +**Vortex** sets `printWidth` to `160` in [`.prettierrc.json`](https://github.com/drevops/vortex/blob/main/.prettierrc.json) instead of the Prettier default of `80`, so a statement stays on one line unless it is genuinely long. Documentation blocks are held to `80` columns through `jsdocPrintWidth`, matching the width the PHP coding standard applies to comments. + +CSS carries its own `printWidth` override so that declarations are never wrapped. Prettier only reaches CSS when it is invoked directly, such as from an editor on save - within **Vortex**, stylesheets are linted by Stylelint. + +The custom theme ships a second `.prettierrc.json` with the same settings, because a theme carries its own front-end tooling and can be moved into a separate repository. + ## Ignoring ### Global ignoring diff --git a/web/modules/custom/ys_demo/js/tests/ys_demo.test.js b/web/modules/custom/ys_demo/js/tests/ys_demo.test.js index 64ff4d4e8..23e163949 100644 --- a/web/modules/custom/ys_demo/js/tests/ys_demo.test.js +++ b/web/modules/custom/ys_demo/js/tests/ys_demo.test.js @@ -152,9 +152,7 @@ describe('Drupal.behaviors.ysDemo', () => { document.body.innerHTML = createCounterBlockHtml(); Drupal.behaviors.ysDemo.initCounterBlock(document); - const incrementBtn = document.querySelector( - '[data-counter-action="increment"]', - ); + const incrementBtn = document.querySelector('[data-counter-action="increment"]'); incrementBtn.click(); const value = document.querySelector('[data-counter-value]'); @@ -165,9 +163,7 @@ describe('Drupal.behaviors.ysDemo', () => { document.body.innerHTML = createCounterBlockHtml(); Drupal.behaviors.ysDemo.initCounterBlock(document); - const decrementBtn = document.querySelector( - '[data-counter-action="decrement"]', - ); + const decrementBtn = document.querySelector('[data-counter-action="decrement"]'); decrementBtn.click(); const value = document.querySelector('[data-counter-value]'); @@ -178,9 +174,7 @@ describe('Drupal.behaviors.ysDemo', () => { document.body.innerHTML = createCounterBlockHtml(); Drupal.behaviors.ysDemo.initCounterBlock(document); - const incrementBtn = document.querySelector( - '[data-counter-action="increment"]', - ); + const incrementBtn = document.querySelector('[data-counter-action="increment"]'); incrementBtn.click(); incrementBtn.click(); incrementBtn.click(); @@ -196,9 +190,7 @@ describe('Drupal.behaviors.ysDemo', () => { document.body.innerHTML = createCounterBlockHtml(); Drupal.behaviors.ysDemo.initCounterBlock(document); - const incrementBtn = document.querySelector( - '[data-counter-action="increment"]', - ); + const incrementBtn = document.querySelector('[data-counter-action="increment"]'); incrementBtn.click(); incrementBtn.click(); @@ -210,9 +202,7 @@ describe('Drupal.behaviors.ysDemo', () => { document.body.innerHTML = createCounterBlockHtml(); Drupal.behaviors.ysDemo.initCounterBlock(document); - const incrementBtn = document.querySelector( - '[data-counter-action="increment"]', - ); + const incrementBtn = document.querySelector('[data-counter-action="increment"]'); incrementBtn.click(); const value = document.querySelector('[data-counter-value]'); @@ -261,9 +251,7 @@ describe('Drupal.behaviors.ysDemo', () => { document.body.innerHTML = createCounterBlockHtml(); Drupal.behaviors.ysDemo.attach(document); - const incrementBtn = document.querySelector( - '[data-counter-action="increment"]', - ); + const incrementBtn = document.querySelector('[data-counter-action="increment"]'); incrementBtn.click(); const value = document.querySelector('[data-counter-value]'); diff --git a/web/themes/custom/your_site_theme/.prettierrc.json b/web/themes/custom/your_site_theme/.prettierrc.json index b5ed52786..b786f17f6 100644 --- a/web/themes/custom/your_site_theme/.prettierrc.json +++ b/web/themes/custom/your_site_theme/.prettierrc.json @@ -1,9 +1,10 @@ { - "printWidth": 80, + "printWidth": 160, "semi": true, "singleQuote": true, "trailingComma": "all", "plugins": ["@homer0/prettier-plugin-jsdoc"], + "jsdocPrintWidth": 80, "jsdocReplaceTagsSynonyms": false, "overrides": [ { From af905ec84b0cc2bca452a630c4dda89893f44bbf Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 12:58:37 +1000 Subject: [PATCH 07/57] Completed the Rector rename-skip family and audited the skip list. (cherry picked from commit 3e6a29c1cf629ece051378636f2aa71f41fba4b0) Dropped the 'RemoveUnusedPublicMethodParameterRector' skip and its 'Theme' handler line: 2.x ships no 'src/Hook' classes. Applied to the CLI config in place of the installer one. --- .vortex/cli/rector.php | 6 ++++-- .vortex/tests/rector.php | 6 ++++-- rector.php | 6 +++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.vortex/cli/rector.php b/.vortex/cli/rector.php index de25426ed..f6ba9f2c5 100644 --- a/.vortex/cli/rector.php +++ b/.vortex/cli/rector.php @@ -18,13 +18,14 @@ use Rector\CodingStyle\Rector\Catch_\CatchExceptionNameMatchingTypeRector; use Rector\CodingStyle\Rector\ClassLike\NewlineBetweenClassLikeStmtsRector; use Rector\CodingStyle\Rector\ClassMethod\NewlineBeforeNewAssignSetRector; -use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector; use Rector\CodingStyle\Rector\Stmt\NewlineAfterStatementRector; use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\If_\RemoveAlwaysTrueIfConditionRector; use Rector\Naming\Rector\Assign\RenameVariableToMatchMethodCallReturnTypeRector; use Rector\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector; use Rector\Naming\Rector\ClassMethod\RenameVariableToMatchNewTypeRector; +use Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchExprVariableRector; +use Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchMethodCallReturnTypeRector; use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector; use Rector\Php80\Rector\Switch_\ChangeSwitchToMatchRector; use Rector\PHPUnit\CodeQuality\Rector\Class_\YieldDataProviderRector; @@ -47,7 +48,6 @@ CatchExceptionNameMatchingTypeRector::class, ChangeSwitchToMatchRector::class, CompleteDynamicPropertiesRector::class, - CountArrayToEmptyArrayComparisonRector::class, DisallowedEmptyRuleFixerRector::class, InlineArrayReturnAssignRector::class, NewlineAfterStatementRector::class, @@ -57,6 +57,8 @@ PrivatizeFinalClassPropertyRector::class, PrivatizeLocalGetterToPropertyRector::class, RemoveAlwaysTrueIfConditionRector::class, + RenameForeachValueVariableToMatchExprVariableRector::class, + RenameForeachValueVariableToMatchMethodCallReturnTypeRector::class, RenameParamToMatchTypeRector::class, RenameVariableToMatchMethodCallReturnTypeRector::class, RenameVariableToMatchNewTypeRector::class, diff --git a/.vortex/tests/rector.php b/.vortex/tests/rector.php index 5bc6f6f63..0e730da7d 100644 --- a/.vortex/tests/rector.php +++ b/.vortex/tests/rector.php @@ -16,13 +16,14 @@ use Rector\CodingStyle\Rector\Catch_\CatchExceptionNameMatchingTypeRector; use Rector\CodingStyle\Rector\ClassLike\NewlineBetweenClassLikeStmtsRector; use Rector\CodingStyle\Rector\ClassMethod\NewlineBeforeNewAssignSetRector; -use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector; use Rector\CodingStyle\Rector\Stmt\NewlineAfterStatementRector; use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\If_\RemoveAlwaysTrueIfConditionRector; use Rector\Naming\Rector\Assign\RenameVariableToMatchMethodCallReturnTypeRector; use Rector\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector; use Rector\Naming\Rector\ClassMethod\RenameVariableToMatchNewTypeRector; +use Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchExprVariableRector; +use Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchMethodCallReturnTypeRector; use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector; use Rector\Php80\Rector\Switch_\ChangeSwitchToMatchRector; use Rector\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector; @@ -43,7 +44,6 @@ CatchExceptionNameMatchingTypeRector::class, ChangeSwitchToMatchRector::class, CompleteDynamicPropertiesRector::class, - CountArrayToEmptyArrayComparisonRector::class, DisallowedEmptyRuleFixerRector::class, InlineArrayReturnAssignRector::class, NewlineAfterStatementRector::class, @@ -53,6 +53,8 @@ PrivatizeFinalClassPropertyRector::class, PrivatizeLocalGetterToPropertyRector::class, RemoveAlwaysTrueIfConditionRector::class, + RenameForeachValueVariableToMatchExprVariableRector::class, + RenameForeachValueVariableToMatchMethodCallReturnTypeRector::class, RenameParamToMatchTypeRector::class, RenameVariableToMatchMethodCallReturnTypeRector::class, RenameVariableToMatchNewTypeRector::class, diff --git a/rector.php b/rector.php index d9da44410..5dfa25297 100644 --- a/rector.php +++ b/rector.php @@ -24,7 +24,6 @@ use Rector\CodingStyle\Rector\Catch_\CatchExceptionNameMatchingTypeRector; use Rector\CodingStyle\Rector\ClassLike\NewlineBetweenClassLikeStmtsRector; use Rector\CodingStyle\Rector\ClassMethod\NewlineBeforeNewAssignSetRector; -use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector; use Rector\CodingStyle\Rector\Stmt\NewlineAfterStatementRector; use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\If_\RemoveAlwaysTrueIfConditionRector; @@ -32,6 +31,7 @@ use Rector\Naming\Rector\Class_\RenamePropertyToMatchTypeRector; use Rector\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector; use Rector\Naming\Rector\ClassMethod\RenameVariableToMatchNewTypeRector; +use Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchExprVariableRector; use Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchMethodCallReturnTypeRector; use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector; use Rector\Php80\Rector\Switch_\ChangeSwitchToMatchRector; @@ -60,7 +60,6 @@ CatchExceptionNameMatchingTypeRector::class, ChangeSwitchToMatchRector::class, CompleteDynamicPropertiesRector::class, - CountArrayToEmptyArrayComparisonRector::class, DisallowedEmptyRuleFixerRector::class, InlineArrayReturnAssignRector::class, NewlineAfterStatementRector::class, @@ -71,6 +70,7 @@ PrivatizeFinalClassPropertyRector::class, PrivatizeLocalGetterToPropertyRector::class, RemoveAlwaysTrueIfConditionRector::class, + RenameForeachValueVariableToMatchExprVariableRector::class, RenameForeachValueVariableToMatchMethodCallReturnTypeRector::class, RenameParamToMatchTypeRector::class, RenamePropertyToMatchTypeRector::class, @@ -78,7 +78,7 @@ RenameVariableToMatchNewTypeRector::class, SimplifyEmptyCheckOnEmptyArrayRector::class, StringClassNameToClassConstantRector::class => [ - __DIR__ . '/web/sites/default/includes/**/*', + __DIR__ . '/web/sites/default/includes/*', ], // Directories to skip. '*/vendor/*', From 1f2fbf4b93bb29cd02f9368b1b45eb06de3def46 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:00:28 +1000 Subject: [PATCH 08/57] Excluded cache table data from the exported database dump. Forward-ported from main be036fc30c079ff751bb7be6421e30adc5c59cde. Re-implemented in the PHP tooling rewrite; read the variable with 'getenv()' so an empty value still disables the option. --- .vortex/docs/content/development/database.mdx | 2 ++ .../Traits/Subtests/SubtestAhoyTrait.php | 34 +++++++++++++++++++ .vortex/tooling/src/vortex-export-db-file | 13 +++++-- .../tooling/tests/Unit/ExportDbFileTest.php | 12 +++---- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/.vortex/docs/content/development/database.mdx b/.vortex/docs/content/development/database.mdx index 7b05a69a1..5dd62ff38 100644 --- a/.vortex/docs/content/development/database.mdx +++ b/.vortex/docs/content/development/database.mdx @@ -95,4 +95,6 @@ Export timestamped database dumps from the local environment. You can use these dumps to restore the local environment to a specific state: rename the dump file to `.data/db.sql` and run the import command. +Cache tables are exported with their structure but without their data, because Drupal rebuilds them on demand and their contents would only inflate the dump. Set `VORTEX_EXPORT_DB_FILE_STRUCTURE_TABLES` to a comma-separated list of table names, each of which may use the `*` wildcard, to choose which tables are exported this way. Set it to an empty value to export the data of every table. + ➡️ See [Drupal > Provision](../drupal/provision) diff --git a/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php b/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php index e01b8a9d4..c6c815c49 100644 --- a/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php +++ b/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php @@ -235,10 +235,14 @@ protected function subtestAhoyProvision(): void { $this->assertFileNotContainsString('config/default/core.extension.yml', 'generated_content', 'Excluded module "generated_content" should not appear in the exported extension list'); $this->assertFileNotContainsString('config/default/core.extension.yml', 'testmode', 'Excluded module "testmode" should not appear in the exported extension list'); + $this->seedCacheTableRow(); + $this->cmd('ahoy export-db db.sql', '* Exported database dump saved', 'Export database should complete successfully'); $this->syncToHost('.data'); $this->assertFileExists('.data/db.sql', 'Database dump file should exist after export'); + $this->assertDumpExcludesCacheTableData('.data/db.sql'); + $this->cmd( 'ahoy provision', [ @@ -287,6 +291,36 @@ protected function subtestAhoyProvision(): void { $this->logStepFinish(); } + protected function seedCacheTableRow(): void { + $this->logSubstep('Seed a cache table row before the export'); + + // Drupal creates its cache tables lazily and which bins reach the database + // depends on the configured backends, so the row proving that the export + // drops cache data is written into a table this suite owns. + $seed_file = '.data/probe-cache-seed.sql'; + File::dump($seed_file, "CREATE TABLE IF NOT EXISTS cache_vortex_probe (cid VARCHAR(255) NOT NULL PRIMARY KEY, data LONGBLOB);\nINSERT INTO cache_vortex_probe (cid, data) VALUES ('SEEDED_CACHE_ROW_MARKER', 'probe');\n"); + $this->syncToContainer($seed_file); + $this->cmd('ahoy drush sql:query --file=../' . $seed_file, txt: 'Seed row should be written into a cache table'); + + $probe_file = '.data/probe-cache-rows.sql'; + File::dump($probe_file, "SELECT cid FROM cache_vortex_probe;\n"); + $this->syncToContainer($probe_file); + $this->cmd('ahoy drush sql:query --file=../' . $probe_file, '* SEEDED_CACHE_ROW_MARKER', 'Seeded cache row should be stored in the database'); + } + + protected function assertDumpExcludesCacheTableData(string $file): void { + $this->logSubstep('Assert cache tables are exported without their data'); + + $this->assertFileContainsString($file, 'CREATE TABLE `cache_vortex_probe`', 'Cache table structure should be present in the dump'); + $this->assertFileNotContainsString($file, 'INSERT INTO `cache', 'No cache table should carry rows into the dump'); + $this->assertFileNotContainsString($file, 'SEEDED_CACHE_ROW_MARKER', 'The seeded cache row should not appear in the dump'); + + $this->assertFileContainsString($file, 'CREATE TABLE `users_field_data`', 'Non-cache table structure should be present in the dump'); + $this->assertFileContainsString($file, 'INSERT INTO `users_field_data`', 'Non-cache table rows should be present in the dump'); + $this->assertFileContainsString($file, 'CREATE TABLE `config`', 'Configuration table structure should be present in the dump'); + $this->assertFileContainsString($file, 'INSERT INTO `config`', 'Configuration table rows should be present in the dump'); + } + protected function subtestAhoyExportDb(string $filename = '', bool $is_container_image_archive = FALSE): void { $this->logStepStart(); diff --git a/.vortex/tooling/src/vortex-export-db-file b/.vortex/tooling/src/vortex-export-db-file index 02db93dce..bfd38f372 100755 --- a/.vortex/tooling/src/vortex-export-db-file +++ b/.vortex/tooling/src/vortex-export-db-file @@ -19,6 +19,15 @@ require_once __DIR__ . '/helpers.php'; // Directory with database dump file. $db_dir = getenv_default('VORTEX_EXPORT_DB_FILE_DIR', 'VORTEX_DB_DIR', './.data'); +// Tables to export with their structure but without their data. Accepts a +// comma-separated list where each entry may use the `*` wildcard. Drupal +// rebuilds cache tables on demand, so their contents only inflate the dump. +// Set to an empty value to export the data of every table, which is why the +// variable is read directly rather than through getenv_default(): that helper +// treats an empty value as unset and would restore the default. +$structure_tables = getenv('VORTEX_EXPORT_DB_FILE_STRUCTURE_TABLES'); +$structure_tables = $structure_tables === FALSE ? 'cache*' : $structure_tables; + // Custom dump file name provided as first CLI argument. $custom_file = $GLOBALS['argv'][1] ?? ''; @@ -41,8 +50,8 @@ prepare_db_dir($db_dir); TASK( 'Exporting database to the dump file.', sprintf('Exported database dump saved %s.', $dump_file), - function () use ($dump_file_drush, $dump_file): void { - drush('sql:dump --skip-tables-key=common --result-file=' . escapeshellarg($dump_file_drush) . ' -q'); + function () use ($dump_file_drush, $dump_file, $structure_tables): void { + drush('sql:dump --skip-tables-key=common --structure-tables-list=' . escapeshellarg($structure_tables) . ' --result-file=' . escapeshellarg($dump_file_drush) . ' -q'); // Check that file was saved. if (!file_exists($dump_file) || filesize($dump_file) <= 0) { diff --git a/.vortex/tooling/tests/Unit/ExportDbFileTest.php b/.vortex/tooling/tests/Unit/ExportDbFileTest.php index 8c155a58c..ab79de351 100644 --- a/.vortex/tooling/tests/Unit/ExportDbFileTest.php +++ b/.vortex/tooling/tests/Unit/ExportDbFileTest.php @@ -30,7 +30,7 @@ public function testDefaultTimestampFilename(): void { // Drush sql:dump. $this->mockPassthru([ - 'cmd' => './vendor/bin/drush -y sql:dump --skip-tables-key=common --result-file=' . escapeshellarg($dump_file_drush) . ' -q', + 'cmd' => './vendor/bin/drush -y sql:dump --skip-tables-key=common --structure-tables-list=\'cache*\' --result-file=' . escapeshellarg($dump_file_drush) . ' -q', 'result_code' => 0, ]); @@ -52,7 +52,7 @@ public function testCustomFilename(): void { // Drush sql:dump. $this->mockPassthru([ - 'cmd' => './vendor/bin/drush -y sql:dump --skip-tables-key=common --result-file=' . escapeshellarg($dump_file_drush) . ' -q', + 'cmd' => './vendor/bin/drush -y sql:dump --skip-tables-key=common --structure-tables-list=\'cache*\' --result-file=' . escapeshellarg($dump_file_drush) . ' -q', 'result_code' => 0, ]); @@ -78,7 +78,7 @@ public function testDumpFileMissing(): void { // Drush sql:dump succeeds but file not created. $this->mockPassthru([ - 'cmd' => './vendor/bin/drush -y sql:dump --skip-tables-key=common --result-file=' . escapeshellarg($dump_file_drush) . ' -q', + 'cmd' => './vendor/bin/drush -y sql:dump --skip-tables-key=common --structure-tables-list=\'cache*\' --result-file=' . escapeshellarg($dump_file_drush) . ' -q', 'result_code' => 0, ]); @@ -96,7 +96,7 @@ public function testDumpFileEmpty(): void { // Drush sql:dump. $this->mockPassthru([ - 'cmd' => './vendor/bin/drush -y sql:dump --skip-tables-key=common --result-file=' . escapeshellarg($dump_file_drush) . ' -q', + 'cmd' => './vendor/bin/drush -y sql:dump --skip-tables-key=common --structure-tables-list=\'cache*\' --result-file=' . escapeshellarg($dump_file_drush) . ' -q', 'result_code' => 0, ]); @@ -116,7 +116,7 @@ public function testDrushFails(): void { $dump_file_drush = $dump_file; $this->mockPassthru([ - 'cmd' => './vendor/bin/drush -y sql:dump --skip-tables-key=common --result-file=' . escapeshellarg($dump_file_drush) . ' -q', + 'cmd' => './vendor/bin/drush -y sql:dump --skip-tables-key=common --structure-tables-list=\'cache*\' --result-file=' . escapeshellarg($dump_file_drush) . ' -q', 'result_code' => 1, ]); @@ -134,7 +134,7 @@ public function testDirectoryCreation(): void { $dump_file_drush = $dump_file; $this->mockPassthru([ - 'cmd' => './vendor/bin/drush -y sql:dump --skip-tables-key=common --result-file=' . escapeshellarg($dump_file_drush) . ' -q', + 'cmd' => './vendor/bin/drush -y sql:dump --skip-tables-key=common --structure-tables-list=\'cache*\' --result-file=' . escapeshellarg($dump_file_drush) . ' -q', 'result_code' => 0, ]); From fe5145cf91ba16d12d300ac7efc18a75d6fe9220 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:01:47 +1000 Subject: [PATCH 09/57] Asserted that every documentation variable source yields variables. Forward-ported from main 852fdf1fec3c79e0ac3a03c9895cf1a136fce990. Dropped the tooling-script publication: those scripts are PHP on this line and Shellvar reads only shell, so the tooling directory is no longer passed as an input. --- .vortex/docs/.utils/update-docs.sh | 48 ++++++++++++++----- .../.utils/variables/variables.excluded.txt | 2 + 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/.vortex/docs/.utils/update-docs.sh b/.vortex/docs/.utils/update-docs.sh index 4e78ddc37..d055bef87 100755 --- a/.vortex/docs/.utils/update-docs.sh +++ b/.vortex/docs/.utils/update-docs.sh @@ -5,7 +5,10 @@ # @usage # cd .vortex/docs && ./update-docs.sh # -# shellcheck disable=SC2129 +# The assertion markers below contain literal backticks: the table renders paths +# as inline code. +# +# shellcheck disable=SC2129,SC2016 set -eu [ "${VORTEX_DEBUG-}" = "1" ] && set -x @@ -15,14 +18,8 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../" && pwd)" sed_opts=(-i) && [ "$(uname)" = "Darwin" ] && sed_opts=(-i '') OUTPUT_FILE="./content/development/variables.mdx" -sed "${sed_opts[@]}" '/## Variables list/,$d' "${OUTPUT_FILE}" - -echo "## Variables list" >>"${OUTPUT_FILE}" -echo "" >>"${OUTPUT_FILE}" -echo "The list below is automatically generated with [Shellvar](https://github.com/alexSkrypnyk/shellvar) from all Shell scripts. " >>"${OUTPUT_FILE}" -echo >>"${OUTPUT_FILE}" -docker run -v "${ROOT_DIR}:/app" drevops/shellvar:1.7.0 extract \ +table="$(docker run -v "${ROOT_DIR}:/app" drevops/shellvar:1.7.0 extract \ --skip-text="@docs:skip" \ --skip-description-prefix=";<" \ --skip-description-prefix=";>" \ @@ -39,10 +36,39 @@ docker run -v "${ROOT_DIR}:/app" drevops/shellvar:1.7.0 extract \ --fields='name=Name;description=Description;default_value=Default value;paths=Defined or used in' \ .env \ .env.local.example \ - .vortex/tooling/src \ scripts \ - .vortex/docs/.utils/variables/extra \ - >>"${OUTPUT_FILE}" + .vortex/docs/.utils/variables/extra)" + +# An input that yields no variables leaves the table unchanged, which the CI +# diff check reports as up-to-date docs. Each input is asserted to be present in +# the extracted table instead, before the file is written. +# +# $1 - substring marking the input in the "Defined or used in" column. +# $2 - input path to name in the failure message. +assert_source_present() { + case "${table}" in + *"${1}"*) return 0 ;; + esac + + echo "ERROR: Shellvar extracted no variables from '${2}'." >&2 + exit 1 +} + +# The 'sed' replacements below alias the 'extra' stub paths onto display labels, +# so the assertions run while the paths are still verbatim. +assert_source_present '`.env`' '.env' +assert_source_present '`.env.local.example`' '.env.local.example' +assert_source_present '`scripts/' 'scripts' +assert_source_present '`.vortex/docs/.utils/variables/extra/' '.vortex/docs/.utils/variables/extra' + +sed "${sed_opts[@]}" '/## Variables list/,$d' "${OUTPUT_FILE}" + +echo "## Variables list" >>"${OUTPUT_FILE}" +echo "" >>"${OUTPUT_FILE}" +echo "The list below is automatically generated with [Shellvar](https://github.com/alexSkrypnyk/shellvar) from all Shell scripts. " >>"${OUTPUT_FILE}" +echo >>"${OUTPUT_FILE}" + +printf '%s\n' "${table}" >>"${OUTPUT_FILE}" sed "${sed_opts[@]}" "s/.vortex\/docs\/.utils\/variables\/extra\/environment.variables.sh/ENVIRONMENT/g" "${OUTPUT_FILE}" sed "${sed_opts[@]}" "s/.vortex\/docs\/.utils\/variables\/extra\/acquia.variables.sh/ACQUIA ENVIRONMENT/g" "${OUTPUT_FILE}" diff --git a/.vortex/docs/.utils/variables/variables.excluded.txt b/.vortex/docs/.utils/variables/variables.excluded.txt index db0cfa423..9a740b5ee 100755 --- a/.vortex/docs/.utils/variables/variables.excluded.txt +++ b/.vortex/docs/.utils/variables/variables.excluded.txt @@ -4,7 +4,9 @@ DOCKER_BUILDKIT HOME IFS PATH +SCRIPT_DIR SRC_TMPDIR +TARGET_ENV_REMAP TEST_PACKAGE_TOKEN VORTEX_DOCTOR_CHECK_PREFLIGHT VORTEX_PROVISION_LOG_ACTIVE From 0026bb4cc8f04ef8d331de50f15fd2c2fe7dcb72 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:02:03 +1000 Subject: [PATCH 10/57] Disabled JetBrains Mono ligatures so code samples render '->' and '=>' literally. (cherry picked from commit a793ce58aeb62717f5c690407fb613e99b9f703e) Dropped the generated variables table hunk: it documents a variable this line does not carry. --- .vortex/docs/src/css/custom.css | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.vortex/docs/src/css/custom.css b/.vortex/docs/src/css/custom.css index e23095309..429eb134e 100644 --- a/.vortex/docs/src/css/custom.css +++ b/.vortex/docs/src/css/custom.css @@ -52,6 +52,19 @@ display: none; } +/* JetBrains Mono ligatures fuse operators into single glyphs, drawing '->' as + '→' and '=>' as '⇒'. Code samples must show the literal characters. */ +code, +pre, +kbd, +samp { + font-variant-ligatures: none; + font-feature-settings: + 'liga' 0, + 'clig' 0, + 'calt' 0; +} + /* Frosted glass navbar. */ .navbar { backdrop-filter: blur(20px) saturate(180%); @@ -504,6 +517,12 @@ html[data-theme='dark'] .vtx-home .btn-primary:hover { color: var(--ink-2); white-space: nowrap; scrollbar-width: thin; + /* Not a 'code' element, so it needs its own ligature opt-out for '&&'. */ + font-variant-ligatures: none; + font-feature-settings: + 'liga' 0, + 'clig' 0, + 'calt' 0; } .vtx-home .snippet-code .pr { color: var(--teal); From f63228c995d48cb0864636b94bfade6a26e61209 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:03:20 +1000 Subject: [PATCH 11/57] Added generated content provisioning to the development modules deploy step. Forward-ported from main 16fad832fee4e6faf00fe18ce82f2e430aeb5404. Re-implemented against the deploy step that replaced 'scripts/provision-10-enable-dev-modules.sh'; dropped the installer-side script removal, which has no counterpart here. --- .vortex/docs/.utils/variables/variables.excluded.txt | 1 + .vortex/docs/content/drupal/generated-content.mdx | 6 ++++++ .../EnableDevelopmentModulesDeployStep.php | 12 ++++++++++++ web/modules/custom/ys_demo/ys_demo.info.yml | 1 - 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.vortex/docs/.utils/variables/variables.excluded.txt b/.vortex/docs/.utils/variables/variables.excluded.txt index 9a740b5ee..d3f82c516 100755 --- a/.vortex/docs/.utils/variables/variables.excluded.txt +++ b/.vortex/docs/.utils/variables/variables.excluded.txt @@ -1,6 +1,7 @@ CI COMPOSER_MEMORY_LIMIT DOCKER_BUILDKIT +GENERATED_CONTENT_CREATE HOME IFS PATH diff --git a/.vortex/docs/content/drupal/generated-content.mdx b/.vortex/docs/content/drupal/generated-content.mdx index 817e4d9f1..f59f05ff1 100644 --- a/.vortex/docs/content/drupal/generated-content.mdx +++ b/.vortex/docs/content/drupal/generated-content.mdx @@ -92,6 +92,12 @@ on module install. Optionally filter: GENERATED_CONTENT_ITEMS="taxonomy_term-tags,node-article" ``` +### Provisioning + +**Vortex** installs the module from the development modules deploy step in the `local`, `ci`, `dev` and `stage` environments, passing `GENERATED_CONTENT_CREATE=1` so the content is created as part of the install. Setting `DRUPAL_GENERATED_CONTENT_SKIP=1` installs the module without the content. + +➡️ See [Provision](provision) + ## Example in Vortex The `ys_demo` module ships two generated content plugins: diff --git a/web/modules/custom/ys_base/src/Plugin/DeployStep/EnableDevelopmentModulesDeployStep.php b/web/modules/custom/ys_base/src/Plugin/DeployStep/EnableDevelopmentModulesDeployStep.php index cb0c09a74..8617b6068 100644 --- a/web/modules/custom/ys_base/src/Plugin/DeployStep/EnableDevelopmentModulesDeployStep.php +++ b/web/modules/custom/ys_base/src/Plugin/DeployStep/EnableDevelopmentModulesDeployStep.php @@ -123,6 +123,18 @@ public function run(): void { // phpcs:ignore #;< CUSTOM_MODULE_DEMO $this->moduleInstaller->install(['ys_demo']); // phpcs:ignore #;> CUSTOM_MODULE_DEMO + + // phpcs:ignore #;< MODULE_GENERATED_CONTENT + // The module creates its content while it installs, and only when + // GENERATED_CONTENT_CREATE is set, so it installs after the modules that + // supply the generated content plugins. + if (getenv('DRUPAL_GENERATED_CONTENT_SKIP') !== '1') { + putenv('GENERATED_CONTENT_CREATE=1'); + } + + $this->moduleInstaller->install(['generated_content']); + putenv('GENERATED_CONTENT_CREATE'); + // phpcs:ignore #;> MODULE_GENERATED_CONTENT } } diff --git a/web/modules/custom/ys_demo/ys_demo.info.yml b/web/modules/custom/ys_demo/ys_demo.info.yml index 7455db872..faf4939dc 100644 --- a/web/modules/custom/ys_demo/ys_demo.info.yml +++ b/web/modules/custom/ys_demo/ys_demo.info.yml @@ -8,5 +8,4 @@ dependencies: - drupal:node - drupal:views - drupal_helpers:drupal_helpers - - generated_content:generated_content - testmode:testmode From c29e39b47b910620893a8c63aa0bd20821cd63b3 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:04:08 +1000 Subject: [PATCH 12/57] Ran 'composer audit' first and every audit check regardless of failures. (cherry picked from commit 53c6b7508dfe58e0a58fdf215097e23f4ef5906c) Reapplied the CircleCI step reorder by hand against the folded job set; fixtures regenerated separately. --- .circleci/config.yml | 11 +++++++---- .github/workflows/audit.yml | 14 ++++++++------ .vortex/docs/content/_code-lifecycle.mdx | 6 +++--- .../docs/content/continuous-integration/README.mdx | 4 +++- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 65449720e..33fd690fb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -233,10 +233,6 @@ jobs: #;< CI_GITLEAKS - *step_setup_remote_docker - - - run: - name: Scan for committed secrets with Gitleaks - command: docker run --rm -v "${PWD}":/repo -w /repo ghcr.io/gitleaks/gitleaks:v8.30.1 dir . --no-banner || [ "${VORTEX_CI_GITLEAKS_IGNORE_FAILURE:-0}" -eq 1 ] #;> CI_GITLEAKS #;< VORTEX_DEV @@ -252,6 +248,13 @@ jobs: name: Audit Composer packages command: composer audit --locked || [ "${VORTEX_CI_COMPOSER_AUDIT_IGNORE_FAILURE:-0}" -eq 1 ] + #;< CI_GITLEAKS + - run: + name: Scan for committed secrets with Gitleaks + when: always + command: docker run --rm -v "${PWD}":/repo -w /repo ghcr.io/gitleaks/gitleaks:v8.30.1 dir . --no-banner || [ "${VORTEX_CI_GITLEAKS_IGNORE_FAILURE:-0}" -eq 1 ] + #;> CI_GITLEAKS + # Test the provisioned site. Runs in parallel with the lint and build jobs. # Provisioning and testing happen within the same job to save time on # re-provisioning. diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 3485c2e48..4295e88a9 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -82,12 +82,6 @@ jobs: - name: Load environment variables from .env run: t=$(mktemp) && export -p >"${t}" && set -a && . ./.env && set +a && . "${t}" && env >> "$GITHUB_ENV" - #;< CI_GITLEAKS - - name: Scan for committed secrets with Gitleaks - run: docker run --rm -v "${PWD}":/repo -w /repo ghcr.io/gitleaks/gitleaks:v8.30.1 dir . --no-banner - continue-on-error: ${{ vars.VORTEX_CI_GITLEAKS_IGNORE_FAILURE == '1' }} - #;> CI_GITLEAKS - #;< VORTEX_DEV #; Vortex does not track 'composer.lock', so resolve dependencies to give #; the audit below something to read. Nothing is installed, so the runner @@ -97,5 +91,13 @@ jobs: #;> VORTEX_DEV - name: Audit Composer packages + if: ${{ !cancelled() }} run: composer audit --locked continue-on-error: ${{ vars.VORTEX_CI_COMPOSER_AUDIT_IGNORE_FAILURE == '1' }} + + #;< CI_GITLEAKS + - name: Scan for committed secrets with Gitleaks + if: ${{ !cancelled() }} + run: docker run --rm -v "${PWD}":/repo -w /repo ghcr.io/gitleaks/gitleaks:v8.30.1 dir . --no-banner + continue-on-error: ${{ vars.VORTEX_CI_GITLEAKS_IGNORE_FAILURE == '1' }} + #;> CI_GITLEAKS diff --git a/.vortex/docs/content/_code-lifecycle.mdx b/.vortex/docs/content/_code-lifecycle.mdx index 7f181610b..10869ad46 100644 --- a/.vortex/docs/content/_code-lifecycle.mdx +++ b/.vortex/docs/content/_code-lifecycle.mdx @@ -54,9 +54,9 @@ Security Audit Workflow ═════════════════════════════════════════════════════════════════════════════════════════ - Push, pull request or manual run ──► Gitleaks secret scan ──► Composer advisory audit - (committed secrets) (composer audit --locked) + Push, pull request or manual run ──► Composer advisory audit ──► Gitleaks secret scan + (composer audit --locked) (committed secrets) Runs as its own workflow, independently of the pipeline above, and does not gate - deployment. + deployment. Every check runs even if an earlier one failed. ``` diff --git a/.vortex/docs/content/continuous-integration/README.mdx b/.vortex/docs/content/continuous-integration/README.mdx index 271f37514..f117e9c70 100644 --- a/.vortex/docs/content/continuous-integration/README.mdx +++ b/.vortex/docs/content/continuous-integration/README.mdx @@ -71,8 +71,10 @@ Security checks run in their own workflow, separate from the pipeline above, so The workflow runs the same two checks in both providers, and needs neither the application containers nor installed dependencies: -- [Gitleaks](/docs/tools/gitleaks) scans the codebase for committed secrets - `composer audit --locked` checks the packages pinned in `composer.lock` against published security advisories +- [Gitleaks](/docs/tools/gitleaks) scans the codebase for committed secrets + +Every check runs even if an earlier one failed, so a single run reports all the findings at once. The workflow fails if any of the checks failed, unless that check's `_IGNORE_FAILURE` variable is set to `1`. It is triggered by the same pushes, pull requests and tags as the main pipeline, and can also be started on demand - in GitHub Actions from **Actions → Security audit → Run workflow**, and in CircleCI by re-running the `audit` workflow from the pipeline view. From c0216869ab885d272f3891120dd9371a8314aaaf Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:05:56 +1000 Subject: [PATCH 13/57] Refreshed the contrib module documentation and added a Modules reference page. (cherry picked from commit 7184b0524257c63b2cb4a8c8931d986b54062f9d) Restored this line's facts in the merged tables: the 'deploy_steps' requirement, the CLI in place of the installer, and the deploy steps in place of the removed provision scripts. --- .vortex/docs/content/drupal/README.mdx | 31 +- .vortex/docs/content/drupal/composer-json.mdx | 266 +++++------------- .../docs/content/drupal/drupal-helpers.mdx | 46 ++- .../docs/content/drupal/generated-content.mdx | 96 +++++-- .vortex/docs/content/drupal/migrations.mdx | 2 +- .../docs/content/drupal/module-scaffold.mdx | 13 +- .vortex/docs/content/drupal/modules.mdx | 90 ++++++ .vortex/docs/content/drupal/provision.mdx | 2 +- .vortex/docs/content/drupal/settings.mdx | 2 +- .vortex/docs/content/drupal/test-mode.mdx | 76 ----- .vortex/docs/content/drupal/testmode.mdx | 97 +++++++ .../docs/content/drupal/theme-scaffold.mdx | 2 +- .vortex/docs/content/drupal/update-hooks.mdx | 2 +- .vortex/docs/cspell.json | 2 + .vortex/docs/docusaurus.config.js | 4 + 15 files changed, 398 insertions(+), 333 deletions(-) create mode 100644 .vortex/docs/content/drupal/modules.mdx delete mode 100644 .vortex/docs/content/drupal/test-mode.mdx create mode 100644 .vortex/docs/content/drupal/testmode.mdx diff --git a/.vortex/docs/content/drupal/README.mdx b/.vortex/docs/content/drupal/README.mdx index 87c5f4a6b..b4c5299aa 100644 --- a/.vortex/docs/content/drupal/README.mdx +++ b/.vortex/docs/content/drupal/README.mdx @@ -10,8 +10,10 @@ sidebar_position: 1 1. [Composer configuration](composer-json) 2. [Settings management](settings) 3. [Provision script](provision) -4. [Module scaffold](module-scaffold) -5. [Theme scaffold](theme-scaffold) +4. [Update hooks](update-hooks) +5. [Migrations](migrations) +6. [Module scaffold](module-scaffold) +7. [Theme scaffold](theme-scaffold) ## Included modules @@ -21,21 +23,10 @@ provides you with a minimal set of modules and dependencies to get you started. You would need to add more modules and themes once you finish the initial setup. -| Module Name | Description | -|---------------------------------------------------------------------------------|-----------------------------------------------------------------------------------| -| [`clamav`](https://www.drupal.org/project/clamav) | Integrates ClamAV antivirus for file scanning. | -| [`coffee`](https://www.drupal.org/project/coffee) | Provides quick access to admin pages through a search bar. | -| [`config_split`](https://www.drupal.org/project/config_split) | Allows exporting and importing different configurations based on the environment. | -| [`config_update`](https://www.drupal.org/project/config_update) | Tracks and updates configuration changes on your site. | -| [`environment_indicator`](https://www.drupal.org/project/environment_indicator) | Adds a visual indicator for the current environment (e.g., Dev, Stage, Prod). | -| [`navigation_extra_tools`](https://www.drupal.org/project/navigation_extra_tools) | Adds administration shortcuts (clear caches, run cron, run updates) to the core Navigation module. | -| [`pathauto`](https://www.drupal.org/project/pathauto) | Automatically generates URL/path aliases for content. | -| [`redirect`](https://www.drupal.org/project/redirect) | Provides URL redirection management. | -| [`redis`](https://www.drupal.org/project/redis) | Integrates Redis caching backend. | -| [`reroute_email`](https://www.drupal.org/project/reroute_email) | Intercepts outgoing emails and reroutes them to a configurable address. | -| [`robotstxt`](https://www.drupal.org/project/robotstxt) | Manages the robots.txt file for controlling search engine crawler access. | -| [`search_api`](https://www.drupal.org/project/search_api) | Provides a flexible framework for creating search pages. | -| [`search_api_solr`](https://www.drupal.org/project/search_api_solr) | Integrates Apache Solr with Search API. | -| [`seckit`](https://www.drupal.org/project/seckit) | Provides security hardening options for HTTP headers and other protections. | -| [`shield`](https://www.drupal.org/project/shield) | Restricts access to your site by requiring a username and password. | -| [`stage_file_proxy`](https://www.drupal.org/project/stage_file_proxy) | Serves production asset files when accessing non-production environments. | +Every shipped module is listed in [Modules](modules), together with what it +does and what configuration **Vortex** provides for it. Three of them have a +page of their own: + +- [Drupal helpers](drupal-helpers) +- [Generated content](generated-content) +- [Testmode](testmode) diff --git a/.vortex/docs/content/drupal/composer-json.mdx b/.vortex/docs/content/drupal/composer-json.mdx index b7520248a..24b542ece 100644 --- a/.vortex/docs/content/drupal/composer-json.mdx +++ b/.vortex/docs/content/drupal/composer-json.mdx @@ -1,6 +1,6 @@ --- sidebar_label: composer.json -sidebar_position: 1 +sidebar_position: 2 --- # composer.json @@ -67,121 +67,60 @@ The [`repositories`](https://getcomposer.org/doc/04-schema.md#repositories) section defines custom package repositories, essential for accessing packages outside the default Packagist repository. -- [`drupal`](https://www.drupal.org/docs/develop/using-composer/using-packagesdrupalorg): - Serves as the official source for Drupal modules, themes, and distributions. - It's crucial for a Drupal project using Composer, as it allows access to - Drupal-specific packages not available on Packagist. -- `drevops/vortex-tooling` (path): A local - [path repository](https://getcomposer.org/doc/05-repositories.md#path) - pointing at `.vortex/tooling`, used only inside the **Vortex** repository so - the in-tree tooling package resolves during development. The CLI install command strips - this entry during site creation, so your project installs - [`drevops/vortex-tooling`](https://github.com/drevops/vortex-tooling) from - Packagist instead. +| Repository | Type | Description | +|------------|------|-------------| +| [`packages.drupal.org/8`](https://www.drupal.org/docs/develop/using-composer/using-packagesdrupalorg) | `composer` | The official source for Drupal modules, themes and distributions, which are not published on Packagist. | +| [`.vortex/tooling`](https://getcomposer.org/doc/05-repositories.md#path) | `path` | Resolves the in-tree tooling package while developing **Vortex** itself. The CLI install command removes this entry during site creation, so your project installs [`drevops/vortex-tooling`](https://github.com/drevops/vortex-tooling) from Packagist instead. | ### `require` The [`require`](https://getcomposer.org/doc/04-schema.md#require) section -specifies the essential packages and libraries your project needs. Many of the -Drupal modules listed below ship with [pre-configured settings](settings.mdx) so -they work out of the box. - -- `php`: Specifies the minimum PHP version required to run this project. This - should be specified as a range rather than an exact version number. - E.g. `>=8.4` and not `8.4.0`. -- [`composer/installers`](https://github.com/composer/installers): Allows to - install packages to the correct location based on the specified package type - such as `drupal-module`, `drupal-theme`, `drupal-profile`, etc. -- [`cweagans/composer-patches`](https://github.com/cweagans/composer-patches): - Enables git-based patching of Composer packages, useful for incorporating fixes - not yet in official releases. Version 2.x uses `git apply` for cross-platform - consistency and generates a `patches.lock.json` file to ensure reproducible - builds with SHA-256 checksums. - ➡️ See [Development > Composer > Patching](../development/composer#patching) -- [`drevops/vortex-tooling`](https://github.com/drevops/vortex-tooling): Ships - the **Vortex** operational scripts - build, provision, deployment, and - notification tooling - exposed as Composer binaries under - `vendor/bin/vortex-*` (backed by `vendor/drevops/vortex-tooling/src/`). -- [`drupal/clamav`](https://www.drupal.org/project/clamav): Scans uploaded files - for malware with the ClamAV engine before they are saved. -- [`drupal/coffee`](https://www.drupal.org/project/coffee): Adds a keyboard - shortcut to jump straight to any administration page by typing its name. -- [`drupal/config_split`](https://www.drupal.org/project/config_split): Splits - configuration into sets that are conditionally imported, enabling - environment-specific configuration such as development-only modules. -- [`drupal/config_update`](https://www.drupal.org/project/config_update): - Provides tools and Drush commands to report, revert, and import configuration - changes relative to the defaults shipped by modules. -- [`drupal/core-composer-scaffold`](https://www.drupal.org/docs/develop/using-composer/using-drupals-composer-scaffold): - Allows downloading and placing **Drupal Scaffold** files (like `index.php`, - `update.php`, etc.) from the `drupal/core` project into their desired location - inside the web root. -- [`drupal/core-recommended`](https://github.com/drupal/core-recommended): A - package that provides a carefully selected set of dependencies, including - specific versions, which are tested and recommended for a particular Drupal - core version. It simplifies dependency management by ensuring compatibility - and stability, as these dependencies are maintained and curated by the Drupal - community. -- [`drupal/deploy_steps`](https://www.drupal.org/project/deploy_steps): Provides - a structured way to define and run ordered deployment operations during the - site provisioning phase, replacing ad-hoc post-deployment scripts. -- [`drupal/devel`](https://www.drupal.org/project/devel): A suite of development - tools for inspecting variables, entities, and the service container while - debugging. -- [`drupal/drupal_helpers`](https://www.drupal.org/project/drupal_helpers): A - collection of helper functions that simplify writing update hooks and - deployment operations. -- [`drupal/environment_indicator`](https://www.drupal.org/project/environment_indicator): - Shows a colored banner identifying the current environment to prevent - accidental changes on the wrong site. -- [`drupal/generated_content`](https://www.drupal.org/project/generated_content): - Generates realistic placeholder content from declarative definitions for - development and testing. -- [`drupal/migrate_plus`](https://www.drupal.org/project/migrate_plus): Extends - the core Migrate API with extra source and process plugins and - configuration-entity migrations. -- [`drupal/migrate_tools`](https://www.drupal.org/project/migrate_tools): - Provides Drush commands and a UI to run, roll back, and monitor migrations. -- [`drupal/navigation_extra_tools`](https://www.drupal.org/project/navigation_extra_tools): - Adds administration shortcuts - clear caches, run cron, run database updates - - to the core Navigation module. -- [`drupal/pathauto`](https://www.drupal.org/project/pathauto): Automatically - generates URL aliases for content based on configurable patterns. -- [`drupal/redirect`](https://www.drupal.org/project/redirect): Manages URL - redirects and creates them automatically when content URLs change. -- [`drupal/redis`](https://www.drupal.org/project/redis): Integrates Drupal with - the Redis in-memory store for cache and lock backends. -- [`drupal/reroute_email`](https://www.drupal.org/project/reroute_email): - Reroutes all outbound email to a configured address on non-production - environments to avoid emailing real users. -- [`drupal/robotstxt`](https://www.drupal.org/project/robotstxt): Manages the - `robots.txt` file from the admin UI, useful when it cannot be placed on disk - (for example, in multisite setups). -- [`drupal/sdc_devel`](https://www.drupal.org/project/sdc_devel): Development and - validation tooling for Single Directory Components (SDC), reporting issues in - component definitions and templates. -- [`drupal/search_api`](https://www.drupal.org/project/search_api): Provides a - framework for building search experiences with pluggable indexing backends. -- [`drupal/search_api_solr`](https://www.drupal.org/project/search_api_solr): A - Search API backend that integrates Apache Solr for fast, scalable search. -- [`drupal/seckit`](https://www.drupal.org/project/seckit): Adds configurable - security-hardening HTTP headers, including Content Security Policy and - anti-framing protection. -- [`drupal/shield`](https://www.drupal.org/project/shield): Protects - non-production environments behind HTTP basic authentication. -- [`drupal/stage_file_proxy`](https://www.drupal.org/project/stage_file_proxy): - Fetches media files from a remote site on demand, so local environments do not - need a full copy of the files directory. -- [`drupal/testmode`](https://www.drupal.org/project/testmode): Adjusts site - behavior during automated tests, for example by filtering out generated - content from listings. -- [`drupal/xmlsitemap`](https://www.drupal.org/project/xmlsitemap): Generates a - multilingual XML sitemap to help search engines index the site. -- [`drush/drush`](https://github.com/drush-ops/drush): A command-line shell and - scripting interface for Drupal, providing a wide range of utilities to manage - and interact with your Drupal sites. -- [`webflo/drupal-finder`](https://github.com/webflo/drupal-finder): Locates - Drupal installations in a directory structure. +specifies the essential packages and libraries your project needs. + +The **Documentation** column links to the page covering that package in more +depth. A [Settings](settings.mdx#per-module-overrides) link means **Vortex** +ships a per-module override file that applies environment-aware defaults for +that module. Some modules still need a running service or environment-specific +values on top of those defaults. + +➡️ See [Modules](modules.mdx) for what each override actually configures and +where each module is installed. + +| Package | Description | Documentation | +|---------|-------------|---------------| +| `php` | The minimum PHP version required to run this project. Specify a range such as `>=8.4` rather than an exact version such as `8.4.0`. | | +| [`composer/installers`](https://github.com/composer/installers) | Installs packages into the correct location based on their package type, such as `drupal-module`, `drupal-theme` or `drupal-profile`. | | +| [`cweagans/composer-patches`](https://github.com/cweagans/composer-patches) | Patches Composer packages with `git apply`, incorporating fixes that are not yet in an official release. Patch metadata and SHA-256 checksums are recorded in `patches.lock.json` for reproducible builds. | [Patching](../development/composer.mdx#patching) | +| [`drevops/vortex-tooling`](https://github.com/drevops/vortex-tooling) | Ships the **Vortex** operational scripts - build, provision, deployment and notification tooling - that your site runs from `vendor/drevops/vortex-tooling/src/`. | | +| [`drupal/clamav`](https://www.drupal.org/project/clamav) | Scans uploaded files for malware with the ClamAV engine before they are saved. | [Settings](settings.mdx#per-module-overrides) | +| [`drupal/coffee`](https://www.drupal.org/project/coffee) | Adds a keyboard shortcut to jump straight to any administration page by typing its name. | | +| [`drupal/config_split`](https://www.drupal.org/project/config_split) | Splits configuration into sets that are conditionally imported, enabling environment-specific configuration such as development-only modules. | [Settings](settings.mdx#per-module-overrides) | +| [`drupal/config_update`](https://www.drupal.org/project/config_update) | Provides tools and Drush commands to report, revert and import configuration changes relative to the defaults shipped by modules. | | +| [`drupal/core-composer-scaffold`](https://www.drupal.org/docs/develop/using-composer/using-drupals-composer-scaffold) | Downloads **Drupal Scaffold** files such as `index.php` and `update.php` from the `drupal/core` project and places them inside the web root. | | +| [`drupal/core-recommended`](https://github.com/drupal/core-recommended) | Pins the set of dependency versions that the Drupal community tests and recommends for a particular core version, ensuring compatibility and stability. | | +| [`drupal/deploy_steps`](https://www.drupal.org/project/deploy_steps) | Defines and runs ordered deployment operations during site provisioning, replacing ad-hoc post-deployment scripts. | [Provision](provision.mdx) | +| [`drupal/devel`](https://www.drupal.org/project/devel) | A suite of development tools for inspecting variables, entities and the service container while debugging. | [Settings](settings.mdx#per-module-overrides) | +| [`drupal/drupal_helpers`](https://www.drupal.org/project/drupal_helpers) | A collection of helper functions that simplify writing update hooks and deployment operations. | [Drupal helpers](drupal-helpers.mdx) | +| [`drupal/environment_indicator`](https://www.drupal.org/project/environment_indicator) | Shows a colored banner identifying the current environment to prevent accidental changes on the wrong site. | [Settings](settings.mdx#per-module-overrides) | +| [`drupal/generated_content`](https://www.drupal.org/project/generated_content) | Generates deterministic placeholder content from declarative definitions for development and testing. | [Generated content](generated-content.mdx), [Settings](settings.mdx#per-module-overrides) | +| [`drupal/migrate_plus`](https://www.drupal.org/project/migrate_plus) | Extends the core Migrate API with extra source and process plugins and configuration-entity migrations. | [Migrations](migrations.mdx) | +| [`drupal/migrate_tools`](https://www.drupal.org/project/migrate_tools) | Provides Drush commands and a UI to run, roll back and monitor migrations. | [Migrations](migrations.mdx) | +| [`drupal/navigation_extra_tools`](https://www.drupal.org/project/navigation_extra_tools) | Adds administration shortcuts - clear caches, run cron, run database updates - to the core Navigation module. | | +| [`drupal/pathauto`](https://www.drupal.org/project/pathauto) | Automatically generates URL aliases for content based on configurable patterns. | | +| [`drupal/redirect`](https://www.drupal.org/project/redirect) | Manages URL redirects and creates them automatically when content URLs change. | | +| [`drupal/redis`](https://www.drupal.org/project/redis) | Integrates Drupal with the Redis in-memory store for cache and lock backends. | [Settings](settings.mdx#per-module-overrides) | +| [`drupal/reroute_email`](https://www.drupal.org/project/reroute_email) | Reroutes all outbound email to a configured address on non-production environments to avoid emailing real users. | [Settings](settings.mdx#per-module-overrides) | +| [`drupal/robotstxt`](https://www.drupal.org/project/robotstxt) | Manages the `robots.txt` file from the admin UI, useful when it cannot be placed on disk (for example, in multisite setups). | [Settings](settings.mdx#per-module-overrides) | +| [`drupal/sdc_devel`](https://www.drupal.org/project/sdc_devel) | Development and validation tooling for Single Directory Components (SDC), reporting issues in component definitions and templates. | | +| [`drupal/search_api`](https://www.drupal.org/project/search_api) | Provides a framework for building search experiences with pluggable indexing backends. | | +| [`drupal/search_api_solr`](https://www.drupal.org/project/search_api_solr) | A Search API backend that integrates Apache Solr for fast, scalable search. | | +| [`drupal/seckit`](https://www.drupal.org/project/seckit) | Adds configurable security-hardening HTTP headers, including Content Security Policy and anti-framing protection. | [Settings](settings.mdx#per-module-overrides) | +| [`drupal/shield`](https://www.drupal.org/project/shield) | Protects non-production environments behind HTTP basic authentication. | [Settings](settings.mdx#per-module-overrides) | +| [`drupal/stage_file_proxy`](https://www.drupal.org/project/stage_file_proxy) | Fetches media files from a remote site on demand, so local environments do not need a full copy of the files directory. | [Settings](settings.mdx#per-module-overrides) | +| [`drupal/testmode`](https://www.drupal.org/project/testmode) | Adjusts site behavior during automated tests, for example by filtering out generated content from listings. | [Testmode](testmode.mdx), [Settings](settings.mdx#per-module-overrides) | +| [`drupal/xmlsitemap`](https://www.drupal.org/project/xmlsitemap) | Generates a multilingual XML sitemap to help search engines index the site. | [Settings](settings.mdx#per-module-overrides) | +| [`drush/drush`](https://github.com/drush-ops/drush) | A command-line shell and scripting interface for Drupal, providing a wide range of utilities to manage and interact with your Drupal sites. | [Drush](../tools/drush.mdx) | +| [`webflo/drupal-finder`](https://github.com/webflo/drupal-finder) | Locates Drupal installations in a directory structure. | | ### `require-dev` @@ -192,83 +131,32 @@ production environments. This distinction helps to keep the production deployment streamlined and efficient, while still supporting a comprehensive and effective development environment. -- [`behat/behat`](https://github.com/Behat/Behat): A PHP framework for - Behavior-Driven Development (BDD), allowing you to write human-readable - stories that describe the behavior of your application. It facilitates - communication between developers, stakeholders, and clients. -- [`dantleech/gherkin-lint`](https://github.com/dantleech/gherkin-lint): A - linting tool for Gherkin feature files used in Behat tests. Ensures - consistency and quality in BDD test scenarios by checking syntax and - formatting. -- [`dealerdirect/phpcodesniffer-composer-installer`](https://github.com/Dealerdirect/phpcodesniffer-composer-installer): - This tool automatically configures PHP_CodeSniffer to use the coding - standards (like PSR-2 or Drupal coding standards) installed in a project. -- [`drevops/behat-format-progress-fail`](https://github.com/drevops/behat-format-progress-fail): - Enhances the output format of Behat tests, focusing specifically on progress - and failure scenarios. This makes it easier to spot and address test failures. -- [`drevops/behat-screenshot`](https://github.com/drevops/behat-screenshot): An - extension for Behat that automatically captures screenshots when tests fail. - This is helpful for debugging and understanding why a test failed. -- [`drevops/behat-steps`](https://github.com/drevops/behat-steps): Provides a - collection of pre-defined step definitions for Behat. This package speeds up - the process of writing new Behat tests by providing common step - implementations. -- [`drevops/phpcs-standard`](https://github.com/drevops/phpcs-standard): A - custom PHP_CodeSniffer coding standard that extends Drupal coding standards - with additional rules and best practices specific to DrevOps projects. -- [`drupal/coder`](https://www.drupal.org/project/coder): Provides - PHP_CodeSniffer rules for Drupal coding standards. Version 9.x supports - PHP_CodeSniffer 4.x and includes updated rules for modern Drupal development. -- [`drupal/drupal-extension`](https://github.com/jhedstrom/drupalextension): A - Behat extension that provides integration with Drupal, offering step - definitions specific to Drupal functionality. It facilitates the creation and - management of Drupal sites for testing purposes. -- [`ergebnis/composer-normalize`](https://github.com/ergebnis/composer-normalize): A - composer plugin for normalizing `composer.json`. -- [`lullabot/mink-selenium2-driver`](https://github.com/lullabot/mink-selenium2-driver): - A maintained fork of the Mink Selenium2 driver that lets Behat control real - browsers through Selenium WebDriver. -- [`lullabot/php-webdriver`](https://github.com/lullabot/php-webdriver): A - maintained fork of the PHP WebDriver client used by the Mink Selenium2 driver - to communicate with browsers. -- [`mglaman/phpstan-drupal`](https://github.com/mglaman/phpstan-drupal): - Integrates PHPStan static analysis with Drupal-specific code, helping identify - potential issues and bugs in Drupal modules and themes. -- [`mikey179/vfsstream`](https://github.com/bovigo/vfsStream): A virtual file - system for PHPUnit tests, allowing file operations to be exercised without - touching the real file system. -- [`palantirnet/drupal-rector`](https://github.com/palantirnet/drupal-rector): - Automates the process of updating deprecated code, making Drupal upgrade - processes more efficient. -- [`phpcompatibility/php-compatibility`](https://github.com/PHPCompatibility/PHPCompatibility): - Provides a collection of sniffs for PHP_CodeSniffer to check PHP code for - compatibility with different PHP versions, crucial for ensuring long-term - maintainability. Version 10.x supports PHP_CodeSniffer 4.x. -- [`phpspec/prophecy-phpunit`](https://github.com/phpspec/prophecy-phpunit): - Integrates the Prophecy mocking library with PHPUnit to provide advanced - mocking capabilities in tests. -- [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer): - This package automatically installs and enables PHPStan extensions, - streamlining the setup process for PHPStan in your project. -- [`phpstan/phpstan`](https://github.com/phpstan/phpstan): A static analysis - tool for PHP that focuses on finding bugs in code without running it. Helps - catch type errors, incorrect method calls, and other potential issues during - development. -- [`phpunit/phpunit`](https://github.com/sebastianbergmann/phpunit): The - industry-standard testing framework for PHP. Provides a comprehensive set of - tools for writing and running unit tests, integration tests, and functional - tests. -- [`pyrech/composer-changelogs`](https://github.com/pyrech/composer-changelogs): - Provides a summary of package changes (like updates, removals, and additions) - after running `composer update`, improving the visibility of package changes - and updates in your project. -- [`rector/rector`](https://github.com/rectorphp/rector): An automated - refactoring tool that instantly upgrades and refactors PHP code. Helps with - code modernization, framework migrations, and automated application of coding - standards. -- [`vincentlanglet/twig-cs-fixer`](https://github.com/VincentLanglet/Twig-CS-Fixer): This tool - ensures that Twig templates adhere to a set coding standard, helping maintain - consistency and readability in template files. +| Package | Description | Documentation | +|---------|-------------|---------------| +| [`behat/behat`](https://github.com/Behat/Behat) | A PHP framework for Behavior-Driven Development (BDD), running human-readable stories that describe the behavior of your application. It facilitates communication between developers, stakeholders and clients. | [Behat](../tools/behat.mdx) | +| [`dantleech/gherkin-lint`](https://github.com/dantleech/gherkin-lint) | Lints the Gherkin feature files used in Behat tests, checking syntax and formatting for consistency. | [Gherkin Lint](../tools/gherkin-lint.mdx) | +| [`dealerdirect/phpcodesniffer-composer-installer`](https://github.com/Dealerdirect/phpcodesniffer-composer-installer) | Registers the coding standards installed through Composer with PHP_CodeSniffer, so they can be referenced by name. | [PHPCS](../tools/phpcs.mdx) | +| [`drevops/behat-format-progress-fail`](https://github.com/drevops/behat-format-progress-fail) | A Behat output formatter that prints compact progress and expands the detail only for failing scenarios. | [Behat](../tools/behat.mdx) | +| [`drevops/behat-screenshot`](https://github.com/drevops/behat-screenshot) | Automatically captures a screenshot when a Behat scenario fails, which helps to understand why a test failed. | [Behat](../tools/behat.mdx) | +| [`drevops/behat-steps`](https://github.com/drevops/behat-steps) | A collection of pre-defined Behat step definitions for Drupal, speeding up the process of writing new tests. | [Behat](../tools/behat.mdx) | +| [`drevops/phpcs-standard`](https://github.com/drevops/phpcs-standard) | A PHP_CodeSniffer coding standard that extends the Drupal coding standards with additional rules and best practices. | [PHPCS](../tools/phpcs.mdx) | +| [`drupal/coder`](https://www.drupal.org/project/coder) | Provides the PHP_CodeSniffer rules for the Drupal coding standards. The 9.x line targets PHP_CodeSniffer 4.x. | [PHPCS](../tools/phpcs.mdx) | +| [`drupal/drupal-extension`](https://github.com/jhedstrom/drupalextension) | A Behat extension that integrates with Drupal, offering Drupal-specific step definitions and bootstrapping the site for testing. | [Behat](../tools/behat.mdx) | +| [`ergebnis/composer-normalize`](https://github.com/ergebnis/composer-normalize) | A Composer plugin that normalizes the formatting and key order of `composer.json`. | | +| [`lullabot/mink-selenium2-driver`](https://github.com/lullabot/mink-selenium2-driver) | A maintained fork of the Mink Selenium2 driver that lets Behat control real browsers through Selenium WebDriver. | [Behat](../tools/behat.mdx) | +| [`lullabot/php-webdriver`](https://github.com/lullabot/php-webdriver) | A maintained fork of the PHP WebDriver client used by the Mink Selenium2 driver to communicate with browsers. | [Behat](../tools/behat.mdx) | +| [`mglaman/phpstan-drupal`](https://github.com/mglaman/phpstan-drupal) | Teaches PHPStan about Drupal APIs, so static analysis understands hooks, services and entities in modules and themes. | [PHPStan](../tools/phpstan.mdx) | +| [`mikey179/vfsstream`](https://github.com/bovigo/vfsStream) | A virtual file system for PHPUnit tests, allowing file operations to be exercised without touching the real file system. | [PHPUnit](../tools/phpunit.mdx) | +| [`palantirnet/drupal-rector`](https://github.com/palantirnet/drupal-rector) | Rector rules that rewrite deprecated Drupal code, making core upgrades more efficient. | [Rector](../tools/rector.mdx) | +| [`phpcompatibility/php-compatibility`](https://github.com/PHPCompatibility/PHPCompatibility) | PHP_CodeSniffer sniffs that check code for compatibility with specific PHP versions. The 10.x line targets PHP_CodeSniffer 4.x. | [PHPCS](../tools/phpcs.mdx) | +| [`phpspec/prophecy-phpunit`](https://github.com/phpspec/prophecy-phpunit) | Integrates the Prophecy mocking library with PHPUnit to provide advanced mocking capabilities in tests. | [PHPUnit](../tools/phpunit.mdx) | +| [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer) | Automatically registers the installed PHPStan extensions, removing the need to wire them up by hand. | [PHPStan](../tools/phpstan.mdx) | +| [`phpstan/phpstan`](https://github.com/phpstan/phpstan) | A static analysis tool that finds type errors, incorrect method calls and other bugs without running the code. | [PHPStan](../tools/phpstan.mdx) | +| [`phpunit/phpunit`](https://github.com/sebastianbergmann/phpunit) | The industry-standard PHP testing framework, used here for unit, kernel and functional tests. | [PHPUnit](../tools/phpunit.mdx) | +| [`pyrech/composer-changelogs`](https://github.com/pyrech/composer-changelogs) | Prints a summary of package additions, updates and removals after running `composer update`. | | +| [`rector/rector`](https://github.com/rectorphp/rector) | An automated refactoring tool that upgrades and modernizes PHP code. | [Rector](../tools/rector.mdx) | +| [`softcreatr/jsonpath`](https://github.com/SoftCreatR/JSONPath) | A JSONPath implementation required by the `drevops/behat-steps` JSON assertion steps. | [Behat](../tools/behat.mdx) | +| [`vincentlanglet/twig-cs-fixer`](https://github.com/VincentLanglet/Twig-CS-Fixer) | Checks and fixes Twig templates against a coding standard, keeping template files consistent and readable. | [Twig CS Fixer](../tools/twig-cs-fixer.mdx) | ### `conflict` diff --git a/.vortex/docs/content/drupal/drupal-helpers.mdx b/.vortex/docs/content/drupal/drupal-helpers.mdx index 54cbaa7b9..63e27a067 100644 --- a/.vortex/docs/content/drupal/drupal-helpers.mdx +++ b/.vortex/docs/content/drupal/drupal-helpers.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 6 +sidebar_position: 10 --- # Drupal helpers @@ -8,6 +8,10 @@ sidebar_position: 6 library that provides static facade helpers for common Drupal development tasks, primarily intended for use within deploy hooks and update scripts. +This page covers how **Vortex** uses the module. The +[module documentation](https://git.drupalcode.org/project/drupal_helpers/-/blob/HEAD/README.md) +is the reference for its full API. + ## Helper facade The `Helper` class provides convenient access to helper services without needing @@ -26,36 +30,50 @@ Helper::menu()->createTree('main', [ ]); // Delete all entities of a type. -Helper::entity()->deleteAll('node', 'article'); +Helper::entity()->deleteAll('node', 'page'); ``` +Each facade throws a `RuntimeException` when the helper needs a module that is +not installed, so a deploy hook fails loudly rather than half-applying. + ## Available helpers -| Helper | Access | Common operations | -|--------|--------|-------------------| +Every facade is listed below. The **Examples** column shows a few of the methods +each one exposes, not the complete set - see the module documentation for that. + +| Helper | Access | Examples | +|--------|--------|----------| | Term | `Helper::term()` | `createTree()`, `deleteAll()`, `find()` | | Menu | `Helper::menu()` | `createTree()`, `deleteTree()`, `findItem()`, `updateItem()` | -| Entity | `Helper::entity()` | `deleteAll()`, `batch()` | -| Config | `Helper::config()` | Import and manage config YAML | -| User | `Helper::user()` | Create accounts, assign roles | -| Redirect | `Helper::redirect()` | Create redirects, import from CSV | -| Field | `Helper::field()` | Delete fields with data purging | +| Entity | `Helper::entity()` | `deleteAll()` | +| Config | `Helper::config()` | `get()`, `set()`, `import()`, `setFrontPage()` | +| User | `Helper::user()` | `create()`, `createMultiple()`, `assignRoles()`, `removeRoles()` | +| Redirect | `Helper::redirect()` | `create()`, `deleteBySource()`, `importFromCsv()` | +| Field | `Helper::field()` | `delete()`, `deleteInstance()` | ## Batched operations -For large datasets, pass a `$sandbox` array to enable automatic batching: +Every facade except `Helper::config()` accepts a `$sandbox` array, which turns +on `batch()` and `batchEntity()` so large datasets are processed across multiple +deploy hook passes: ```php -function ys_base_deploy_update_articles(array &$sandbox): ?string { - return Helper::entity($sandbox)->batch('node', 'article', function ($node) { +function ys_base_deploy_update_pages(array &$sandbox): ?string { + return Helper::entity($sandbox)->batchEntity('node', 'page', function ($node) { $node->set('field_migrated', TRUE); $node->save(); }); } ``` +`batchEntity()` queries the entity IDs itself; `batch()` takes an arbitrary +array of items instead. Both return the progress message Drush prints, and the +batch size defaults to 50 - pass a second argument to the facade to change it, +for example `Helper::entity($sandbox, 100)`. + ## Example in Vortex The [`ys_demo.deploy.php`](https://github.com/drevops/vortex/blob/main/web/modules/custom/ys_demo/ys_demo.deploy.php) -file demonstrates using drupal_helpers to create a menu link for the articles -page during deployment. +file uses `Helper::menu()` to add a `Pages` link to the main navigation during +deployment, guarding the operation with `findItem()` so re-running the hook does +not create a duplicate. diff --git a/.vortex/docs/content/drupal/generated-content.mdx b/.vortex/docs/content/drupal/generated-content.mdx index f59f05ff1..0e20dcd5f 100644 --- a/.vortex/docs/content/drupal/generated-content.mdx +++ b/.vortex/docs/content/drupal/generated-content.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 7 +sidebar_position: 11 --- # Generated content @@ -9,6 +9,10 @@ a plugin-based system for programmatically generating deterministic content entities. Unlike random dummy content, generated content produces reproducible sets useful for visual regression testing and consistent demo environments. +This page covers how **Vortex** uses the module. The +[module documentation](https://git.drupalcode.org/project/generated_content/-/blob/HEAD/README.md) +is the reference for its full API. + ## Plugin system Content generators are PHP classes placed in a module's @@ -18,27 +22,38 @@ attribute. ### Creating a plugin ```php -namespace Drupal\ys_demo\Plugin\GeneratedContent; +namespace Drupal\ys_demo\Plugin\GeneratedContent\Node; use Drupal\generated_content\Attribute\GeneratedContent; use Drupal\generated_content\Plugin\GeneratedContent\GeneratedContentPluginBase; -use Drupal\taxonomy\Entity\Term; #[GeneratedContent( - id: 'ys_demo_taxonomy_term_tags', - entity_type: 'taxonomy_term', - bundle: 'tags', - weight: 10, + id: 'ys_demo_node_page', + entity_type: 'node', + bundle: 'page', + weight: 20, )] -class TaxonomyTermTags extends GeneratedContentPluginBase { +class Page extends GeneratedContentPluginBase { public function generate(): array { $entities = []; + $storage = $this->entityTypeManager->getStorage('node'); + + for ($i = 1; $i <= 20; $i++) { + $node = $storage->create([ + 'type' => 'page', + 'title' => sprintf('Demo page %s %s', $i, $this->helper::staticName()), + 'status' => 1, + ]); + + $node->set('body', [ + 'value' => $this->helper::staticRichText(3), + 'format' => 'full_html', + ]); - foreach (['Technology', 'Science', 'Health'] as $name) { - $term = Term::create(['vid' => 'tags', 'name' => $name]); - $term->save(); - $entities[] = $term; + $node->save(); + + $entities[] = $node; } return $entities; @@ -56,17 +71,51 @@ class TaxonomyTermTags extends GeneratedContentPluginBase { | `bundle` | string | yes | Target bundle | | `weight` | int | no | Execution order (lower = earlier) | | `tracking` | bool | no | Track created entities for cleanup (default: `TRUE`) | +| `label` | TranslatableMarkup | no | Human-readable name shown in the admin UI | | `helper` | string | no | Custom helper class extending `GeneratedContentHelper` | -### Cross-referencing entities +## Generation helpers + +`$this->helper` is a `GeneratedContentHelper` singleton that produces field +values, creates file assets, and looks up entities that earlier plugins +created. The examples below are a sample of what it offers - the module +documentation lists the full set. + +**Static** helpers return the same values on every run, which is what visual +regression testing needs: + +```php +$this->helper::staticName(); // Fixed-length name. +$this->helper::staticSentence(5); // Sentence of 5 words. +$this->helper::staticRichText(3); // 3 paragraphs of HTML. +``` + +**Random** helpers vary on every run, which suits content that only needs to +make the site look populated: -Use `weight` to control execution order and `$this->helper` to reference -previously generated entities: +```php +$this->helper::randomName(); +$this->helper::randomSentence(5, 10); // Between 5 and 10 words. +$this->helper::randomEmail(); +$this->helper::randomTimestamp('-1year', 'now'); +``` + +**Entity** helpers reference content that earlier plugins produced. Use +`weight` to make sure those plugins ran first, and pick the `static` variant +when the reference itself must stay stable between runs: ```php // In a node plugin with weight: 20 (runs after terms at weight: 10). -$tags = $this->helper::randomTerms('tags', 3); -$node->set('field_tags', $tags); +$node->set('field_tags', $this->helper::randomTerms('tags', 3)); +$node->set('field_author', $this->helper::staticUser()); +``` + +**Asset** helpers create managed files from the dummy assets that ship with the +module, covering image, document, audio and video extensions: + +```php +$node->set('field_image', $this->helper::createFile('png')); +$node->set('field_attachment', $this->helper::createFile('pdf')); ``` ## Triggering generation @@ -75,7 +124,7 @@ $node->set('field_tags', $tags); ```shell drush generated-content:create-content -drush generated-content:create-content node article +drush generated-content:create-content node page ``` ### Admin UI @@ -89,7 +138,7 @@ Set `GENERATED_CONTENT_CREATE=1` before provisioning to auto-generate content on module install. Optionally filter: ```shell -GENERATED_CONTENT_ITEMS="taxonomy_term-tags,node-article" +GENERATED_CONTENT_ITEMS="taxonomy_term-tags,node-page" ``` ### Provisioning @@ -100,7 +149,8 @@ GENERATED_CONTENT_ITEMS="taxonomy_term-tags,node-article" ## Example in Vortex -The `ys_demo` module ships two generated content plugins: - -- `TaxonomyTermTags` — generates 5 taxonomy terms in the `tags` vocabulary -- `NodeArticle` — generates 20 article nodes referencing generated tags +The `ys_demo` module ships a single generated content plugin, +[`Page`](https://github.com/drevops/vortex/blob/main/web/modules/custom/ys_demo/src/Plugin/GeneratedContent/Node/Page.php): +it creates 20 `page` nodes whose titles and body text come from the static +helpers, so the same pages appear on every rebuild. The nodes back the demo +`/pages` view used by the [Testmode](testmode.mdx) example. diff --git a/.vortex/docs/content/drupal/migrations.mdx b/.vortex/docs/content/drupal/migrations.mdx index 1d6a75c43..b074dec4d 100644 --- a/.vortex/docs/content/drupal/migrations.mdx +++ b/.vortex/docs/content/drupal/migrations.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 7 +sidebar_position: 6 --- # Migrations diff --git a/.vortex/docs/content/drupal/module-scaffold.mdx b/.vortex/docs/content/drupal/module-scaffold.mdx index 1f010a10d..343142e46 100644 --- a/.vortex/docs/content/drupal/module-scaffold.mdx +++ b/.vortex/docs/content/drupal/module-scaffold.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 5 +sidebar_position: 7 --- # Module scaffold @@ -33,12 +33,13 @@ to run deployment commands during the site [provisioning](provision) process. The `ys_demo` module demonstrates integration patterns for several contributed modules: -- [Drupal helpers](drupal-helpers) — utility facades for deploy hooks -- [Generated content](generated-content) — plugin-based content generation -- [Test mode](test-mode) — content filtering during Behat tests +- [Drupal helpers](drupal-helpers) - utility facades for deploy hooks +- [Generated content](generated-content) - plugin-based content generation +- [Testmode](testmode) - content filtering during Behat tests -The demo module ships an articles view at `/articles`, generated content plugins -for tags and articles, and testmode configuration for Behat testing. +The demo module ships a pages view at `/pages`, a generated content plugin that +populates it, and the deploy hooks that place a counter block, add the `Pages` +menu link, and register the view with testmode. ## Tests scaffold diff --git a/.vortex/docs/content/drupal/modules.mdx b/.vortex/docs/content/drupal/modules.mdx new file mode 100644 index 000000000..be89ad505 --- /dev/null +++ b/.vortex/docs/content/drupal/modules.mdx @@ -0,0 +1,90 @@ +--- +sidebar_position: 9 +--- + +# Modules + +**Vortex** is not a Drupal distribution: it ships no installation profile and +no recipes. It provides a small set of contributed modules that most projects +need, each already wired into the environment-aware +[settings](settings.mdx) and the [provisioning](provision.mdx) scripts. You add +the rest once the initial setup is done. + +Every module listed here is optional. The CLI asks which ones to keep, +and deselecting one removes its Composer requirement, its settings override, +its line in the provisioning script, its Behat feature and its settings test +assertions in a single pass. + +## How modules are configured + +| Mechanism | Location | Effect | +|-----------|----------|--------| +| Composer requirement | `require` in `composer.json` | Makes the module available to the site. ➡️ See [composer.json](composer-json.mdx#require) | +| Settings override | `web/sites/default/includes/modules/settings..php` | Applies configuration conditioned on the detected environment, reading values from environment variables. ➡️ See [Settings](settings.mdx#per-module-overrides) | +| Config export exclusion | `$settings['config_exclude_modules']` in a settings override | Keeps a development-only module out of the exported configuration, so enabling it locally never dirties the config | +| Provisioning | `CreateContentModelDeployStep` in `ys_base` | Creates the demo content model in the `local`, `ci`, `dev` and `stage` environments only | +| Development provisioning | `EnableDevelopmentModulesDeployStep` in `ys_base` | Installs the demo and development-only modules in the same four environments | +| Module dependency | `ys_demo.info.yml` | Installs the module as a dependency of the demo module | + +A module can be required without being installed anywhere: the settings +override is then ready for the day the project enables it. + +## Site building + +| Module | Purpose | Configuration provided by **Vortex** | +|--------|---------|--------------------------------------| +| [`coffee`](https://www.drupal.org/project/coffee) | Jump to any administration page by typing its name | Installed during provisioning. No settings override | +| [`config_split`](https://www.drupal.org/project/config_split) | Conditionally imported configuration sets | `settings.config_split.php` enables the split matching the detected environment - `local`, `ci`, `dev` or `stage` - so each environment imports only its own set | +| [`config_update`](https://www.drupal.org/project/config_update) | Report, revert and import configuration against module defaults | Installed during provisioning. No settings override | +| [`navigation_extra_tools`](https://www.drupal.org/project/navigation_extra_tools) | Cache, cron and update shortcuts in the core Navigation module | Installed during provisioning, after the core Navigation module replaces the classic Toolbar. No settings override | +| [`pathauto`](https://www.drupal.org/project/pathauto) | Automatic URL aliases from configurable patterns | Installed during provisioning. No settings override | +| [`redirect`](https://www.drupal.org/project/redirect) | URL redirect management | Installed during provisioning. No settings override | +| [`xmlsitemap`](https://www.drupal.org/project/xmlsitemap) | Multilingual XML sitemap | `settings.xmlsitemap.php` stops cron regeneration and search engine submission outside production, so non-production environments are never advertised | + +## Security + +| Module | Purpose | Configuration provided by **Vortex** | +|--------|---------|--------------------------------------| +| [`seckit`](https://www.drupal.org/project/seckit) | Security-hardening HTTP headers | `settings.seckit.php` disables the Content Security Policy and the upgrade-insecure-requests header locally and in CI, where the site is not served over HTTPS. Not installed during provisioning - enable it when the project needs it | +| [`shield`](https://www.drupal.org/project/shield) | HTTP basic authentication in front of the site | `settings.shield.php` forces Shield on in non-production environments other than `local` and `ci`, where it is forced off. Production is left untouched, so Shield stays under UI control there. Credentials come from `DRUPAL_SHIELD_USER` and `DRUPAL_SHIELD_PASS`; `DRUPAL_SHIELD_PRINT` overrides the prompt title, `DRUPAL_SHIELD_DISABLED` turns it off for one environment, and `DRUPAL_SHIELD_ALLOW_ACME_CHALLENGE` opens the Let's Encrypt challenge path | +| [`clamav`](https://www.drupal.org/project/clamav) | Malware scanning of uploaded files | `settings.clamav.php` selects daemon or executable mode from `DRUPAL_CLAMAV_MODE` and points the daemon at `CLAMAV_HOST` and `CLAMAV_PORT`, but only when `DRUPAL_CLAMAV_ENABLED` is set. Ships only when the ClamAV service is selected | + +## Operations + +| Module | Purpose | Configuration provided by **Vortex** | +|--------|---------|--------------------------------------| +| [`environment_indicator`](https://www.drupal.org/project/environment_indicator) | Colored banner naming the current environment | `settings.environment_indicator.php` names the indicator after the detected environment and colors it per environment - red for production, yellow for stage, green for dev - with the toolbar integration and favicon marker turned on | +| [`reroute_email`](https://www.drupal.org/project/reroute_email) | Redirects outbound email away from real users | `settings.reroute_email.php` enables rerouting in every environment except `local`, `ci`, `stage` and `prod`, which covers pull request environments. `DRUPAL_REROUTE_EMAIL_ADDRESS` and `DRUPAL_REROUTE_EMAIL_ALLOWED` set the target and the allowlist, and `DRUPAL_REROUTE_EMAIL_DISABLED` turns it off entirely | +| [`robotstxt`](https://www.drupal.org/project/robotstxt) | Serves `robots.txt` from the database | `settings.robotstxt.php` serves a `Disallow: /` file outside production, so non-production environments are not indexed | +| [`stage_file_proxy`](https://www.drupal.org/project/stage_file_proxy) | Fetches media files from a remote site on demand | `settings.stage_file_proxy.php` sets the origin from `DRUPAL_STAGE_FILE_PROXY_ORIGIN` outside production and injects the Shield credentials into that URL when they are set, so a shielded origin still serves files | +| [`redis`](https://www.drupal.org/project/redis) | Redis cache and lock backends | `settings.redis.php` points the connection at `REDIS_HOST` and `REDIS_SERVICE_PORT`, makes Redis the default cache backend, registers the module container YAML files and swaps the bootstrap container over - all gated on `DRUPAL_REDIS_ENABLED` and on the `redis` PHP extension being loaded, so a two-stage deployment can provision the service before switching the cache. Ships only when the Redis service is selected | + +## Search + +| Module | Purpose | Configuration provided by **Vortex** | +|--------|---------|--------------------------------------| +| [`search_api`](https://www.drupal.org/project/search_api) | Search framework with pluggable backends | Installed during provisioning alongside the Solr backend. No settings override | +| [`search_api_solr`](https://www.drupal.org/project/search_api_solr) | Apache Solr backend for Search API | Installed during provisioning. Ships only when the Solr service is selected | + +## Development and testing + +| Module | Purpose | Configuration provided by **Vortex** | +|--------|---------|--------------------------------------| +| [`devel`](https://www.drupal.org/project/devel) | Inspect variables, entities and the service container | Installed by the development provisioning script. `settings.devel.php` excludes it from the exported configuration | +| [`sdc_devel`](https://www.drupal.org/project/sdc_devel) | Validation for Single Directory Components | Installed by the development provisioning script. No settings override | +| [`generated_content`](https://www.drupal.org/project/generated_content) | Deterministic placeholder content from plugins | Installed by the development provisioning script with `GENERATED_CONTENT_CREATE=1`, unless `DRUPAL_GENERATED_CONTENT_SKIP=1` installs it without generating. `settings.generated_content.php` excludes it from the exported configuration. ➡️ See [Generated content](generated-content.mdx) | +| [`testmode`](https://www.drupal.org/project/testmode) | Filters non-test content out of registered views | Installed as a dependency of the demo module. `settings.testmode.php` excludes it from the exported configuration. ➡️ See [Testmode](testmode.mdx) | +| [`drupal_helpers`](https://www.drupal.org/project/drupal_helpers) | Static facades for deploy hooks and update scripts | Installed as a dependency of the demo module. No settings override. ➡️ See [Drupal helpers](drupal-helpers.mdx) | + +## Migrations + +These two modules ship only when the migration feature is selected. The +CLI removes both requirements otherwise. + +| Module | Purpose | Configuration provided by **Vortex** | +|--------|---------|--------------------------------------| +| [`migrate_plus`](https://www.drupal.org/project/migrate_plus) | Extra source and process plugins, and configuration-entity migrations | Used by the demo migration module against the second database. No settings override | +| [`migrate_tools`](https://www.drupal.org/project/migrate_tools) | Drush commands and a UI to run, roll back and monitor migrations | Driven by `MigrateContentDeployStep` in `ys_migrate` during provisioning. No settings override | + +➡️ See [Migrations](migrations.mdx) for the second database and the demo +migration. diff --git a/.vortex/docs/content/drupal/provision.mdx b/.vortex/docs/content/drupal/provision.mdx index 680f8c724..8387ec454 100644 --- a/.vortex/docs/content/drupal/provision.mdx +++ b/.vortex/docs/content/drupal/provision.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 3 +sidebar_position: 4 --- # Provision diff --git a/.vortex/docs/content/drupal/settings.mdx b/.vortex/docs/content/drupal/settings.mdx index 257e4f59b..7ba2d02d5 100644 --- a/.vortex/docs/content/drupal/settings.mdx +++ b/.vortex/docs/content/drupal/settings.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 2 +sidebar_position: 3 --- # Settings diff --git a/.vortex/docs/content/drupal/test-mode.mdx b/.vortex/docs/content/drupal/test-mode.mdx deleted file mode 100644 index 071d64f59..000000000 --- a/.vortex/docs/content/drupal/test-mode.mdx +++ /dev/null @@ -1,76 +0,0 @@ ---- -sidebar_position: 8 ---- - -# Test mode - -[Testmode](https://www.drupal.org/project/testmode) filters site content during -Behat tests, preventing live or generated content from interfering with test -assertions. - -## How it works - -1. Test content follows a naming convention — titles prefixed with `[TEST]` -2. Views are registered in testmode configuration -3. Behat scenarios tagged with `@testmode` automatically enable/disable filtering -4. When enabled, registered views only show content matching the `[TEST]` pattern - -## Configuration - -Testmode is configured via `testmode.settings`: - -| Key | Type | Description | -|-----|------|-------------| -| `views_node` | string[] | Node view machine names to filter | -| `views_term` | string[] | Term view machine names to filter | -| `views_user` | string[] | User view machine names to filter | -| `pattern_node` | string[] | MySQL LIKE patterns for node titles | -| `pattern_term` | string[] | MySQL LIKE patterns for term names | -| `pattern_user` | string[] | MySQL LIKE patterns for user emails | - -### Registering a view programmatically - -Use deploy hooks to register views with testmode: - -```php -function ys_demo_deploy_configure_testmode(): string { - $testmode = \Drupal\testmode\Testmode::getInstance(); - - $views = $testmode->getNodeViews(); - if (!in_array('my_view', $views)) { - $views[] = 'my_view'; - $testmode->setNodeViews($views); - } - - return 'Configured testmode for my_view.'; -} -``` - -## Behat integration - -The `@testmode` tag activates test mode for individual scenarios via -`TestmodeTrait` from -[behat-steps](https://github.com/drevops/behat-steps): - -```gherkin -@testmode -Scenario: Articles view shows only test content - Given the following article content: - | title | status | - | [TEST] Test mode article | 1 | - | Regular production article | 1 | - When I visit "/articles" - Then I should see "[TEST] Test mode article" - And I should not see "Regular production article" -``` - -The `[TEST]` prefix in content titles matches the default `[TEST%` pattern -configured in testmode. Only matching content appears in registered views. - -## Example in Vortex - -The `ys_demo` module: - -- Ships an articles view at `/articles` -- Registers it with testmode via a deploy hook -- Includes a Behat feature demonstrating the `@testmode` tag diff --git a/.vortex/docs/content/drupal/testmode.mdx b/.vortex/docs/content/drupal/testmode.mdx new file mode 100644 index 000000000..e22d8e4c4 --- /dev/null +++ b/.vortex/docs/content/drupal/testmode.mdx @@ -0,0 +1,97 @@ +--- +sidebar_position: 12 +--- + +# Testmode + +[Testmode](https://www.drupal.org/project/testmode) filters site content during +Behat tests, preventing live or generated content from interfering with test +assertions. + +This page covers how **Vortex** uses the module. The +[module documentation](https://git.drupalcode.org/project/testmode/-/blob/HEAD/README.md) +is the reference for its full API. + +## How it works + +1. Test content follows a naming convention - titles prefixed with `[TEST]` +2. Views are registered in Testmode configuration +3. Behat scenarios tagged with `@testmode` automatically enable and disable filtering +4. When enabled, registered views only show content matching the `[TEST]` pattern + +## Configuration + +Testmode is configured through the `testmode.settings` configuration object: + +| Key | Type | Description | +|-----|------|-------------| +| `views_node` | string[] | Node view machine names to filter | +| `views_term` | string[] | Term view machine names to filter | +| `views_user` | string[] | User view machine names to filter | +| `pattern_node` | string[] | MySQL LIKE patterns for node titles | +| `pattern_term` | string[] | MySQL LIKE patterns for term names | +| `pattern_user` | string[] | MySQL LIKE patterns for user emails | +| `list_term` | bool | Whether to filter term listings | + +**Vortex** adds `testmode` to `config_exclude_modules` in +`settings.testmode.php`, so the module never leaks into an exported +configuration. + +### Registering a view programmatically + +The `Testmode` singleton reads and writes that configuration. Use it from a +deploy hook to register a view: + +```php +use Drupal\testmode\Testmode; + +function ys_demo_deploy_configure_testmode(): string { + $testmode = Testmode::getInstance(); + + $views = $testmode->getNodeViews(); + if (!in_array('ys_demo_pages', $views)) { + $views[] = 'ys_demo_pages'; + $testmode->setNodeViews($views); + } + + return 'Configured testmode to filter the pages view.'; +} +``` + +The same singleton exposes `getTermViews()`, `getUserViews()` and the matching +pattern getters and setters, plus `enableTestMode()`, `disableTestMode()` and +`isTestMode()` for driving the mode directly. + +## Behat integration + +The `@testmode` tag activates Testmode for individual scenarios via +`TestmodeTrait` from +[behat-steps](https://github.com/drevops/behat-steps): + +```gherkin +@api @testmode +Scenario: Pages view shows only test content when test mode is enabled + Given the following page content: + | title | status | moderation_state | + | [TEST] First test page | 1 | published | + | [TEST] Second test page | 1 | published | + When I go to "/pages" + Then I should see "[TEST] First test page" + And I should see "[TEST] Second test page" + And I should not see "Demo page" +``` + +The `[TEST]` prefix in content titles matches the default `[TEST%` pattern +configured in Testmode. Only matching content appears in registered views, so +the generated `Demo page` nodes stay out of the assertions. + +## Example in Vortex + +The `ys_demo` module: + +- Ships a pages view at `/pages` +- Registers it with Testmode via the `ys_demo_deploy_configure_testmode` deploy hook +- Includes the + [`pages.feature`](https://github.com/drevops/vortex/blob/main/tests/behat/features/pages.feature) + Behat feature demonstrating the `@testmode` tag against the + [generated content](generated-content.mdx) it filters out diff --git a/.vortex/docs/content/drupal/theme-scaffold.mdx b/.vortex/docs/content/drupal/theme-scaffold.mdx index 22714546e..4a586a778 100644 --- a/.vortex/docs/content/drupal/theme-scaffold.mdx +++ b/.vortex/docs/content/drupal/theme-scaffold.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 6 +sidebar_position: 8 --- # Theme scaffold diff --git a/.vortex/docs/content/drupal/update-hooks.mdx b/.vortex/docs/content/drupal/update-hooks.mdx index 7d6420c29..be18c538d 100644 --- a/.vortex/docs/content/drupal/update-hooks.mdx +++ b/.vortex/docs/content/drupal/update-hooks.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 4 +sidebar_position: 5 --- # Update hooks diff --git a/.vortex/docs/cspell.json b/.vortex/docs/cspell.json index a6cda6653..59c25b250 100644 --- a/.vortex/docs/cspell.json +++ b/.vortex/docs/cspell.json @@ -56,6 +56,7 @@ "hotfixes", "htpasswd", "initialise", + "jsonpath", "lagooncli", "lando", "langid", @@ -90,6 +91,7 @@ "seckit", "shellvar", "simpletest", + "softcreatr", "solrcore", "sqldump", "testmode", diff --git a/.vortex/docs/docusaurus.config.js b/.vortex/docs/docusaurus.config.js index 15aa9746b..8694b9528 100644 --- a/.vortex/docs/docusaurus.config.js +++ b/.vortex/docs/docusaurus.config.js @@ -295,6 +295,10 @@ const config = { from: ['/contributing'], to: '/docs/contributing', }, + { + from: '/docs/drupal/test-mode', + to: '/docs/drupal/testmode', + }, { from: '/docs/contributing/maintenance/scripts', to: '/docs/contributing/maintenance/template', From 35dbbf1cdd78df4e25d448af756eff24168cb6b0 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:06:40 +1000 Subject: [PATCH 14/57] Disabled the Solr search server while migrations run. Forward-ported from main 3ef3c7677a797260f4cb445d7fc34c8241ff9177. Re-implemented in 'MigrateContentDeployStep', which replaced 'scripts/provision-20-migration.sh'; dropped the shell output-helper guidance, which has no counterpart in the PHP tooling. --- .vortex/docs/content/drupal/migrations.mdx | 6 ++++ .../DeployStep/MigrateContentDeployStep.php | 28 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/.vortex/docs/content/drupal/migrations.mdx b/.vortex/docs/content/drupal/migrations.mdx index b074dec4d..1e9bba60f 100644 --- a/.vortex/docs/content/drupal/migrations.mdx +++ b/.vortex/docs/content/drupal/migrations.mdx @@ -111,6 +111,11 @@ It handles: - Corruption detection (probes for a known table to verify the database) - Running Drupal migrations via `drush migrate:import` (in a memory-bounded, resumable subprocess) - Optional rollback before import +- Disabling the Solr search server while the migrations run + +On projects with the Solr service, the plugin disables the search server before the migrations and enables it again afterward, so migrated entities are not indexed one at a time. The `RebuildSearchIndex` deploy step rebuilds the index straight after. + +Set `DRUPAL_MIGRATION_SEARCH_DISABLE=0` to leave the search server running and index entities as they are migrated. ### Configuration variables @@ -125,6 +130,7 @@ The plugin reads these environment variables (via `getenv()`): | `DRUPAL_MIGRATION_FEEDBACK` | `50` | Progress feedback frequency | | `DRUPAL_MIGRATION_SOURCE_DB_IMPORT` | `$VORTEX_PROVISION_OVERRIDE_DB` | Import source database (`1` to import, `0` to skip) | | `DRUPAL_MIGRATION_SOURCE_DB_PROBE_TABLE` | `categories` | Table name to probe for corruption detection | +| `DRUPAL_MIGRATION_SEARCH_DISABLE` | `1` | Disable the Solr search server while migrations run (`0` to keep indexing) | ### Adding migrations diff --git a/web/modules/custom/ys_migrate/src/Plugin/DeployStep/MigrateContentDeployStep.php b/web/modules/custom/ys_migrate/src/Plugin/DeployStep/MigrateContentDeployStep.php index 0c430da95..d87ab591f 100644 --- a/web/modules/custom/ys_migrate/src/Plugin/DeployStep/MigrateContentDeployStep.php +++ b/web/modules/custom/ys_migrate/src/Plugin/DeployStep/MigrateContentDeployStep.php @@ -96,7 +96,35 @@ public function run(): void { $options['update'] = TRUE; } + // Indexing each migrated entity as it is saved makes the import slower and + // leaves the index holding intermediate states, so the search server is + // disabled for the duration of the run and the index is rebuilt after it. + $disable_search = $this->searchDisabled(); + + if ($disable_search) { + $this->drush('search-api:server-disable', ['solr']); + } + $this->drush('migrate:import', [], $options); + + if ($disable_search) { + $this->drush('search-api:server-enable', ['solr']); + $this->drush('search-api:enable-all'); + } + } + + /** + * Checks whether the search server should be disabled during the import. + * + * @return bool + * TRUE when the Solr server is present and disabling it was not opted out. + */ + protected function searchDisabled(): bool { + if ($this->env('DRUPAL_MIGRATION_SEARCH_DISABLE', '1') !== '1') { + return FALSE; + } + + return $this->moduleHandler->moduleExists('search_api_solr'); } /** From a51ce70c217ab37f76a893701cdc8e67da1c7fd2 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:08:41 +1000 Subject: [PATCH 15/57] Freed preinstalled runner toolchains in the GitHub Actions test and build jobs. (cherry picked from commit c0c2aaa1c0c348ae32b6998518367cf24ba25efe) Removes the toolchains directly rather than through a bind-mounting container, because these jobs run on the runner rather than inside one. Named the variable 'VORTEX_CI_FREE_DISK_SPACE' to match the prefix already used here, and targeted the 'test' job that absorbed the separate 'database' job. --- .github/workflows/build-test-deploy.yml | 22 +++++++++ .../.utils/variables/extra/ci.variables.sh | 3 ++ .../content/continuous-integration/README.mdx | 45 ++++++++++++------- .../continuous-integration/github-actions.mdx | 8 ++++ 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build-test-deploy.yml b/.github/workflows/build-test-deploy.yml index eafc12d1a..3b85242f3 100644 --- a/.github/workflows/build-test-deploy.yml +++ b/.github/workflows/build-test-deploy.yml @@ -230,6 +230,17 @@ jobs: #;> !PROVISION_TYPE_PROFILE steps: + # Frees disk space by removing preinstalled toolchains unused by this project. + - name: Free up disk space on the runner + if: ${{ vars.VORTEX_CI_FREE_DISK_SPACE == '1' }} + run: | + set -- /usr/local/.ghcup /usr/share/swift /usr/local/share/powershell /usr/share/dotnet /opt/hostedtoolcache/CodeQL + # set -- "$@" /usr/local/lib/android # Removes about 10GB more, but takes about 50s. Uncomment to enable. + sudo rm -rf "$@" || true + + - name: Report disk usage + run: df -h + - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: @@ -565,6 +576,17 @@ jobs: VORTEX_DEBUG: ${{ vars.VORTEX_DEBUG }} steps: + # Frees disk space by removing preinstalled toolchains unused by this project. + - name: Free up disk space on the runner + if: ${{ vars.VORTEX_CI_FREE_DISK_SPACE == '1' }} + run: | + set -- /usr/local/.ghcup /usr/share/swift /usr/local/share/powershell /usr/share/dotnet /opt/hostedtoolcache/CodeQL + # set -- "$@" /usr/local/lib/android # Removes about 10GB more, but takes about 50s. Uncomment to enable. + sudo rm -rf "$@" || true + + - name: Report disk usage + run: df -h + - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: diff --git a/.vortex/docs/.utils/variables/extra/ci.variables.sh b/.vortex/docs/.utils/variables/extra/ci.variables.sh index bf9a4b535..7e81da0e5 100755 --- a/.vortex/docs/.utils/variables/extra/ci.variables.sh +++ b/.vortex/docs/.utils/variables/extra/ci.variables.sh @@ -21,6 +21,9 @@ VORTEX_DEPLOY_ALLOW_LABEL= # Proceed with container image push after it was exported. VORTEX_EXPORT_DB_CONTAINER_REGISTRY_PUSH_PROCEED= +# Set to `1` to remove preinstalled toolchains from the GitHub Actions runner and free disk space. +VORTEX_CI_FREE_DISK_SPACE= + # Ignore Hadolint failures. VORTEX_CI_HADOLINT_IGNORE_FAILURE=0 diff --git a/.vortex/docs/content/continuous-integration/README.mdx b/.vortex/docs/content/continuous-integration/README.mdx index f117e9c70..b607b7654 100644 --- a/.vortex/docs/content/continuous-integration/README.mdx +++ b/.vortex/docs/content/continuous-integration/README.mdx @@ -211,28 +211,39 @@ by setting the `VORTEX_DEBUG` variable to `1` in your CI provider's settings. ### Runner disk space -Continuous integration jobs run on hosted runners with a fixed amount of disk -space - around 14 GB free on the GitHub Actions `ubuntu-latest` runner, and a -fixed allocation on CircleCI. Pulled container images, database dumps, and every -file created while provisioning share that space. +Continuous integration jobs run on hosted runners with a fixed amount of disk space - a fixed allocation on CircleCI, and on GitHub Actions a root volume that is largely consumed before the job starts. Measured on one `ubuntu-latest` image, preinstalled software occupied 59 GB of a 72 GB volume, leaving the job around 13 GB. Pulled container images, database dumps, and every file created while provisioning share what is left. -When it runs out, the runner is terminated from the outside: the step that was -running never reports an error and the log archive may be lost entirely, so the -failure looks like a hang rather than a disk problem. +When it runs out, the runner is terminated from the outside: the step that was running never reports an error and the log archive may be lost entirely, so the failure looks like a hang rather than a disk problem. **Vortex** keeps the build within that budget: +- The `test` and `build` jobs print `df -h` in a **Report disk usage** step, so the disk state is on the record for every run. - The Docker build cache and dangling images are pruned once the stack is up. -- The database dump is removed from the runner as soon as it has been copied - into the container, so only one copy is held during provisioning. - -If a build dies during provisioning without reporting an error, suspect the -disk. The most reliable fix is to reduce what has to fit: a sanitized dump or a -[database container image](#caching-strategy) instead of a full dump. On GitHub -Actions, a [larger runner](/docs/continuous-integration/github-actions#change-runner-size) -also comes with more disk. On CircleCI, disk space is not tied to the -[resource class](/docs/continuous-integration/circleci#change-runner-resource-class), -so upgrading it adds CPU and memory but no extra room for the build. +- The database dump is removed from the runner as soon as it has been copied into the container, so only one copy is held during provisioning. + +#### Reclaim the preinstalled toolchains + +On GitHub Actions, the `test` and `build` jobs can remove the preinstalled toolchains no **Vortex** job uses - GHCup, Swift, PowerShell, .NET and CodeQL - before doing anything else. In the measurement above this freed about 15 GB in around 14 seconds, taking the job from roughly 13 GB of headroom to 28 GB. + +The removal is destructive to the runner for the rest of the job, so it is off by default. Turn it on by setting the `VORTEX_CI_FREE_DISK_SPACE` variable to `1` in **Settings → Secrets and variables → Actions → Variables**. Leave it unset on a project whose workflow runs a step that needs Haskell, Swift, PowerShell, .NET or CodeQL. + +:::note + +Every figure here comes from one runner image at one point in time, and GitHub reissues that image regularly. Read the **Report disk usage** output of a recent run for the current numbers - if the reclaimed amount has shrunk, the path list needs revisiting. + +::: + +#### Free even more space + +With `VORTEX_CI_FREE_DISK_SPACE` enabled, the Android SDK is the single largest remaining tree at about 10 GB, but removing it takes around 50 seconds. It ships commented out in the workflow for that reason - uncomment the line in the **Free up disk space on the runner** step of both the `test` and `build` jobs to enable it: + +```yaml +set -- "$@" /usr/local/lib/android +``` + +#### When space still runs out + +If a build dies during provisioning without reporting an error, suspect the disk and read the **Report disk usage** output. The most reliable fix is to reduce what has to fit: a sanitized dump or a [database container image](#caching-strategy) instead of a full dump. On GitHub Actions, a [larger runner](/docs/continuous-integration/github-actions#change-runner-size) also comes with more disk. On CircleCI, disk space is not tied to the [resource class](/docs/continuous-integration/circleci#change-runner-resource-class), so upgrading it adds CPU and memory but no extra room for the build. ### Ignore tool failures diff --git a/.vortex/docs/content/continuous-integration/github-actions.mdx b/.vortex/docs/content/continuous-integration/github-actions.mdx index c0c0037e6..92cb4d365 100644 --- a/.vortex/docs/content/continuous-integration/github-actions.mdx +++ b/.vortex/docs/content/continuous-integration/github-actions.mdx @@ -94,6 +94,14 @@ runs-on: ubuntu-latest-4-cores # Options: ubuntu-latest, ubuntu-latest-4-cores, Larger runners also come with more disk, which matters for projects whose provisioning is disk-heavy. See [Runner disk space](/docs/continuous-integration#runner-disk-space). +### Preinstalled toolchains + +Most of the `ubuntu-latest` runner's root volume is consumed by preinstalled software before a job starts - in one measurement, 59 GB of 72 GB, leaving around 13 GB to work with. The `test` and `build` jobs can remove the toolchains no **Vortex** job uses - GHCup, Swift, PowerShell, .NET and CodeQL - before doing anything else, which freed about 15 GB in that same measurement. GitHub reissues the runner image regularly, so read the **Report disk usage** step of a recent run for the current numbers. + +The removal is off by default because it is destructive to the runner. To enable it, set the `VORTEX_CI_FREE_DISK_SPACE` variable to `1` in **Settings → Secrets and variables → Actions → Variables**. + +See [Runner disk space](/docs/continuous-integration#runner-disk-space) for the full measurements, how to free the Android SDK as well, and what to do when a job still runs out. + ### Change test parallelism To speed up test execution, you can increase the number of parallel runners in From e1e18f3f772b1e0d6ce4e6dd45e670dc99db3193 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Fri, 14 Aug 2026 17:03:49 +1000 Subject: [PATCH 16/57] [#3012] Migrated release drafter's default version resolver to a 'version-resolver' category. (#3013) (cherry picked from commit 90a02b36054eb44fc66f67e1d02166e5e8f2b0cc) --- .github/release-drafter.yml | 5 +++-- .../handler_process/_baseline/.github/release-drafter.yml | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 8b0738371..32072475c 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -2,8 +2,9 @@ name-template: '$RESOLVED_VERSION' tag-template: '$RESOLVED_VERSION' change-template: '- $TITLE @$AUTHOR (#$NUMBER)' change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. -version-resolver: - default: minor +categories: + - type: 'version-resolver' + semver-increment: 'minor' template: | ## What's new since $PREVIOUS_TAG diff --git a/.vortex/cli/tests/Fixtures/handler_process/_baseline/.github/release-drafter.yml b/.vortex/cli/tests/Fixtures/handler_process/_baseline/.github/release-drafter.yml index 8b0738371..32072475c 100644 --- a/.vortex/cli/tests/Fixtures/handler_process/_baseline/.github/release-drafter.yml +++ b/.vortex/cli/tests/Fixtures/handler_process/_baseline/.github/release-drafter.yml @@ -2,8 +2,9 @@ name-template: '$RESOLVED_VERSION' tag-template: '$RESOLVED_VERSION' change-template: '- $TITLE @$AUTHOR (#$NUMBER)' change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. -version-resolver: - default: minor +categories: + - type: 'version-resolver' + semver-increment: 'minor' template: | ## What's new since $PREVIOUS_TAG From d1749f328d553ae8e8799ca27f271f0af700688f Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:09:10 +1000 Subject: [PATCH 17/57] Required a '~' version constraint when installing the tooling package. Forward-ported from main 44a2acc27c066e24ae5d799143a23f8b6c4690a6. Pinned the example at the '2.0' series this line ships rather than main's '1.4'. --- .vortex/tooling/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.vortex/tooling/README.md b/.vortex/tooling/README.md index 4a84599a9..34fe6bced 100644 --- a/.vortex/tooling/README.md +++ b/.vortex/tooling/README.md @@ -10,7 +10,13 @@ to be added to your Drupal consumer project site. ## Installation ```bash -composer require drevops/vortex-tooling +composer require drevops/vortex-tooling:~2.0.0 +``` + +Always install with a `~` constraint. It accepts patch releases but holds the minor version, so a new minor release cannot reach your deployments until you raise the constraint yourself: + +```json +"drevops/vortex-tooling": "~2.0.0", ``` Once installed, you run the shipped scripts as Composer binaries from From 81d4f80b2451e6e252f46154e83d6e4c748b85b3 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:09:23 +1000 Subject: [PATCH 18/57] Anchored environment variable prefixes forwarded by 'ahoy cli'. (cherry picked from commit 93d422ed49684565bc3193e2039fd57488c61c4e) Fixtures regenerated separately. --- .ahoy.yml | 4 +- .../Traits/Subtests/SubtestAhoyTrait.php | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/.ahoy.yml b/.ahoy.yml index 300f9efa1..895496db6 100644 --- a/.ahoy.yml +++ b/.ahoy.yml @@ -107,9 +107,9 @@ commands: aliases: [ssh, shell] cmd: | if [ "${#}" -ne 0 ]; then - docker compose exec $(env | cut -f1 -d= | grep "TERM\|COMPOSE_\|GITHUB_\|PACKAGE_\|DOCKER_\|DRUPAL_\|VORTEX_\|ENVIRONMENT_\|LOCALDEV_URL$" | sed 's/^/-e /') cli bash -c "$*" + docker compose exec $(env | cut -f1 -d= | grep -E '^(TERM|LOCALDEV_URL)$|^(COMPOSE|GITHUB|PACKAGE|DOCKER|DRUPAL|VORTEX|ENVIRONMENT)_' | sed 's/^/-e /') cli bash -c "$*" else - docker compose exec $(env | cut -f1 -d= | grep "TERM\|COMPOSE_\|GITHUB_\|PACKAGE_\|DOCKER_\|DRUPAL_\|VORTEX_\|ENVIRONMENT_\|LOCALDEV_URL$" | sed 's/^/-e /') cli bash + docker compose exec $(env | cut -f1 -d= | grep -E '^(TERM|LOCALDEV_URL)$|^(COMPOSE|GITHUB|PACKAGE|DOCKER|DRUPAL|VORTEX|ENVIRONMENT)_' | sed 's/^/-e /') cli bash fi composer: diff --git a/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php b/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php index c6c815c49..61b30eb1e 100644 --- a/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php +++ b/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php @@ -131,6 +131,47 @@ protected function subtestAhoyCli(): void { txt: '`ahoy cli` forwards a host ENVIRONMENT_TYPE into the container' ); + // Prefixes are matched at the start of the name, so a host variable that + // merely contains one is not forwarded. + $this->cmdFail( + "ahoy cli 'printenv MY_DRUPAL_SECRET'", + '! unforwardedvar', + env: ['MY_DRUPAL_SECRET' => 'unforwardedvar'], + txt: '`ahoy cli` does not forward a host variable that contains an allowed prefix mid-name' + ); + + // 'LOCALDEV_URL' is matched in full: the exact name is forwarded, a name + // ending with it is not. + $this->cmdFail( + "ahoy cli 'printenv MY_LOCALDEV_URL'", + '! unforwardedvar', + env: ['MY_LOCALDEV_URL' => 'unforwardedvar'], + txt: '`ahoy cli` does not forward a host variable that ends with an allowed name' + ); + + $this->cmd( + "ahoy cli 'printenv LOCALDEV_URL'", + 'anchoredlocaldevurl', + env: ['LOCALDEV_URL' => 'anchoredlocaldevurl'], + txt: '`ahoy cli` forwards an exact host LOCALDEV_URL into the container' + ); + + // 'TERM' is matched in full, so terminal variables built around it are not + // forwarded, while 'TERM' itself still is. + $this->cmdFail( + "ahoy cli 'printenv ITERM_PROFILE'", + '! unforwardedvar', + env: ['ITERM_PROFILE' => 'unforwardedvar'], + txt: '`ahoy cli` does not forward a host variable that contains TERM' + ); + + $this->cmd( + "ahoy cli 'printenv TERM'", + 'anchoredtermvar', + env: ['TERM' => 'anchoredtermvar'], + txt: '`ahoy cli` forwards a host TERM into the container' + ); + $this->logStepFinish(); } From 54765ee5881dc5ce0890f13cffb6a11eca95527e Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:09:37 +1000 Subject: [PATCH 19/57] Fixed 'getProtectedValue()' reading the property from the reflection class instead of the object. Forward-ported from main 6ddee19e02cad18272de96badcb78cd37400851b. Ported the template fix only: the added harness unit tests cover traits that do not exist under '.vortex/tests' on this line. --- .../custom/ys_base/tests/src/Traits/ReflectionTrait.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/modules/custom/ys_base/tests/src/Traits/ReflectionTrait.php b/web/modules/custom/ys_base/tests/src/Traits/ReflectionTrait.php index bf153f30c..9e17699da 100644 --- a/web/modules/custom/ys_base/tests/src/Traits/ReflectionTrait.php +++ b/web/modules/custom/ys_base/tests/src/Traits/ReflectionTrait.php @@ -68,18 +68,18 @@ protected static function setProtectedValue(object $object, string $property, mi * Get protected value from the object. * * @param object $object - * Object to set the value on. + * Object to get the value from. * @param string $property * Property name to get the value. Property should exists in the object. * * @return mixed * Protected property value. */ - protected static function getProtectedValue($object, $property): mixed { + protected static function getProtectedValue(object $object, string $property): mixed { $class = new \ReflectionClass($object::class); $property = $class->getProperty($property); - return $property->getValue($class); + return $property->getValue($object); } } From 57f3dd2c96f6a76300b2ac52b9f54b8c0fcffdd6 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:10:04 +1000 Subject: [PATCH 20/57] Skipped Behat animated screenshots in CI on 'drevops/behat-screenshot' 2.6.0. (cherry picked from commit d19ec229f5c149587524240fc0df68d3fdf34a99) Bumped 'drevops/behat-screenshot' from '^2.4.2' rather than main's '^2.5.0'; fixtures regenerated separately. --- .circleci/config.yml | 4 ++-- .circleci/vortex-test-common.yml | 4 ++-- .github/workflows/build-test-deploy.yml | 4 ++-- .vortex/docs/content/development/behat.mdx | 8 ++++++++ composer.json | 2 +- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 33fd690fb..85ea88761 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -502,8 +502,8 @@ jobs: command: | if [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${CIRCLE_NODE_INDEX}}"; fi echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" - docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ - docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ [ "${VORTEX_CI_BEHAT_IGNORE_FAILURE:-0}" -eq 1 ] no_output_timeout: 30m #;> TOOL_BEHAT diff --git a/.circleci/vortex-test-common.yml b/.circleci/vortex-test-common.yml index e0c9e03dd..46d7ec03c 100644 --- a/.circleci/vortex-test-common.yml +++ b/.circleci/vortex-test-common.yml @@ -359,8 +359,8 @@ jobs: command: | if [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${CIRCLE_NODE_INDEX}}"; fi echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" - docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ - docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ [ "${VORTEX_CI_BEHAT_IGNORE_FAILURE:-0}" -eq 1 ] no_output_timeout: 30m #;> TOOL_BEHAT diff --git a/.github/workflows/build-test-deploy.yml b/.github/workflows/build-test-deploy.yml index 3b85242f3..cc8ae89c6 100644 --- a/.github/workflows/build-test-deploy.yml +++ b/.github/workflows/build-test-deploy.yml @@ -509,8 +509,8 @@ jobs: # shellcheck disable=SC2170 if [ "${STRATEGY_JOB_TOTAL}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${STRATEGY_JOB_INDEX}}"; fi echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" - docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ - docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" + docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" env: VORTEX_CI_BEHAT_PROFILE: ${{ vars.VORTEX_CI_BEHAT_PROFILE }} STRATEGY_JOB_TOTAL: ${{ strategy.job-total }} diff --git a/.vortex/docs/content/development/behat.mdx b/.vortex/docs/content/development/behat.mdx index 6be4da8a4..fe6e5fc76 100644 --- a/.vortex/docs/content/development/behat.mdx +++ b/.vortex/docs/content/development/behat.mdx @@ -116,6 +116,14 @@ In continuous integration pipeline, screenshots are stored as build artifacts. In GitHub Actions, they can be downloaded from the `Summary` tab. In CircleCI they are accessible in the `Artifacts` tab. +### Animated screenshots + +Per-step screenshots are combined into an animated GIF for each scenario, making a failing test easier to follow. This is enabled in `behat.yml` under the `animation` key of the `DrevOps\BehatScreenshotExtension` block. + +Animation forces a full-page capture after every passed step, so its cost grows with the number of steps and roughly doubles the wall time of a run. **Vortex** passes `BEHAT_SCREENSHOT_ANIMATION_SKIP=1` into the Behat step in continuous integration to skip animation there, while keeping it available for local runs where a recording is worth the wait. + +Set `animation.enabled` to `false` in `behat.yml` to disable animation everywhere, or tag an individual scenario or feature with `@screenshots:animated:skip` to exempt it. + ## Format Out of the box, **Vortex** comes with [Behat Progress formatter](https://github.com/drevops/behat-format-progress-fail) diff --git a/composer.json b/composer.json index b262dcb35..e9b7c756a 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "dantleech/gherkin-lint": "^0.2.4", "dealerdirect/phpcodesniffer-composer-installer": "^1.2.1", "drevops/behat-format-progress-fail": "^1.5.1", - "drevops/behat-screenshot": "^2.4.2", + "drevops/behat-screenshot": "^2.6.0", "drevops/behat-steps": "^3.13.0", "drevops/phpcs-standard": "^0.7.0", "drupal/coder": "^9@alpha", From cc0d8e8b8431a3b1735cfeddfe3f59660ea3ad03 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:11:35 +1000 Subject: [PATCH 21/57] Replaced per-step CI runner conditions with declared 'VORTEX_CI_IS_*_RUNNER' roles. (cherry picked from commit 01e6ccb154014f474526f7bebfd73449bd600ffb) Declared the roles on the job's own env rather than main's container executor, which this line does not use, and named them with the 'VORTEX_CI_' prefix already in use here. Fixtures regenerated separately. --- .circleci/config.yml | 48 ++++-- .circleci/vortex-test-common.yml | 48 ++++-- .github/workflows/build-test-deploy.yml | 42 +++-- .../content/continuous-integration/README.mdx | 145 +++++++++++++++++- .../continuous-integration/circleci.mdx | 18 ++- .../continuous-integration/github-actions.mdx | 18 ++- behat.yml | 12 +- 7 files changed, 278 insertions(+), 53 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 85ea88761..d856ab921 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -277,6 +277,33 @@ jobs: - *step_process_codebase_for_ci - *load_variables_from_dotenv + - run: + name: Set test runner roles + command: | + # Which tools run on this node. Tools that only need to run once are + # pinned to the first node. Add a line for every additional tool and + # reference it in that tool's step. + # https://www.vortextemplate.com/docs/continuous-integration#test-parallelism + { + echo "export VORTEX_CI_RUNNER_INDEX=${CIRCLE_NODE_INDEX:-0}" + echo "export VORTEX_CI_RUNNER_TOTAL=${CIRCLE_NODE_TOTAL:-1}" + #;< TOOL_JEST + echo "export VORTEX_CI_IS_JEST_RUNNER=$([ "${CIRCLE_NODE_INDEX:-0}" -eq 0 ] && echo 1 || echo 0)" + #;> TOOL_JEST + #;< TOOL_PHPUNIT + echo "export VORTEX_CI_IS_PHPUNIT_RUNNER=$([ "${CIRCLE_NODE_INDEX:-0}" -eq 0 ] && echo 1 || echo 0)" + #;> TOOL_PHPUNIT + #;< DRUPAL_THEME + #;< MODULE_SDC_DEVEL + echo "export VORTEX_CI_IS_SDC_DEVEL_RUNNER=$([ "${CIRCLE_NODE_INDEX:-0}" -eq 0 ] && echo 1 || echo 0)" + #;> MODULE_SDC_DEVEL + #;> DRUPAL_THEME + #;< TOOL_BEHAT + # Runs on every node, using the `p` profile. + echo "export VORTEX_CI_IS_BEHAT_RUNNER=1" + #;> TOOL_BEHAT + } >> "${BASH_ENV}" + - run: name: Validate Composer configuration command: composer validate --strict || [ "${VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE:-0}" -eq 1 ] @@ -423,20 +450,22 @@ jobs: #;< TOOL_JEST - run: name: Test with Jest - command: docker compose exec -T cli bash -c "yarn test" || [ "${VORTEX_CI_JEST_IGNORE_FAILURE:-0}" -eq 1 ] + command: | + [ "${VORTEX_CI_IS_JEST_RUNNER:-1}" = "1" ] || exit 0 + docker compose exec -T cli bash -c "yarn test" || [ "${VORTEX_CI_JEST_IGNORE_FAILURE:-0}" -eq 1 ] #;> TOOL_JEST #;< TOOL_PHPUNIT - run: name: Test with PHPUnit command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_PHPUNIT_RUNNER:-1}" = "1" ] || exit 0 docker compose exec -T cli vendor/bin/phpunit || [ "${VORTEX_CI_PHPUNIT_IGNORE_FAILURE:-0}" -eq 1 ] - run: name: Process PHPUnit logs and coverage command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_PHPUNIT_RUNNER:-1}" = "1" ] || exit 0 mkdir -p "${VORTEX_CI_ARTIFACTS}" if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then docker compose cp cli:/app/.logs/. "${VORTEX_CI_ARTIFACTS}/" @@ -445,7 +474,7 @@ jobs: - run: name: Extract code coverage command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_PHPUNIT_RUNNER:-1}" = "1" ] || exit 0 RATE=$(grep -om1 'line-rate="[0-9.]*"' /tmp/artifacts/coverage/phpunit/cobertura.xml | tr -cd '0-9.') PERCENT=$(awk "BEGIN {printf \"%.2f\", $RATE*100}") echo "Coverage: $PERCENT% (threshold: ${VORTEX_CI_CODE_COVERAGE_THRESHOLD:-90}%)" @@ -454,7 +483,7 @@ jobs: - run: name: Post coverage summary as PR comment command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_PHPUNIT_RUNNER:-1}" = "1" ] || exit 0 [ "${VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP:-0}" = "1" ] && exit 0 .circleci/post-coverage-comment.sh /tmp/artifacts/coverage/phpunit/coverage.txt @@ -462,7 +491,7 @@ jobs: - run: name: Upload code coverage reports to Codecov command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_PHPUNIT_RUNNER:-1}" = "1" ] || exit 0 if [ -n "${CODECOV_TOKEN}" ] && [ -d /tmp/artifacts/coverage ] && ! echo "${CIRCLE_BRANCH}" | grep -q '^deps/'; then curl -fOs https://uploader.codecov.io/v0.8.0/linux/codecov chmod +x codecov @@ -473,7 +502,7 @@ jobs: - run: name: Check code coverage threshold command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_PHPUNIT_RUNNER:-1}" = "1" ] || exit 0 if [ "${COVERAGE_PERCENT//.}" -lt "$((${VORTEX_CI_CODE_COVERAGE_THRESHOLD:-90}*100))" ]; then echo "FAIL: coverage ${COVERAGE_PERCENT}% is below threshold ${VORTEX_CI_CODE_COVERAGE_THRESHOLD:-90}%" exit 1 @@ -485,7 +514,7 @@ jobs: - run: name: Validate Single Directory Components command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_SDC_DEVEL_RUNNER:-1}" = "1" ] || exit 0 [ "${VORTEX_FRONTEND_BUILD_SKIP:-0}" -eq 1 ] && exit 0 output=$(docker compose exec -T cli vendor/bin/drush sdc-devel:validate "${DRUPAL_THEME}" 2>&1) echo "${output}" @@ -500,7 +529,8 @@ jobs: - run: name: Test with Behat command: | - if [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${CIRCLE_NODE_INDEX}}"; fi + [ "${VORTEX_CI_IS_BEHAT_RUNNER:-1}" = "1" ] || exit 0 + if [ "${VORTEX_CI_RUNNER_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${VORTEX_CI_RUNNER_INDEX}}"; fi echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ diff --git a/.circleci/vortex-test-common.yml b/.circleci/vortex-test-common.yml index 46d7ec03c..f94588510 100644 --- a/.circleci/vortex-test-common.yml +++ b/.circleci/vortex-test-common.yml @@ -134,6 +134,33 @@ jobs: - *step_process_codebase_for_ci - *load_variables_from_dotenv + - run: + name: Set test runner roles + command: | + # Which tools run on this node. Tools that only need to run once are + # pinned to the first node. Add a line for every additional tool and + # reference it in that tool's step. + # https://www.vortextemplate.com/docs/continuous-integration#test-parallelism + { + echo "export VORTEX_CI_RUNNER_INDEX=${CIRCLE_NODE_INDEX:-0}" + echo "export VORTEX_CI_RUNNER_TOTAL=${CIRCLE_NODE_TOTAL:-1}" + #;< TOOL_JEST + echo "export VORTEX_CI_IS_JEST_RUNNER=$([ "${CIRCLE_NODE_INDEX:-0}" -eq 0 ] && echo 1 || echo 0)" + #;> TOOL_JEST + #;< TOOL_PHPUNIT + echo "export VORTEX_CI_IS_PHPUNIT_RUNNER=$([ "${CIRCLE_NODE_INDEX:-0}" -eq 0 ] && echo 1 || echo 0)" + #;> TOOL_PHPUNIT + #;< DRUPAL_THEME + #;< MODULE_SDC_DEVEL + echo "export VORTEX_CI_IS_SDC_DEVEL_RUNNER=$([ "${CIRCLE_NODE_INDEX:-0}" -eq 0 ] && echo 1 || echo 0)" + #;> MODULE_SDC_DEVEL + #;> DRUPAL_THEME + #;< TOOL_BEHAT + # Runs on every node, using the `p` profile. + echo "export VORTEX_CI_IS_BEHAT_RUNNER=1" + #;> TOOL_BEHAT + } >> "${BASH_ENV}" + - run: name: Validate Composer configuration command: composer validate --strict || [ "${VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE:-0}" -eq 1 ] @@ -280,20 +307,22 @@ jobs: #;< TOOL_JEST - run: name: Test with Jest - command: docker compose exec -T cli bash -c "yarn test" || [ "${VORTEX_CI_JEST_IGNORE_FAILURE:-0}" -eq 1 ] + command: | + [ "${VORTEX_CI_IS_JEST_RUNNER:-1}" = "1" ] || exit 0 + docker compose exec -T cli bash -c "yarn test" || [ "${VORTEX_CI_JEST_IGNORE_FAILURE:-0}" -eq 1 ] #;> TOOL_JEST #;< TOOL_PHPUNIT - run: name: Test with PHPUnit command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_PHPUNIT_RUNNER:-1}" = "1" ] || exit 0 docker compose exec -T cli vendor/bin/phpunit || [ "${VORTEX_CI_PHPUNIT_IGNORE_FAILURE:-0}" -eq 1 ] - run: name: Process PHPUnit logs and coverage command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_PHPUNIT_RUNNER:-1}" = "1" ] || exit 0 mkdir -p "${VORTEX_CI_ARTIFACTS}" if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then docker compose cp cli:/app/.logs/. "${VORTEX_CI_ARTIFACTS}/" @@ -302,7 +331,7 @@ jobs: - run: name: Extract code coverage command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_PHPUNIT_RUNNER:-1}" = "1" ] || exit 0 RATE=$(grep -om1 'line-rate="[0-9.]*"' /tmp/artifacts/coverage/phpunit/cobertura.xml | tr -cd '0-9.') PERCENT=$(awk "BEGIN {printf \"%.2f\", $RATE*100}") echo "Coverage: $PERCENT% (threshold: ${VORTEX_CI_CODE_COVERAGE_THRESHOLD:-90}%)" @@ -311,7 +340,7 @@ jobs: - run: name: Post coverage summary as PR comment command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_PHPUNIT_RUNNER:-1}" = "1" ] || exit 0 [ "${VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP:-0}" = "1" ] && exit 0 .circleci/post-coverage-comment.sh /tmp/artifacts/coverage/phpunit/coverage.txt @@ -319,7 +348,7 @@ jobs: - run: name: Upload code coverage reports to Codecov command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_PHPUNIT_RUNNER:-1}" = "1" ] || exit 0 if [ -n "${CODECOV_TOKEN}" ] && [ -d /tmp/artifacts/coverage ] && ! echo "${CIRCLE_BRANCH}" | grep -q '^deps/'; then curl -fOs https://uploader.codecov.io/v0.8.0/linux/codecov chmod +x codecov @@ -330,7 +359,7 @@ jobs: - run: name: Check code coverage threshold command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_PHPUNIT_RUNNER:-1}" = "1" ] || exit 0 if [ "${COVERAGE_PERCENT//.}" -lt "$((${VORTEX_CI_CODE_COVERAGE_THRESHOLD:-90}*100))" ]; then echo "FAIL: coverage ${COVERAGE_PERCENT}% is below threshold ${VORTEX_CI_CODE_COVERAGE_THRESHOLD:-90}%" exit 1 @@ -342,7 +371,7 @@ jobs: - run: name: Validate Single Directory Components command: | - [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + [ "${VORTEX_CI_IS_SDC_DEVEL_RUNNER:-1}" = "1" ] || exit 0 [ "${VORTEX_FRONTEND_BUILD_SKIP:-0}" -eq 1 ] && exit 0 output=$(docker compose exec -T cli vendor/bin/drush sdc-devel:validate "${DRUPAL_THEME}" 2>&1) echo "${output}" @@ -357,7 +386,8 @@ jobs: - run: name: Test with Behat command: | - if [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${CIRCLE_NODE_INDEX}}"; fi + [ "${VORTEX_CI_IS_BEHAT_RUNNER:-1}" = "1" ] || exit 0 + if [ "${VORTEX_CI_RUNNER_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${VORTEX_CI_RUNNER_INDEX}}"; fi echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ diff --git a/.github/workflows/build-test-deploy.yml b/.github/workflows/build-test-deploy.yml index cc8ae89c6..be7974d43 100644 --- a/.github/workflows/build-test-deploy.yml +++ b/.github/workflows/build-test-deploy.yml @@ -228,6 +228,27 @@ jobs: # Which branch to use as a source of DB caches. VORTEX_CI_DB_CACHE_BRANCH: "develop" #;> !PROVISION_TYPE_PROFILE + # Which tools run on this instance. Tools that only need to run once are + # pinned to the first instance. Add a line for every additional tool and + # reference it in that tool's step. + # https://www.vortextemplate.com/docs/continuous-integration#test-parallelism + VORTEX_CI_RUNNER_INDEX: ${{ strategy.job-index }} + VORTEX_CI_RUNNER_TOTAL: ${{ strategy.job-total }} + #;< TOOL_JEST + VORTEX_CI_IS_JEST_RUNNER: ${{ matrix.instance == 0 || strategy.job-total == 1 }} + #;> TOOL_JEST + #;< TOOL_PHPUNIT + VORTEX_CI_IS_PHPUNIT_RUNNER: ${{ matrix.instance == 0 || strategy.job-total == 1 }} + #;> TOOL_PHPUNIT + #;< DRUPAL_THEME + #;< MODULE_SDC_DEVEL + VORTEX_CI_IS_SDC_DEVEL_RUNNER: ${{ matrix.instance == 0 || strategy.job-total == 1 }} + #;> MODULE_SDC_DEVEL + #;> DRUPAL_THEME + #;< TOOL_BEHAT + # Runs on every instance, using the `p` profile. + VORTEX_CI_IS_BEHAT_RUNNER: true + #;> TOOL_BEHAT steps: # Frees disk space by removing preinstalled toolchains unused by this project. @@ -407,19 +428,19 @@ jobs: #;< TOOL_JEST - name: Test with Jest - if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} + if: ${{ env.VORTEX_CI_IS_JEST_RUNNER == 'true' }} run: docker compose exec -T cli bash -c "yarn test" continue-on-error: ${{ vars.VORTEX_CI_JEST_IGNORE_FAILURE == '1' }} #;> TOOL_JEST #;< TOOL_PHPUNIT - name: Test with PHPUnit - if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} + if: ${{ env.VORTEX_CI_IS_PHPUNIT_RUNNER == 'true' }} run: docker compose exec -T cli vendor/bin/phpunit continue-on-error: ${{ vars.VORTEX_CI_PHPUNIT_IGNORE_FAILURE == '1' }} - name: Process PHPUnit logs and coverage - if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} + if: ${{ env.VORTEX_CI_IS_PHPUNIT_RUNNER == 'true' }} run: | mkdir -p ".logs" if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then @@ -427,7 +448,7 @@ jobs: fi - name: Extract code coverage - if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} + if: ${{ env.VORTEX_CI_IS_PHPUNIT_RUNNER == 'true' }} run: | RATE=$(grep -om1 'line-rate="[0-9.]*"' .logs/coverage/phpunit/cobertura.xml | tr -cd '0-9.') PERCENT=$(awk "BEGIN {printf \"%.2f\", $RATE*100}") @@ -445,7 +466,7 @@ jobs: VORTEX_CI_CODE_COVERAGE_THRESHOLD: ${{ vars.VORTEX_CI_CODE_COVERAGE_THRESHOLD || '90' }} - name: Post coverage summary as PR comment - if: ${{ github.event_name == 'pull_request' && (matrix.instance == 0 || strategy.job-total == 1) && vars.VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP != '1' }} + if: ${{ github.event_name == 'pull_request' && env.VORTEX_CI_IS_PHPUNIT_RUNNER == 'true' && vars.VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP != '1' }} uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5 with: header: coverage-gha @@ -466,7 +487,7 @@ jobs: #;< CODE_COVERAGE_PROVIDER_CODECOV - name: Upload coverage report to Codecov uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7 - if: ${{ (matrix.instance == 0 || strategy.job-total == 1) && env.CODECOV_TOKEN != '' }} + if: ${{ env.VORTEX_CI_IS_PHPUNIT_RUNNER == 'true' && env.CODECOV_TOKEN != '' }} continue-on-error: true with: directory: .logs/coverage @@ -477,7 +498,7 @@ jobs: #;> CODE_COVERAGE_PROVIDER_CODECOV - name: Check code coverage threshold - if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} + if: ${{ env.VORTEX_CI_IS_PHPUNIT_RUNNER == 'true' }} run: | if [ "${COVERAGE_PERCENT//.}" -lt "$((VORTEX_CI_CODE_COVERAGE_THRESHOLD*100))" ]; then echo "FAIL: coverage ${COVERAGE_PERCENT}% is below threshold ${VORTEX_CI_CODE_COVERAGE_THRESHOLD}%" @@ -490,7 +511,7 @@ jobs: #;< DRUPAL_THEME #;< MODULE_SDC_DEVEL - name: Validate Single Directory Components - if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} + if: ${{ env.VORTEX_CI_IS_SDC_DEVEL_RUNNER == 'true' }} run: | [ "${VORTEX_FRONTEND_BUILD_SKIP:-0}" -eq 1 ] && exit 0 output=$(docker compose exec -T cli vendor/bin/drush sdc-devel:validate "${DRUPAL_THEME}" 2>&1) @@ -505,16 +526,15 @@ jobs: #;< TOOL_BEHAT - name: Test with Behat + if: ${{ env.VORTEX_CI_IS_BEHAT_RUNNER == 'true' }} run: | # shellcheck disable=SC2170 - if [ "${STRATEGY_JOB_TOTAL}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${STRATEGY_JOB_INDEX}}"; fi + if [ "${VORTEX_CI_RUNNER_TOTAL}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${VORTEX_CI_RUNNER_INDEX}}"; fi echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" env: VORTEX_CI_BEHAT_PROFILE: ${{ vars.VORTEX_CI_BEHAT_PROFILE }} - STRATEGY_JOB_TOTAL: ${{ strategy.job-total }} - STRATEGY_JOB_INDEX: ${{ strategy.job-index }} continue-on-error: ${{ vars.VORTEX_CI_BEHAT_IGNORE_FAILURE == '1' }} timeout-minutes: 30 #;> TOOL_BEHAT diff --git a/.vortex/docs/content/continuous-integration/README.mdx b/.vortex/docs/content/continuous-integration/README.mdx index b607b7654..f682991f9 100644 --- a/.vortex/docs/content/continuous-integration/README.mdx +++ b/.vortex/docs/content/continuous-integration/README.mdx @@ -3,6 +3,9 @@ sidebar_label: Overview sidebar_position: 1 --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # Continuous Integration **Vortex** offers continuous integration configurations for [GitHub Actions](/docs/continuous-integration/github-actions) @@ -155,14 +158,111 @@ parallelism settings. | Task | First container | Other containers | |------|:---:|:---:| +| Jest tests | ✓ | — | | PHPUnit tests | ✓ | — | | Code coverage check and PR comment | ✓ | — | +| Single Directory Component validation | ✓ | — | | Behat tests | ✓ (profile `p0`) | ✓ (profile `p1`, `p2`, ...) | -PHPUnit and coverage reporting run exclusively on the first container -to avoid duplicate work. Behat tests run on all containers using profile-based +Everything except Behat runs exclusively on the first container to avoid +duplicate work. Behat tests run on all containers using profile-based distribution. +### Choosing which container runs what + +Each tool reads a `CI_IS__RUNNER` variable that decides whether it runs on +the current container. All of them are declared together at the top of the build +job, so the whole distribution is visible and editable in one place: + + + + +```yaml title=".github/workflows/build-test-deploy.yml" +env: + VORTEX_CI_RUNNER_INDEX: ${{ strategy.job-index }} + VORTEX_CI_RUNNER_TOTAL: ${{ strategy.job-total }} + VORTEX_CI_IS_JEST_RUNNER: ${{ matrix.instance == 0 || strategy.job-total == 1 }} + VORTEX_CI_IS_PHPUNIT_RUNNER: ${{ matrix.instance == 0 || strategy.job-total == 1 }} + VORTEX_CI_IS_SDC_DEVEL_RUNNER: ${{ matrix.instance == 0 || strategy.job-total == 1 }} + VORTEX_CI_IS_BEHAT_RUNNER: true +``` + +Each tool's step then reads its own flag: + +```yaml +- name: Test with PHPUnit + if: ${{ env.VORTEX_CI_IS_PHPUNIT_RUNNER == 'true' }} +``` + + + + +```yaml title=".circleci/config.yml" +- run: + name: Set test runner roles + command: | + { + echo "export VORTEX_CI_RUNNER_INDEX=${CIRCLE_NODE_INDEX:-0}" + echo "export VORTEX_CI_RUNNER_TOTAL=${CIRCLE_NODE_TOTAL:-1}" + echo "export VORTEX_CI_IS_JEST_RUNNER=$([ "${CIRCLE_NODE_INDEX:-0}" -eq 0 ] && echo 1 || echo 0)" + echo "export VORTEX_CI_IS_PHPUNIT_RUNNER=$([ "${CIRCLE_NODE_INDEX:-0}" -eq 0 ] && echo 1 || echo 0)" + echo "export VORTEX_CI_IS_SDC_DEVEL_RUNNER=$([ "${CIRCLE_NODE_INDEX:-0}" -eq 0 ] && echo 1 || echo 0)" + echo "export VORTEX_CI_IS_BEHAT_RUNNER=1" + } >> "${BASH_ENV}" +``` + +Each tool's step then reads its own flag as its first line: + +```yaml +- run: + name: Test with PHPUnit + command: | + [ "${VORTEX_CI_IS_PHPUNIT_RUNNER:-1}" = "1" ] || exit 0 +``` + + + + +`VORTEX_CI_RUNNER_INDEX` and `VORTEX_CI_RUNNER_TOTAL` carry the current container's index and +the container count under the same names on both providers. + +To run a tool of your own on a specific container, add one more flag alongside +the others and reference it from your step: + +```yaml +CI_IS_CYPRESS_RUNNER: ${{ matrix.instance == 1 }} +``` + +#### Giving a tool its own container + +Start by [adding a container](#adding-more-containers) for the tool to move onto + +- the default configuration has containers `0` and `1` only. Then point the +tool's flag at the new container and exclude that container from Behat. With a +third container added, that is: + +```yaml +VORTEX_CI_IS_PHPUNIT_RUNNER: ${{ matrix.instance == 2 }} +VORTEX_CI_IS_BEHAT_RUNNER: ${{ matrix.instance != 2 }} +``` + +:::warning + +Point the flag at a container that exists. A flag whose condition matches no +container disables the tool everywhere, and nothing reports it - the steps are +simply skipped and the job still passes. + +::: + +:::warning + +Use the **last** container for this, never the first. Behat derives its profile +name from the container index, and `p0` is the catch-all that runs every +scenario without a `@pX` tag. Excluding container 0 from Behat means `p0` never +runs and those scenarios silently stop being tested. + +::: + ### Balancing Behat tests Because the first container handles PHPUnit and coverage in addition @@ -196,11 +296,44 @@ duration of the longest single container rather than the sum of all tests. ::: -See the provider-specific pages for how to change the number of parallel -containers: +### Adding more containers + +Raising the container count takes two changes that must stay in step - the +container count itself, and a matching Behat profile for every new container. + +1. Increase the container count. See the provider-specific pages: + + - [CircleCI — Change test parallelism](/docs/continuous-integration/circleci#change-test-parallelism) + - [GitHub Actions — Change test parallelism](/docs/continuous-integration/github-actions#change-test-parallelism) + +2. Add a profile to `behat.yml` for each new container that runs Behat. + **Vortex** ships with `p0` and `p1` only, and Behat fails with + `profile 'p2' does not exist` if a container running Behat has no profile + named after its index. A container excluded from Behat needs no profile: + + ```yaml title="behat.yml" + p2: + gherkin: + cache: '/tmp/behat_gherkin_cache' + filters: + tags: '@smoke,@p2&&~@skipped' + ``` + +3. Exclude the new tag from the `p0` catch-all, so its scenarios do not also run + on the first container: + + ```yaml title="behat.yml" + p0: + gherkin: + cache: '/tmp/behat_gherkin_cache' + filters: + tags: '@smoke,~@p1&&~@p2&&~@skipped' + ``` + +4. Tag scenarios with `@p2` to move them onto the new container. -- [CircleCI — Change test parallelism](/docs/continuous-integration/circleci#change-test-parallelism) -- [GitHub Actions — Change test parallelism](/docs/continuous-integration/github-actions#change-test-parallelism) +Scenarios tagged `@smoke` run on every container by design, so they stay out of +the balancing arithmetic. ## Maintenance diff --git a/.vortex/docs/content/continuous-integration/circleci.mdx b/.vortex/docs/content/continuous-integration/circleci.mdx index dd5d3bc33..d8727e47a 100644 --- a/.vortex/docs/content/continuous-integration/circleci.mdx +++ b/.vortex/docs/content/continuous-integration/circleci.mdx @@ -86,13 +86,17 @@ test: parallelism: 4 # Run tests across 4 containers ``` -When adding more containers, distribute Behat scenarios across them using -profile tags (`@p0`, `@p1`, `@p2`, etc.) in your feature files. Since the first -container also runs linting, PHPUnit, and coverage, assign more Behat scenarios -to the additional containers to keep build times balanced. - -See [Test parallelism](/docs/continuous-integration#test-parallelism) for details -on how tests are distributed across containers. +Every added container that runs Behat also needs a matching `pN` profile in +`behat.yml` - without it, Behat fails with `profile 'p2' does not exist`. A +container excluded from Behat needs no profile. Then distribute Behat scenarios +across the containers using profile tags (`@p0`, `@p1`, `@p2`, etc.) in your +feature files. Since the first container also runs Jest, PHPUnit, coverage, and +Single Directory Component validation, assign more Behat scenarios to the +additional containers to keep build times balanced. + +See [Adding more containers](/docs/continuous-integration#adding-more-containers) +for the full procedure, and [Choosing which container runs what](/docs/continuous-integration#choosing-which-container-runs-what) +to move a tool onto a different container. ### SSH access for debugging diff --git a/.vortex/docs/content/continuous-integration/github-actions.mdx b/.vortex/docs/content/continuous-integration/github-actions.mdx index 92cb4d365..ae9e9d2ce 100644 --- a/.vortex/docs/content/continuous-integration/github-actions.mdx +++ b/.vortex/docs/content/continuous-integration/github-actions.mdx @@ -114,13 +114,17 @@ strategy: instance: [0, 1, 2, 3] # Run tests across 4 containers ``` -When adding more containers, distribute Behat scenarios across them using -profile tags (`@p0`, `@p1`, `@p2`, etc.) in your feature files. Since the first -container also runs linting, PHPUnit, and coverage, assign more Behat scenarios -to the additional containers to keep build times balanced. - -See [Test parallelism](/docs/continuous-integration#test-parallelism) for details -on how tests are distributed across containers. +Every added container that runs Behat also needs a matching `pN` profile in +`behat.yml` - without it, Behat fails with `profile 'p2' does not exist`. A +container excluded from Behat needs no profile. Then distribute Behat scenarios +across the containers using profile tags (`@p0`, `@p1`, `@p2`, etc.) in your +feature files. Since the first container also runs Jest, PHPUnit, coverage, and +Single Directory Component validation, assign more Behat scenarios to the +additional containers to keep build times balanced. + +See [Adding more containers](/docs/continuous-integration#adding-more-containers) +for the full procedure, and [Choosing which container runs what](/docs/continuous-integration#choosing-which-container-runs-what) +to move a tool onto a different container. ### Manual deployment diff --git a/behat.yml b/behat.yml index 8a2c36ca4..eb40b0acd 100644 --- a/behat.yml +++ b/behat.yml @@ -96,16 +96,20 @@ default: # Show explicit fail information and continue the test run. DrevOps\BehatFormatProgressFail\FormatExtension: ~ -# Profile for parallel testing. -# Runs all tests not tagged with 'smoke' or '@p1' and not tagged with '@skipped'. -# This is a 'catch-all' profile that runs any tests not tagged with '@pX'. +# Profiles for parallel testing. CI runs the profile named after the index of +# the runner it is on, so 'pN' requires a runner N. Adding a runner means adding +# a matching profile here and excluding its tag from the 'p0' catch-all below. +# https://www.vortextemplate.com/docs/continuous-integration#test-parallelism + +# Runs all tests tagged with '@smoke' or not tagged with '@p1', and not tagged +# with '@skipped'. This is a 'catch-all' profile that runs any tests not tagged +# with '@pX'. p0: gherkin: cache: '/tmp/behat_gherkin_cache' filters: tags: '@smoke,~@p1&&~@skipped' -# Profile for parallel testing. # Runs all tests tagged with '@smoke' or '@p1' and not tagged with '@skipped'. p1: gherkin: From 98162248f660c36d8138d6168877f8c76b27aa4f Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:16:22 +1000 Subject: [PATCH 22/57] Reconciled the Vortex documentation for coherence and corrected stale facts across all sections. (cherry picked from commit dd5b0ac0766a52da539053f4b1b1aa0e6dd6f02a) Kept this line's facts wherever main corrected its own: the CLI, PHP tooling, deploy steps, the environment detector and the runner-less CI. Rewrote the added tooling page for the PHP scripts and PHPUnit, repaired the auto-merged BATS sections in the template page, and dropped the parallelism page, which duplicates the section already in the CI overview. --- .vortex/docs/.utils/update-docs.sh | 3 - .vortex/docs/content/README.mdx | 14 +- .vortex/docs/content/_code-lifecycle.mdx | 9 - .vortex/docs/content/architecture.mdx | 32 ++-- .../content/continuous-integration/README.mdx | 145 +++++++------- .../continuous-integration/circleci.mdx | 24 +-- .../continuous-integration/github-actions.mdx | 84 ++++++-- .vortex/docs/content/contributing/README.mdx | 37 ++-- .../contributing/maintenance/README.mdx | 13 +- .../maintenance/_release_template.md | 25 ++- .../content/contributing/maintenance/cli.mdx | 25 ++- .../maintenance/documentation.mdx | 88 +++++---- .../contributing/maintenance/release.mdx | 29 +-- .../maintenance/script-boilerplate.sh | 14 +- .../contributing/maintenance/template.mdx | 86 ++++----- .../contributing/maintenance/tooling.mdx | 91 +++++++++ .vortex/docs/content/contributing/roadmap.mdx | 5 +- .vortex/docs/content/deployment/README.mdx | 77 ++++---- .vortex/docs/content/deployment/artifact.mdx | 32 ++-- .vortex/docs/content/deployment/lagoon.mdx | 58 +++--- .../docs/content/deployment/notifications.mdx | 18 +- .vortex/docs/content/deployment/webhook.mdx | 14 ++ .vortex/docs/content/development/README.mdx | 118 ++++-------- .vortex/docs/content/development/ai.mdx | 25 +-- .vortex/docs/content/development/behat.mdx | 74 +++---- .vortex/docs/content/development/composer.mdx | 4 + .vortex/docs/content/development/database.mdx | 53 +++-- .../docs/content/development/debugging.mdx | 13 +- .vortex/docs/content/development/faqs.mdx | 28 +-- .vortex/docs/content/development/jest.mdx | 4 +- .vortex/docs/content/development/phpunit.mdx | 21 +- .../content/development/visual-regression.mdx | 3 +- .vortex/docs/content/drupal/README.mdx | 38 ++-- .vortex/docs/content/drupal/composer-json.mdx | 53 ++--- .../docs/content/drupal/generated-content.mdx | 10 + .vortex/docs/content/drupal/migrations.mdx | 56 +++--- .../docs/content/drupal/module-scaffold.mdx | 29 ++- .vortex/docs/content/drupal/modules.mdx | 7 +- .vortex/docs/content/drupal/provision.mdx | 27 +-- .vortex/docs/content/drupal/settings.mdx | 76 ++++---- .../docs/content/drupal/theme-scaffold.mdx | 24 ++- .vortex/docs/content/drupal/update-hooks.mdx | 33 ++-- .vortex/docs/content/faqs.mdx | 11 +- .vortex/docs/content/features.mdx | 18 +- .vortex/docs/content/hosting/README.mdx | 56 +++--- .vortex/docs/content/hosting/acquia.mdx | 27 +-- .vortex/docs/content/hosting/lagoon.mdx | 36 +++- .vortex/docs/content/releasing/README.mdx | 141 ++++++-------- .vortex/docs/content/releasing/gitflow.mdx | 102 +++++----- .vortex/docs/content/releasing/versioning.mdx | 13 +- .vortex/docs/content/support.mdx | 88 ++++----- .vortex/docs/content/tools/README.mdx | 4 +- .vortex/docs/content/tools/ahoy.mdx | 15 +- .vortex/docs/content/tools/behat.mdx | 37 ++-- .vortex/docs/content/tools/dclint.mdx | 40 ++-- .vortex/docs/content/tools/diffy.mdx | 16 +- .vortex/docs/content/tools/docker.mdx | 76 ++++---- .vortex/docs/content/tools/doctor.mdx | 181 +++++------------- .vortex/docs/content/tools/drush.mdx | 16 +- .vortex/docs/content/tools/eslint.mdx | 28 ++- .vortex/docs/content/tools/gherkin-lint.mdx | 28 +-- .vortex/docs/content/tools/git-artifact.mdx | 20 +- .vortex/docs/content/tools/gitleaks.mdx | 19 +- .vortex/docs/content/tools/hadolint.mdx | 16 +- .vortex/docs/content/tools/jest.mdx | 96 ++-------- .vortex/docs/content/tools/phpcs.mdx | 25 +-- .vortex/docs/content/tools/phpstan.mdx | 44 ++--- .vortex/docs/content/tools/phpunit.mdx | 27 +-- .vortex/docs/content/tools/pygmy.mdx | 16 +- .vortex/docs/content/tools/rector.mdx | 101 ++++------ .vortex/docs/content/tools/renovate.mdx | 99 +++++----- .vortex/docs/content/tools/twig-cs-fixer.mdx | 65 ++++--- .vortex/docs/content/updating-vortex.mdx | 22 ++- .vortex/docs/cspell.json | 8 +- 74 files changed, 1615 insertions(+), 1495 deletions(-) create mode 100644 .vortex/docs/content/contributing/maintenance/tooling.mdx diff --git a/.vortex/docs/.utils/update-docs.sh b/.vortex/docs/.utils/update-docs.sh index d055bef87..e909e51c7 100755 --- a/.vortex/docs/.utils/update-docs.sh +++ b/.vortex/docs/.utils/update-docs.sh @@ -77,6 +77,3 @@ sed "${sed_opts[@]}" "s/.vortex\/docs\/.utils\/variables\/extra\/.env.local.exam sed "${sed_opts[@]}" "s/.vortex\/docs\/.utils\/variables\/extra\/.env.variables.sh/.env/g" "${OUTPUT_FILE}" sed "${sed_opts[@]}" "s/.vortex\/docs\/.utils\/variables\/extra\/docker-compose.variables.sh/docker-compose.yml/g" "${OUTPUT_FILE}" sed "${sed_opts[@]}" "s/.vortex\/docs\/.utils\/variables\/extra\/ci.variables.sh/CI config/g" "${OUTPUT_FILE}" - -echo "---" >>"${OUTPUT_FILE}" -echo "Variable list generated with [Shellvar - Utility to work with shell variables](https://github.com/AlexSkrypnyk/shellvar)" >>"${OUTPUT_FILE}" diff --git a/.vortex/docs/content/README.mdx b/.vortex/docs/content/README.mdx index ade97c3ed..c1bcb827b 100644 --- a/.vortex/docs/content/README.mdx +++ b/.vortex/docs/content/README.mdx @@ -2,7 +2,7 @@ title: '' sidebar_label: Introduction sidebar_position: 1 -description: Vortex is a Drupal project template designed to streamline onboarding, accelerate development, and support long-term maintainability. It provides a complete foundation for building and deploying Drupal sites — including containerized local environments, automated testing and code quality tools, continuous integration pipeline configurations, and integrations with popular hosting platforms. Everything is pre-configured and ready to use, so teams can focus on building features instead of setting up infrastructure. +description: Vortex is a Drupal project template designed to streamline onboarding, accelerate development, and support long-term maintainability. It provides a complete foundation for building and deploying Drupal sites - including containerized local environments, automated testing and code quality tools, continuous integration pipeline configurations, and integrations with popular hosting platforms. Everything is pre-configured and ready to use, so teams can focus on building features instead of setting up infrastructure. --- @@ -19,7 +19,7 @@ description: Vortex is a Drupal project template designed to streamline onboardi **Vortex** is a Drupal project template designed to streamline onboarding, accelerate development, and support long-term maintainability. -It provides a complete foundation for building and deploying Drupal sites — +It provides a complete foundation for building and deploying Drupal sites - including containerized local environments, automated testing and code quality tools, continuous integration pipeline configurations, and integrations with popular hosting platforms. Everything is pre-configured and ready to use, so teams can focus on @@ -32,10 +32,10 @@ and start contributing right away. The template is actively maintained and kept in sync with the latest tools. Every change is verified through automated tests to ensure updates remain stable -and reliable — reducing the risk of regressions and making it easier to maintain +and reliable - reducing the risk of regressions and making it easier to maintain projects over time. -Learn more about Vortex from our [DrupalSouth 2025 presentation](https://docs.google.com/presentation/d/e/2PACX-1vQMBTteMr6cALYGtI3xwqvt9HFpzSzTsV3ie5qhVMPK5eSZBudyQp7H1_Wfoy7HMYqfgN2ooH4rlWJL/pub?start=false&loop=false&delayms=5000) +Learn more about **Vortex** from our [DrupalSouth 2025 presentation](https://docs.google.com/presentation/d/e/2PACX-1vQMBTteMr6cALYGtI3xwqvt9HFpzSzTsV3ie5qhVMPK5eSZBudyQp7H1_Wfoy7HMYqfgN2ooH4rlWJL/pub?start=false&loop=false&delayms=5000) ([Google Slides](https://docs.google.com/presentation/d/1nsFfd9C_ddKD5O0sQeJ8_6pJjU0XWaXXwDmD0SwCIL4/) | [PDF](https://docs.google.com/presentation/d/1nsFfd9C_ddKD5O0sQeJ8_6pJjU0XWaXXwDmD0SwCIL4/export/pdf)). @@ -124,7 +124,7 @@ foundation for developing and maintaining Drupal projects. ### 📖 Documentation
    -
  • 🌐 Centralised documentation at https://www.vortextemplate.com
  • +
  • 🌐 Centralized documentation at https://www.vortextemplate.com
  • 🗂️ Scaffolded structure for project-specific docs
  • 📋 Onboarding checklist to guide team setup
@@ -134,8 +134,8 @@ foundation for developing and maintaining Drupal projects.
  • 📦 Adds boilerplate files based on custom selections
  • 🏗️ Renames files and replaces strings during installation
  • -
  • 🔄️ Allows to update existing installations to a newer boilerplate version
  • -
  • 🔌 Standalone CLI application extensible with a flexible API to support new integrations
  • +
  • 🔄️ Updates existing installations to a newer boilerplate version
  • +
  • 🔌 Standalone CLI application extensible with an API to support new integrations
## 🧱 Solid base for your projects diff --git a/.vortex/docs/content/_code-lifecycle.mdx b/.vortex/docs/content/_code-lifecycle.mdx index 10869ad46..7af9447a1 100644 --- a/.vortex/docs/content/_code-lifecycle.mdx +++ b/.vortex/docs/content/_code-lifecycle.mdx @@ -50,13 +50,4 @@ ═════════════════════════════════════════════════════════════════════════════════════════ ┊ PR Environment ┊ Dev Staging Production ┊ (auto-removed) ┊ develop branch main branch production branch or tag - - - Security Audit Workflow - ═════════════════════════════════════════════════════════════════════════════════════════ - Push, pull request or manual run ──► Composer advisory audit ──► Gitleaks secret scan - (composer audit --locked) (committed secrets) - - Runs as its own workflow, independently of the pipeline above, and does not gate - deployment. Every check runs even if an earlier one failed. ``` diff --git a/.vortex/docs/content/architecture.mdx b/.vortex/docs/content/architecture.mdx index dae4ac358..b75485b3b 100644 --- a/.vortex/docs/content/architecture.mdx +++ b/.vortex/docs/content/architecture.mdx @@ -18,8 +18,8 @@ maintainability: and avoids reinvention. - **Avoid silent errors**: Misconfigurations should fail loudly. - **Readability counts**: Code and configuration are meant to be understood. -- **Explicit logging helps**: Scripts log every major step, so it’s easy to - follow what’s going on. +- **Explicit logging helps**: Scripts log every major step, so it's easy to + follow what's going on. ## System components @@ -113,11 +113,11 @@ running Drush commands, you run a single provision script. This script handles: - Running updates, config imports, cache rebuilds, and deploy hooks - Executing post-provision custom scripts -Because provisioning is centralized, it runs exactly in the same way in every +Because provisioning is centralized, it runs the same way in every environment: local, CI, or hosting. This eliminates "works on my machine" problems and makes the process predictable for everyone. -It also allows to add more automation around provisioning, like conditionally +It also makes it possible to add more automation around provisioning, like conditionally running migrations or creating demo content. ➡️ See [Drupal > Provision](./drupal/provision) @@ -148,7 +148,7 @@ Here's how it works: environment inside each module-specific file. This structure gives you clarity, avoids config sprawl, and lets you remove a -module’s settings cleanly when no longer needed. +module's settings cleanly when no longer needed. **Vortex** also includes tests for these settings to ensure they are loaded correctly in each environment. @@ -164,11 +164,13 @@ correctly in each environment. - **Rector** - **Twig CS Fixer** - **ESLint** +- **Stylelint** -You’ll also find scaffolds for: +You'll also find scaffolds for: - **PHPUnit**: Unit and kernel testing - **Behat**: Behavior-driven testing with screenshot capture and extra steps +- **Jest**: JavaScript unit testing ➡️ See [Tools](./tools) @@ -233,8 +235,8 @@ These scripts: ### Customizing scripts -All scripts support configuration via environment variables, allowing them -to be easily adapted to specific project or environment needs. +All scripts support configuration via environment variables, so they can be +adapted to specific project or environment needs. During initial project setup, `.env` file is updated with project-specific values like project name, email etc. Then, environment variables (secrets, @@ -244,17 +246,17 @@ tokens, etc.) are set in CI or hosting environments. ### Router scripts -Most **Vortex** commands are implemented as router scripts entry points like -`fetch-db.sh` or `deploy.sh` that dynamically invoke a more specific logic -for your setup, based on configuration or environment variables. +Most **Vortex** commands are implemented as _router_ script entry points, like +`vortex-fetch-db` or `vortex-deploy`, that dynamically invoke the more specific +logic for your setup, based on configuration or environment variables. For example: -- `fetch-db.sh` is a router script that fetches a database from any +- `vortex-fetch-db` is a router script that fetches a database from any supported hosting provider or custom location without needing to know the specifics of each provider. -- `deploy.sh` is a router script that deploys code to any hosting provider in a - consistent manner, regardless of whether it's Acquia, Lagoon, or another +- `vortex-deploy` is a router script that deploys code to any hosting provider + in a consistent manner, regardless of whether it's Acquia, Lagoon, or another platform.
@@ -267,8 +269,6 @@ For example:
-➡️ See [Variables](./development/variables) - ## Environment variables **Vortex** uses environment variables extensively to configure behavior across diff --git a/.vortex/docs/content/continuous-integration/README.mdx b/.vortex/docs/content/continuous-integration/README.mdx index f682991f9..7c169b8e5 100644 --- a/.vortex/docs/content/continuous-integration/README.mdx +++ b/.vortex/docs/content/continuous-integration/README.mdx @@ -3,18 +3,14 @@ sidebar_label: Overview sidebar_position: 1 --- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; +# Continuous integration -# Continuous Integration +**Vortex** offers continuous integration configurations for [GitHub Actions](./github-actions.mdx) +and [CircleCI](./circleci.mdx) that automate the process of building, testing, +and deploying your site. -**Vortex** offers continuous integration configurations for [GitHub Actions](/docs/continuous-integration/github-actions) -and [CircleCI](/docs/continuous-integration/circleci) providers that allow you to -automate the process of building, testing, and deploying your site. - -The workflow configuration is identical for both continuous integration -providers. You would need to choose one of them and follow the setup -instructions. +The workflow structure is identical for both continuous integration providers. +Choose one of them and follow its setup instructions. The continuous integration pipeline consists of multiple jobs executed on the CI provider's standard environment - a CircleCI convenience image or a @@ -44,9 +40,9 @@ import CodeLifecycle from '../_code-lifecycle.mdx'; - Validates Composer configuration - Assembles the codebase by installing dependencies - Provisions a website -- Runs PHPUnit tests (first instance only) -- Checks code coverage and posts PR comment (first instance only) -- Runs BDD tests (distributed across all instances) +- Runs PHPUnit and Jest tests (first container only) +- Checks code coverage and posts a PR comment (first container only) +- Runs BDD tests (distributed across all containers - see [Test parallelism](./parallelism.mdx)) - Collects and stores test results and artifacts ### 3. Build @@ -61,29 +57,41 @@ import CodeLifecycle from '../_code-lifecycle.mdx'; - Runs after successful completion of the `lint`, `test`, and `build` jobs - Uses the built codebase without development dependencies from the `build` job - Adds required secrets and environment variables -- Triggers a deployment using a router script +- Triggers a deployment using a router script - see [Deployment](../deployment/README.mdx) ## Security audit -Security checks run in their own workflow, separate from the pipeline above, so that a failing audit is never confused with a failing linter and can be re-run on its own: +Security checks run in their own workflow, separate from the pipeline above, so +that a failing audit is never confused with a failing linter and can be re-run +on its own: | Provider | Location | |----------|----------| | GitHub Actions | The `Security audit` workflow in `.github/workflows/audit.yml` | | CircleCI | The `audit` workflow in `.circleci/config.yml` | -The workflow runs the same two checks in both providers, and needs neither the application containers nor installed dependencies: +The workflow runs the same 2 checks in both providers, and needs neither the +application containers nor installed dependencies: - `composer audit --locked` checks the packages pinned in `composer.lock` against published security advisories -- [Gitleaks](/docs/tools/gitleaks) scans the codebase for committed secrets +- [Gitleaks](../tools/gitleaks.mdx) scans the codebase for committed secrets -Every check runs even if an earlier one failed, so a single run reports all the findings at once. The workflow fails if any of the checks failed, unless that check's `_IGNORE_FAILURE` variable is set to `1`. +Every check runs even if an earlier one failed, so a single run reports all the +findings at once. The workflow fails if any of the checks failed, unless that +check's `_IGNORE_FAILURE` variable (see [Ignore tool failures](#ignore-tool-failures)) +is set to `1`. -It is triggered by the same pushes, pull requests and tags as the main pipeline, and can also be started on demand - in GitHub Actions from **Actions → Security audit → Run workflow**, and in CircleCI by re-running the `audit` workflow from the pipeline view. +It is triggered by the same pushes, pull requests and tags as the main +pipeline, and can also be started on demand - in GitHub Actions from +**Actions → Security audit → Run workflow**, and in CircleCI by re-running the +`audit` workflow from the pipeline view. :::note -Because the audit is a separate workflow, it is not a dependency of the `deploy` job - a failing audit does not by itself stop a deployment. To block merges and deployments on it, add its check to the repository's branch protection rules as a required status check. +Because the audit is a separate workflow, it is not a dependency of the +`deploy` job - a failing audit does not by itself stop a deployment. To block +merges and deployments on it, add its check to the repository's branch +protection rules as a required status check. ::: @@ -92,26 +100,29 @@ Because the audit is a separate workflow, it is not a dependency of the `deploy` The database is fetched on the first continuous integration run of the day and cached so that the remaining runs on the same day reuse the cached database dump. -By default, the database is cached per-branch for 24 hours. If cache is not -available, the fallback default branch is used. +The cache key is built from a configured cache source branch (the +`VORTEX_CI_DB_CACHE_BRANCH` variable, `develop` by default) and a daily +timestamp - every run on any branch reads the same shared cache. If no cache +exists for the current day, the previous day's cache for the same source branch +is used as a fallback. :::note -Database caching is a very powerful feature that allows to speed up the -continuous integration runs on large projects with a lot of data. +Database caching speeds up continuous integration runs considerably on projects +with a lot of data. In case of a project with a large database >1GB, the database import itself may take a long time, so it may be worth looking into either packaging the database dump into a container image or using a sanitized database dump with only the required data for the tests. -**Vortex** supports both creating and using a database container image with embedded -data. You may use [MariaDB data container for Drupal with database captured as Docker layers](https://github.com/drevops/mariadb-drupal-data) to create -an initial database image. +**Vortex** supports both creating and using a database container image with +embedded data. You may use [MariaDB data container for Drupal with database captured as Docker layers](https://github.com/drevops/mariadb-drupal-data) +to create an initial database image. -There are other tools also available for this purpose, such as [Drush GDPR Dumper](https://github.com/robiningelbrecht/drush-gdpr-dumper) -that allows to remove data from the database dump during Drush database -export command without the need for an intermediate database import step. +Other tools serve the same goal: [Drush GDPR Dumper](https://github.com/robiningelbrecht/drush-gdpr-dumper), +for example, removes data during the Drush database export itself, without an +intermediate database import step. ::: @@ -123,9 +134,9 @@ cache keys: ```yaml # Before -v25.11.0 +v26.8.0 # After -v25.11.1 +v26.8.1 ``` The version tag is the **Vortex** release version (CalVer). Bumping only its @@ -134,7 +145,9 @@ shipped by a future **Vortex** update. ## Trigger conditions -The continuous integration pipeline is triggered by: +Both providers build on branch pushes, pull requests, tags matching semantic +version (`1.2.3`, `1.2.3-rc.1`) or date-based (`2023-04-17`) patterns, and a +nightly schedule that refreshes the database cache. - **Push events** to the following branches: - `production`, `main`, `master`, `develop` @@ -344,9 +357,14 @@ by setting the `VORTEX_DEBUG` variable to `1` in your CI provider's settings. ### Runner disk space -Continuous integration jobs run on hosted runners with a fixed amount of disk space - a fixed allocation on CircleCI, and on GitHub Actions a root volume that is largely consumed before the job starts. Measured on one `ubuntu-latest` image, preinstalled software occupied 59 GB of a 72 GB volume, leaving the job around 13 GB. Pulled container images, database dumps, and every file created while provisioning share what is left. +Hosted runners come with a fixed amount of disk space, and running out of it is +easy to misread: the runner is terminated from the outside, the step that was +running never reports an error, and the failure looks like a hang rather than a +disk problem. If a build dies during provisioning without reporting an error, +suspect the disk first. -When it runs out, the runner is terminated from the outside: the step that was running never reports an error and the log archive may be lost entirely, so the failure looks like a hang rather than a disk problem. +The most reliable fix is to reduce what has to fit: a sanitized dump or a +[database container image](#caching-strategy) instead of a full dump. **Vortex** keeps the build within that budget: @@ -385,42 +403,27 @@ Sometimes you may want to allow builds to pass despite linter and test failures. Set the corresponding `VORTEX_CI_*_IGNORE_FAILURE` variable to `1` to ignore failures (but still run the tool and see the results in the logs): -| Tool | Purpose | Variable | -|-------------------------------------------------------------|------------------------------------------|-----------------------------------------------| -| [Behat](/docs/tools/behat) | Run BDD acceptance tests | `VORTEX_CI_BEHAT_IGNORE_FAILURE` | -| Composer normalize | Ensure `composer.json` is sorted | `VORTEX_CI_COMPOSER_NORMALIZE_IGNORE_FAILURE` | -| Composer security audit | Check dependencies for vulnerabilities | `VORTEX_CI_COMPOSER_AUDIT_IGNORE_FAILURE` | -| Composer validate | Validate `composer.json` and lock file | `VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE` | -| [DCLint](/docs/tools/dclint) | Lint Docker Compose files | `VORTEX_CI_DCLINT_IGNORE_FAILURE` | -| [ESLint](/docs/tools/eslint) | Run ESLint and Stylelint | `VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE` | -| [Gherkin Lint](/docs/tools/gherkin-lint) | Lint Behat feature files | `VORTEX_CI_GHERKIN_LINT_IGNORE_FAILURE` | -| [Hadolint](/docs/tools/hadolint) | Lint Dockerfiles for best practices | `VORTEX_CI_HADOLINT_IGNORE_FAILURE` | -| [PHPCS](/docs/tools/phpcs) | Check PHP coding standards | `VORTEX_CI_PHPCS_IGNORE_FAILURE` | -| [PHPStan](/docs/tools/phpstan) | Static analysis for PHP | `VORTEX_CI_PHPSTAN_IGNORE_FAILURE` | -| [PHPUnit](/docs/tools/phpunit) | Run unit, kernel, and functional tests | `VORTEX_CI_PHPUNIT_IGNORE_FAILURE` | -| [Rector](/docs/tools/rector) | Check for automated refactoring rules | `VORTEX_CI_RECTOR_IGNORE_FAILURE` | -| [Twig CS Fixer](/docs/tools/twig-cs-fixer) | Lint Twig templates | `VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE` | +| Tool | Purpose | Variable | +|-----------------------------------------------------|------------------------------------------|-----------------------------------------------| +| [Behat](../tools/behat.mdx) | Run BDD acceptance tests | `VORTEX_CI_BEHAT_IGNORE_FAILURE` | +| Composer normalize | Ensure `composer.json` is sorted | `VORTEX_CI_COMPOSER_NORMALIZE_IGNORE_FAILURE` | +| Composer security audit | Check dependencies for vulnerabilities | `VORTEX_CI_COMPOSER_AUDIT_IGNORE_FAILURE` | +| Composer validate | Validate `composer.json` and lock file | `VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE` | +| [DCLint](../tools/dclint.mdx) | Lint Docker Compose files | `VORTEX_CI_DCLINT_IGNORE_FAILURE` | +| [ESLint](../tools/eslint.mdx) and Stylelint | Lint JavaScript and CSS | `VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE` | +| [Gherkin Lint](../tools/gherkin-lint.mdx) | Lint Behat feature files | `VORTEX_CI_GHERKIN_LINT_IGNORE_FAILURE` | +| [Gitleaks](../tools/gitleaks.mdx) | Scan the codebase for committed secrets | `VORTEX_CI_GITLEAKS_IGNORE_FAILURE` | +| [Hadolint](../tools/hadolint.mdx) | Lint Dockerfiles for best practices | `VORTEX_CI_HADOLINT_IGNORE_FAILURE` | +| [Jest](../tools/jest.mdx) | Run JavaScript unit tests | `VORTEX_CI_JEST_IGNORE_FAILURE` | +| [PHPCS](../tools/phpcs.mdx) | Check PHP coding standards | `VORTEX_CI_PHPCS_IGNORE_FAILURE` | +| [PHPStan](../tools/phpstan.mdx) | Static analysis for PHP | `VORTEX_CI_PHPSTAN_IGNORE_FAILURE` | +| [PHPUnit](../tools/phpunit.mdx) | Run unit, kernel, and functional tests | `VORTEX_CI_PHPUNIT_IGNORE_FAILURE` | +| [Rector](../tools/rector.mdx) | Check for automated refactoring rules | `VORTEX_CI_RECTOR_IGNORE_FAILURE` | +| [SDC Devel](../drupal/theme-scaffold.mdx#single-directory-components) | Validate Single Directory Components | `VORTEX_CI_SDC_DEVEL_IGNORE_FAILURE` | +| [Twig CS Fixer](../tools/twig-cs-fixer.mdx) | Lint Twig templates | `VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE` | ### Configure deployment skip conditions -Sometimes it may be necessary to skip deployments for specific branches or pull -requests. For example, you may want to temporarily avoid deploying more changes -into already deployed environments which still run CI checks on new commits. - -To skip deployments for specific branches, set the `VORTEX_DEPLOY_SKIP_BRANCHES` -variable to a comma-separated list of exact branch names: - -```shell -VORTEX_DEPLOY_ALLOW_SKIP=1 # Enable deployment skipping -VORTEX_DEPLOY_SKIP_BRANCHES="feature/test,hotfix/urgent,project/experimental" -``` - -To skip deployments for specific pull requests, set the `VORTEX_DEPLOY_SKIP_PRS` -variable to a comma-separated list of PR numbers: - -```shell -VORTEX_DEPLOY_ALLOW_SKIP=1 # Enable deployment skipping -VORTEX_DEPLOY_SKIP_PRS="123,456,789" -``` - -To skip all deployments entirely, set `VORTEX_DEPLOY_SKIP=1`. +Deployments can be skipped for specific branches or pull requests while their +CI checks keep running - see +[Deployment > Skipping deployments](../deployment/README.mdx#skipping-deployments). diff --git a/.vortex/docs/content/continuous-integration/circleci.mdx b/.vortex/docs/content/continuous-integration/circleci.mdx index d8727e47a..203d0907a 100644 --- a/.vortex/docs/content/continuous-integration/circleci.mdx +++ b/.vortex/docs/content/continuous-integration/circleci.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 2 +sidebar_position: 3 --- # CircleCI @@ -13,14 +13,14 @@ For general CircleCI documentation, refer to the :::info For information about the CI workflow structure, jobs, and caching strategy, -see the [CI overview](/docs/continuous-integration). +see the [Continuous integration overview](./README.mdx). ::: ## Onboarding CircleCI onboarding is part of the project setup flow. See -[Set up integrations](/docs/installation?ci-provider=circleci#5-set-up-continuous-integration) +[Set up continuous integration](/docs/installation?ci-provider=circleci#5-set-up-continuous-integration) in the Installation guide and select **CircleCI**. ## Maintenance @@ -42,6 +42,7 @@ only runs for specific branch patterns, controlled by a regex filter in the | `bugfix/**` | Non-urgent bug fix branches (e.g., `bugfix/fix-auth`, `bugfix/456-crash`) | | `project/**` | Long-lived project branches for large initiatives | | `ci*` | Temporary CI testing branches (e.g., `ci`, `ci-debug`) | +| `N.x` | Major version branches (e.g., `1.x`, `2.x`) | To modify which branches trigger deployments, update the `only` filter regex in the `deploy` job. @@ -73,7 +74,7 @@ resource_class: large # Options: small, medium, large, xlarge Resource classes scale CPU and memory only. Disk space is not tied to the resource class, so upgrading it will not help a job that runs out of space - -see [Runner disk space](/docs/continuous-integration#runner-disk-space). +see [Runner disk space](./README.mdx#runner-disk-space). ### Change test parallelism @@ -87,16 +88,9 @@ test: ``` Every added container that runs Behat also needs a matching `pN` profile in -`behat.yml` - without it, Behat fails with `profile 'p2' does not exist`. A -container excluded from Behat needs no profile. Then distribute Behat scenarios -across the containers using profile tags (`@p0`, `@p1`, `@p2`, etc.) in your -feature files. Since the first container also runs Jest, PHPUnit, coverage, and -Single Directory Component validation, assign more Behat scenarios to the -additional containers to keep build times balanced. - -See [Adding more containers](/docs/continuous-integration#adding-more-containers) -for the full procedure, and [Choosing which container runs what](/docs/continuous-integration#choosing-which-container-runs-what) -to move a tool onto a different container. +`behat.yml`. See [Test parallelism](./parallelism.mdx) for the full procedure, +how scenarios are balanced across containers, and how to move a tool onto a +different container. ### SSH access for debugging @@ -128,5 +122,5 @@ is `90` (percent). Coverage reports can be posted as PR comments. This requires a `GITHUB_TOKEN` environment variable with permission to post comments. Each new report replaces -the previous one — older comments are minimized to keep the PR timeline clean. +the previous one - older comments are minimized to keep the PR timeline clean. To disable PR comments, set `VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP` to `1`. diff --git a/.vortex/docs/content/continuous-integration/github-actions.mdx b/.vortex/docs/content/continuous-integration/github-actions.mdx index ae9e9d2ce..24eaa9606 100644 --- a/.vortex/docs/content/continuous-integration/github-actions.mdx +++ b/.vortex/docs/content/continuous-integration/github-actions.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 3 +sidebar_position: 2 --- # GitHub Actions @@ -14,19 +14,19 @@ For general GitHub Actions documentation, refer to the :::info For information about the CI workflow structure, jobs, and caching strategy, -see the [CI overview](/docs/continuous-integration). +see the [Continuous integration overview](./README.mdx). ::: ## Onboarding GitHub Actions onboarding is part of the project setup flow. See -[Set up integrations](/docs/installation?ci-provider=github-actions#5-set-up-continuous-integration) +[Set up continuous integration](/docs/installation?ci-provider=github-actions#5-set-up-continuous-integration) in the Installation guide and select **GitHub Actions**. ## Maintenance -### Update deployment branches +### Update trigger branches The workflow triggers on pushes and pull requests to specific branches defined in the `on.push.branches` and `on.pull_request.branches` sections of @@ -63,8 +63,8 @@ Security checks live in a second workflow, `.github/workflows/audit.yml`, which triggers on the same branches and tags as the main workflow and can also be started on demand from **Actions → Security audit → Run workflow**. -See [Security audit](/docs/continuous-integration#security-audit) for what it -runs and how to make it block merges and deployments. +See [Security audit](./README.mdx#security-audit) for what it runs and how to +make it block merges and deployments. ### Configure database caching @@ -92,15 +92,66 @@ runs-on: ubuntu-latest-4-cores # Options: ubuntu-latest, ubuntu-latest-4-cores, ``` Larger runners also come with more disk, which matters for projects whose -provisioning is disk-heavy. See [Runner disk space](/docs/continuous-integration#runner-disk-space). +provisioning is disk-heavy. See [Runner disk space](#runner-disk-space). -### Preinstalled toolchains +### Runner disk space Most of the `ubuntu-latest` runner's root volume is consumed by preinstalled software before a job starts - in one measurement, 59 GB of 72 GB, leaving around 13 GB to work with. The `test` and `build` jobs can remove the toolchains no **Vortex** job uses - GHCup, Swift, PowerShell, .NET and CodeQL - before doing anything else, which freed about 15 GB in that same measurement. GitHub reissues the runner image regularly, so read the **Report disk usage** step of a recent run for the current numbers. The removal is off by default because it is destructive to the runner. To enable it, set the `VORTEX_CI_FREE_DISK_SPACE` variable to `1` in **Settings → Secrets and variables → Actions → Variables**. -See [Runner disk space](/docs/continuous-integration#runner-disk-space) for the full measurements, how to free the Android SDK as well, and what to do when a job still runs out. +- The `build` and `database` jobs print `df -h` in a **Report disk usage** + step, so the disk state is on the record for every run. +- The Docker build cache and dangling images are pruned once the stack is up. +- The database dump is removed from the runner as soon as it has been copied + into the container, so only one copy is held during provisioning. + +#### Reclaim the preinstalled toolchains + +The `build` and `database` jobs can remove the preinstalled toolchains no +**Vortex** job uses - GHCup, Swift, PowerShell, .NET and CodeQL - before doing +anything else. In the measurement above this freed about 15 GB in around 14 +seconds, taking the job from roughly 13 GB of headroom to 28 GB. + +The removal is destructive to the runner for the rest of the job, so it is off +by default. Turn it on by setting the `CI_FREE_DISK_SPACE` variable to `1` in +**Settings → Secrets and variables → Actions → Variables**. Leave it unset on a +project whose workflow runs a step that needs Haskell, Swift, PowerShell, .NET +or CodeQL. + +:::note + +Every figure here comes from one runner image at one point in time, and GitHub +reissues that image regularly. Read the **Report disk usage** output of a +recent run for the current numbers - if the reclaimed amount has shrunk, the +path list needs revisiting. + +::: + +#### Free even more space + +With `CI_FREE_DISK_SPACE` enabled, the Android SDK is the single largest +remaining tree at about 10 GB, but removing it takes around 50 seconds. It +ships commented out in the workflow for that reason - uncomment the line in the +**Free up disk space on the runner** step of both the `build` and `database` +jobs to enable it: + +```yaml +set -- "$@" /host/usr/local/lib/android +``` + +The removal runs through a container that bind-mounts the runner's root +filesystem, because the job itself runs in a container and cannot see the +host's directories directly. This is also why the general-purpose disk-cleanup +actions available on the Marketplace have no effect in a **Vortex** job. + +#### When space still runs out + +If a build dies during provisioning without reporting an error, suspect the +disk and read the **Report disk usage** output. The most reliable fix is to +reduce what has to fit: a sanitized dump or a +[database container image](./README.mdx#caching-strategy) instead of a full +dump. A [larger runner](#change-runner-size) also comes with more disk. ### Change test parallelism @@ -115,16 +166,9 @@ strategy: ``` Every added container that runs Behat also needs a matching `pN` profile in -`behat.yml` - without it, Behat fails with `profile 'p2' does not exist`. A -container excluded from Behat needs no profile. Then distribute Behat scenarios -across the containers using profile tags (`@p0`, `@p1`, `@p2`, etc.) in your -feature files. Since the first container also runs Jest, PHPUnit, coverage, and -Single Directory Component validation, assign more Behat scenarios to the -additional containers to keep build times balanced. - -See [Adding more containers](/docs/continuous-integration#adding-more-containers) -for the full procedure, and [Choosing which container runs what](/docs/continuous-integration#choosing-which-container-runs-what) -to move a tool onto a different container. +`behat.yml`. See [Test parallelism](./parallelism.mdx) for the full procedure, +how scenarios are balanced across containers, and how to move a tool onto a +different container. ### Manual deployment @@ -180,7 +224,7 @@ variable in **Settings → Secrets and variables → Actions → Variables**. De is `90` (percent). Coverage reports are automatically posted as PR comments. Each new report -replaces the previous one — older comments are minimized to keep the PR +replaces the previous one - older comments are minimized to keep the PR timeline clean. To disable this, set `VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP` to `1`. diff --git a/.vortex/docs/content/contributing/README.mdx b/.vortex/docs/content/contributing/README.mdx index 3d480a87c..f60ced783 100644 --- a/.vortex/docs/content/contributing/README.mdx +++ b/.vortex/docs/content/contributing/README.mdx @@ -5,33 +5,34 @@ sidebar_position: 1 # Contributing -**Vortex** is an open source project. There are numerous ways to -contribute to **Vortex** whether you’re a developer or a non-technical -user. - -## What forms of contribution are we looking for? - -Any form of contribution to **Vortex**, be it rectifying a bug, proposing -a new feature, or enhancing our documentation, is highly appreciated. We are -glad for your willingness to assist! +**Vortex** is an open source project, and contributions of every kind are +welcome - fixing a bug, proposing a feature, or improving the documentation, +whether you're a developer or a non-technical user. ## I've discovered a problem -We're constantly eager to address any issues, so your reports are always -appreciated. However, we kindly ask you to first check if your problem has -already been reported in the [issue queue](https://github.com/drevops/vortex/issues). +Report it in the [issue queue](https://github.com/drevops/vortex/issues) - +after checking whether the problem has already been reported there. ## I want to suggest a feature or an idea -Fantastic! Please [submit your idea](https://github.com/drevops/vortex/issues) -and we'll gladly review it. While we can't promise every suggestion will be -implemented, we are always keen to hear innovative ideas for enhancing -the **Vortex**. +[Submit your idea](https://github.com/drevops/vortex/issues) to the same issue +queue and we'll review it. Not every suggestion lands, but ideas for improving +**Vortex** are always read. + +## I want to submit a change + +Changes come in as pull requests against the +[drevops/vortex](https://github.com/drevops/vortex) repository. The +[Maintenance](maintenance/README.mdx) section documents how the template, the +installer, and this documentation site are developed and tested, so your change +arrives with the right tests and fixtures. ## Talk to us on Slack -Another good way is also to talk to us via `#drevops` Slack about your idea. -Join today! +Another good way is to talk to us in the `#drevops` channel of the Drupal +Slack about your idea. Join today! +
Badge of the Drupal slack
diff --git a/.vortex/docs/content/contributing/maintenance/README.mdx b/.vortex/docs/content/contributing/maintenance/README.mdx index a93bdcc47..2ff3e1d5e 100644 --- a/.vortex/docs/content/contributing/maintenance/README.mdx +++ b/.vortex/docs/content/contributing/maintenance/README.mdx @@ -6,12 +6,13 @@ sidebar_position: 1 # Maintenance This section covers the processes and guidelines for maintaining the -**Vortex** project itself, including the template, CLI, documentation, -and release workflows. +**Vortex** project itself, including the template, CLI, tooling package, +documentation, and release workflows. | Topic | Description | |-------|-------------| -| [Template](template) | Maintaining template scripts and testing | -| [CLI](cli) | Maintaining the template CLI and testing | -| [Documentation](documentation) | Authoring and publishing Vortex documentation | -| [Release](release) | Versioning strategy and release process | +| [Template](template.mdx) | Maintaining the shipped template and its testing layers | +| [CLI](cli.mdx) | Maintaining the template CLI and its snapshot tests | +| [Tooling](tooling.mdx) | Maintaining the `drevops/vortex-tooling` scripts package | +| [Documentation](documentation.mdx) | Authoring and publishing the **Vortex** documentation | +| [Release](release.mdx) | Versioning strategy and the release process | diff --git a/.vortex/docs/content/contributing/maintenance/_release_template.md b/.vortex/docs/content/contributing/maintenance/_release_template.md index 540a581e6..a88f4311d 100644 --- a/.vortex/docs/content/contributing/maintenance/_release_template.md +++ b/.vortex/docs/content/contributing/maintenance/_release_template.md @@ -1,12 +1,12 @@ -## [VERSION] — [SHORT TITLE] +## [VERSION] - [SHORT TITLE] -[Very short summary, 1–3 sentences. E.g. “This release updates the base template to Drupal X.Y, improves the CLI UX, and expands documentation for local dev.”] +[Very short summary, 1-3 sentences. E.g. "This release updates the base template to Drupal X.Y, improves the CLI UX, and expands documentation for local dev."] --- ## 🔍 Highlights -- [1–3 top-level items that matter most to users] +- [1-3 top-level items that matter most to users] - [Optional: link to detailed docs if relevant] --- @@ -26,7 +26,7 @@ - [New features or major additions in the project template] - 🛠 **Changed** - - [Improvements, refactors, behaviour changes (but not fully breaking)] + - [Improvements, refactors, behavior changes (but not fully breaking)] - 🐞 **Fixed** - [Bug fixes] @@ -36,6 +36,13 @@ --- +### 🧰 Tooling + +- [Changes to the `drevops/vortex-tooling` scripts package, with the tag released with this version] +- [If none] None. + +--- + ### 🎛 CLI - ✨ **New** @@ -64,21 +71,23 @@ ## 📋 Release checklist -- [ ] Updated all dependencies outside of the schedule -- [ ] Updated container images to the latest versions and checked that `@see` links +- [ ] Updated all dependencies outside of the schedule. +- [ ] Updated container images to the latest versions and checked that `@see` links are working. - [ ] Updated PHP version in `composer.json` for `config.platform`. - [ ] Updated PHP version in `phpcs.xml` for `testVersion`. - [ ] Updated PHP version in `phpstan.neon` for `phpVersion`. +- [ ] Tagged `drevops/vortex-tooling` before the Vortex tag when the tooling changed, and required the freshly tagged version with a tilde constraint in `composer.json` so the requirement stays within that version's minor. - [ ] Updated minor version of all packages in `composer.json`. -- [ ] Tagged `drevops/vortex-tooling` before the Vortex tag when the tooling changed, and pinned the freshly tagged version as the upper boundary in `composer.json`. - [ ] Updated minor version of dependencies in theme's `package.json`. - [ ] Aligned the CI runner PHP version with `config.platform` (the `cimg/php` tag in `.circleci/config.yml` and the `setup-php` `php-version` in the GitHub Actions workflows). - [ ] Incremented the cache version in `.circleci/config.yml` and `.github/workflows/build-test-deploy.yml`. - [ ] Updated documentation. +- [ ] Regenerated the installer snapshots. +- [ ] Re-recorded the demo videos. - [ ] Tagged the Vortex release. --- **Full Changelog**: https://github.com/drevops/vortex/compare/[PREVIOUS_VERSION]...[NEW_VERSION] -@AlexSkrypnyk, @renovate[bot] and [renovate[bot]](https://github.com/apps/renovate) +[CONTRIBUTORS] diff --git a/.vortex/docs/content/contributing/maintenance/cli.mdx b/.vortex/docs/content/contributing/maintenance/cli.mdx index 4bce6793a..4fe997813 100644 --- a/.vortex/docs/content/contributing/maintenance/cli.mdx +++ b/.vortex/docs/content/contributing/maintenance/cli.mdx @@ -56,7 +56,7 @@ priority order: `VORTEX_CLI_INSTALL_PROMPT_` naming convention. 3. **Discovery** — auto-detection from existing project files (e.g., `composer.json`, `.env`, hosting-specific files like `.lagoon.yml`). -4. **Handler defaults** — fallback values defined by each handler. +4. **Handler defaults** - fallback values defined by each handler. This means the CLI can be driven entirely by a config file or environment variables without any user interaction, which is essential for reproducible @@ -160,9 +160,9 @@ is tested in isolation with mocks provided by For every test permutation, the install command *initiates a fresh project* from the Vortex template with a specific combination of user selections and runs assertions against the resulting files. Because a single template change can -affect a hundred plus installation permutations, snapshot testing makes it easy to -**review the impact of a change across all scenarios as diffs** — ensuring that -regressions are caught before they reach consumers. +affect a hundred plus installation permutations, snapshot testing makes it easy +to **review the impact of a change across all scenarios as diffs** - ensuring +that regressions are caught before they reach consumers. Each handler has a dedicated functional test class extending `AbstractHandlerProcessTestCase`, covering every aspect of the installation @@ -173,7 +173,7 @@ These tests use the [`alexskrypnyk/snapshot`](https://github.com/AlexSkrypnyk/sn library for snapshot-based testing. The snapshot system works on a **baseline + diff** pattern: -- `_baseline/` contains the *complete* reference installation — a full set of +- `_baseline/` contains the *complete* reference installation - a full set of template files as they would appear after a default installation. - Each scenario directory (e.g., `services_no_clamav/`, `hosting_acquia/`, `deploy_types_all_circleci/`) contains only the *delta* changes from the @@ -190,7 +190,7 @@ cover scenarios like: - **CI providers**: GitHub Actions, CircleCI - **Hosting**: Acquia, Lagoon - **Services**: Solr, Redis, ClamAV (enabled/disabled combinations) -- **Deployment**: artifact, container image, webhook, Lagoon, all combined +- **Deployment**: artifact, webhook, Lagoon, all combined - **Database sources**: Acquia, Lagoon, FTP, S3, URL, container registry - And many more permutations @@ -218,7 +218,7 @@ Fixture files should never be modified directly. When the template or CLI logic changes, update the snapshots: ```shell -# From .vortex/ directory (recommended). +cd .vortex ahoy update-snapshots # Manual (for debugging specific scenarios). @@ -232,6 +232,17 @@ fixture files to match the current output. The resulting changes appear as diffs in version control, making it straightforward to review exactly how a template or CLI change affects each scenario. +:::warning + +Commit your template and CLI changes **before** running +`ahoy update-snapshots` - the snapshots regenerate from the committed baseline, +so uncommitted changes are not picked up. `ahoy update-snapshots` is the only +sanctioned way to regenerate fixtures: invoking the test suite with +`UPDATE_SNAPSHOTS` by hand bypasses part of the workflow and produces partial, +inconsistent fixtures. + +::: + ## Releasing The CLI is packaged as a PHAR archive using diff --git a/.vortex/docs/content/contributing/maintenance/documentation.mdx b/.vortex/docs/content/contributing/maintenance/documentation.mdx index d4bc9c5e7..b746e5ebb 100644 --- a/.vortex/docs/content/contributing/maintenance/documentation.mdx +++ b/.vortex/docs/content/contributing/maintenance/documentation.mdx @@ -1,40 +1,48 @@ --- -sidebar_position: 4 +sidebar_position: 5 --- # Documentation -There are 2 types of the documentation that **Vortex** provides: +There are 2 types of documentation that **Vortex** provides: -1. **Vortex template documentation** (this site) - Generic information on - **how** to perform operations, applicable to all projects built with Vortex. - Deployed to https://www.vortextemplate.com/docs/ -2. **Per-project documentation** - Project-specific information on **what** the - project does, distributed in the `docs/` directory when Vortex is installed. +1. **Vortex template documentation** (this site) - generic information on + **how** to perform operations, applicable to all projects built with + **Vortex**. Deployed to https://www.vortextemplate.com/docs/ +2. **Per-project documentation** - project-specific information on **what** + the project does, distributed in the `docs/` directory when **Vortex** is + installed. The key relationship: per-project documentation describes **what** (coding standards, testing requirements, release configuration) while referencing the -Vortex documentation for **how** to perform specific operations. +**Vortex** documentation for **how** to perform specific operations. ## www.vortextemplate.com This documentation provides generic "how-to" guides suitable for any project -using the Vortex template. It covers tooling, automation, and operations that -are common across all Vortex-based projects. +using the **Vortex** template. It covers tooling, automation, and operations +that are common across all **Vortex**-based projects. -The source is written in Markdown and located in -[`.vortex/docs`](https://github.com/drevops/vortex/blob/main/.vortex/docs) +The source is written in Markdown and located in the +[`.vortex/docs`](https://github.com/drevops/vortex/tree/main/.vortex/docs) directory. This is removed when you install **Vortex** for a consumer site. ### Local build +Run the docs commands from the `.vortex/` directory: + ```shell -cd .vortex/docs -ahoy build +cd .vortex + +# Start the local development server with live reload. +ahoy docs + +# Build the production site. The build fails on broken internal links. +ahoy build-docs ``` Parts of the documentation are generated automatically from the codebase. -To update it, run: +To update them, run: ```shell cd .vortex @@ -44,43 +52,53 @@ ahoy update-docs If you have the documentation site running locally, the content changes will be available immediately. -### Check spelling and links +### Lint, spellcheck and test ```shell cd .vortex + +# Lint the Markdown and check American English spelling. +ahoy lint-docs + +# Run the Jest component tests and the spellcheck. ahoy test-docs ``` -If required, add spelling exclusions to `.vortex/docs/cspell.json` -file. +If required, add spelling exclusions to the `.vortex/docs/cspell.json` file. + +Internal links are not checked by these commands - the Docusaurus build is the +link checker, failing on broken internal links (`ahoy build-docs`). + +### Documentation videos + +The 6 terminal demo videos embedded in the docs are regenerated with +`ahoy update-videos [names]` from `.vortex/` - see +[Installer > Installer video](installer.mdx#installer-video) for the pipeline. ### Publishing -An automated continuous integration build publishes this documentation. +Automated continuous integration builds publish this documentation: -- on tag, publishes to https://www.vortextemplate.com/docs/ -- on every commit to a branch that contains `release-docs` string publishes to https://www.vortextemplate.com/docs/ -- on every push to `develop`, publishes to development version of documentation https://vortex-docs.netlify.app -- on every push or tag, publishes to the temporary version of documentation with a link added to the PR as a comment. +- on every tag, to https://www.vortextemplate.com/docs/ +- on every commit to a branch whose name contains `release-docs`, to https://www.vortextemplate.com/docs/ +- on every push to `main`, to the development version at https://vortex-docs.netlify.app +- for pull request branches whose test workflows pass, to a temporary preview, with the link posted to the PR as a comment ## Consumer site documentation -**Vortex** provides a scaffold of the consumer site documentation in the -[`docs`](https://github.com/drevops/vortex/blob/main/docs) directory. - -After **Vortex** is installed into the consumer site, these docs are -intended to be used by the site maintainers and stay up-to-date with the -project changes. +**Vortex** ships a scaffold of consumer site documentation in the +[`docs`](https://github.com/drevops/vortex/tree/main/docs) directory. It is +installed into every consumer site, where the site maintainers keep it +up-to-date with their project. -This documentation describes the **what** for the project: +The scaffold covers the **what** of a project: - Coding standards and agreements specific to the project - Testing requirements and configuration - Release and deployment configuration - Project-specific procedures and decisions -When describing **how** to perform an operation, per-project documentation -should reference the relevant page on https://www.vortextemplate.com/docs/ -rather than duplicating instructions. This keeps project docs focused on -decisions and configuration while leveraging the maintained Vortex -documentation for operational details. +The scaffold references this site for the **how** of each operation instead of +duplicating instructions - so when maintaining the template, keep the scaffold +pointing at the right pages here, and keep the **how** content on this site +rather than in the shipped `docs/` files. diff --git a/.vortex/docs/content/contributing/maintenance/release.mdx b/.vortex/docs/content/contributing/maintenance/release.mdx index b616f22c3..033a7ea73 100644 --- a/.vortex/docs/content/contributing/maintenance/release.mdx +++ b/.vortex/docs/content/contributing/maintenance/release.mdx @@ -1,12 +1,16 @@ --- -sidebar_position: 5 +sidebar_position: 6 --- # Release -## Versioning Strategy +This page covers releasing the **Vortex** template itself. (How a project +_built from_ **Vortex** releases is documented in +[Releasing](../../releasing/README.mdx).) -Vortex uses [Semantic Versioning](https://semver.org/) for releases: `MAJOR.MINOR.PATCH` +## Versioning strategy + +**Vortex** uses [Semantic Versioning](https://semver.org/) for releases: `MAJOR.MINOR.PATCH` ### Semantic version (MAJOR.MINOR.PATCH) @@ -56,9 +60,9 @@ When `main` advances to a new major version: :::note -Vortex uses **GitHub Flow** with the `main` branch always containing the latest major version. +**Vortex** uses **GitHub Flow** with the `main` branch always containing the latest major version. -Consumer projects created from Vortex should continue to use Git Flow. +Consumer projects created from **Vortex** should continue to use [Git Flow](../../releasing/gitflow.mdx). ::: @@ -73,12 +77,12 @@ The following rules apply to every release: 1. **Vortex is always tagged.** A release always creates the **Vortex** tag, whether or not the tooling changed. 2. **The tooling is always referenced by a tag.** The `drevops/vortex-tooling` requirement in **Vortex**'s root `composer.json` must point at a published tag - never a branch or a development alias. -3. **The tooling is tagged before Vortex when it changed.** When `.vortex/tooling/` has changed since the previous **Vortex** release, create and publish the new `drevops/vortex-tooling` tag *before* tagging **Vortex**. -4. **Vortex caps its tooling requirement at the tagged version.** When the tooling is tagged for a release, require that freshly tagged `drevops/vortex-tooling` version in root `composer.json` with a tilde constraint (for example `~1.3.0`). This makes the freshly tagged version the upper boundary of the requirement at the minor level: later patch releases within the same minor are still accepted, but the requirement cannot float onto a newer minor that was never published with the release. +3. **The tooling is tagged before Vortex when it changed.** When `.vortex/tooling/` has changed since the previous **Vortex** release, create and publish the new `drevops/vortex-tooling` tag _before_ tagging **Vortex**. +4. **Vortex pins its tooling requirement to the tagged version's minor.** When the tooling is tagged for a release, require that freshly tagged `drevops/vortex-tooling` version in root `composer.json` with a tilde constraint (for example `~1.3.0`). The tagged version is the lower bound and the next minor is the exclusive upper bound: later patch releases within the same minor are still accepted, but the requirement cannot float onto a newer minor that was never published with the release. -## Release Process +## Release process -Follow the steps below to release a new version of the Vortex: +Follow the steps below to release a new version of **Vortex**: 1. Run renovate bot locally to update all dependencies outside of the schedule: @@ -95,9 +99,10 @@ Follow the steps below to release a new version of the Vortex: 7. Increment minor version of all packages in `composer.json`. Run `composer update -W && composer bump`. When the tooling was tagged in the previous step, require that freshly tagged `drevops/vortex-tooling` version with a tilde constraint (for example `~1.3.0`, see [Tagging](#tagging)); otherwise keep the existing tag constraint. 8. Update minor version of dependencies in theme's `package.json`. 9. Increment the cache version in `.circleci/config.yml` and `.github/workflows/build-test-deploy.yml`. -10. Updated documentation with `cd .vortex && ahoy update-docs`. -11. Update the install demo video with `cd .vortex && ahoy update-videos cli-install`. -12. Tag the **Vortex** release and publish the GitHub release using the template below. **Vortex** is always tagged; when the tooling changed, its tag is created first (see [Tagging](#tagging)). +10. Update the documentation with `cd .vortex && ahoy update-docs`. +11. Commit the changes, then regenerate the CLI snapshots with `cd .vortex && ahoy update-snapshots` and commit the regenerated fixtures. +12. Update the install demo video with `cd .vortex && ahoy update-videos cli-install` and commit the output. +13. Tag the **Vortex** release and publish the GitHub release using the template below. **Vortex** is always tagged; when the tooling changed, its tag is created first (see [Tagging](#tagging)). import CodeBlock from '@theme/CodeBlock'; import ReleaseTemplate from '!!raw-loader!./_release_template.md' diff --git a/.vortex/docs/content/contributing/maintenance/script-boilerplate.sh b/.vortex/docs/content/contributing/maintenance/script-boilerplate.sh index 036dfae4c..e3e3ff270 100755 --- a/.vortex/docs/content/contributing/maintenance/script-boilerplate.sh +++ b/.vortex/docs/content/contributing/maintenance/script-boilerplate.sh @@ -15,8 +15,9 @@ VORTEX_EXAMPLE_URL="${VORTEX_EXAMPLE_URL:-http://example.com}" # ------------------------------------------------------------------------------ # @formatter:off -info() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[34m[INFO] %s\033[0m\n" "${1}" || printf "[INFO] %s\n" "${1}"; } +info() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[36m[INFO] %s\033[0m\n" "${1}" || printf "[INFO] %s\n" "${1}"; } note() { printf " %s\n" "${1}"; } +task() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[34m[TASK] %s\033[0m\n" "${1}" || printf "[TASK] %s\n" "${1}"; } pass() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[32m[ OK ] %s\033[0m\n" "${1}" || printf "[ OK ] %s\n" "${1}"; } fail() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[31m[FAIL] %s\033[0m\n" "${1}" || printf "[FAIL] %s\n" "${1}"; exit "${2:-1}"; } # @formatter:on @@ -26,7 +27,14 @@ info "Started Vortex operations." [ -z "${VORTEX_EXAMPLE_URL}" ] && fail "Missing required value for VORTEX_EXAMPLE_URL." command -v curl >/dev/null || fail "curl command is not available." -# Example of the script body. -curl -L -s -o /dev/null -w "%{http_code}" "${VORTEX_EXAMPLE_URL}" | grep -q '200\|403' && note "Requested example page." +# Example of the script body. Every task is closed by a pass or a fail. The +# assignment is guarded so a curl transport error reports through fail instead +# of aborting the script via 'set -e' before fail can run. +task "Requesting example page." +if ! status="$(curl -L -s -o /dev/null -w "%{http_code}" "${VORTEX_EXAMPLE_URL}")"; then + fail "Unable to reach ${VORTEX_EXAMPLE_URL}." +fi +echo "${status}" | grep -q '200\|403' || fail "Example page returned status ${status}." +pass "Requested example page." pass "Finished Vortex operations." diff --git a/.vortex/docs/content/contributing/maintenance/template.mdx b/.vortex/docs/content/contributing/maintenance/template.mdx index 0f14e39a3..02fbff04e 100644 --- a/.vortex/docs/content/contributing/maintenance/template.mdx +++ b/.vortex/docs/content/contributing/maintenance/template.mdx @@ -9,12 +9,9 @@ testing the template functionality. ## Authoring scripts -:::info - -Heads up! Scripts are changing from Bash to PHP in `2.0` release. Track the progress -in [this issue](https://github.com/drevops/vortex/issues/1192). - -::: +These requirements apply to the shell scripts in the template: the `scripts/` +provision subscripts and the maintenance scripts. The shipped tooling scripts +are PHP and follow their own conventions - see [Tooling](tooling.mdx). ### Requirements @@ -55,20 +52,27 @@ Please refer to [RFC2119](https://www.ietf.org/rfc/rfc2119.txt) for meaning of w # ------------------------------------------------------------------------------ ``` -6. SHOULD include formatting helper functions. `fail` prints the message and - terminates the script with status `1`, or with the status passed as its - second argument. It is always the last statement of a failure branch - +6. SHOULD include formatting helper functions. `info` opens and closes an + operation, `task` announces work that is starting, `note` is a standalone + remark that starts no task, and `fail` prints the message and terminates + the script with status `1`, or with the status passed as its second + argument - it is always the last statement of a failure branch, and anything that explains the failure runs before it: ```shell # @formatter:off + info() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[36m[INFO] %s\033[0m\n" "${1}" || printf "[INFO] %s\n" "${1}"; } note() { printf " %s\n" "${1}"; } - info() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[34m[INFO] %s\033[0m\n" "${1}" || printf "[INFO] %s\n" "${1}"; } + task() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[34m[TASK] %s\033[0m\n" "${1}" || printf "[TASK] %s\n" "${1}"; } pass() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[32m[ OK ] %s\033[0m\n" "${1}" || printf "[ OK ] %s\n" "${1}"; } fail() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[31m[FAIL] %s\033[0m\n" "${1}" || printf "[FAIL] %s\n" "${1}"; exit "${2:-1}"; } # @formatter:on ``` + Every `task` MUST be closed by a `pass` or a `fail` - a task announces work + that is starting, so it always reports its outcome, even when the work + itself cannot fail. + 7. SHOULD include variable values checks with errors and early exit, i.e.: ```shell @@ -87,19 +91,19 @@ Please refer to [RFC2119](https://www.ietf.org/rfc/rfc2119.txt) for meaning of w info "Started GitHub notification for operation ${VORTEX_NOTIFY_EVENT}." ``` -10. MUST contain an `pass` message about the finish of the script body, e.g.: +10. MUST contain a `pass` message about the finish of the script body, e.g.: ```shell pass "Finished GitHub notification for operation ${VORTEX_NOTIFY_EVENT}." ``` -11. MUST use uppercase global variables +11. MUST use uppercase global variables. 12. MUST use lowercase local variables. 13. MUST use long options instead of short options for readability. I.e., `drush cache:rebuild` instead of `drush cr`. 14. MUST use `VORTEX_` prefix for variables, unless it is a known 3-rd party variable like `PACKAGE_TOKEN` or `COMPOSER`. -15. MUST use script-specific prefix. I.e., for `notify.sh`, the variable to skip - notifications should start with `VORTEX_NOTIFY_`. +15. MUST use a script-specific prefix. I.e., for `vortex-notify`, the variable + to skip notifications should start with `VORTEX_NOTIFY_`. 16. MAY rely on variables from the external scripts (not prefixed with a script-specific prefix), but MUST declare such variables in the header of the file. @@ -124,7 +128,7 @@ Follow these guidelines when creating or updating **Vortex** variables. 4. **Vortex** action-specific script variables MUST be scoped within their own script. For instance, the `VORTEX_PROVISION_OVERRIDE_DB` - variable in the `provision.sh`. + variable in the `vortex-provision` script. 5. Drupal-related variables SHOULD start with `DRUPAL_` and SHOULD have a module name added as a second prefix. This is to separate **Vortex**, @@ -156,8 +160,7 @@ issues at different levels of integration before they reach consumers. ### Unit testing -[Bats](https://github.com/bats-core/bats-core) is used to unit test the -shell scripts shipped via the +[PHPUnit](https://phpunit.de/) is used to unit test the scripts shipped via the [`drevops/vortex-tooling`](https://packagist.org/packages/drevops/vortex-tooling) Composer package (installed at `vendor/drevops/vortex-tooling/src/`). Each script is tested in isolation with external commands (like `drush`, `docker`, @@ -166,21 +169,10 @@ This allows us to verify that individual scripts handle environment variables, flags, and edge cases correctly without needing a running Drupal site or Docker containers. -The [`bats-helpers`](https://github.com/drevops/bats-helpers) library provides -a step-based testing approach with built-in mocking and assertions, making it -straightforward to define expected inputs and outputs for each script. - Unit tests execute in seconds, providing fast feedback during development. -There are 25+ test files covering deployment, database operations, -notifications, provisioning, and other automation scripts. - -:::info - -Scripts are transitioning from Bash to PHP in **Vortex** `2.0`, which will -make the use of BATS obsolete. Track the progress -in [this issue](https://github.com/drevops/vortex/issues/1192). - -::: +The test files in `.vortex/tooling/tests/Unit/` cover deployment, database +operations, notifications, provisioning, and the other automation scripts - +see [Tooling > Testing](tooling.mdx#testing) for how to run them. ### End-to-end testing @@ -206,13 +198,13 @@ library provides test helpers, traits, and assertions purpose-built for running shell commands and validating their output within PHPUnit tests. End-to-end tests take minutes to run because they operate on real containers -and a real Drupal site, but they provide the highest confidence that -everything works together correctly. +and a real Drupal site, and they verify that everything works together +correctly. ### Example site The [DrevOps website](https://github.com/drevops/website) is a real-world -production site built using **Vortex**. It serves as the ultimate validation +production site built using **Vortex**. It serves as the final validation ground: if **Vortex** works correctly, the website should continue to build, test, and deploy without issues after each upstream update. @@ -241,22 +233,24 @@ TEST_PACKAGE_TOKEN= TEST_VORTEX_CONTAINER_REGISTRY_USER= TE Functional tests rely on database fixtures - see the section below on [updating test assets](#updating-test-assets). -### Unit tests with BATS +### Unit tests for the tooling scripts -BATS unit tests for shipped shell scripts live in the -[`drevops/vortex-tooling`](https://packagist.org/packages/drevops/vortex-tooling) -Composer package. +PHPUnit unit tests for the shipped scripts live in +`.vortex/tooling/tests/Unit/`. Run them from `.vortex/`: ```shell -cd .vortex/tooling +cd .vortex -# Install npm dependencies. -yarn install +# Run all tooling tests. +ahoy test-tooling -# Run a single test. -node_modules/.bin/bats .vortex/tooling/tests/unit/deploy.bats +# Run a single test class. +ahoy test-tooling -- --filter=DeployLagoonTest ``` +➡️ See [Tooling > Testing](tooling.mdx#testing) for the package's full test +layers. + ## Updating test assets There are *demo* and *test* database dumps captured as *files* and *container @@ -275,10 +269,10 @@ Without arguments, runs `all` for a full refresh. Default tag is `latest`. | --- | --- | --- | | `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. | +| `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` | 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. | +| `all` | Optimized 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 diff --git a/.vortex/docs/content/contributing/maintenance/tooling.mdx b/.vortex/docs/content/contributing/maintenance/tooling.mdx new file mode 100644 index 000000000..3fc63c5d7 --- /dev/null +++ b/.vortex/docs/content/contributing/maintenance/tooling.mdx @@ -0,0 +1,91 @@ +--- +sidebar_label: Tooling +sidebar_position: 4 +--- + +# Tooling + +The operational scripts that consumer projects run - build, provision, +database, deployment and notification commands - live in `.vortex/tooling/` +and are published as the standalone +[`drevops/vortex-tooling`](https://packagist.org/packages/drevops/vortex-tooling) +Composer package. Consumer projects install the package and run the scripts +from `vendor/bin/vortex-*`. + +## Package layout + +| Path | Purpose | +|------|---------| +| `src/` | The shipped scripts - the only directory in the published archive | +| `tests/Unit/` | PHPUnit unit tests covering the shipped scripts | +| `playground/` | Manual scripts that hit live services (Slack, JIRA, New Relic); not automated | + +`tests/` and `playground/` are stripped from the published archive via +`.gitattributes` `export-ignore`. + +While developing **Vortex** itself, the template's root `composer.json` +resolves the package from the in-tree copy through a `path` repository with a +pinned version; the CLI install command strips that entry so consumer sites +resolve the published package from Packagist. + +## Script conventions + +The shipped scripts are PHP and use PHP internals wherever possible, so a +consumer site needs no extra host binaries. Every script follows the same +pattern: a `helpers.php` include, variables read through `getenv_default()` and +`getenv_required()` with their fallback chains, and the shared output helpers. + +The output helpers have a strict contract: + +- `INFO()` opens and closes an operation. +- `TASK()` announces work that is starting and reports its outcome. It takes + both the starting and the finishing message, so a task always closes. +- `NOTE()` is a standalone remark that starts no task. +- `FAIL()` reports the failure and aborts. + +```php +TASK('Disabling Search API Solr server.', 'Disabled Search API Solr server.', function (): void { + drush('search-api:server-disable solr'); +}); +``` + +Consumer projects that need to alter a shipped script do so with a +`cweagans/composer-patches` patch, never by overriding it at runtime. + +## Testing + +The scripts are tested at 3 levels: + +- **Unit** - PHPUnit tests in `tooling/tests/Unit/` cover the shipped scripts + with external commands mocked. Run them from `.vortex/`: + + ```shell + cd .vortex + ahoy test-tooling + + # Run a single test class. + ahoy test-tooling -- --filter=DeployLagoonTest + ``` + +- **Integration** - the scripts are exercised end-to-end by the template's + PHPUnit functional tests in `.vortex/tests/` - see + [Template > Testing](template.mdx#testing). +- **Manual** - `tooling/playground/` holds scripts that hit live services. + They are not automated; see `tooling/playground/README.md`. + +## Publishing + +The `Vortex - Publish tooling` workflow +(`.github/workflows/vortex-publish-tooling.yml`) mirrors `.vortex/tooling/` to +the [`drevops/vortex-tooling`](https://github.com/drevops/vortex-tooling) +repository on every push to `main`, `2.x`, and any branch whose name contains +`vortex-tooling`, matching the source branch name. The published commit subject +matches the source commit subject, and the body records provenance. + +The package version is injected at publish time - never hardcode a `version` +in the package `composer.json`. + +Releases of the package are tagged on the mirror repository. When the tooling +changed since the last release, it is tagged **before** the **Vortex** release, +so the template can require the new tag - see +[Release > Tagging](release.mdx#tagging). diff --git a/.vortex/docs/content/contributing/roadmap.mdx b/.vortex/docs/content/contributing/roadmap.mdx index 74bb3948f..80e3eba2f 100644 --- a/.vortex/docs/content/contributing/roadmap.mdx +++ b/.vortex/docs/content/contributing/roadmap.mdx @@ -16,9 +16,8 @@ bug fixes required. ## Current goals -1. Improving installation to use `composer create-project`. -2. [Support for DDEV](https://github.com/drevops/vortex/issues/949) -3. [Support for Lando](https://github.com/drevops/vortex/issues/815) +1. [Support for DDEV](https://github.com/drevops/vortex/issues/949) +2. [Support for Lando](https://github.com/drevops/vortex/issues/815) ## Future goals diff --git a/.vortex/docs/content/deployment/README.mdx b/.vortex/docs/content/deployment/README.mdx index dc347b2f4..153990860 100644 --- a/.vortex/docs/content/deployment/README.mdx +++ b/.vortex/docs/content/deployment/README.mdx @@ -9,16 +9,22 @@ Deployment to a remote location is performed by the `deploy` _router_ script shipped via the [`drevops/vortex-tooling`](https://packagist.org/packages/drevops/vortex-tooling) Composer package and installed at `vendor/drevops/vortex-tooling/src/vortex-deploy`. -The script runs in continuous integration pipeline `deploy` job only after all -tests pass. +The script runs in the continuous integration pipeline's `deploy` job after the +`build` and `lint` jobs pass. (On GitHub Actions, a +[manual deployment](../continuous-integration/github-actions.mdx#manual-deployment) +can also run the `deploy` job on its own.) The script deploys the code to a remote location by calling the -relevant scripts based on the type of deployment defined in `$VORTEX_DEPLOY_TYPES` +relevant scripts based on the type of deployment defined in the `$VORTEX_DEPLOY_TYPES` variable as a comma-separated list of one or multiple supported deployment types: -- [`webhook`](webhook) - A webhook URL is called via CURL. -- [`artifact`](artifact) - A code artifact is created and sent to a remote repository. -- [`lagoon`](lagoon) - A Lagoon webhook URL is called via CURL to trigger a deployment. +- [`webhook`](webhook.mdx) - A webhook URL is called via CURL. +- [`artifact`](artifact.mdx) - A code artifact is created and sent to a remote repository. +- [`lagoon`](lagoon.mdx) - The Lagoon CLI is used to trigger a deployment on the Lagoon platform. + +Once the code lands on the hosting platform, the platform provisions the site +and sends [notifications](notifications.mdx) - that part runs on the hosting +side, not in the `deploy` job.
Expand to see deployment in the complete code lifecycle @@ -45,46 +51,49 @@ branches: - `hotfix/**` - `project/**` -This will run the deployment script to forward the code to the remote hosting +This runs the deployment script to forward the code to the remote hosting platform and tell it to use the current branch's code for the deployment. When a pull request is raised against one of the branches above, a deployment -will run for the pull request's branch. Depending on the hosting platform, this -may create a temporary environment for the pull request with the name of the -pull request. - -Note that `feature/*` or `bugfix/*` (or any other branches) branches are not -included in the default list of deployment targets. This is to prevent -deployments from branches that are typically short-lived and have a pull -request raised, which will trigger a deployment. Adding these branches to the -list of deployment targets will lead to duplicated CI runs and deployments. - -`project/*` branches are long-lived branches that are typically used for -larger features or projects that may span multiple pull requests. Such branches -would be manually synced with the `develop` branch and deployed to a remote -environment for testing. An example of this would be a migration branch that -has migration code and configuration that is not yet ready to be merged into -`develop` but needs to be deployed to a remote environment for testing. +runs for the pull request's branch. Depending on the hosting platform, this +may create a temporary environment named after the pull request. + +Short-lived `feature/*` and `bugfix/*` branches deploy through their pull +requests rather than through direct pushes - deploying both the push and the +PR would double every CI run and deployment. Branches created by automated +dependency updates (`deps/*`) never deploy. + +`project/*` branches are long-lived branches typically used for larger +features or projects spanning multiple pull requests - for example, a +migration branch whose code and configuration are not ready to merge into +`develop` but need a remote environment for testing. Such branches are +manually synced with `develop` and deploy on direct pushes. + +The exact patterns differ slightly per provider - see +[GitHub Actions > Update trigger branches](../continuous-integration/github-actions.mdx#update-trigger-branches) +and [CircleCI > Update deployment branches](../continuous-integration/circleci.mdx#update-deployment-branches). ### Deployment action -By default, an existing database will be retained during a deployment. +By default, an existing database is retained during a deployment. + +The `$VORTEX_DEPLOY_ACTION` variable changes what a deployment does: -To change this behavior and overwrite the database with a fresh copy from -production environment, set the `$VORTEX_DEPLOY_ACTION` variable to -`deploy_override_db`. +- `deploy` (default) - deploy the code and preserve the environment's database. +- `deploy_override_db` - deploy the code and overwrite the database with a fresh copy from the production environment. +- `destroy` - remove the deployed environment, on platforms that support it (Lagoon). ## Skipping deployments You can skip all deployments by setting the `VORTEX_DEPLOY_SKIP` environment variable to `1`. -This is especially useful in continuous integration pipelines where you may want to build and test -without triggering a deployment. +This is useful when you want the continuous integration pipeline to build and +test without triggering a deployment. ### Skipping deployments for specific pull requests or branches -To skip specific Pull Requests or branches, set `$VORTEX_DEPLOY_ALLOW_SKIP` to `1` +To skip specific pull requests or branches, set `$VORTEX_DEPLOY_ALLOW_SKIP` to `1` and provide lists in the following variables: #### Skipping specific pull requests @@ -161,7 +170,7 @@ evaluated first and always wins, so a pull request listed in | Topic | Description | |-------|-------------| -| [Webhook](webhook) | Deploy by calling a webhook URL | -| [Artifact](artifact) | Deploy code artifacts to remote repositories | -| [Lagoon](lagoon) | Deploy to Lagoon hosting platform | -| [Notifications](notifications) | Send deployment notifications | +| [Webhook](webhook.mdx) | Deploy by calling a webhook URL | +| [Artifact](artifact.mdx) | Deploy code artifacts to remote repositories | +| [Lagoon](lagoon.mdx) | Deploy to the Lagoon hosting platform | +| [Notifications](notifications.mdx) | Send deployment notifications | diff --git a/.vortex/docs/content/deployment/artifact.mdx b/.vortex/docs/content/deployment/artifact.mdx index 859f18c0e..3ab6c63e4 100644 --- a/.vortex/docs/content/deployment/artifact.mdx +++ b/.vortex/docs/content/deployment/artifact.mdx @@ -13,7 +13,7 @@ require pre-built code artifacts. When `artifact` is included in `$VORTEX_DEPLOY_TYPES`, the deployment script: -1. Creates a clean code artifact using [Git Artifact](/docs/tools/git-artifact) +1. Creates a clean code artifact using [Git Artifact](../tools/git-artifact.mdx) 2. Applies the `.gitignore.artifact` rules to control which files are included 3. Pushes the artifact to the configured remote repository 4. The hosting platform then deploys from that repository @@ -24,13 +24,17 @@ When `artifact` is included in `$VORTEX_DEPLOY_TYPES`, the deployment script: | Variable | Required | Default | Location | Description | |----------|----------|---------|----------|-------------| -| `VORTEX_DEPLOY_ARTIFACT_GIT_REMOTE` | **Yes** | | `.env` | Remote repository URL for the artifact | -| `VORTEX_DEPLOY_ARTIFACT_ROOT` | No | Current directory | `.env` | Root directory for artifact creation | -| `VORTEX_DEPLOY_ARTIFACT_SRC` | No | Current directory | `.env` | Source directory to package | -| `VORTEX_DEPLOY_ARTIFACT_DST` | No | `.artifact` | `.env` | Destination directory for artifact | -| `VORTEX_DEPLOY_ARTIFACT_GIT_USER_NAME` | **Yes** | | CI | Git user name for commits | -| `VORTEX_DEPLOY_ARTIFACT_GIT_USER_EMAIL` | **Yes** | | CI | Git user email for commits | -| `VORTEX_DEPLOY_ARTIFACT_LOG` | No | | `.env` | Log file path | +| `VORTEX_DEPLOY_ARTIFACT_GIT_REMOTE` | **Yes** | | CI or `.env` | Remote repository URL for the artifact | +| `VORTEX_DEPLOY_ARTIFACT_GIT_USER_EMAIL` | **Yes** | | CI | Email address of the user committing to the remote repository | +| `VORTEX_DEPLOY_ARTIFACT_GIT_USER_NAME` | No | `Deployment Robot` | CI | Name of the user committing to the remote repository | +| `VORTEX_DEPLOY_ARTIFACT_DST_BRANCH` | No | `[branch]` | `.env` | Remote branch to push to; supports [tokens](https://github.com/drevops/git-artifact#token-support) | +| `VORTEX_DEPLOY_ARTIFACT_ROOT` | No | Current directory | CI | Root directory the deployment script runs from | +| `VORTEX_DEPLOY_ARTIFACT_SRC` | No | | CI | Source directory with the built code to package | +| `VORTEX_DEPLOY_ARTIFACT_LOG` | No | `/deployment_log.txt` | `.env` | Deployment log file path | + +The SSH key selection, the pinned `git-artifact` version, and the optional +stale-branch cleanup are covered in [Git Artifact](../tools/git-artifact.mdx); +the full variable list is in the [Variables reference](../development/variables.mdx). ### Setup @@ -46,11 +50,12 @@ When `artifact` is included in `$VORTEX_DEPLOY_TYPES`, the deployment script: VORTEX_DEPLOY_ARTIFACT_GIT_REMOTE=git@github.com:your-org/your-project-artifact.git ``` -3. Add Git user credentials to your CI provider's environment variables: +3. Add the Git user email to your CI provider's environment variables (and + optionally a user name to replace the default `Deployment Robot`): ```shell - VORTEX_DEPLOY_ARTIFACT_GIT_USER_NAME="Deployment Bot" VORTEX_DEPLOY_ARTIFACT_GIT_USER_EMAIL="deploy@example.com" + VORTEX_DEPLOY_ARTIFACT_GIT_USER_NAME="Deployment Bot" ``` ## Artifact file control @@ -95,10 +100,9 @@ the artifact, or to re-include something that a broader rule excludes: ## Use cases - **Acquia hosting** - Acquia requires code artifacts pushed to their Git repository -- **Pantheon hosting** - Similar artifact-based deployment model -- **Custom hosting** - Any platform that deploys from a Git repository +- **Other Git-based hosting** - any platform that deploys from a Git repository it controls ## See also -- [Git Artifact tool](/docs/tools/git-artifact) - Detailed documentation on the artifact tool -- [Acquia hosting](/docs/hosting/acquia) - Acquia-specific deployment configuration +- [Git Artifact tool](../tools/git-artifact.mdx) - detailed documentation on the artifact tool +- [Acquia hosting](../hosting/acquia.mdx) - Acquia platform integration diff --git a/.vortex/docs/content/deployment/lagoon.mdx b/.vortex/docs/content/deployment/lagoon.mdx index 65457306e..0290612c0 100644 --- a/.vortex/docs/content/deployment/lagoon.mdx +++ b/.vortex/docs/content/deployment/lagoon.mdx @@ -6,31 +6,42 @@ sidebar_position: 4 # Lagoon deployment Lagoon deployment triggers a deployment on the [Lagoon](https://lagoon.sh/) -hosting platform using the -[Lagoon CLI](https://github.com/uselagoon/lagoon-cli). +hosting platform using the [Lagoon CLI](https://github.com/uselagoon/lagoon-cli). ## How it works When `lagoon` is included in `$VORTEX_DEPLOY_TYPES`, the deployment script -uses the Lagoon CLI to trigger a deployment. Lagoon then: +downloads the Lagoon CLI, points it at the configured Lagoon instance, and +requests a deployment for the current branch or pull request over SSH. Lagoon +then: 1. Pulls the latest code from your repository 2. Builds container images using your `docker-compose.yml` 3. Deploys containers to Kubernetes 4. Runs post-rollout tasks defined in `.lagoon.yml` +Tag deployments are not supported by Lagoon - the script reports this and +skips the deployment. + ## Configuration ### Environment variables | Variable | Required | Default | Location | Description | |----------|----------|---------|----------|-------------| -| `LAGOON_PROJECT` | **Yes** | | `.env` | Your Lagoon project name | +| `VORTEX_DEPLOY_LAGOON_PROJECT` | **Yes** | Value of `LAGOON_PROJECT` | `.env` | Your Lagoon project name | | `VORTEX_DEPLOY_LAGOON_INSTANCE` | No | `amazeeio` | `.env` | Lagoon instance name | -| `VORTEX_DEPLOY_LAGOON_INSTANCE_GRAPHQL` | No | Auto-generated | `.env` | Lagoon GraphQL endpoint | -| `VORTEX_DEPLOY_LAGOON_INSTANCE_HOSTNAME` | No | Auto-generated | `.env` | Lagoon SSH hostname | -| `VORTEX_DEPLOY_LAGOON_INSTANCE_PORT` | No | Auto-generated | `.env` | Lagoon SSH port | -| `VORTEX_DEPLOY_LAGOON_BRANCH` | No | Current branch | CI | Branch to deploy | +| `VORTEX_DEPLOY_LAGOON_INSTANCE_GRAPHQL` | No | `https://api.lagoon.amazeeio.cloud/graphql` | `.env` | Lagoon GraphQL endpoint | +| `VORTEX_DEPLOY_LAGOON_INSTANCE_HOSTNAME` | No | `ssh.lagoon.amazeeio.cloud` | `.env` | Lagoon SSH hostname | +| `VORTEX_DEPLOY_LAGOON_INSTANCE_PORT` | No | `32222` | `.env` | Lagoon SSH port | +| `VORTEX_DEPLOY_LAGOON_BRANCH` | No | Value of `VORTEX_DEPLOY_BRANCH` | CI | Branch to deploy | +| `VORTEX_DEPLOY_LAGOON_SSH_FINGERPRINT` | No | Value of `VORTEX_DEPLOY_SSH_FINGERPRINT` | CI | Fingerprint of the SSH key used to authenticate | +| `VORTEX_DEPLOY_LAGOON_LAGOONCLI_VERSION` | No | `v0.32.0` | CI | Lagoon CLI version to install | +| `VORTEX_DEPLOY_LAGOON_FAIL_ENV_LIMIT_EXCEEDED` | No | `0` | CI | Fail the build when the Lagoon environment limit is exceeded (`1`) or pass it (`0`) | + +The full list of `VORTEX_DEPLOY_LAGOON_*` variables, including the pull request +context variables set by CI, is in the +[Variables reference](../development/variables.mdx). ### Setup @@ -53,27 +64,24 @@ uses the Lagoon CLI to trigger a deployment. Lagoon then: ## Post-deployment automation -When code is deployed, Vortex automatically: - -1. **Provisions the site** - Runs database updates, imports configuration, clears caches -2. **Sends notifications** - Notifies configured channels about the deployment - -This is implemented using post-rollout tasks defined in the +Once Lagoon rolls out the new containers, the post-rollout tasks defined in [`.lagoon.yml`](https://github.com/drevops/vortex/blob/main/.lagoon.yml) -configuration file. +provision the site and send notifications - see +[Lagoon hosting](../hosting/lagoon.mdx#deployment-automation) for what runs. -## Environment types +## Environments -Lagoon creates different environment types based on your branch: +The shipped [`.lagoon.yml`](https://github.com/drevops/vortex/blob/main/.lagoon.yml) +defines environment-specific settings (cron jobs, routes) for the `main` and +`develop` branches, and treats an environment as production when Lagoon marks +it as such or when the branch matches `VORTEX_LAGOON_PRODUCTION_BRANCH` +(default: `main`). -| Branch pattern | Environment type | -|---------------|------------------| -| `main`, `master` | Production | -| `develop` | Development | -| `release/*`, `hotfix/*` | Staging | -| Pull requests | Development (ephemeral) | +Which branches get an environment, their types, and pull request (ephemeral) +environments are configured per project in Lagoon itself - see +[Lagoon environment types](https://docs.lagoon.sh/concepts-advanced/environment-types/). ## See also -- [Lagoon hosting](/docs/hosting/lagoon) - Detailed Lagoon hosting configuration -- [Lagoon documentation](https://docs.lagoon.sh/) - Official Lagoon docs +- [Lagoon hosting](../hosting/lagoon.mdx) - platform integration, environment detection, and routine operations +- [Lagoon documentation](https://docs.lagoon.sh/) - official Lagoon docs diff --git a/.vortex/docs/content/deployment/notifications.mdx b/.vortex/docs/content/deployment/notifications.mdx index a6490a4fe..003c8e2bd 100644 --- a/.vortex/docs/content/deployment/notifications.mdx +++ b/.vortex/docs/content/deployment/notifications.mdx @@ -1,15 +1,16 @@ --- sidebar_label: Notifications -sidebar_position: 6 +sidebar_position: 5 --- # Notifications -**Vortex** provides a flexible notification system that sends updates -across multiple channels when deployments occur. +**Vortex** provides a notification system that sends updates across multiple +channels when deployments occur. -Notifications are triggered automatically during deployment to a -hosting environment. +Notifications are dispatched by the `vendor/bin/vortex-notify` router script, +which the _hosting platform_ runs around a deployment - from Lagoon +post-rollout tasks or Acquia Cloud Hooks - rather than by the CI `deploy` job. ## Channels @@ -25,7 +26,9 @@ Available channels: `diffy`, `email`, `github`, `jira`, `newrelic`, `slack`, `we ## Global environment variables These variables apply to all notification channels unless overridden by -channel-specific settings. +channel-specific settings. The full generated list of `VORTEX_NOTIFY_*` +variables with their source scripts is in the +[Variables reference](../development/variables.mdx). | Variable | Required | Default | Location | Description | |----------|----------|---------|----------|-------------| @@ -45,6 +48,9 @@ notification script. | `VORTEX_NOTIFY_SHA` | **Yes** | Git commit SHA | | `VORTEX_NOTIFY_PR_NUMBER` | No | Pull request number (empty for branch deployments) | | `VORTEX_NOTIFY_LABEL` | **Yes** | Human-readable deployment label | +| `VORTEX_NOTIFY_ENVIRONMENT_URL` | **Yes** | URL of the deployed environment, used by the `%environment_url%` token | +| `VORTEX_NOTIFY_LOGIN_URL` | No | Login URL for the `%login_url%` token; defaults to `/user/login` | +| `VORTEX_NOTIFY_EVENT` | No | Deployment event: `post_deployment` (default) or `pre_deployment`; any other value fails the run | ## Branch filtering diff --git a/.vortex/docs/content/deployment/webhook.mdx b/.vortex/docs/content/deployment/webhook.mdx index 29077c1e0..5b60381df 100644 --- a/.vortex/docs/content/deployment/webhook.mdx +++ b/.vortex/docs/content/deployment/webhook.mdx @@ -10,6 +10,15 @@ Webhook deployment triggers a remote deployment by calling an HTTP endpoint. This is useful for integrating with external systems or custom deployment pipelines that expose webhook URLs. +:::note + +This is a deployment _type_ - it triggers the deployment itself. To call a +webhook _about_ a deployment (for example, to tell a monitoring system one +happened), use the [webhook notification channel](notifications.mdx#webhook) +instead. + +::: + ## How it works When `webhook` is included in `$VORTEX_DEPLOY_TYPES`, the deployment script @@ -42,3 +51,8 @@ as parameters. - Triggering deployments on platforms that provide webhook endpoints - Integrating with custom deployment orchestration systems - Notifying external services when code is ready for deployment + +## See also + +- [Deployment overview](README.mdx) - the deployment router and skip rules +- [Notifications](notifications.mdx) - messages sent after a deployment lands diff --git a/.vortex/docs/content/development/README.mdx b/.vortex/docs/content/development/README.mdx index 930d785d1..39a4c31e5 100644 --- a/.vortex/docs/content/development/README.mdx +++ b/.vortex/docs/content/development/README.mdx @@ -32,13 +32,13 @@ import AsciinemaPlayer from '@site/src/components/AsciinemaPlayer'; ## Common commands The most common day-to-day commands. Each video is a live recording captured on -a fresh **Vortex** project; re-record them with `ahoy update-videos`. +a fresh **Vortex** project. ### Build the project -```shell title="Build" -ahoy build -``` +`ahoy build` rebuilds the whole stack from scratch: it recreates the +containers, installs the Composer and front-end dependencies, compiles the +theme assets, and provisions the site from the database dump. ```shell - ahoy restart + ahoy up ``` @@ -238,36 +237,9 @@ To update environment variables in your local development environment: -➡️ See [Variables](./variables.mdx) for comprehensive variable reference. - -## Performance optimization - - - - ```shell - # Enable CSS/JS aggregation - ahoy drush config:set system.performance css.preprocess 1 - ahoy drush config:set system.performance js.preprocess 1 - # Clear render cache - ahoy drush cache:rebuild-external - # Check database updates needed - ahoy drush updatedb:status - ``` - - - ```shell - # Enable CSS/JS aggregation - docker compose exec cli drush config:set system.performance css.preprocess 1 - docker compose exec cli drush config:set system.performance js.preprocess 1 - # Clear render cache - docker compose exec cli drush cache:rebuild-external - # Check database updates needed - docker compose exec cli drush updatedb:status - ``` - - +➡️ See [Variables](variables.mdx) for the full variable reference. -## Common issues & solutions +## Common issues and solutions ### Site not loading @@ -293,14 +265,14 @@ To update environment variables in your local development environment: ```shell - docker compose ps # Check if database container is running - ahoy reset # Nuclear option: rebuild everything + docker compose ps # Check if the database container is running + ahoy reset # Last resort: rebuild everything ``` ```shell - docker compose ps # Check if database container is running - docker compose down --volumes && docker compose up -d # Nuclear option + docker compose ps # Check if the database container is running + docker compose down --volumes && docker compose up -d # Last resort ``` @@ -312,39 +284,24 @@ To update environment variables in your local development environment: sudo chown -R $USER:$USER . ``` -### Memory issues during composer install +## Reading logs ```shell - # Increase PHP memory temporarily - ahoy composer install --no-dev --optimize-autoloader - ``` - - - ```shell - # Increase PHP memory temporarily - docker compose exec cli composer install --no-dev --optimize-autoloader - ``` - - - -## Log files - - - - ```shell - # View ahoy logs + # Show container logs ahoy logs - # Check container logs - docker compose logs --tail=50 cli + # Check recent logs of a single container + ahoy logs -- --tail=50 cli # View Drupal watchdog logs ahoy drush watchdog:show --count=20 ``` ```shell - # Check container logs + # Show container logs + docker compose logs + # Check recent logs of a single container docker compose logs --tail=50 cli # View Drupal watchdog logs docker compose exec cli drush watchdog:show --count=20 @@ -367,8 +324,13 @@ import CodeLifecycle from '../_code-lifecycle.mdx'; | Topic | Description | |-------|-------------| -| [Database](database) | Fetching, refreshing, and exporting databases | -| [Composer](composer) | Managing packages, patching, security auditing | -| [Debugging](debugging) | Xdebug, curl testing, container access | -| [PHPUnit](phpunit) | Unit, Kernel, and Functional testing | -| [Behat](behat) | Behavior-Driven Development (BDD) testing | +| [Database](database.mdx) | Fetching, refreshing, and exporting databases | +| [Composer](composer.mdx) | Managing packages, patching, security auditing | +| [Debugging](debugging.mdx) | Xdebug, curl testing, container access | +| [PHPUnit](phpunit.mdx) | Unit, kernel, and functional testing | +| [Behat](behat.mdx) | Behavior-driven (BDD) testing | +| [Jest](jest.mdx) | JavaScript unit testing | +| [Visual regression](visual-regression.mdx) | Diffy-powered visual regression on deployments | +| [AI](ai.mdx) | The AI agent configuration files | +| [FAQs](faqs.mdx) | Answers to common operational questions | +| [Variables](variables.mdx) | The full environment variable reference | diff --git a/.vortex/docs/content/development/ai.mdx b/.vortex/docs/content/development/ai.mdx index 1ea3d41b1..fefdb3062 100644 --- a/.vortex/docs/content/development/ai.mdx +++ b/.vortex/docs/content/development/ai.mdx @@ -1,6 +1,6 @@ --- sidebar_label: AI -sidebar_position: 7 +sidebar_position: 9 --- # AI @@ -18,13 +18,13 @@ throughout **Vortex**: agent reads all files in `docs/` to understand project-specific decisions such as testing conventions, CI configuration, deployment rules, and release processes. -- **How** to perform operations is sourced from the - https://www.vortextemplate.com/docs (this site). The - `AGENTS.md` file instructs agents to fetch operational guides from the - website and cache them locally in `.artifacts/`. +- **How** to perform operations is sourced from + [www.vortextemplate.com/docs](https://www.vortextemplate.com/docs) (this + site). The `AGENTS.md` file instructs agents to fetch operational guides + from the website and cache them locally in `.artifacts/`. This means AI agents receive the same layered guidance as human developers: -project-level decisions first, then Vortex-level procedures. +project-level decisions first, then **Vortex**-level procedures. ## Configuration files @@ -36,15 +36,16 @@ an agent-agnostic file that any AI tool can use. Key sections include: -- **Daily development tasks** — a reference of `ahoy` commands for building, +- **Daily development tasks** - a reference of `ahoy` commands for building, testing, and managing the local environment. -- **Critical rules** — constraints that agents must always follow (e.g., never +- **Critical rules** - constraints that agents must always follow (e.g., never modify `vendor/drevops/vortex-tooling/src/`, always export config after admin UI changes). -- **Key directories** — the project's directory layout. -- **Documentation** — instructions for agents to check `docs/` files for +- **Key directories** - the project's directory layout. +- **Documentation** - instructions for agents to check `docs/` files for project-specific decisions and to fetch operational documentation from - https://www.vortextemplate.com/docs. Fetched documentation is cached locally so - that agents can reuse it across sessions without repeated requests. + [www.vortextemplate.com/docs](https://www.vortextemplate.com/docs). Fetched + documentation is cached locally so that agents can reuse it across sessions + without repeated requests. ### `CLAUDE.md` diff --git a/.vortex/docs/content/development/behat.mdx b/.vortex/docs/content/development/behat.mdx index fe6e5fc76..fe2efa483 100644 --- a/.vortex/docs/content/development/behat.mdx +++ b/.vortex/docs/content/development/behat.mdx @@ -6,9 +6,9 @@ sidebar_position: 6 # Behat **Vortex** uses [Behat](https://behat.org) for Behavior-Driven Development (BDD) -testing. Behat allows to write human-readable stories that describe the behavior +testing. Behat lets you write human-readable stories that describe the behavior of the application. Behat tests primarily focus on critical user journeys, -serving as comprehensive end-to-end validations. +serving as end-to-end validations. **Vortex** provides full Behat support, including configuration in [`behat.yml`](https://github.com/drevops/vortex/blob/main/behat.yml) and a [browser container](https://github.com/drevops/vortex/blob/main/docker-compose.yml) to run tests interactively in a real browser with @@ -40,12 +40,13 @@ import TabItem from '@theme/TabItem'; ```shell - # Run all Behat tests - docker compose exec cli vendor/bin/behat + # Run all Behat tests. The `-d memory_limit=-1` flag lifts the PHP memory + # limit for long runs, matching what `ahoy test-bdd` does. + docker compose exec cli php -d memory_limit=-1 vendor/bin/behat # Run specific feature file - docker compose exec cli vendor/bin/behat tests/behat/features/homepage.feature + docker compose exec cli php -d memory_limit=-1 vendor/bin/behat tests/behat/features/homepage.feature # Run scenarios with specific tag - docker compose exec cli vendor/bin/behat -- --tags=@smoke + docker compose exec cli php -d memory_limit=-1 vendor/bin/behat --tags=@smoke ``` @@ -62,7 +63,7 @@ import TabItem from '@theme/TabItem'; ```shell # Generate step definitions reference - docker compose exec cli vendor/bin/behat -- --definitions=l + docker compose exec cli php -d memory_limit=-1 vendor/bin/behat --definitions=l ``` @@ -76,31 +77,37 @@ You can add your custom steps into this file. ## Profiles -Behat's `default` profile configured with sensible defaults to allow running it -with provided extensions. +Behat's `default` profile runs every scenario except those tagged `@skipped`. -In continuous integration environment, the profile can be overridden using \ -`$VORTEX_CI_BEHAT_PROFILE` environment variable. +In the continuous integration environment, the profile can be overridden using +the `$VORTEX_CI_BEHAT_PROFILE` environment variable. ## Parallel runs -In continuous integration pipeline, Behat tests can run within multiple runners -to increase the speed of the test suite. To achieve this, Behat tags are used to -mark features and scenarios with `@p*` tags. +In the continuous integration pipeline, Behat tests can run within multiple +runners to increase the speed of the test suite. To achieve this, Behat tags +are used to mark features and scenarios. + +Out of the box, **Vortex** provides support for unlimited parallel runners, but +only 2 parallel profiles, `p0` and `p1`: -Out of the box, **Vortex** provides support for unlimited parallel -runners, but only 2 parallel profiles `p0` and `p1`: a feature can be tagged by -either `@p0` or `@p1` to run in a dedicated runner, or with both tags to run in -both runners. +- `p0` (the first runner) is the catch-all: it runs every scenario **not** + tagged `@p1`, plus all `@smoke` scenarios. +- `p1` (the second runner) runs scenarios tagged `@p1`, plus all `@smoke` + scenarios. -Note that you can easily add more `p*` profiles in your `behat.yml` by copying -existing `p1` profile and changing several lines of configuration. +In practice: leave a feature untagged to run it on the first runner, tag it +`@p1` to move it to the second runner, or tag it `@smoke` to run it on every +runner. An untagged feature always lands on the first runner, so forgetting to +tag never orphans a test. -Features without `@p*` tags will always run in the first CI pipeline runner, so -even if you forget to tag the feature, it will still be allocated to a runner. +You can add more `p*` profiles in your `behat.yml` by copying the existing `p1` +profile and changing several lines of configuration. -If CI pipeline has only one runner - a `default` profile will be used and all tests -(except for those that tagged with `@skipped`) will be run. +If the pipeline has only one runner and `VORTEX_CI_BEHAT_PROFILE` is unset, +the `default` profile is used and all tests run there except those tagged +`@skipped`. An explicitly set `VORTEX_CI_BEHAT_PROFILE` stays active even on a +single runner. ## Skipping tests @@ -127,21 +134,22 @@ Set `animation.enabled` to `false` in `behat.yml` to disable animation everywher ## Format Out of the box, **Vortex** comes with [Behat Progress formatter](https://github.com/drevops/behat-format-progress-fail) -output formatter to show progress as TAP and failures inline. This allows to -continue test runs after a failure while maintaining a minimal output. +output formatter to show progress as TAP and failures inline. This lets a test +run continue after a failure while maintaining a minimal output. ## Reporting -Test reports are stored in `.logs/behat` directory. - -Continuous integration pipeline usually uses them to track test performance and -stability. +Behat writes test reports in JUnit format to the `.logs/test_results/behat` +directory. The continuous integration pipeline stores them as artifacts and +uses them to track test performance and stability. ## Boilerplate test features -**Vortex** provides BDD [tests boilerplate](https://github.com/drevops/vortex/blob/main/tests/behat/features) for [homepage](https://github.com/drevops/vortex/blob/main/tests/behat/features/homepage.feature) -and [login](https://github.com/drevops/vortex/blob/main/tests/behat/features/login.feature) -user journeys. +**Vortex** provides BDD [tests boilerplate](https://github.com/drevops/vortex/blob/main/tests/behat/features) +covering core user journeys ([homepage](https://github.com/drevops/vortex/blob/main/tests/behat/features/homepage.feature), +[login](https://github.com/drevops/vortex/blob/main/tests/behat/features/login.feature), search) +and the shipped module integrations (Redis, ClamAV, redirects, `robots.txt`, +XML sitemap, accessibility). These boilerplate tests run in continuous integration pipeline when you install **Vortex** and can be used as a starting point for writing your own. diff --git a/.vortex/docs/content/development/composer.mdx b/.vortex/docs/content/development/composer.mdx index 1c2d0b3d1..62b4caad7 100644 --- a/.vortex/docs/content/development/composer.mdx +++ b/.vortex/docs/content/development/composer.mdx @@ -5,6 +5,10 @@ sidebar_position: 3 # Composer packages +This page covers the day-to-day workflows: requiring, updating, patching and +auditing packages. For a key-by-key reference of the shipped `composer.json` +file, see [Drupal > composer.json](../drupal/composer-json.mdx). + import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; diff --git a/.vortex/docs/content/development/database.mdx b/.vortex/docs/content/development/database.mdx index 5dd62ff38..85de4c7f8 100644 --- a/.vortex/docs/content/development/database.mdx +++ b/.vortex/docs/content/development/database.mdx @@ -8,6 +8,15 @@ sidebar_position: 2 import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +The running site's database lives **inside the `database` container** - the +`.data/db.sql` file on the host is only a dump used for imports and exports. +Removing the container (for example, with `ahoy reset`) discards the live +database, and the next build re-creates it from the dump. + +Fetching and exporting run on the **host** (they move dump files around), while +refreshing runs inside the **containers** (it imports into the running +database) - the tabs below reflect that difference. + ## Fetching database To fetch the database with the latest data from the production environment, @@ -46,32 +55,46 @@ name it `db.sql`, and place it in the `.data` directory. ## Refreshing database -Import the database dump into the local environment and run all necessary updates. +There are 2 distinct ways to load the dump from `.data` into the running +database container - pick the one matching what you need. + +### Import and run updates + +`ahoy provision` imports the dump **and** applies all the provisioning steps: +database updates, configuration import, cache rebuilds and deploy hooks. Use +it any time you need the local environment reset to a fully updated state. ```shell - # Import database and run updates ahoy provision - # Import database without running updates - ahoy import-db ``` ```shell - # Import database and run updates docker compose exec cli ./vendor/bin/vortex-provision - # Import database without running updates - docker compose exec cli ./vendor/bin/vortex-import-db ``` -Run the provision command any time you need to reset the local environment to -use the fresh database dump stored in `.data`. +### Import only + +`ahoy import-db` imports the raw dump **without** running any updates, +configuration imports or scripts. Use it to quickly reset the database +contents to the state captured in the dump. -The import-db command is useful if you want to quickly reset the database -without applying any updates or changes. + + + ```shell + ahoy import-db + ``` + + + ```shell + docker compose exec cli ./vendor/bin/vortex-import-db + ``` + + ## Exporting database @@ -95,6 +118,10 @@ Export timestamped database dumps from the local environment. You can use these dumps to restore the local environment to a specific state: rename the dump file to `.data/db.sql` and run the import command. -Cache tables are exported with their structure but without their data, because Drupal rebuilds them on demand and their contents would only inflate the dump. Set `VORTEX_EXPORT_DB_FILE_STRUCTURE_TABLES` to a comma-separated list of table names, each of which may use the `*` wildcard, to choose which tables are exported this way. Set it to an empty value to export the data of every table. +Cache tables are exported with their structure but without their data, because +Drupal rebuilds them on demand and their contents would only inflate the dump. +Set `VORTEX_EXPORT_DB_FILE_STRUCTURE_TABLES` to a comma-separated list of table +names, each of which may use the `*` wildcard, to choose which tables are +exported this way. Set it to an empty value to export the data of every table. -➡️ See [Drupal > Provision](../drupal/provision) +➡️ See [Drupal > Provision](../drupal/provision.mdx) diff --git a/.vortex/docs/content/development/debugging.mdx b/.vortex/docs/content/development/debugging.mdx index 03f680c77..38d7eef40 100644 --- a/.vortex/docs/content/development/debugging.mdx +++ b/.vortex/docs/content/development/debugging.mdx @@ -5,6 +5,9 @@ sidebar_position: 4 # Debugging +This page covers 3 debugging aids: step-debugging PHP with Xdebug, testing +authenticated pages with curl, and inspecting the containers directly. + ## Xdebug https://xdebug.org/ @@ -15,10 +18,10 @@ https://xdebug.org/ **Vortex** comes with Xdebug pre-installed and configured for local development thanks to [Lagoon images](https://github.com/uselagoon/lagoon-images). -Xdebug is also configured to work in coverage mode, allowing to run tests with -code coverage enabled. +Xdebug is also configured to work in coverage mode, so tests can run with code +coverage enabled. -➡️ See [PHPUnit](phpunit) +➡️ See [PHPUnit](phpunit.mdx) ### Usage @@ -199,9 +202,9 @@ docker compose ps docker compose logs [service_name] # Examples: docker compose logs cli -docker compose logs mariadb +docker compose logs database # Access container shell docker compose exec cli bash -docker compose exec mariadb mysql -u drupal -p drupal +docker compose exec database mysql -u drupal -p drupal ``` diff --git a/.vortex/docs/content/development/faqs.mdx b/.vortex/docs/content/development/faqs.mdx index 032da5468..99f5117bb 100644 --- a/.vortex/docs/content/development/faqs.mdx +++ b/.vortex/docs/content/development/faqs.mdx @@ -45,7 +45,8 @@ ahoy mycommand -- myarg1 myarg2 --myoption1 --myoption2=myvalue ```shell - docker compose exec cli drush uli + # Unblocks user 1 and generates a one-time login link, like `ahoy login`. + docker compose exec cli ./vendor/bin/vortex-login ``` @@ -185,7 +186,7 @@ Provided that your stack is already running: -## How to just import a database? +## How to import a database without running updates? Provided that your stack is already running: @@ -208,7 +209,7 @@ Provided that your stack is already running: -## How to add Drupal modules +## How to add Drupal modules? @@ -223,38 +224,43 @@ Provided that your stack is already running: -## How to add patches for Drupal modules +## How to add patches for Drupal modules? -1. Add `title` to patch on https://drupal.org to the `patches` array in `extra` - section in `composer.json`. +1. Add the patch to the `patches` array in the `extra` section of + `composer.json`, with the patch title as the key: ```json "extra": { "patches": { "drupal/somepackage": { - "Remote patch description": "https://www.drupal.org/files/issues/issue.patch" + "Remote patch description": "https://www.drupal.org/files/issues/issue.patch", "Local patch description": "patches/package-description.patch" } } } ``` -2. Run +2. Regenerate the patches lock file and re-apply the patches: ```shell - ahoy composer require drupal/somepackage + ahoy composer patches-relock + ahoy composer patches-repatch + ahoy composer update --lock ``` ```shell - docker compose exec cli composer require drupal/somepackage + docker compose exec cli composer patches-relock + docker compose exec cli composer patches-repatch + docker compose exec cli composer update --lock ``` -➡️ See [Composer > Patching](composer.mdx#patching) +➡️ See [Composer > Patching](composer.mdx#patching) for the full patching +workflow, including removing patches. ## What should I do when `composer audit` reports a security vulnerability? diff --git a/.vortex/docs/content/development/jest.mdx b/.vortex/docs/content/development/jest.mdx index c02c46bc8..459ae14cb 100644 --- a/.vortex/docs/content/development/jest.mdx +++ b/.vortex/docs/content/development/jest.mdx @@ -1,6 +1,6 @@ --- sidebar_label: Jest -sidebar_position: 6 +sidebar_position: 7 --- # Jest @@ -81,7 +81,7 @@ Set globals in `beforeEach` and clean them up in `afterEach`: | Global | Setup | When needed | |--------|-------|-------------| -| `Drupal` | `global.Drupal = { behaviors: {} }` | Always — required by all Drupal behaviors | +| `Drupal` | `global.Drupal = { behaviors: {} }` | Always - required by all Drupal behaviors | | `jQuery` | `global.jQuery = require('jquery')` or a mock | When the source file uses `jQuery` or `$` | | `drupalSettings` | `global.drupalSettings = { path: { baseUrl: '/' } }` | When the source file reads `drupalSettings` | | `localStorage` | Provided by jsdom; call `localStorage.clear()` | When the source file uses `localStorage` | diff --git a/.vortex/docs/content/development/phpunit.mdx b/.vortex/docs/content/development/phpunit.mdx index ed4fedeff..06258da63 100644 --- a/.vortex/docs/content/development/phpunit.mdx +++ b/.vortex/docs/content/development/phpunit.mdx @@ -84,11 +84,12 @@ The `phpunit.xml` file in the project root configures: ## Skipping tests -Add `@group skip` annotation to a test class or method to exclude it from the test run: +Add a `@group skipped` annotation to a test class or method to exclude it from +the test run - `phpunit.xml` excludes the `skipped` group by default: ```php /** - * @group skip + * @group skipped */ public function testSomethingToSkip(): void { // This test will be skipped @@ -97,9 +98,9 @@ public function testSomethingToSkip(): void { ## Reporting -Test reports are stored in `.logs/phpunit` directory, separated into multiple -files and named after the suite name. These reports are usually used in -continuous integration to track test performance and stability. +PHPUnit writes a JUnit XML report to `.logs/test_results/phpunit/phpunit.xml`. +The continuous integration pipeline collects it to track test performance and +stability. ## Boilerplate @@ -112,21 +113,21 @@ These boilerplate tests run in continuous integration pipeline when you install ### Drupal settings tests -**Vortex** provides [Drupal settings tests](https://github.com/drevops/vortex/blob/main/tests/phpunit/Drupal/DrupalSettingsTest.php) +**Vortex** provides [Drupal settings tests](https://github.com/drevops/vortex/blob/main/tests/phpunit/Drupal) to check that Drupal settings are correct based on the environment type the site is running: with the number of custom modules multiplied by the number of environment types, it is easy to miss certain settings which may lead to unexpected issues when deploying a project to a different environment. -It is intended to be used in your site and kept up-to-date with the +They are intended to be used in your site and kept up-to-date with the changes made to the `settings.php` file. ### Continuous integration pipeline configuration tests -**Vortex** provides [continuous integration pipeline configuration tests](https://github.com/drevops/vortex/blob/main/tests/phpunit/CircleCiConfigTest.php) -to check that the continuous integration configuration is correct. It is +**Vortex** provides [CircleCI configuration tests](https://github.com/drevops/vortex/blob/main/tests/phpunit/CircleCiConfigTest.php) +to check that the continuous integration configuration is correct. They are intended to be used in your site and kept up-to-date with the continuous -integration configurations. +integration configuration. For example, there are tests for regular expressions used to filter the branches and tags before they are deployed to the hosting environment. diff --git a/.vortex/docs/content/development/visual-regression.mdx b/.vortex/docs/content/development/visual-regression.mdx index f3676eafe..cf945927d 100644 --- a/.vortex/docs/content/development/visual-regression.mdx +++ b/.vortex/docs/content/development/visual-regression.mdx @@ -1,5 +1,6 @@ --- sidebar_label: Visual regression +sidebar_position: 8 --- # Visual regression @@ -258,7 +259,7 @@ With `VR_DIFFY_PR_SKIP_BRANCHES=deps/*` (default) and | `deps/drupal-core-11.2` | no | **yes** (matches `deps/*`) | | `deps/drupal-core-11.2` | yes | yes (matches `deps/*`, label irrelevant) | -Consumers using Dependabot just append: +Consumers using Dependabot append: `VR_DIFFY_PR_SKIP_BRANCHES=deps/*,dependabot/*`. Consumers who want the label as the only gate set the variable to empty. diff --git a/.vortex/docs/content/drupal/README.mdx b/.vortex/docs/content/drupal/README.mdx index b4c5299aa..bce5d3b6c 100644 --- a/.vortex/docs/content/drupal/README.mdx +++ b/.vortex/docs/content/drupal/README.mdx @@ -7,26 +7,28 @@ sidebar_position: 1 **Vortex** provides the following Drupal features: -1. [Composer configuration](composer-json) -2. [Settings management](settings) -3. [Provision script](provision) -4. [Update hooks](update-hooks) -5. [Migrations](migrations) -6. [Module scaffold](module-scaffold) -7. [Theme scaffold](theme-scaffold) +| Topic | Description | +|-------|-------------| +| [composer.json](composer-json.mdx) | Every key of the root `composer.json`, with the full package tables | +| [Settings](settings.mdx) | Environment type detection and per-module settings overrides | +| [Provision](provision.mdx) | The provision script: database import, updates, config import, custom scripts | +| [Update hooks](update-hooks.mdx) | The Drupal hook types that run during deployments | +| [Migrations](migrations.mdx) | The optional second database powering Drupal migrations | +| [Module scaffold](module-scaffold.mdx) | The `ys_base` example module and its siblings | +| [Theme scaffold](theme-scaffold.mdx) | The `your_site_theme` example theme and its build system | ## Included modules -Please note that **Vortex** is not a Drupal distribution, and it does not aim -to provide a full Drupal installation profile or a set of recipes. Instead, it -provides you with a minimal set of modules and dependencies to get you started. +**Vortex** is not a Drupal distribution, and it does not aim to provide a full +installation profile. It ships a minimal set of modules and dependencies to get +you started - the only content model it carries is the small `recipes/page` +recipe that the provisioning process applies to build the demo site. You would +add more modules and themes once you finish the initial setup. -You would need to add more modules and themes once you finish the initial setup. +Every shipped module is listed in [Modules](modules.mdx), together with what it +does and what configuration **Vortex** provides for it. 3 of them have a page +of their own: -Every shipped module is listed in [Modules](modules), together with what it -does and what configuration **Vortex** provides for it. Three of them have a -page of their own: - -- [Drupal helpers](drupal-helpers) -- [Generated content](generated-content) -- [Testmode](testmode) +- [Drupal helpers](drupal-helpers.mdx) - utility facades for update hooks +- [Generated content](generated-content.mdx) - plugin-based demo content generation +- [Testmode](testmode.mdx) - content filtering during Behat test runs diff --git a/.vortex/docs/content/drupal/composer-json.mdx b/.vortex/docs/content/drupal/composer-json.mdx index 24b542ece..f5748c588 100644 --- a/.vortex/docs/content/drupal/composer-json.mdx +++ b/.vortex/docs/content/drupal/composer-json.mdx @@ -10,14 +10,14 @@ you to declare the libraries your project depends on and manages them for you. :::tip -➡️ See [Development > Composer](../development/composer) for information on how to add and manage dependencies. +➡️ See [Development > Composer](../development/composer.mdx) for information on how to add and manage dependencies. ::: -## `composer.json` +## File overview -**Vortex** comes with a pre-configured `composer.json` that lists essential -dependencies for Drupal projects, along with development tools to help you +**Vortex** comes with a pre-configured `composer.json` that lists the +dependencies a Drupal project needs, along with development tools to help you maintain your code quality. The `composer.json` file is the core configuration file for Composer, detailing @@ -64,7 +64,7 @@ the [SPDX Open Source License Registry](https://spdx.org/licenses/). ### `repositories` The [`repositories`](https://getcomposer.org/doc/04-schema.md#repositories) -section defines custom package repositories, essential for accessing packages +section defines custom package repositories, needed for accessing packages outside the default Packagist repository. | Repository | Type | Description | @@ -75,7 +75,7 @@ outside the default Packagist repository. ### `require` The [`require`](https://getcomposer.org/doc/04-schema.md#require) section -specifies the essential packages and libraries your project needs. +specifies the packages and libraries your project needs. The **Documentation** column links to the page covering that package in more depth. A [Settings](settings.mdx#per-module-overrides) link means **Vortex** @@ -126,10 +126,9 @@ where each module is installed. The [`require-dev`](https://getcomposer.org/doc/04-schema.md#require-dev) section lists packages used for development purposes, like code quality checks -and testing. These tools are essential for development but not required in -production environments. This distinction helps to keep the production -deployment streamlined and efficient, while still supporting a comprehensive and -effective development environment. +and testing. These tools are needed during development but not in production +environments, so the production deployment stays smaller while the development +environment keeps its full tooling. | Package | Description | Documentation | |---------|-------------|---------------| @@ -152,7 +151,7 @@ effective development environment. | [`phpspec/prophecy-phpunit`](https://github.com/phpspec/prophecy-phpunit) | Integrates the Prophecy mocking library with PHPUnit to provide advanced mocking capabilities in tests. | [PHPUnit](../tools/phpunit.mdx) | | [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer) | Automatically registers the installed PHPStan extensions, removing the need to wire them up by hand. | [PHPStan](../tools/phpstan.mdx) | | [`phpstan/phpstan`](https://github.com/phpstan/phpstan) | A static analysis tool that finds type errors, incorrect method calls and other bugs without running the code. | [PHPStan](../tools/phpstan.mdx) | -| [`phpunit/phpunit`](https://github.com/sebastianbergmann/phpunit) | The industry-standard PHP testing framework, used here for unit, kernel and functional tests. | [PHPUnit](../tools/phpunit.mdx) | +| [`phpunit/phpunit`](https://github.com/sebastianbergmann/phpunit) | The PHP testing framework, used here for unit, kernel and functional tests. | [PHPUnit](../tools/phpunit.mdx) | | [`pyrech/composer-changelogs`](https://github.com/pyrech/composer-changelogs) | Prints a summary of package additions, updates and removals after running `composer update`. | | | [`rector/rector`](https://github.com/rectorphp/rector) | An automated refactoring tool that upgrades and modernizes PHP code. | [Rector](../tools/rector.mdx) | | [`softcreatr/jsonpath`](https://github.com/SoftCreatR/JSONPath) | A JSONPath implementation required by the `drevops/behat-steps` JSON assertion steps. | [Behat](../tools/behat.mdx) | @@ -161,7 +160,7 @@ effective development environment. ### `conflict` The [`conflict`](https://getcomposer.org/doc/04-schema.md#conflict) section -prevents installation conflicts with standalone Drupal core, crucial for +prevents installation conflicts with standalone Drupal core, which matters for avoiding version clashes and ensuring consistency in core files. ### `minimum-stability` @@ -194,18 +193,11 @@ specifies key configurations for Composer's behavior in the project. plugins. Each plugin needs to be explicitly allowed to ensure it can execute. - [`policy`](https://getcomposer.org/doc/06-config.md#policy): Introduced in Composer 2.10.0, this unified setting controls security auditing and version - blocking of dependencies. **Vortex** configures it to keep vulnerabilities - visible without blocking installs: - - `advisories.block` (set to `false`): security advisories do not block - `composer install`, `composer update`, or `composer require`, keeping builds - reproducible when new advisories are published upstream. - - `advisories.audit` (set to `fail`): `composer audit` still fails on - advisories, so they remain visible locally and in CI. - - `abandoned.audit` (set to `report`): abandoned packages are reported as - warnings without failing the audit. - - See - [Composer Security Auditing](../development/composer#security-auditing) - for the full reference, ignoring advisories, and best practices. + blocking of dependencies. **Vortex** configures it so that advisories never + block installs but still fail `composer audit`, keeping vulnerabilities + visible without breaking reproducible builds. + ➡️ See [Security auditing](../development/composer.mdx#security-auditing) + for the option values, ignoring advisories, and the CI integration. - [`bump-after-update`](https://getcomposer.org/doc/06-config.md#bump-after-update): Automatically updates version constraints in `composer.json` to match currently installed package versions after running `composer update`. **Vortex** sets this @@ -230,7 +222,7 @@ specifies key configurations for Composer's behavior in the project. ### `autoload-dev` The [`autoload-dev`](https://getcomposer.org/doc/04-schema.md#autoload-dev) -section is essential for defining how Composer automatically loads PHP +section defines how Composer automatically loads PHP development-specific classes within the project, without needing to manually include or require each class file. @@ -241,11 +233,6 @@ a source of custom configuration for various packages. These packages read settings from this section to tailor their behavior according to the specific needs and structure of your Drupal project. -- **patches.lock.json**: Automatically generated by `cweagans/composer-patches` - v2.x. This file contains patch metadata and SHA-256 checksums, and must be - committed to version control (like `composer.lock`). It ensures reproducible - builds across teams and CI/CD environments by verifying patch integrity and - making the patch state explicit and trackable. - [`drupal-scaffold`](https://www.drupal.org/docs/develop/using-composer/using-drupals-composer-scaffold): This setting controls which files should be scaffolded: - `locations`: Specifies the location of the web root (the directory @@ -262,8 +249,10 @@ needs and structure of your Drupal project. the file paths found in the patch file. This determines how the paths in the patch file are interpreted relative to the current directory where the patch is being applied. -- `patches`: Specifies the patches to be applied to specific packages. - ➡️ See [Development > Composer > Patching](../development/composer#patching) +- `patches`: Specifies the patches to be applied to specific packages. The + applied patch state lives in the generated `patches.lock.json` file, which is + committed like `composer.lock`. + ➡️ See [Development > Composer > Patching](../development/composer.mdx#patching) ## Dependency bumping diff --git a/.vortex/docs/content/drupal/generated-content.mdx b/.vortex/docs/content/drupal/generated-content.mdx index 0e20dcd5f..c3c883d72 100644 --- a/.vortex/docs/content/drupal/generated-content.mdx +++ b/.vortex/docs/content/drupal/generated-content.mdx @@ -62,6 +62,16 @@ class Page extends GeneratedContentPluginBase { } ``` +:::note + +For a bundle under content moderation, also set the `moderation_state` field +to `published` before saving - the shipped +[`Page` plugin](https://github.com/drevops/vortex/blob/main/web/modules/custom/ys_demo/src/Plugin/GeneratedContent/Node/Page.php) +shows the guarded check. Without it, generated nodes stay as unpublished +drafts. + +::: + ### Attribute parameters | Parameter | Type | Required | Description | diff --git a/.vortex/docs/content/drupal/migrations.mdx b/.vortex/docs/content/drupal/migrations.mdx index 1e9bba60f..e8570b9ed 100644 --- a/.vortex/docs/content/drupal/migrations.mdx +++ b/.vortex/docs/content/drupal/migrations.mdx @@ -4,7 +4,7 @@ sidebar_position: 6 # Migrations -Vortex provides optional support for a **second database** to power Drupal +**Vortex** provides optional support for a **second database** to power Drupal migrations. When enabled, a `database2` Docker service runs alongside the primary database, and a `settings.migration.php` file registers the `$databases['migrate']` connection that Drupal's Migrate API uses by default. @@ -18,10 +18,10 @@ When running `vortex install`, answer **Yes** to ### Manual setup -If you already have a Vortex project and want to add migration support: +If you already have a **Vortex** project and want to add migration support: 1. Ensure `docker-compose.yml` contains the `database2` service and - `DATABASE2_*` environment variables (copy from a fresh Vortex install + `DATABASE2_*` environment variables (copy from a fresh **Vortex** install with migration enabled). 2. Add `VORTEX_FETCH_DB2_FILE`, `VORTEX_FETCH_DB2_SOURCE`, and `VORTEX_FETCH_DB2_URL` to `.env`. @@ -44,12 +44,13 @@ If you already have a Vortex project and want to add migration support: ## Docker service -The `database2` service uses the same Lagoon MySQL image as the primary -database but does not use a custom Dockerfile or database-in-image storage. +The `database2` service uses the same Lagoon database image as the primary +database, and the image can be overridden with the `VORTEX_DB2_IMAGE` variable +(for example, to use a database-in-image seed): ```yaml database2: - image: uselagoon/mysql-8.4:26.1.0 + image: "${VORTEX_DB2_IMAGE:-uselagoon/mysql-8.4:26.8.0}" environment: <<: *default-environment MYSQL_DATABASE: drupal @@ -58,27 +59,21 @@ database2: ports: - '3306' labels: - lagoon.type: none + lagoon.type: mariadb ``` -The service is labeled `lagoon.type: none` because the migration database -is only used locally and in CI — it is not deployed to hosting environments. +The image tag tracks the primary database image - check the shipped +[`docker-compose.yml`](https://github.com/drevops/vortex/blob/main/docker-compose.yml) +for the current version. ## Drupal settings ### `settings.migration.php` -```php -$databases['migrate']['default'] = [ - 'database' => getenv('DATABASE2_NAME') ?: 'drupal', - 'username' => getenv('DATABASE2_USERNAME') ?: 'drupal', - 'password' => getenv('DATABASE2_PASSWORD') ?: 'drupal', - 'host' => getenv('DATABASE2_HOST') ?: 'localhost', - 'port' => getenv('DATABASE2_PORT') ?: '', - 'prefix' => '', - 'driver' => 'mysql', -]; -``` +import CodeBlock from '@theme/CodeBlock'; +import MigrationSettingsExample from '!!raw-loader!@site/../../web/sites/default/settings.migration.php'; + +{MigrationSettingsExample} Drupal's Migrate API `SqlBase` source plugin uses `key: migrate` by default, which resolves to this connection. @@ -89,13 +84,15 @@ which resolves to this connection. |---------|-------------| | `ahoy fetch-db2` | Fetch the migration database dump | | `ahoy fetch-db2 --fresh` | Force a fresh fetch | +| `ahoy reload-db2` | Recreate the `database2` container and re-import its dump | | `ahoy db2` | Open the migration database in Sequel Ace | -The `fetch-db2` command reuses the existing `fetch-db.sh` script with -`VORTEX_DB_INDEX=2`, which makes all scripts resolve indexed variable names -(e.g., `VORTEX_FETCH_DB2_SOURCE` instead of `VORTEX_FETCH_DB_SOURCE`, -`VORTEX_DB2_IMAGE` instead of `VORTEX_DB_IMAGE`) so all existing fetch -sources (URL, FTP, Acquia, Lagoon, S3) work for the migration database as well. +The `fetch-db2` command reuses the existing `vendor/bin/vortex-fetch-db` +tooling script with `VORTEX_DB_INDEX=2`, which makes all scripts resolve +indexed variable names (e.g., `VORTEX_FETCH_DB2_SOURCE` instead of +`VORTEX_FETCH_DB_SOURCE`, `VORTEX_DB2_IMAGE` instead of `VORTEX_DB_IMAGE`) so +all existing fetch sources (URL, FTP, Acquia, Lagoon, S3) work for the +migration database as well. ## Migration deploy step @@ -140,6 +137,10 @@ Add your migration IDs to the plugin's `MIGRATIONS` constant: protected const MIGRATIONS = ['ys_migrate_categories']; ``` +The per-migration entity limit comes from `DRUPAL_MIGRATION_IMPORT_LIMIT` +(set it to `all` for unlimited), and progress feedback from +`DRUPAL_MIGRATION_FEEDBACK`. + ### Corruption detection If `DRUPAL_MIGRATION_SOURCE_DB_IMPORT` is `0`, the plugin probes the source database @@ -149,7 +150,7 @@ dump file. ## Demo migration module -Vortex ships with a demo migration module `ys_migrate` in +**Vortex** ships with a demo migration module `ys_migrate` in `web/modules/custom/ys_migrate/`. It demonstrates the full migration workflow by migrating `categories` from the source database into Drupal's `tags` taxonomy vocabulary. @@ -212,3 +213,6 @@ alongside the primary database before running provision. ```bash ahoy provision ``` + +➡️ See [Provision](provision.mdx) for how the custom provision scripts run, and +[Modules](modules.mdx) for the shipped migration modules. diff --git a/.vortex/docs/content/drupal/module-scaffold.mdx b/.vortex/docs/content/drupal/module-scaffold.mdx index 343142e46..2e77a11e1 100644 --- a/.vortex/docs/content/drupal/module-scaffold.mdx +++ b/.vortex/docs/content/drupal/module-scaffold.mdx @@ -8,17 +8,17 @@ sidebar_position: 7 is an example of a Drupal module. We recommend creating a custom `ys_base` module for your project to hold -general-purpose functionality that doesn’t belong in a dedicated, +general-purpose functionality that doesn't belong in a dedicated, feature-specific module. The `ys` prefix is abbreviated from your project name (`your_site` in this case). We recommend using this technique to prefix all modules, and use the site machine name for a theme name. -:::info Token Replacement +:::info Token replacement - The `your_site`, `ys` prefix and other similar tokens are placeholders. They - will be replaced with your actual project prefix when you install **Vortex**. +The `your_site`, `ys` prefix and other similar tokens are placeholders. They +will be replaced with your actual project prefix when you install **Vortex**. ::: @@ -33,23 +33,32 @@ to run deployment commands during the site [provisioning](provision) process. The `ys_demo` module demonstrates integration patterns for several contributed modules: -- [Drupal helpers](drupal-helpers) - utility facades for deploy hooks -- [Generated content](generated-content) - plugin-based content generation -- [Testmode](testmode) - content filtering during Behat tests +- [Drupal helpers](drupal-helpers.mdx) - utility facades for deploy hooks +- [Generated content](generated-content.mdx) - plugin-based content generation +- [Testmode](testmode.mdx) - content filtering during Behat tests (declared as a dependency, so it installs together with `ys_demo`) The demo module ships a pages view at `/pages`, a generated content plugin that populates it, and the deploy hooks that place a counter block, add the `Pages` menu link, and register the view with testmode. +## Other shipped modules + +2 more custom modules ship alongside the scaffold and demo modules: + +- `ys_search` - configures the Search API index, the Solr server, and the + search view for the [Solr service](../tools/docker.mdx#services); it also + brings in the `workflows` and `content_moderation` core modules. +- `ys_migrate` - a demo migration module - see [Migrations](migrations.mdx). + ## Tests scaffold The `tests` directory contains working examples of tests that can be used as a starting point in your project. It also has a set of helper `Traits` that you may find useful when writing your -tests. Simply remove them if you do not need them. +tests. Remove them if you do not need them. --- -➡️ See [Development](../development) for more details on how to work with -the custom modules. +➡️ See [Development](../development/README.mdx) for more details on how to work +with the custom modules. diff --git a/.vortex/docs/content/drupal/modules.mdx b/.vortex/docs/content/drupal/modules.mdx index be89ad505..c4eceb53b 100644 --- a/.vortex/docs/content/drupal/modules.mdx +++ b/.vortex/docs/content/drupal/modules.mdx @@ -4,9 +4,10 @@ sidebar_position: 9 # Modules -**Vortex** is not a Drupal distribution: it ships no installation profile and -no recipes. It provides a small set of contributed modules that most projects -need, each already wired into the environment-aware +**Vortex** is not a Drupal distribution: it ships no installation profile, and +the only recipe it carries is the small `recipes/page` recipe that creates the +demo content model during provisioning. It provides a small set of contributed +modules that most projects need, each already wired into the environment-aware [settings](settings.mdx) and the [provisioning](provision.mdx) scripts. You add the rest once the initial setup is done. diff --git a/.vortex/docs/content/drupal/provision.mdx b/.vortex/docs/content/drupal/provision.mdx index 8387ec454..58debb6ca 100644 --- a/.vortex/docs/content/drupal/provision.mdx +++ b/.vortex/docs/content/drupal/provision.mdx @@ -36,7 +36,7 @@ environments, eliminating "it works on my machine" issues. project-specific logic like enabling test modules or running migrations. In short, `provision` orchestrates standalone Drush commands in a consistent, -repeatable, and configurable process — turning a manual setup step into a +repeatable, and configurable process - turning a manual setup step into a reliable automation layer. ## Database import vs full provisioning @@ -112,7 +112,7 @@ section. │ │ └─ Dump file missing? │ │ ├─ ④ Fallback? ──Yes──► 📦 Install from profile ✓ │ │ └─ ④ Fallback? ──No───► 🏁 EXIT 1 (fail) ✗ -│ └─ else ──► preserve content, ⑦ skip sanitization +│ └─ else ──► preserve content, ⑩ skip sanitization │ └─ 💡 Existing site found = NO ├─ Container image set? @@ -131,6 +131,8 @@ section. │ No ▼ ⑥ 🚧 Enable maintenance mode + ▼ + 🆔 Set site UUID from configuration (if config files present) ▼ 🔄 Run DB updates ▼ @@ -141,6 +143,8 @@ section. ▼ ⬇️ Import configuration (if config files present) ▼ + ⬇️ Import config_split configuration (if the module is enabled) + ▼ ⑨ 🔁 Repeat configuration import (opt-in) ▼ 🧹 Rebuild caches @@ -190,8 +194,8 @@ hosting provider's specific environment. ### Database sanitization The `provision` script includes a step to sanitize the database after -provisioning. This helps ensure that sensitive data — like real email addresses, -passwords, and user information — is replaced with safe, generic values in +provisioning. This helps ensure that sensitive data - like real email addresses, +passwords, and user information - is replaced with safe, generic values in non-production environments. It prevents issues like accidentally sending emails to real users or exposing private data during testing, making shared environments safer to work with. @@ -204,13 +208,13 @@ with access to the dump file can still see sensitive data. If your database has highly sensitive data, consider sanitizing the database dump before it can be downloaded (sanitize on export). There are tools available for this purpose, such as [Drush GDPR Dumper](https://github.com/robiningelbrecht/drush-gdpr-dumper) -or [MTK](https://github.com/skpr/mtk). These tools can be easily integrated into **Vortex**-based projects -without changing the provisioning process. +or [MTK](https://github.com/skpr/mtk). These tools can be integrated into +**Vortex**-based projects without changing the provisioning process. ::: -By default, the database sanitization step is enabled by default on all -environments except production. To disable database sanitization, set the +The database sanitization step is enabled by default on all environments except +production. To disable database sanitization, set `VORTEX_PROVISION_SANITIZE_DB_SKIP=1` in the `.env` file or in your hosting provider's specific environment. @@ -306,8 +310,9 @@ match their own empty search backend. Other environments keep their existing index. Set `DRUPAL_SEARCH_INDEX_SKIP=1` in the `.env` file or your hosting provider's environment to opt out. -:::tip Related Documentation -For information about different types of Drupal hooks used during deployment and updates. +:::tip Related documentation + +For the different types of Drupal hooks that run during deployment and updates, +see [Update hooks](update-hooks.mdx). -➡️ See [Update Hooks](./update-hooks) ::: diff --git a/.vortex/docs/content/drupal/settings.mdx b/.vortex/docs/content/drupal/settings.mdx index 7ba2d02d5..7f2ee29c7 100644 --- a/.vortex/docs/content/drupal/settings.mdx +++ b/.vortex/docs/content/drupal/settings.mdx @@ -4,12 +4,12 @@ sidebar_position: 3 # Settings -Drupal site configuration — including database connections, file paths, and -environment-specific behavior — is controlled through the `settings.php` and +Drupal site configuration - including database connections, file paths, and +environment-specific behavior - is controlled through the `settings.php` and `services.yml` files. This section explains how **Vortex** structures and extends these files to support consistent setup across environments. -**Vortex** ships with its own streamlined version of +**Vortex** ships with its own version of the [`settings.php`](https://github.com/drevops/vortex/blob/main/web/sites/default/settings.php) and [`services.yml`](https://github.com/drevops/vortex/blob/main/web/sites/default/services.yml) files. @@ -23,24 +23,24 @@ files are also provided if you choose to use them instead. ## Approach -Managing Drupal settings across multiple environments — such as local, CI, -development, staging, and production — often requires conditional configuration. +Managing Drupal settings across multiple environments - such as local, CI, +development, staging, and production - often requires conditional configuration. Different environments may need to enable or disable modules, change performance settings, use different APIs, or point to different services. -The challenge is that Drupal doesn’t offer a standard way to manage these +The challenge is that Drupal doesn't offer a standard way to manage these environment-specific differences. Its configuration system is not designed to handle conditional logic, such as applying different settings based on runtime environment, or retrieving values from environment variables. Modules like `config_split` can help by allowing you to maintain separate -configuration sets per environment, but they are limited: they don’t support +configuration sets per environment, but they are limited: they don't support environment-based conditions inside the configuration YAML files, cannot access environment variables directly, and are not suitable when you need dynamic logic (e.g. setting values based on external service URLs). -**Vortex** does support `config_split` as part of its standard tooling, and it’s -ideal for use cases where declarative configuration is sufficient — for example, +**Vortex** does support `config_split` as part of its standard tooling, and it's +ideal for use cases where declarative configuration is sufficient - for example, enabling a module in staging but not production. However, when settings require conditional logic or need to pull values from the environment, `config_split` does not suffice. In addition, it is not possible to automatically test which @@ -57,8 +57,7 @@ with conditions applied based on the detected _environment type_. This structure offers several benefits: - It keeps environment detection separate from configuration logic. -- It makes it easy to see how a module behaves in a specific environment — all -in one place. +- It shows how a module behaves in a specific environment - all in one place. - If a module is no longer needed, its override file can be safely removed without modifying the `settings.php` file. - It prevents environment-specific settings from leaking into unrelated parts of @@ -78,7 +77,7 @@ removed, its override file should be removable without affecting unrelated settings. - **Use environment variables for configuration that changes by environment.**
-Prefix all such variables with `DRUPAL_` (e.g. `DRUPAL_MY_SETTING`) to easily +Prefix all such variables with `DRUPAL_` (e.g. `DRUPAL_MY_SETTING`) to distinguish them from other environment variables.
Always define a default hardcoded value for each environment variable. @@ -154,11 +153,15 @@ The following environment variables can be used to customize general settings: | `DRUPAL_CONFIG_PATH` | | `../config/default` | Location of configuration sync directory | | `DRUPAL_PUBLIC_FILES` | | `sites/default/files` | Public files directory path | | `DRUPAL_PRIVATE_FILES` | | `sites/default/files/private` | Private files directory path | -| `DRUPAL_TEMPORARY_FILES` | | `/tmp` | Temporary files directory path | +| `DRUPAL_TEMPORARY_FILES` | `TMP` | `/tmp` | Temporary files directory path | | `DRUPAL_HASH_SALT` | | _Generated from database host_ | Cryptographic salt for security | | `DRUPAL_TIMEZONE` | `TZ` | `UTC` | Runtime timezone | | `DRUPAL_MAINTENANCE_THEME` | `DRUPAL_THEME` | `claro` | Theme used during maintenance mode | -| `DRUPAL_CACHE_PAGE_MAX_AGE` | | `900` | Page cache expiration time (seconds) | + +The page cache lifetime (`DRUPAL_CACHE_PAGE_MAX_AGE`, default `900` seconds) is +set in the `settings.system.php` [per-module override](#per-module-overrides), +not in this section. The full generated list of `DRUPAL_*` variables is in the +[Variables reference](../development/variables.mdx). #### Hash salt @@ -294,28 +297,30 @@ Each settings file should: #### Shield [Shield](https://www.drupal.org/project/shield) restricts access to your site -by requiring HTTP authentication credentials. **Vortex** configures Shield to -protect non-production environments from public access while keeping production, -local development, and CI environments accessible without authentication. +by requiring HTTP authentication credentials. **Vortex** enforces Shield in +non-production hosted environments, keeps local development and CI accessible +without authentication, and leaves production alone so Shield stays under UI +control there. **Environment behavior:** -| Environment | Shield enabled | Reason | -|-------------|----------------|-------------------------------------------------| -| Local | No | No need for HTTP auth during local development | -| CI | No | Automated tests must access the site freely | -| Dev | **Yes** | Protects development environments from crawlers | -| Preview | **Yes** | Protects ephemeral preview environments | -| Stage | **Yes** | Protects staging environments from public access| -| Prod | No | Production is publicly accessible | +| Environment | Shield enabled | Reason | +|-------------|----------------|-------------------------------------------------------------------| +| Local | No | No need for HTTP auth during local development | +| CI | No | Automated tests must access the site freely | +| Dev | **Yes** | Protects development environments from crawlers | +| Preview | **Yes** | Protects ephemeral preview environments | +| Stage | **Yes** | Protects staging environments from public access | +| Prod | Not enforced | Left to the site configuration - enable or disable through the UI | **Environment variables:** -| Variable | Default | Purpose | -|-----------------------|----------------------|-------------------------------------------| -| `DRUPAL_SHIELD_USER` | | HTTP authentication username | -| `DRUPAL_SHIELD_PASS` | | HTTP authentication password | -| `DRUPAL_SHIELD_PRINT` | `Restricted access.` | Message shown in the authentication popup | +| Variable | Default | Purpose | +|--------------------------------------|---------|-----------------------------------------------------| +| `DRUPAL_SHIELD_USER` | | HTTP authentication username | +| `DRUPAL_SHIELD_PASS` | | HTTP authentication password | +| `DRUPAL_SHIELD_PRINT` | | Message shown in the authentication popup (module default when unset) | +| `DRUPAL_SHIELD_ALLOW_ACME_CHALLENGE` | | Set to any non-empty value to keep the `/.well-known/acme-challenge/*` path open for Let's Encrypt certificate generation | **Overriding default behavior:** @@ -382,9 +387,12 @@ At the end of the `settings.php`, there is an option to include additional local settings. This allows you to override some settings for the local environment without affecting the main configuration. You can copy `example.settings.local.php` and `example.services.local.yml` -to `settings.local.php` and `services.local.yml`, respectively, to utilize this +to `settings.local.php` and `services.local.yml`, respectively, to use this functionality. +Set the `DRUPAL_SETTINGS_LOCAL_SKIP` environment variable to `1` to skip the +local overrides even when `settings.local.php` exists. + import LocalSettingsExample from '!!raw-loader!@site/../../web/sites/default/example.settings.local.php';
@@ -410,8 +418,8 @@ to verify that settings are applied correctly for each detected environment type. These tests are intended to be maintained within your project, helping you -ensure that environment-driven configuration — including both environment types -and environment variables — behaves as expected. +ensure that environment-driven configuration - including both environment types +and environment variables - behaves as expected. To run unit tests for settings: @@ -419,4 +427,4 @@ To run unit tests for settings: vendor/bin/phpunit --group=drupal_settings ``` -You may simply remove these tests if you do not want to maintain them. +Remove these tests if you do not want to maintain them. diff --git a/.vortex/docs/content/drupal/theme-scaffold.mdx b/.vortex/docs/content/drupal/theme-scaffold.mdx index 4a586a778..898609859 100644 --- a/.vortex/docs/content/drupal/theme-scaffold.mdx +++ b/.vortex/docs/content/drupal/theme-scaffold.mdx @@ -17,7 +17,7 @@ while modules use the abbreviated prefix (`ys_` for `your_site`). We understand that front-end theming is often highly project-specific and subject to team preferences. The provided `your_site_theme` scaffold is not -intended to dictate how your theme should be built — it simply demonstrates how +intended to dictate how your theme should be built - it demonstrates how custom themes can _integrate_ with the **Vortex** tooling and automations. Feel free to adapt or replace it with your preferred theme. @@ -26,7 +26,10 @@ Feel free to adapt or replace it with your preferred theme. ## Build system -The theme includes a complete Node.js-based build system using [Grunt](https://gruntjs.com/): +The theme includes a complete Node.js-based build system composed of npm +scripts over [Sass](https://sass-lang.com/), [PostCSS](https://postcss.org/) +with Autoprefixer, [Terser](https://terser.org/) and +[chokidar](https://github.com/open-cli-tools/chokidar-cli): - **SCSS compilation** - **JavaScript concatenation and minification** @@ -76,6 +79,9 @@ ahoy few # Lint front-end code ahoy lint-fe + +# Fix front-end lint issues +ahoy lint-fe-fix ``` These commands run within the container to use the Node.js environment @@ -92,6 +98,9 @@ the build system without learning a new process. ## File structure +The main directories and files (build outputs, lint configuration files, and +static assets are trimmed for brevity): + ```text your_site_theme/ ├── components/ # Single Directory Components (SDC) @@ -138,13 +147,14 @@ The theme ships a sample [Single Directory Component](https://www.drupal.org/doc link, demonstrating real component usage from a template. Components are validated with [SDC Devel](https://www.drupal.org/project/sdc_devel), -which is enabled during provisioning in non-production environments: +which the provisioning process enables in the `local`, `ci`, `dev` and `stage` +environments: ```shell ahoy lint-sdc ``` -This command requires a provisioned site and also runs in continuous +This command requires a provisioned site and also runs in the continuous integration pipeline. ## Tests scaffold @@ -153,9 +163,9 @@ The `tests` directory contains working examples of tests that can be used as a starting point in your project. It also has a set of helper `Traits` that you may find useful when writing your -tests. Simply remove them if you do not need them. +tests. Remove them if you do not need them. --- -➡️ See [Development](../development) for more details on how to work with -the theme. +➡️ See [Development](../development/README.mdx) for more details on how to work +with the theme. diff --git a/.vortex/docs/content/drupal/update-hooks.mdx b/.vortex/docs/content/drupal/update-hooks.mdx index be18c538d..b1f483065 100644 --- a/.vortex/docs/content/drupal/update-hooks.mdx +++ b/.vortex/docs/content/drupal/update-hooks.mdx @@ -4,32 +4,31 @@ sidebar_position: 5 # Update hooks -Update hooks in Drupal are essential for managing database schema changes, -data migrations, and environment-specific deployment tasks. They are -irreplaceable mechanisms to automate changes to take place during deployments. +Update hooks automate database schema changes, data migrations, and +environment-specific deployment tasks, so a change ships together with the code +that needs it instead of being applied by hand. -Drupal provides several types of hooks for database updates. Understanding these -different hook types helps you choose the right approach for your deployment needs. +Drupal provides several types of hooks for database updates. Understanding the +differences helps you choose the right one for a deployment task. ## Hook types overview -| Hook Type | Use Case | Execution | File Location | +| Hook type | Use case | Execution | File location | |---------------------------|--------------------------------------------|----------------------------------|--------------------------| -| `hook_update_n()` | Database schema changes, data migrations | drush updatedb | `MODULE.install` | +| `hook_update_N()` | Database schema changes, data migrations | drush updatedb | `MODULE.install` | | `hook_post_update_NAME()` | Entity updates and other module operations | drush updatedb | `MODULE.post_update.php` | -| `hook_deploy_NAME()` | Environment-specific deployment tasks | drush deploy | `MODULE.deploy.php` | +| `hook_deploy_NAME()` | Environment-specific deployment tasks | drush deploy:hook | `MODULE.deploy.php` | All of these hooks run automatically during the -[provisioning process](./provision) using `drush`. +[provisioning process](provision.mdx): `drush updatedb` runs the update and +post-update hooks, and `drush deploy:hook` runs the deploy hooks. -To automate changes during site deployments, we advise to use **deploy hooks**. -Note that deploy hooks run only once: Drupal tracks which hooks have been -executed by name. +To automate changes during site deployments, use **deploy hooks**. Note that +deploy hooks run only once: Drupal tracks which hooks have been executed by +name. ## Example deploy hook -### - ```php function ys_base_deploy_create_about_page(): string { $environment = \Drupal\Core\Site\Settings::get('environment'); @@ -65,9 +64,9 @@ function ys_base_deploy_create_about_page(): string { ### Debugging commands ```shell -# List available deploy hooks -drush deploy:hook --list +# Show pending deploy hooks. +drush deploy:hook-status -# Run deploy hooks manually (for testing) +# Run deploy hooks manually (for testing). drush deploy:hook ``` diff --git a/.vortex/docs/content/faqs.mdx b/.vortex/docs/content/faqs.mdx index 0f2264255..cf007d6c7 100644 --- a/.vortex/docs/content/faqs.mdx +++ b/.vortex/docs/content/faqs.mdx @@ -1,5 +1,10 @@ # FAQs +Answers to the questions teams ask when deciding whether to adopt **Vortex**. +For operational questions once you're working in a project - clearing caches, +using Xdebug, adding modules and patches - see the +[Development FAQs](development/faqs.mdx). + ## Why use Vortex instead of the Drupal Composer template? The Drupal Composer template gives you a starting point, but you still need to @@ -7,7 +12,7 @@ add everything else: CI pipelines, tooling, automations, deployment scripts, hosting configurations, and documentation. Then you need to test that everything works together correctly. -You also need to watch for _false negatives_ — faulty tests that pass silently +You also need to watch for _false negatives_ - faulty tests that pass silently in CI but fail at release time, blocking your deployment. And you need to maintain all of this across every project you run. @@ -19,7 +24,7 @@ documentation and consistent tooling across all projects. ## Can I use Vortex with an existing project? Yes, you can install **Vortex** into your existing project. See -[Installation](installation#installing-vortex-into-an-existing-project). +[Installation](installation.mdx#installing-vortex-into-an-existing-project). ## Can I keep my existing CI provider? @@ -64,7 +69,7 @@ be happy to fix it. Please share your feedback in the Run the update command to pull in the latest **Vortex** version. The command updates the boilerplate code automatically. You then resolve any conflicts -manually — similar to merging changes between projects, but automated. +manually - similar to merging changes between projects, but automated. Any customizations your team made will also need manual resolution. This is a trade-off between relying on an upgradable project template, where you have full diff --git a/.vortex/docs/content/features.mdx b/.vortex/docs/content/features.mdx index 66285cdd5..f44155c05 100644 --- a/.vortex/docs/content/features.mdx +++ b/.vortex/docs/content/features.mdx @@ -31,7 +31,7 @@ import { - Pre-configured [general settings](https://github.com/drevops/vortex/blob/main/web/sites/default/settings.php) - Environment type detection based on the hosting provider - - Optimised per-module settings for quick start + - Optimized per-module settings for quick start - Multi-database configuration for MySQL and MariaDB ### Module & theme scaffolds @@ -106,7 +106,7 @@ import { - Build and test assets are stored as pipeline artifact and accessible after the build - Code coverage reports are stored as pipeline artifact and accessible after the build - - Code coverage reports are pushed to [Codecov](https://codecov.io/) for easy access and visualization. + - Code coverage reports are pushed to [Codecov](https://codecov.io/) for access and visualization ☁️ Hosting Integrations | Integrations with cloud hosting providers @@ -144,11 +144,13 @@ import { - [PHPStan](https://phpstan.org/) for static code analysis - [Rector](https://getrector.com/) with [Drupal-specific configurations](https://github.com/palantirnet/drupal-rector) for automated code upgrades - [ESLint](https://eslint.org/) for checking JavaScript coding standards + - [Stylelint](https://stylelint.io/) for checking CSS coding standards - [Twig CS Fixer](https://github.com/VincentLanglet/Twig-CS-Fixer) for checking Twig coding standards ### Unit and functional testing - [PHPUnit](https://phpunit.de/) for unit and functional testing + - [Jest](https://jestjs.io/) for JavaScript unit testing - Configured for Drupal testing ### Behavior-driven testing @@ -181,7 +183,7 @@ import { ### Agent-agnostic configuration - - [`AGENTS.md`](https://github.com/drevops/vortex/blob/main/AGENTS.md) works with any AI tool — Claude Code, Cursor, Copilot, and others + - [`AGENTS.md`](https://github.com/drevops/vortex/blob/main/AGENTS.md) works with any AI tool - Claude Code, Cursor, Copilot, and others - [`CLAUDE.md`](https://github.com/drevops/vortex/blob/main/CLAUDE.md) for Claude Code-specific integration - No vendor lock-in @@ -243,9 +245,9 @@ import { - Customized [README.md](https://github.com/drevops/vortex/blob/main/README.dist.md) for easy onboarding - [Scaffold for project-specific documentation](https://github.com/drevops/vortex/blob/main/docs) for easy documentation management - ### Vortex Documentation + ### Vortex documentation - - [Vortex documentation](https://www.vortextemplate.com/docs/) for all your Vortex-onboarded projects + - [Vortex documentation](https://www.vortextemplate.com/docs/) for all your **Vortex**-onboarded projects - Framework architecture and design principles for faster team onboarding - Tooling information and usage guides for efficient day-to-day use @@ -254,8 +256,8 @@ import { 🌀 Vortex | Consistency across your projects - Vortex provides several features to ensure consistency and reliability - across your Vortex-onboarded projects so that you can rely on future updates + **Vortex** provides several features to ensure consistency and reliability + across your **Vortex**-onboarded projects so that you can rely on future updates and improvements. ### Product maturity @@ -265,7 +267,7 @@ import { not-for-profit, financial services, and technology sectors. - Constantly evolving with the latest Drupal, PHP, containerization, CI/CD practices, and tooling. - - Managed as a product with a dedicated team and a [roadmap](/contributing/roadmap). + - Managed as a product with a dedicated team and a [roadmap](/docs/contributing/roadmap). - Released monthly with new features, improvements, and bug fixes. ### Multi-layered automated testing for every change diff --git a/.vortex/docs/content/hosting/README.mdx b/.vortex/docs/content/hosting/README.mdx index 07183c7ef..23327999b 100644 --- a/.vortex/docs/content/hosting/README.mdx +++ b/.vortex/docs/content/hosting/README.mdx @@ -5,48 +5,42 @@ sidebar_position: 1 # Hosting -**Vortex** provides integrations with several hosting providers. +**Vortex** provides integrations with several hosting providers: -- [Acquia](/docs/hosting/acquia) -- [Lagoon](/docs/hosting/lagoon) +- [Acquia](acquia.mdx) +- [Lagoon](lagoon.mdx) :::info More integrations coming soon - We will be adding more hosting provider integrations in the future. +More hosting provider integrations are planned - see the [roadmap](../contributing/roadmap.mdx). ::: -The deployment pipelines to all environments of these providers are streamlined -and automated to ensure that the deployment process is consistent and reliable. +This section covers where your site _runs_: what each provider integration +does, how the environment is detected, and the routine operations (fetching +databases, copying files, purging caches) available on each platform. -## Hosting providers vs environments - -Understanding the distinction between **hosting providers** and **environments** -is crucial for properly configuring and deploying your website. - -### Hosting providers - -**Hosting providers** are the _platforms_ where your _environments_ actually run. -They can be cloud-based or on-premises solutions that provide the infrastructure -to run the webserver, database, and other services required for your site. +How code _gets to_ a provider - deployment types, triggers, and skip rules - is +covered in the [Deployment](../deployment/README.mdx) section. -Multiple _environments_ can exist on the same _hosting provider_. - -### Environments - -**Environments** refer to the contained _bundles_ of infrastructure services -that allow to _run_ your website. +## Hosting providers vs environments -An environment represents a specific logical stage of your website deployment -and releasing lifecycle: from local development to production. +**Hosting providers** are the _platforms_ where your _environments_ actually +run. They can be cloud-based or on-premises solutions that provide the +infrastructure to run the webserver, database, and other services required for +your site. -Hosting providers typically provide DEV, STAGE, and PROD environments. +**Environments** are the contained _bundles_ of those infrastructure services +that run your website. An environment represents a specific logical stage of +your website deployment and releasing lifecycle: from local development to +production. Multiple environments can exist on the same hosting provider. -Some providers also support additional short-lived ephemeral environments also -called "preview", "on-demand", or "feature" environments. These help to test -new features or changes in isolation before merging them into the main codebase. +Hosting providers typically provide DEV, STAGE, and PROD environments. Some +also support short-lived ephemeral environments - called "preview", +"on-demand", or "feature" environments - that help to test new features or +changes in isolation before merging them into the main codebase. -**Vortex** provides a mechanism to detect the environment type and allows you to -configure your website behavior based on this environment type. +**Vortex** detects the environment type at runtime and lets you configure your +website behavior based on it. -➡️ See [Drupal > Settings > Environment type detection](../drupal/settings#environment-type-detection) +➡️ See [Drupal > Settings > Environment type detection](../drupal/settings.mdx#environment-type-detection) diff --git a/.vortex/docs/content/hosting/acquia.mdx b/.vortex/docs/content/hosting/acquia.mdx index 476350358..7caf31417 100644 --- a/.vortex/docs/content/hosting/acquia.mdx +++ b/.vortex/docs/content/hosting/acquia.mdx @@ -1,23 +1,26 @@ --- -sidebar_position: 1 +sidebar_position: 2 --- # Acquia [Acquia](https://www.acquia.com/) is a cloud hosting platform specifically -designed for Drupal applications, offering managed infrastructure, developer -tools, and enterprise-grade security. +designed for Drupal applications, offering managed infrastructure and developer +tools. For general Acquia documentation, refer to the [Acquia Documentation](https://docs.acquia.com/). +This page covers how the site behaves once it runs on Acquia. For how code gets +there from CI, see [Artifact deployment](../deployment/artifact.mdx). + ## Integration -Vortex provides the following integration with Acquia: +**Vortex** provides the following integration with Acquia: ### Tasks -With Vortex, you can: +With **Vortex**, you can: - **Fetch a database** from an Acquia environment for local development or CI - **Copy the database** between Acquia environments (e.g., refresh staging from production) @@ -29,7 +32,7 @@ source for the application name and API credentials. ### Deployment automation -When code is deployed, Vortex automatically: +When code is deployed, **Vortex** automatically: 1. **Provisions the site** - Runs database updates, imports configuration, clears caches 2. **Purges edge cache** - Clears the Varnish cache to serve fresh content @@ -50,18 +53,18 @@ and environment variables. #### Acquia settings file -By default, Vortex includes the Acquia-provided settings file from +By default, **Vortex** includes the Acquia-provided settings file from `/var/www/site-php/{group}/{group}-settings.inc`. You can override this path by setting the `DRUPAL_ACQUIA_SETTINGS_FILE` environment variable. #### Temporary file path -Vortex configures the temporary file path (`file_temp_path`) with a three-tier -priority: +**Vortex** configures the temporary file path (`file_temp_path`) with a +three-tier priority: 1. **Default**: `/tmp` 2. **Shared GFS mount**: If `DRUPAL_TMP_PATH_IS_SHARED` is set, uses - `/mnt/gfs/{group}.{env}/tmp` — a per-head mounted directory on Acquia's + `/mnt/gfs/{group}.{env}/tmp` - a per-head mounted directory on Acquia's shared filesystem. This is useful for operations like bulk uploads that require a shared temporary directory across web heads. See [Acquia temporary files documentation](https://docs.acquia.com/acquia-cloud-platform/manage-apps/files/temporary#section-important-considerations) @@ -72,10 +75,10 @@ priority: ## Onboarding Acquia onboarding is part of the project setup flow. See -[Set up integrations](/docs/installation?hosting-provider=acquia#4-set-up-hosting) +[Set up hosting](/docs/installation?hosting-provider=acquia#4-set-up-hosting) in the Installation guide and select **Acquia**. -## Routine Operations +## Routine operations ### Fetch database diff --git a/.vortex/docs/content/hosting/lagoon.mdx b/.vortex/docs/content/hosting/lagoon.mdx index c91b69c19..37bb77293 100644 --- a/.vortex/docs/content/hosting/lagoon.mdx +++ b/.vortex/docs/content/hosting/lagoon.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 2 +sidebar_position: 3 --- # Lagoon @@ -11,16 +11,19 @@ Kubernetes. For general Lagoon documentation, refer to the [Lagoon Documentation](https://docs.lagoon.sh/). +This page covers how the site behaves once it runs on Lagoon. For how code gets +there from CI, see [Lagoon deployment](../deployment/lagoon.mdx). + ## Integration -Vortex provides the following integration with Lagoon: +**Vortex** provides the following integration with Lagoon: ### Tasks -With Vortex, you can: +With **Vortex**, you can: - **Fetch a database** from a Lagoon environment for local development or CI -- **Trigger deployments** to Lagoon environments +- **Trigger deployments** to Lagoon environments - see [Lagoon deployment](../deployment/lagoon.mdx) - **Run custom commands** on Lagoon environments When running these tasks, your `.env` and `.env.local` files are used as the @@ -28,15 +31,14 @@ source for the project name and SSH configuration. ### Deployment automation -When code is deployed, Vortex automatically: +When code is deployed, **Vortex** automatically: 1. **Provisions the site** - Runs database updates, imports configuration, clears caches 2. **Sends notifications** - Notifies configured channels about the deployment This is implemented using post-rollout tasks defined in the [`.lagoon.yml`](https://github.com/drevops/vortex/blob/main/.lagoon.yml) -configuration file, which also defines Docker image mappings and -environment-specific configurations. +configuration file, which also defines per-environment cron jobs and routes. ### Environment detection @@ -49,10 +51,10 @@ and environment variables provided by the platform. ## Onboarding Lagoon onboarding is part of the project setup flow. See -[Set up integrations](/docs/installation?hosting-provider=lagoon#4-set-up-hosting) +[Set up hosting](/docs/installation?hosting-provider=lagoon#4-set-up-hosting) in the Installation guide and select **Lagoon**. -## Routine Operations +## Routine operations ### Fetch database @@ -91,3 +93,19 @@ always the fallback when no reusable dump is available. Force a new dump with: ```shell ahoy fetch-db --fresh ``` + +### Run a custom task + +Run an arbitrary command in a Lagoon environment through the platform-agnostic +task runner, which drives the Lagoon CLI: + +```shell +VORTEX_PLATFORM=lagoon \ +VORTEX_TASK_CUSTOM_LAGOON_BRANCH=develop \ +VORTEX_TASK_CUSTOM_LAGOON_COMMAND="drush cron" \ +./vendor/bin/vortex-task custom +``` + +The task reads the project name and SSH configuration from your `.env` and +`.env.local` files; `VORTEX_TASK_CUSTOM_LAGOON_BRANCH` selects the environment +and `VORTEX_TASK_CUSTOM_LAGOON_COMMAND` is the command to run there. diff --git a/.vortex/docs/content/releasing/README.mdx b/.vortex/docs/content/releasing/README.mdx index 5a675e2bd..4d9f3920f 100644 --- a/.vortex/docs/content/releasing/README.mdx +++ b/.vortex/docs/content/releasing/README.mdx @@ -5,128 +5,99 @@ sidebar_position: 1 # Releasing -Software releases are a critical part of the development lifecycle. A well-structured release process ensures code quality, minimizes deployment risks, and maintains clear communication across teams. +This section describes how **your project** - a site built from **Vortex** - +releases to production. (How the **Vortex** template itself is released is a +different process, documented in +[Contributing > Maintenance > Release](../contributing/maintenance/release.mdx).) -A typical release process consists of several key components: +A release process has a few moving parts, and **Vortex** provides defaults and +integration points for each of them: -1. **Release Manager** - A person or team responsible for coordinating and executing releases -2. **Release Flow** - The sequence of environments and steps code goes through from development to production -3. **Versioning Workflow** - The branching strategy that governs how code moves through environments (GitFlow being the most common) -4. **Version Scheme** - The numbering system used to identify releases (CalVer, SemVer, or custom) -5. **Release Documentation** - Runsheets, checklists, and procedures stored in accessible locations -6. **Automated Deployment** - CI/CD pipelines that handle the technical deployment process - -**Vortex** provides comprehensive support for all these components, with sensible defaults and integration points for popular hosting platforms. +1. **Release flow** - the sequence of environments code goes through from development to production +2. **Versioning workflow** - the branching strategy, documented in [GitFlow](gitflow.mdx) +3. **Version scheme** - the numbering system, chosen at install time and documented in [Versioning](versioning.mdx) +4. **Release documentation** - your project's own `docs/releasing.md` +5. **Automated deployment** - the CI pipeline and hosting integrations that do the technical work ## Release flow -Projects typically follow a three-tier environment strategy with clear directional flows: - -```mermaid -graph LR - Dev[Development] -->|code| Stage[Stage] - Stage -->|code| Prod[Production] - Prod -.->|database| Stage - Stage -.->|database| Dev +Projects typically follow a 3-tier environment strategy. Code moves "up" +through the environments while the database moves "down": - style Dev fill:#e1f5ff - style Stage fill:#fff4e1 - style Prod fill:#e8f5e9 +```text + code: Development ───► Stage ───► Production + database: Development ◄─── Stage ◄─── Production ``` -- **Development** - Latest development code, not yet released. May be unstable, but CI tests should pass. -- **Stage** - Pre-production environment. Mirrors production as closely as possible. Used for final release testing. -- **Production** - Live customer-facing application. Stable and reliable. Source of truth for data. - -:::info Environment Agreement -The specific environment setup (dev/stage/production) should be agreed upon within your team and documented in your project. Some teams may use additional environments (e.g., UAT, pre-prod) or different naming conventions. -::: +- **Development** - latest development code, not yet released. May be unstable, but CI tests should pass. +- **Stage** - pre-production environment. Mirrors production as closely as possible. Used for final release testing. +- **Production** - the live customer-facing application. Stable and reliable. Source of truth for data. -Code goes "up" (from lower to higher environments) while database goes "down" (from higher to lower environments). +The production database is the primary source of truth - it is what code is +applied to. When performing a release, you are applying a new version of code +to a database within a specific environment. To make sure code changes work +with real data, lower environments test against a copy of the production +database: -This means that the production database is the primary source of truth - it's what code is applied to. When performing a release, you are applying a new version of code to a database within a specific environment. +1. The database is copied from a higher environment to a lower one (e.g., production → stage → development). +2. Code is deployed to that environment. +3. Testing confirms everything works against the copied database. -To ensure that code changes work correctly with real data structures, the following process is followed in lower environments: +:::tip -1. Database is copied from a higher environment to a lower one (e.g., production → stage → development) -2. Code is deployed to that environment to be tested against the copied database -3. Testing is performed to ensure everything works correctly +CI pipelines use a copy of the production database (refreshed daily) to run +all tests, so code changes are validated against real data structures too. -:::tip -CI pipelines also use a copy of the production database (refreshed daily) to run all tests, ensuring code changes work with real data structures. ::: -You would use a Versioning Workflow (like GitFlow) to manage how code moves -across releases using the Version Scheme (like CalVer or SemVer). +:::info Environment agreement -You would document your release procedures in Release Documentation (like -`docs/releasing.md`) and create a release runsheet to guide release managers -through the release process. +The specific environment setup (dev/stage/production) should be agreed within +your team and documented in your project. Some teams use additional +environments (e.g., UAT, pre-prod) or different naming conventions. -Finally, the actual deployment to production is handled via Automated Deployment -where, based on your hosting provider, the deployment process is fully automated. +::: ## Production deployment process -Once code is finalized and pushed, the deployment process to production is technically identical to deploying to other environments and is **fully automated**. +Once code is finalized and pushed, deployment to production is technically +identical to deploying to any other environment and is fully automated. -For Acquia and Lagoon hosting, **Vortex** integrates directly with their deployment systems to ensure smooth releases. +For Acquia and Lagoon hosting, **Vortex** integrates directly with their +deployment systems. For other hosting providers, you can integrate the +provision steps into your hosting deployment configuration if it supports +post-deployment hooks or custom scripts. -For other hosting providers, you can integrate the provision steps into your hosting deployment configuration if it supports post-deployment hooks or custom scripts. - -➡️ See [Drupal > Provision](/docs/drupal/provision#provisioning-flow) to learn more about what provisioning steps are performed during deployments. +➡️ See [Drupal > Provision](../drupal/provision.mdx#provisioning-flow) to learn +what provisioning steps run during deployments. ## Documentation ### `docs/releasing.md` -Your project includes a `docs/releasing.md` file that serves as the canonical release documentation for your team. This file contains: - -- **Version scheme summary** - Which versioning system your project uses (CalVer, SemVer, or Other) -- **GitFlow instructions** - Step-by-step guide for your specific git-flow setup -- **Release procedures** - Custom procedures specific to your project +Your project includes a `docs/releasing.md` file that serves as the canonical +release documentation for your team. As shipped, it records the expected +release outcome and your project's version scheme, and leaves a marked section +for project-specific configuration. -You can extend this file with: +Extend it with: -- **Detailed release procedures** - A comprehensive outline of _what_ actions to take during releases: - - Steps to create and finish releases - - Steps to deploy to production - - Steps to rollback - - Steps to verify releases -- **Release run template** - A detailed checklist of _who_ does _what_ and _when_ during releases. This would be cloned into a separate runsheet for each release. +- **Detailed release procedures** - an outline of _what_ actions to take during releases: creating and finishing releases, deploying to production, rolling back, verifying. +- **A release run template** - a checklist of _who_ does _what_ and _when_ during a release, cloned into a separate runsheet for each release. ## Monitoring ### New Relic integration -**Vortex** provides integration with New Relic for release tracking and monitoring. - -**Deployment Markers**: When configured, **Vortex** automatically creates deployment markers in New Relic when releases are deployed to your environments. These markers help correlate performance changes, errors, and other metrics with specific releases. - -**Benefits**: - -- Visualize before/after release performance -- Correlate errors with specific deployments -- Track deployment frequency -- Monitor release impact in real-time - -➡️ See [Deployment > Notifications](/docs/deployment/notifications) - -## Best practices +**Vortex** integrates with New Relic for release tracking: when configured, it +creates deployment markers as releases reach your environments, so performance +changes and errors can be correlated with specific releases. -1. **Always use release branches** - Never release directly from `develop` or feature branches -2. **Backup before release** - Ensure you have recent backups of code and data -3. **Document your process** - Keep `docs/releasing.md` updated with team conventions -4. **Automate everything possible** - Leverage **Vortex**'s automation capabilities -5. **Test in Stage first** - Always validate releases in a production-like environment -6. **Communicate releases** - Notify stakeholders before, during, and after releases -7. **Monitor post-release** - Watch metrics and logs for issues after deployment -8. **Have a rollback plan** - Document and test your rollback procedures -9. **Use consistent versioning** - Stick to your chosen version scheme +➡️ See [Deployment > Notifications](../deployment/notifications.mdx#new-relic) ## See also | Topic | Description | |-------|-------------| -| [GitFlow](gitflow) | Branch structure and release operations | -| [Versioning](versioning) | CalVer, SemVer, and custom version schemes | +| [GitFlow](gitflow.mdx) | Branch structure and release operations | +| [Versioning](versioning.mdx) | CalVer, SemVer, and custom version schemes | diff --git a/.vortex/docs/content/releasing/gitflow.mdx b/.vortex/docs/content/releasing/gitflow.mdx index acb2bca76..3b17c1304 100644 --- a/.vortex/docs/content/releasing/gitflow.mdx +++ b/.vortex/docs/content/releasing/gitflow.mdx @@ -6,58 +6,43 @@ sidebar_position: 2 # GitFlow versioning workflow [git-flow](https://nvie.com/posts/a-successful-git-branching-model/) is a -versioning workflow that allows you to maintain clean separation between development -and production code. - -It allows you to have a stable development branch (`develop`) and a production-ready -branch (`main`), while providing dedicated branches for feature development, -release preparation, and hotfixes. - -```mermaid -gitGraph - commit id: "Initial" - branch develop - checkout develop - commit id: "Feature work" - branch feature/new-feature - checkout feature/new-feature - commit id: "Feature commits" - checkout develop - merge feature/new-feature - commit id: "More work" - branch release/1.0.0 - checkout release/1.0.0 - commit id: "Version bump" - commit id: "Bug fixes" - checkout main - merge release/1.0.0 tag: "v1.0.0" - checkout develop - merge release/1.0.0 - checkout develop - commit id: "Continue dev" - checkout main - branch hotfix/1.0.1 - checkout hotfix/1.0.1 - commit id: "Critical fix" - checkout main - merge hotfix/1.0.1 tag: "v1.0.1" - checkout develop - merge hotfix/1.0.1 +versioning workflow that separates development code from production code. + +You keep a stable development branch (`develop`) and a production-ready +branch (`main`), with dedicated branches for feature development, release +preparation, and hotfixes. + +```text +develop ──► git flow release start X.Y.Z + │ + ▼ + release/X.Y.Z + (final fixes, release notes) + │ + ▼ + git flow release finish X.Y.Z + │ + ├──► merges into `main` and tags `X.Y.Z` + ├──► merges back into `develop` + │ + ▼ + deploy the `X.Y.Z` tag + (or push `main` to `production` for branch-based hosting) ``` ## Branch structure -- **`develop`** - Development branch where features are integrated -- **`main`** - Production-ready code, always stable and tagged with releases -- **`feature/*`** - Individual feature development branches -- **`release/*`** - Release preparation branches (e.g., `release/25.1.0`) -- **`hotfix/*`** - Emergency fixes for production (e.g., `hotfix/25.1.1`) +- **`develop`** - development branch where features are integrated +- **`main`** - production-ready code, always stable and tagged with releases +- **`feature/*`** - individual feature development branches +- **`release/*`** - release preparation branches (e.g., `release/25.1.0`) +- **`hotfix/*`** - emergency fixes for production (e.g., `hotfix/25.1.1`)
`production` branch Most hosting providers support deploying specific git tags directly. In this - case, no separate `production` branch is needed - you simply tag releases on + case, no separate `production` branch is needed - you tag releases on `main` and deploy those tags. Some hosting providers (like **Lagoon**) require a git branch to deploy from, @@ -66,16 +51,18 @@ gitGraph release. While it's possible to automate copying `main` to `production` on tag creation - via CI/CD, this automation was conceptually avoided to prevent accidental + via CI/CD, this automation was deliberately avoided to prevent accidental deployments to production without human oversight.
## Release operations -Below are the typical steps to perform a release using git-flow. See [cheat sheet](https://danielkummer.github.io/git-flow-cheatsheet/) for a quick reference on git-flow commands. +Below are the typical steps to perform a release using git-flow. See the +[cheat sheet](https://danielkummer.github.io/git-flow-cheatsheet/) for a quick +reference on git-flow commands. -1. **Start Release** +1. **Start the release** ```shell git flow release start X.Y.Z @@ -84,25 +71,25 @@ Below are the typical steps to perform a release using git-flow. See [cheat shee Creates a `release/X.Y.Z` branch from `develop`. It is recommended to push the branch to remote. -2. **Release Preparation** +2. **Prepare the release** - Final bug fixes - Documentation updates - Release notes preparation -3. **Finish Release** +3. **Finish the release** ```shell git flow release finish X.Y.Z ``` - - Merges release branch to `main` + - Merges the release branch to `main` - Tags the release - Merges back to `develop` - Deletes the release branch -4. **Deploy to Production** - - **Tag-based hosting:** Deploy the tag directly - - **Branch-based hosting (e.g., Lagoon):** Manually sync to `production` branch +4. **Deploy to production** + - **Tag-based hosting:** deploy the tag directly + - **Branch-based hosting (e.g., Lagoon):** manually sync to the `production` branch ```shell git push origin main:production @@ -110,12 +97,15 @@ Below are the typical steps to perform a release using git-flow. See [cheat shee ## Expected release outcome -A successful release should meet these criteria: +A successful release meets these criteria (they also ship in your project's +`docs/releasing.md`): -1. Release branch exists as `release/X.Y.Z` in GitHub repository -2. Release tag exists as `X.Y.Z` in GitHub repository -3. The `HEAD` of the `main` branch has `X.Y.Z` tag +1. Release branch exists as `release/X.Y.Z` in the GitHub repository +2. Release tag exists as `X.Y.Z` in the GitHub repository +3. The `HEAD` of the `main` branch has the `X.Y.Z` tag 4. The hash of the `HEAD` of the `main` branch exists in the `develop` branch - This ensures everything pushed to `main` exists in `develop` - Important if `main` had any hotfixes not yet merged to `develop` 5. There are no open PRs in GitHub related to the release +6. On branch-based hosting, the hash of the `HEAD` of the `production` branch + matches the hash of the `HEAD` of the `main` branch diff --git a/.vortex/docs/content/releasing/versioning.mdx b/.vortex/docs/content/releasing/versioning.mdx index fc3a38eed..cff5fbad0 100644 --- a/.vortex/docs/content/releasing/versioning.mdx +++ b/.vortex/docs/content/releasing/versioning.mdx @@ -20,13 +20,13 @@ During installation, you can choose between Calendar Versioning (CalVer), Semant ### Why CalVer - **Release frequency transparency**: When you have multiple releases per month, dates make it easy to identify when a release happened -- **Intuitive tracking**: Stakeholders can immediately understand "this is from January 2025" vs memorizing version numbers -- **Natural progression**: No ambiguity about major vs minor changes - just the chronological order -- **Marketing alignment**: Easier to communicate to non-technical audiences ("our Q1 2025 release") +- **Intuitive tracking**: Stakeholders can immediately understand "this is from January 2025" without memorizing version numbers +- **Natural progression**: No ambiguity about major vs minor changes - the order is chronological +- **Stakeholder communication**: Easier to reference for non-technical audiences ("our Q1 2025 release") ### Examples -- ✅ Correct: `25.1.0`, `25.11.1`, `25.1.10`, `25.10.1`, `9.12.0` +- ✅ Correct: `25.1.0`, `25.11.1`, `25.1.10`, `25.10.1` - ❌ Incorrect: `25.0.0` (no month 0), `2025.1.1` (full year), `25.01.0` (leading zero), `01.1.0` (leading zero in year) Learn more: [CalVer.org](https://calver.org/) @@ -67,9 +67,10 @@ VORTEX_RELEASE_VERSION_SCHEME=calver # or semver, or other ### Release notes publishing -For CalVer and SemVer projects, **Vortex** provides a GitHub Actions workflow to automate release notes drafting: +**Vortex** provides a GitHub Actions workflow to automate release notes drafting: - **Draft release notes** are automatically updated when commits are pushed to the `develop` branch -- Next version is calculated based on your version scheme +- The next version number is proposed per your scheme: computed from the current date for CalVer, and resolved by [release-drafter](https://github.com/release-drafter/release-drafter) (minor increment by default) for SemVer +- The workflow reads the scheme from the `VORTEX_RELEASE_VERSION_SCHEME` **repository variable**, defaulting to `calver` when the variable is unset - set it in your repository settings to match your project's scheme, since the `.env` value does not drive this workflow - Release notes accumulate changes until the release is finalized - On release finish, you can use the draft to publish the final release notes diff --git a/.vortex/docs/content/support.mdx b/.vortex/docs/content/support.mdx index ffd54f094..e6bca9a62 100644 --- a/.vortex/docs/content/support.mdx +++ b/.vortex/docs/content/support.mdx @@ -1,90 +1,78 @@ --- -description: Get help with Vortex - multiple support channels and resources available +description: Get help with Vortex - support channels and how to report issues --- # Support -Get help with **Vortex** through our comprehensive support channels and resources. +Get help with **Vortex** through the channels below. -:::tip **Quick Start** -New to **Vortex**? Start with our [Installation Guide](/docs/installation) and then explore the [Features](/docs/features). -::: - -## 🛟 Getting Help +:::tip Quick start -### Quick Self-Help +New to **Vortex**? Start with the [Installation guide](/docs/installation) and +then explore the [Features](/docs/features). -Start with these resources to solve common issues quickly: +::: -- 📖 **[Documentation](/docs)** - Comprehensive guides and references -- ❓ **[FAQs](/docs/faqs)** - Frequently asked questions -- 🩺 **Doctor Tool** - Run `ahoy doctor` to diagnose common issues +## 🛟 Getting help -### Community Support +### Self-help -#### Slack Community +Start with these resources to solve common issues: -- Join the [`#vortex-project-template`](https://drupal.slack.com/archives/CRE86HQTW) Slack channel -- Get help from the community and core maintainers -- Share ideas and collaborate with other users +- 📖 **[Documentation](/docs)** - guides and references for every subsystem +- ❓ **[FAQs](/docs/faqs)** and **[Development FAQs](/docs/development/faqs)** - answers to adoption and day-to-day questions +- 🩺 **[Doctor](/docs/tools/doctor)** - run `ahoy doctor` to diagnose common local environment issues -#### GitHub Discussions +### Community support -- [GitHub Issues](https://github.com/drevops/vortex/issues) for bug reports and feature requests -- [Project Board](https://github.com/orgs/drevops/projects/2/views/1) to track development progress -- Check existing issues before submitting new ones +- Join the [`#vortex-project-template`](https://drupal.slack.com/archives/CRE86HQTW) Slack channel to get help from the community and core maintainers +- Use [GitHub issues](https://github.com/drevops/vortex/issues) for bug reports and feature requests +- Track development progress on the [project board](https://github.com/orgs/drevops/projects/2/views/1) -### Professional Support +### Professional support -For organizations requiring dedicated support, we offer professional services: +For organizations requiring dedicated support, DrevOps offers professional +services: -- **New project onboarding** - Get started with expert guidance -- **Existing project integration** - Migrate your current projects to **Vortex** -- **Support plans with SLAs** - Guaranteed response times -- **Priority feature implementation** - Fast-track your requirements -- **Version updates and migrations** - Seamless upgrade assistance -- **Custom implementations** - Tailored solutions for your needs +- New project onboarding +- Migrating existing projects to **Vortex** +- Support plans with guaranteed response times +- Priority feature implementation +- Version updates and migrations +- Custom implementations 📧 **Email:** [support@drevops.com](mailto:support@drevops.com) -## 🚨 Reporting Issues +## 🚨 Reporting issues -### Before You Report +Before you report: -1. Check existing issues on [GitHub](https://github.com/drevops/vortex/issues) +1. Check the existing issues on [GitHub](https://github.com/drevops/vortex/issues) 2. Run diagnostics with `ahoy doctor` 3. Review the [FAQs](/docs/faqs) for common solutions 4. Test with a clean environment if possible -### How to Report +When reporting, include: -When reporting issues, please include: - -- System information (`ahoy doctor info`) +- System information (`ahoy doctor info` - its output is redacted and safe to share) - Steps to reproduce the problem - Error messages and logs -- **Vortex** version and configuration details - -## 🔒 Security Issues +- Your **Vortex** version and configuration details -For security vulnerabilities, please email us directly: [support@drevops.com](mailto:support@drevops.com) +## 🔒 Security issues -*Do not report security issues in public GitHub issues.* +For security vulnerabilities, email us directly at +[support@drevops.com](mailto:support@drevops.com). Do not report security +issues in public GitHub issues. ## 🤝 Contributing -See our [Contributing Guide](/docs/contributing/). +See the [Contributing guide](/docs/contributing/). -## 💖 Supporting the Project +## 💖 Supporting the project Help sustain **Vortex** development: - ⭐ Star the project on [GitHub](https://github.com/drevops/vortex) - 📢 Spread the word to help others discover **Vortex** -- 💝 [GitHub Sponsors](https://github.com/sponsors/drevops) - One-time or recurring donations - ---- - -:::info **Enterprise Users** -Looking for enterprise-grade support? Contact us at [support@drevops.com](mailto:support@drevops.com) for custom support plans and professional services. -::: +- 💝 Donate via [GitHub Sponsors](https://github.com/sponsors/drevops) - one-time or recurring diff --git a/.vortex/docs/content/tools/README.mdx b/.vortex/docs/content/tools/README.mdx index ee8adb344..6ecf8811b 100644 --- a/.vortex/docs/content/tools/README.mdx +++ b/.vortex/docs/content/tools/README.mdx @@ -21,6 +21,7 @@ Head over to the tool-specific documentation to learn more. | [Ahoy](ahoy.mdx) | CLI command wrapper | | [Behat](behat.mdx) | Testing framework for auto-testing your business expectations | | [DCLint](dclint.mdx) | A command-line tool for validating and enforcing best practices in Docker Compose files | +| [Diffy](diffy.mdx) | A hosted visual regression testing service | | [Docker](docker.mdx) | A platform for containerizing and running applications | | [Doctor](doctor.mdx) | Check **Vortex** project requirements or retrieve environment information | | [Drush](drush.mdx) | Command line shell and Unix scripting interface for Drupal | @@ -28,7 +29,8 @@ Head over to the tool-specific documentation to learn more. | [Gherkin Lint](gherkin-lint.mdx) | Provides a Gherkin linter for PHP | | [Git artifact](git-artifact.mdx) | Package and push files to remote repositories | | [Gitleaks](gitleaks.mdx) | Detect hardcoded secrets like passwords, API keys, and tokens | -| [Hadolint](hadolint.mdx) | A smarter Dockerfile linter that helps you build best practice container images | +| [Hadolint](hadolint.mdx) | A Dockerfile linter that helps you build best practice container images | +| [Jest](jest.mdx) | JavaScript unit testing framework for custom modules | | [PHPCS](phpcs.mdx) | Check that code adheres to coding standards | | [PHPStan](phpstan.mdx) | PHP Static Analysis Tool | | [PHPUnit](phpunit.mdx) | The PHP Testing Framework | diff --git a/.vortex/docs/content/tools/ahoy.mdx b/.vortex/docs/content/tools/ahoy.mdx index ab7f268f3..a090b9503 100644 --- a/.vortex/docs/content/tools/ahoy.mdx +++ b/.vortex/docs/content/tools/ahoy.mdx @@ -5,10 +5,10 @@ https://github.com/ahoy-cli/ahoy > Automate and organize your workflows, no matter what technology you use. > > Ahoy is command line tool that gives each of your projects their own CLI app -with zero code and dependencies. +> with zero code and dependencies. -Usually, Ahoy is used to wrap the commands to make the development process -consistent and easy to use. +Ahoy wraps longer commands into short, memorable ones, so the development +process stays consistent across projects and team members. **Vortex** comes with [pre-configured `.ahoy.yml`](https://github.com/drevops/vortex/blob/main/.ahoy.yml) that has commands wrapped around the most common tasks: @@ -45,20 +45,19 @@ Adding a new command with passed arguments: ```yml commands: custom: - cmd: .vendor/bin/phpunit --group="smoke" "$@" + cmd: vendor/bin/phpunit --group="smoke" "$@" ``` -Adding a new multi-line command with usage details and calling another Ahoy -commands after confirmation: +Adding a new multi-line command with usage details that calls other Ahoy +commands after a confirmation: ```yml - commands: custom: usage: Custom action cmd: | ahoy confirm "Are you sure?" && - .vendor/bin/phpunit --group="smoke" "$@" && + vendor/bin/phpunit --group="smoke" "$@" && ahoy info ``` diff --git a/.vortex/docs/content/tools/behat.mdx b/.vortex/docs/content/tools/behat.mdx index a2db3fa3b..978e16266 100644 --- a/.vortex/docs/content/tools/behat.mdx +++ b/.vortex/docs/content/tools/behat.mdx @@ -44,12 +44,12 @@ import TabItem from '@theme/TabItem'; -**Running tagged tests with `@group_name` tag** +### Running tagged tests with a `@group_name` tag ```shell - ahoy test-bdd --tags=group_name + ahoy test-bdd -- --tags=group_name ``` @@ -112,29 +112,32 @@ Screenshots for failed tests or purposely made screenshots are stored in Both test results and screenshots are stored as artifacts when run in continuous integration pipelines. -### Profiles +## Profiles -Behat runs with `default` profile defined in the configuration file: this runs -all tests not tagged with `@skipped` tag. +Behat runs with the `default` profile defined in the configuration file: this +runs all tests not tagged with the `@skipped` tag. -There are also `p1` and `p2` profiles defined that run tests not tagged with -`@skipped` and tagged with `@p1` and `@p2` tags respectively. These profiles are -used in continuous integration pipeline to run tests in parallel if parallel -runners number is greater than `1`. -Behat will run tests using `@pX` tags on tests, where `X` is the runner index -starting from `0`. +There are also `p0` and `p1` profiles defined, used in the continuous +integration pipeline to run tests in parallel when the number of runners is +greater than `1`. Behat picks the profile named after the runner index, +starting from `0`: -In the example below, if there is one runner, Behat will run all scenarios, but -if there are 2 runners - Behat will run only scenarios with `@p0` tag on the -first runner and scenarios with `@p1` tag on the second runner. +- `p0` (the first runner) is the catch-all: it runs every scenario **not** + tagged `@p1`, plus all `@smoke` scenarios. +- `p1` (the second runner) runs scenarios tagged `@p1`, plus all `@smoke` + scenarios. + +In the example below, with one runner Behat runs both scenarios; with 2 +runners the first scenario runs on the first runner (untagged scenarios stay +there) and the second scenario runs on the second runner: ```gherkin -@homepage @smoke +@homepage Feature: Homepage Ensure that homepage is displayed as expected. - @api @p0 + @api Scenario: Anonymous user visits homepage Given I go to the homepage And I should be in the "" path @@ -147,7 +150,7 @@ Feature: Homepage Then I save screenshot ``` -The profile can be overridden using `$VORTEX_CI_BEHAT_PROFILE` environment +The profile can be overridden using the `$VORTEX_CI_BEHAT_PROFILE` environment variable set in the continuous integration pipeline configuration. ## Writing tests diff --git a/.vortex/docs/content/tools/dclint.mdx b/.vortex/docs/content/tools/dclint.mdx index b144c1b1a..37fe5985f 100644 --- a/.vortex/docs/content/tools/dclint.mdx +++ b/.vortex/docs/content/tools/dclint.mdx @@ -4,7 +4,14 @@ https://github.com/zavoloklom/docker-compose-linter > A command-line tool for validating and enforcing best practices in Docker Compose files. -**Vortex** does not install DCLint. Please follow the [instructions](https://github.com/zavoloklom/docker-compose-linter#installation) to install it on your system. +:::note + +**Vortex** does not install DCLint locally. Follow the [instructions](https://github.com/zavoloklom/docker-compose-linter#installation) +to install it on your system. + +In CI, DCLint runs from its official Docker image. + +::: DCLint is selected by default during installation. Deselecting it in the `Development tools` question removes the `.dclintrc` configuration file and the @@ -13,35 +20,42 @@ continuous integration step from every supported provider. ## Usage ```shell -dclint docker-compose.yml +dclint . ``` +## Configuration + +Global configuration takes place in the [`.dclintrc`](https://github.com/drevops/vortex/blob/main/.dclintrc) +file. Beyond the defaults, it defines a `service-keys-order` rule group that +keeps the keys of every service in `docker-compose.yml` in a consistent order, +so diffs stay small and services stay easy to compare. + ## Ignoring To ignore **all DCLint rules** within a file, place in the file header: ```yaml # dclint disable -version: '3.8' services: - app: - image: ubuntu + cli: + build: . ``` -To ignore only **specific rules**: +To ignore **specific rules** for a single line, place a `disable-line` +directive on its own line immediately above it: ```yaml -version: '3.8' services: - # dclint disable-next-line no-version-field - app: - image: ubuntu + nginx: + ports: + # dclint disable-line require-quotes-in-ports + - 8080:8080 ``` ## Ignoring fail in continuous integration pipeline -This tool runs in continuous integration pipeline by default and fails the build -if there are any violations. +This tool runs in the continuous integration pipeline by default and fails the +build if there are any violations. -Set `VORTEX_CI_DCLINT_IGNORE_FAILURE` environment variable to `1` to ignore +Set the `VORTEX_CI_DCLINT_IGNORE_FAILURE` environment variable to `1` to ignore failures. The tool will still run and report violations, if any. diff --git a/.vortex/docs/content/tools/diffy.mdx b/.vortex/docs/content/tools/diffy.mdx index 4998092fc..7c4b09ae5 100644 --- a/.vortex/docs/content/tools/diffy.mdx +++ b/.vortex/docs/content/tools/diffy.mdx @@ -21,9 +21,8 @@ environment), and surfaces the per-page differences in a web UI. page and per breakpoint. - **DOM manipulation** - mask, hide, or replace elements before screenshots (useful for hiding ads, popups, dynamic timestamps). -- **CI-friendly CLI** - the - [`diffy-cli`](https://github.com/DiffyWebsite/diffy-cli) PHAR can be driven - from any CI runner or shell script. +- **CLI** - the [`diffy-cli`](https://github.com/DiffyWebsite/diffy-cli) PHAR + can be driven from any CI runner or shell script. - **REST API** - all CLI operations are available as direct HTTP calls. ## Account and project setup @@ -38,12 +37,13 @@ environment), and surfaces the per-page differences in a web UI. The numeric **project ID** is visible in the Diffy project URL and is needed by any automation that talks to the Diffy API. -## Pricing +:::note -Diffy bills per screenshot set. Each comparison consumes one screenshot set -per environment compared (so a "production vs. PR env" comparison consumes -two). Plan accordingly when wiring it into CI - frequent dispatches against -a chatty repository can exhaust a plan quickly. +Diffy plan usage depends on the screenshots captured. Reuse a saved baseline +screenshot set where possible, and limit which branches trigger comparisons +when wiring it into CI - frequent automated dispatches consume a plan quickly. + +::: ## Vortex integration diff --git a/.vortex/docs/content/tools/docker.mdx b/.vortex/docs/content/tools/docker.mdx index 6822fd949..9e0aa3bc3 100644 --- a/.vortex/docs/content/tools/docker.mdx +++ b/.vortex/docs/content/tools/docker.mdx @@ -15,8 +15,8 @@ that make this possible. :::info -Lagoon images are **production-grade** and are used in production environments. -They receive regular updates and are maintained by the Lagoon team. +Lagoon images run in production environments, receive regular updates, and are +maintained by the Lagoon team. If you are using Lagoon as your hosting provider, then all of your environments are using identical images to run the project. @@ -32,31 +32,28 @@ see the official documentation. ::: -Docker is a technology that allows to define services such as a web server or -a database server as standalone _containers_, which are then run in an isolated -environment and can talk to each other. +Docker lets you define services such as a web server or a database server as +standalone _containers_, which then run in an isolated environment and can talk +to each other. The containers are started from _images_ - templates that define what is -installed -in the container and how it is configured. _Images_ allow to run _containers_ +installed in the container and how it is configured - so every container starts with consistent content and configuration. -Docker is an engine that runs containers, built from images, allowing them to -share host system resources and communicate to each other. When run locally, -Docker can be controlled with Docker CLI command, called `docker`. +Docker is the engine that runs containers built from images, letting them share +host system resources and communicate with each other. When run locally, Docker +is controlled with the Docker CLI command, `docker`. -Docker Compose is a tool that allows to define and run multi-container Docker -applications in a single `docker-compose.yml` file: multiple containers that -work together can be described in a single file, which makes it easier to -manage them. +Docker Compose describes a multi-container Docker application in a single +`docker-compose.yml` file: all the containers that work together are defined in +one place, which makes them easier to manage. -When working with Drupal, which requires multiple service containers to run, a -developer would normally use Docker Compose CLI commands (rather than Docker CLI -commands) called `docker compose`. Note that this -commands runs in the context of the current directory, so it is important to -run them from the project root directory. This means that the issued commands -will only affect the containers defined in the `docker-compose.yml` file in the -current directory and will not affect any other containers running on the host. +When working with Drupal, which requires multiple service containers, a +developer would normally use the Docker Compose CLI (`docker compose`) rather +than the Docker CLI. These commands run in the context of the current +directory, so run them from the project root: they only affect the containers +defined in the `docker-compose.yml` file there, not other containers running on +the host. When a project is fully configured, the usage of Docker-based application comes down to a handful of commands to manage the state of the containers (per @@ -80,7 +77,7 @@ and in the continuous integration pipeline. Some of the commands are wrapped in the Ahoy script as a shorthand. But all the commands can be run directly using `docker compose` command. -Specific commands are described in the relevant [Development](/docs/development) +Specific commands are described in the relevant [Development](../development/README.mdx) documentation. ## Understanding `docker-compose.yml` @@ -114,7 +111,7 @@ There are 2 volumes defined: is where a PHP engine accesses the application code. - Files directory `./web/sites/default/files` maps to `/app/web/sites/default/files` directory within a container as an override - to the default volume definition. This allows to use different type of syncing + to the default volume definition. This allows a different type of syncing to optimize performance, because files are not changed as often as the code. There are 2 more volumes defined and commented out - these are used in @@ -122,7 +119,7 @@ environments without volume mounting support, such as CircleCI. These volumes definitions are automatically uncommented in the continuous integration environment, and they replace the host volume mounting, which is removed. -`VOLUME_FLAGS` environment variable allows to define the consistency of the data +The `VOLUME_FLAGS` environment variable defines the consistency of the data within mounted volumes. The values are: - `default`: Equivalent to `consistent`. @@ -133,8 +130,9 @@ within mounted volumes. The values are: - `delegated`: The container runtime's view of the mount is authoritative. There may be delays before updates made in a container are visible on the host. -The default value is `delegated`, so that any changes done in the container are -immediately visible on the host. +The default value is `delegated`: it prioritizes the container's write +performance for heavy operations like dependency installation, at the cost of +container-made changes taking a moment to appear on the host. ### Default user @@ -142,14 +140,13 @@ The default user is defined as `1000` - this is the user ID that is used in the container to run the application. This is the same user ID as the host user, so that the files created in the container are owned by the host user. -Changes this value if your user ID is different. +Change this value if your user ID is different. ### Environment variables -By default, the Docker Composer reads environment variables from the `.env` -file. **Vortex** provides an additional capability to read files from `.env.local` -file as well. This allows to override the environment variables locally without -modifying the `.env` file. +By default, Docker Compose reads environment variables from the `.env` +file. **Vortex** additionally reads the `.env.local` file, so you can override +environment variables locally without modifying the `.env` file. The variables read from `.env` and `.env.local` files then passed into the containers. @@ -195,12 +192,12 @@ the [official documentation](https://docs.docker.com/compose/environment-variabl Services section describes the configuration for each container. The following services are defined in the `docker-compose.yml` file provided by -Vortex: +**Vortex**: - `cli` - a container that runs a shell. This container is used to run commands in the context of the project, such as `composer` or `drush`. This is also a container where cron jobs are run within a hosting environment (if that - environment supports containerisation). + environment supports containerization). - `webserver` - a container that runs a web server. This container is used to serve the application and pass requests to the appserver container. - `appserver` - a container that runs a PHP engine. This container is used to run the @@ -211,8 +208,12 @@ Vortex: - `database` - a container that runs a database server. This container is used to store the application data. It can be accessed from the host via a randomly assigned port - run `docker compose port database 3306` to get the port number. -- `cache` - an optional container that runs a Redis server. This container is - used to store the application cache. +- `database2` - an optional second database container holding the source + database for [migrations](../drupal/migrations.mdx). It is only present when + the migration feature is enabled. +- `cache` - an optional container that runs a Redis-compatible + ([Valkey](https://valkey.io/)) server. This container is used to store the + application cache. - `search` - an optional container that runs Apache Solr 9 search server. This container is used to store the application search index and provide full-text search capabilities. It uses the `uselagoon/solr-drupal-9` image which includes @@ -259,7 +260,8 @@ Excluded from the image build context: - Host-installed dependencies, which are reinstalled during the build (`vendor`, `node_modules`, `web/themes/**/node_modules`). - Composer-generated Drupal directories, which are regenerated during the build - (`web/core`, `web/modules/contrib`, `drush/Commands/contrib`). + (`web/core`, `web/libraries`, `web/modules/contrib`, `web/profiles/contrib`, + `web/themes/contrib`, `drush/Commands/contrib`). - Content files, caches, logs, and compiled theme assets (`web/sites/*/files`, `.data`, `.logs`, `web/themes/**/build`). @@ -275,7 +277,5 @@ After updating the `docker-compose.yml` file, it is useful sometimes to validate it before running the build. This can be done with the following command: ```shell - docker compose -f docker-compose.yml config - ``` diff --git a/.vortex/docs/content/tools/doctor.mdx b/.vortex/docs/content/tools/doctor.mdx index c52e75f0f..14bb5f1d9 100644 --- a/.vortex/docs/content/tools/doctor.mdx +++ b/.vortex/docs/content/tools/doctor.mdx @@ -1,153 +1,70 @@ # Doctor -Doctor is a standalone, self-contained script designed to -inspect the current state of your project. Its primary functions include -checking project requirements and displaying system information. +Doctor is a standalone, self-contained script that inspects the current state +of your project: it checks the project requirements and displays system +information. It ships as `vendor/bin/vortex-doctor` via the +[`drevops/vortex-tooling`](https://packagist.org/packages/drevops/vortex-tooling) +Composer package. -Also, it runs before and after you build the project with `ahoy build` to make -sure that all the requirements are met. - -## Features - -Doctor script performs a series of checks to ensure the project -environment is correctly set up: - -- Availability of the necessary tools (Docker, Docker Compose, Pygmy, Ahoy etc.) -- Port availability on the host machine -- Pygmy availability -- Container status -- Presence of SSH key within a container -- Webserver status -- Application bootstrap status +Run it first when the local environment misbehaves - it pinpoints the layer +that is broken (tooling, containers, or the site itself) before you start +guessing. ## Checking project status ```shell -$ ahoy doctor +ahoy doctor +``` +Doctor performs a series of checks, each reported on its own line: + +```text [INFO] Checking project requirements [ OK ] All required tools are present. [ OK ] Port 80 is available. [ OK ] Pygmy is running. -[ OK ] All containers are running -[ OK ] SSH key is available within CLI container. +[ OK ] All containers are running. +[ OK ] SSH key is available within the CLI container. [ OK ] Web server is running and accessible at http://vortex.docker.amazee.io. [ OK ] Bootstrapped website at http://vortex.docker.amazee.io. [ OK ] All required checks have passed. ``` +The checks, in order: + +1. **Tools** - Docker, Docker Compose, Pygmy and Ahoy are installed. +2. **Port** - port 80 on the host is either free or held by Docker (skipped on Linux). +3. **Pygmy** - the [Pygmy](pygmy.mdx) services are running. +4. **Containers** - all project containers are running. +5. **SSH** - an SSH key is available within the CLI container. +6. **Webserver** - the web server responds at the local development URL. +7. **Bootstrap** - the Drupal site bootstraps successfully. + +A failing check prints what to do about it (for example, +`Pygmy service is not running. Run 'pygmy up' or 'pygmy restart' to fix.`). + +## Configuration + +Each check can be toggled with a `VORTEX_DOCTOR_CHECK_` variable +(`TOOLS`, `PORT`, `PYGMY`, `CONTAINERS`, `SSH`, `WEBSERVER`, `BOOTSTRAP`) set +to `0` or `1`. + +2 presets combine them: + +- `VORTEX_DOCTOR_CHECK_MINIMAL=1` - only the tools and containers checks; for + environments without Pygmy or a routable web server. +- `VORTEX_DOCTOR_CHECK_PREFLIGHT=1` - only the tools, port and Pygmy checks; + for validating a machine before the stack is up. + ## Getting system information ```shell -$ ahoy doctor info - -System information report - -OPERATING SYSTEM -ProductName: macOS -ProductVersion: 13.1 -BuildVersion: 22C65 - -DOCKER -Path to binary: /usr/local/bin/docker -Docker version 23.0.5, build bc4487a -Client: - Context: default - Debug Mode: false - Plugins: - buildx: Docker Buildx (Docker Inc.) - Version: v0.10.4 - Path: /Users/johndoe/.docker/cli-plugins/docker-buildx - compose: Docker Compose (Docker Inc.) - Version: v2.17.3 - Path: /Users/johndoe/.docker/cli-plugins/docker-compose - dev: Docker Dev Environments (Docker Inc.) - Version: v0.1.0 - Path: /Users/johndoe/.docker/cli-plugins/docker-dev - extension: Manages Docker extensions (Docker Inc.) - Version: v0.2.19 - Path: /Users/johndoe/.docker/cli-plugins/docker-extension - init: Creates Docker-related starter files for your project (Docker Inc.) - Version: v0.1.0-beta.4 - Path: /Users/johndoe/.docker/cli-plugins/docker-init - sbom: View the packaged-based Software Bill Of Materials (SBOM) for an image (Anchore Inc.) - Version: 0.6.0 - Path: /Users/johndoe/.docker/cli-plugins/docker-sbom - scan: Docker Scan (Docker Inc.) - Version: v0.26.0 - Path: /Users/johndoe/.docker/cli-plugins/docker-scan - scout: Command line tool for Docker Scout (Docker Inc.) - Version: v0.10.0 - Path: /Users/johndoe/.docker/cli-plugins/docker-scout - -Server: - Containers: 69 - Running: 51 - Paused: 0 - Stopped: 18 - Images: 460 - Server Version: 23.0.5 - Storage Driver: overlay2 - Backing Filesystem: extfs - Supports d_type: true - Using metacopy: false - Native Overlay Diff: true - userxattr: false - Logging Driver: json-file - Cgroup Driver: cgroupfs - Cgroup Version: 2 - Plugins: - Volume: local - Network: bridge host ipvlan macvlan null overlay - Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog - Swarm: inactive - Runtimes: io.containerd.runc.v2 runc - Default Runtime: runc - Init Binary: docker-init - containerd version: 2806fc1057397dbaeefbea0e4e17bddfbd388f38 - runc version: v1.1.5-0-gf19387a - init version: de40ad0 - Security Options: - seccomp - Profile: builtin - cgroupns - Kernel Version: 5.15.49-linuxkit - Operating System: Docker Desktop - OSType: linux - Architecture: aarch64 - CPUs: 5 - Total Memory: 31.31GiB - Name: docker-desktop - ID: 05da5eb2-2904-49ae-965d-bb10b896e7ac - Docker Root Dir: /var/lib/docker - Debug Mode: false - HTTP Proxy: http.docker.internal:3128 - HTTPS Proxy: http.docker.internal:3128 - No Proxy: hubproxy.docker.internal - Registry: https://index.docker.io/v1/ - Experimental: false - Insecure Registries: - hubproxy.docker.internal:5555 - 127.0.0.0/8 - Live Restore Enabled: false - - -DOCKER COMPOSE V2 -Docker Compose version v2.17.3 - -DOCKER-COMPOSE V1 -Path to binary: /usr/local/bin/docker-compose -WARNING: Compose V1 is no longer supported and will be removed from Docker Desktop in an upcoming release. See https://docs.docker.com/go/compose-v1-eol/ -docker-compose version 1.29.2, build 5becea4c -docker-py version: 5.0.0 -CPython version: 3.9.0 -OpenSSL version: OpenSSL 1.1.1h 22 Sep 2020 - -PYGMY -Path to binary: /Users/johndoe/gems/bin/pygmy -Pygmy version unidentifiable. - -AHOY -Path to binary: /usr/local/bin/ahoy -2.0.2 +ahoy doctor info ``` + +Prints a system information report to attach to a support request: the +operating system version, the Docker, Docker Compose, Pygmy and Ahoy binaries +with their versions, and the Docker daemon details. Usernames, IDs, hashes and +IP addresses are redacted, so the output is safe to share. + +➡️ See [Support](../support.mdx) for how to report an issue. diff --git a/.vortex/docs/content/tools/drush.mdx b/.vortex/docs/content/tools/drush.mdx index a4a8285b4..c76e117de 100644 --- a/.vortex/docs/content/tools/drush.mdx +++ b/.vortex/docs/content/tools/drush.mdx @@ -12,14 +12,14 @@ scripts and Behat tests. It also allows a developer to interact with the site via CLI during development. -While all the standard Drush commands supported, **Vortex** also provides some -shorthand commands to abstract some of the common tasks: +While all the standard Drush commands are supported, **Vortex** also provides +shorthand commands that wrap the common tasks: -- fetching the database dump from the remote environment -- importing the database dump into the local environment -- running database updates and clearing caches +- `ahoy fetch-db` - fetch the database dump from the remote environment +- `ahoy import-db` - import the database dump into the local environment +- `ahoy provision` - import the database and run updates, config imports and cache rebuilds -➡️ See [Development](/docs/development) +➡️ See [Development](../development/README.mdx) ## Usage @@ -133,8 +133,8 @@ container. ``` 2. Run Drush commands using the Lagoon site aliases: ```shell - docker compose exec cli drush drush @lagoon.develop status # Show status of the develop environment - docker compose exec cli drush drush @lagoon.pr-123 ssh # SSH into the web container of the PR-123 environment + docker compose exec cli drush @lagoon.develop status # Show status of the develop environment + docker compose exec cli drush @lagoon.pr-123 ssh # SSH into the web container of the PR-123 environment ``` diff --git a/.vortex/docs/content/tools/eslint.mdx b/.vortex/docs/content/tools/eslint.mdx index 4a365643d..76e5714f5 100644 --- a/.vortex/docs/content/tools/eslint.mdx +++ b/.vortex/docs/content/tools/eslint.mdx @@ -25,7 +25,10 @@ import TabItem from '@theme/TabItem'; ```shell + # Lint all front-end code: Twig, JavaScript and CSS. ahoy lint-fe + # Lint only JavaScript in custom modules. + ahoy cli "yarn run lint-js" ``` @@ -37,12 +40,16 @@ import TabItem from '@theme/TabItem'; ### Fix violations -ESLint supports automatic fixing of many violations using the `--fix` flag. Prettier integration provides additional auto-formatting capabilities. +ESLint fixes many violations automatically using the `--fix` flag, and the +Prettier integration reformats the code at the same time. ```shell + # Fix all front-end lint issues. ahoy lint-fe-fix + # Fix only JavaScript issues in custom modules. + ahoy cli "yarn run lint-fix-js" ``` @@ -71,12 +78,13 @@ The configuration includes Drupal-specific globals: - `CKEditor5` - And other common Drupal frontend libraries -Targets include custom modules only: +Targets include custom modules only. The `--max-warnings=0` flag makes +warnings fail the check too: ```json { "scripts": { - "lint-js": "eslint web/modules/custom --ext .js" + "lint-js": "eslint web/modules/custom --ext .js --max-warnings=0 --no-error-on-unmatched-pattern" } } ``` @@ -86,7 +94,7 @@ Adding or removing targets in `package.json`: ```json { "scripts": { - "lint-js": "eslint web/modules/custom web/sites/default --ext .js" + "lint-js": "eslint web/modules/custom web/sites/default --ext .js --max-warnings=0 --no-error-on-unmatched-pattern" } } ``` @@ -117,8 +125,11 @@ vendor/ web/core/ web/libraries/ web/modules/contrib/ +web/profiles/contrib/ web/themes/contrib/ +web/sites/*/files/ *.min.js +*.min.css ``` ### Inline ignoring @@ -158,8 +169,9 @@ console.log('Debug'); // eslint-disable-line no-console ## Ignoring fail in continuous integration pipeline -This tool runs in continuous integration pipeline by default and fails the build -if there are any violations. +This tool runs in the continuous integration pipeline by default and fails the +build if there are any violations. -Set `VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE` environment variable to `1` to ignore -failures. The tool will still run and report violations, if any. +Set the `VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE` environment variable to `1` to +ignore failures (this variable also covers Stylelint, which +runs in the same step). The tool will still run and report violations, if any. diff --git a/.vortex/docs/content/tools/gherkin-lint.mdx b/.vortex/docs/content/tools/gherkin-lint.mdx index a1d24f719..5a6aa5c81 100644 --- a/.vortex/docs/content/tools/gherkin-lint.mdx +++ b/.vortex/docs/content/tools/gherkin-lint.mdx @@ -1,4 +1,8 @@ -# Gherkin lint +--- +sidebar_label: Gherkin Lint +--- + +# Gherkin Lint https://github.com/dantleech/gherkin-lint-php @@ -31,15 +35,15 @@ See [configuration reference](https://github.com/dantleech/gherkin-lint-php?tab= All global configuration takes place in the [`gherkinlint.json`](https://github.com/drevops/vortex/blob/main/gherkinlint.json) file. The values are merged with the default configuration. -To check enabled rules, run +To check enabled rules, run: ```shell -vendor/bin/gherkinlint rules +ahoy cli vendor/bin/gherkinlint rules ``` ## Ignoring -Ignoring rules **globally** takes place in the [`gherkinlint.json`](https://github.com/drevops/vortex/blob/main/gherkinlint.json) file. +Ignoring rules **globally** takes place in the [`gherkinlint.json`](https://github.com/drevops/vortex/blob/main/gherkinlint.json) file. ```json { @@ -51,24 +55,20 @@ Ignoring rules **globally** takes place in the [`gherkinlint.json`](https://git } ``` -Gherkin Lint does not support ignoring of all rules in the file. - To ignore **a specific rule** within a file, place in the file header: ```yaml # @gherkinlint-disable-rule keyword-order, someother-rule ``` -Gherkin Lint does not support ignoring of the **code blocks**. - -Gherkin Lint does not support ignoring rules on the **current line**. - -Gherkin Lint does not support ignoring rules on the **next line**. +Gherkin Lint does not support ignoring all rules within a file, ignoring code +blocks, or ignoring individual lines - per-file rule suppression is the only +inline mechanism. ## Ignoring fail in continuous integration pipeline -This tool runs in continuous integration pipeline by default and fails the build -if there are any violations. +This tool runs in the continuous integration pipeline by default and fails the +build if there are any violations. -Set `VORTEX_CI_GHERKIN_LINT_IGNORE_FAILURE` environment variable to `1` to +Set the `VORTEX_CI_GHERKIN_LINT_IGNORE_FAILURE` environment variable to `1` to ignore failures. The tool will still run and report violations, if any. diff --git a/.vortex/docs/content/tools/git-artifact.mdx b/.vortex/docs/content/tools/git-artifact.mdx index aea82acbf..ac77e11c7 100644 --- a/.vortex/docs/content/tools/git-artifact.mdx +++ b/.vortex/docs/content/tools/git-artifact.mdx @@ -6,7 +6,7 @@ https://github.com/drevops/git-artifact Some hosting providers, like Acquia, restrict certain build operations, making it necessary to develop and build your site elsewhere before deploying it. This -tool streamlines that process: it uses a `.gitignore.artifact` file to control +tool handles that transfer: it uses a `.gitignore.artifact` file to control which files get transferred, and overwrites the destination repository's history with each push, while preserving the source history. @@ -31,10 +31,10 @@ The file already contains all required targets to get the full site build with production-only dependencies (dev-dependencies are excluded from the code artifact during the continuous integration pipeline build). -Modifying targets in the `.gitignore.artifact` file works just like updating -a regular `.gitignore` file. +Modifying targets in the `.gitignore.artifact` file works the same way as +updating a regular `.gitignore` file. -➡️ See [Deployment > Artifact](/docs/deployment/artifact#artifact-file-control) +➡️ See [Deployment > Artifact](../deployment/artifact.mdx#artifact-file-control) for the deny-list model and the list of files the artifact excludes. The `git-artifact` binary is downloaded from @@ -46,13 +46,13 @@ The version and checksum are controlled by: - `VORTEX_DEPLOY_ARTIFACT_GIT_ARTIFACT_VERSION`: Version to download (default: `1.7.0`). - `VORTEX_DEPLOY_ARTIFACT_GIT_ARTIFACT_SHA256`: Expected SHA256 checksum of the binary. -It is required to set the following environment variables in the continuous -integration environment: +Set the following environment variables in the continuous integration +environment: -- `VORTEX_DEPLOY_ARTIFACT_GIT_USER_NAME`: Email address of the user who will be - committing to a remote repository. -- `VORTEX_DEPLOY_ARTIFACT_GIT_USER_EMAIL`: Name of the user who will be - committing to a remote repository. +- `VORTEX_DEPLOY_ARTIFACT_GIT_USER_EMAIL` (required): Email address of the user + who will be committing to a remote repository. +- `VORTEX_DEPLOY_ARTIFACT_GIT_USER_NAME` (optional): Name of the user who will + be committing to a remote repository (default: `Deployment Robot`). ## Stale branch cleanup diff --git a/.vortex/docs/content/tools/gitleaks.mdx b/.vortex/docs/content/tools/gitleaks.mdx index dc9fe5b36..ca6f6231c 100644 --- a/.vortex/docs/content/tools/gitleaks.mdx +++ b/.vortex/docs/content/tools/gitleaks.mdx @@ -6,9 +6,12 @@ https://github.com/gitleaks/gitleaks :::note -**Vortex** does not install Gitleaks locally. Please follow the [instructions](https://github.com/gitleaks/gitleaks#installing) to install it on your system. +**Vortex** does not install Gitleaks locally. Follow the +[instructions](https://github.com/gitleaks/gitleaks#installing) to install it +on your system. -In CI, Gitleaks runs from its official Docker image as part of the [security audit workflow](/docs/continuous-integration#security-audit). +In CI, Gitleaks runs from its official Docker image as part of the +[security audit workflow](../continuous-integration/README.mdx#security-audit). ::: @@ -18,7 +21,14 @@ In CI, Gitleaks runs from its official Docker image as part of the [security aud gitleaks dir . ``` -Gitleaks reads its configuration from the `.gitleaks.toml` file at the repository root. The shipped allowlist is tuned for Drupal projects so that a clean install reports no findings while real secrets are still detected. +## Configuration + +Global configuration takes place in the [`.gitleaks.toml`](https://github.com/drevops/vortex/blob/main/.gitleaks.toml) +file at the repository root (see the +[configuration documentation](https://github.com/gitleaks/gitleaks#configuration)). +It extends the default rule set with an allowlist for the placeholder values a +Drupal project template legitimately contains, so a clean install reports no +findings while real secrets are still detected. ## Ignoring @@ -28,7 +38,8 @@ To ignore a single line, add a `gitleaks:allow` comment to it: $settings['example'] = 'not-a-real-secret'; // gitleaks:allow ``` -To ignore a path or a recurring known-safe value across the codebase, add it to the `.gitleaks.toml` file at the repository root. See the [configuration documentation](https://github.com/gitleaks/gitleaks#configuration). +To ignore a path or a recurring known-safe value across the codebase, add it to +the `.gitleaks.toml` file. ## Ignoring fail in continuous integration pipeline diff --git a/.vortex/docs/content/tools/hadolint.mdx b/.vortex/docs/content/tools/hadolint.mdx index bb981f4b7..4bb00f4b0 100644 --- a/.vortex/docs/content/tools/hadolint.mdx +++ b/.vortex/docs/content/tools/hadolint.mdx @@ -6,9 +6,11 @@ https://github.com/hadolint/hadolint :::note -**Vortex** does not install Hadolint locally. Please follow the [instructions](https://github.com/hadolint/hadolint#install) to install it on your system. +**Vortex** does not install Hadolint locally. Follow the +[instructions](https://github.com/hadolint/hadolint#install) to install it on +your system. -In CI, Hadolint is installed automatically as part of the pipeline. +In CI, Hadolint runs from its official Docker image. ::: @@ -32,7 +34,7 @@ To ignore **all Hadolint rules** within a file, place in the file header: FROM ubuntu ``` -To ignore only the current and the **next line**: +To ignore rules for the **next instruction** only: ```Dockerfile FROM ubuntu @@ -43,8 +45,8 @@ RUN cd /tmp && echo "hello!" ## Ignoring fail in continuous integration pipeline -This tool runs in continuous integration pipeline by default and fails the build -if there are any violations. +This tool runs in the continuous integration pipeline by default and fails the +build if there are any violations. -Set `VORTEX_CI_HADOLINT_IGNORE_FAILURE` environment variable to `1` to ignore -failures. The tool will still run and report violations, if any. +Set the `VORTEX_CI_HADOLINT_IGNORE_FAILURE` environment variable to `1` to +ignore failures. The tool will still run and report violations, if any. diff --git a/.vortex/docs/content/tools/jest.mdx b/.vortex/docs/content/tools/jest.mdx index d47a0c290..75871ebec 100644 --- a/.vortex/docs/content/tools/jest.mdx +++ b/.vortex/docs/content/tools/jest.mdx @@ -2,7 +2,7 @@ sidebar_label: Jest --- -# Jest – JavaScript Testing Framework +# Jest - JavaScript testing framework https://jestjs.io/ @@ -33,15 +33,18 @@ import TabItem from '@theme/TabItem'; ### Running tests matching a pattern +`ahoy test-js` does not forward extra arguments, so run subsets through +`ahoy cli`: + ```shell - ahoy test-js -- --testPathPattern=ys_demo + ahoy cli "yarn test ys_demo" ``` ```shell - docker compose exec cli bash -c "yarn test --testPathPattern=ys_demo" + docker compose exec cli bash -c "yarn test ys_demo" ``` @@ -51,7 +54,7 @@ import TabItem from '@theme/TabItem'; ```shell - ahoy test-js -- -t "should increment the value" + ahoy cli "yarn test -t 'should increment the value'" ``` @@ -68,8 +71,9 @@ See [Jest configuration reference](https://jestjs.io/docs/configuration). All global configuration takes place in the [`jest.config.js`](https://github.com/drevops/vortex/blob/main/jest.config.js) file. By default, Jest will discover and run test files in `web/modules/custom/*/js/` -directories. Test files must use the `.test.js` extension and be co-located -alongside the source files they test. +directories and their subdirectories. Test files must use the `.test.js` +extension and live in a `tests/` subdirectory next to the source files they +test. The configuration uses the `jsdom` test environment to provide browser globals like `document`, `window`, and `localStorage`. @@ -83,84 +87,22 @@ changes. ## Writing tests -Test files are placed next to the source file they test: +Test files live in a `tests/` subdirectory next to the source files they test: ```text web/modules/custom/my_module/ └── js/ - ├── my_module.js # Source file - └── my_module.test.js # Test file + ├── my_module.js # Source file + └── tests/ + └── my_module.test.js # Test file ``` -### Loading Drupal behaviors - -Drupal JavaScript uses the IIFE pattern with `Drupal` as a global. Tests load -the source file using `eval()` after setting up the global: - -```javascript -/** - * @jest-environment jsdom - */ - -const fs = require('fs'); -const path = require('path'); - -describe('Drupal.behaviors.myModule', () => { - beforeEach(() => { - localStorage.clear(); - global.Drupal = { behaviors: {} }; - - const filePath = path.resolve(__dirname, 'my_module.js'); - const code = fs.readFileSync(filePath, 'utf8'); - eval(code); - }); +Tests load the source file with `require()` after setting up the required +globals, and the `.eslintrc.json` override for `*.test.js` files enables the +`jest` environment and allows `global-require` to support that pattern. - afterEach(() => { - delete global.Drupal; - }); - - it('should attach behavior', () => { - document.body.innerHTML = '
'; - Drupal.behaviors.myModule.attach(document); - - const el = document.querySelector('[data-my-module]'); - expect(el.classList.contains('processed')).toBe(true); - }); -}); -``` - -### Key patterns - -- **`global.Drupal = { behaviors: {} }`** in `beforeEach` provides the Drupal - global that the IIFE receives as a parameter. -- **`eval(fs.readFileSync(...))`** loads and executes the source file, which - registers the behavior on `Drupal.behaviors`. -- **`delete global.Drupal`** in `afterEach` ensures test isolation. -- **`document.body.innerHTML`** sets up the DOM for each test using jsdom. -- **`jest.useFakeTimers()`** controls `setTimeout` and `setInterval` for - testing timed behavior. - -### ESLint configuration - -The `.eslintrc.json` file includes an override for `*.test.js` files that -enables the `jest` environment and disables `no-eval` to allow the source -loading pattern: - -```json -{ - "overrides": [ - { - "files": ["*.test.js"], - "env": { "jest": true }, - "rules": { - "no-eval": "off", - "max-nested-callbacks": ["warn", 5], - "jsdoc/check-tag-names": "off" - } - } - ] -} -``` +For the test template, loading Drupal behaviors, and mocking globals, see the +[Jest development guide](/docs/development/jest). ## Ignoring fail in continuous integration pipeline diff --git a/.vortex/docs/content/tools/phpcs.mdx b/.vortex/docs/content/tools/phpcs.mdx index 07292f3ff..56731ced2 100644 --- a/.vortex/docs/content/tools/phpcs.mdx +++ b/.vortex/docs/content/tools/phpcs.mdx @@ -39,7 +39,8 @@ import TabItem from '@theme/TabItem'; ```shell - ahoy lint-fix + # Fix all back-end lint issues (Rector, then PHPCBF). + ahoy lint-be-fix ``` @@ -51,7 +52,7 @@ import TabItem from '@theme/TabItem'; ## Configuration -See [configuration reference](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Configuration-Options). +See [configuration reference](https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki/Configuration-Options). All global configuration takes place in the [`phpcs.xml`](https://github.com/drevops/vortex/blob/main/phpcs.xml) file. @@ -59,11 +60,12 @@ By default, PHPCS will check against the following rules: - `Drupal` - `DrupalPractice` -- `Generic.Debug.ESLint` +- `Generic.PHP.RequireStrictTypes` +- `PHPCompatibility` - `DrevOps` - `SlevomatCodingStandard.TypeHints.DNFTypeHintFormat` -Targets include custom modules and themes, settings and tests. +Targets include custom modules and themes, settings, tests and scripts. Adding or removing targets: @@ -71,10 +73,11 @@ Adding or removing targets: path/to/dir_or_file ``` -Run checks against platform version specified in `composer.json` key `config.platform.php`: +The `PHPCompatibility` checks run against the platform version specified in the +`composer.json` key `config.platform.php`: ```xml - + ``` ## Ignoring @@ -82,7 +85,7 @@ Run checks against platform version specified in `composer.json` key `config.pla Ignoring rules **globally** takes place in the [`phpcs.xml`](https://github.com/drevops/vortex/blob/main/phpcs.xml) file: ```xml - + *\/dir\/another\/*\.php *\/dir\/another\/*\.inc @@ -109,7 +112,7 @@ $a = 1; // phpcs:enable ``` -To ignore only the current and the **next line**: +To ignore only the **next line**: ```php // phpcs:ignore @@ -118,8 +121,8 @@ $a = 1; ## Ignoring fail in continuous integration pipeline -This tool runs in continuous integration pipeline by default and fails the build -if there are any violations. +This tool runs in the continuous integration pipeline by default and fails the +build if there are any violations. -Set `VORTEX_CI_PHPCS_IGNORE_FAILURE` environment variable to `1` to ignore +Set the `VORTEX_CI_PHPCS_IGNORE_FAILURE` environment variable to `1` to ignore failures. The tool will still run and report violations, if any. diff --git a/.vortex/docs/content/tools/phpstan.mdx b/.vortex/docs/content/tools/phpstan.mdx index 2a0ec708b..65c673bcc 100644 --- a/.vortex/docs/content/tools/phpstan.mdx +++ b/.vortex/docs/content/tools/phpstan.mdx @@ -2,20 +2,17 @@ sidebar_label: PHPStan --- -# PHPStan - PHP Static Analysis Tool +# PHPStan - PHP static analysis tool https://phpstan.org/user-guide/getting-started -> PHPStan focuses on finding errors in your code without actually running it. It -> catches whole classes of bugs even before you write tests for the code. It -> moves -> PHP closer to compiled languages in the sense that the correctness of each -> line -> of the code can be checked before you run the actual line. +> PHPStan focuses on finding errors in your code without actually running it. +> It catches whole classes of bugs even before you write tests for the code. +> It moves PHP closer to compiled languages in the sense that the correctness +> of each line of the code can be checked before you run the actual line. -**Vortex** comes with [pre-configured PHPStan ruleset](https://github.com/drevops/vortex/blob/main/phpstan.neon) -for -Drupal projects. +**Vortex** comes with a [pre-configured PHPStan ruleset](https://github.com/drevops/vortex/blob/main/phpstan.neon) +for Drupal projects. ## Usage @@ -52,10 +49,12 @@ All global configuration takes place in the [`phpstan.neon`](https://github.com/drevops/vortex/blob/main/phpstan.neon) file. -By default, PHPStan will check with the Drupal context in mind thanks to +The analysis runs at [level 7](https://phpstan.org/user-guide/rule-levels) +against the PHP version pinned in the `composer.json` key +`config.platform.php`, and checks with the Drupal context in mind thanks to [mglaman/phpstan-drupal](https://github.com/mglaman/phpstan-drupal). -Targets include custom modules and themes, settings and tests. +Targets include custom modules and themes, settings, tests and scripts. Adding or removing targets: @@ -76,7 +75,7 @@ the [`phpstan.neon`](https://github.com/drevops/vortex/blob/main/phpstan.neon) f ```yaml parameters: ignoreErrors: - - # Comment about why this rules is excluded. + - # Comment about why this rule is excluded. # 'message' is a regular expression with `#` as begin and end delimiters. message: '#.*no value type specified in iterable type array.#' paths: @@ -84,14 +83,11 @@ parameters: - path/to/exclude/a_file.php ``` -PHPStan does not support ignoring of **all PHPStan rules** within a file. +PHPStan does not support ignoring all rules within a file, ignoring a specific +rule within a file, or ignoring [code blocks](https://github.com/phpstan/phpstan/issues/4452) - +inline suppression is per-line only. -PHPStan does not support ignoring of **a specific rule** within a file. - -PHPStan [does not support](https://github.com/phpstan/phpstan/issues/4452) -ignoring of the **code blocks**. - -To ignore only the current and the **next line**: +To ignore only the **next line**: ```php // @phpstan-ignore-next-line @@ -100,8 +96,8 @@ $a = 1; ## Ignoring fail in continuous integration pipeline -This tool runs in continuous integration pipeline by default and fails the build -if there are any violations. +This tool runs in the continuous integration pipeline by default and fails the +build if there are any violations. -Set `VORTEX_CI_PHPSTAN_IGNORE_FAILURE` environment variable to `1` to ignore -failures. The tool will still run and report violations, if any. +Set the `VORTEX_CI_PHPSTAN_IGNORE_FAILURE` environment variable to `1` to +ignore failures. The tool will still run and report violations, if any. diff --git a/.vortex/docs/content/tools/phpunit.mdx b/.vortex/docs/content/tools/phpunit.mdx index 87d426d5a..0de4c1f8d 100644 --- a/.vortex/docs/content/tools/phpunit.mdx +++ b/.vortex/docs/content/tools/phpunit.mdx @@ -2,13 +2,13 @@ sidebar_label: PHPUnit --- -# PHPUnit – The PHP Testing Framework +# PHPUnit - PHP testing framework https://github.com/sebastianbergmann/phpunit/ > PHPUnit is a programmer-oriented testing framework for PHP. -**Vortex** comes with [pre-configured PHPCS ruleset](https://github.com/drevops/vortex/blob/main/phpunit.xml) for Drupal projects. +**Vortex** comes with a [pre-configured PHPUnit configuration](https://github.com/drevops/vortex/blob/main/phpunit.xml) for Drupal projects. ## Usage @@ -55,27 +55,27 @@ import TabItem from '@theme/TabItem'; ```shell - ahoy test-unit --filter=MyTest + ahoy test-unit -- --filter=MyTest ``` ```shell - docker compose exec cli vendor/bin/phpunit -- --filter=MyTest + docker compose exec cli vendor/bin/phpunit --filter=MyTest ``` -**Running tagged tests with `@group group_name` annotation** +### Running tagged tests with a `@group group_name` annotation ```shell - ahoy test-unit --group=group_name + ahoy test-unit -- --group=group_name ``` ```shell - docker compose exec cli vendor/bin/phpunit -- --group=group_name + docker compose exec cli vendor/bin/phpunit --group=group_name ``` @@ -87,21 +87,14 @@ See [configuration reference](https://docs.phpunit.de/en/10.4/configuration.html All global configuration takes place in the [`phpunit.xml`](https://github.com/drevops/vortex/blob/main/phpunit.xml) file. By default, PHPUnit will run tests for custom modules and themes, Drupal -settings and continuous integration configuration. +settings, and the shipped CircleCI configuration test. -The recommended way to adding test targets is via using test suites: +The recommended way of adding test targets is via test suites: ```xml my/custom/dir/*/tests - -``` - -Run checks against platform version specified in `composer.json` key `config.platform.php`: - -```xml - ``` ### Chrome session flags @@ -154,7 +147,7 @@ minimum percentage (default: `90`). ### PR comments Coverage reports are posted as PR comments automatically. Each new report -replaces the previous one — older comments are minimized to keep the PR +replaces the previous one - older comments are minimized to keep the PR timeline clean. The comment includes a header indicating the CI source (GitHub Actions or CircleCI). diff --git a/.vortex/docs/content/tools/pygmy.mdx b/.vortex/docs/content/tools/pygmy.mdx index 59cad564c..ce2c90af1 100644 --- a/.vortex/docs/content/tools/pygmy.mdx +++ b/.vortex/docs/content/tools/pygmy.mdx @@ -8,16 +8,20 @@ https://github.com/pygmystack/pygmy > Development Environment running on your Linux based system. It is built to work > with Docker for Mac! (quite a lot for such a small whale 🐳) -**What `pygmy` will handle for you:** +Pygmy provides the shared services the **Vortex** container stack relies on: -- An HTTP reverse proxy for nice URLs and HTTPS offloading. -- A DNS system so we don't have to remember IP addresses. -- SSH agents to use SSH keys within containers. -- A system that receives and displays mail locally available at http://mailhog.docker.amazee.io/. +- An HTTP reverse proxy for readable URLs and HTTPS offloading. +- A DNS system, so services are addressed by name instead of IP address. +- SSH agents, so SSH keys are available within containers. +- A local mail catcher that receives and displays outgoing mail at http://mailhog.docker.amazee.io/. + +The `docker-compose.yml` shipped with **Vortex** attaches its services to the +external `amazeeio-network` that Pygmy creates, so Pygmy must be running before +`ahoy up` can start the stack - [`ahoy doctor`](doctor.mdx) checks for it. ## Installation -Please follow the [official installation instructions](https://github.com/pygmystack/pygmy/tree/main#installation). +Follow the [official installation instructions](https://github.com/pygmystack/pygmy/tree/main#installation). ## Usage diff --git a/.vortex/docs/content/tools/rector.mdx b/.vortex/docs/content/tools/rector.mdx index b49b3047a..d37dadbc0 100644 --- a/.vortex/docs/content/tools/rector.mdx +++ b/.vortex/docs/content/tools/rector.mdx @@ -8,35 +8,33 @@ https://github.com/rectorphp/rector > Rector instantly upgrades and refactors the PHP code of your application. -**Vortex** comes with [pre-configured Rector configuration](https://github.com/drevops/vortex/blob/main/rector.php) -for Drupal projects. The configuration is based on -the configuration provided -by [Drupal Rector](https://github.com/palantirnet/drupal-rector). +**Vortex** comes with a [pre-configured Rector configuration](https://github.com/drevops/vortex/blob/main/rector.php) +for Drupal projects, based on the configuration provided by +[Drupal Rector](https://github.com/palantirnet/drupal-rector). -## What is Rector? +## What Rector does Rector automatically refactors your PHP and Drupal code to: -- **Fix deprecated Drupal APIs** - Prepares code for Drupal major version upgrades -- **Modernize PHP syntax** - Leverages PHP 8.3 language features -- **Improve code quality** - Enhances readability and maintainability -- **Reduce technical debt** - Automated cleanup and optimization +- **Fix deprecated Drupal APIs** - prepares code for Drupal major version upgrades +- **Modernize PHP syntax** - rewrites code to use PHP 8.4 language features +- **Keep code consistent** - applies the same refactoring rules across the codebase ## When to use Rector -- **Before Drupal Upgrades**: Run before upgrading Drupal major versions to fix deprecations -- **During Development**: Run periodically (weekly/monthly) to catch deprecations early -- **Code Reviews**: Include in CI pipeline for automated quality checks -- **Refactoring Legacy Code**: When modernizing older codebases +- **Before Drupal upgrades** - run before upgrading Drupal major versions to fix deprecations +- **During development** - run periodically (weekly/monthly) to catch deprecations early +- **In code review** - it runs in the CI pipeline as one of the code quality checks +- **When refactoring legacy code** - to modernize older codebases in bulk :::note - Rector is often used ad hoc to perform bulk refactoring. +Rector is often used ad hoc to perform bulk refactoring. - In **Vortex**, it is integrated as a regular code quality tool - that can be run at any time to check for deprecated code and - automatically fix issues. This allows us to keep the codebase up-to-date - _continuously_ rather than waiting for major upgrades. +In **Vortex**, it is integrated as a regular code quality tool that can be run +at any time to check for deprecated code and automatically fix issues. This +keeps the codebase up-to-date _continuously_ rather than waiting for major +upgrades. ::: @@ -65,7 +63,8 @@ import TabItem from '@theme/TabItem'; ```shell - ahoy lint-fix + # Fix all back-end lint issues (Rector, then PHPCBF). + ahoy lint-be-fix ``` @@ -88,20 +87,21 @@ Targets include custom modules and themes, settings and tests. ### Config sets Rector provides [config sets](https://getrector.com/documentation/set-lists) -functionality that allows to enable/disable rules in bulk. +that enable or disable rules in bulk. -**Vortex** provides the config sets for Drupal 8, 9 and 10 deprecated code and -code style fixes. +**Vortex** enables the PHP 8.4 sets and the Drupal sets from +[Drupal Rector](https://github.com/palantirnet/drupal-rector). The Drupal sets +are resolved from the installed `drupal/core` version, so the rules track core +upgrades without changes to `rector.php`. -The config sets are meant to be adjusted per-project as needed. - -A full list of available config sets can be found in +The config sets are meant to be adjusted per-project as needed. A full list of +available config sets can be found on the [Rules overview](https://getrector.com/documentation/rules-overview) page. ## Ignoring See more -on [Ignoring Rules Or Paths](https://getrector.com/documentation/ignoring-rules-or-paths) +on the [Ignoring Rules Or Paths](https://getrector.com/documentation/ignoring-rules-or-paths) page. Ignoring rules **globally** takes place in @@ -134,39 +134,16 @@ Rector does not support ignoring of the **code blocks**. ## Cache management -### How caching works - -Rector caches parsed file information and analysis results to speed up subsequent runs: - -- **Cache location**: `/tmp/_rector` (system temp directory) -- **Stores**: Parsed AST, file metadata, rule analysis results -- **Performance**: Subsequent runs are ~40-60% faster by reusing results for unchanged files - -### When to clear cache - -Clear Rector cache in these situations: - -1. **After changing `rector.php` configuration** - - Added/removed rules - - Changed skip list - - Modified paths - -2. **After updating Rector or Drupal Rector packages** - - ```shell - composer update rector/rector palantirnet/drupal-rector - ``` - -3. **When seeing unexpected behavior** - - Rules not applying when they should - - Stale results - - False negatives in CI - -4. **After switching git branches** with different Rector configurations +Rector caches parsed file information and analysis results in the system temp +directory (`/tmp/rector_cached_files`) to speed up subsequent runs by reusing +results for unchanged files. -### Clear cache +Clear the cache after changing the `rector.php` configuration (rules, skip +list, or paths), after updating the `rector/rector` or +`palantirnet/drupal-rector` packages, after switching git branches with +different Rector configurations, or when results look stale. -To clear cache and check for violations: +To clear the cache and check for violations: @@ -181,7 +158,7 @@ To clear cache and check for violations: -To clear cache and apply changes: +To clear the cache and apply changes: @@ -198,8 +175,8 @@ To clear cache and apply changes: ## Ignoring fail in continuous integration pipeline -This tool runs in continuous integration pipeline by default and fails the build -if there are any violations. +This tool runs in the continuous integration pipeline by default and fails the +build if there are any violations. -Set `VORTEX_CI_RECTOR_IGNORE_FAILURE` environment variable to `1` to ignore -failures. The tool will still run and report violations, if any. +Set the `VORTEX_CI_RECTOR_IGNORE_FAILURE` environment variable to `1` to +ignore failures. The tool will still run and report violations, if any. diff --git a/.vortex/docs/content/tools/renovate.mdx b/.vortex/docs/content/tools/renovate.mdx index 7876e3bed..6f7bcd388 100644 --- a/.vortex/docs/content/tools/renovate.mdx +++ b/.vortex/docs/content/tools/renovate.mdx @@ -2,7 +2,7 @@ sidebar_label: Renovate --- -# Renovate - Automated updates +# Renovate - automated updates **Vortex** uses [Renovate](https://renovatebot.com) for automated dependency updates. @@ -37,7 +37,7 @@ All other groups open PRs for manual review. ### Disabled updates -These are intentionally skipped by Renovate — update them manually: +These are intentionally skipped by Renovate - update them manually: | Group | What is skipped | Why | |---|---|---| @@ -62,73 +62,76 @@ These are intentionally skipped by Renovate — update them manually: ## Self-hosted vs GitHub app Renovate can run as a hosted GitHub app or as a standalone self-hosted service -in CircleCI or GitHub Actions. +in CircleCI or GitHub Actions. A self-hosted service suits projects that +restrict third-party access to their repositories. -A self-hosted service can be beneficial when your project is restricted in terms -of third-party access. +:::note -**Note**: If the `RENOVATE_TOKEN` is not provided and the job is configured, the job will still run but the Renovate -update steps will be skipped gracefully without causing a build failure. +If the job is configured but a required variable is not provided, the job +still runs and the Renovate update steps are skipped gracefully without +failing the build. On GitHub Actions the required variables are +`RENOVATE_TOKEN` and `RENOVATE_GIT_AUTHOR`; CircleCI additionally requires +`RENOVATE_REPOSITORIES`, since it has no repository to default to. -### Setting up Renovate self-hosted in CircleCI +::: -#### Required environment variables +### Setting up the self-hosted service -The following environment variables **must** be manually created in the CircleCI project settings: +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; -1. `RENOVATE_TOKEN` (required): GitHub access token with repository write permissions -2. `RENOVATE_REPOSITORIES` (required): Repository to run Renovate on in format `yourorg/repo` -3. `RENOVATE_GIT_AUTHOR` (required): Author for Renovate commits in format `Name ` (e.g., `Renovate Bot `) + + -#### Optional environment variables +**Required variables**, created in the GitHub repository settings: -These can be created to customize behavior (will use defaults if not set): +- `RENOVATE_TOKEN`: GitHub access token with repository write permissions. + *Location: Settings > Secrets and variables > Actions > Repository secrets.* +- `RENOVATE_GIT_AUTHOR`: Author for Renovate commits in the format + `Name ` (e.g., `Renovate Bot `). + *Location: Settings > Secrets and variables > Actions > Repository variables.* -- `RENOVATE_DRY_RUN`: Set to `true` to test runs without making changes (default: `false`) -- `RENOVATE_DEPENDENCY_DASHBOARD`: Set to `true` to enable the dependency dashboard (default: `false`) +**Optional variables** (defaults apply if not set): -Note that triggering actions from the self-hosted service is not supported. +- `RENOVATE_REPOSITORIES`: Repository to run Renovate on in the format + `yourorg/repo` (default: the current repository). +- `RENOVATE_DRY_RUN`: Set to `true` to test runs without making changes + (default: `false`). +- `RENOVATE_DEPENDENCY_DASHBOARD`: Set to `true` to enable the dependency + dashboard (default: `false`). -#### Manual trigger +**Manual trigger:** run the workflow from the **Actions** tab using the +workflow dispatch feature. -The Renovate job in CircleCI can be triggered manually from the CircleCI pipeline UI. +**Debug logging:** set the `LOG_LEVEL` environment variable to `debug` in the +workflow configuration. -#### Debug logging + + -Debug logging is enabled by default with `LOG_LEVEL: 'debug'` to provide detailed -information about the Renovate execution process. This helps with troubleshooting -dependency update issues and understanding why certain updates might be skipped. +**Required variables**, created in the CircleCI project settings: -### Setting up Renovate self-hosted in GitHub Actions +- `RENOVATE_TOKEN`: GitHub access token with repository write permissions. +- `RENOVATE_REPOSITORIES`: Repository to run Renovate on in the format + `yourorg/repo`. +- `RENOVATE_GIT_AUTHOR`: Author for Renovate commits in the format + `Name ` (e.g., `Renovate Bot `). -#### Required environment variables +**Optional variables** (defaults apply if not set): -The following **must** be manually created in the GitHub repository settings: +- `RENOVATE_DRY_RUN`: Set to `true` to test runs without making changes + (default: `false`). +- `RENOVATE_DEPENDENCY_DASHBOARD`: Set to `true` to enable the dependency + dashboard (default: `false`). -1. `RENOVATE_TOKEN` (required): GitHub access token with repository write permissions - *Location: Settings > Secrets and variables > Actions > Repository secrets* -2. `RENOVATE_GIT_AUTHOR` (required): Author for Renovate commits in format `Name ` (e.g., `Renovate Bot `) - *Location: Settings > Secrets and variables > Actions > Repository variables* +**Manual trigger:** trigger the Renovate job from the CircleCI pipeline UI. -#### Optional environment variables +**Debug logging:** enabled by default with `LOG_LEVEL: 'debug'`. -These can be created to customize behavior (will use defaults if not set): + + -- `RENOVATE_REPOSITORIES`: Repository to run Renovate on in format `yourorg/repo` (default: uses current repository) -- `RENOVATE_DRY_RUN`: Set to `true` to test runs without making changes (default: `false`) -- `RENOVATE_DEPENDENCY_DASHBOARD`: Set to `true` to enable the dependency dashboard (default: `false`) - -Note that triggering actions from the self-hosted service is not supported. - -#### Manual trigger - -The Renovate job in GitHub Actions can be triggered manually from the Actions tab -in the GitHub repository UI using the workflow dispatch feature. - -#### Debug logging - -Debug logging is available and can be enabled by setting the `LOG_LEVEL` environment -variable to `debug` in the workflow configuration for detailed troubleshooting. +Triggering GitHub Actions from the self-hosted service is not supported. ## Dependency dashboard diff --git a/.vortex/docs/content/tools/twig-cs-fixer.mdx b/.vortex/docs/content/tools/twig-cs-fixer.mdx index 2f1298bea..bb2978688 100644 --- a/.vortex/docs/content/tools/twig-cs-fixer.mdx +++ b/.vortex/docs/content/tools/twig-cs-fixer.mdx @@ -1,3 +1,7 @@ +--- +sidebar_label: Twig CS Fixer +--- + # Twig CS Fixer https://github.com/VincentLanglet/Twig-CS-Fixer @@ -6,22 +10,41 @@ https://github.com/VincentLanglet/Twig-CS-Fixer > > Twig CS Fixer aims to be what phpcs is to php. It checks your codebase for violations on coding standards. -**Vortex** comes with [pre-configured Twig-cs-fixer ruleset](https://github.com/drevops/vortex/blob/main/.twig-cs-fixer.php) for Drupal projects. +**Vortex** comes with a [pre-configured Twig CS Fixer ruleset](https://github.com/drevops/vortex/blob/main/.twig-cs-fixer.php) for Drupal projects. ## Usage +### Check for violations + import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ```shell + # Lint all front-end code: Twig, JavaScript and CSS. ahoy lint-fe ``` ```shell - docker compose exec cli vendor/bin/twig-cs-fixer + docker compose exec cli vendor/bin/twig-cs-fixer lint + ``` + + + +### Fix violations + + + + ```shell + # Fix all front-end lint issues. + ahoy lint-fe-fix + ``` + + + ```shell + docker compose exec cli vendor/bin/twig-cs-fixer lint --fix ``` @@ -44,30 +67,23 @@ $finder->in(__DIR__ . '/web/themes/custom'); ## Ignoring -Ignoring rules **globally** takes place in the [`.twig-cs-fixer.php`](https://github.com/drevops/vortex/blob/main/.twig-cs-fixer.php) file: +Excluding **target directories** takes place in the [`.twig-cs-fixer.php`](https://github.com/drevops/vortex/blob/main/.twig-cs-fixer.php) +file - `$finder->exclude()` removes directories from the set of linted files +rather than disabling a rule: ```php $finder->exclude('myCustomDirectory'); ``` -All errors have an identifier with the syntax: `A.B:C:D` with +Every error has an identifier in the form `A.B:C:D`, where: -- `A`: The rule short name (mainly made from the class name) -- `B`: The error identifier (like the error level or a specific name) -- `C`: The line the error occurs -- `D`: The position of the token in the line the error occurs +- `A`: the rule short name (mainly made from the class name) +- `B`: the error identifier (like the error level or a specific name) +- `C`: the line the error occurs on +- `D`: the position of the token in that line -The four parts are optional, all those format are working - -- `A` -- `A.B` -- `A.B:C` -- `A.B:C:D` -- `A:C` -- `A:C:D` -- `A::D` - -If you need to know the errors identifier you have/want to ignore, you can run +All 4 parts are optional: `A`, `A.B`, `A.B:C`, `A.B:C:D`, `A:C`, `A:C:D` and +`A::D` all work. To find the identifier of an error you want to ignore, run the linter command with the `--debug` option. To ignore **all Twig CS Fixer rules** within a file, place in the file header: @@ -87,19 +103,20 @@ Twig CS Fixer does not support ignoring of the **code blocks**. To ignore only the **current line**: ```twig -{# twig-cs-fixer-disable-next-line A.B:C:D #} +{{ token }} {# twig-cs-fixer-disable-line A.B:C:D #} ``` To ignore only the **next line**: ```twig -{# twig-cs-fixer-disable-line A.B:C:D #} +{# twig-cs-fixer-disable-next-line A.B:C:D #} +{{ token }} ``` ## Ignoring fail in continuous integration pipeline -This tool runs in continuous integration pipeline by default and fails the build -if there are any violations. +This tool runs in the continuous integration pipeline by default and fails the +build if there are any violations. -Set `VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE` environment variable to `1` to +Set the `VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE` environment variable to `1` to ignore failures. The tool will still run and report violations, if any. diff --git a/.vortex/docs/content/updating-vortex.mdx b/.vortex/docs/content/updating-vortex.mdx index e4858355a..a4a288a85 100644 --- a/.vortex/docs/content/updating-vortex.mdx +++ b/.vortex/docs/content/updating-vortex.mdx @@ -65,11 +65,13 @@ Specifically check if any environment variables were added or changed. It may be a good idea to commit changes in smaller chunks to make it easier to review and revert if necessary. - Note that `composer.json` would be most likely deviated from the **Vortex** - version, so you may want to additionally review the changes to the package - versions and dependencies. We recommend temporarily reverting changes to - `composer.json` and `composer.lock` to preserve your project's version, and - manually updating the packages versions in your `composer.json`. + Your `composer.json` has most likely deviated from the **Vortex** version, + so review the package version changes carefully. We recommend temporarily + reverting the changes to `composer.json` and `composer.lock` to preserve + your project's versions, then updating the package versions in your + `composer.json` manually. After settling the constraints, run + `ahoy composer update` and commit the regenerated `composer.lock`, so the + build installs the versions you selected rather than the stale locked ones. ```shell title="Review changes" git status @@ -99,11 +101,11 @@ Specifically check if any environment variables were added or changed. ## Best practices -1. **Regular Updates**: Update **Vortex** monthly or when security patches are released -2. **Test Updates**: Always test updates in development before applying to production -3. **Staged Rollout**: Update development → staging → production environments -4. **Document Changes**: Keep track of what changed and why -5. **Monitor After Update**: Watch for issues in the days following an update +1. **Regular updates**: Update **Vortex** monthly or when security patches are released +2. **Test updates**: Always test updates in development before applying to production +3. **Staged rollout**: Update development → staging → production environments +4. **Document changes**: Keep track of what changed and why +5. **Monitor after update**: Watch for issues in the days following an update ## Getting help diff --git a/.vortex/docs/cspell.json b/.vortex/docs/cspell.json index 59c25b250..cbe1d97cb 100644 --- a/.vortex/docs/cspell.json +++ b/.vortex/docs/cspell.json @@ -30,7 +30,6 @@ "behat", "bootstrappable", "calver", - "centralised", "checkstyle", "clamav", "cobertura", @@ -55,13 +54,11 @@ "hadolint", "hotfixes", "htpasswd", - "initialise", "jsonpath", "lagooncli", "lando", "langid", "langlet", - "licence", "localdev", "lucene", "lullabot", @@ -71,8 +68,6 @@ "multisite", "novnc", "oomphinc", - "optimise", - "optimised", "palantirnet", "pathauto", "phpcbf", @@ -95,11 +90,10 @@ "solrcore", "sqldump", "testmode", - "standardise", "toml", "updatedb", "uselagoon", - "utilising", + "Valkey", "vfsstream", "vincentlanglet", "vlucas", From 3b6500f2e0319cc2fa3df47167b6e8afd4c213fb Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:16:53 +1000 Subject: [PATCH 23/57] Updated template Composer dependencies and wired nine 'behat-steps' traits into 'FeatureContext'. (cherry picked from commit 70714d35e1d1516a2695c04f45b205cf57da7b77) Fixtures regenerated separately. --- composer.json | 8 ++++---- tests/behat/bootstrap/FeatureContext.php | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index e9b7c756a..c22804ba1 100644 --- a/composer.json +++ b/composer.json @@ -45,9 +45,9 @@ "dealerdirect/phpcodesniffer-composer-installer": "^1.2.1", "drevops/behat-format-progress-fail": "^1.5.1", "drevops/behat-screenshot": "^2.6.0", - "drevops/behat-steps": "^3.13.0", - "drevops/phpcs-standard": "^0.7.0", - "drupal/coder": "^9@alpha", + "drevops/behat-steps": "^3.14.0", + "drevops/phpcs-standard": "^1.0.0", + "drupal/coder": "^9.0.1", "drupal/drupal-extension": "^6.1", "ergebnis/composer-normalize": "^2.52.0", "lullabot/mink-selenium2-driver": "^1.7.4", @@ -61,7 +61,7 @@ "phpstan/phpstan": "^2.2.8", "phpunit/phpunit": "^11.5.56", "pyrech/composer-changelogs": "^2.2", - "rector/rector": "^2.6.1", + "rector/rector": "^2.6.3", "softcreatr/jsonpath": "^0.10 || ^1.0", "vincentlanglet/twig-cs-fixer": "^4.0.2" }, diff --git a/tests/behat/bootstrap/FeatureContext.php b/tests/behat/bootstrap/FeatureContext.php index 77a4d17f0..6fd4cdb69 100644 --- a/tests/behat/bootstrap/FeatureContext.php +++ b/tests/behat/bootstrap/FeatureContext.php @@ -8,10 +8,14 @@ declare(strict_types=1); use DrevOps\BehatSteps\AccessibilityTrait; +use DrevOps\BehatSteps\CommandTrait; use DrevOps\BehatSteps\CookieTrait; use DrevOps\BehatSteps\DateTrait; +use DrevOps\BehatSteps\DiagnosticsTrait; +use DrevOps\BehatSteps\Drupal\BigPipeTrait; use DrevOps\BehatSteps\Drupal\BlockTrait; use DrevOps\BehatSteps\Drupal\CacheTrait; +use DrevOps\BehatSteps\Drupal\ConfigTrait; use DrevOps\BehatSteps\Drupal\ContentBlockTrait; use DrevOps\BehatSteps\Drupal\ContentTrait; use DrevOps\BehatSteps\Drupal\DraggableviewsTrait; @@ -20,13 +24,16 @@ use DrevOps\BehatSteps\Drupal\FileTrait; use DrevOps\BehatSteps\Drupal\MediaTrait; use DrevOps\BehatSteps\Drupal\MenuTrait; +use DrevOps\BehatSteps\Drupal\ModuleTrait; use DrevOps\BehatSteps\MetatagTrait; use DrevOps\BehatSteps\Drupal\OverrideTrait; use DrevOps\BehatSteps\Drupal\ParagraphsTrait; +use DrevOps\BehatSteps\Drupal\QueueTrait; // phpcs:ignore #;< MODULE_REDIRECT use DrevOps\BehatSteps\Drupal\RedirectTrait; // phpcs:ignore #;> MODULE_REDIRECT use DrevOps\BehatSteps\Drupal\SearchApiTrait; +use DrevOps\BehatSteps\Drupal\StateTrait; use DrevOps\BehatSteps\Drupal\TaxonomyTrait; use DrevOps\BehatSteps\Drupal\TestmodeTrait; use DrevOps\BehatSteps\Drupal\UserTrait; @@ -39,10 +46,12 @@ use DrevOps\BehatSteps\JsonTrait; use DrevOps\BehatSteps\KeyboardTrait; use DrevOps\BehatSteps\LinkTrait; +use DrevOps\BehatSteps\ModalTrait; use DrevOps\BehatSteps\PathTrait; use DrevOps\BehatSteps\ResponseTrait; use DrevOps\BehatSteps\ResponsiveTrait; use DrevOps\BehatSteps\RestTrait; +use DrevOps\BehatSteps\TableTrait; use DrevOps\BehatSteps\WaitTrait; use DrevOps\BehatSteps\XmlTrait; use Drupal\DrupalExtension\Context\DrupalContext; @@ -53,12 +62,16 @@ class FeatureContext extends DrupalContext { use AccessibilityTrait; + use BigPipeTrait; use BlockTrait; use CacheTrait; + use CommandTrait; + use ConfigTrait; use ContentBlockTrait; use ContentTrait; use CookieTrait; use DateTrait; + use DiagnosticsTrait; use DraggableviewsTrait; use EckTrait; use ElementTrait; @@ -74,9 +87,12 @@ class FeatureContext extends DrupalContext { use MediaTrait; use MenuTrait; use MetatagTrait; + use ModalTrait; + use ModuleTrait; use OverrideTrait; use ParagraphsTrait; use PathTrait; + use QueueTrait; // phpcs:ignore #;< MODULE_REDIRECT use RedirectTrait; // phpcs:ignore #;> MODULE_REDIRECT @@ -84,6 +100,8 @@ class FeatureContext extends DrupalContext { use ResponsiveTrait; use RestTrait; use SearchApiTrait; + use StateTrait; + use TableTrait; use TaxonomyTrait; use TestmodeTrait; use UserTrait; From 2ecc60f17ab14fe18e17001d098b4f80af711e45 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:17:59 +1000 Subject: [PATCH 24/57] Added 'VORTEX_CI_BEHAT_PROFILE_OFFSET' to decouple the Behat profile from the runner index. (cherry picked from commit 66f481d68dd6416d0fbe2d86e6560220c36fb76a) Named with the 'VORTEX_CI_' prefix used here, and documented in the CI overview, where this line keeps the parallelism section. Fixtures regenerated separately. --- .circleci/config.yml | 9 +++++++-- .circleci/vortex-test-common.yml | 9 +++++++-- .github/workflows/build-test-deploy.yml | 8 ++++++-- .../content/continuous-integration/README.mdx | 19 ++++++++++++------- .vortex/docs/content/development/behat.mdx | 6 ++++++ behat.yml | 8 +++++--- 6 files changed, 43 insertions(+), 16 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d856ab921..641122c4d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -299,8 +299,13 @@ jobs: #;> MODULE_SDC_DEVEL #;> DRUPAL_THEME #;< TOOL_BEHAT - # Runs on every node, using the `p` profile. + # Runs on every node, using the `p` profile. echo "export VORTEX_CI_IS_BEHAT_RUNNER=1" + # Subtracted from the node index to derive the profile number. + # Raise it by one for every leading node that does not run + # Behat, so the first Behat node still selects the `p0` + # catch-all profile. + echo "export VORTEX_CI_BEHAT_PROFILE_OFFSET=0" #;> TOOL_BEHAT } >> "${BASH_ENV}" @@ -530,7 +535,7 @@ jobs: name: Test with Behat command: | [ "${VORTEX_CI_IS_BEHAT_RUNNER:-1}" = "1" ] || exit 0 - if [ "${VORTEX_CI_RUNNER_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${VORTEX_CI_RUNNER_INDEX}}"; fi + if [ "${VORTEX_CI_RUNNER_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p$((VORTEX_CI_RUNNER_INDEX - VORTEX_CI_BEHAT_PROFILE_OFFSET))}"; fi echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ diff --git a/.circleci/vortex-test-common.yml b/.circleci/vortex-test-common.yml index f94588510..0e9c2710a 100644 --- a/.circleci/vortex-test-common.yml +++ b/.circleci/vortex-test-common.yml @@ -156,8 +156,13 @@ jobs: #;> MODULE_SDC_DEVEL #;> DRUPAL_THEME #;< TOOL_BEHAT - # Runs on every node, using the `p` profile. + # Runs on every node, using the `p` profile. echo "export VORTEX_CI_IS_BEHAT_RUNNER=1" + # Subtracted from the node index to derive the profile number. + # Raise it by one for every leading node that does not run + # Behat, so the first Behat node still selects the `p0` + # catch-all profile. + echo "export VORTEX_CI_BEHAT_PROFILE_OFFSET=0" #;> TOOL_BEHAT } >> "${BASH_ENV}" @@ -387,7 +392,7 @@ jobs: name: Test with Behat command: | [ "${VORTEX_CI_IS_BEHAT_RUNNER:-1}" = "1" ] || exit 0 - if [ "${VORTEX_CI_RUNNER_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${VORTEX_CI_RUNNER_INDEX}}"; fi + if [ "${VORTEX_CI_RUNNER_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p$((VORTEX_CI_RUNNER_INDEX - VORTEX_CI_BEHAT_PROFILE_OFFSET))}"; fi echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ diff --git a/.github/workflows/build-test-deploy.yml b/.github/workflows/build-test-deploy.yml index be7974d43..479faa9a3 100644 --- a/.github/workflows/build-test-deploy.yml +++ b/.github/workflows/build-test-deploy.yml @@ -246,8 +246,12 @@ jobs: #;> MODULE_SDC_DEVEL #;> DRUPAL_THEME #;< TOOL_BEHAT - # Runs on every instance, using the `p` profile. + # Runs on every instance, using the `p` profile. VORTEX_CI_IS_BEHAT_RUNNER: true + # Subtracted from the instance index to derive the profile number. Raise + # it by one for every leading instance that does not run Behat, so the + # first Behat instance still selects the `p0` catch-all profile. + VORTEX_CI_BEHAT_PROFILE_OFFSET: 0 #;> TOOL_BEHAT steps: @@ -529,7 +533,7 @@ jobs: if: ${{ env.VORTEX_CI_IS_BEHAT_RUNNER == 'true' }} run: | # shellcheck disable=SC2170 - if [ "${VORTEX_CI_RUNNER_TOTAL}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${VORTEX_CI_RUNNER_INDEX}}"; fi + if [ "${VORTEX_CI_RUNNER_TOTAL}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p$((VORTEX_CI_RUNNER_INDEX - VORTEX_CI_BEHAT_PROFILE_OFFSET))}"; fi echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" diff --git a/.vortex/docs/content/continuous-integration/README.mdx b/.vortex/docs/content/continuous-integration/README.mdx index 7c169b8e5..40ba47d7f 100644 --- a/.vortex/docs/content/continuous-integration/README.mdx +++ b/.vortex/docs/content/continuous-integration/README.mdx @@ -267,14 +267,19 @@ simply skipped and the job still passes. ::: -:::warning - -Use the **last** container for this, never the first. Behat derives its profile -name from the container index, and `p0` is the catch-all that runs every -scenario without a `@pX` tag. Excluding container 0 from Behat means `p0` never -runs and those scenarios silently stop being tested. +Behat derives its profile name from the container index, and `p0` is the +catch-all that runs every scenario without a `@pX` tag. Excluding container 0 +from Behat would therefore leave `p0` unrun and those scenarios silently +untested, so profile numbers count from the first container that runs Behat +instead: `VORTEX_CI_BEHAT_PROFILE_OFFSET` is subtracted from the container index +to pick the profile. Raise it by one for every leading container excluded from +Behat, and the first Behat container still selects `p0`: -::: +```yaml +VORTEX_CI_IS_PHPUNIT_RUNNER: ${{ matrix.instance == 0 }} +VORTEX_CI_IS_BEHAT_RUNNER: ${{ matrix.instance != 0 }} +VORTEX_CI_BEHAT_PROFILE_OFFSET: 1 +``` ### Balancing Behat tests diff --git a/.vortex/docs/content/development/behat.mdx b/.vortex/docs/content/development/behat.mdx index fe2efa483..04083d11c 100644 --- a/.vortex/docs/content/development/behat.mdx +++ b/.vortex/docs/content/development/behat.mdx @@ -104,6 +104,12 @@ tag never orphans a test. You can add more `p*` profiles in your `behat.yml` by copying the existing `p1` profile and changing several lines of configuration. +Profile numbers count from the first runner that runs Behat: the CI +configuration subtracts `VORTEX_CI_BEHAT_PROFILE_OFFSET` (`0` by default) from +the runner index to pick the profile, so dedicating leading runners to other +tools requires no profile renumbering or feature re-tagging. See +[Test parallelism](../continuous-integration#giving-a-tool-its-own-container). + If the pipeline has only one runner and `VORTEX_CI_BEHAT_PROFILE` is unset, the `default` profile is used and all tests run there except those tagged `@skipped`. An explicitly set `VORTEX_CI_BEHAT_PROFILE` stays active even on a diff --git a/behat.yml b/behat.yml index eb40b0acd..003a73a67 100644 --- a/behat.yml +++ b/behat.yml @@ -96,9 +96,11 @@ default: # Show explicit fail information and continue the test run. DrevOps\BehatFormatProgressFail\FormatExtension: ~ -# Profiles for parallel testing. CI runs the profile named after the index of -# the runner it is on, so 'pN' requires a runner N. Adding a runner means adding -# a matching profile here and excluding its tag from the 'p0' catch-all below. +# Profiles for parallel testing. CI derives the profile number from the index +# of the runner it is on minus 'CI_BEHAT_PROFILE_OFFSET', so profiles stay +# numbered from 'p0' regardless of which runner Behat starts on. Adding a +# runner that runs Behat means adding a profile for its derived number and +# excluding its tag from the 'p0' catch-all below. # https://www.vortextemplate.com/docs/continuous-integration#test-parallelism # Runs all tests tagged with '@smoke' or not tagged with '@p1', and not tagged From 238dc01302559de72ad09409fba046b6d96dfff5 Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:18:17 +1000 Subject: [PATCH 25/57] Updated 'drevops/behat-steps' to 3.14.1. (cherry picked from commit 7f8d04442ddac9559e97bec4d77b0e3bdd95f4f5) Left the re-recorded 'test-bdd' assets out: the recordings belong to the other line and are re-recorded here. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c22804ba1..be405b9fe 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "dealerdirect/phpcodesniffer-composer-installer": "^1.2.1", "drevops/behat-format-progress-fail": "^1.5.1", "drevops/behat-screenshot": "^2.6.0", - "drevops/behat-steps": "^3.14.0", + "drevops/behat-steps": "^3.14.1", "drevops/phpcs-standard": "^1.0.0", "drupal/coder": "^9.0.1", "drupal/drupal-extension": "^6.1", From aaa75de360aa681ff9763883a7898fe62b05025f Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Mon, 31 Aug 2026 13:21:58 +1000 Subject: [PATCH 26/57] Restructured the documentation into a single 'Development' section with per-topic subsections and canonical tool pages. (cherry picked from commit 645b0b6f25c15670105a6f6fe88b1bbbd1c4d20e) Adopted the new information architecture while keeping this line's facts throughout: the CLI, deploy steps in place of provision scripts, the PHP tooling, the environment detector and the prefixed CI variables. Re-homed the deploy step example beside its page and repointed every link the move broke. The added doctor and info recordings come from the other line and need re-recording here. --- .env | 6 +- .vortex/docs/.utils/update-videos.php | 28 +- .../variables/extra/acquia.variables.sh | 10 +- .../variables/extra/environment.variables.sh | 2 +- .../variables/extra/lagoon.variables.sh | 10 +- .vortex/docs/content/README.mdx | 4 +- .vortex/docs/content/architecture.mdx | 185 +++++-- .vortex/docs/content/cli.mdx | 2 +- .../content/continuous-integration/README.mdx | 67 ++- .../continuous-integration/circleci.mdx | 4 +- .../continuous-integration/github-actions.mdx | 4 +- .../maintenance/_release_template.md | 2 +- .vortex/docs/content/deployment/README.mdx | 4 +- .vortex/docs/content/deployment/artifact.mdx | 61 ++- .vortex/docs/content/deployment/lagoon.mdx | 2 +- .../docs/content/deployment/notifications.mdx | 361 -------------- .../deployment/notifications/README.mdx | 130 +++++ .../deployment/notifications/_category_.json | 6 + .../deployment/notifications/diffy.mdx | 38 ++ .../deployment/notifications/email.mdx | 24 + .../deployment/notifications/github.mdx | 30 ++ .../content/deployment/notifications/jira.mdx | 35 ++ .../deployment/notifications/newrelic.mdx | 43 ++ .../deployment/notifications/slack.mdx | 36 ++ .../deployment/notifications/webhook.mdx | 39 ++ .vortex/docs/content/deployment/webhook.mdx | 6 +- .../ExampleDeployStep.php | 0 .vortex/docs/content/development/README.mdx | 298 ++++------- .vortex/docs/content/development/ai.mdx | 56 ++- .vortex/docs/content/development/behat.mdx | 169 ------- .../development/code-quality/README.mdx | 72 +++ .../development/code-quality/_category_.json | 6 + .../code-quality}/dclint.mdx | 23 +- .../code-quality}/eslint.mdx | 21 +- .../code-quality}/gherkin-lint.mdx | 14 +- .../code-quality}/hadolint.mdx | 20 +- .../code-quality}/phpcs.mdx | 14 +- .../code-quality}/phpstan.mdx | 16 +- .../code-quality}/rector.mdx | 96 ++-- .../development/code-quality/sdc-devel.mdx | 89 ++++ .../development/code-quality/stylelint.mdx | 210 ++++++++ .../code-quality}/twig-cs-fixer.mdx | 14 +- .vortex/docs/content/development/composer.mdx | 467 +++++++++++++----- .vortex/docs/content/development/database.mdx | 127 ----- .../dependency-updates.mdx} | 47 +- .../development/environment/README.mdx | 142 ++++++ .../development/environment/_category_.json | 6 + .../environment}/ahoy.mdx | 63 ++- .../{ => environment}/debugging.mdx | 12 +- .../environment}/docker.mdx | 23 +- .../environment}/doctor.mdx | 59 ++- .../environment}/drush.mdx | 43 +- .../content/development/environment/logs.mdx | 32 ++ .../environment}/pygmy.mdx | 10 +- .../environment/troubleshooting.mdx | 56 +++ .vortex/docs/content/development/faqs.mdx | 40 +- .../{drupal => development}/migrations.mdx | 5 +- .../content/development/modules/README.mdx | 34 ++ .../development/modules/_category_.json | 6 + .../modules/contributed-modules.mdx | 320 ++++++++++++ .../modules}/drupal-helpers.mdx | 5 +- .../modules}/generated-content.mdx | 5 +- .../modules}/module-scaffold.mdx | 14 +- .../modules}/testmode.mdx | 11 +- .vortex/docs/content/development/phpunit.mdx | 141 ------ .../{drupal => development}/provision.mdx | 333 ++++++++++--- .../content/development/security/README.mdx | 35 ++ .../development/security/_category_.json | 6 + .../development/security/dependency-audit.mdx | 129 +++++ .../security/secret-scanning.mdx} | 23 +- .../{drupal => development}/settings.mdx | 111 +++-- .../content/development/testing/README.mdx | 96 ++++ .../development/testing/_category_.json | 6 + .../content/development/testing/behat.mdx | 333 +++++++++++++ .../development/{ => testing}/jest.mdx | 124 ++++- .../content/development/testing/phpunit.mdx | 246 +++++++++ .../themes.mdx} | 19 +- .../docs/content/development/variables.mdx | 6 +- .../content/development/visual-regression.mdx | 33 +- .vortex/docs/content/drupal/README.mdx | 34 -- .vortex/docs/content/drupal/composer-json.mdx | 298 ----------- .vortex/docs/content/drupal/modules.mdx | 91 ---- .vortex/docs/content/drupal/update-hooks.mdx | 72 --- .vortex/docs/content/hosting/README.mdx | 2 +- .vortex/docs/content/hosting/acquia.mdx | 8 +- .vortex/docs/content/hosting/lagoon.mdx | 2 +- .vortex/docs/content/installation.mdx | 336 +++++++++---- .vortex/docs/content/releasing/README.mdx | 4 +- .vortex/docs/content/support.mdx | 2 +- .vortex/docs/content/tools/README.mdx | 40 -- .vortex/docs/content/tools/behat.mdx | 256 ---------- .vortex/docs/content/tools/diffy.mdx | 53 -- .vortex/docs/content/tools/git-artifact.mdx | 75 --- .vortex/docs/content/tools/jest.mdx | 113 ----- .vortex/docs/content/tools/phpunit.mdx | 190 ------- .vortex/docs/cspell.json | 2 + .vortex/docs/docusaurus.config.js | 188 ++++++- .vortex/docs/sidebars.js | 38 +- .vortex/docs/static/img/doctor-info.json | 40 ++ .vortex/docs/static/img/doctor-info.png | Bin 0 -> 163732 bytes .vortex/docs/static/img/doctor-info.svg | 1 + .vortex/docs/static/img/doctor.json | 24 + .vortex/docs/static/img/doctor.png | Bin 0 -> 86730 bytes .vortex/docs/static/img/doctor.svg | 1 + .vortex/docs/static/img/info.json | 18 + .vortex/docs/static/img/info.png | Bin 0 -> 106417 bytes .vortex/docs/static/img/info.svg | 1 + docs/testing.md | 4 +- web/sites/default/settings.php | 12 +- 109 files changed, 4401 insertions(+), 2929 deletions(-) delete mode 100644 .vortex/docs/content/deployment/notifications.mdx create mode 100644 .vortex/docs/content/deployment/notifications/README.mdx create mode 100644 .vortex/docs/content/deployment/notifications/_category_.json create mode 100644 .vortex/docs/content/deployment/notifications/diffy.mdx create mode 100644 .vortex/docs/content/deployment/notifications/email.mdx create mode 100644 .vortex/docs/content/deployment/notifications/github.mdx create mode 100644 .vortex/docs/content/deployment/notifications/jira.mdx create mode 100644 .vortex/docs/content/deployment/notifications/newrelic.mdx create mode 100644 .vortex/docs/content/deployment/notifications/slack.mdx create mode 100644 .vortex/docs/content/deployment/notifications/webhook.mdx rename .vortex/docs/content/{drupal => development}/ExampleDeployStep.php (100%) delete mode 100644 .vortex/docs/content/development/behat.mdx create mode 100644 .vortex/docs/content/development/code-quality/README.mdx create mode 100644 .vortex/docs/content/development/code-quality/_category_.json rename .vortex/docs/content/{tools => development/code-quality}/dclint.mdx (71%) rename .vortex/docs/content/{tools => development/code-quality}/eslint.mdx (87%) rename .vortex/docs/content/{tools => development/code-quality}/gherkin-lint.mdx (82%) rename .vortex/docs/content/{tools => development/code-quality}/hadolint.mdx (71%) rename .vortex/docs/content/{tools => development/code-quality}/phpcs.mdx (88%) rename .vortex/docs/content/{tools => development/code-quality}/phpstan.mdx (86%) rename .vortex/docs/content/{tools => development/code-quality}/rector.mdx (92%) create mode 100644 .vortex/docs/content/development/code-quality/sdc-devel.mdx create mode 100644 .vortex/docs/content/development/code-quality/stylelint.mdx rename .vortex/docs/content/{tools => development/code-quality}/twig-cs-fixer.mdx (88%) delete mode 100644 .vortex/docs/content/development/database.mdx rename .vortex/docs/content/{tools/renovate.mdx => development/dependency-updates.mdx} (93%) create mode 100644 .vortex/docs/content/development/environment/README.mdx create mode 100644 .vortex/docs/content/development/environment/_category_.json rename .vortex/docs/content/{tools => development/environment}/ahoy.mdx (58%) rename .vortex/docs/content/development/{ => environment}/debugging.mdx (98%) rename .vortex/docs/content/{tools => development/environment}/docker.mdx (96%) rename .vortex/docs/content/{tools => development/environment}/doctor.mdx (71%) rename .vortex/docs/content/{tools => development/environment}/drush.mdx (75%) create mode 100644 .vortex/docs/content/development/environment/logs.mdx rename .vortex/docs/content/{tools => development/environment}/pygmy.mdx (91%) create mode 100644 .vortex/docs/content/development/environment/troubleshooting.mdx rename .vortex/docs/content/{drupal => development}/migrations.mdx (98%) create mode 100644 .vortex/docs/content/development/modules/README.mdx create mode 100644 .vortex/docs/content/development/modules/_category_.json create mode 100644 .vortex/docs/content/development/modules/contributed-modules.mdx rename .vortex/docs/content/{drupal => development/modules}/drupal-helpers.mdx (96%) rename .vortex/docs/content/{drupal => development/modules}/generated-content.mdx (97%) rename .vortex/docs/content/{drupal => development/modules}/module-scaffold.mdx (84%) rename .vortex/docs/content/{drupal => development/modules}/testmode.mdx (89%) delete mode 100644 .vortex/docs/content/development/phpunit.mdx rename .vortex/docs/content/{drupal => development}/provision.mdx (66%) create mode 100644 .vortex/docs/content/development/security/README.mdx create mode 100644 .vortex/docs/content/development/security/_category_.json create mode 100644 .vortex/docs/content/development/security/dependency-audit.mdx rename .vortex/docs/content/{tools/gitleaks.mdx => development/security/secret-scanning.mdx} (65%) rename .vortex/docs/content/{drupal => development}/settings.mdx (91%) create mode 100644 .vortex/docs/content/development/testing/README.mdx create mode 100644 .vortex/docs/content/development/testing/_category_.json create mode 100644 .vortex/docs/content/development/testing/behat.mdx rename .vortex/docs/content/development/{ => testing}/jest.mdx (62%) create mode 100644 .vortex/docs/content/development/testing/phpunit.mdx rename .vortex/docs/content/{drupal/theme-scaffold.mdx => development/themes.mdx} (93%) delete mode 100644 .vortex/docs/content/drupal/README.mdx delete mode 100644 .vortex/docs/content/drupal/composer-json.mdx delete mode 100644 .vortex/docs/content/drupal/modules.mdx delete mode 100644 .vortex/docs/content/drupal/update-hooks.mdx delete mode 100644 .vortex/docs/content/tools/README.mdx delete mode 100644 .vortex/docs/content/tools/behat.mdx delete mode 100644 .vortex/docs/content/tools/diffy.mdx delete mode 100644 .vortex/docs/content/tools/git-artifact.mdx delete mode 100644 .vortex/docs/content/tools/jest.mdx delete mode 100644 .vortex/docs/content/tools/phpunit.mdx create mode 100644 .vortex/docs/static/img/doctor-info.json create mode 100644 .vortex/docs/static/img/doctor-info.png create mode 100644 .vortex/docs/static/img/doctor-info.svg create mode 100644 .vortex/docs/static/img/doctor.json create mode 100644 .vortex/docs/static/img/doctor.png create mode 100644 .vortex/docs/static/img/doctor.svg create mode 100644 .vortex/docs/static/img/info.json create mode 100644 .vortex/docs/static/img/info.png create mode 100644 .vortex/docs/static/img/info.svg diff --git a/.env b/.env index 3bbb159a4..4ffa8e615 100644 --- a/.env +++ b/.env @@ -114,7 +114,7 @@ DRUPAL_CLAMAV_MODE=daemon # or fresh install from profile), running updates, appying configuration # changes, clearing caches and performing other tasks that prepare the site for # use. -# @see https://www.vortextemplate.com/docs/drupal/provision +# @see https://www.vortextemplate.com/docs/development/provision # Set to 'profile' to install a site from profile instead of the database dump. VORTEX_PROVISION_TYPE=database @@ -146,13 +146,13 @@ VORTEX_PROVISION_VERIFY_CONFIG_UNCHANGED_AFTER_UPDATE=0 # # Database sanitization is enabled by default in all non-production # environments and is always skipped in the production environment. -# @see https://www.vortextemplate.com/docs/drupal/provision#database-sanitization +# @see https://www.vortextemplate.com/docs/development/provision#database-sanitization VORTEX_PROVISION_SANITIZE_DB_SKIP=0 # Sanitization email pattern. # # Applied if database sanitization is enabled. -# @see https://www.vortextemplate.com/docs/drupal/provision#database-sanitization +# @see https://www.vortextemplate.com/docs/development/provision#database-sanitization VORTEX_PROVISION_SANITIZE_DB_EMAIL=user_%uid@your-site-domain.example #;> !PROVISION_TYPE_PROFILE diff --git a/.vortex/docs/.utils/update-videos.php b/.vortex/docs/.utils/update-videos.php index 0138fbdc2..3707789ce 100755 --- a/.vortex/docs/.utils/update-videos.php +++ b/.vortex/docs/.utils/update-videos.php @@ -95,6 +95,30 @@ 'poster_ms' => 2000, 'typer' => TRUE, ], + 'info' => [ + 'command' => 'ahoy info', + 'speed' => 1.0, + 'cols' => 80, + 'rows' => 42, + 'poster_ms' => NULL, + 'typer' => TRUE, + ], + 'doctor' => [ + 'command' => 'ahoy doctor', + 'speed' => 1.0, + 'cols' => 80, + 'rows' => 42, + 'poster_ms' => NULL, + 'typer' => TRUE, + ], + 'doctor-info' => [ + 'command' => 'ahoy doctor info', + 'speed' => 1.0, + 'cols' => 80, + 'rows' => 42, + 'poster_ms' => NULL, + 'typer' => TRUE, + ], ]; function usage(): void { @@ -367,7 +391,7 @@ function main(array $argv): int { $recorder->note('Requested: ' . implode(', ', $requested)); $recorder->note('Mode: ' . ($keep ? 'reuse workspace (--keep)' : 'wipe + bootstrap')); - $needs_built_project = array_intersect($requested, ['build', 'provision', 'lint', 'test', 'test-bdd']) !== []; + $needs_built_project = array_intersect($requested, ['build', 'provision', 'lint', 'test', 'test-bdd', 'info', 'doctor', 'doctor-info']) !== []; $extra_deps = ['expect']; if ($needs_built_project) { @@ -427,7 +451,7 @@ function main(array $argv): int { } } - $order = ['build', 'provision', 'lint', 'test', 'test-bdd']; + $order = ['build', 'info', 'doctor', 'doctor-info', 'provision', 'lint', 'test', 'test-bdd']; foreach ($order as $name) { if (!in_array($name, $requested, TRUE)) { continue; diff --git a/.vortex/docs/.utils/variables/extra/acquia.variables.sh b/.vortex/docs/.utils/variables/extra/acquia.variables.sh index e0795e44d..ec9ff8a93 100755 --- a/.vortex/docs/.utils/variables/extra/acquia.variables.sh +++ b/.vortex/docs/.utils/variables/extra/acquia.variables.sh @@ -17,25 +17,25 @@ VORTEX_PROVISION_ACQUIA_SKIP= # NewRelic API key, usually of type 'USER'. # -# @see https://www.vortextemplate.com/docs/deployment/notifications#new-relic +# @see https://www.vortextemplate.com/docs/deployment/notifications/newrelic VORTEX_NOTIFY_NEWRELIC_APIKEY= # JIRA API token. # -# @see https://www.vortextemplate.com/docs/deployment/notifications#jira +# @see https://www.vortextemplate.com/docs/deployment/notifications/jira VORTEX_NOTIFY_JIRA_TOKEN= # GitHub token. # -# @see https://www.vortextemplate.com/docs/deployment/notifications#github +# @see https://www.vortextemplate.com/docs/deployment/notifications/github VORTEX_NOTIFY_GITHUB_TOKEN= # Slack webhook URL. # The incoming Webhook URL from your Slack app configuration. -# @see https://www.vortextemplate.com/docs/deployment/notifications#slack +# @see https://www.vortextemplate.com/docs/deployment/notifications/slack VORTEX_NOTIFY_SLACK_WEBHOOK="${VORTEX_NOTIFY_SLACK_WEBHOOK:-}" # Custom webhook URL. # -# @see https://www.vortextemplate.com/docs/deployment/notifications#webhook +# @see https://www.vortextemplate.com/docs/deployment/notifications/webhook VORTEX_NOTIFY_WEBHOOK_URL= diff --git a/.vortex/docs/.utils/variables/extra/environment.variables.sh b/.vortex/docs/.utils/variables/extra/environment.variables.sh index ee903d9ab..a07263cf2 100755 --- a/.vortex/docs/.utils/variables/extra/environment.variables.sh +++ b/.vortex/docs/.utils/variables/extra/environment.variables.sh @@ -24,5 +24,5 @@ ENVIRONMENT_TYPE= # or secret. If not set, a fallback is derived from the database host, which is # suitable for local and CI use only. # -# @see https://www.vortextemplate.com/docs/drupal/settings +# @see https://www.vortextemplate.com/docs/development/settings DRUPAL_HASH_SALT="" diff --git a/.vortex/docs/.utils/variables/extra/lagoon.variables.sh b/.vortex/docs/.utils/variables/extra/lagoon.variables.sh index e3517f1f3..ea294e48c 100755 --- a/.vortex/docs/.utils/variables/extra/lagoon.variables.sh +++ b/.vortex/docs/.utils/variables/extra/lagoon.variables.sh @@ -18,25 +18,25 @@ NEWRELIC_LICENSE= # Notification NewRelic API key, usually of type 'USER'. # -# @see https://www.vortextemplate.com/docs/deployment/notifications#new-relic +# @see https://www.vortextemplate.com/docs/deployment/notifications/newrelic VORTEX_NOTIFY_NEWRELIC_APIKEY= # Notification JIRA API token. # -# @see https://www.vortextemplate.com/docs/deployment/notifications#jira +# @see https://www.vortextemplate.com/docs/deployment/notifications/jira VORTEX_NOTIFY_JIRA_TOKEN= # Notification GitHub token. # -# @see https://www.vortextemplate.com/docs/deployment/notifications#github +# @see https://www.vortextemplate.com/docs/deployment/notifications/github VORTEX_NOTIFY_GITHUB_TOKEN= # Notification Slack webhook URL. # The incoming Webhook URL from your Slack app configuration. -# @see https://www.vortextemplate.com/docs/deployment/notifications#slack +# @see https://www.vortextemplate.com/docs/deployment/notifications/slack VORTEX_NOTIFY_SLACK_WEBHOOK="${VORTEX_NOTIFY_SLACK_WEBHOOK:-}" # Notification custom webhook URL. # -# @see https://www.vortextemplate.com/docs/deployment/notifications#webhook +# @see https://www.vortextemplate.com/docs/deployment/notifications/webhook VORTEX_NOTIFY_WEBHOOK_URL= diff --git a/.vortex/docs/content/README.mdx b/.vortex/docs/content/README.mdx index c1bcb827b..e1353867f 100644 --- a/.vortex/docs/content/README.mdx +++ b/.vortex/docs/content/README.mdx @@ -80,7 +80,7 @@ foundation for developing and maintaining Drupal projects. maxWidth: '120px' }}>
📦
-
Template
+
Template
+
@@ -92,7 +92,7 @@ foundation for developing and maintaining Drupal projects. maxWidth: '120px' }}>
📖
-
Documentation
+
Documentation
+
diff --git a/.vortex/docs/content/architecture.mdx b/.vortex/docs/content/architecture.mdx index b75485b3b..a51d78fbb 100644 --- a/.vortex/docs/content/architecture.mdx +++ b/.vortex/docs/content/architecture.mdx @@ -21,40 +21,18 @@ maintainability: - **Explicit logging helps**: Scripts log every major step, so it's easy to follow what's going on. -## System components - -The system is made up of several modular pieces that work together: - -- Local development environment -- Drupal management -- Code quality and testing tools -- Continuous integration workflows -- Hosting provider configurations -- Automated dependency updates -- Project documentation -- Automation scripts to connect components - -You can see how these parts connect in the diagram below: - - -System components -System components - - ## Code lifecycle **Vortex** standardizes the code lifecycle across local, CI, and hosting environments. This ensures that the same steps are followed everywhere, reducing the chance of errors and making processes predictable. -
- Expand to see the complete code lifecycle diagram +The lifecycle below is the map of the whole system - every section of this page +describes one of its stages: import CodeLifecycle from './_code-lifecycle.mdx'; - - -
+ ## Local development environment @@ -94,7 +72,7 @@ rule cannot express them: The other two ignore files apply the same deny-list model to different targets: - `.dockerignore` controls the container image build context. - ➡️ See [Tools > Docker](./tools/docker#build-context-and-dockerignore) + ➡️ See [Tools > Docker](./development/environment/docker#build-context-and-dockerignore) - `.gitignore.artifact` controls the deployment artifact. ➡️ See [Deployment > Artifact](./deployment/artifact#artifact-file-control) @@ -120,7 +98,7 @@ problems and makes the process predictable for everyone. It also makes it possible to add more automation around provisioning, like conditionally running migrations or creating demo content. -➡️ See [Drupal > Provision](./drupal/provision) +➡️ See [Drupal > Provision](./development/provision) ### Module and theme scaffolds @@ -133,8 +111,8 @@ tests. These show you how to: Use these scaffolds as starting points for your own work. -➡️ See [Drupal > Module Scaffold](./drupal/module-scaffold) -and [Drupal > Theme Scaffold](./drupal/theme-scaffold) +➡️ See [Drupal > Module Scaffold](./development/modules/module-scaffold) +and [Drupal > Theme Scaffold](./development/themes) ### Settings management @@ -153,7 +131,7 @@ module's settings cleanly when no longer needed. **Vortex** also includes tests for these settings to ensure they are loaded correctly in each environment. -➡️ See [Drupal > Settings](./drupal/settings) +➡️ See [Drupal > Settings](./development/settings) ## Code quality and testing @@ -172,7 +150,7 @@ You'll also find scaffolds for: - **Behat**: Behavior-driven testing with screenshot capture and extra steps - **Jest**: JavaScript unit testing -➡️ See [Tools](./tools) +➡️ See [Tools](./development) ## Continuous integration workflows @@ -213,7 +191,7 @@ integrations. You can host RenovateBot yourself or use the cloud version, and you can tweak the schedule as needed. -➡️ See [Tools > Renovate](./tools/renovate) +➡️ See [Tools > Renovate](./development/dependency-updates) ## Documentation & onboarding @@ -233,6 +211,32 @@ These scripts: - Support environment variables to adapt behavior - Are modular and easy to extend +The scripts are not part of the template's own files: they are a dependency. +Your project's `composer.json` requires `drevops/vortex-tooling` with a tilde +constraint (`~1.4.0` in the shipped template), which accepts patch releases but +holds the minor version, so a new minor release cannot reach your deployments +until you raise the constraint yourself. Once installed, every script is +available as a Composer binary at `vendor/bin/vortex-