diff --git a/src/Command/SelfUpdateCommand.php b/src/Command/SelfUpdateCommand.php index ea9a1529..b60860fa 100644 --- a/src/Command/SelfUpdateCommand.php +++ b/src/Command/SelfUpdateCommand.php @@ -13,6 +13,7 @@ use Php\Pie\File\FullPathToSelf; use Php\Pie\File\SudoFilePut; use Php\Pie\SelfManage\Update\FetchPieReleaseFromGitHub; +use Php\Pie\SelfManage\Update\PiePharMissingFromLatestRelease; use Php\Pie\SelfManage\Update\ReleaseMetadata; use Php\Pie\SelfManage\Verify\FailedToVerifyRelease; use Php\Pie\SelfManage\Verify\VerifyPieReleaseUsingAttestation; @@ -92,8 +93,15 @@ public function execute(InputInterface $input, OutputInterface $output): int $output->writeln('Downloading the latest nightly release.'); } else { - $latestRelease = $fetchLatestPieRelease->latestReleaseMetadata(); - $pieVersion = PieVersion::get(); + try { + $latestRelease = $fetchLatestPieRelease->latestReleaseMetadata(); + } catch (PiePharMissingFromLatestRelease $piePharMissingFromLatestRelease) { + $output->writeln(sprintf('%s', $piePharMissingFromLatestRelease->getMessage())); + + return Command::FAILURE; + } + + $pieVersion = PieVersion::get(); if (preg_match('/^(?.+)@(?[a-f0-9]{7})$/', $pieVersion, $matches)) { // Have to change the version to something the Semver library understands diff --git a/src/Downloading/GithubPackageReleaseAssets.php b/src/Downloading/GithubPackageReleaseAssets.php index 89b26c9a..5b10942e 100644 --- a/src/Downloading/GithubPackageReleaseAssets.php +++ b/src/Downloading/GithubPackageReleaseAssets.php @@ -79,7 +79,7 @@ private function getReleaseAssetsForPackage( Assert::notNull($package->downloadUrl()); try { - $decodedRepsonse = $httpDownloader->get( + $decodedResponse = $httpDownloader->get( $this->githubApiBaseUrl . '/repos/' . $package->githubOrgAndRepository() . '/releases/tags/' . $package->version(), [ 'retry-auth-failure' => true, @@ -98,9 +98,9 @@ private function getReleaseAssetsForPackage( throw $t; } - Assert::isArray($decodedRepsonse); - Assert::keyExists($decodedRepsonse, 'assets'); - Assert::isList($decodedRepsonse['assets']); + Assert::isArray($decodedResponse); + Assert::keyExists($decodedResponse, 'assets'); + Assert::isList($decodedResponse['assets']); return array_map( static function (array $asset): array { @@ -111,7 +111,7 @@ static function (array $asset): array { return $asset; }, - $decodedRepsonse['assets'], + $decodedResponse['assets'], ); } } diff --git a/src/SelfManage/Update/FetchPieRelease.php b/src/SelfManage/Update/FetchPieRelease.php index ef02be0e..3ec78c7e 100644 --- a/src/SelfManage/Update/FetchPieRelease.php +++ b/src/SelfManage/Update/FetchPieRelease.php @@ -9,6 +9,7 @@ /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ interface FetchPieRelease { + /** @throws PiePharMissingFromLatestRelease */ public function latestReleaseMetadata(): ReleaseMetadata; /** Download the given pie.phar and return the filename (should be a temp file) */ diff --git a/src/SelfManage/Update/FetchPieReleaseFromGitHub.php b/src/SelfManage/Update/FetchPieReleaseFromGitHub.php index e56ce9ed..efba02ad 100644 --- a/src/SelfManage/Update/FetchPieReleaseFromGitHub.php +++ b/src/SelfManage/Update/FetchPieReleaseFromGitHub.php @@ -12,6 +12,7 @@ use function array_filter; use function array_map; +use function count; use function file_put_contents; use function reset; use function sys_get_temp_dir; @@ -34,7 +35,7 @@ public function latestReleaseMetadata(): ReleaseMetadata { $url = $this->githubApiBaseUrl . self::PIE_LATEST_RELEASE_URL; - $decodedRepsonse = $this->httpDownloader->get( + $decodedResponse = $this->httpDownloader->get( $url, [ 'retry-auth-failure' => true, @@ -45,11 +46,11 @@ public function latestReleaseMetadata(): ReleaseMetadata ], )->decodeJson(); - Assert::isArray($decodedRepsonse); - Assert::keyExists($decodedRepsonse, 'tag_name'); - Assert::stringNotEmpty($decodedRepsonse['tag_name']); - Assert::keyExists($decodedRepsonse, 'assets'); - Assert::isList($decodedRepsonse['assets']); + Assert::isArray($decodedResponse); + Assert::keyExists($decodedResponse, 'tag_name'); + Assert::stringNotEmpty($decodedResponse['tag_name']); + Assert::keyExists($decodedResponse, 'assets'); + Assert::isList($decodedResponse['assets']); $assetsNamedPiePhar = array_filter( array_map( @@ -62,16 +63,21 @@ static function (array $asset): array { return $asset; }, - $decodedRepsonse['assets'], + $decodedResponse['assets'], ), static function (array $asset): bool { return $asset['name'] === self::PIE_PHAR_NAME; }, ); + + if (! count($assetsNamedPiePhar)) { + throw PiePharMissingFromLatestRelease::fromRelease($decodedResponse['tag_name']); + } + $firstAssetNamedPiePhar = reset($assetsNamedPiePhar); return new ReleaseMetadata( - $decodedRepsonse['tag_name'], + $decodedResponse['tag_name'], $firstAssetNamedPiePhar['browser_download_url'], ); } diff --git a/src/SelfManage/Update/PiePharMissingFromLatestRelease.php b/src/SelfManage/Update/PiePharMissingFromLatestRelease.php new file mode 100644 index 00000000..20cd5ff5 --- /dev/null +++ b/src/SelfManage/Update/PiePharMissingFromLatestRelease.php @@ -0,0 +1,21 @@ +downloadUrl); } + public function testLatestReleaseNotHavingPiePharThrowsException(): void + { + $httpDownloader = $this->createMock(HttpDownloader::class); + $authHelper = $this->createMock(AuthHelper::class); + + $url = self::TEST_GITHUB_URL . '/repos/php/pie/releases/latest'; + $authHelper + ->method('addAuthenticationHeader') + ->willReturn(['Authorization: Bearer fake-token']); + $httpDownloader->expects(self::once()) + ->method('get') + ->with( + $url, + [ + 'retry-auth-failure' => true, + 'http' => [ + 'method' => 'GET', + 'header' => ['Authorization: Bearer fake-token'], + ], + ], + ) + ->willReturn( + new Response( + ['url' => $url], + 200, + [], + json_encode([ + 'tag_name' => '1.2.3', + 'assets' => [ + [ + 'name' => 'not-pie.phar', + 'browser_download_url' => self::TEST_GITHUB_URL . '/do/not/download/this', + ], + ], + ]), + ), + ); + + $fetch = new FetchPieReleaseFromGitHub(self::TEST_GITHUB_URL, $httpDownloader, $authHelper); + + $this->expectException(PiePharMissingFromLatestRelease::class); + $fetch->latestReleaseMetadata(); + } + public function testDownloadContent(): void { $url = self::TEST_GITHUB_URL . '/path/to/pie.phar';