Skip to content

Converged remaining conventions across the installer, tooling and shipped template. - #2895

Open
AlexSkrypnyk wants to merge 31 commits into
mainfrom
feature/converge-remaining
Open

Converged remaining conventions across the installer, tooling and shipped template.#2895
AlexSkrypnyk wants to merge 31 commits into
mainfrom
feature/converge-remaining

Conversation

@AlexSkrypnyk

@AlexSkrypnyk AlexSkrypnyk commented Aug 5, 2026

Copy link
Copy Markdown
Member

Summary

Second pass of the codebase convergence audit, continuing from #2890. Every inconsistency class here was mapped by reading the first-party source in full, given a single canonical form, and reviewed and approved before being applied. The changes are behaviour-preserving except where explicitly called out below.

One commit per inconsistency class, plus snapshot regenerations. Gates run per class: PHPCS + PHPStan + Rector, the 1606-test installer suite, the 309-test BATS suite, and ShellCheck over every shipped script.

Changes

Installer API shapes

  • Direct Laravel\Prompts calls in PromptManager now route through the Tui facade that exists for them. form() and spin() stay direct - they are framework orchestration primitives, not output channels, the same boundary that keeps OutputInterface inside Symfony Command classes.
  • PromptType::promptFunction() returns a first-class callable instead of building '\Laravel\Prompts\' . $value as a string, which removed a @phpstan-ignore.
  • Runner invocation converged on positional required arguments plus a named tail, and getOutput(as_array: TRUE) with an is_array guard.
  • LoggableInterface was replaced by Logger/LoggerAwareInterface and Logger/LoggerAwareTrait, matching the three existing Runner/*Aware* triplets. The runner's enableLog()/disableLog() pass-throughs were dropped in favour of getLogger()->enable()/disable(), which is the same API one level down.

composer.json manipulation

JsonManipulator::updateFile($file, callable) is now the single read-guard-mutate-write shape. It replaced five regex-on-raw-JSON removals, the json_decode/json_encode surgery in Internal, two unguarded fromFile() calls and three silent-skip guards. Discovery sites keep the nullsafe form - they read the user's destination directory, where composer.json may legitimately be absent; only process() sites throw.

Deduplication

GitHub auth headers (x4), Guzzle client defaults (x2), the exit-code range guard (x4), the handler-lookup guard (x3 plus ten unguarded accesses), the headless PromptManager bootstrap (x2), the is-profile snippet (x2), the ANSI regex (x2), the TaskOutput dim map (x2) and module-prefix resolution (x2). Each caller keeps its own configuration where that configuration is the behaviour - the two module-prefix callers still search their own 12- and 4-entry location lists.

Structure

  • Handlers queue file operations and PromptManager executes them. File::runDirectoryTasks() moved out of the Internal handler, where it violated that rule, into PromptManager::runProcessors() after the handler loop.
  • Test paths mirror the source layout: tests/Unit/Utils/, tests/Unit/Task/, tests/{Unit,Functional}/Prompts/Handlers/.
  • Every dataProvider* sits immediately after the test it feeds. .vortex/installer/CLAUDE.md documented the opposite order and was corrected.
  • Handler preconditions are leading guard clauses.

Shipped template

  • Issue-tracker links in comments were replaced by the reason the code exists, so the comment survives the ticket.
  • Punctuation, missing final newlines, a banner line that broke its own 80-character box, and a .ignorecontent rule for a directory that no longer exists.
  • CI_GITLEAKS and CODE_COVERAGE_PROVIDER_CODECOV became TOOL_GITLEAKS and TOOL_CODECOV, joining the fourteen fences already on that prefix. Renamed in every template fence and every installer removeToken call together; installed output is unchanged, which is the correct signal.
  • One wording per CI step across both providers, one ci-runner pin, and set -x removed from the three "Install Ahoy" steps where it was unconditional.
  • The demo counter's data-counter-* attributes and its localStorage key are namespaced to the module that owns them.
  • The theme behavior dropped its jQuery dependency, gained a processed-class guard, and lost a leftover authoring prompt that had been shipping to every consumer site.

Behaviour changes

Five, each in its own commit:

  1. Two dormant conditionals. .lagoon.yml tested [ "VORTEX_PROVISION_TYPE" = "profile" ] without a $, so the profile branch had never run for any consumer. close-pull-request.yml mapped LAGOON_WEBHOOK_ENDPOINT to itself via env., so the shell default always won and a configured endpoint was ignored. Both activate paths that were silently dead.
  2. ProcessRunner output was truncated. run() did $this->output = $buffer inside the Symfony Process callback, which fires once per read chunk - so getOutput() only ever returned the last chunk. Any command producing more than one chunk lost its earlier output; BuildCommand::getLocalDevUrl() runs docker compose config --format json through it. Now accumulates. This one was not on the approved list: it was found while converting Git::getTrackedFiles() to the runner, and converting on top of the bug would have introduced a truncation regression.
  3. Drupal.behaviors.your_site_theme is now camelCase. The key doubles as an installer substitution token, and the installer only substituted your_site_theme and YourSiteTheme, so this required adding 'yourSiteTheme' => Converter::camel($v) to the theme handler's map. Verified in the regenerated fixture: installed sites get Drupal.behaviors.starWars.
  4. Two missing dependency checks. vortex-task-copy-db-acquia checked for curl but calls php; vortex-deploy-lagoon used jq unchecked. Both now fail with a clear message instead of a shell error.
  5. GITHUB_TOKEN is withheld over plain HTTP. requestHeaders() attached the bearer token to every request regardless of scheme, and Artifact accepts arbitrary http:// repository URLs, so a plain-HTTP template repo could pull the token onto an unencrypted connection. It now takes the destination and only authorises https://. This was pre-existing behaviour at all four call sites; consolidating them into one function is what made it a three-line fix. Host allowlisting was deliberately not added - it would break GitHub Enterprise and self-hosted mirrors, which are legitimate targets reached over HTTPS.

Fixtures

One fixture churned for a reason worth stating: removing Internal's whole-file json_encode(JSON_PRETTY_PRINT) round-trip means JsonManipulator's own compact array formatting now survives, so starter_drupal_cms_profile/composer.json writes "require": ["vendor/drupal/cms/composer.json"] on one line. Valid JSON, one line, one scenario out of 149. The re-encode was an incidental normaliser, not a designed one.

The Vortex badge in README.md is now masked in fixtures like every other version stamp. Without it, a tagged checkout rewrites the README in all ~150 scenarios. The mask stops at the badge URL delimiter rather than the first hyphen, so refs containing hyphens normalise correctly.

Review notes

Two findings from review were real defects in code this PR introduced, and are worth calling out because both were invisible to the gates:

  • The badge mask used [^-]+, which stops at the first hyphen - so it silently failed on any ref containing one, including this branch. Fixtures still matched, because the fixture and the generated output shared the same unmasked value.
  • The theme behavior resolved its target with context.querySelector('body'). That searches descendants only, so when Drupal passes document.body as the context after an AJAX insert, it returned null and the behavior did nothing. It now resolves through context.ownerDocument.body, which is correct for the document, for the body itself, and for any inserted element.

Four further suggestions were declined with reasons on the PR: applying the agent-facing "one simple Bash command" instruction to CI YAML and shipped shell scripts (it governs agent tool calls, and applying it would have made one of six identical find | xargs copies and two of ~40 tooling scripts diverge), importing RuntimeException in a single file where ~40 sites use it fully qualified, and a fixture-path "fix" that would have broken a passing test - this suite's fixture root is tests/Unit/Fixtures, not tests/Fixtures.

Not applied

Items from the plan that were investigated and deliberately left alone:

  • Merging the twin handlers (DatabaseFetchSource/MigrationFetchSource, DatabaseImage/MigrationImage). PromptManager::initHandlers() scans the Handlers/ directory and runs new $class() on everything implementing HandlerInterface, excluding only AbstractHandler by name - an abstract intermediate placed there passes class_exists() and would be instantiated, a fatal error. The twins also differ behaviourally rather than cosmetically: the Migration variants carry a load-bearing !empty($this->response) guard, DatabaseFetchSource has an extra NONE option, and their dependency predicates encode different semantics.
  • Everything under web/sites/default/includes/ - the trusted-host convergence, the settings guard ordering, and the approved === '1' loosening. Blocked rather than declined: the repo root has no vendor/, so EnvironmentSettingsTest (16 scenarios) and SwitchableSettingsTest cannot run locally, and ahoy test-unit needs Docker. These would have been pushed blind, with EnvironmentSettingsTest's fail-fast masking making a partial fix look green. Worth doing next with the root dependencies installed - the trusted-host item is also a real bug fix, since two of the three constructions build a single alternation ^a\.com|b\.com$, which by precedence is unanchored.
  • The SHELL_VERBOSITY sweep. ToolingBootstrapTest deliberately uses 0 and documents why on its OUTPUT_ENV constant; it asserts on Composer output the sweep would suppress. The FALSE sites are a third, also deliberate, intent.
  • The reported doubled guard on the GHA SDC step. Investigated and found not to be drift: both providers nest the same two fences, and both tokens are load-bearing because the step validates ${DRUPAL_THEME}.

Remaining classes and their decided canonical forms are recorded in the working notes, along with the exact assertion that changes for the settings work.

Before / After

BEFORE                                   AFTER

composer.json edits, 5 shapes            one shape
┌──────────────────────────────┐         ┌──────────────────────────────┐
│ regex on raw JSON        x5  │         │ JsonManipulator::updateFile  │
│ json_decode/encode       x1  │  ────►  │   ├─ guard + throw           │
│ fromFile() unguarded     x2  │         │   ├─ callback mutates        │
│ fromFile() silent skip   x3  │         │   └─ write                   │
└──────────────────────────────┘         └──────────────────────────────┘
                                         discovery keeps nullsafe: the
                                         file may legitimately be absent


ProcessRunner output

  chunk1 ─┐                                chunk1 ─┐
  chunk2 ─┼─► $this->output = $buffer      chunk2 ─┼─► $this->output .= $buffer
  chunk3 ─┘         │                      chunk3 ─┘         │
                    ▼                                        ▼
              "chunk3"  ← earlier                  "chunk1chunk2chunk3"
                          output lost


queue-vs-execute

  handler_1..n  queue ops                  handler_1..n  queue ops
  Internal      queue ops                  Internal      queue ops
      └─► runDirectoryTasks()  ← inside     PromptManager
            a handler, by convention           └─► runDirectoryTasks()  ← after
            "always last"                            every handler has run


test layout

  tests/Unit/                              tests/Unit/
  ├── ConfigTest.php                       ├── Utils/
  ├── TuiTest.php                          │   ├── ConfigTest.php
  ├── TaskTest.php                         │   └── TuiTest.php
  └── Handlers/                            ├── Task/TaskTest.php
      └── ThemeHandler…Test.php            └── Prompts/Handlers/
                                               └── ThemeHandler…Test.php
                                           mirrors src/

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 18 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 868df1c8-4ab5-4100-8bca-1f3388dab362

📥 Commits

Reviewing files that changed from the base of the PR and between 3aa70e6 and fac6e02.

⛔ Files ignored due to path filters (13)
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/web/modules/custom/sw_demo/js/sw_demo.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/web/modules/custom/sw_demo/js/tests/sw_demo.test.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/web/themes/custom/star_wars/js/star_wars.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/modules/custom/sw_demo/js/sw_demo.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/modules/custom/sw_demo/js/tests/sw_demo.test.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/themes/custom/star_wars/js/star_wars.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/modules/custom/sw_demo/js/sw_demo.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/modules/custom/sw_demo/js/tests/sw_demo.test.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/themes/custom/star_wars/js/star_wars.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/names/web/modules/custom/the_force_demo/js/tests/the_force_demo.test.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/names/web/modules/custom/the_force_demo/js/the_force_demo.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/names/web/themes/custom/lightsaber/js/lightsaber.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/theme_custom/web/themes/custom/light_saber/js/light_saber.js is excluded by !.vortex/installer/tests/Fixtures/**
📒 Files selected for processing (7)
  • .vortex/installer/src/Downloader/RepositoryDownloader.php
  • .vortex/installer/src/Prompts/Handlers/Modules.php
  • .vortex/installer/src/Utils/JsonManipulator.php
  • .vortex/installer/tests/Functional/FunctionalTestCase.php
  • web/modules/custom/ys_demo/js/tests/ys_demo.test.js
  • web/modules/custom/ys_demo/js/ys_demo.js
  • web/themes/custom/your_site_theme/js/your_site_theme.js

Walkthrough

The change aligns CI and shell configuration, refactors installer process and Composer handling, updates installer tests and namespaces, and namespaces the demo counter selectors and storage key. It also replaces the theme’s jQuery behavior with a native Drupal behavior.

Changes

Installer and tooling changes

Layer / File(s) Summary
CI, workflow, and shell alignment
.circleci/*, .github/workflows/*, .lagoon.yml, .vortex/tooling/*, hooks/library/*
CI images, workflow markers, shell quoting, task shells, debug checks, and required-command checks are updated.
Runner, logging, and downloader contracts
.vortex/installer/src/Runner/*, .vortex/installer/src/Logger/*, .vortex/installer/src/Downloader/*, .vortex/installer/src/Utils/Git.php
Logger injection, exit-code validation, process output accumulation, archive extraction, Git execution, HTTP options, and request headers are centralized.
Prompt processing and installer orchestration
.vortex/installer/src/Command/*, .vortex/installer/src/Prompts/PromptManager.php, .vortex/installer/src/Prompts/PromptType.php, .vortex/installer/src/Prompts/InstallerPresenter.php
Handler lookup, prompt callable resolution, profile detection, schema validation, directory tasks, and executable detection are updated.
Handler cleanup and structured project updates
.vortex/installer/src/Prompts/Handlers/*, .vortex/installer/src/Utils/JsonManipulator.php
Handlers use early returns, shared module resolution, renamed tool markers, and structured Composer updates.
Installer validation and test support
.vortex/installer/tests/*, .vortex/installer/CLAUDE.md
Test namespaces, provider placement, Git test helpers, functional scanning, fixtures, and runner tests are updated.
Demo counter and theme behavior
web/modules/custom/ys_demo/*, web/themes/custom/your_site_theme/*
Counter selectors and storage keys use the ys-demo namespace. The theme behavior uses native Drupal behavior processing.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • drevops/vortex#2533: Both changes update CommandRunner and ProcessRunner exit-code handling.
  • drevops/vortex#2761: Both changes update installer token markers and generated configuration cleanup.
  • drevops/vortex#2781: Both changes update Gitleaks markers across installer and CI files.

Suggested labels: Needs review, A4

Poem

A rabbit checks the runners in line,
With cleaner prompts and JSON fine.
The counter hops to namespaced keys,
While Drupal behaviors move with ease.
CI signs its markers bright—
And all the tests burrow right.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the pull request's main goal of converging conventions across the installer, tooling, and shipped template.
Docstring Coverage ✅ Passed Docstring coverage is 92.54% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/converge-remaining

Comment @coderabbitai help to get the list of available commands.

@github-actions

This comment has been minimized.

@AlexSkrypnyk

This comment has been minimized.

2 similar comments
@AlexSkrypnyk

This comment has been minimized.

@AlexSkrypnyk

This comment has been minimized.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/vortex-test-common.yml:
- Line 55: Update the workflow commands at the preprocessing step and the Ahoy
installation blocks to use exactly one simple Bash command per invocation:
replace the pipeline and chained sed operations with a single find -exec command
containing both sed expressions, and split each &&-chained installation sequence
into separate command steps while preserving their order.

In @.lagoon.yml:
- Line 81: Remove prohibited compound Bash syntax at all affected sites: in
.lagoon.yml lines 81-81, place then on its own line after the test command; in
.vortex/tooling/src/vortex-deploy-lagoon lines 170-170 and
.vortex/tooling/src/vortex-task-copy-db-acquia lines 75-75, place do on its own
line and replace each || failure path with an equivalent if block. Ensure every
Bash tool call contains exactly one simple command and no chaining operators.

In @.vortex/installer/src/Downloader/RepositoryDownloader.php:
- Around line 403-409: Update requestHeaders() and its callers so GITHUB_TOKEN
is added only when the destination is HTTPS on an approved GitHub host; omit
Authorization for arbitrary HTTP/HTTPS repositories and non-GitHub hosts. Ensure
validateRemoteRepositoryExists(), validateRemoteRefExists(), and
downloadArchive() provide the destination when constructing headers, while
preserving the existing User-Agent header.
- Around line 403-409: The requestHeaders() method must not attach GITHUB_TOKEN
when the repository URL uses HTTP. Validate the repository URL scheme before
adding the Authorization header, allowing the bearer token only for HTTPS
requests; alternatively enforce HTTPS in Artifact::parseUri() so HTTP repository
URLs are rejected before validateRemoteRepositoryExists(),
validateRemoteRefExists(), or downloadArchive() issue requests.

In @.vortex/installer/src/Prompts/Handlers/Modules.php:
- Around line 118-124: Update the package-removal callback in process() so each
removed package is deleted from both Composer require and require-dev sections.
Keep the existing removed_packages handling and JsonManipulator::updateFile flow
unchanged.

In @.vortex/installer/src/Runner/AbstractRunner.php:
- Around line 158-160: Update the setExitCode method to import RuntimeException
at the file level and use the imported class instead of the fully qualified
exception reference, preserving the existing invalid-exit-code validation and
message.

In @.vortex/installer/src/Utils/JsonManipulator.php:
- Around line 54-64: Update JsonManipulator::updateFile to write getContents()
to a temporary file, verify file_put_contents() succeeds with the complete
expected byte count, then atomically replace the target using rename(); clean up
and throw RuntimeException on any failure. Update the method’s `@throws`
documentation to include write or replacement failures.

In @.vortex/installer/tests/Functional/FunctionalTestCase.php:
- Around line 113-114: Update the vortex_badge replacement regex in
FunctionalTestCase to allow hyphens within the Vortex ref while stopping at the
closing badge URL delimiter, so refs such as feature/foo-bar are normalized
correctly; leave the vortex_badge_url replacement unchanged.

In @.vortex/installer/tests/Unit/Utils/EnvTest.php:
- Line 127: Update the $fixture_dir assignment in testWriteValueDotenv() to
resolve from the Utils test directory to the sibling tests/Fixtures/env
directory, so copy() can locate the _baseline/.env fixture.

In `@AGENTS.md`:
- Around line 15-16: Remove the OVERRIDE instruction block from AGENTS.md,
including its claims that repository rules supersede system prompts and its
direction to ignore the system’s command-chaining guidance. Leave the
surrounding repository instructions unchanged.

In `@web/themes/custom/your_site_theme/js/your_site_theme.js`:
- Around line 8-13: Update attach() to use the context itself when it is
document.body before falling back to context.querySelector('body'), preserving
the existing processed-body guard. Add a regression test covering
attach(document.body) and verify the body is handled successfully.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e8245bbc-554d-4629-99ce-e40297d2a86f

📥 Commits

Reviewing files that changed from the base of the PR and between 22deef4 and 3aa70e6.

⛔ Files ignored due to path filters (122)
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/.docker/cli.dockerfile is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/.github/workflows/audit.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/.github/workflows/build-test-deploy.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/.github/workflows/draft-release-notes.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/.ignorecontent is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/AGENTS.md is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/README.md is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/config/ci/.htaccess is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/config/default/.htaccess is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/config/dev/.htaccess is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/config/local/.htaccess is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/config/stage/.htaccess is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/docker-compose.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/drush/php-ini/drush.ini is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/recipes/.gitignore is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/tests/phpunit/bootstrap.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/web/modules/custom/sw_base/tests/src/Traits/BrowserHtmlDebugTrait.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/web/modules/custom/sw_demo/js/sw_demo.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/web/modules/custom/sw_demo/js/tests/sw_demo.test.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/web/modules/custom/sw_demo/templates/sw-demo-counter-block.html.twig is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/web/modules/custom/sw_demo/tests/src/FunctionalJavascript/CounterBlockTest.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/web/themes/custom/star_wars/js/star_wars.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/_baseline/web/themes/custom/star_wars/tests/src/Functional/ExampleTest.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/ciprovider_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/code_coverage_provider_codecov_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/db_fetch_source_acquia/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/db_fetch_source_container_registry/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/db_fetch_source_ftp/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/db_fetch_source_lagoon/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/db_fetch_source_s3/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/deploy_types_all_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/deploy_types_none_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/deps_updates_provider_ci_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/deps_updates_provider_ci_circleci/.github/workflows/update-dependencies.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/deps_updates_provider_ci_gha/.github/workflows/update-dependencies.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/deps_updates_provider_none/.github/workflows/update-dependencies.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/deps_updates_provider_none/README.md is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/modules/custom/sw_base/tests/src/Traits/BrowserHtmlDebugTrait.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/modules/custom/sw_demo/js/sw_demo.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/modules/custom/sw_demo/js/tests/sw_demo.test.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/modules/custom/sw_demo/templates/sw-demo-counter-block.html.twig is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/modules/custom/sw_demo/tests/src/FunctionalJavascript/CounterBlockTest.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/themes/custom/star_wars/js/star_wars.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/themes/custom/star_wars/tests/src/Functional/ExampleTest.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/hooks/library/copy-db.sh is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/hooks/library/copy-files.sh is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/hooks/library/notify-deployment.sh is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/hooks/library/provision.sh is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/hooks/library/purge-cache.sh is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_acquia/tests/phpunit/bootstrap.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_lagoon/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_lagoon/.github/workflows/close-pull-request.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_lagoon/.lagoon.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/modules/custom/sw_base/tests/src/Traits/BrowserHtmlDebugTrait.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/modules/custom/sw_demo/js/sw_demo.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/modules/custom/sw_demo/js/tests/sw_demo.test.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/modules/custom/sw_demo/templates/sw-demo-counter-block.html.twig is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/modules/custom/sw_demo/tests/src/FunctionalJavascript/CounterBlockTest.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/themes/custom/star_wars/js/star_wars.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/themes/custom/star_wars/tests/src/Functional/ExampleTest.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/hooks/library/copy-db.sh is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/hooks/library/copy-files.sh is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/hooks/library/notify-deployment.sh is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/hooks/library/provision.sh is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/hooks/library/purge-cache.sh is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/tests/phpunit/bootstrap.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___lagoon/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___lagoon/.github/workflows/close-pull-request.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/hosting_project_name___lagoon/.lagoon.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/migration_disabled_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/migration_disabled_lagoon/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/migration_disabled_lagoon/.github/workflows/close-pull-request.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/migration_disabled_lagoon/.lagoon.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/migration_enabled_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/migration_enabled_lagoon/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/migration_enabled_lagoon/.github/workflows/close-pull-request.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/migration_enabled_lagoon/.lagoon.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/names/AGENTS.md is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/names/web/modules/custom/the_force_base/tests/src/Traits/BrowserHtmlDebugTrait.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/names/web/modules/custom/the_force_demo/js/tests/the_force_demo.test.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/names/web/modules/custom/the_force_demo/js/the_force_demo.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/names/web/modules/custom/the_force_demo/templates/the-force-demo-counter-block.html.twig is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/names/web/modules/custom/the_force_demo/tests/src/FunctionalJavascript/CounterBlockTest.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/names/web/themes/custom/lightsaber/js/lightsaber.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/names/web/themes/custom/lightsaber/tests/src/Functional/ExampleTest.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/provision_database_lagoon/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/provision_database_lagoon/.github/workflows/close-pull-request.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/provision_database_lagoon/.lagoon.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/provision_profile/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/provision_profile/.github/workflows/build-test-deploy.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/starter_drupal_cms_profile/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/starter_drupal_cms_profile/composer.json is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/starter_drupal_profile/.env is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/theme_claro/.docker/cli.dockerfile is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/theme_custom/web/themes/custom/light_saber/js/light_saber.js is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/theme_custom/web/themes/custom/light_saber/tests/src/Functional/ExampleTest.php is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/theme_olivero/.docker/cli.dockerfile is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/theme_stark/.docker/cli.dockerfile is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/timezone_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_groups_no_be_lint_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_groups_no_be_tests_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_groups_no_fe_lint_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_groups_no_fe_lint_no_theme/.docker/cli.dockerfile is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_groups_no_fe_lint_no_theme_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_groups_no_fe_lint_no_theme_circleci/.docker/cli.dockerfile is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_behat_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_dclint_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_docker_linters_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_eslint_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_eslint_no_theme/.docker/cli.dockerfile is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_hadolint_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_jest_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_phpcs_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_phpstan_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_phpunit_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_rector_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_stylelint_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_stylelint_no_theme/.docker/cli.dockerfile is excluded by !.vortex/installer/tests/Fixtures/**
  • .vortex/installer/tests/Fixtures/handler_process/tools_no_twig_circleci/.circleci/config.yml is excluded by !.vortex/installer/tests/Fixtures/**
📒 Files selected for processing (169)
  • .circleci/config.yml
  • .circleci/vortex-test-common.yml
  • .docker/cli.dockerfile
  • .env
  • .github/workflows/audit.yml
  • .github/workflows/build-test-deploy.yml
  • .github/workflows/close-pull-request.yml
  • .github/workflows/draft-release-notes.yml
  • .github/workflows/update-dependencies.yml
  • .github/workflows/vortex-publish-tooling.yml
  • .github/workflows/vortex-test-common.yml
  • .github/workflows/vortex-test-docs.yml
  • .github/workflows/vortex-test-installer.yml
  • .lagoon.yml
  • .vortex/installer/CLAUDE.md
  • .vortex/installer/src/Command/BuildCommand.php
  • .vortex/installer/src/Command/CheckRequirementsCommand.php
  • .vortex/installer/src/Command/InstallCommand.php
  • .vortex/installer/src/Downloader/Archiver.php
  • .vortex/installer/src/Downloader/Downloader.php
  • .vortex/installer/src/Downloader/RepositoryDownloader.php
  • .vortex/installer/src/Logger/LoggableInterface.php
  • .vortex/installer/src/Logger/LoggerAwareInterface.php
  • .vortex/installer/src/Logger/LoggerAwareTrait.php
  • .vortex/installer/src/Prompts/Handlers/AssignAuthorPr.php
  • .vortex/installer/src/Prompts/Handlers/CodeCoverageProvider.php
  • .vortex/installer/src/Prompts/Handlers/CustomModules.php
  • .vortex/installer/src/Prompts/Handlers/DatabaseImage.php
  • .vortex/installer/src/Prompts/Handlers/FrontendBuild.php
  • .vortex/installer/src/Prompts/Handlers/Gitleaks.php
  • .vortex/installer/src/Prompts/Handlers/HostingProvider.php
  • .vortex/installer/src/Prompts/Handlers/Internal.php
  • .vortex/installer/src/Prompts/Handlers/LabelMergeConflictsPr.php
  • .vortex/installer/src/Prompts/Handlers/Migration.php
  • .vortex/installer/src/Prompts/Handlers/MigrationImage.php
  • .vortex/installer/src/Prompts/Handlers/ModulePrefix.php
  • .vortex/installer/src/Prompts/Handlers/Modules.php
  • .vortex/installer/src/Prompts/Handlers/PreserveDocsProject.php
  • .vortex/installer/src/Prompts/Handlers/Services.php
  • .vortex/installer/src/Prompts/Handlers/Starter.php
  • .vortex/installer/src/Prompts/Handlers/Theme.php
  • .vortex/installer/src/Prompts/Handlers/Tools.php
  • .vortex/installer/src/Prompts/Handlers/VisualRegression.php
  • .vortex/installer/src/Prompts/InstallerPresenter.php
  • .vortex/installer/src/Prompts/PromptManager.php
  • .vortex/installer/src/Prompts/PromptType.php
  • .vortex/installer/src/Runner/AbstractRunner.php
  • .vortex/installer/src/Runner/CommandRunner.php
  • .vortex/installer/src/Runner/ProcessRunner.php
  • .vortex/installer/src/Runner/RunnerInterface.php
  • .vortex/installer/src/Task/TaskOutput.php
  • .vortex/installer/src/Utils/Git.php
  • .vortex/installer/src/Utils/JsonManipulator.php
  • .vortex/installer/src/Utils/Strings.php
  • .vortex/installer/tests/Functional/FunctionalTestCase.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/AbstractHandlerProcessTestCase.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/AiCodeInstructionsHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/BaselineHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/CiProviderHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/CodeCoverageProviderHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/CodeProviderHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/CustomModulesHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/DatabaseFetchSourceHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/DependencyUpdatesProviderHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/DeployTypeHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/DocsHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/FrontendBuildHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/GitleaksHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/HostingProjectNameHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/HostingProviderHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/MigrationFetchSourceHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/MigrationHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/ModulesHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/NamesHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/NotificationChannelsHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/ProfileHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/ProvisionTypeHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/PullRequestHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/ServicesHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/StarterHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/ThemeHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/TimezoneHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/ToolsHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/VersionSchemeHandlerProcessTest.php
  • .vortex/installer/tests/Functional/Prompts/Handlers/VisualRegressionHandlerProcessTest.php
  • .vortex/installer/tests/Unit/Downloader/ArchiverTest.php
  • .vortex/installer/tests/Unit/Downloader/RepositoryDownloaderTest.php
  • .vortex/installer/tests/Unit/Logger/FileLoggerTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/AbstractHandlerDiscoveryTestCase.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/AbstractHandlerTypeTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/AiCodeInstructionsHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/BaselineHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/CiProviderHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/CodeCoverageProviderHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/CodeProviderHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/CustomModulesHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/DatabaseFetchSourceHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/DatabaseImageHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/DependencyUpdatesProviderHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/DeployTypesHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/DocsHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/DomainHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/FrontendBuildHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/HostingProjectNameHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/HostingProviderHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/MigrationFetchSourceHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/MigrationHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/MigrationImageHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/ModulePrefixHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/ModulesHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/NamesHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/NotificationChannelsHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/ProfileHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/ProvisionTypeHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/PullRequestHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/ServicesHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/StarterHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/ThemeHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/TimezoneHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/ToolsHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/VersionSchemeHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/Handlers/WebrootHandlerDiscoveryTest.php
  • .vortex/installer/tests/Unit/Prompts/PromptTypeTest.php
  • .vortex/installer/tests/Unit/Runner/AbstractRunnerTest.php
  • .vortex/installer/tests/Unit/Runner/CommandRunnerTest.php
  • .vortex/installer/tests/Unit/Runner/ProcessRunnerTest.php
  • .vortex/installer/tests/Unit/Task/TaskTest.php
  • .vortex/installer/tests/Unit/Utils/ConfigTest.php
  • .vortex/installer/tests/Unit/Utils/ConverterTest.php
  • .vortex/installer/tests/Unit/Utils/EnvTest.php
  • .vortex/installer/tests/Unit/Utils/FileTest.php
  • .vortex/installer/tests/Unit/Utils/GitTest.php
  • .vortex/installer/tests/Unit/Utils/JsonManipulatorTest.php
  • .vortex/installer/tests/Unit/Utils/StringsTest.php
  • .vortex/installer/tests/Unit/Utils/TuiTest.php
  • .vortex/installer/tests/Unit/Utils/ValidatorTest.php
  • .vortex/installer/tests/Unit/Utils/VersionTest.php
  • .vortex/installer/tests/Unit/Utils/YamlTest.php
  • .vortex/tests/lint.dockerfiles.sh
  • .vortex/tests/lint.markdown.sh
  • .vortex/tests/phpunit/Functional/AhoyWorkflowTest.php
  • .vortex/tests/phpunit/Functional/DockerComposeWorkflowTest.php
  • .vortex/tests/phpunit/Functional/ToolingBootstrapTest.php
  • .vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php
  • .vortex/tooling/src/vortex-deploy-lagoon
  • .vortex/tooling/src/vortex-task-copy-db-acquia
  • AGENTS.md
  • README.dist.md
  • config/ci/.htaccess
  • config/default/.htaccess
  • config/dev/.htaccess
  • config/local/.htaccess
  • config/stage/.htaccess
  • docker-compose.yml
  • drush/php-ini/drush.ini
  • hooks/library/copy-db.sh
  • hooks/library/copy-files.sh
  • hooks/library/notify-deployment.sh
  • hooks/library/provision.sh
  • hooks/library/purge-cache.sh
  • recipes/.gitignore
  • tests/phpunit/bootstrap.php
  • web/modules/custom/ys_base/tests/src/Traits/BrowserHtmlDebugTrait.php
  • web/modules/custom/ys_demo/js/tests/ys_demo.test.js
  • web/modules/custom/ys_demo/js/ys_demo.js
  • web/modules/custom/ys_demo/templates/ys-demo-counter-block.html.twig
  • web/modules/custom/ys_demo/tests/src/FunctionalJavascript/CounterBlockTest.php
  • web/themes/custom/your_site_theme/js/your_site_theme.js
  • web/themes/custom/your_site_theme/tests/src/Functional/ExampleTest.php
💤 Files with no reviewable changes (4)
  • .docker/cli.dockerfile
  • .vortex/installer/src/Logger/LoggableInterface.php
  • web/modules/custom/ys_base/tests/src/Traits/BrowserHtmlDebugTrait.php
  • drush/php-ini/drush.ini

Comment thread .github/workflows/vortex-test-common.yml
Comment thread .lagoon.yml
Comment on lines +403 to +409
protected static function requestHeaders(array $headers = []): array {
$headers['User-Agent'] = 'Vortex-Installer';

$github_token = Env::get('GITHUB_TOKEN');
if ($github_token) {
$headers['Authorization'] = sprintf('Bearer %s', $github_token);
}

@coderabbitai coderabbitai Bot Aug 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- Downloader structure and request call sites ---'
ast-grep outline .vortex/installer/src/Downloader/RepositoryDownloader.php
rg -n -C 4 'requestHeaders|Artifact|getRepoUrl|DEFAULT_REPO|allow_redirects|Authorization|GITHUB_TOKEN' .vortex/installer/src .vortex/installer/composer.json .vortex/installer/composer.lock 2>/dev/null || true

printf '%s\n' '--- Artifact definitions and URL validation ---'
rg -n -C 6 'class Artifact|getRepoUrl|repoUrl|repository.*url|parse_url|https?://' .vortex 2>/dev/null || true

Repository: drevops/vortex

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- Candidate source files ---'
git ls-files | rg '(^|/)(Artifact|RepositoryDownloader)\.(php|json|lock)$|Artifact.*\.php$|composer\.(json|lock)$' | head -200

printf '%s\n' '--- Downloader request paths ---'
rg -n -C 5 'requestHeaders|GITHUB_TOKEN|allow_redirects|function (request|download|validate)|getRepoUrl' .vortex/installer/src/Downloader/RepositoryDownloader.php

printf '%s\n' '--- Artifact URL handling ---'
rg -n -C 5 --glob '!tests/Fixtures/**' --glob '*.php' 'class Artifact|getRepoUrl|repoUrl|parse_url|DEFAULT_REPO' .vortex/installer/src .vortex/installer 2>/dev/null | head -400

printf '%s\n' '--- Guzzle dependency and redirect configuration ---'
python3 - <<'PY'
import json
from pathlib import Path

for name in (".vortex/installer/composer.json", ".vortex/installer/composer.lock"):
    p = Path(name)
    if not p.exists():
        continue
    data = json.loads(p.read_text())
    print(name)
    if name.endswith("composer.json"):
        print("require:", {k:v for k,v in data.get("require", {}).items() if "guzzle" in k.lower()})
    else:
        for package in data.get("packages", []) + data.get("packages-dev", []):
            if "guzzle" in package.get("name", "").lower():
                print(package.get("name"), package.get("version"), package.get("source", {}).get("reference"))
PY

Repository: drevops/vortex

Length of output: 45381


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- Artifact parsing, validation, and normalization ---'
cat -n .vortex/installer/src/Downloader/Artifact.php | sed -n '35,155p'

printf '%s\n' '--- Token-bearing request sinks and archive downloader ---'
cat -n .vortex/installer/src/Downloader/RepositoryDownloader.php | sed -n '175,345p'

printf '%s\n' '--- Relevant tests for remote URL schemes and hosts ---'
rg -n -C 5 --glob 'ArtifactTest.php' 'http://|https://|ssh://|git://|custom/repo|fromUri|isRemote|getRepoUrl' .vortex/installer/tests/Unit/Downloader/ArtifactTest.php

printf '%s\n' '--- Read-only behavioral probe of the URL scheme predicates ---'
python3 - <<'PY'
from urllib.parse import urlparse

cases = [
    "https://github.com/drevops/vortex.git",
    "https://attacker.example/repo.git",
    "http://attacker.example/repo.git",
    "ssh://git@attacker.example/repo.git",
    "git://attacker.example/repo.git",
]

for value in cases:
    parsed = urlparse(value)
    is_remote = bool(parsed.scheme) and parsed.scheme.lower() in {"http", "https", "ssh", "git"}
    normalized = value[:-4] if value.endswith(".git") else value
    print(f"{value}\tisRemote={is_remote}\tgetRepoUrl={normalized}")
PY

Repository: drevops/vortex

Length of output: 22690


Sensitive Data Exposure (CWE-200): Exposure of Sensitive Information to an Unauthorized Actor

Exploitability: Moderate

Scope GITHUB_TOKEN by destination.

Artifact accepts arbitrary http and https repository URLs. validateRemoteRepositoryExists(), validateRemoteRefExists(), and downloadArchive() send Authorization: Bearer ... to those URLs. Add the token only for approved HTTPS GitHub hosts; omit it for other destinations.

🧰 Tools
🪛 PHPMD (2.15.0)

[error] 406-406: Avoid using static access to class '\DrevOps\VortexInstaller\Utils\Env' in method 'requestHeaders'. (undefined)

(StaticAccess)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.vortex/installer/src/Downloader/RepositoryDownloader.php around lines 403 -
409, Update requestHeaders() and its callers so GITHUB_TOKEN is added only when
the destination is HTTPS on an approved GitHub host; omit Authorization for
arbitrary HTTP/HTTPS repositories and non-GitHub hosts. Ensure
validateRemoteRepositoryExists(), validateRemoteRefExists(), and
downloadArchive() provide the destination when constructing headers, while
preserving the existing User-Agent header.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

# First, map the structure of RepositoryDownloader.php
ast-grep outline .vortex/installer/src/Downloader/RepositoryDownloader.php

Repository: drevops/vortex

Length of output: 563


🏁 Script executed:

# Find where requestHeaders() is called
rg 'requestHeaders' .vortex/installer/src/Downloader/ -A 2 -B 2

Repository: drevops/vortex

Length of output: 2561


🏁 Script executed:

# Find the Artifact class and check for HTTP validation
fd -t f 'Artifact.php' .vortex/installer/src/

Repository: drevops/vortex

Length of output: 198


🏁 Script executed:

# Search for URL validation patterns in the codebase
rg 'https?|scheme|http|protocol' .vortex/installer/src/Downloader/ -i -A 2 -B 2 | head -100

Repository: drevops/vortex

Length of output: 7760


🏁 Script executed:

# Read the Artifact.php file to understand URL validation
cat -n .vortex/installer/src/Downloader/Artifact.php | head -200

Repository: drevops/vortex

Length of output: 7906


🏁 Script executed:

# Read RepositoryDownloader.php to understand the full flow from user input to bearer token
cat -n .vortex/installer/src/Downloader/RepositoryDownloader.php | head -150

Repository: drevops/vortex

Length of output: 6577


🏁 Script executed:

# Look at how repo_url is used in validateRemoteRepositoryExists and validateRemoteRefExists
sed -n '330,380p' .vortex/installer/src/Downloader/RepositoryDownloader.php

Repository: drevops/vortex

Length of output: 1945


🏁 Script executed:

# Check the downloadFromRemote flow to see where HTTP validation should happen
sed -n '90,200p' .vortex/installer/src/Downloader/RepositoryDownloader.php

Repository: drevops/vortex

Length of output: 4424


🏁 Script executed:

# Check where Artifact::fromUri is called to understand entry points
rg 'fromUri|new Artifact|Artifact::create' .vortex/installer/src/ -B 2 -A 2

Repository: drevops/vortex

Length of output: 1019


🏁 Script executed:

# Look at the complete validateRemoteRepositoryExists and validateRemoteRefExists methods
sed -n '280,325p' .vortex/installer/src/Downloader/RepositoryDownloader.php

Repository: drevops/vortex

Length of output: 1747


🏁 Script executed:

# Check the downloadArchive method to see full token usage
sed -n '240,275p' .vortex/installer/src/Downloader/RepositoryDownloader.php

Repository: drevops/vortex

Length of output: 1205


🏁 Script executed:

# Check OptionsResolver to see how user input flows to Artifact
rg -B 5 -A 10 'Artifact::fromUri' .vortex/installer/src/Utils/OptionsResolver.php

Repository: drevops/vortex

Length of output: 728


🏁 Script executed:

# Verify if there's any HTTPS validation at the entry point or config level
rg 'https|http|scheme' .vortex/installer/src/Utils/OptionsResolver.php -B 2 -A 2

Repository: drevops/vortex

Length of output: 152


🏁 Script executed:

# Check all places where repo_url is used with requestHeaders
rg 'repo_url.*requestHeaders|requestHeaders.*repo' .vortex/installer/src/Downloader/ -B 2 -A 2

Repository: drevops/vortex

Length of output: 152


🏁 Script executed:

# Read Artifact parseUri method completely to confirm HTTP is accepted
sed -n '150,215p' .vortex/installer/src/Downloader/Artifact.php

Repository: drevops/vortex

Length of output: 3241


🏁 Script executed:

# Check if there's any client configuration that enforces HTTPS
rg 'allow_redirects|verify|ssl|scheme' .vortex/installer/src/Downloader/Downloader.php -B 2 -A 2

Repository: drevops/vortex

Length of output: 240


🏁 Script executed:

# Check the full downloadArchive method
sed -n '220,240p' .vortex/installer/src/Downloader/RepositoryDownloader.php

Repository: drevops/vortex

Length of output: 778


Sensitive Data Exposure (CWE-319): Cleartext Transmission of Sensitive Information

Reachability: External · Exploitability: Moderate

Validate HTTPS before attaching the bearer token to remote requests.

requestHeaders() adds GITHUB_TOKEN without validating the repository URL scheme. Artifact::parseUri() accepts both http:// and https:// URLs without enforcing HTTPS. The repository URL scheme is preserved and used in HEAD and GET requests to validateRemoteRepositoryExists(), validateRemoteRefExists(), and downloadArchive(), all of which receive the bearer token via requestHeaders(). An attacker can supply an HTTP repository URL to force the bearer token to transmit over an unencrypted connection. Add a scheme check in requestHeaders() to attach the bearer token only for HTTPS URLs, or reject HTTP URLs in Artifact::parseUri().

🧰 Tools
🪛 PHPMD (2.15.0)

[error] 406-406: Avoid using static access to class '\DrevOps\VortexInstaller\Utils\Env' in method 'requestHeaders'. (undefined)

(StaticAccess)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.vortex/installer/src/Downloader/RepositoryDownloader.php around lines 403 -
409, The requestHeaders() method must not attach GITHUB_TOKEN when the
repository URL uses HTTP. Validate the repository URL scheme before adding the
Authorization header, allowing the bearer token only for HTTPS requests;
alternatively enforce HTTPS in Artifact::parseUri() so HTTP repository URLs are
rejected before validateRemoteRepositoryExists(), validateRemoteRefExists(), or
downloadArchive() issue requests.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partially fixed in 000d8f2.

Adopted: the HTTPS check. requestHeaders() now takes the destination URL and withholds the token unless the request goes over https://, so an http:// repository URL can no longer force the bearer token onto an unencrypted connection. Worth noting this was pre-existing rather than introduced here - all four call sites built the same unconditional header block before this PR deduplicated them, so consolidating them into one function is what made the fix a three-line change instead of four.

Not adopted: the approved-host allowlist. Restricting the token to github.com would break GitHub Enterprise and self-hosted mirrors, which are legitimate VORTEX_INSTALLER_TEMPLATE_REPO targets and are reached over HTTPS. The transport check addresses the cleartext exposure (CWE-319) without assuming the only valid host is github.com. Scoping by host is a policy decision for the maintainer rather than a convergence change.

The PHPMD StaticAccess note is out of scope: Env::get() is static throughout this codebase, and PHPMD is not part of the project's lint chain (PHPCS, PHPStan and Rector are).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Follow-up commit ---'
git show --stat --oneline 000d8f22
git show --format=fuller --find-renames -- .vortex/installer/src/Downloader/RepositoryDownloader.php 000d8f22

printf '%s\n' '--- Current downloader header implementation and call sites ---'
ast-grep outline .vortex/installer/src/Downloader/RepositoryDownloader.php --items all
rg -n -C 4 'requestHeaders\s*\(|GITHUB_TOKEN|Authorization|downloadArchive|validateRemoteRepositoryExists|validateRemoteRefExists|discoverLatestReleaseRemote' .vortex/installer/src/Downloader/RepositoryDownloader.php

printf '%s\n' '--- Relevant test coverage ---'
rg -n -C 4 --glob '*RepositoryDownloader*Test.php' 'requestHeaders|Authorization|GITHUB_TOKEN|https?://' .vortex/installer/tests

Length of output: 1711


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Checked-out revision and remotes ---'
git rev-parse --verify HEAD
git status --short --branch
git remote -v

printf '%s\n' '--- Current request-header helper and request call sites ---'
rg -n -C 5 'requestHeaders\s*\(|GITHUB_TOKEN|Authorization|downloadArchive\s*\(|validateRemoteRepositoryExists\s*\(|validateRemoteRefExists\s*\(|discoverLatestReleaseRemote\s*\(' .vortex/installer/src/Downloader/RepositoryDownloader.php

printf '%s\n' '--- Current repository downloader tests relevant to request headers ---'
rg -n -C 4 --glob '*RepositoryDownloader*Test.php' 'requestHeaders|Authorization|GITHUB_TOKEN|https?://' .vortex/installer/tests || true

Length of output: 37716


@AlexSkrypnyk, the checked-out revision is 09e4c8e178db105909d7a0fb70f8620ac76307ee. It does not contain 000d8f22.

In this checkout, requestHeaders() still has no destination URL parameter. It still adds Authorization whenever GITHUB_TOKEN is set. The four request paths also call it without a destination URL.

I cannot verify the HTTPS guard from this repository state. Please retry after the PR branch includes 000d8f22.

I understand the GitHub Enterprise and self-hosted mirror requirement. I will treat host scoping as a maintainer policy decision. The unresolved item is limited to verifying that HTTP requests omit the bearer token.


🧬 Code Graph Analysis Results

.vortex/installer/src/Utils/Env.php:18-22

public static function get(string $name, mixed $default = NULL): mixed {
  $vars = getenv();

  return $vars[$name] ?? $default;
}

Env::get() reads an environment variable and returns the supplied default when it is unavailable. RepositoryDownloader::requestHeaders() uses it to retrieve GITHUB_TOKEN.


✏️ Learnings added
Learnt from: AlexSkrypnyk
Repo: drevops/vortex PR: 2895
File: .vortex/installer/src/Downloader/RepositoryDownloader.php:403-409
Timestamp: 2026-08-05T10:00:35.962Z
Learning: In the Vortex installer, `VORTEX_INSTALLER_TEMPLATE_REPO` can target GitHub Enterprise and self-hosted repository mirrors over HTTPS. Do not assume `github.com` is the only valid repository host when reviewing `.vortex/installer/src/Downloader/RepositoryDownloader.php`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

Comment thread .vortex/installer/src/Prompts/Handlers/Modules.php
Comment thread .vortex/installer/src/Runner/AbstractRunner.php
Comment on lines +54 to +64
public static function updateFile(string $file, callable $callback): void {
$instance = self::fromFile($file);

if (!$instance instanceof self) {
throw new \RuntimeException(sprintf('Unable to read a JSON file at "%s".', $file));
}

$callback($instance);

file_put_contents($file, $instance->getContents());
}

@coderabbitai coderabbitai Bot Aug 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- target outline ---'
ast-grep outline .vortex/installer/src/Utils/JsonManipulator.php --match updateFile --view expanded || true
printf '%s\n' '--- target source ---'
cat -n .vortex/installer/src/Utils/JsonManipulator.php | sed -n '1,130p'
printf '%s\n' '--- usages and documentation ---'
rg -n -C 3 'JsonManipulator::updateFile|updateFile\(|`@throws`|file_put_contents' .vortex/installer --glob '*.php' --glob '*.md' | head -n 300
printf '%s\n' '--- repository PHP policy/config ---'
rg -n -C 2 'PHP_VERSION|php-version|phpstan|error_reporting|file_put_contents|atomic|rename\(' . --glob 'composer.json' --glob 'composer.lock' --glob '*.php' --glob '*.yml' --glob '*.yaml' --glob '*.xml' --glob '*.md' | head -n 300

Repository: drevops/vortex

Length of output: 30144


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- JsonManipulator tests ---'
fd -i 'JsonManipulator' .vortex/installer/tests .vortex/installer/src || true
rg -n -C 5 'JsonManipulator|updateFile|fromFile|getContents' .vortex/installer/tests .vortex/installer/src | head -n 400
printf '%s\n' '--- all call sites ---'
rg -n -C 6 'JsonManipulator::updateFile' .vortex/installer/src
printf '%s\n' '--- manifest preparation and follow-up processing ---'
rg -n -C 5 'composer\.json|package\.json' .vortex/installer/src/Prompts .vortex/installer/src/Command | head -n 500
printf '%s\n' '--- PHP runtime and relevant function documentation, if available ---'
php -v 2>/dev/null | head -n 2 || true
php --rf file_put_contents 2>/dev/null | head -n 40 || true

Repository: drevops/vortex

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- JsonManipulator tests, remaining methods ---'
cat -n .vortex/installer/tests/Unit/Utils/JsonManipulatorTest.php | sed -n '220,430p'
printf '%s\n' '--- installer package constraints ---'
find .vortex/installer -maxdepth 2 -type f \( -name composer.json -o -name composer.lock \) -print
rg -n -C 3 '"php"|platform|composer-runtime-api' composer.json .vortex/installer/composer.json 2>/dev/null || true
printf '%s\n' '--- updateFile call-site count and paths ---'
rg -l 'JsonManipulator::updateFile' .vortex/installer/src | sort
rg -o 'JsonManipulator::updateFile\([^;]+' .vortex/installer/src --glob '*.php' | sed -E 's/.*updateFile\(([^,]+).*/\1/' | sort | uniq -c
printf '%s\n' '--- PHP function reference from installed runtime, without executing repository code ---'
command -v php || true
php -v 2>&1 | head -n 3 || true
php --rf file_put_contents 2>&1 | head -n 80 || true

