Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Duster Laravel Package

tightenco/duster

Duster automatically applies Tighten's default Laravel code style by combining TLint, PHP_CodeSniffer, PHP CS Fixer, and Pint. Lint or fix everything at once, target only uncommitted files with --dirty, and customize includes/excludes via duster.json.

View on GitHub
Deep Wiki
Context7

Getting Started

Install as a dev dependency — Duster is not a runtime package and never ships to production:

composer require tightenco/duster --dev

Run lint first, never fix first, on an existing project:

./vendor/bin/duster lint

lint is read-only and tells you exactly which tool is unhappy with which file. Once you've seen the damage, run ./vendor/bin/duster fix. Do this on a clean git working tree so you can review the diff — the first run on a mature codebase can touch hundreds of files, and you want that as one isolated "chore: apply Duster" commit.

First meaningful use case is incremental adoption. On a legacy app, don't fix everything at once:

./vendor/bin/duster fix --dirty

--dirty restricts the run to files with uncommitted changes according to Git, so you fix style only in code you're already touching. That's the workflow most teams actually stay on.

From there, wire it into automation:

./vendor/bin/duster github-actions   # publishes a Duster workflow
./vendor/bin/duster husky-hooks      # publishes pre-commit hooks

./vendor/bin/duster commands lists everything available. If you want to know what a rule does before arguing with it, read style-guide.md — it has compiled rules and before/after examples for all four underlying tools.

Implementation Patterns

The four-tool pipeline

Duster is a thin orchestrator over TLint (Tighten preset), PHP_CodeSniffer (Tighten ruleset, roughly PSR-1), PHP CS Fixer (custom fixers like CustomOrderedClassElementsFixer), and Pint (Laravel preset + Tighten tweaks). You get all four from one binary; you don't install or configure them individually unless you want to.

CI: use the action, not the binary

Prefer the dedicated duster-action over shelling out to ./vendor/bin/duster lint in a hand-rolled workflow. The critical detail is that workflows which commit to the repo stop any currently running workflows and do not trigger a new run. If you run Duster Fix in CI, every other workflow needs to be re-triggered:

on:
  # Commits made in Duster Fix will not trigger any workflows
  # This workflow is configured to run after Duster finishes
  workflow_run:
    workflows: ["Duster Fix"]
    types:
      - completed

Same applies on a smaller scale with the Husky pre-commit hook: the hook modifies files after you've staged them, so always re-stage or use git add -u in the hook.

Adding tools with --using and scripts

Order and membership are both controllable:

./vendor/bin/duster lint --using="phpstan,tlint,pint"

--using replaces the default tool list. Omitting phpcs means PHPCS doesn't run at all — useful when you know it's the noisy one, dangerous if you intended it as a superset.

To add a non-Duster tool permanently, register it in duster.json:

{
    "scripts": {
        "lint": {
            "phpstan": ["./vendor/bin/phpstan", "analyse"]
        }
    },
    "processTimeout": 120
}

The key (phpstan) is the name you pass to --using; the value is the argv array handed to Symfony\Component\Process\Process. Scripts picked up here run automatically on lint/fix. Note lint and fix are separate keys — defining a script only under lint means duster fix will not run it. If you want it in both, declare it twice.

Scoping files with duster.json

{
    "include": ["bin", "scripts", "src", "tests"],
    "exclude": ["tests/fixtures", "**/folderToExclude/**"]
}

Globs are supported (powered by Symfony Finder since v2.7.0). This is the cleanest lever for monorepos and packages with fixture directories full of deliberately malformed PHP.

Per-tool config overrides

When you need to deviate from Tighten's standard on one axis, drop the corresponding config file in the project root and Duster will defer to it:

  • tlint.json — TLint
  • .phpcs.xml.dist — PHP_CodeSniffer
  • .php-cs-fixer.dist.php — PHP CS Fixer
  • pint.json — Pint

Start from Duster's own files in standards/ rather than writing from scratch — .phpcs.xml.dist in particular should carry the <rule ref="Tighten"/> line with your customizations below it, so you keep the base ruleset instead of accidentally forking it.

Containerized environments

No local PHP? Pass --env:

./vendor/bin/duster husky-hooks --env=ddev

Supported: ddev, warden, lando, sail. With Sail you can also skip the flag entirely:

./vendor/bin/sail bin duster lint

Sail ships a bin passthrough script for package binaries, which is more reliable than ./vendor/bin/sail php ./vendor/bin/duster because it doesn't depend on how your container maps the vendor directory.

Gotchas and Tips

PHPCS is the one tool that cannot fully self-heal. When it can't autofix, Duster (since v3.4.5) tells you to run duster lint for more information — that's not a bug, it's PHPCS reporting issues that require a human decision. Wire lint into CI as the authoritative gate and treat fix as a developer convenience, not a CI step.

--dirty exiting non-zero on no changes was fixed in v1.1.1. If you're on anything from that era or are copying workflow YAML from an old blog post, verify your pinned version — a pre-commit hook that fails with no dirty files is a miserable thing to debug.

Trailing slashes matter in exclude. v3.4.3 added trailing slashes to excluded directories specifically to fix matching. Write "tests/fixtures/", not "tests/fixtures", and prefer **/dir/** for anything nested.

processTimeout defaults to 60 seconds. A PHPStan analyse over a large app will blow through that and surface as a generic process failure rather than a PHPStan timeout. Bump it as soon as you add a static analysis script.

Paths with spaces broke TLint historically (fixed in v1.0.3) and PHPCS path handling has been patched repeatedly — v3.4.4 made Duster pass explicit paths even when a project has a custom PHPCS config, and v3.4.7 added cwd to codesniffer paths. If you hit "file not found" style errors with a custom .phpcs.xml.dist, upgrade before you start rewriting configs.

Some files are excluded by default and you can't easily tell. Pest.php is excluded from the default file set (v2.7.2), and the tests directory is excluded from the env() ban rule (v3.4.6). If a rule seems to "not fire" in a test file, check whether it's a deliberate exclusion rather than a broken config.

Pin the version in CI. Duster bundles Pint, PHP_CodeSniffer, and PHP CS Fixer. A minor bump (see the 3.4.x line, where several consecutive releases only touched dependencies and path handling) can change formatting output across the whole codebase. composer require tightenco/duster:3.4.* --dev and let Renovate/Dependabot PR the bump so you see the resulting diff in a reviewable branch.

Extension point is duster.json, not PHP classes. There's no plugin API — additional tooling goes through scripts, file scoping through include/exclude, and per-tool behavior through the four native config files. That's the whole surface area; if you find yourself wanting more, you probably want a separate Makefile/Composer script that calls duster as one step.

Debugging order: run duster lint --using="tlint" alone, then phpcs, then the rest. Because Duster swallows tool output into a unified report, isolating one tool is the fastest way to tell whether a failure is a real code issue or a config/path problem.

Keep the "chore: apply Duster" commit isolated in git history. Adding a .git-blame-ignore-revs entry for it (git config blame.ignoreRevsFile .git-blame-ignore-revs) preserves git blame sanity for the rest of the team after you run fix across a legacy codebase for the first time.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log