diff --git a/README.md b/README.md index c8b3e3e..aad3309 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,31 @@ ## Release Note Generator -This is a Laravel package that generates markdown formatted release notes between two branches/tags. +This is a command line tool that generates markdown formatted release notes between two branches/tags. ## Installation -Install the package via composer: +Install the package globally via composer: ``` sh -composer require foodkit/jira-release-notes +composer require foodkit/jira-release-notes --global ``` -Register the service provider: +## Configuration -``` -// config/app.php - -'providers' => [ - // ... - FoodKit\ReleaseNote\Provider\ReleaseNoteServiceProvider::class, -]; +The following configuration parameters can be passed as argument: -``` +* `--host` issue tracker host (https://project.atlassian.net) +* `--user` issue tracker username +* `--pass` issue tracker password +* `--regex` issue prefix regular expression -To publish the config file to `config/release-notes.php` run: +Or, they can be placed in `.env` file within a project: -``` sh -php artisan vendor:publish --provider="FoodKit\ReleaseNote\Provider\ReleaseNoteServiceProvider" ``` - -Next add the `FoodKit\ReleaseNote\Commands\GenerateReleaseNote` class to your console kernel. - -```text-html-php -// app/Console/Kernel.php - -protected $commands = [ - ... - \FoodKit\ReleaseNote\Commands\GenerateReleaseNote::class, -] +JIRA_USERNAME=user +JIRA_PASSWORD=secret +JIRA_URL=https://ginjath.atlassian.net +JIRA_ISSUE_REGEX=/GT-[\d]+/ ``` ## Usage @@ -44,11 +33,11 @@ protected $commands = [ This command will generate the release notes between two tags. ``` sh -php artisan release-note:generate --start=v2.7.8 --end=v2.8.0 +release-notes generate --start=v2.7.8 --end=v2.8.0 ``` -This will generate the release notes between two branches. +This will generate the release notes between two branches. ``` sh -php artisan release-note:generate --start=develop --end=master +release-notes generate --start=develop --end=master ``` \ No newline at end of file diff --git a/bin/release-notes b/bin/release-notes new file mode 100644 index 0000000..e92650d --- /dev/null +++ b/bin/release-notes @@ -0,0 +1,16 @@ +#!/usr/bin/env php +add($command); +$application->run(); \ No newline at end of file diff --git a/composer.json b/composer.json index fa86534..f0c9d5c 100644 --- a/composer.json +++ b/composer.json @@ -13,13 +13,17 @@ "email": "phpfour@gmail.com" } ], + "bin": [ + "bin/release-notes" + ], "autoload": { "psr-4": { "FoodKit\\ReleaseNote\\": "src" } }, "require": { - "illuminate/console": "5.2.*", - "guzzlehttp/guzzle": "6.2.*" + "guzzlehttp/guzzle": "6.2.*", + "vlucas/phpdotenv": "^2.4", + "symfony/console": "2.8.*|3.0.*" } } diff --git a/composer.lock b/composer.lock index 394e228..e3f9454 100644 --- a/composer.lock +++ b/composer.lock @@ -4,75 +4,8 @@ "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": "daad2968cc6cf168190d88edf72595b5", + "content-hash": "a5139513a6abc2daeccb0ad441e0c28c", "packages": [ - { - "name": "doctrine/inflector", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/inflector.git", - "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", - "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "phpunit/phpunit": "4.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\Inflector\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Common String Manipulations with regard to casing and singular/plural rules.", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "inflection", - "pluralize", - "singularize", - "string" - ], - "time": "2015-11-06T14:35:42+00:00" - }, { "name": "guzzlehttp/guzzle", "version": "6.2.3", @@ -251,259 +184,6 @@ ], "time": "2017-03-20T17:10:46+00:00" }, - { - "name": "illuminate/console", - "version": "v5.2.45", - "source": { - "type": "git", - "url": "https://github.com/illuminate/console.git", - "reference": "c6d838c6f9ac3f1aec28cde93bf613283153785e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/console/zipball/c6d838c6f9ac3f1aec28cde93bf613283153785e", - "reference": "c6d838c6f9ac3f1aec28cde93bf613283153785e", - "shasum": "" - }, - "require": { - "illuminate/contracts": "5.2.*", - "illuminate/support": "5.2.*", - "nesbot/carbon": "~1.20", - "php": ">=5.5.9", - "symfony/console": "2.8.*|3.0.*" - }, - "suggest": { - "guzzlehttp/guzzle": "Required to use the ping methods on schedules (~5.3|~6.0).", - "mtdowling/cron-expression": "Required to use scheduling component (~1.0).", - "symfony/process": "Required to use scheduling component (2.8.*|3.0.*)." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.2-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Console\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Console package.", - "homepage": "http://laravel.com", - "time": "2016-08-01T13:49:14+00:00" - }, - { - "name": "illuminate/contracts", - "version": "v5.2.45", - "source": { - "type": "git", - "url": "https://github.com/illuminate/contracts.git", - "reference": "22bde7b048a33c702d9737fc1446234fff9b1363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/22bde7b048a33c702d9737fc1446234fff9b1363", - "reference": "22bde7b048a33c702d9737fc1446234fff9b1363", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.2-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Contracts\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Contracts package.", - "homepage": "http://laravel.com", - "time": "2016-08-08T11:46:08+00:00" - }, - { - "name": "illuminate/support", - "version": "v5.2.45", - "source": { - "type": "git", - "url": "https://github.com/illuminate/support.git", - "reference": "510230ab62a7d85dc70203f4fdca6fb71a19e08a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/510230ab62a7d85dc70203f4fdca6fb71a19e08a", - "reference": "510230ab62a7d85dc70203f4fdca6fb71a19e08a", - "shasum": "" - }, - "require": { - "doctrine/inflector": "~1.0", - "ext-mbstring": "*", - "illuminate/contracts": "5.2.*", - "paragonie/random_compat": "~1.4", - "php": ">=5.5.9" - }, - "replace": { - "tightenco/collect": "self.version" - }, - "suggest": { - "illuminate/filesystem": "Required to use the composer class (5.2.*).", - "jeremeamia/superclosure": "Required to be able to serialize closures (~2.2).", - "symfony/polyfill-php56": "Required to use the hash_equals function on PHP 5.5 (~1.0).", - "symfony/process": "Required to use the composer class (2.8.*|3.0.*).", - "symfony/var-dumper": "Improves the dd function (2.8.*|3.0.*)." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.2-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Support\\": "" - }, - "files": [ - "helpers.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Support package.", - "homepage": "http://laravel.com", - "time": "2016-08-05T14:49:58+00:00" - }, - { - "name": "nesbot/carbon", - "version": "1.22.1", - "source": { - "type": "git", - "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc", - "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "symfony/translation": "~2.6 || ~3.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~2", - "phpunit/phpunit": "~4.0 || ~5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.23-dev" - } - }, - "autoload": { - "psr-4": { - "Carbon\\": "src/Carbon/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Brian Nesbitt", - "email": "brian@nesbot.com", - "homepage": "http://nesbot.com" - } - ], - "description": "A simple API extension for DateTime.", - "homepage": "http://carbon.nesbot.com", - "keywords": [ - "date", - "datetime", - "time" - ], - "time": "2017-01-16T07:55:07+00:00" - }, - { - "name": "paragonie/random_compat", - "version": "v1.4.2", - "source": { - "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "965cdeb01fdcab7653253aa81d40441d261f1e66" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/965cdeb01fdcab7653253aa81d40441d261f1e66", - "reference": "965cdeb01fdcab7653253aa81d40441d261f1e66", - "shasum": "" - }, - "require": { - "php": ">=5.2.0" - }, - "require-dev": { - "phpunit/phpunit": "4.*|5.*" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - }, - "type": "library", - "autoload": { - "files": [ - "lib/random.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "pseudorandom", - "random" - ], - "time": "2017-03-13T16:22:52+00:00" - }, { "name": "psr/http-message", "version": "1.0.1", @@ -674,69 +354,54 @@ "time": "2017-06-09T14:24:12+00:00" }, { - "name": "symfony/translation", - "version": "v3.3.5", + "name": "vlucas/phpdotenv", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "35dd5fb003c90e8bd4d8cabdf94bf9c96d06fdc3" + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/35dd5fb003c90e8bd4d8cabdf94bf9c96d06fdc3", - "reference": "35dd5fb003c90e8bd4d8cabdf94bf9c96d06fdc3", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", + "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", "shasum": "" }, "require": { - "php": ">=5.5.9", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/config": "<2.8", - "symfony/yaml": "<3.3" + "php": ">=5.3.9" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0", - "symfony/intl": "^2.8.18|^3.2.5", - "symfony/yaml": "~3.3" - }, - "suggest": { - "psr/log": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" + "phpunit/phpunit": "^4.8 || ^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "2.4-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Dotenv\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause-Attribution" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "http://www.vancelucas.com" } ], - "description": "Symfony Translation Component", - "homepage": "https://symfony.com", - "time": "2017-06-24T16:45:30+00:00" + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "time": "2016-09-01T10:05:43+00:00" } ], "packages-dev": [], diff --git a/config/release-notes.php b/config/release-notes.php deleted file mode 100644 index 13cae80..0000000 --- a/config/release-notes.php +++ /dev/null @@ -1,13 +0,0 @@ - [ - 'type' => 'jira', - 'host' => env('JIRA_URL', 'https://project.atlassian.net'), - 'username' => env('JIRA_USERNAME', 'user'), - 'password' => env('JIRA_PASSWORD', 'secret'), - 'regex' => env('JIRA_ISSUE_REGEX', '/GT-[\d]+/'), - ], - -]; \ No newline at end of file diff --git a/src/Commands/GenerateReleaseNote.php b/src/Commands/GenerateReleaseNote.php deleted file mode 100644 index 662590f..0000000 --- a/src/Commands/GenerateReleaseNote.php +++ /dev/null @@ -1,70 +0,0 @@ -input->getOption('start')) { - $start = $this->ask('Start Tag/Branch:'); - } - - if (!$end = $this->input->getOption('end')) { - $end = $this->ask('End Tag/Branch:'); - } - - $config = config("release-notes.issue_tracker"); - - /** @var IssueTrackerInterface $tracker */ - $issueTracker = IssueTrackerFactory::create($config['type'], $config); - - /** @var ReleaseNoteGenerator $generator */ - $generator = new ReleaseNoteGenerator($issueTracker); - - $this->output->writeln($generator->generate($start, $end)); - } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments() - { - return []; - } - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions() - { - return [ - ['start', null, InputOption::VALUE_OPTIONAL, 'The start tag/branch'], - ['end', null, InputOption::VALUE_OPTIONAL, 'The end tag/branch'], - ]; - } -} diff --git a/src/Commands/GenerateReleaseNoteCommand.php b/src/Commands/GenerateReleaseNoteCommand.php new file mode 100644 index 0000000..7405031 --- /dev/null +++ b/src/Commands/GenerateReleaseNoteCommand.php @@ -0,0 +1,97 @@ +setName('generate') + ->setDescription('Generate a release note given two tags/branches.') + ->addOption('start', null, InputOption::VALUE_OPTIONAL, 'The start tag/branch') + ->addOption('end', null, InputOption::VALUE_OPTIONAL, 'The end tag/branch') + ->addOption('type', null, InputOption::VALUE_OPTIONAL, 'Issue tracker type', 'jira') + ->addOption('host', null, InputOption::VALUE_OPTIONAL, 'Issue tracker host (ex: https://project.atlassian.net)') + ->addOption('user', null, InputOption::VALUE_OPTIONAL, 'Issue tracker username') + ->addOption('pass', null, InputOption::VALUE_OPTIONAL, 'Issue tracker password') + ->addOption('regex', null, InputOption::VALUE_OPTIONAL, 'Issue prefix regex'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $helper = $this->getHelper('question'); + + if (!$start = $input->getOption('start')) { + $question = new Question('Start Tag/Branch: '); + $start = $helper->ask($input, $output, $question); + } + + if (!$end = $input->getOption('end')) { + $question = new Question('End Tag/Branch: '); + $end = $helper->ask($input, $output, $question); + } + + $config = $this->getConfig($input); + + /** @var IssueTrackerInterface $tracker */ + $issueTracker = IssueTrackerFactory::create($config['type'], $config); + + /** @var ReleaseNoteGenerator $generator */ + $generator = new ReleaseNoteGenerator($issueTracker); + + $output->writeln($generator->generate($start, $end)); + } + + private function getConfig(InputInterface $input) + { + $dotenv = new \Dotenv\Dotenv(getcwd()); + $dotenv->load(); + + $config = [ + 'type' => 'jira', + 'host' => getenv('JIRA_URL'), + 'username' => getenv('JIRA_USERNAME'), + 'password' => getenv('JIRA_PASSWORD'), + 'regex' => getenv('JIRA_ISSUE_REGEX'), + ]; + + if ($input->getOption('type')) { + $config['type'] = $input->getOption('type'); + } + + if ($input->getOption('host')) { + $config['host'] = $input->getOption('host'); + } + + if ($input->getOption('user')) { + $config['username'] = $input->getOption('user'); + } + + if ($input->getOption('pass')) { + $config['password'] = $input->getOption('pass'); + } + + if ($input->getOption('regex')) { + $config['regex'] = $input->getOption('regex'); + } + + foreach ($config as $key => $value) { + if (empty($value)) { + throw new RuntimeException("The required parameter '$key' is not configured."); + } + } + + return $config; + } +} diff --git a/src/Provider/ReleaseNoteServiceProvider.php b/src/Provider/ReleaseNoteServiceProvider.php deleted file mode 100644 index 373e02d..0000000 --- a/src/Provider/ReleaseNoteServiceProvider.php +++ /dev/null @@ -1,28 +0,0 @@ -publishes([ - __DIR__.'/../../config/release-notes.php' => config_path('release-notes.php'), - ], 'config'); - } - - /** - * Register the service provider. - * - * @return void - */ - public function register() - { - $this->mergeConfigFrom(__DIR__.'/../../config/release-notes.php', 'release-notes'); - } -} diff --git a/src/Service/ReleaseNoteGenerator.php b/src/Service/ReleaseNoteGenerator.php index 5128ac0..7a8eb26 100644 --- a/src/Service/ReleaseNoteGenerator.php +++ b/src/Service/ReleaseNoteGenerator.php @@ -36,6 +36,10 @@ public function generate($start, $end) } } + if (count($summaries) == 0) { + return 'No referenced issue found.'; + } + $notes = "# Release notes - $end\n\n"; foreach ($summaries as $key => $summary) { @@ -43,6 +47,30 @@ public function generate($start, $end) $notes .= "* [$key]($url) - $summary\n"; } + if ($compareUrl = $this->getCompareUrl($start, $end)) { + $notes .= "\n[Compare]($compareUrl)"; + } + return $notes; } + + private function getCompareUrl($start, $end) + { + $command = "git config --get remote.origin.url"; + $origin = trim(shell_exec($command)); + + if (strpos($origin, 'github.com') !== false) { + + if (strpos($origin, 'git@') !== false) { + $origin = str_replace('git@github.com:', 'https://github.com/', $origin); + } + + $origin = str_replace('.git', '', $origin); + $compareUrl = $origin . '/compare/' . $start . '...' . $end; + + return $compareUrl; + } + + return null; + } }