sweetchuck/lint-report
Laravel package to generate lint reports from your PHP and frontend tooling, suitable for CI pipelines. Aggregates linter outputs into a consistent report format to help track issues across runs and surface results in build artifacts.
Installation:
composer require sweetchuck/lint-report
Add the service provider to config/app.php:
'providers' => [
Sweetchuck\LintReport\LintReportServiceProvider::class,
],
Publish Config (optional):
php artisan vendor:publish --provider="Sweetchuck\LintReport\LintReportServiceProvider" --tag="config"
This generates config/lint-report.php with default settings.
First Use Case: Generate a report from a lint tool (e.g., PHPStan, PSR-12):
use Sweetchuck\LintReport\Facades\LintReport;
$report = LintReport::generate([
'tool' => 'phpstan',
'results' => [
['file' => 'app/Models/User.php', 'message' => 'Type error: Expected string, got int', 'severity' => 'error'],
],
]);
config/lint-report.php).tool_handlers in config (e.g., PHPStan, ESLint, PHP-CS-Fixer).storage/app/lint-reports/ by default.Run Linters: Execute your linter (e.g., PHPStan) and capture output as an array:
vendor/bin/phpstan analyse --error-format=json > phpstan-results.json
Parse the JSON into an array for the package:
$results = json_decode(file_get_contents('phpstan-results.json'), true);
Generate Report:
$report = LintReport::generate([
'tool' => 'phpstan',
'results' => $results,
'options' => ['include_severity' => true],
]);
Store/Process Report:
$report->save(); // Uses default path: storage/app/lint-reports/{tool}-{timestamp}.json
$report->toMarkdown(); // For issue templates
$report->toArray(); // For API responses
Tool-Specific Handling:
Override default tool handlers in config/lint-report.php:
'tool_handlers' => [
'phpstan' => \App\Services\CustomPhpStanHandler::class,
],
Implement Sweetchuck\LintReport\Contracts\ToolHandler:
class CustomPhpStanHandler implements ToolHandler {
public function transform(array $results): array {
return array_map(fn($item) => [
'file' => $item['file'],
'message' => $item['message'],
'line' => $item['line'] ?? null,
], $results);
}
}
Batch Processing: Process multiple lint runs in a loop:
foreach ($lintTools as $tool) {
$results = $this->runLinter($tool);
LintReport::generate(compact('tool', 'results'))->save();
}
Artisan Command: Create a custom command for CLI integration:
use Sweetchuck\LintReport\Facades\LintReport;
class GenerateLintReportCommand extends Command {
protected $signature = 'lint:report {tool} {--results= : Path to results file}';
public function handle() {
$results = json_decode(file_get_contents($this->option('results')), true);
$report = LintReport::generate([
'tool' => $this->argument('tool'),
'results' => $results,
]);
$report->save();
$this->info("Report saved: {$report->path}");
}
}
LintReport facade in tests:
$this->mock(LintReport::class)->shouldReceive('generate')->andReturn($mockReport);
GenerateLintReportJob::dispatch($tool, $results);
Route::post('/lint-webhook', function (Request $request) {
$report = LintReport::generate($request->all());
$report->save();
return response()->json(['status' => 'processed']);
});
Tool Compatibility:
tool_handlers to transform raw output.File Path Handling:
$report->setPath('relative/path/to/report.json');
storage/ paths; use Laravel’s storage_path().Memory Limits:
foreach (array_chunk($results, 100) as $chunk) {
LintReport::generate(['tool' => 'phpstan', 'results' => $chunk])->save();
}
Config Overrides:
config/lint-report.php before publishing.Validate Input:
Ensure results array adheres to the expected schema:
$validator = Validator::make($input, [
'tool' => 'required|string',
'results' => 'required|array',
'results.*.file' => 'required|string',
'results.*.message' => 'required|string',
]);
Log Raw Data: Dump raw lint results before processing:
\Log::debug('Raw lint results', ['results' => $results]);
Check Storage Permissions:
Ensure storage/app/lint-reports/ is writable:
mkdir -p storage/app/lint-reports
chmod -R 775 storage/app/lint-reports
Custom Report Formats:
Extend the Report class to add new formats (e.g., CSV, XML):
class CsvReport extends \Sweetchuck\LintReport\Report {
public function toCsv(): string {
$headers = ['file', 'message', 'severity'];
$rows = array_map(fn($item) => [$item['file'], $item['message'], $item['severity']], $this->results);
return implode("\n", array_merge([implode(',', $headers)], array_map(fn($row) => implode(',', $row), $rows)));
}
}
Database Storage: Store reports in a database table:
class DatabaseReport extends \Sweetchuck\LintReport\Report {
public function save(): void {
DB::table('lint_reports')->insert([
'tool' => $this->tool,
'results' => json_encode($this->results),
'created_at' => now(),
]);
}
}
Slack Notifications: Integrate with Slack via a custom handler:
class SlackNotifier {
public function notify(LintReport $report) {
$webhook = config('services.slack.webhook');
$message = $report->toMarkdown();
Http::post($webhook, ['form_params' => ['text' => $message]]);
}
}
Template Reports: Use Blade templates for human-readable reports:
$report->render('reports::template', ['title' => 'Lint Report']);
Create a view at resources/views/reports/template.blade.php.
GitHub Actions: Upload reports as artifacts:
- name: Generate Lint Report
run: php artisan lint:report phpstan --results=phpstan-results.json
- name: Upload Report
uses: actions/upload-artifact@v3
with:
name: lint-report
path: storage/app/lint-reports/*.json
Severity Filtering: Filter results by severity in the config:
'default_options' => [
'min_severity' => 'warning', // Ignore 'info' severity
],
Retroactive Analysis: Parse historical lint data
How can I help you explore Laravel packages today?