diff --git a/.github/wiki b/.github/wiki index 3751f1e85..34c3747e6 160000 --- a/.github/wiki +++ b/.github/wiki @@ -1 +1 @@ -Subproject commit 3751f1e85cbbb350848e23dbd2d12dc0182b55f7 +Subproject commit 34c3747e66043b351e6cc5b9cd5403c0d1ea88e9 diff --git a/CHANGELOG.md b/CHANGELOG.md index dc9ea00ff..abbc78d36 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 + +- Show DevTools version metadata in CLI application output so `list` and `--version` expose the installed package version from `Application` metadata (#339). + ## [1.25.3] - 2026-05-11 ### Fixed diff --git a/src/Console/DevTools.php b/src/Console/DevTools.php index 7360b1a14..a5d4640b9 100644 --- a/src/Console/DevTools.php +++ b/src/Console/DevTools.php @@ -26,6 +26,7 @@ use FastForward\DevTools\SelfUpdate\SelfUpdateRunnerInterface; use FastForward\DevTools\SelfUpdate\SelfUpdateScopeResolverInterface; use FastForward\DevTools\SelfUpdate\VersionCheckNotifierInterface; +use FastForward\DevTools\SelfUpdate\VersionCheckerInterface; use FastForward\DevTools\SelfUpdate\WorkingDirectorySwitcherInterface; use Override; use Symfony\Component\Console\Application; @@ -73,6 +74,7 @@ final class DevTools extends Application * @param VersionCheckNotifierInterface $versionCheckNotifier emits non-blocking version freshness warnings * @param SelfUpdateRunnerInterface $selfUpdateRunner runs explicit or automatic self-update flows * @param SelfUpdateScopeResolverInterface $selfUpdateScopeResolver resolves whether the active binary is global + * @param VersionCheckerInterface $versionChecker resolves the installed DevTools version for metadata output * @param EnvironmentInterface $environment reads environment flags for optional auto-update behavior * @param RuntimeEnvironmentInterface $runtimeEnvironment resolves runtime environment capabilities */ @@ -82,10 +84,11 @@ public function __construct( private readonly VersionCheckNotifierInterface $versionCheckNotifier, private readonly SelfUpdateRunnerInterface $selfUpdateRunner, private readonly SelfUpdateScopeResolverInterface $selfUpdateScopeResolver, + private readonly VersionCheckerInterface $versionChecker, private readonly EnvironmentInterface $environment, private readonly RuntimeEnvironmentInterface $runtimeEnvironment, ) { - parent::__construct('Fast Forward Dev Tools'); + parent::__construct('Fast Forward Dev Tools', $this->versionChecker->getCurrentVersion()); $this->setDefaultCommand('dev-tools:standards'); $this->setCommandLoader($commandLoader); diff --git a/src/SelfUpdate/ComposerVersionChecker.php b/src/SelfUpdate/ComposerVersionChecker.php index ad2023fbd..e2178de8a 100644 --- a/src/SelfUpdate/ComposerVersionChecker.php +++ b/src/SelfUpdate/ComposerVersionChecker.php @@ -23,6 +23,7 @@ use FastForward\DevTools\Path\DevToolsPathResolver; use FastForward\DevTools\Process\ProcessBuilderInterface; use JsonException; +use Throwable; use function Safe\preg_match; use function Safe\json_decode; @@ -34,6 +35,8 @@ { private const string PACKAGE = 'fast-forward/dev-tools'; + private const string VERSION_UNKNOWN = '0.0.0'; + private const int TIMEOUT_SECONDS = 5; /** @@ -52,10 +55,9 @@ public function check(): ?VersionCheckResult return null; } - $currentVersion = InstalledVersions::getPrettyVersion(self::PACKAGE) - ?? InstalledVersions::getVersion(self::PACKAGE); + $currentVersion = $this->getCurrentVersion(); - if (null === $currentVersion) { + if (self::VERSION_UNKNOWN === $currentVersion) { return null; } @@ -68,6 +70,29 @@ public function check(): ?VersionCheckResult return new VersionCheckResult($currentVersion, $latestVersion); } + /** + * Returns the installed DevTools version without running external Composer commands. + * + * This method MUST return the package version when composer metadata is + * available. + * It MUST return `VERSION_UNKNOWN` when metadata is unavailable or on + * resolution errors. + */ + public function getCurrentVersion(): string + { + if (! InstalledVersions::isInstalled(self::PACKAGE)) { + return self::VERSION_UNKNOWN; + } + + try { + return InstalledVersions::getPrettyVersion(self::PACKAGE) + ?? InstalledVersions::getVersion(self::PACKAGE) + ?? self::VERSION_UNKNOWN; + } catch (Throwable) { + return self::VERSION_UNKNOWN; + } + } + /** * Resolves the latest stable DevTools version available to Composer. */ diff --git a/src/SelfUpdate/VersionCheckerInterface.php b/src/SelfUpdate/VersionCheckerInterface.php index bf8fcfa24..3a32b0216 100644 --- a/src/SelfUpdate/VersionCheckerInterface.php +++ b/src/SelfUpdate/VersionCheckerInterface.php @@ -28,4 +28,13 @@ interface VersionCheckerInterface * Returns version information when it can be resolved without blocking command execution. */ public function check(): ?VersionCheckResult; + + /** + * Returns the resolved DevTools version for display and internal comparison. + * + * The method MUST return a safe version string even when installed metadata + * is unavailable. + * It MAY be a fallback value. + */ + public function getCurrentVersion(): string; } diff --git a/tests/Console/DevToolsTest.php b/tests/Console/DevToolsTest.php index d8511670a..1116e9231 100644 --- a/tests/Console/DevToolsTest.php +++ b/tests/Console/DevToolsTest.php @@ -44,6 +44,7 @@ use FastForward\DevTools\SelfUpdate\ComposerVersionChecker; use FastForward\DevTools\SelfUpdate\SelfUpdateRunnerInterface; use FastForward\DevTools\SelfUpdate\SelfUpdateScopeResolverInterface; +use FastForward\DevTools\SelfUpdate\VersionCheckerInterface; use FastForward\DevTools\SelfUpdate\VersionCheckNotifier; use FastForward\DevTools\SelfUpdate\VersionCheckNotifierInterface; use FastForward\DevTools\SelfUpdate\WorkingDirectorySwitcher; @@ -124,6 +125,11 @@ final class DevToolsTest extends TestCase */ private ObjectProphecy $selfUpdateScopeResolver; + /** + * @var ObjectProphecy + */ + private ObjectProphecy $versionChecker; + /** * @var ObjectProphecy */ @@ -154,8 +160,11 @@ protected function setUp(): void $this->versionCheckNotifier = $this->prophesize(VersionCheckNotifierInterface::class); $this->selfUpdateRunner = $this->prophesize(SelfUpdateRunnerInterface::class); $this->selfUpdateScopeResolver = $this->prophesize(SelfUpdateScopeResolverInterface::class); + $this->versionChecker = $this->prophesize(VersionCheckerInterface::class); $this->environment = $this->prophesize(EnvironmentInterface::class); $this->runtimeEnvironment = $this->prophesize(RuntimeEnvironmentInterface::class); + $this->versionChecker->getCurrentVersion() + ->willReturn('1.2.3'); $this->runtimeEnvironment->isAgentPresent() ->willReturn(false); $this->originalWorkspaceDirectoryEnv = getenv(ManagedWorkspace::ENV_WORKSPACE_DIR); @@ -214,6 +223,7 @@ public function __construct() ->willReturn($customCommand); self::assertSame('Fast Forward Dev Tools', $this->devTools->getName()); + self::assertSame('1.2.3', $this->devTools->getVersion()); self::assertTrue($this->devTools->has('custom')); self::assertSame($customCommand, $this->devTools->get('custom')); } @@ -593,6 +603,7 @@ private function createDevTools(): DevTools $this->versionCheckNotifier->reveal(), $this->selfUpdateRunner->reveal(), $this->selfUpdateScopeResolver->reveal(), + $this->versionChecker->reveal(), $this->environment->reveal(), $this->runtimeEnvironment->reveal(), );