Composer Scripts — One-Command Developer Onboarding

Composer Scripts — One-Command Developer Onboarding

Reading Time: 3 minutes

Series: Every Laravel Project Should Have These Building Blocks 
Part: 30 of 35 Level: Beginner–Intermediate Prerequisites: None


What You’ll Learn

  • Why composer.json scripts are better than a README with steps
  • The composer setup onboarding script
  • composer lint:fixcomposer lintcomposer test:types
  • composer dev — running everything in parallel
  • Auto-installing git hooks via post-autoload-dump

The Problem with README Instructions

Every project has a README that says something like:

Getting started:
1. cp .env.example .env
2. composer install
3. php artisan key:generate
4. php artisan migrate
5. php artisan db:seed
6. npm install && npm run build

Six steps, and that’s for a simple project. When a new developer joins, they inevitably miss a step, get an error, and spend 30 minutes debugging an environment that should have taken 5 minutes. When you change the setup process, the README gets outdated.

composer scripts make setup a single command.


The composer.json Scripts Block

{
    "scripts": {
        "setup": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
            "@php artisan key:generate --ansi",
            "@php artisan migrate --ansi",
            "@php artisan db:seed --ansi",
            "npm install",
            "npm run build"
        ],

        "dev": [
            "Composer\\Config::disableProcessTimeout",
            "@putenv SHELL_VERBOSITY=1",
            "@php artisan serve &",
            "@php artisan queue:listen --tries=1 &",
            "@php artisan pail --timeout=0 &",
            "npm run dev"
        ],

        "lint": [
            "vendor/bin/rector --dry-run",
            "vendor/bin/pint --test"
        ],

        "lint:fix": [
            "vendor/bin/rector",
            "vendor/bin/pint"
        ],

        "test": "php artisan test --compact",

        "test:types": "vendor/bin/phpstan analyse --memory-limit=512M",

        "quality": [
            "@lint:fix",
            "@test:types",
            "@test"
        ],

        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover --ansi",
            "@php -r \"file_exists('.hooks/pre-commit') && shell_exec('git config core.hooksPath .hooks');\"",
            "@php -r \"file_exists('.hooks/pre-commit') && shell_exec('chmod +x .hooks/pre-commit');\""
        ]
    }
}

What Each Script Does

composer setup

First-time project setup, idempotent:

"setup": [
    "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
    "@php artisan key:generate --ansi",
    "@php artisan migrate --ansi",
    "@php artisan db:seed --ansi",
    "npm install",
    "npm run build"
]

The file_exists('.env') || copy(...) line only copies .env.example if .env doesn’t already exist — so running composer setup again after setup won’t overwrite customized env values.

A new developer runs composer install && composer setup and they’re done.

composer dev

Start everything needed for local development simultaneously:

"dev": [
    "Composer\\Config::disableProcessTimeout",
    "@php artisan serve &",
    "@php artisan queue:listen --tries=1 &",
    "@php artisan pail --timeout=0 &",
    "npm run dev"
]

disableProcessTimeout prevents Composer from killing long-running commands. The & runs each command in the background. npm run dev runs in the foreground, so the whole thing stays alive until you Ctrl+C.

One command replaces:

  • Terminal 1: php artisan serve
  • Terminal 2: php artisan queue:listen
  • Terminal 3: php artisan pail
  • Terminal 4: npm run dev

composer lint / composer lint:fix

Check vs. fix:

"lint":     ["vendor/bin/rector --dry-run", "vendor/bin/pint --test"],
"lint:fix": ["vendor/bin/rector",           "vendor/bin/pint"]

lint is for CI — it fails if anything needs changing, without making changes. lint:fix is for local development — it makes the changes.

composer test:types

Static analysis only:

"test:types": "vendor/bin/phpstan analyse --memory-limit=512M"

composer quality

Before pushing a PR:

"quality": [
    "@lint:fix",
    "@test:types",
    "@test"
]

Runs: Rector → Pint → Larastan → Tests. If all pass, you’re ready to push.


post-autoload-dump — Auto-Installing Git Hooks

Every composer install or composer update triggers post-autoload-dump. Use it to auto-install the git pre-commit hook:

"post-autoload-dump": [
    "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
    "@php artisan package:discover --ansi",
    "@php -r \"file_exists('.hooks/pre-commit') && shell_exec('git config core.hooksPath .hooks');\"",
    "@php -r \"file_exists('.hooks/pre-commit') && shell_exec('chmod +x .hooks/pre-commit');\""
]

This means:

  1. Developer runs composer install
  2. Git hooks path is automatically configured to .hooks/
  3. Pre-commit hook is made executable
  4. Every subsequent git commit automatically runs the quality checks

Developers never manually install the hooks. They can’t forget.


The README That Ships With This Setup

With these scripts in place, your README’s “Getting Started” section becomes:

## Getting Started

```bash
composer install
composer setup

That’s it. For local development:

composer dev

Before pushing:

composer quality

Four lines of README instead of six manual steps.


Key Takeaways

  • composer setup wraps all first-time setup into one command. Use file_exists() checks to make it idempotent.
  • composer dev starts the HTTP server, queue worker, log viewer, and Vite together. One command replaces four terminals.
  • composer lint checks only; composer lint:fix fixes. Match CI vs. local workflows.
  • composer quality = lint:fix + static analysis + tests. The pre-push command.
  • post-autoload-dump auto-installs git hooks on every composer install — developers can’t forget to set them up.

Tips and Gotchas

⚠️ Warning: The composer dev script uses & to background processes. On Windows, & doesn’t work in bash — this script requires WSL or a POSIX-compatible shell. Windows developers should use composer run dev inside WSL or add a PowerShell alternative.

💡 Tip: Add composer setup to your onboarding documentation as the only step after composer install. If composer setup fails, that’s a bug in your setup script — not a developer problem. Fix the script.

🔥 Expert Note: The post-autoload-dump hook runs on every composer installcomposer update, and composer require. This means your git hooks are reinstalled every time someone adds a package. New team members who run composer install after cloning the repo automatically get the hooks — no manual step, no opportunity to skip it.

Further Reading


← Slow Query | Next: Git Hooks →

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.