diff --git a/.gitignore b/.gitignore index b1389f1..70000de 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,9 @@ public/build /coverage /docs/.vitepress/dist docs/.vitepress/cache/ + +# QA tools +.infection/ +infection.log +infection-summary.log +.rector-cache/ diff --git a/composer.json b/composer.json index 0ac08ac..6bf2317 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ }, "require-dev": { "fakerphp/faker": "^1.23", + "infection/infection": "^0.32.3", "larastan/larastan": "^3.9", "laravel/pint": "^1.18", "mockery/mockery": "^1.6", @@ -26,6 +27,9 @@ "phpstan/phpstan": "^2.1", "phpstan/phpstan-deprecation-rules": "^2.0", "phpunit/phpunit": "^11.5", + "psalm/plugin-laravel": "^3.0", + "rector/rector": "^2.3", + "roave/security-advisories": "dev-latest", "spatie/laravel-activitylog": "^4.8", "vimeo/psalm": "^6.14" }, @@ -72,7 +76,8 @@ "sort-packages": true, "allow-plugins": { "php-http/discovery": true, - "phpstan/extension-installer": true + "phpstan/extension-installer": true, + "infection/extension-installer": true } }, "minimum-stability": "stable", diff --git a/infection.json5 b/infection.json5 new file mode 100644 index 0000000..5acb831 --- /dev/null +++ b/infection.json5 @@ -0,0 +1,20 @@ +{ + "$schema": "https://raw.githubusercontent.com/infection/infection/0.29.0/resources/schema.json", + "source": { + "directories": ["src"] + }, + "logs": { + "text": "infection.log", + "summary": "infection-summary.log" + }, + "mutators": { + "@default": true + }, + "phpUnit": { + "configDir": "." + }, + "testFramework": "phpunit", + "tmpDir": ".infection", + "minMsi": 50, + "minCoveredMsi": 70 +} diff --git a/psalm.xml b/psalm.xml index b4e4aff..1366687 100644 --- a/psalm.xml +++ b/psalm.xml @@ -8,6 +8,10 @@ findUnusedBaselineEntry="false" findUnusedCode="false" > + + + + diff --git a/qa.yaml b/qa.yaml new file mode 100644 index 0000000..86a463e --- /dev/null +++ b/qa.yaml @@ -0,0 +1,107 @@ +# PHP Quality Assurance Pipeline +# This file defines the QA process for `core php qa` command +# +# Usage: core php qa [--fix] [--full] +# --fix Apply automatic fixes where possible +# --full Run full suite including slow checks (mutation testing) + +name: PHP Quality Assurance +version: 1.0.0 + +# Tool versions and config files +tools: + pint: + config: pint.json + description: Code style (PSR-12 + Laravel conventions) + + phpstan: + config: phpstan.neon + level: 1 + description: Static analysis (type checking) + + psalm: + config: psalm.xml + level: 8 + description: Static analysis (deeper type inference) + + infection: + config: infection.json5 + description: Mutation testing (test quality) + + rector: + config: rector.php + description: Automated refactoring and upgrades + +# QA Pipeline stages +stages: + # Stage 1: Quick checks (< 30 seconds) + quick: + - name: Security Audit + command: composer audit + description: Check dependencies for known vulnerabilities + fix: false + + - name: Code Style + command: ./vendor/bin/pint --test + fix_command: ./vendor/bin/pint + description: Check PSR-12 and Laravel code style + + - name: PHPStan + command: ./vendor/bin/phpstan analyse --no-progress + description: Static analysis level 1 + fix: false + + # Stage 2: Standard checks (< 2 minutes) + standard: + - name: Psalm + command: ./vendor/bin/psalm --no-progress + description: Deep static analysis + fix: false + + - name: Tests + command: ./vendor/bin/phpunit --testdox + description: Run test suite + fix: false + + # Stage 3: Full checks (can be slow) + full: + - name: Rector (dry-run) + command: ./vendor/bin/rector process --dry-run + fix_command: ./vendor/bin/rector process + description: Check for automated improvements + + - name: Mutation Testing + command: ./vendor/bin/infection --min-msi=50 --min-covered-msi=70 --threads=4 + description: Test suite quality via mutation testing + fix: false + slow: true + +# Exit codes +exit_codes: + 0: All checks passed + 1: Code style issues (fixable) + 2: Static analysis errors + 3: Test failures + 4: Security vulnerabilities + 5: Mutation score too low + +# Recommended CI configuration +ci: + # Run on every push + push: + - quick + - standard + + # Run on PRs to main + pull_request: + - quick + - standard + - full + +# Thresholds +thresholds: + phpstan_level: 1 + psalm_level: 8 + test_coverage: 70 + mutation_msi: 50 + mutation_covered_msi: 70 diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..a09b257 --- /dev/null +++ b/rector.php @@ -0,0 +1,38 @@ +withPaths([ + __DIR__.'/src', + ]) + ->withSkip([ + __DIR__.'/src/Core/Activity', + __DIR__.'/src/Core/Tests', + __DIR__.'/src/Mod/Trees', + ]) + ->withSets([ + // PHP version upgrades + LevelSetList::UP_TO_PHP_82, + + // Code quality + SetList::CODE_QUALITY, + SetList::CODING_STYLE, + SetList::DEAD_CODE, + SetList::EARLY_RETURN, + SetList::TYPE_DECLARATION, + ]) + ->withImportNames( + importShortClasses: false, + removeUnusedImports: true + ) + ->withPreparedSets( + deadCode: true, + codeQuality: true, + typeDeclarations: true, + earlyReturn: true, + );