From 0b99f3a1f335c074edc45eee0ea8a62737cf4e54 Mon Sep 17 00:00:00 2001 From: Phan An Date: Mon, 2 Sep 2024 12:32:08 +0200 Subject: [PATCH] feat: add a command to release Koel --- app/Console/Commands/ReleaseCommand.php | 149 ++++++++++++++++++++++++ composer.json | 4 +- composer.lock | 74 ++++++++++-- tag.sh | 30 ----- 4 files changed, 219 insertions(+), 38 deletions(-) create mode 100644 app/Console/Commands/ReleaseCommand.php delete mode 100755 tag.sh diff --git a/app/Console/Commands/ReleaseCommand.php b/app/Console/Commands/ReleaseCommand.php new file mode 100644 index 00000000..22db710d --- /dev/null +++ b/app/Console/Commands/ReleaseCommand.php @@ -0,0 +1,149 @@ +getCurrentVersion(); + + $releaseVersion = match ($this->argument('version')) { + 'patch' => (clone $this->currentVersion)->incrementPatch()->prefix(), + 'minor' => (clone $this->currentVersion)->incrementMinor()->prefix(), + 'major' => (clone $this->currentVersion)->incrementMajor()->prefix(), + null => $this->acquireReleaseVersionInteractively(), + default => self::tryParseVersion($this->argument('version')) ?? $this->acquireReleaseVersionInteractively(), + }; + + try { + $this->release($releaseVersion); + } catch (Throwable $e) { + error($e->getMessage()); + warning('Something went wrong. Double-check the working directory and maybe try again.'); + + return self::FAILURE; + } + + return self::SUCCESS; + } + + private function release(string $version): void + { + // Ensure the version is prefixed. + $version = Version::parse($version)->prefix(); + + info("Releasing version $version..."); + + File::put(base_path('.version'), $version); + + $gitCommands = [ + 'add .', + "commit -m 'chore(release): bump version to $version'", + 'push', + "tag $version", + 'tag latest -f', + 'push origin --tags -f', + 'checkout release', + 'pull', + 'merge master', + 'push', + ]; + + foreach ($gitCommands as $command) { + $this->components->task("Executing `git $command`", static fn () => self::runOkOrThrow("git $command")); + } + + info("Success! The new version $version has been tagged."); + info('Now go to https://github.com/koel/koel/releases and finish the draft release notes.'); + } + + private function acquireReleaseVersionInteractively(): string + { + $patchVersion = (clone $this->currentVersion)->incrementPatch()->prefix(); + + $suggestedVersions = [ + $patchVersion => 'Patch', + (clone $this->currentVersion)->incrementMinor()->prefix() => 'Minor', + (clone $this->currentVersion)->incrementMajor()->prefix() => 'Major', + ]; + + $options = []; + + foreach ($suggestedVersions as $version => $name) { + $options[$version] = "$name -> $version"; + } + + $options['custom'] = 'Custom'; + + $selected = select( + label: 'What are we releasing?', + options: $options, + default: $patchVersion, + ); + + if ($selected === 'custom') { + $selected = text( + label: 'Enter the version you want to release', + placeholder: $patchVersion, + required: true, + validate: static fn (string $value) => self::tryParseVersion($value) + ? null + : 'Invalid version format', + ); + } + + return Version::parse($selected)->prefix(); + } + + private static function tryParseVersion(string $version): ?string + { + try { + return Version::parse($version)->prefix(); + } catch (Throwable) { + return null; + } + } + + public function getCurrentVersion(): void + { + $this->currentVersion = new Version(File::get(base_path('.version'))); + + note('Current version: ' . $this->currentVersion->prefix()); + } + + private static function ensureCleanWorkingDirectory(): void + { + if (Process::run('git status --porcelain')->output()) { + error('Your working directly is not clean. Please commit or stash your changes before proceeding.'); + + exit(self::FAILURE); + } + } + + private static function runOkOrThrow(string $command): void + { + throw_unless(Process::forever()->run($command)->successful()); + } +} diff --git a/composer.json b/composer.json index 32e9db97..2c75e6cc 100644 --- a/composer.json +++ b/composer.json @@ -55,7 +55,9 @@ "fakerphp/faker": "^1.13", "slevomat/coding-standard": "^7.0", "laravel/tinker": "^2.9", - "larastan/larastan": "^2.9" + "larastan/larastan": "^2.9", + "phlak/semver": "^5.0", + "laravel/prompts": "^0.1.25" }, "suggest": { "ext-zip": "Allow downloading multiple songs as Zip archives" diff --git a/composer.lock b/composer.lock index 2cfa220d..6e3c103d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9da46e14c758c109ce78f30cbaa63a09", + "content-hash": "518f509cd591617642652600dccb90c6", "packages": [ { "name": "algolia/algoliasearch-client-php", @@ -2635,16 +2635,16 @@ }, { "name": "laravel/prompts", - "version": "v0.1.19", + "version": "v0.1.25", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "0ab75ac3434d9f610c5691758a6146a3d1940c18" + "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/0ab75ac3434d9f610c5691758a6146a3d1940c18", - "reference": "0ab75ac3434d9f610c5691758a6146a3d1940c18", + "url": "https://api.github.com/repos/laravel/prompts/zipball/7b4029a84c37cb2725fc7f011586e2997040bc95", + "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95", "shasum": "" }, "require": { @@ -2684,11 +2684,12 @@ "license": [ "MIT" ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.1.19" + "source": "https://github.com/laravel/prompts/tree/v0.1.25" }, - "time": "2024-04-16T14:20:35+00:00" + "time": "2024-08-12T22:06:33+00:00" }, { "name": "laravel/sanctum", @@ -10911,6 +10912,65 @@ }, "time": "2022-02-21T01:04:05+00:00" }, + { + "name": "phlak/semver", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/PHLAK/SemVer.git", + "reference": "3c4db3a7953ac414b947c183548b6a5b825da738" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHLAK/SemVer/zipball/3c4db3a7953ac414b947c183548b6a5b825da738", + "reference": "3c4db3a7953ac414b947c183548b6a5b825da738", + "shasum": "" + }, + "require": { + "php": "^8.0 || ^8.1 || ^8.2 || 8.3" + }, + "require-dev": { + "phlak/coding-standards": "^2.0", + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^9.0 || ^10.0", + "yoast/phpunit-polyfills": "^2.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/Support/helpers.php" + ], + "psr-4": { + "PHLAK\\SemVer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Kankiewicz", + "email": "Chris@ChrisKankiewicz.com" + } + ], + "description": "Semantic versioning helper library", + "support": { + "issues": "https://github.com/PHLAK/SemVer/issues", + "source": "https://github.com/PHLAK/SemVer/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PHLAK", + "type": "github" + }, + { + "url": "https://paypal.me/ChrisKankiewicz", + "type": "paypal" + } + ], + "time": "2024-08-02T16:57:17+00:00" + }, { "name": "php-mock/php-mock", "version": "2.5.0", diff --git a/tag.sh b/tag.sh deleted file mode 100755 index 4a4389dc..00000000 --- a/tag.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash - -if [[ $(git status --porcelain) ]]; then - echo 'ERROR: Please commit all changes before tagging a new version.' - exit 1 -fi - -if [ -z "$1" ]; then - echo 'ERROR: You must supply a version number.' - exit 1 -fi - -TAG=$1 -echo "$TAG" > .version - -git -c color.ui=always add . -git -c color.ui=always commit -m "chore(release): bump version to ${TAG}" -git -c color.ui=always push -git -c color.ui=always tag "$TAG" -git -c color.ui=always tag latest -f -git -c color.ui=always push --tags -f - -# Update and push the release branch so that documentation is updated -echo "Update the release branch" -git -c color.ui=always checkout release -git -c color.ui=always pull -git -c color.ui=always merge master -git -c color.ui=always push - -echo "${TAG} tagged. Now go to https://github.com/koel/koel/releases and finish the draft release."