diff --git a/.github/wiki b/.github/wiki index bce2ecf64a..6feadca091 160000 --- a/.github/wiki +++ b/.github/wiki @@ -1 +1 @@ -Subproject commit bce2ecf64a026c396a0c3a2dc9b37eb837abdba2 +Subproject commit 6feadca0911ba4de9a4a7e711d591df343c0a06e diff --git a/CHANGELOG.md b/CHANGELOG.md index e5f117288a..b6366b7227 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Restore global DevTools new-version notifications by using a supported Symfony Process success check during release lookups (#300) + ## [1.24.3] - 2026-04-30 ### Fixed diff --git a/src/SelfUpdate/ComposerVersionChecker.php b/src/SelfUpdate/ComposerVersionChecker.php index 46b3398bff..ad2023fbde 100644 --- a/src/SelfUpdate/ComposerVersionChecker.php +++ b/src/SelfUpdate/ComposerVersionChecker.php @@ -23,7 +23,6 @@ use FastForward\DevTools\Path\DevToolsPathResolver; use FastForward\DevTools\Process\ProcessBuilderInterface; use JsonException; -use Symfony\Component\Process\Process; use function Safe\preg_match; use function Safe\json_decode; @@ -83,7 +82,9 @@ private function resolveLatestStableVersion(): ?string $process->setTimeout(self::TIMEOUT_SECONDS); - if (Process::SUCCESS !== $process->run()) { + $process->run(); + + if (! $process->isSuccessful()) { return null; } diff --git a/tests/SelfUpdate/ComposerVersionCheckerTest.php b/tests/SelfUpdate/ComposerVersionCheckerTest.php new file mode 100644 index 0000000000..b4c2b2c3f7 --- /dev/null +++ b/tests/SelfUpdate/ComposerVersionCheckerTest.php @@ -0,0 +1,118 @@ + + * @license https://opensource.org/licenses/MIT MIT License + * + * @see https://github.com/php-fast-forward/ + * @see https://github.com/php-fast-forward/dev-tools + * @see https://github.com/php-fast-forward/dev-tools/issues + * @see https://php-fast-forward.github.io/dev-tools/ + * @see https://datatracker.ietf.org/doc/html/rfc2119 + */ + +namespace FastForward\DevTools\Tests\SelfUpdate; + +use FastForward\DevTools\Process\ProcessBuilderInterface; +use FastForward\DevTools\SelfUpdate\ComposerVersionChecker; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; +use Prophecy\Prophecy\ObjectProphecy; +use ReflectionMethod; +use Symfony\Component\Process\Process; + +#[CoversClass(ComposerVersionChecker::class)] +final class ComposerVersionCheckerTest extends TestCase +{ + use ProphecyTrait; + + /** + * @var ObjectProphecy + */ + private ObjectProphecy $processBuilder; + + private ComposerVersionChecker $checker; + + /** + * @return void + */ + protected function setUp(): void + { + $this->processBuilder = $this->prophesize(ProcessBuilderInterface::class); + $this->checker = new ComposerVersionChecker($this->processBuilder->reveal()); + } + + /** + * @return void + */ + #[Test] + public function resolveLatestStableVersionWillReturnFirstStableComposerVersion(): void + { + $process = new Process([ + 'php', + '-r', + 'echo json_encode(["versions" => ["1.x-dev", "v1.24.3", "v1.24.2"]]);', + ]); + + $this->expectComposerShowLookup($process); + + self::assertSame('v1.24.3', $this->invokeResolveLatestStableVersion($this->checker)); + } + + /** + * @return void + */ + #[Test] + public function resolveLatestStableVersionWillReturnNullWhenComposerShowFails(): void + { + $process = new Process(['php', '-r', 'fwrite(STDERR, "lookup failed"); exit(1);']); + + $this->expectComposerShowLookup($process); + + self::assertNull($this->invokeResolveLatestStableVersion($this->checker)); + } + + /** + * @param Process $process + * + * @return void + */ + private function expectComposerShowLookup(Process $process): void + { + $builder = $this->processBuilder->reveal(); + + $this->processBuilder->withArgument('fast-forward/dev-tools') + ->willReturn($builder) + ->shouldBeCalledOnce(); + $this->processBuilder->withArgument('--available') + ->willReturn($builder) + ->shouldBeCalledOnce(); + $this->processBuilder->withArgument('--format=json') + ->willReturn($builder) + ->shouldBeCalledOnce(); + $this->processBuilder->withArgument('--no-interaction') + ->willReturn($builder) + ->shouldBeCalledOnce(); + $this->processBuilder->build('composer show') + ->willReturn($process) + ->shouldBeCalledOnce(); + } + + /** + * @param ComposerVersionChecker $checker + * + * @return ?string + */ + private function invokeResolveLatestStableVersion(ComposerVersionChecker $checker): ?string + { + return (new ReflectionMethod($checker, 'resolveLatestStableVersion'))->invoke($checker); + } +}