diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 0000000..dc4b6fa --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,49 @@ +on: + - push + +name: Run PHPStan checks + +jobs: + mutation: + name: PHPStan ${{ matrix.php }}-${{ matrix.os }} + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: + - ubuntu-latest + + php: + - "8.2" + - "8.3" + - "8.4" + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: pcov + ini-values: assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On + tools: composer:v2, cs2pr + + - name: Determine composer cache directory + run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV + + - name: Cache dependencies installed with composer + uses: actions/cache@v4 + with: + path: ${{ env.COMPOSER_CACHE_DIR }} + key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: | + php${{ matrix.php }}-composer- + + - name: Install dependencies with composer + run: composer install --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + + - name: Run static analysis with PHPStan + run: vendor/bin/phpstan analyse diff --git a/README.md b/README.md index 455e171..e258b0c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,17 @@ # dot-cli -> [!IMPORTANT] +Dotkernel component to build console applications based on [laminas-cli](https://github.com/laminas/laminas-cli). + > dot-cli is a wrapper on top of [laminas-cli](https://github.com/laminas/laminas-cli) +## Documentation + +Documentation is available at: https://docs.dotkernel.org/dot-cli/. + +## Badges + ![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/dot-cli) -![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-cli/3.8.1) +![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-cli/3.9.0) [![GitHub issues](https://img.shields.io/github/issues/dotkernel/dot-cli)](https://github.com/dotkernel/dot-cli/issues) [![GitHub forks](https://img.shields.io/github/forks/dotkernel/dot-cli)](https://github.com/dotkernel/dot-cli/network) @@ -13,8 +20,7 @@ [![Build Static](https://github.com/dotkernel/dot-cli/actions/workflows/continuous-integration.yml/badge.svg?branch=3.0)](https://github.com/dotkernel/dot-cli/actions/workflows/continuous-integration.yml) [![codecov](https://codecov.io/gh/dotkernel/dot-cli/graph/badge.svg?token=0DFCK2GUBT)](https://codecov.io/gh/dotkernel/dot-cli) - -Dotkernel component to build console applications based on [laminas-cli](https://github.com/laminas/laminas-cli). +[![PHPStan](https://github.com/dotkernel/dot-cli/actions/workflows/static-analysis.yml/badge.svg?branch=3.0)](https://github.com/dotkernel/dot-cli/actions/workflows/static-analysis.yml) ## Requirements diff --git a/composer.json b/composer.json index d0d6af9..a99af41 100644 --- a/composer.json +++ b/composer.json @@ -35,8 +35,9 @@ "require-dev": { "laminas/laminas-coding-standard": "^3.0", "mikey179/vfsstream": "^1.6.7", - "phpunit/phpunit": "^10.2", - "vimeo/psalm": "^6.0" + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.2" }, "autoload-dev": { "psr-4": { @@ -52,7 +53,6 @@ "cs-check": "phpcs", "cs-fix": "phpcbf", "test": "phpunit --colors=always", - "test-coverage": "phpunit --colors=always --coverage-clover clover.xml", - "static-analysis": "psalm --shepherd --stats" + "static-analysis": "phpstan analyse --memory-limit 1G" } } diff --git a/docs/book/v3/overview.md b/docs/book/v3/overview.md index e8f6a6b..b18d4e6 100644 --- a/docs/book/v3/overview.md +++ b/docs/book/v3/overview.md @@ -1,3 +1,19 @@ # Overview +Dotkernel component to build console applications based on [laminas-cli](https://github.com/laminas/laminas-cli). + > dot-cli is a wrapper on top of [laminas-cli](https://github.com/laminas/laminas-cli) + +## Badges + +![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/dot-cli) +![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-cli/3.9.0) + +[![GitHub issues](https://img.shields.io/github/issues/dotkernel/dot-cli)](https://github.com/dotkernel/dot-cli/issues) +[![GitHub forks](https://img.shields.io/github/forks/dotkernel/dot-cli)](https://github.com/dotkernel/dot-cli/network) +[![GitHub stars](https://img.shields.io/github/stars/dotkernel/dot-cli)](https://github.com/dotkernel/dot-cli/stargazers) +[![GitHub license](https://img.shields.io/github/license/dotkernel/dot-cli)](https://github.com/dotkernel/dot-cli/blob/3.0/LICENSE) + +[![Build Static](https://github.com/dotkernel/dot-cli/actions/workflows/continuous-integration.yml/badge.svg?branch=3.0)](https://github.com/dotkernel/dot-cli/actions/workflows/continuous-integration.yml) +[![codecov](https://codecov.io/gh/dotkernel/dot-cli/graph/badge.svg?token=0DFCK2GUBT)](https://codecov.io/gh/dotkernel/dot-cli) +[![PHPStan](https://github.com/dotkernel/dot-cli/actions/workflows/static-analysis.yml/badge.svg?branch=3.0)](https://github.com/dotkernel/dot-cli/actions/workflows/static-analysis.yml) diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..349be25 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,8 @@ +includes: + - vendor/phpstan/phpstan-phpunit/extension.neon +parameters: + level: 5 + paths: + - src + - test + treatPhpDocTypesAsCertain: false diff --git a/psalm-baseline.xml b/psalm-baseline.xml deleted file mode 100644 index 9352203..0000000 --- a/psalm-baseline.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - new TerminateListener($config) - new ContainerCommandLoader($container, $config['commands']) - - - new TerminateListener($config) - new ContainerCommandLoader($container, $config['commands']) - - - diff --git a/psalm.xml b/psalm.xml deleted file mode 100644 index 9dd8f07..0000000 --- a/psalm.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - diff --git a/test/Command/DemoCommandTest.php b/test/Command/DemoCommandTest.php index 1987287..956256a 100644 --- a/test/Command/DemoCommandTest.php +++ b/test/Command/DemoCommandTest.php @@ -20,7 +20,7 @@ class DemoCommandTest extends TestCase public function testWillCreateCommand(): void { $command = new DemoCommand(); - $this->assertInstanceOf(DemoCommand::class, $command); + $this->assertSame(DemoCommand::class, $command::class); } /** diff --git a/test/FileLockerTest.php b/test/FileLockerTest.php index 996d2e5..4027d47 100644 --- a/test/FileLockerTest.php +++ b/test/FileLockerTest.php @@ -32,28 +32,28 @@ public function testAccessors(): void $fileLocker = new FileLocker(); $this->assertFalse($fileLocker->isEnabled()); $fileLocker->enable(); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertTrue($fileLocker->isEnabled()); $fileLocker->disable(); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertFalse($fileLocker->isEnabled()); $fileLocker->setEnabled(true); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertTrue($fileLocker->isEnabled()); $fileLocker->setEnabled(false); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertFalse($fileLocker->isEnabled()); $this->assertNull($fileLocker->getDirPath()); $fileLocker->setDirPath('test'); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertSame('test', $fileLocker->getDirPath()); $this->assertNull($fileLocker->getCommandName()); $fileLocker->setCommandName('test'); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertSame('test', $fileLocker->getCommandName()); $this->assertNull($fileLocker->getLockFile()); $fileLocker->setLockFile('test'); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertSame('test', $fileLocker->getLockFile()); $this->assertSame('test/command-test.lock', $fileLocker->getLockFilePath()); } @@ -65,7 +65,7 @@ public function testWillInitLockFile(): void $fileLocker = new FileLocker(true, $config['dirPath'], 'test'); $this->assertNull($fileLocker->getLockFile()); $fileLocker->initLockFile(); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertIsResource($fileLocker->getLockFile()); } @@ -79,7 +79,7 @@ public function testWillNotLockWhenDisabled(): void $fileLocker = new FileLocker(false, $config['dirPath'], 'test'); $this->assertNull($fileLocker->getLockFile()); $fileLocker->lock(); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertNull($fileLocker->getLockFile()); } @@ -93,7 +93,7 @@ public function testWillNotLockWithoutValidCommandName(): void $fileLocker = new FileLocker(true, $config['dirPath']); $this->assertNull($fileLocker->getLockFile()); $fileLocker->lock(); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertNull($fileLocker->getLockFile()); } @@ -107,7 +107,7 @@ public function testWillLockWhenLockedAndEnabledAndHasValidCommandName(): void $fileLocker = new FileLocker(true, $config['dirPath'], 'test'); $this->assertNull($fileLocker->getLockFile()); $fileLocker->lock(); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertIsResource($fileLocker->getLockFile()); $this->assertFileExists($fileLocker->getLockFilePath()); } @@ -119,7 +119,7 @@ public function testWillNotUnlockWhenDisabled(): void $fileLocker = new FileLocker(false, $config['dirPath'], 'test'); $this->assertNull($fileLocker->getLockFile()); $fileLocker->unlock(); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertNull($fileLocker->getLockFile()); } @@ -130,7 +130,7 @@ public function testWillNotUnlockWithoutValidCommandName(): void $fileLocker = new FileLocker(false, $config['dirPath']); $this->assertNull($fileLocker->getLockFile()); $fileLocker->unlock(); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertNull($fileLocker->getLockFile()); } @@ -141,7 +141,7 @@ public function testWillNotUnlockWhenEnabledAndWithoutValidCommandName(): void $fileLocker = new FileLocker(true, $config['dirPath']); $this->assertNull($fileLocker->getLockFile()); $fileLocker->unlock(); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertNull($fileLocker->getLockFile()); } @@ -154,11 +154,11 @@ public function testWillUnlockWhenLockedAndEnabledAndHasValidCommandName(): void $fileLocker = new FileLocker(true, $config['dirPath'], 'test'); $fileLocker->lock(); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertIsResource($fileLocker->getLockFile()); $this->assertFileExists($fileLocker->getLockFilePath()); $fileLocker->unlock(); - $this->assertInstanceOf(FileLocker::class, $fileLocker); + $this->assertSame(FileLocker::class, $fileLocker::class); $this->assertFileIsReadable($fileLocker->getLockFilePath()); $this->assertFileIsWritable($fileLocker->getLockFilePath()); }