From 69c7a3f3a2c0356055c39b4dfca11746bd8c01fb Mon Sep 17 00:00:00 2001 From: James Couball Date: Mon, 8 Jan 2024 17:29:30 -0800 Subject: [PATCH] Show release PR URL and other minor changes in the output. (#54) --- exe/create-github-release | 20 +++- exe/revert-github-release | 12 ++- lib/create_github_release/project.rb | 53 +++++++++- spec/create_github_release/project_spec.rb | 110 ++++++++++++++++++++- 4 files changed, 183 insertions(+), 12 deletions(-) diff --git a/exe/create-github-release b/exe/create-github-release index 833b60c..e8fc757 100755 --- a/exe/create-github-release +++ b/exe/create-github-release @@ -13,18 +13,28 @@ puts unless options.quiet CreateGithubRelease::ReleaseTasks.new(project).run puts <<~MESSAGE unless project.quiet - Release '#{project.next_release_tag}' created successfully - See the release notes at #{project.release_url} + SUCCESS: created release '#{project.next_release_tag}' Next steps: - * Get someone to review and approve the release pull request - * Merge the pull request manually from the command line with the following commands: + + * Review the release notes: + + #{project.release_url} + + * Get someone to review and approve the release pull request: + + #{project.release_pr_url} + + * Merge the pull request manually from the command line with the following + commands: git checkout #{project.default_branch} git merge --ff-only #{project.release_branch} git push - Wait for the CI build to pass and then release the gem with the following command: + * Wait for the CI build to pass on the default branch and then release the + gem with the following command: rake release:rubygem_push + MESSAGE diff --git a/exe/revert-github-release b/exe/revert-github-release index 62fb512..b60303b 100755 --- a/exe/revert-github-release +++ b/exe/revert-github-release @@ -86,11 +86,13 @@ class Parser It must be run in the root directory of the work tree with the release branch checked out (which is the state create-github-release leaves you in). - This script should be run beefore the release PR is merged. + This script should be run before the release PR is merged. - This script removes the release branch and release tag both in the local - repository and on the remote. Deleting the branch on GitHub will close the - GitHub PR automatically. + This script does the following: + * Deletes the local and remote release branch + * Deletes the local and remote release tag + * Deletes the GitHub release object + * Closes the GitHub release PR Options: BANNER @@ -172,4 +174,4 @@ end revert_release!(options) -puts "Reverted release #{options.release_version}" +puts "SUCCESS: reverted release '#{options.release_version}'" diff --git a/lib/create_github_release/project.rb b/lib/create_github_release/project.rb index b885618..c2e88cd 100644 --- a/lib/create_github_release/project.rb +++ b/lib/create_github_release/project.rb @@ -69,7 +69,7 @@ def initialize(options) :release_type, :pre, :pre_type, :release_url, :remote, :remote_base_url, :remote_repository, :remote_url, :changelog_path, :changes, :next_release_description, :last_release_changelog, :next_release_changelog, - :first_commit, :verbose, :quiet + :first_commit, :verbose, :quiet, :release_pr_number, :release_pr_url # attr_writer :first_release @@ -324,6 +324,47 @@ def release_log_url end end + # @!attribute [rw] release_pr_number + # + # The number of the pull request created for the release or nil + # + # The PR must already exist. + # + # @example + # options = CreateGithubRelease::CommandLine::Options.new(release_type: 'major') + # project = CreateGithubRelease::Project.new(options) + # project.release_pr_number #=> '123' + # + # @return [String, nil] + # + # @api public + # + def release_pr_number + @release_pr_number ||= begin + pr_number = `gh pr list --search "head:#{release_branch}" --json number --jq ".[].number"`.chomp + pr_number.empty? ? nil : pr_number + end + end + + # @!attribute [rw] release_pr_url + # + # The url of the pull request created for the release or nil + # + # The PR must already exist. + # + # @example + # options = CreateGithubRelease::CommandLine::Options.new(release_type: 'major') + # project = CreateGithubRelease::Project.new(options) + # project.release_pr_url #=> 'https://github.com/org/repo/pull/123' + # + # @return [String] + # + # @api public + # + def release_pr_url + @release_pr_url ||= release_pr_number.nil? ? nil : URI.parse("#{remote_url}/pull/#{release_pr_number}") + end + # @!attribute [rw] release_type # # The type of the release being created (e.g. 'major', 'minor', 'patch') @@ -713,12 +754,15 @@ def next_release_changelog CreateGithubRelease::Changelog.new(last_release_changelog, next_release_description).to_s end + # rubocop:disable Metrics/AbcSize + # Show the project details as a string # # @example # options = CreateGithubRelease::CommandLine::Options.new(release_type: 'major') # project = CreateGithubRelease::Project.new(options) # puts projects.to_s + # first_release: false # default_branch: main # next_release_tag: v1.0.0 # next_release_date: 2023-02-01 @@ -733,6 +777,8 @@ def next_release_changelog # remote_base_url: https://github.com/ # remote_repository: org/repo # remote_url: https://github.com/org/repo + # release_pr_number: 123 + # release_pr_url: https://github.com/org/repo/pull/123 # changelog_path: CHANGELOG.md # verbose?: false # quiet?: false @@ -758,11 +804,16 @@ def to_s remote_base_url: #{remote_base_url} remote_repository: #{remote_repository} remote_url: #{remote_url} + release_pr_number: #{release_pr_number} + release_pr_url: #{release_pr_url} + changelog_path: #{changelog_path} verbose?: #{verbose?} quiet?: #{quiet?} OUTPUT end + # rubocop:enable Metrics/AbcSize + # @!attribute [rw] verbose # # If `true` enables verbose output diff --git a/spec/create_github_release/project_spec.rb b/spec/create_github_release/project_spec.rb index ae7602f..f01ef1f 100644 --- a/spec/create_github_release/project_spec.rb +++ b/spec/create_github_release/project_spec.rb @@ -981,6 +981,110 @@ end end + describe '#release_pr_number' do + subject { project.release_pr_number } + + let(:release_pr_number) { '123' } + let(:remote_url) { 'https://github.com/org/repo' } + let(:release_pr_url) { "#{remote_url}/pull/#{release_pr_number}" } + let(:next_release_version) { '1.0.0' } + let(:next_release_branch) { "release-v#{next_release_version}" } + + context 'when explicitly set' do + let(:project_init_block) { ->(p) { p.release_pr_number = release_pr_number } } + it { is_expected.to eq(release_pr_number) } + end + + context 'when there is no release PR' do + let(:project_init_block) { ->(p) { p.next_release_version = next_release_version } } + + let(:mocked_commands) do + [ + MockedCommand.new( + %(gh pr list --search "head:#{next_release_branch}" --json number --jq ".[].number"), + stdout: "\n" + ) + ] + end + + it 'should return nil' do + expect(subject).to be_nil + end + end + + context 'when the release PR is 123' do + let(:project_init_block) { ->(p) { p.next_release_version = '1.0.0' } } + + let(:mocked_commands) do + [ + MockedCommand.new( + 'gh pr list --search "head:release-v1.0.0" --json number --jq ".[].number"', + stdout: "#{release_pr_number}\n" + ) + ] + end + + it 'should return "123"' do + expect(subject).to eq(release_pr_number) + end + end + end + + describe '#release_pr_url' do + subject { project.release_pr_url } + + let(:release_pr_number) { '123' } + let(:remote_url) { 'https://github.com/org/repo' } + let(:release_pr_url) { URI.parse("#{remote_url}/pull/#{release_pr_number}") } + let(:next_release_version) { '1.0.0' } + let(:next_release_branch) { "release-v#{next_release_version}" } + + context 'when explicitly set' do + let(:project_init_block) { ->(p) { p.release_pr_url = release_pr_url } } + it { is_expected.to eq(release_pr_url) } + end + + context 'when there is not release PR' do + let(:project_init_block) do + lambda do |p| + p.remote_url = remote_url + p.next_release_version = next_release_version + end + end + + let(:mocked_commands) do + [ + MockedCommand.new( + %(gh pr list --search "head:#{next_release_branch}" --json number --jq ".[].number"), + stdout: "\n" + ) + ] + end + + it { is_expected.to be_nil } + end + + context 'when the release PR is 123' do + let(:project_init_block) do + lambda do |p| + p.remote_url = remote_url + p.next_release_version = next_release_version + end + end + + let(:mocked_commands) do + [ + MockedCommand.new( + %(gh pr list --search "head:#{next_release_branch}" --json number --jq ".[].number"), + stdout: "#{release_pr_number}\n" + ) + ] + end + + it { is_expected.to eq(release_pr_url) } + end + end + describe '#to_s' do subject { project.to_s } @@ -991,7 +1095,8 @@ MockedCommand.new('git show --format=format:%aI --quiet "v1.0.0"', stdout: "2023-02-01 00:00:00 -0800\n"), MockedCommand.new('semverify current', stdout: "0.1.0\n"), MockedCommand.new("git remote get-url 'origin'", stdout: "https://github.com/org/repo.git\n"), - MockedCommand.new('git tag --list "v1.0.0"', stdout: "v1.0.0\n") + MockedCommand.new('git tag --list "v1.0.0"', stdout: "v1.0.0\n"), + MockedCommand.new('gh pr list --search "head:release-v1.0.0" --json number --jq ".[].number"', stdout: "123\n") ] end @@ -1011,6 +1116,9 @@ remote_base_url: https://github.com/ remote_repository: org/repo remote_url: https://github.com/org/repo + release_pr_number: 123 + release_pr_url: https://github.com/org/repo/pull/123 + changelog_path: CHANGELOG.md verbose?: false quiet?: false EXPECTED_RESULT