From 16940a9175d14ff1b74d06d0f02c9acc879a63df Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Wed, 18 Sep 2019 16:39:51 +0200 Subject: [PATCH 1/7] Create "release" command --- composer.json | 3 +- composer.lock | 170 ++++++++++++++++++++++++++- src/Application.php | 2 + src/Command/GithubReleaseCommand.php | 75 ++++++++++++ 4 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 src/Command/GithubReleaseCommand.php diff --git a/composer.json b/composer.json index 459a086..014ba1c 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ "symfony/dom-crawler": "^4.1", "symfony/css-selector": "^4.1", "symfony/console": "^4.1", - "twig/twig": "^2.7.3" + "twig/twig": "^2.7.3", + "symfony/http-client": "^4.3" }, "require-dev": { "gajus/dindent": "^2.0", diff --git a/composer.lock b/composer.lock index 6690bad..f7a5f7e 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "9782b4b0d78029875484174bac915130", + "content-hash": "e7231ae15513b88eacee0ce033eba7ca", "packages": [ { "name": "doctrine/event-manager", @@ -429,6 +429,53 @@ ], "time": "2016-08-06T14:39:51+00:00" }, + { + "name": "psr/log", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2018-11-20T15:27:04+00:00" + }, { "name": "ralouphie/getallheaders", "version": "3.0.3", @@ -824,6 +871,125 @@ "homepage": "https://symfony.com", "time": "2019-08-14T12:26:46+00:00" }, + { + "name": "symfony/http-client", + "version": "v4.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "9a4fa769269ed730196a5c52c742b30600cf1e87" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/9a4fa769269ed730196a5c52c742b30600cf1e87", + "reference": "9a4fa769269ed730196a5c52c742b30600cf1e87", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/log": "^1.0", + "symfony/http-client-contracts": "^1.1.6", + "symfony/polyfill-php73": "^1.11" + }, + "provide": { + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "1.1" + }, + "require-dev": { + "nyholm/psr7": "^1.0", + "psr/http-client": "^1.0", + "symfony/http-kernel": "^4.3", + "symfony/process": "^4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony HttpClient component", + "homepage": "https://symfony.com", + "time": "2019-08-20T14:27:59+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v1.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "6005fe61a33724405d56eb5b055d5d370192a1bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/6005fe61a33724405d56eb5b055d5d370192a1bd", + "reference": "6005fe61a33724405d56eb5b055d5d370192a1bd", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "suggest": { + "symfony/http-client-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "time": "2019-08-08T10:05:21+00:00" + }, { "name": "symfony/polyfill-ctype", "version": "v1.12.0", diff --git a/src/Application.php b/src/Application.php index 5e69204..c8ebdb8 100644 --- a/src/Application.php +++ b/src/Application.php @@ -7,6 +7,7 @@ use Symfony\Component\Console\Input\InputOption; use SymfonyDocsBuilder\Command\BuildDocsCommand; use SymfonyDocsBuilder\Command\CheckUrlsCommand; +use SymfonyDocsBuilder\Command\GithubReleaseCommand; class Application { @@ -41,6 +42,7 @@ public function run(InputInterface $input): int ); $this->application->getDefinition()->addOption($inputOption); $this->application->add(new BuildDocsCommand($this->buildContext)); + $this->application->add(new GithubReleaseCommand()); return $this->application->run($input); } diff --git a/src/Command/GithubReleaseCommand.php b/src/Command/GithubReleaseCommand.php new file mode 100644 index 0000000..0701734 --- /dev/null +++ b/src/Command/GithubReleaseCommand.php @@ -0,0 +1,75 @@ +setDescription('Create a release on github with a .phar as attachment.'); + // todo valid with regex + $this->addArgument('tag', InputArgument::REQUIRED, 'Release\'s tag.'); + $this->addArgument('name', InputArgument::REQUIRED, 'Release name'); + $this->addArgument('description', InputArgument::REQUIRED, 'Release description'); + } + + protected function initialize(InputInterface $input, OutputInterface $output) + { + $this->io = new SymfonyStyle($input, $output); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $process = Process::fromShellCommandline('./bin/compile', __DIR__.'/../..'); + $process->mustRun(); + + // TODO token form .env + $client = HttpClient::create( + [ + 'headers' => [ + 'Authorization' => 'token 52a83ae437c06017d72fc9461392f02b39dc8c0f', + ], + ] + ); + + try { + $client->request( + 'POST', + 'https://api.github.com/repos/nikophil/test/releases', + [ + 'json' => [ + 'tag_name' => $input->getArgument('tag'), + 'target_commitish' => 'master', + 'name' => $input->getArgument('name'), + 'body' => $input->getArgument('description'), + 'draft' => false, + 'prerelease' => false + ] + ] + ); + } catch (ClientException $exception) { + $this->io->error('Error while trying to create release. Maybe the tag name already exists?'); + + return; + } + } +} From 8f35febfba46f93912546ba0a53ab43d1b2cd30f Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Wed, 18 Sep 2019 17:36:45 +0200 Subject: [PATCH 2/7] Split command into several functions --- src/Command/GithubReleaseCommand.php | 91 ++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 12 deletions(-) diff --git a/src/Command/GithubReleaseCommand.php b/src/Command/GithubReleaseCommand.php index 0701734..9f19c4b 100644 --- a/src/Command/GithubReleaseCommand.php +++ b/src/Command/GithubReleaseCommand.php @@ -3,13 +3,16 @@ namespace SymfonyDocsBuilder\Command; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\HttpClient\Exception\ClientException; use Symfony\Component\HttpClient\HttpClient; +use Symfony\Component\Process\Exception\ProcessFailedException; use Symfony\Component\Process\Process; +use Symfony\Contracts\HttpClient\HttpClientInterface; class GithubReleaseCommand extends Command { @@ -17,6 +20,12 @@ class GithubReleaseCommand extends Command /** @var SymfonyStyle */ private $io; + /** @var HttpClientInterface */ + private $client; + + private $tag; + private $name; + private $description; public function __construct() { @@ -28,21 +37,44 @@ protected function configure() $this->setDescription('Create a release on github with a .phar as attachment.'); // todo valid with regex $this->addArgument('tag', InputArgument::REQUIRED, 'Release\'s tag.'); - $this->addArgument('name', InputArgument::REQUIRED, 'Release name'); - $this->addArgument('description', InputArgument::REQUIRED, 'Release description'); + $this->addArgument('name', InputArgument::OPTIONAL, 'Release name', 'Symfony docs builder %s'); + $this->addArgument('description', InputArgument::OPTIONAL, 'Symfony docs builder %s'); } protected function initialize(InputInterface $input, OutputInterface $output) { $this->io = new SymfonyStyle($input, $output); + $this->client = $this->createHttpClient(); + + $tag = $input->getArgument('tag'); + if (!preg_match('/v\d+\.\d+\.\d+/', $tag)) { + throw new RuntimeException(sprintf('"%s" is not a valid tag.', $tag)); + } + + $this->tag = $tag; + $this->name = sprintf($input->getArgument('name'), $tag); + $this->description = sprintf($input->getArgument('description'), $tag); } protected function execute(InputInterface $input, OutputInterface $output) + { + $this->compilePhar(); + + $this->addAssetToRelease($this->createRelease()); + } + + /** + * @throws ProcessFailedException + */ + private function compilePhar(): void { $process = Process::fromShellCommandline('./bin/compile', __DIR__.'/../..'); $process->mustRun(); + } - // TODO token form .env + private function createHttpClient(): HttpClientInterface + { +// TODO token form .env $client = HttpClient::create( [ 'headers' => [ @@ -51,25 +83,60 @@ protected function execute(InputInterface $input, OutputInterface $output) ] ); + return $client; + } + + private function createRelease(): int + { try { - $client->request( + $response = $this->client->request( 'POST', + // todo change repo 'https://api.github.com/repos/nikophil/test/releases', [ 'json' => [ - 'tag_name' => $input->getArgument('tag'), + 'tag_name' => $this->tag, 'target_commitish' => 'master', - 'name' => $input->getArgument('name'), - 'body' => $input->getArgument('description'), - 'draft' => false, - 'prerelease' => false - ] + 'name' => $this->name, + 'description' => $this->description, + 'draft' => false, + 'prerelease' => false, + ], + ] + ); + + return (int) $response->toArray()['id']; + } catch (ClientException $exception) { + throw new RuntimeException('Error while trying to create release. Maybe the tag name already exists?', 0, $exception); + } + } + + private function addAssetToRelease(int $releaseId): void + { + try { + $this->client->request( + 'POST', + sprintf('https://uploads.github.com/repos/nikophil/test/releases/%s/assets?name=docs.phar', $releaseId), + [ + 'headers' => ['Content-Type' => 'application/octet-stream'], + 'body' => file_get_contents(__DIR__.'/../../docs.phar'), ] ); } catch (ClientException $exception) { - $this->io->error('Error while trying to create release. Maybe the tag name already exists?'); + $this->deleteRelease($releaseId); + throw new RuntimeException('Error while adding asset to release. Maybe the tag name already exists?', 0, $exception); + } + } - return; + private function deleteRelease(int $releaseId): void + { + try { + $this->client->request( + 'DELETE', + sprintf('https://api.github.com/repos/nikophil/test/releases/%s', $releaseId) + ); + } catch (ClientException $exception) { + throw new RuntimeException('Error while deleting release.', 0, $exception); } } } From 8293e7ad7e06a6e770453d7f80017c94f78e977d Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Wed, 18 Sep 2019 21:08:01 +0200 Subject: [PATCH 3/7] Load token from .env --- .env.dist | 1 + .gitignore | 1 + composer.json | 3 +- composer.lock | 59 +++++++++++++++++++++++++++- src/Command/GithubReleaseCommand.php | 50 ++++++++++++++++------- 5 files changed, 97 insertions(+), 17 deletions(-) create mode 100644 .env.dist diff --git a/.env.dist b/.env.dist new file mode 100644 index 0000000..f9dac60 --- /dev/null +++ b/.env.dist @@ -0,0 +1 @@ +GITHUB_API_TOKEN= diff --git a/.gitignore b/.gitignore index afff842..ac47f34 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /tests/_cache /var/ /docs.phar +/.env diff --git a/composer.json b/composer.json index 014ba1c..bf50f3e 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,8 @@ "symfony/css-selector": "^4.1", "symfony/console": "^4.1", "twig/twig": "^2.7.3", - "symfony/http-client": "^4.3" + "symfony/http-client": "^4.3", + "symfony/dotenv": "^4.3" }, "require-dev": { "gajus/dindent": "^2.0", diff --git a/composer.lock b/composer.lock index f7a5f7e..11a48c2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "e7231ae15513b88eacee0ce033eba7ca", + "content-hash": "dad8b60d970da1220f4fe4ce96d61412", "packages": [ { "name": "doctrine/event-manager", @@ -772,6 +772,63 @@ "homepage": "https://symfony.com", "time": "2019-08-26T08:26:39+00:00" }, + { + "name": "symfony/dotenv", + "version": "v4.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/dotenv.git", + "reference": "1785b18148a016b8f4e6a612291188d568e1f9cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/1785b18148a016b8f4e6a612291188d568e1f9cd", + "reference": "1785b18148a016b8f4e6a612291188d568e1f9cd", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "require-dev": { + "symfony/process": "~3.4|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Dotenv\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Registers environment variables from a .env file", + "homepage": "https://symfony.com", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "time": "2019-08-03T21:50:52+00:00" + }, { "name": "symfony/filesystem", "version": "v4.3.4", diff --git a/src/Command/GithubReleaseCommand.php b/src/Command/GithubReleaseCommand.php index 9f19c4b..8eb8a83 100644 --- a/src/Command/GithubReleaseCommand.php +++ b/src/Command/GithubReleaseCommand.php @@ -8,6 +8,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Dotenv\Dotenv; use Symfony\Component\HttpClient\Exception\ClientException; use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\Process\Exception\ProcessFailedException; @@ -16,6 +17,10 @@ class GithubReleaseCommand extends Command { + // todo change repo + private const GITHUB_USER = 'nikophil'; + private const GITHUB_REPO = 'test'; + protected static $defaultName = 'github:release'; /** @var SymfonyStyle */ @@ -35,7 +40,6 @@ public function __construct() protected function configure() { $this->setDescription('Create a release on github with a .phar as attachment.'); - // todo valid with regex $this->addArgument('tag', InputArgument::REQUIRED, 'Release\'s tag.'); $this->addArgument('name', InputArgument::OPTIONAL, 'Release name', 'Symfony docs builder %s'); $this->addArgument('description', InputArgument::OPTIONAL, 'Symfony docs builder %s'); @@ -43,16 +47,23 @@ protected function configure() protected function initialize(InputInterface $input, OutputInterface $output) { - $this->io = new SymfonyStyle($input, $output); - $this->client = $this->createHttpClient(); + $dotenv = new Dotenv(); + $dotenv->load(__DIR__.'/../../.env'); + + if (empty($_ENV['GITHUB_API_TOKEN'])) { + throw new RuntimeException('Please fill "GITHUB_API_TOKEN" in file "[PROJECT_DIR]/.env"'); + } + + $this->io = new SymfonyStyle($input, $output); + $this->client = $this->createHttpClient($_ENV['GITHUB_API_TOKEN']); $tag = $input->getArgument('tag'); - if (!preg_match('/v\d+\.\d+\.\d+/', $tag)) { + if (!preg_match('/^v\d+\.\d+\.\d+$/', $tag)) { throw new RuntimeException(sprintf('"%s" is not a valid tag.', $tag)); } - $this->tag = $tag; - $this->name = sprintf($input->getArgument('name'), $tag); + $this->tag = $tag; + $this->name = sprintf($input->getArgument('name'), $tag); $this->description = sprintf($input->getArgument('description'), $tag); } @@ -72,13 +83,12 @@ private function compilePhar(): void $process->mustRun(); } - private function createHttpClient(): HttpClientInterface + private function createHttpClient(string $githubToken): HttpClientInterface { -// TODO token form .env $client = HttpClient::create( [ 'headers' => [ - 'Authorization' => 'token 52a83ae437c06017d72fc9461392f02b39dc8c0f', + 'Authorization' => sprintf('token %s', $githubToken), ], ] ); @@ -91,8 +101,7 @@ private function createRelease(): int try { $response = $this->client->request( 'POST', - // todo change repo - 'https://api.github.com/repos/nikophil/test/releases', + sprintf('https://api.github.com/repos/%s/%s/releases', self::GITHUB_USER, self::GITHUB_REPO), [ 'json' => [ 'tag_name' => $this->tag, @@ -107,7 +116,13 @@ private function createRelease(): int return (int) $response->toArray()['id']; } catch (ClientException $exception) { - throw new RuntimeException('Error while trying to create release. Maybe the tag name already exists?', 0, $exception); + if (401 === $exception->getCode()) { + $message = 'Invalid token'; + } else { + $message = 'Maybe the tag name already exists?'; + } + + throw new RuntimeException(sprintf('Error while trying to create release: %s.', $message), 0, $exception); } } @@ -116,7 +131,12 @@ private function addAssetToRelease(int $releaseId): void try { $this->client->request( 'POST', - sprintf('https://uploads.github.com/repos/nikophil/test/releases/%s/assets?name=docs.phar', $releaseId), + sprintf( + 'https://uploads.github.com/repos/%s/%s/releases/%s/assets?name=docs.phar', + self::GITHUB_USER, + self::GITHUB_REPO, + $releaseId + ), [ 'headers' => ['Content-Type' => 'application/octet-stream'], 'body' => file_get_contents(__DIR__.'/../../docs.phar'), @@ -124,7 +144,7 @@ private function addAssetToRelease(int $releaseId): void ); } catch (ClientException $exception) { $this->deleteRelease($releaseId); - throw new RuntimeException('Error while adding asset to release. Maybe the tag name already exists?', 0, $exception); + throw new RuntimeException('Error while adding asset to release.', 0, $exception); } } @@ -133,7 +153,7 @@ private function deleteRelease(int $releaseId): void try { $this->client->request( 'DELETE', - sprintf('https://api.github.com/repos/nikophil/test/releases/%s', $releaseId) + sprintf('https://api.github.com/repos/%s/%s/releases/%s', self::GITHUB_USER, self::GITHUB_REPO, $releaseId) ); } catch (ClientException $exception) { throw new RuntimeException('Error while deleting release.', 0, $exception); From ea46247fd4648b6854a4e5a6a031a0b8795be1a9 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Wed, 18 Sep 2019 22:42:17 +0200 Subject: [PATCH 4/7] Use flat php file to create release instead of a symfony command --- bin/create_release | 25 ++++ composer.json | 6 +- composer.lock | 116 +++++++-------- src/Command/GithubReleaseCommand.php | 162 --------------------- src/Phar/Compiler.php | 2 +- src/Release/GithubApiHttpClientFactory.php | 37 +++++ src/Release/Releaser.php | 127 ++++++++++++++++ 7 files changed, 251 insertions(+), 224 deletions(-) create mode 100755 bin/create_release delete mode 100644 src/Command/GithubReleaseCommand.php create mode 100644 src/Release/GithubApiHttpClientFactory.php create mode 100644 src/Release/Releaser.php diff --git a/bin/create_release b/bin/create_release new file mode 100755 index 0000000..6d10f98 --- /dev/null +++ b/bin/create_release @@ -0,0 +1,25 @@ +#!/usr/bin/env php +doRelease(...$argv); +} catch (Exception $e) { + echo 'Failed to create a new release: ['.get_class($e).'] '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine()."\n"; + exit(1); +} diff --git a/composer.json b/composer.json index bf50f3e..2111e33 100644 --- a/composer.json +++ b/composer.json @@ -20,12 +20,12 @@ "symfony/css-selector": "^4.1", "symfony/console": "^4.1", "twig/twig": "^2.7.3", - "symfony/http-client": "^4.3", - "symfony/dotenv": "^4.3" + "symfony/http-client": "^4.3" }, "require-dev": { "gajus/dindent": "^2.0", "symfony/phpunit-bridge": "^4.1", - "symfony/process": "^4.2" + "symfony/process": "^4.2", + "symfony/dotenv": "^4.3" } } diff --git a/composer.lock b/composer.lock index 11a48c2..4cb0478 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "dad8b60d970da1220f4fe4ce96d61412", + "content-hash": "e3c1dde6a88639effbdefc0cf424f2f3", "packages": [ { "name": "doctrine/event-manager", @@ -772,63 +772,6 @@ "homepage": "https://symfony.com", "time": "2019-08-26T08:26:39+00:00" }, - { - "name": "symfony/dotenv", - "version": "v4.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/dotenv.git", - "reference": "1785b18148a016b8f4e6a612291188d568e1f9cd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/1785b18148a016b8f4e6a612291188d568e1f9cd", - "reference": "1785b18148a016b8f4e6a612291188d568e1f9cd", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "require-dev": { - "symfony/process": "~3.4|~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Dotenv\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Registers environment variables from a .env file", - "homepage": "https://symfony.com", - "keywords": [ - "dotenv", - "env", - "environment" - ], - "time": "2019-08-03T21:50:52+00:00" - }, { "name": "symfony/filesystem", "version": "v4.3.4", @@ -1394,6 +1337,63 @@ ], "time": "2014-10-08T10:03:04+00:00" }, + { + "name": "symfony/dotenv", + "version": "v4.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/dotenv.git", + "reference": "1785b18148a016b8f4e6a612291188d568e1f9cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/1785b18148a016b8f4e6a612291188d568e1f9cd", + "reference": "1785b18148a016b8f4e6a612291188d568e1f9cd", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "require-dev": { + "symfony/process": "~3.4|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Dotenv\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Registers environment variables from a .env file", + "homepage": "https://symfony.com", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "time": "2019-08-03T21:50:52+00:00" + }, { "name": "symfony/phpunit-bridge", "version": "v4.3.4", diff --git a/src/Command/GithubReleaseCommand.php b/src/Command/GithubReleaseCommand.php deleted file mode 100644 index 8eb8a83..0000000 --- a/src/Command/GithubReleaseCommand.php +++ /dev/null @@ -1,162 +0,0 @@ -setDescription('Create a release on github with a .phar as attachment.'); - $this->addArgument('tag', InputArgument::REQUIRED, 'Release\'s tag.'); - $this->addArgument('name', InputArgument::OPTIONAL, 'Release name', 'Symfony docs builder %s'); - $this->addArgument('description', InputArgument::OPTIONAL, 'Symfony docs builder %s'); - } - - protected function initialize(InputInterface $input, OutputInterface $output) - { - $dotenv = new Dotenv(); - $dotenv->load(__DIR__.'/../../.env'); - - if (empty($_ENV['GITHUB_API_TOKEN'])) { - throw new RuntimeException('Please fill "GITHUB_API_TOKEN" in file "[PROJECT_DIR]/.env"'); - } - - $this->io = new SymfonyStyle($input, $output); - $this->client = $this->createHttpClient($_ENV['GITHUB_API_TOKEN']); - - $tag = $input->getArgument('tag'); - if (!preg_match('/^v\d+\.\d+\.\d+$/', $tag)) { - throw new RuntimeException(sprintf('"%s" is not a valid tag.', $tag)); - } - - $this->tag = $tag; - $this->name = sprintf($input->getArgument('name'), $tag); - $this->description = sprintf($input->getArgument('description'), $tag); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->compilePhar(); - - $this->addAssetToRelease($this->createRelease()); - } - - /** - * @throws ProcessFailedException - */ - private function compilePhar(): void - { - $process = Process::fromShellCommandline('./bin/compile', __DIR__.'/../..'); - $process->mustRun(); - } - - private function createHttpClient(string $githubToken): HttpClientInterface - { - $client = HttpClient::create( - [ - 'headers' => [ - 'Authorization' => sprintf('token %s', $githubToken), - ], - ] - ); - - return $client; - } - - private function createRelease(): int - { - try { - $response = $this->client->request( - 'POST', - sprintf('https://api.github.com/repos/%s/%s/releases', self::GITHUB_USER, self::GITHUB_REPO), - [ - 'json' => [ - 'tag_name' => $this->tag, - 'target_commitish' => 'master', - 'name' => $this->name, - 'description' => $this->description, - 'draft' => false, - 'prerelease' => false, - ], - ] - ); - - return (int) $response->toArray()['id']; - } catch (ClientException $exception) { - if (401 === $exception->getCode()) { - $message = 'Invalid token'; - } else { - $message = 'Maybe the tag name already exists?'; - } - - throw new RuntimeException(sprintf('Error while trying to create release: %s.', $message), 0, $exception); - } - } - - private function addAssetToRelease(int $releaseId): void - { - try { - $this->client->request( - 'POST', - sprintf( - 'https://uploads.github.com/repos/%s/%s/releases/%s/assets?name=docs.phar', - self::GITHUB_USER, - self::GITHUB_REPO, - $releaseId - ), - [ - 'headers' => ['Content-Type' => 'application/octet-stream'], - 'body' => file_get_contents(__DIR__.'/../../docs.phar'), - ] - ); - } catch (ClientException $exception) { - $this->deleteRelease($releaseId); - throw new RuntimeException('Error while adding asset to release.', 0, $exception); - } - } - - private function deleteRelease(int $releaseId): void - { - try { - $this->client->request( - 'DELETE', - sprintf('https://api.github.com/repos/%s/%s/releases/%s', self::GITHUB_USER, self::GITHUB_REPO, $releaseId) - ); - } catch (ClientException $exception) { - throw new RuntimeException('Error while deleting release.', 0, $exception); - } - } -} diff --git a/src/Phar/Compiler.php b/src/Phar/Compiler.php index 525af7e..a55cbba 100644 --- a/src/Phar/Compiler.php +++ b/src/Phar/Compiler.php @@ -59,7 +59,7 @@ public function compile($pharFile = 'docs.phar') $finder->files() ->ignoreVCS(true) ->name('*.php') - ->notName('Compiler.php') + ->exclude(['Release', 'Phar']) ->in(__DIR__.'/..') ; foreach ($finder as $file) { diff --git a/src/Release/GithubApiHttpClientFactory.php b/src/Release/GithubApiHttpClientFactory.php new file mode 100644 index 0000000..44b434e --- /dev/null +++ b/src/Release/GithubApiHttpClientFactory.php @@ -0,0 +1,37 @@ +load(__DIR__.'/../../.env'); + + if (empty($_SERVER['GITHUB_API_TOKEN'])) { + throw new \RuntimeException('Please fill "GITHUB_API_TOKEN" in file "[PROJECT_DIR]/.env"'); + } + + $this->githubApiToken = $_SERVER['GITHUB_API_TOKEN']; + } + + public function createHttpClient(): HttpClientInterface + { + $client = HttpClient::create( + [ + 'headers' => [ + 'Authorization' => sprintf('token %s', $this->githubApiToken), + ], + ] + ); + + return $client; + } +} diff --git a/src/Release/Releaser.php b/src/Release/Releaser.php new file mode 100644 index 0000000..7cdc898 --- /dev/null +++ b/src/Release/Releaser.php @@ -0,0 +1,127 @@ +client = $githubApiHttpClientFactory->createHttpClient(); + } + + public function doRelease(string $tag, string $name = 'Symfony docs builder %s', string $description = 'Symfony docs builder %s') + { + if (!preg_match('/^v\d+\.\d+\.\d+$/', $tag)) { + throw new \RuntimeException(sprintf('"%s" is not a valid tag.', $tag)); + } + + $this->compilePhar(); + + $this->addAssetToRelease($releaseId = $this->createDraftRelease($tag, $name, $description)); + + $this->publishRelease($releaseId); + } + + /** + * @throws ProcessFailedException + */ + private function compilePhar(): void + { + $process = Process::fromShellCommandline('./bin/compile', __DIR__.'/../..'); + $process->mustRun(); + } + + private function createDraftRelease(string $tag, string $name, string $description): int + { + try { + $response = $this->client->request( + 'POST', + sprintf('https://api.github.com/repos/%s/%s/releases', self::GITHUB_USER, self::GITHUB_REPO), + [ + 'json' => [ + 'tag_name' => $tag, + 'target_commitish' => 'master', + 'name' => sprintf($name, $tag), + 'description' => sprintf($description, $tag), + 'draft' => true, + 'prerelease' => false, + ], + ] + ); + + return (int) $response->toArray()['id']; + } catch (ClientException $exception) { + if (401 === $exception->getCode()) { + $message = 'Invalid token'; + } else { + $message = 'Maybe the tag name already exists?'; + } + + throw new \RuntimeException(sprintf('Error while trying to create release: %s.', $message), 0, $exception); + } + } + + private function addAssetToRelease(int $releaseId): void + { + try { + $this->client->request( + 'POST', + sprintf( + 'https://uploads.github.com/repos/%s/%s/releases/%s/assets?name=docs.phar', + self::GITHUB_USER, + self::GITHUB_REPO, + $releaseId + ), + [ + 'headers' => ['Content-Type' => 'application/octet-stream'], + 'body' => file_get_contents(__DIR__.'/../../docs.phar'), + ] + ); + } catch (ClientException $exception) { + $this->deleteRelease($releaseId); + throw new \RuntimeException('Error while adding asset to release.', 0, $exception); + } + } + + private function publishRelease(int $releaseId): void + { + try { + $this->client->request( + 'PATCH', + sprintf('https://api.github.com/repos/%s/%s/releases/%s', self::GITHUB_USER, self::GITHUB_REPO, $releaseId), + [ + 'json' => [ + 'draft' => false, + ], + ] + ); + } catch (ClientException $exception) { + $this->deleteRelease($releaseId); + throw new \RuntimeException('Error while publishing release.', 0, $exception); + } + } + + private function deleteRelease(int $releaseId): void + { + try { + $this->client->request( + 'DELETE', + sprintf('https://api.github.com/repos/%s/%s/releases/%s', self::GITHUB_USER, self::GITHUB_REPO, $releaseId) + ); + } catch (ClientException $exception) { + throw new \RuntimeException('Error while deleting release.', 0, $exception); + } + } +} From b832e621a1c06d7d4f1b76b245899a47f0e8bccb Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Thu, 19 Sep 2019 00:33:36 +0200 Subject: [PATCH 5/7] test releaser --- bin/create_release | 5 +- src/Release/Releaser.php | 40 ++++++-------- tests/Release/ReleaserTest.php | 97 ++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 26 deletions(-) create mode 100644 tests/Release/ReleaserTest.php diff --git a/bin/create_release b/bin/create_release index 6d10f98..01c8bf2 100755 --- a/bin/create_release +++ b/bin/create_release @@ -3,6 +3,7 @@ require __DIR__.'/../vendor/autoload.php'; +use SymfonyDocsBuilder\Phar\Compiler; use SymfonyDocsBuilder\Release\GithubApiHttpClientFactory; use SymfonyDocsBuilder\Release\Releaser; @@ -10,7 +11,7 @@ error_reporting(-1); ini_set('display_errors', 1); try { - $releaser = new Releaser(new GithubApiHttpClientFactory()); + $releaser = new Releaser((new GithubApiHttpClientFactory())->createHttpClient(), new Compiler()); if ($argc === 1) { throw new RuntimeException('Not enough arguments. usage: "./bin/release tag [release_name [release_description]]"'); @@ -18,7 +19,7 @@ try { array_shift($argv); - $releaser->doRelease(...$argv); + $releaser->createRelease(...$argv); } catch (Exception $e) { echo 'Failed to create a new release: ['.get_class($e).'] '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine()."\n"; exit(1); diff --git a/src/Release/Releaser.php b/src/Release/Releaser.php index 7cdc898..28a04a4 100644 --- a/src/Release/Releaser.php +++ b/src/Release/Releaser.php @@ -2,10 +2,8 @@ namespace SymfonyDocsBuilder\Release; -use Symfony\Component\HttpClient\Exception\ClientException; -use Symfony\Component\Process\Exception\ProcessFailedException; -use Symfony\Component\Process\Process; use Symfony\Contracts\HttpClient\HttpClientInterface; +use SymfonyDocsBuilder\Phar\Compiler; class Releaser { @@ -15,34 +13,27 @@ class Releaser /** @var HttpClientInterface */ private $client; + private $compiler; - public function __construct(GithubApiHttpClientFactory $githubApiHttpClientFactory) + public function __construct(HttpClientInterface $client, Compiler $compiler) { - $this->client = $githubApiHttpClientFactory->createHttpClient(); + $this->client = $client; + $this->compiler = $compiler; } - public function doRelease(string $tag, string $name = 'Symfony docs builder %s', string $description = 'Symfony docs builder %s') + public function createRelease(string $tag, string $name = 'Symfony docs builder %s', string $description = 'Symfony docs builder %s') { if (!preg_match('/^v\d+\.\d+\.\d+$/', $tag)) { throw new \RuntimeException(sprintf('"%s" is not a valid tag.', $tag)); } - $this->compilePhar(); + $this->compiler->compile(); $this->addAssetToRelease($releaseId = $this->createDraftRelease($tag, $name, $description)); $this->publishRelease($releaseId); } - /** - * @throws ProcessFailedException - */ - private function compilePhar(): void - { - $process = Process::fromShellCommandline('./bin/compile', __DIR__.'/../..'); - $process->mustRun(); - } - private function createDraftRelease(string $tag, string $name, string $description): int { try { @@ -62,14 +53,15 @@ private function createDraftRelease(string $tag, string $name, string $descripti ); return (int) $response->toArray()['id']; - } catch (ClientException $exception) { + } catch (\RuntimeException $exception) { if (401 === $exception->getCode()) { - $message = 'Invalid token'; + $message = 'Error while trying to create release: Invalid token.'; } else { - $message = 'Maybe the tag name already exists?'; + $message = 'Error while trying to create release.'; } - throw new \RuntimeException(sprintf('Error while trying to create release: %s.', $message), 0, $exception); + // todo: create new exception which can be exploited in ./bin/create_release + throw new \RuntimeException($message, 0, $exception); } } @@ -89,7 +81,7 @@ private function addAssetToRelease(int $releaseId): void 'body' => file_get_contents(__DIR__.'/../../docs.phar'), ] ); - } catch (ClientException $exception) { + } catch (\RuntimeException $exception) { $this->deleteRelease($releaseId); throw new \RuntimeException('Error while adding asset to release.', 0, $exception); } @@ -107,9 +99,9 @@ private function publishRelease(int $releaseId): void ], ] ); - } catch (ClientException $exception) { + } catch (\RuntimeException $exception) { $this->deleteRelease($releaseId); - throw new \RuntimeException('Error while publishing release.', 0, $exception); + throw new \RuntimeException('Error while publishing release. Maybe the tag name already exists?', 0, $exception); } } @@ -120,7 +112,7 @@ private function deleteRelease(int $releaseId): void 'DELETE', sprintf('https://api.github.com/repos/%s/%s/releases/%s', self::GITHUB_USER, self::GITHUB_REPO, $releaseId) ); - } catch (ClientException $exception) { + } catch (\RuntimeException $exception) { throw new \RuntimeException('Error while deleting release.', 0, $exception); } } diff --git a/tests/Release/ReleaserTest.php b/tests/Release/ReleaserTest.php new file mode 100644 index 0000000..4be525e --- /dev/null +++ b/tests/Release/ReleaserTest.php @@ -0,0 +1,97 @@ +createMock(Compiler::class)); + + $compiler->expects($this->never())->method('compile'); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('"invalid tag" is not a valid tag.'); + + $releaser->createRelease('invalid tag'); + } + + public function testCreateRelease(): void + { + $callback = static function ($method, $url) { + switch (true) { + case 'POST' === $method + && 'https://api.github.com/repos/nikophil/test/releases' === $url: + return new MockResponse('{"id":1}'); + case 'POST' === $method + && 'https://uploads.github.com/repos/nikophil/test/releases/1/assets?name=docs.phar' === $url: + case 'PATCH' === $method + && 'https://api.github.com/repos/nikophil/test/releases/1' === $url: + return new MockResponse(); + } + + self::fail(sprintf("Unexpected request:\n- method: %s\n- url: %s", $method, $url)); + }; + + $releaser = new Releaser(new MockHttpClient($callback), $compiler = $this->createMock(Compiler::class)); + + $compiler->expects($this->once())->method('compile'); + + $releaser->createRelease('v1.0.0'); + } + + public function testCreateReleaseThrowExceptionIf401IsReturned(): void + { + $releaser = new Releaser(new MockHttpClient([new MockResponse('', ['http_code' => 401])]), $this->createMock(Compiler::class)); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Error while trying to create release: Invalid token.'); + + $releaser->createRelease('v1.0.0'); + } + + public function testReleaseIsDeletedIfAddAssetReturnsAnError(): void + { + $callback = static function ($method, $url) { + switch (true) { + case 'POST' === $method + && 'https://api.github.com/repos/nikophil/test/releases' === $url: + return new MockResponse('{"id":1}'); + case 'POST' === $method + && 'https://uploads.github.com/repos/nikophil/test/releases/1/assets?name=docs.phar' === $url: + return new MockResponse('', ['http_code' => 500]); + case 'DELETE' === $method + && 'https://api.github.com/repos/nikophil/test/releases/1' === $url: + return new MockResponse(); + } + + self::fail(sprintf("Unexpected request:\n- method: %s\n- url: %s", $method, $url)); + }; + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Error while adding asset to release.'); + + $releaser = new Releaser(new MockHttpClient($callback), $this->createMock(Compiler::class)); + + $releaser->createRelease('v1.0.0'); + } +} \ No newline at end of file From dcdbcee6d8b96a3378150c63770c0155b5ed662e Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Thu, 19 Sep 2019 16:22:34 +0200 Subject: [PATCH 6/7] create specific exceptions for release process --- bin/create_release | 8 +++- src/Release/Exception/DeleteReleaseFailed.php | 29 ++++++++++++ src/Release/Exception/ReleaseFailed.php | 44 +++++++++++++++++++ src/Release/Releaser.php | 33 ++++++-------- tests/Release/ReleaserTest.php | 5 ++- 5 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 src/Release/Exception/DeleteReleaseFailed.php create mode 100644 src/Release/Exception/ReleaseFailed.php diff --git a/bin/create_release b/bin/create_release index 01c8bf2..1cbe66c 100755 --- a/bin/create_release +++ b/bin/create_release @@ -4,6 +4,7 @@ require __DIR__.'/../vendor/autoload.php'; use SymfonyDocsBuilder\Phar\Compiler; +use SymfonyDocsBuilder\Release\Exception\ReleaseFailed; use SymfonyDocsBuilder\Release\GithubApiHttpClientFactory; use SymfonyDocsBuilder\Release\Releaser; @@ -21,6 +22,11 @@ try { $releaser->createRelease(...$argv); } catch (Exception $e) { - echo 'Failed to create a new release: ['.get_class($e).'] '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine()."\n"; + if ($e instanceof ReleaseFailed) { + echo $e->toString(); + } else { + echo 'Failed to create a new release: ['.get_class($e).'] '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine()."\n"; + } + exit(1); } diff --git a/src/Release/Exception/DeleteReleaseFailed.php b/src/Release/Exception/DeleteReleaseFailed.php new file mode 100644 index 0000000..37cc75d --- /dev/null +++ b/src/Release/Exception/DeleteReleaseFailed.php @@ -0,0 +1,29 @@ +originException = $originException; + + parent::__construct('Error while deleting release.', 0, $previous); + } + + public function toString(): string + { + return sprintf( + "%s\n\nOriginal exception was: [%s]\n\t\"%s\"\n\tat %s:%s\n", + parent::toString(), + get_class($this->originException), + $this->originException->getMessage(), + $this->originException->getFile(), + $this->originException->getLine() + ); + } +} diff --git a/src/Release/Exception/ReleaseFailed.php b/src/Release/Exception/ReleaseFailed.php new file mode 100644 index 0000000..1a59430 --- /dev/null +++ b/src/Release/Exception/ReleaseFailed.php @@ -0,0 +1,44 @@ +getCode()) { + $message = 'Error while trying to create release: Invalid token.'; + } else { + $message = 'Error while trying to create release.'; + } + + return new self($message, 0, $previous); + } + + public static function whileAttachingAssetToRelease(HttpExceptionInterface $previous): self + { + return new self('Error while adding asset to release.', 0, $previous); + } + + public static function whilePublishingRelease(HttpExceptionInterface $previous): self + { + return new self('Error while publishing release. Maybe the tag name already exists?', 0, $previous); + } + + public function toString(): string + { + return sprintf( + "Failed to create a new release: [%s]\n\t\"%s\"\n\tat %s:%s\n\nHttpException was: [%s]\n\t\"%s\"\n\tat %s:%s\n", + self::class, + $this->getMessage(), + $this->getFile(), + $this->getLine(), + get_class($this->getPrevious()), + $this->getPrevious()->getMessage(), + $this->getPrevious()->getFile(), + $this->getPrevious()->getLine() + ); + } +} diff --git a/src/Release/Releaser.php b/src/Release/Releaser.php index 28a04a4..a90c63f 100644 --- a/src/Release/Releaser.php +++ b/src/Release/Releaser.php @@ -2,8 +2,10 @@ namespace SymfonyDocsBuilder\Release; +use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; use SymfonyDocsBuilder\Phar\Compiler; +use SymfonyDocsBuilder\Release\Exception\ReleaseFailed; class Releaser { @@ -21,7 +23,7 @@ public function __construct(HttpClientInterface $client, Compiler $compiler) $this->compiler = $compiler; } - public function createRelease(string $tag, string $name = 'Symfony docs builder %s', string $description = 'Symfony docs builder %s') + public function createRelease(string $tag, string $name = 'Symfony docs builder %s', string $description = 'Symfony docs builder %s'): void { if (!preg_match('/^v\d+\.\d+\.\d+$/', $tag)) { throw new \RuntimeException(sprintf('"%s" is not a valid tag.', $tag)); @@ -53,15 +55,8 @@ private function createDraftRelease(string $tag, string $name, string $descripti ); return (int) $response->toArray()['id']; - } catch (\RuntimeException $exception) { - if (401 === $exception->getCode()) { - $message = 'Error while trying to create release: Invalid token.'; - } else { - $message = 'Error while trying to create release.'; - } - - // todo: create new exception which can be exploited in ./bin/create_release - throw new \RuntimeException($message, 0, $exception); + } catch (HttpExceptionInterface $exception) { + throw ReleaseFailed::whileCreatingDraft($exception); } } @@ -81,9 +76,8 @@ private function addAssetToRelease(int $releaseId): void 'body' => file_get_contents(__DIR__.'/../../docs.phar'), ] ); - } catch (\RuntimeException $exception) { - $this->deleteRelease($releaseId); - throw new \RuntimeException('Error while adding asset to release.', 0, $exception); + } catch (HttpExceptionInterface $exception) { + $this->deleteRelease($releaseId, ReleaseFailed::whileAttachingAssetToRelease($exception)); } } @@ -99,21 +93,22 @@ private function publishRelease(int $releaseId): void ], ] ); - } catch (\RuntimeException $exception) { - $this->deleteRelease($releaseId); - throw new \RuntimeException('Error while publishing release. Maybe the tag name already exists?', 0, $exception); + } catch (HttpExceptionInterface $exception) { + $this->deleteRelease($releaseId, ReleaseFailed::whilePublishingRelease($exception)); } } - private function deleteRelease(int $releaseId): void + private function deleteRelease(int $releaseId, ReleaseFailed $previous): void { try { $this->client->request( 'DELETE', sprintf('https://api.github.com/repos/%s/%s/releases/%s', self::GITHUB_USER, self::GITHUB_REPO, $releaseId) ); - } catch (\RuntimeException $exception) { - throw new \RuntimeException('Error while deleting release.', 0, $exception); + } catch (HttpExceptionInterface $exception) { + throw new DeleteReleaseFailed($previous, $exception); } + + throw $previous; } } diff --git a/tests/Release/ReleaserTest.php b/tests/Release/ReleaserTest.php index 4be525e..68a3525 100644 --- a/tests/Release/ReleaserTest.php +++ b/tests/Release/ReleaserTest.php @@ -6,6 +6,7 @@ use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\Response\MockResponse; use SymfonyDocsBuilder\Phar\Compiler; +use SymfonyDocsBuilder\Release\Exception\ReleaseFailed; use SymfonyDocsBuilder\Release\Releaser; class ReleaserTest extends TestCase @@ -63,7 +64,7 @@ public function testCreateReleaseThrowExceptionIf401IsReturned(): void { $releaser = new Releaser(new MockHttpClient([new MockResponse('', ['http_code' => 401])]), $this->createMock(Compiler::class)); - $this->expectException(\RuntimeException::class); + $this->expectException(ReleaseFailed::class); $this->expectExceptionMessage('Error while trying to create release: Invalid token.'); $releaser->createRelease('v1.0.0'); @@ -87,7 +88,7 @@ public function testReleaseIsDeletedIfAddAssetReturnsAnError(): void self::fail(sprintf("Unexpected request:\n- method: %s\n- url: %s", $method, $url)); }; - $this->expectException(\RuntimeException::class); + $this->expectException(ReleaseFailed::class); $this->expectExceptionMessage('Error while adding asset to release.'); $releaser = new Releaser(new MockHttpClient($callback), $this->createMock(Compiler::class)); From cbe535d5d1dd034a54ea818c9617c81802a4f8f5 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Thu, 19 Sep 2019 16:32:46 +0200 Subject: [PATCH 7/7] Change repo name --- src/Release/Releaser.php | 5 ++--- tests/Release/ReleaserTest.php | 12 ++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Release/Releaser.php b/src/Release/Releaser.php index a90c63f..bc9779b 100644 --- a/src/Release/Releaser.php +++ b/src/Release/Releaser.php @@ -9,9 +9,8 @@ class Releaser { - // todo change repo - private const GITHUB_USER = 'nikophil'; - private const GITHUB_REPO = 'test'; + private const GITHUB_USER = 'weaverryan'; + private const GITHUB_REPO = 'docs-builder'; /** @var HttpClientInterface */ private $client; diff --git a/tests/Release/ReleaserTest.php b/tests/Release/ReleaserTest.php index 68a3525..212c51e 100644 --- a/tests/Release/ReleaserTest.php +++ b/tests/Release/ReleaserTest.php @@ -41,12 +41,12 @@ public function testCreateRelease(): void $callback = static function ($method, $url) { switch (true) { case 'POST' === $method - && 'https://api.github.com/repos/nikophil/test/releases' === $url: + && 'https://api.github.com/repos/weaverryan/docs-builder/releases' === $url: return new MockResponse('{"id":1}'); case 'POST' === $method - && 'https://uploads.github.com/repos/nikophil/test/releases/1/assets?name=docs.phar' === $url: + && 'https://uploads.github.com/repos/weaverryan/docs-builder/releases/1/assets?name=docs.phar' === $url: case 'PATCH' === $method - && 'https://api.github.com/repos/nikophil/test/releases/1' === $url: + && 'https://api.github.com/repos/weaverryan/docs-builder/releases/1' === $url: return new MockResponse(); } @@ -75,13 +75,13 @@ public function testReleaseIsDeletedIfAddAssetReturnsAnError(): void $callback = static function ($method, $url) { switch (true) { case 'POST' === $method - && 'https://api.github.com/repos/nikophil/test/releases' === $url: + && 'https://api.github.com/repos/weaverryan/docs-builder/releases' === $url: return new MockResponse('{"id":1}'); case 'POST' === $method - && 'https://uploads.github.com/repos/nikophil/test/releases/1/assets?name=docs.phar' === $url: + && 'https://uploads.github.com/repos/weaverryan/docs-builder/releases/1/assets?name=docs.phar' === $url: return new MockResponse('', ['http_code' => 500]); case 'DELETE' === $method - && 'https://api.github.com/repos/nikophil/test/releases/1' === $url: + && 'https://api.github.com/repos/weaverryan/docs-builder/releases/1' === $url: return new MockResponse(); }