Installer Overview
The Installer package is the temporary browser setup layer for a new Capell application. It collects install input, runs preflight checks, delegates install work to Core Actions, gives the user a progress/report surface, and can remove itself once the site is ready.
Core owns the install primitives. Installer owns the web routes, views, Filament setup pages, dashboard warning, install guide patches, installer session state, and post-install cleanup.
Request Flow
Section titled “Request Flow”| Step | Owner | What happens |
|---|---|---|
Show /install | InstallController::show() | Regenerates the CSRF token, builds page data with BuildInstallerPageDataAction, and shows active install status if the current session may see it. |
| Build form state | BuildInstallerPageDataAction | Reads package metadata from CapellCore::getPackages(), fetches downloadable package metadata when available, builds theme options, language options, existing user options, default package selections, and a preflight report. |
| Submit install | InstallController::store() | Validates the form and converts it to InstallInputData through InstallInputFactory. The browser flow usually uses step-based JSON requests; non-AJAX and queued installs are still supported. |
| Run preflight | InstallerPreflight | Checks PHP extensions, process support, PHP/Composer/Git binaries, writable paths, database readiness, cache readiness, queue readiness, and Composer dry-runs when optional packages are selected. |
| Run install steps | RunInstallStepAction in Core | Installer stores the input and plan in cache, then calls one step at a time so the browser can keep showing progress and retry around CSRF refreshes. |
| Show progress/report | InstallerSessionRepository, FileLogProgressReporter, CacheProgressReporter | Progress lines and status are kept under capell.install.{installId}.* cache keys for two hours. Reports can be downloaded from the progress route by the same install session. |
| Finish cleanup | RemoveSetupPackageAction | The success page enables installer removal for the current session. The delete route removes capell-app/installer from the host app. |
EnsureNotInstalled guards the main install routes. It treats the app as installed when the Admin package is installed, the sites table exists, and at least one site exists. Reinstall is allowed only when CAPELL_SETUP_ALLOW_REINSTALL is true, which defaults to APP_DEBUG.
Install Guide Patches
Section titled “Install Guide Patches”Install guide patches are the installer-owned extension point. A patch implements Capell\Installer\Support\InstallGuide\Patch:
| Method | Purpose |
|---|---|
id() | Stable machine key used by the install guide UI and tests. |
group() | Groups related checks in the guide. |
label() / description() | Human copy shown to the installer user. |
docUrl() | Optional link to the longer setup guide. |
defaultEnabled() | Whether the patch should be selected by default when it can run. |
probe() | Returns a PatchStatus describing whether the host already matches the expected state. |
reason() | Explains why a patch is needed or why it cannot run. |
apply() | Makes the smallest idempotent host-file change. |
The service provider registers first-party patches in registerPatches() through PatchRegistry. Add new first-party patches there, keep them idempotent, and cover each patch with a focused test under packages/installer/tests/Feature/InstallGuide/Patches.
Patch code should use the installer patching helpers (EnvFileEditor, PhpFileEditor, ConfigArrayEditor) instead of raw string edits when possible. A patch that only documents manual work should stay explicit, as the DocOnly*Patch classes do.
Theme And Package Selection
Section titled “Theme And Package Selection”The installer always has safe theme choices from ThemePackageCandidates, including none, foundation, corporate, and saas when available. Installed and downloadable package metadata can add more choices with a theme key, package name, description, requirements, and preview image.
Default selected packages come from two places:
- trusted core package defaults from Core;
CAPELL_SETUP_DEFAULT_PACKAGES, filtered to packages that are available to the installer.
Optional Composer packages are shown only when the Composer package can be resolved by the web PHP process. If a package is missing from the installer, check Composer repository access before changing UI code.
Fresh Database Safety
Section titled “Fresh Database Safety”Fresh apps may configure database-backed sessions or cache before the required tables exist. InstallerServiceProvider::fallbackDatabaseDriversIfNeeded() switches the current web request to file-backed session/cache when those database tables are missing. This is request-scoped and only runs for web requests.
The installer still refuses to start when the active cache store cannot store progress. If the progress request says the cache store is unavailable, either create the cache table, temporarily use CACHE_STORE=file, or run the CLI installer.
Debugging
Section titled “Debugging”| Symptom | Likely cause | Check | Fix |
|---|---|---|---|
/install shows an already-installed notice on a fresh app | The Admin package, sites table, and a site row exist | php artisan tinker then Capell\Installer\Support\InstallerInstallationState::capellIsInstalled() | Use the installed admin, or set CAPELL_SETUP_ALLOW_REINSTALL=true only for a controlled local reinstall. |
/install 404s | Package provider or route file is not loaded | php artisan route:list --name=capell-installer | Confirm capell-app/installer is installed/discovered and run php artisan optimize:clear. |
Preflight blocks on proc_open | Web PHP cannot run subprocesses | Check PHP disabled functions for the web SAPI | Enable proc_open or run the CLI installer from an environment that can run Composer and Artisan. |
| Preflight warns about PHP binary | CAPELL_SETUP_PHP_BINARY points at FPM or a missing binary | php artisan config:show capell-installer.php_binary | Set CAPELL_SETUP_PHP_BINARY to the CLI PHP executable and clear config cache. |
| Composer dry-run fails | Web PHP cannot see Composer, Git, credentials, or path repositories | composer config repositories and the preflight report | Set CAPELL_SETUP_COMPOSER_BINARY, fix Composer auth/repositories, then restart the installer. |
| Progress page says the install session expired | capell.install.{installId}.* cache keys expired or were cleared | Check cache driver and capell.install.lock | Restart the installer. For long installs, use a persistent cache store and keep queue workers alive. |
| Another install appears locked | capell.install.lock still points at a queued/running install | Inspect the cache key or revisit /install from the same browser | Cancel from the UI, wait for the lock TTL, or clear only the installer lock in a local/dev environment. |
| Guide patch is unavailable | probe() found an unsafe or already-satisfied host state | Read the patch reason() in the UI/report | Fix the host file manually or update the patch probe/apply pair with a regression test. |
| Success page still exposes installer removal/report URLs | Installer package remains installed after setup | composer show capell-app/installer | Remove the package or restrict access. Treat progress/report URLs as bootstrap-only secrets. |
Verification
Section titled “Verification”Run the package test suite after changing installer routes, validation, preflight checks, patching, session state, or cleanup:
vendor/bin/pest packages/installer/tests --configuration=phpunit.xmlRun the browser installer spec after changing the web flow or CSRF retry behavior:
npm run test:installer-browserRun screenshot checks when installer pages or visual states change:
npm run screenshotsnpm run screenshots:check