Skip to content

Issue/575 console boot that cannot lock out core:env - #577

Merged
armanist merged 11 commits into
quantum-php:masterfrom
armanist:issue/575-console-env-preflight
Oct 14, 2026
Merged

armanist merged 11 commits into
quantum-php:masterfrom
armanist:issue/575-console-env-preflight

Conversation

@armanist

@armanist armanist commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #575

What changed

  • ConsoleAppAdapter no longer compares the first argument with core:env. It runs one BootPipeline inside a try; when the boot fails it keeps the failure and continues. The Symfony Application is created once as UNKNOWN and named from the app config after a successful boot, and the environment becomes mutable only after a successful boot.
  • RunCommandStage re-throws that original failure only for a CliCommand that requires the environment, so core:env, list and help still run in a failed boot. start() throws the recorded failure when anything else fails first, for example a command constructor.
  • CliCommand::$requiresEnvironment (default true) with requiresEnvironment(); EnvCommand sets it to false. ConsoleContext takes an optional boot failure and exposes it with getBootFailure().
  • EnvCommand looks for the existing .env in the application base directory instead of the current working directory, so the overwrite confirmation is no longer skipped when the command runs from another directory.

Why

The reduced boot was keyed to the string core:env, so the adapter had to know a command name. Deciding by whether the env file exists looked like the fix, but a broken .env (empty, or a Dotenv syntax error) then got the full boot, which failed, and core:env --yes could not repair it, where master could. The boot is now attempted, the failure is recorded and enforced per command, the same way Django does it. The change of approach is explained in the comment on #575.

Breaking changes

None for applications. CliCommand gains a property and a getter with a default that keeps today's behavior, and ConsoleContext gains an optional constructor argument. The reduced boot for core:env no longer exists: core:env goes through the same boot as every command, and a failed boot no longer stops it.

How behavior was proven

  • A matrix in throwaway copies of quantum/project, master against this branch: 7 project states (ok, .env missing, empty, malformed, broken app config, broken logging config, fs.php calling env() with no .env) by 5 commands (core:env --yes, list, core:version, unknown command, no command). The branch is never worse than master. core:env --yes repairs wherever master did, list works in the failure states where master crashed, and commands that require the environment show the same original error as master.
  • A syntax error in a framework helper file gives the identical parse error on master and on this branch.
  • Failure-state tests were written first and shown red against the previous adapter: malformed, empty and missing .env, a requiring and an opt-out command, list, an unknown command, and a command constructor that throws.
  • Mutation checks on the adapter and the run stage in a throwaway copy: 10 of 11 caught. The uncaught one lists the run stage twice, which no test counts.

Validation

  • Full vendor/bin/phpunit: 1680 tests, 4022 assertions, 28 errors (Redis/Memcached connections, the same 28 as on master), 4 skipped (optional Twig not installed)
  • composer cs:check and composer phpstan passed