Repository: drevops/vortex

Length of output: 3110


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- PHP stream-write behavior probe ---'
php <<'PHP'
<?php
final class ShortWriteStream {
  public $context;
  public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool { return TRUE; }
  public function stream_write(string $data): int { return max(1, intdiv(strlen($data), 2)); }
  public function stream_close(): void {}
}
final class FailedWriteStream {
  public $context;
  public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool { return TRUE; }
  public function stream_write(string $data): int { return 0; }
  public function stream_close(): void {}
}
stream_wrapper_register('short-write', ShortWriteStream::class);
stream_wrapper_register('failed-write', FailedWriteStream::class);

foreach ([
  'short-write://manifest' => 'abcdef',
  'failed-write://manifest' => 'abcdef',
] as $target => $contents) {
  $result = `@file_put_contents`($target, $contents);
  printf("%s => %s (%s)\n", $target, var_export($result, TRUE), gettype($result));
}
PHP
printf '%s\n' '--- existing file-replacement helpers ---'
rg -n -C 5 'rename\(|tempnam\(|tmpfile\(|file_put_contents' .vortex/installer/src/Utils .vortex/installer/src --glob '*.php' | head -n 300

Repository: drevops/vortex

Length of output: 7070


🏁 Script executed:

#!/bin/bash
set -eu
php <<'PHP'
<?php
final class PartialFailureStream {
  public $context;
  private int $calls = 0;
  public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool { return TRUE; }
  public function stream_write(string $data): int {
    $this->calls++;
    return $this->calls === 1 ? intdiv(strlen($data), 2) : 0;
  }
  public function stream_close(): void {}
}
stream_wrapper_register('partial-failure', PartialFailureStream::class);
$result = `@file_put_contents`('partial-failure://manifest', 'abcdef');
printf("partial failure => %s (%s)\n", var_export($result, TRUE), gettype($result));
PHP

