Skip to content

fix: register the register:app artisan command reliably - #1607

Merged
KodeStar merged 2 commits into
2.xfrom
fix/register-app-command
Oct 3, 2026
Merged

KodeStar merged 2 commits into
2.xfrom
fix/register-app-command

Conversation

@KodeStar

@KodeStar KodeStar commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Fixes #1606.

What

php artisan register:app fails in the linuxserver image with There are no commands defined in the "register" namespace.

Why

The cause is not the withRouting(commands: ...) call suggested in the issue: Application::configure() already calls withCommands() with no arguments, which adds app/Console/Commands. The real problem is ordering:

  • That path is only added to the console kernel in an app->booted() callback.
  • AppServiceProvider::boot() can call Artisan::call() (storage:link, migrate, key:generate). That nested call bootstraps the kernel and marks its commands as loaded while providers are still booting, before the path exists. Discovery never runs again, so register:app is missing for the rest of that process.
  • In the Docker image this happens on every run: the storage:link guard checks for public/storage/.gitignore, and the image does not ship storage/app/public/.gitignore. So storage:link runs on every boot, including every web request.

From source checkouts the .gitignore is present, which is why it does not reproduce outside the container (except on the first run after a version bump, when migrate runs at boot).

Changes

  • In console runs, AppServiceProvider::register() adds app/Console/Commands and routes/console.php to the console kernel directly. This runs before any boot-time Artisan call, so discovery works for every command (including future closure commands in routes/console.php), not just register:app.
  • Guard storage:link on public/storage itself rather than a dotfile inside it. This also stops a pointless storage:link on every request in the container.
  • tests/Feature/ConsoleCommandsTest.php: asserts every command class in app/Console/Commands is registered. The test DB is always fresh, so every test boot goes through the boot-time migrate call; the test fails before this change and passes after.

Verified

  • Full suite: 165 tests green.
  • In lscr.io/linuxserver/heimdall:latest (2.8.3) with the patched provider: register:app and a test closure command in routes/console.php both run, with an existing DB and with a fresh DB (migrate at boot).

AppServiceProvider::boot() calls Artisan (storage:link, migrate,
key:generate). An Artisan call made while providers are still booting
loads the console commands before the framework's booted callback adds
app/Console/Commands to the discovery paths, so register:app was never
registered for that process.

In the Docker image this happened on every run: the storage:link guard
looked for public/storage/.gitignore, which the image does not ship, so
storage:link ran on every boot (and every web request).

- Register RegisterApp explicitly in AppServiceProvider::register(),
  which runs before any boot-time Artisan call.
- Guard storage:link on the public/storage link itself.

Fixes #1606
…sses

Adding app/Console/Commands and routes/console.php to the console kernel
in register() fixes discovery for every command, including closure
commands in routes/console.php, rather than only for RegisterApp. The
test now checks every command in app/Console/Commands is registered.
@KodeStar
KodeStar merged commit 7346dce into 2.x Oct 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

php artisan register:app fails: "There are no commands defined in the "register" namespace"

2 participants