skip to content

How do Laravel Pint's --test, --bail and --repair options differ in what they change and in their exit codes?

level: middleimportance: should knowfreq 35%

answer

  1. write or not, and pass or fail
  2. --test: dry run, fails on drift
  3. --bail: stops at the first bad file
  4. --repair: writes fixes, still fails
  5. parse errors fail every mode

basics

~20 s

--test writes nothing and exits 1 if any file would change; --bail does the same but stops at the first offending file; --repair writes the fixes yet still exits 1 when it changed anything. Plain pint fixes and exits 0.

solid answer

~40 s

Plain `./vendor/bin/pint` rewrites files and exits 0 even after fixing dozens of them, so it cannot gate a build. `--test` is a dry run: nothing is written, the summary lists the offending files, and the exit code is 1 when any file would change. `--bail` is a dry run that stops at the first file with a violation, which gives faster feedback but an incomplete list. `--repair` writes the fixes like a normal run and then exits 1 if it changed anything, which suits a hook or bot that should correct the code yet still stop the commit so a person re-stages and reviews the result. In every mode, a file Pint cannot parse also makes it exit non-zero.

code

bash · 4 lines
bash
./vendor/bin/pint --test; echo "exit: $?"      # 1 if any file would change, nothing written
./vendor/bin/pint --bail; echo "exit: $?"      # stops at the first offending file
./vendor/bin/pint --repair; echo "exit: $?"    # files fixed, still 1 if anything changed
./vendor/bin/pint --test --output-to-file=pint.xml --output-format=junit

go deeper

for a junior

Recall that --test checks without writing and fails when something is wrong, while plain pint fixes files and succeeds.

for a middle

Explain each mode as two answers, write or not and pass or fail, including that --repair writes and still exits 1 and that parse errors fail every mode.

for a senior

Choose the mode per context: --test with a machine-readable report in CI, --repair in hooks, --parallel and a persisted cache for speed, and never plain pint as a gate.

for a principal

Decide whether style is enforced by failing builds or by automatic fixes, and make that choice consistent across hooks, CI and bots so developers get one clear signal.

## Two questions every Pint run answers Every run of **Laravel Pint** makes two separate decisions: 1. **Does it write the fixes to disk?** Fix mode rewrites files; a *dry run* only calculates what would change. 2. **Does it exit with success or failure?** Build tools, Git hooks and Composer scripts only read the **exit code**: 0 means success, anything else means failure. The options `--test`, `--bail` and `--repair` exist because the default answers to those two questions, *write* and *succeed*, are right for a developer at a keyboard and wrong for an automated gate. ## The four modes compared | Command | Writes files | Exits 1 when | Typical use | |---|---|---|---| | `pint` | yes | a file cannot be parsed or fixed | formatting locally | | `pint --test` | no | any file would change | CI gate, full report | | `pint --bail` | no | the first file that would change | fast fail on huge trees | | `pint --repair` | yes | any file was changed | hooks and bots that fix and stop | A few details behind the table: - **Plain `pint` succeeds after fixing.** If it rewrote fifty files, it did its job, so the exit code is 0. Running it in CI therefore proves nothing about the pushed commit: it formats the CI checkout and passes. - **`--test`** turns the run into a dry run. The summary names every file with a violation and the rules involved; adding `-v` prints the diff for each file as well. The exit code is 1 when anything would change. - **`--bail`** is a dry run that also stops at the **first** violation. It is quicker on a very large tree, but you learn about one file at a time. - **`--repair`** keeps fix mode, so files are rewritten, but flips the exit code to 1 when at least one file changed. A clean tree exits 0. - **Errors fail every mode.** A file with a syntax error that Pint cannot parse, or a fixer that throws, produces a non-zero exit even in plain fix mode. ## Choosing one for a pipeline For a CI job, `--test` is the usual choice: the job fails, the log lists every offending file, and nothing is written into a checkout that nobody will commit. `--bail` trades the full list for speed. `--repair` fits a different workflow: a pre-commit hook or a formatting bot that should fix the code *and* stop, so that a person looks at the result before it goes further. Plain `pint` belongs on a developer's machine. The Laravel starter kits wire this up in `composer.json`: ```json "lint": ["pint --parallel"], "lint:check": ["pint --parallel --test"] ``` Their `composer test` script calls `@lint:check` before running the test suite, so a style failure stops the suite from running. ## Reporting and speed in CI A CI log is easier to consume when it is machine-readable: - `--format` switches the console output to a reporter such as `json`, `junit`, `checkstyle`, `xml` or `txt`, which a CI system can turn into annotations or test results. - `--output-to-file` together with `--output-format` writes a report to a file while the console keeps the normal summary. - `--parallel` (short `-p`), marked experimental in Pint 1.32, spreads files across worker processes and uses every available core unless `--max-processes` caps it. Pint also uses a **cache file** that records what it has already checked. Pointing `--cache-file` (or a `cache-file` key in `pint.json`) at a path the CI job keeps between runs lets later runs skip files that have not changed. ## Common mistakes 1. Running plain `pint` in CI and treating green as proof of formatting. 2. Using `--repair` in CI without committing the result, so the job fails yet the fix is thrown away with the checkout. 3. Expecting `--bail` to list every problem. 4. Forgetting that the exit code also reflects parse errors, and blaming style when a file simply does not compile. 5. Reading a failed `--test` log and guessing at the fix. The summary names each file and the rules that fired; adding `-v` prints the exact diff, and running plain `pint` locally applies it. Each option answers the two questions differently, and naming both answers is the clearest way to explain them in an interview.

  • Why might a team choose --repair over --test in a pre-commit hook?
    `--test` only reports, so the developer must run Pint again by hand. `--repair` fixes the files and still exits 1, which aborts the commit; the developer inspects the changes, re-stages them and commits again. The fix is automatic, but nothing slips into a commit unreviewed.
  • How do you make Pint faster on a large codebase in CI?
    Pass `--parallel` (experimental in Pint 1.32), which spreads files across worker processes and uses every core unless `--max-processes` limits it, and point `--cache-file` (or `cache-file` in `pint.json`) at a path the job keeps between runs, so unchanged files are skipped.
  • What does --output-to-file add over --format?
    `--format=junit` replaces the console output with the report. `--output-to-file=pint.xml` plus `--output-format=junit` writes the report to a file and leaves the normal human-readable summary on the console, so the log stays readable and the CI system still gets a parseable report.

saying these in an interview costs you the question

  • --test fixes the files first and then reports what it changed.
  • Plain pint exits 1 whenever it had to fix a file.
  • --repair is a dry run that only suggests fixes.
  • --bail still reports every violation, just more tersely.
  • A green plain pint step in CI proves the pushed commit was formatted.