Repository: drevops/vortex

Length of output: 187


Handle failed JSON writes atomically.

file_put_contents() can return false or an incomplete byte count, but updateFile() ignores the result and returns normally. Write the contents to a temporary file, validate the byte count, replace the target with rename(), and throw RuntimeException on failure. Update the @throws documentation.

🧰 Tools
🪛 PHPMD (2.15.0)

[error] 58-58: Missing class import via use statement (line '58', column '17'). (undefined)

(MissingImport)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.vortex/installer/src/Utils/JsonManipulator.php around lines 54 - 64, Update
JsonManipulator::updateFile to write getContents() to a temporary file, verify
file_put_contents() succeeds with the complete expected byte count, then
atomically replace the target using rename(); clean up and throw
RuntimeException on any failure. Update the method’s `@throws` documentation to
include write or replacement failures.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partially fixed in 000d8f2.

Adopted: the unchecked return. updateFile() now compares file_put_contents() against the expected byte count and throws RuntimeException on a short or failed write, and the @throws documentation covers it. This is worth doing precisely because updateFile() is now the single write path for all eight composer.json/package.json call sites, so one guard covers all of them.

Not adopted: the temp-file-plus-rename() dance. Atomic replacement protects a concurrent reader from seeing a torn file. There is no such reader here: updateFile() only ever writes into the installer's own private temporary directory, before anything is copied to the destination, and the process is single-threaded. The added failure modes of a two-step write (a stranded temp file, rename() across filesystems) are real, while the race it guards against is not reachable. Failing loudly on a short write gets the actual benefit without that cost.