Notes for reviewers

  • core:env runs in whatever state a failed boot leaves, so it must not rely on config.
  • An fs.php that calls env() with no .env still cannot be repaired. The same happens on master.
  • An empty .env no longer crashes: the app runs with the name UNKNOWN.
  • After a failed boot no error handler is installed, so the failure shows as a plain PHP uncaught exception, exactly as on master. Connecting it to ErrorHandler is a separate change.
  • Docs pages to update in quantum-php-docs: cli/qt-command-line-tool.md, cli/console-commands.md, packages/console/*, packages/environment/*, packages/app/adapters.md, packages/app/architecture.md.

Summary by CodeRabbit

  • New Features
    • Console commands that do not require an environment can run even when application boot fails. Commands that do require one report the boot failure instead.
  • Bug Fixes
    • The environment setup command now checks for an existing .env file in the application directory, even when run from another working directory.
    • Running the environment setup command no longer prevents the remaining application boot stages from running.

armanist and others added 10 commits October 14, 2026 13:38
…v file exists

Add a non-throwing fileExists() that tells whether the environment file the
app is configured for exists. It follows the same resolution as load(): the
app_env of shared/config/env.php picks .env or .env.<app_env>. The resolution
is shared with resolveEnvironmentFile() through two small private helpers, and
it uses App::getBaseDir() instead of the base_dir() helper, so the method can
be asked before the helpers are loaded. load() behaves as before.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ironment

Add a requiresEnvironment property to CliCommand, true by default, with a
public requiresEnvironment() getter, and set it to false in EnvCommand, which
creates the environment file and must run when there is none. Nothing uses
the getter yet; the run stage will in a following commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…v file is missing

After the unknown command guard, RunCommandStage now asks the resolved
command. A CliCommand that requires the environment is stopped with the same
EnvException the boot raises when the environment file does not exist, before
anything runs. Commands that declare they do not need it, such as core:env,
and plain Symfony commands, such as list and help, still run.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t is decided

Add characterization tests for the boot done by the console adapter: an
unknown command gets the full boot, core:env with an option still gets the
reduced boot, the environment is mutable after a full boot, and the Symfony
application is named from the app config after a full boot and UNKNOWN after
the reduced boot. They pass on the current code and guard the following change
that decides the boot by the environment file instead of the command name.

The boot for a missing command name is deliberately not pinned, because that
behavior is meant to change later.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t by the command name

The console adapter no longer compares the first argument with core:env. It
asks Environment::fileExists() once and uses the answer for both the boot
stages and the mutable environment step: with the environment file the app
boots fully, whatever the command; without it only the helpers are loaded, so
core:env can create the file. Commands that need the environment are stopped by
RunCommandStage with the usual EnvException, and list and help now work
without the file.

Behavior change: core:env now gets the full boot when the environment file
exists. Three tests used core:env with the file present as the trigger for the
reduced boot (BootsReducedStagesForCoreEnvCommand, ...WithOptions and
NamesTheApplicationUnknownAfterTheReducedBoot); they keep their assertions and
now hide the environment file instead. New tests cover the full boot of core:env
with the file, the reduced boot of any command without it, and start() with and
without the file.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e dir

The overwrite check of core:env used the relative path .env, which PHP resolves
against the current working directory, while the copy always went to the base
dir. Running the command from another directory therefore skipped the
confirmation and silently overwrote the project's existing .env. The check now
uses the same base dir path as the copy.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The console run will continue after a failed boot and decide per command
whether the failure matters, so the context needs to hold it. The new
constructor argument is optional, so existing callers are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…failure per command

Deciding the boot by the env file could not repair a broken .env: an empty
or malformed file got the full boot, which failed, so `core:env --yes` was
locked out where the old name check could still repair it.

The adapter now runs the whole boot in a try, keeps the failure and
continues. RunCommandStage re-throws that failure only for a command that
requires the environment, so `core:env`, `list` and `help` run in a failed
boot and every other command reports the original error. start() throws
the recorded failure when anything else fails first (for example a command
constructor). The application starts as UNKNOWN and is named from the app
config after a successful boot, and the environment becomes mutable only
then.

Replaced on purpose: the three tests that pinned the reduced boot for a
missing env file, and the RunCommandStage tests that pinned the
file-missing guard; both now describe the recorded boot failure.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…y it any more

The console no longer asks whether the env file exists: it boots, keeps the
failure and decides per command. fileExists(), its two private helpers, the
getBaseDir() switch in resolveEnvironmentFile() and the tests that came with
them are removed, so the Environment package is back to what master has.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-14T14:57:54.543601Z 58840f4 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 34 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c2a132e2-e858-4bb8-b3bb-8b24e99a1e8e
📥 Commits

Reviewing files that changed from the base of the PR and between 58840f4 and dc8a0db.

📒 Files selected for processing: 2
  • src/App/Adapters/Console/ConsoleAppAdapter.php
  • tests/Unit/App/Adapters/Console/ConsoleAppAdapterTest.php

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 799fae57-1411-4161-87ce-f3831b3a7b4d
📥 Commits

Reviewing files that changed from the base of the PR and between 75127e9 and 58840f4.

📒 Files selected for processing: 12
  • CHANGELOG.md
  • src/App/Adapters/Console/ConsoleAppAdapter.php
  • src/App/Adapters/Console/ConsoleContext.php
  • src/App/Stages/Console/RunCommandStage.php
  • src/Console/CliCommand.php
  • src/Console/Commands/EnvCommand.php
  • tests/Unit/App/Adapters/Console/ConsoleAppAdapterTest.php
  • tests/Unit/App/Adapters/Console/ConsoleContextTest.php
  • tests/Unit/App/Stages/Console/ConsoleStageTestCase.php
  • tests/Unit/App/Stages/Console/RunCommandStageTest.php
  • tests/Unit/Console/CliCommandTest.php
  • tests/Unit/Console/Commands/EnvCommandTest.php

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Walkthrough

The console adapter now runs all boot stages and stores boot failures for later command dispatch. Commands can declare whether they require the environment. core:env does not require an existing environment file and checks for .env in the application base directory.

Changes: Console environment behavior

Layer / File(s) Summary
Boot stages and retained failures
src/App/Adapters/Console/ConsoleAppAdapter.php, src/App/Adapters/Console/ConsoleContext.php, tests/Unit/App/Adapters/Console/ConsoleAppAdapterTest.php, tests/Unit/App/Adapters/Console/ConsoleContextTest.php, tests/Unit/App/Stages/Console/ConsoleStageTestCase.php
The adapter runs the helpers, environment, app-config, and error-handler stages. It stores boot failures, passes them to ConsoleContext, and uses configured application name and version values with UNKNOWN fallbacks. Tests cover boot outcomes and context access.
Environment requirements in command dispatch
src/Console/CliCommand.php, src/App/Stages/Console/RunCommandStage.php, tests/Unit/Console/CliCommandTest.php, tests/Unit/App/Stages/Console/RunCommandStageTest.php, tests/Unit/App/Adapters/Console/ConsoleAppAdapterTest.php, CHANGELOG.md
CliCommand defaults to requiring the environment. When boot failed, RunCommandStage rethrows that failure for commands that require it; other commands can run. Tests cover these dispatch paths.
Environment command file path
src/Console/Commands/EnvCommand.php, tests/Unit/Console/Commands/EnvCommandTest.php, CHANGELOG.md
EnvCommand does not require an existing environment file. Its overwrite check uses the application base directory. Tests cover prompting, replacement, and file creation.

Priority: ⬇️ Low

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

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant ConsoleAppAdapter
  participant ConsoleContext
  participant RunCommandStage
  participant Application
  participant CliCommand
  ConsoleAppAdapter->>ConsoleContext: Pass stored boot failure
  RunCommandStage->>ConsoleContext: Get boot failure
  RunCommandStage->>Application: Resolve requested command
  RunCommandStage->>CliCommand: Check requiresEnvironment
  RunCommandStage->>Application: Run command when dispatch continues
Loading
Merge Risk: ⚪ Minimal · up to 58840

The reported error-masking issue does not affect the production console configuration. No actionable merge-blocking issue remains after normal checks.

Pre-merge checks: Passed 3 · Failed 2

Failed Failed checks: 2 warnings

Check name Status Explanation Resolution
Linked Issues check Warning Issue #575 requires boot selection from the environment-file state, a non-throwing Environment::fileExists() that shares resolution with loading, and reduced boot when the file is absent. The review… Implement the #575 file-based boot decision and Environment::fileExists() behavior. Add or retain tests for shared file resolution, full boot when the file exists, and reduced boot with working list and help when it does not.
Docstring Coverage Warning Docstring coverage is 23.44% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 64 functions across 11 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 3 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title identifies the main change: console boot failures must not prevent core:env from running. It is specific and related to the pull request, although the wording is slightly awkward.
Out of Scope Changes check Passed The reviewed changes remain connected to #575. Boot-failure retention, command environment requirements, ConsoleContext access, failure-state tests, the base-directory overwrite check, and the chang…
Full details: Linked Issues check

Explanation

Issue #575 requires boot selection from the environment-file state, a non-throwing Environment::fileExists() that shares resolution with loading, and reduced boot when the file is absent. The reviewed code in ConsoleAppAdapter always runs the full boot and records a failure. It does not add Environment::fileExists(). The CliCommand opt-out, EnvCommand base-directory check, tests, and changelog cover other #575 requirements, but they do not implement the required file-based boot decision.

Full details: Docstring Coverage

Explanation

Docstring coverage is 23.44% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 64 functions across 11 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 58840f4357

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/App/Adapters/Console/ConsoleAppAdapter.php
…cing them

The application is named from the app config after a successful boot, and
`?:` also replaced an empty string or '0' with UNKNOWN. `??` still covers
the null name that used to crash the console and keeps every other value.
The two tests that set the app config before building the adapter now load
the helpers themselves, so they also pass when run alone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@armanist
armanist merged commit 9789438 into quantum-php:master Oct 14, 2026
6 checks passed
@armanist
armanist deleted the issue/575-console-env-preflight branch October 14, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decide the console reduced boot by the environment file, not by the core:env command name

3 participants