diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 3ddf8217..9cbb3250 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -39,22 +39,21 @@ jobs: - name: "Run Psalm" run: "vendor/bin/psalm" -# Disabled for now... - see https://github.com/scoutapp/scout-apm-php/issues/209 -# roave-backwards-compatibility-check: -# name: "Check for Backward Compatibility breaks" -# runs-on: ubuntu-latest -# steps: -# - uses: actions/checkout@v2 -# with: -# fetch-depth: 0 -# - name: "Install PHP" -# uses: shivammathur/setup-php@v2 -# with: -# coverage: "none" -# php-version: "7.4" # https://github.com/Roave/BackwardCompatibilityCheck/issues/283 -# - name: "Require Roave/BackwardCompatibilityCheck" -# run: "composer require --no-update --no-interaction --prefer-dist --prefer-stable --dev roave/backward-compatibility-check" -# - name: "Composer update with new requirements" -# run: "composer update --no-interaction --prefer-dist --prefer-stable" -# - name: "Check for BC breaks" -# run: "vendor/bin/roave-backward-compatibility-check --format=markdown" + roave-backwards-compatibility-check: + name: "Check for Backward Compatibility breaks" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: "Install PHP" + uses: shivammathur/setup-php@v2 + with: + coverage: "none" + php-version: "7.4" # https://github.com/Roave/BackwardCompatibilityCheck/issues/283 + - name: "Require Roave/BackwardCompatibilityCheck" + run: "composer require --no-update --no-interaction --prefer-dist --prefer-stable --dev roave/backward-compatibility-check:dev-allow-dev-deps-to-be-installed" + - name: "Composer update with new requirements" + run: "composer update --no-interaction --prefer-dist --prefer-stable" + - name: "Check for BC breaks" + run: ".github/workflows/roave-backwards-compatibility-check.sh" diff --git a/.github/workflows/roave-backwards-compatibility-check.sh b/.github/workflows/roave-backwards-compatibility-check.sh new file mode 100755 index 00000000..b0060751 --- /dev/null +++ b/.github/workflows/roave-backwards-compatibility-check.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +set -uo pipefail + +cd "$(dirname "$0")/../.." || exit 2 + +# This file is a hack to suppress warnings from Roave BC check +# Based on: https://github.com/guzzle/guzzle/blob/7a30f3bc91b3ab57860efbe8272649cc23dbbcc2/.github/workflows/bc.entrypoint + +echo "Running BC check, please wait..." + +# Capture output to variable AND print it +OUTPUT=$(vendor/bin/roave-backward-compatibility-check --format=markdown --install-development-dependencies 2>&1) + +# Remove rows we want to suppress +OUTPUT=`echo "$OUTPUT" | sed '/Roave\\\BetterReflection\\\Reflection\\\ReflectionClass "Symfony\\\Component\\\HttpKernel\\\Event\\\FilterControllerEvent" could not be found in the located source/'d` +OUTPUT=`echo "$OUTPUT" | sed '/Roave\\\BetterReflection\\\Reflection\\\ReflectionClass "Scoutapm\\\ScoutApmBundle\\\Twig\\\TwigMethods" could not be found in the located source/'d` + +# Number of rows we found with "[BC]" in them +BC_BREAKS=`echo "$OUTPUT" | grep -o '\[BC\]' | wc -l | awk '{ print $1 }'` + +# The last row of the output is "X backwards-incompatible changes detected". Find X. +STATED_BREAKS=`echo "$OUTPUT" | tail -n 1 | awk -F' ' '{ print $1 }'` + +EXPECTED_STATED_BREAKS=18 + +echo "$OUTPUT" +echo "Lines with [BC] not filtered: $BC_BREAKS" +echo "Stated breaks: $STATED_BREAKS out of expected $EXPECTED_STATED_BREAKS" + +# If +# We found "[BC]" in the command output after we removed suppressed lines +# OR +# We have suppressed X number of BC breaks. If $STATED_BREAKS is larger than X +# THEN +# exit 1 +if [ $BC_BREAKS -gt 0 ] || [ $STATED_BREAKS -gt $EXPECTED_STATED_BREAKS ]; then + echo "EXIT 1" + exit 1 +fi + +# No BC breaks found +echo "EXIT 0" +exit 0 diff --git a/CHANGELOG.md b/CHANGELOG.md index e280d650..d1dc7a4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file, in reverse ### Added - [#238](https://github.com/scoutapp/scout-apm-php/pull/238) Add scheduled CI run every month to ensure compatibility with dependency updates +- [#240](https://github.com/scoutapp/scout-apm-php/pull/240) Add support for psr/log ^1.0|^2.0|^3.0, and added Roave/BackwardCompatibilityCheck to CI ### Changed diff --git a/composer.json b/composer.json index 595b1fc1..3967a201 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "ext-openssl": "*", "ext-sockets": "*", "ext-zlib": "*", - "psr/log": "^1.0", + "psr/log": "^1.0|^2.0|^3.0", "psr/simple-cache": "^1.0", "ralouphie/getallheaders": "^2.0.5|^3.0", "ramsey/uuid": "^3.0|^4.0", @@ -26,7 +26,6 @@ "monolog/monolog": "^1.26|^2.2.0", "phpunit/phpunit": "^7.5.20|^8.5.14|^9.5.2", "psalm/plugin-phpunit": "^0.15.1", - "psr/log": "^1.1", "symfony/config": "^4.0 || ^5.0", "symfony/dependency-injection": "^4.0 || ^5.0", "symfony/event-dispatcher": "^4.0 || ^5.0", diff --git a/composer.lock b/composer.lock index 14562584..4c3116de 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8e524eca6fb0b6f624f79746db326beb", + "content-hash": "0056e8f9337048e962d5adb8a8e2a16b", "packages": [ { "name": "brick/math", @@ -64,16 +64,16 @@ }, { "name": "psr/log", - "version": "1.1.3", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", "shasum": "" }, "require": { @@ -97,7 +97,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for logging libraries", @@ -108,9 +108,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.3" + "source": "https://github.com/php-fig/log/tree/1.1.4" }, - "time": "2020-03-23T09:12:05+00:00" + "time": "2021-05-03T11:20:27+00:00" }, { "name": "psr/simple-cache", diff --git a/tests/Integration/AgentTest.php b/tests/Integration/AgentTest.php index 653fbcb7..f2337217 100644 --- a/tests/Integration/AgentTest.php +++ b/tests/Integration/AgentTest.php @@ -11,7 +11,6 @@ use MongoDB\Driver\Manager; use MongoDB\Driver\Query; use PHPUnit\Framework\TestCase; -use Psr\Log\Test\TestLogger; use Scoutapm\Agent; use Scoutapm\Config; use Scoutapm\Config\ConfigKey; @@ -19,6 +18,7 @@ use Scoutapm\Connector\SocketConnector; use Scoutapm\Events\Span\SpanReference; use Scoutapm\Extension\PotentiallyAvailableExtensionCapabilities; +use Scoutapm\UnitTests\TestLogger; use function assert; use function extension_loaded; diff --git a/tests/Unit/AgentTest.php b/tests/Unit/AgentTest.php index 1b76d0cc..96496a69 100644 --- a/tests/Unit/AgentTest.php +++ b/tests/Unit/AgentTest.php @@ -10,7 +10,6 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LogLevel; -use Psr\Log\Test\TestLogger; use Scoutapm\Agent; use Scoutapm\Cache\DevNullCache; use Scoutapm\Config; diff --git a/tests/Unit/CoreAgent/LauncherTest.php b/tests/Unit/CoreAgent/LauncherTest.php index 2a9f49c7..1e314eae 100644 --- a/tests/Unit/CoreAgent/LauncherTest.php +++ b/tests/Unit/CoreAgent/LauncherTest.php @@ -5,10 +5,10 @@ namespace Scoutapm\UnitTests\CoreAgent; use PHPUnit\Framework\TestCase; -use Psr\Log\Test\TestLogger; use Scoutapm\Config; use Scoutapm\Connector\ConnectionAddress; use Scoutapm\CoreAgent\Launcher; +use Scoutapm\UnitTests\TestLogger; /** @covers \Scoutapm\CoreAgent\Launcher */ final class LauncherTest extends TestCase diff --git a/tests/Unit/TestLogger.php b/tests/Unit/TestLogger.php new file mode 100644 index 00000000..d5ed3086 --- /dev/null +++ b/tests/Unit/TestLogger.php @@ -0,0 +1,173 @@ + */ + public $records = []; + + /** @psalm-var array> */ + public $recordsByLevel = []; + + /** + * @inheritdoc + */ + public function log($level, $message, array $context = []) + { + $record = [ + 'level' => (string) $level, + 'message' => $message, + 'context' => $context, + ]; + + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } + + public function hasRecords(string $level): bool + { + return isset($this->recordsByLevel[$level]); + } + + /** + * @param string|array{message:string, context?: mixed} $record + */ + public function hasRecord($record, string $level): bool + { + if (is_string($record)) { + $record = ['message' => $record]; + } + + return $this->hasRecordThatPasses( + /** @psalm-param LogRecord $rec */ + static function ($rec) use ($record): bool { + if ($rec['message'] !== $record['message']) { + return false; + } + + return ! isset($record['context']) || $rec['context'] === $record['context']; + }, + $level + ); + } + + public function hasRecordThatContains(string $message, string $level): bool + { + return $this->hasRecordThatPasses( + /** @psalm-param LogRecord $rec */ + static function ($rec) use ($message): bool { + return strpos($rec['message'], $message) !== false; + }, + $level + ); + } + + public function hasRecordThatMatches(string $regex, string $level): bool + { + return $this->hasRecordThatPasses( + /** @psalm-param LogRecord $rec */ + static function ($rec) use ($regex): bool { + return preg_match($regex, $rec['message']) > 0; + }, + $level + ); + } + + /** @psalm-param callable(LogRecord):bool $predicate */ + public function hasRecordThatPasses(callable $predicate, string $level): bool + { + if (! isset($this->recordsByLevel[$level])) { + return false; + } + + foreach ($this->recordsByLevel[$level] as $rec) { + if ($predicate($rec)) { + return true; + } + } + + return false; + } + + /** @param array $args */ + public function __call(string $method, array $args): bool + { + if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { + $genericMethod = $matches[1] . ($matches[3] !== 'Records' ? 'Record' : '') . $matches[3]; + $level = strtolower($matches[2]); + if (method_exists($this, $genericMethod)) { + $args[] = $level; + + return (bool) call_user_func_array([$this, $genericMethod], $args); + } + } + + throw new BadMethodCallException('Call to undefined method ' . static::class . '::' . $method . '()'); + } + + public function reset(): void + { + $this->records = []; + $this->recordsByLevel = []; + } +} diff --git a/tests/isolated-memory-test.php b/tests/isolated-memory-test.php index adf15a75..a0c551aa 100644 --- a/tests/isolated-memory-test.php +++ b/tests/isolated-memory-test.php @@ -2,11 +2,11 @@ declare(strict_types=1); -use Psr\Log\Test\TestLogger; use Scoutapm\Agent; use Scoutapm\Config; use Scoutapm\Events\Span\SpanReference; use Scoutapm\Extension\PotentiallyAvailableExtensionCapabilities; +use Scoutapm\UnitTests\TestLogger; require __DIR__ . '/../vendor/autoload.php';