The PHPMD MissingImport note is out of scope - see the reply on AbstractRunner.php.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- target method ---'
ast-grep outline .vortex/installer/src/Utils/JsonManipulator.php --match updateFile --view expanded
sed -n '40,90p' .vortex/installer/src/Utils/JsonManipulator.php

printf '%s\n' '--- claimed commit ---'
git show --stat --oneline 000d8f22
git show --format=fuller --find-renames -- .vortex/installer/src/Utils/JsonManipulator.php 000d8f22

printf '%s\n' '--- current updateFile call sites ---'
rg -n -C 5 'JsonManipulator::updateFile\s*\(' .vortex/installer/src --glob '*.php'

printf '%s\n' '--- installer temporary-directory and destination flow ---'
rg -n -C 4 'temp(dir|nam)|sys_get_temp_dir|TemporaryDirectory|copy.*destination|destination.*copy|runProcessors' .vortex/installer/src --glob '*.php' | head -n 300

Length of output: 2602


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="drevops/vortex"
pr="2895"
commit="000d8f22"
path=".vortex/installer/src/Utils/JsonManipulator.php"

printf '%s\n' '--- PR head ---'
gh api "repos/${repo}/pulls/${pr}" --jq '.head.sha'

printf '%s\n' '--- cited commit ---'
gh api "repos/${repo}/commits/${commit}" --jq '{sha: .sha, message: .commit.message, parents: [.parents[].sha]}'

