Skip to content

feat(build): hard-fail the build when laravel/octane is not installed#94

Merged
stevethomas merged 1 commit into
mainfrom
feat/check-octane-installed
Jun 5, 2026
Merged

feat(build): hard-fail the build when laravel/octane is not installed#94
stevethomas merged 1 commit into
mainfrom
feat/check-octane-installed

Conversation

@stevethomas
Copy link
Copy Markdown
Member

@stevethomas stevethomas commented Jun 5, 2026

Hey, I made a thing! 🥳

What problems are you solving?

  • The web role runs php artisan octane:start (see ProcessCommands::octane()), which only exists when laravel/octane is installed. There was no check — an app reaching deploy without octane crash-loops the web container on boot, and the ECS deployment circuit breaker only rolls it back ~20 minutes later. Expensive, late, easy to miss.
  • This adds a CheckOctaneInstalledStep build preflight, slotted first in BuildCommand::$fargateSteps (ahead of CheckSsrRuntimeStep, ECR login, and the image build/push), so a missing octane fails in milliseconds instead of after a full push + rollout. It gates on tasks.web (a worker-only app never runs octane → SKIPPED).

Is there anything the reviewer needs to know to deploy this?

  • It won't block existing apps' next deploy. Every CL app on Fargate today is already running octane:start, so octane is in their composer.lock — the gate passes silently. It only fires on a genuinely-missing-octane misconfig (a new app, or octane demoted to require-dev).
  • Build-time only, no infra/AWS surface. It reads the committed composer.lock from the repo root — no API calls, nothing provisioned. Rollback is a plain revert.
  • On failure it aborts with a message naming the fix: composer require laravel/octane.

The one decision worth flagging: check composer.lock, not composer.json

The production image installs with --no-dev, so composer.lock's packages array (the prod set) is exactly what ships. Checking it instead of composer.json's require:

Source checked octane in require octane in require-dev
composer.json require ✅ pass ✅ pass — but crash-loops in prod
composer.lock packages[] ✅ pass caught

Reading the lock also makes no assumption about where composer install runs — the committed lock is in the repo root whether the manifest builds host-side or an app-owned Dockerfile installs vendor/ itself. (That's why I didn't inspect the build dir's vendor/laravel/octane: ground truth for a host-side build, but it would false-fail a legitimate Docker-side install.)

Hard-fail, not warn-and-confirm

Unlike the sibling CheckSsrRuntimeStep (a Dockerfile-regex heuristic that can false-negative, so it warns + confirms), the lock is authoritative — a missing octane is a certainty. And a missing web server is fatal, where missing SSR merely degrades to client-side rendering.

Tests & checks

5 new (CheckOctaneInstalledStepTest): skip for worker-only, pass when present, hard-fail when absent, hard-fail when only a dev dependency (the footgun), and hard-fail when composer.lock is missing.

  • ./vendor/bin/pest625 passed (+5)
  • ./vendor/bin/phpstan analyse — no errors
  • ./vendor/bin/pint — pass
  • Docs: build pipeline step 4 in docs/guide/building-and-deploying.md updated

🤖 Generated with Claude Code

The web role runs `octane:start`, which only exists when laravel/octane is
installed. Without it the container crash-loops on boot and the deploy circuit
breaker rolls back ~20min later. Add a CheckOctaneInstalledStep build preflight
(first in BuildCommand's Fargate steps, ahead of ECR login and the image build)
that hard-fails when laravel/octane is absent from composer.lock's production
`packages` set.

Reading the lock's production set — not a composer.json `require` scan — catches
octane sitting in require-dev, which a naive scan passes but the `--no-dev` image
install strips. It also makes no assumption about where composer install runs, so
an app-owned Dockerfile that installs vendor itself isn't false-failed.

Unlike the sibling SSR runtime check this is a clean hard-fail, not
warn-and-confirm: the lock is authoritative, so a missing octane is a certainty,
and a missing web server is fatal where missing SSR merely degrades to CSR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@stevethomas stevethomas merged commit ed5bcca into main Jun 5, 2026
5 checks passed
@stevethomas stevethomas deleted the feat/check-octane-installed branch June 5, 2026 07:13
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.

1 participant