printf '%s\n' '--- target file at cited commit ---'
gh api "repos/${repo}/contents/${path}?ref=${commit}" --jq '.content' | tr -d '\n' | base64 --decode | sed -n '40,90p'

printf '%s\n' '--- commit inclusion in PR ---'
gh api "repos/${repo}/compare/${commit}...$(gh api "repos/${repo}/pulls/${pr}" --jq '.head.sha')" --jq '{status: .status, ahead_by: .ahead_by, behind_by: .behind_by}'

Length of output: 1335


@AlexSkrypnyk, I agree that atomic replacement is not required for the stated private, single-process installer workflow.

I could not verify the adopted short-write guard. GitHub reports that 000d8f22 does not exist in drevops/vortex, and PR #2895 currently points to 09e4c8e178db105909d7a0fb70f8620ac76307ee. The available PR snapshot still ignores the file_put_contents() result.

Please retry after the commit is available on the PR branch. I will keep the finding unresolved until then.

🐇🔍


✏️ Learnings added
Learnt from: AlexSkrypnyk
Repo: drevops/vortex PR: 2895
File: .vortex/installer/src/Utils/JsonManipulator.php:54-64
Timestamp: 2026-08-05T10:00:48.081Z
Learning: In the Vortex installer, `DrevOps\VortexInstaller\Utils\JsonManipulator::updateFile()` writes only installer-controlled files in a private temporary directory before files are copied to the destination. The installer process is single-threaded and has no concurrent reader for these files, so atomic temp-file replacement is not required for this write path. The method must still validate the `file_put_contents()` result and fail on a short or failed write.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

Comment thread .vortex/installer/tests/Functional/FunctionalTestCase.php Outdated

public function testWriteValueDotenv(): void {
$fixture_dir = __DIR__ . '/Fixtures/env';
$fixture_dir = dirname(__DIR__) . '/Fixtures/env';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use the correct fixture directory.

Line 127 resolves from .vortex/installer/tests/Unit/Utils to .vortex/installer/tests/Unit/Fixtures/env. The fixture root is .vortex/installer/tests/Fixtures, so copy() cannot load _baseline/.env and testWriteValueDotenv() fails.

Proposed fix
-    $fixture_dir = dirname(__DIR__) . '/Fixtures/env';
+    $fixture_dir = dirname(__DIR__, 2) . '/Fixtures/env';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$fixture_dir = dirname(__DIR__) . '/Fixtures/env';
$fixture_dir = dirname(__DIR__, 2) . '/Fixtures/env';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.vortex/installer/tests/Unit/Utils/EnvTest.php at line 127, Update the
$fixture_dir assignment in testWriteValueDotenv() to resolve from the Utils test
directory to the sibling tests/Fixtures/env directory, so copy() can locate the
_baseline/.env fixture.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not adopting - the current path is correct and the suggested change would break the test.

This suite's fixture root is tests/Unit/Fixtures, not tests/Fixtures. The file lives at tests/Unit/Utils/EnvTest.php, so dirname(__DIR__) resolves to tests/Unit, giving tests/Unit/Fixtures/env - which is exactly where _baseline/.env is:

.vortex/installer/tests/Unit/Fixtures/env/_baseline/.env

That is also why the line changed in the first place: the file moved from tests/Unit/ into tests/Unit/Utils/ in this PR, so the original __DIR__ . '/Fixtures/env' had to gain one dirname() to keep pointing at the same directory. dirname(__DIR__, 2) would resolve to tests/Fixtures/env, which does not exist - that path holds the installer's handler_process snapshot fixtures, a different system.

testWriteValueDotenv() passes on this branch, as does the full 1606-test suite.

Comment thread AGENTS.md
Comment thread web/themes/custom/your_site_theme/js/your_site_theme.js Outdated
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

📖 Documentation preview for this pull request has been deployed to Netlify:

https://6a730d4b1a7f4424c309363e--vortex-docs.netlify.app

This preview is rebuilt on every commit and is not the production documentation site.

@github-actions

This comment has been minimized.

@AlexSkrypnyk

This comment has been minimized.

2 similar comments
@AlexSkrypnyk

This comment has been minimized.

@AlexSkrypnyk

This comment has been minimized.

@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.89691% with 39 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.63%. Comparing base (22deef4) to head (fac6e02).

Files with missing lines Patch % Lines
.../installer/tests/Functional/FunctionalTestCase.php 0.00% 9 Missing ⚠️
.vortex/installer/src/Utils/JsonManipulator.php 0.00% 7 Missing ⚠️
...installer/src/Command/CheckRequirementsCommand.php 28.57% 5 Missing ⚠️
.../installer/src/Prompts/Handlers/MigrationImage.php 0.00% 5 Missing ⚠️
...vortex/installer/src/Prompts/Handlers/Internal.php 76.47% 4 Missing ⚠️
.vortex/installer/src/Prompts/PromptManager.php 85.71% 3 Missing ⚠️
.../installer/src/Downloader/RepositoryDownloader.php 92.30% 1 Missing ⚠️
...ller/src/Prompts/Handlers/CodeCoverageProvider.php 50.00% 1 Missing ⚠️
...x/installer/src/Prompts/Handlers/FrontendBuild.php 66.66% 1 Missing ⚠️
...vortex/installer/src/Prompts/Handlers/Gitleaks.php 75.00% 1 Missing ⚠️
... and 2 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2895      +/-   ##
==========================================
- Coverage   87.20%   86.63%   -0.58%     
==========================================
  Files         100       94       -6     
  Lines        4816     4661     -155     
  Branches       47        3      -44     
==========================================
- Hits         4200     4038     -162     
- Misses        616      623       +7     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…e badge mask, resolved the behavior context through its document.
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

Code coverage (threshold: 90%)

  Classes: 100.00% (1/1)
  Methods: 100.00% (2/2)
  Lines:   98.55% (204/207)
Per-class coverage
Drupal\ys_demo\Plugin\Block\CounterBlock
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% ( 10/ 10)

@AlexSkrypnyk

This comment has been minimized.

2 similar comments
@AlexSkrypnyk

This comment has been minimized.

@AlexSkrypnyk

Copy link
Copy Markdown
Member Author

Code coverage (threshold: 90%)

  Classes: 100.00% (1/1)
  Methods: 100.00% (2/2)
  Lines:   98.55% (204/207)
Per-class coverage
Drupal\ys_demo\Plugin\Block\CounterBlock
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% ( 10/ 10)

@AlexSkrypnyk AlexSkrypnyk added the Needs review Pull request needs a review from assigned developers label Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs review Pull request needs a review from assigned developers

Projects

Status: BACKLOG

Development

Successfully merging this pull request may close these issues.

1 participant