Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add "create release" feature #11

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ Suitable for branching strategy like below (similar to git-flow):
* Then the staging branch is merged into "production" branch, which is for
production release.


The `--release` flag
--------------------

After you created a "release pull request", you may want to have the related "release" in github.
```
git-pr-release --release
```

Configuration
-------------

Expand Down Expand Up @@ -60,7 +69,15 @@ Release <%= Time.now %>
<% end -%>
```

### `pr-release.release.tag`

The `--release` feature will create a tag with format of "pull-#{pull.number}" by default.
If your release PR titles are simple enough for tags, specify `pull.title`.


Author
------

motemen <[email protected]>, original in-house version written by @hitode909.

hiroshi <[email protected]> (The `--release` feature)
237 changes: 146 additions & 91 deletions bin/git-pr-release
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,146 @@ def host_and_repository
end
end

def create_release_pr(client, repository, production_branch, staging_branch)
git :remote, 'update', 'origin'

### Fetch merged PRs

merged_feature_head_sha1s = git(
:log, '--merges', '--pretty=format:%P', "origin/#{production_branch}..origin/#{staging_branch}"
).map do |line|
main_sha1, feature_sha1 = line.chomp.split /\s+/
feature_sha1
end

merged_pull_request_numbers = git('ls-remote', 'origin', 'refs/pull/*/head').map do |line|
sha1, ref = line.chomp.split /\s+/

if merged_feature_head_sha1s.include? sha1
if %r<^refs/pull/(\d+)/head$>.match ref
pr_number = $1.to_i

if git('merge-base', sha1, "origin/#{production_branch}").first.chomp == sha1
say "##{pr_number} (#{sha1}) is already merged into #{production_branch}", :debug
else
pr_number
end
else
say "Bad pull request head ref format: #{ref}", :warn
nil
end
end
end.compact

if merged_pull_request_numbers.empty?
say 'No pull requests to be released', :error
exit 1
end

merged_prs = merged_pull_request_numbers.map do |nr|
pr = client.pull_request repository, nr
say "To be released: ##{pr.number} #{pr.title}", :notice
pr
end

### Create a release PR

say 'Searching for existing release pull requests...', :info
found_release_pr = client.pull_requests(repository).find do |pr|
pr.head.ref == staging_branch && pr.base.ref == production_branch
end
create_mode = found_release_pr.nil?

if @dry_run
pr_title, new_body = build_pr_title_and_body found_release_pr, merged_prs
pr_body = create_mode ? new_body : merge_pr_body(found_release_pr.body, new_body)

say 'Dry-run. Not updating PR', :info
say pr_title, :notice
say pr_body, :notice
dump_result_as_json( found_release_pr, merged_prs ) if @json
exit 0
end

pr_title, pr_body = nil, nil
release_pr = nil

if create_mode
created_pr = client.create_pull_request(
repository, production_branch, staging_branch, 'Preparing release pull request...', ''
)
unless created_pr
say 'Failed to create a new pull request', :error
exit 1
end
pr_title, pr_body = build_pr_title_and_body created_pr, merged_prs
release_pr = created_pr
else
pr_title, new_body = build_pr_title_and_body found_release_pr, merged_prs
pr_body = merge_pr_body(found_release_pr.body, new_body)
release_pr = found_release_pr
end

say 'Pull request body:', :debug
say pr_body, :debug

updated_pull_request = client.update_pull_request(
repository, release_pr.number, :title => pr_title, :body => pr_body
)

unless updated_pull_request
say 'Failed to update a pull request', :error
exit 1
end

say "#{create_mode ? 'Created' : 'Updated'} pull request: #{updated_pull_request.rels[:html].href}", :notice
dump_result_as_json( release_pr, merged_prs ) if @json
end

def create_release(client, repository, production_branch, number)
say 'Searching for existing release pull requests...', :info
pulls = client.pull_requests(repository, 'closed', :base => production_branch)
say "closed pulls: #{pulls.map(&:number)}", :debug
if number == :latest
pull = pulls.first
else
pull = pulls.find { |p| p.number == number.to_i }
end
unless pull
say 'No closed release pull requests found', :error
exit 1
end

releases = client.releases(repository)
if releases.any? { |release| pull.base.sha == release.target_commitish }
say 'A release is already created for the latest release pull request', :error
exit 1
end

now = Time.now
p git_config('release.tag')
tag_name = case git_config('release.tag')
when 'pull.title'
pull.title
else
"pull-#{pull.number}"
end
name = pull.title
body = <<-BODY
Release PR: ##{pull.number}

#{pull.body}
BODY
release = client.create_release(
repository,
tag_name,
:target_commitish => pull.base.sha,
:name => name,
:body => body
)
say "Created release: #{release.html_url}", :notice
end

host, repository = host_and_repository

if host
Expand All @@ -242,6 +382,9 @@ OptionParser.new do |opts|
opts.on('--json', 'Show data of target PRs in JSON format') do |v|
@json = v
end
opts.on('--release [PR Number]', 'Create a "release" from specified "PR Number" or latest release PR') do |v|
@release = v || :latest
end
end.parse!

### Set up configuration
Expand All @@ -255,96 +398,8 @@ say "Staging branch: #{staging_branch}", :debug

client = Octokit::Client.new :access_token => obtain_token!

git :remote, 'update', 'origin'

### Fetch merged PRs

merged_feature_head_sha1s = git(
:log, '--merges', '--pretty=format:%P', "origin/#{production_branch}..origin/#{staging_branch}"
).map do |line|
main_sha1, feature_sha1 = line.chomp.split /\s+/
feature_sha1
end

merged_pull_request_numbers = git('ls-remote', 'origin', 'refs/pull/*/head').map do |line|
sha1, ref = line.chomp.split /\s+/

if merged_feature_head_sha1s.include? sha1
if %r<^refs/pull/(\d+)/head$>.match ref
pr_number = $1.to_i

if git('merge-base', sha1, "origin/#{production_branch}").first.chomp == sha1
say "##{pr_number} (#{sha1}) is already merged into #{production_branch}", :debug
else
pr_number
end
else
say "Bad pull request head ref format: #{ref}", :warn
nil
end
end
end.compact

if merged_pull_request_numbers.empty?
say 'No pull requests to be released', :error
exit 1
end

merged_prs = merged_pull_request_numbers.map do |nr|
pr = client.pull_request repository, nr
say "To be released: ##{pr.number} #{pr.title}", :notice
pr
end

### Create a release PR

say 'Searching for existing release pull requests...', :info
found_release_pr = client.pull_requests(repository).find do |pr|
pr.head.ref == staging_branch && pr.base.ref == production_branch
end
create_mode = found_release_pr.nil?

if @dry_run
pr_title, new_body = build_pr_title_and_body found_release_pr, merged_prs
pr_body = create_mode ? new_body : merge_pr_body(found_release_pr.body, new_body)

say 'Dry-run. Not updating PR', :info
say pr_title, :notice
say pr_body, :notice
dump_result_as_json( found_release_pr, merged_prs ) if @json
exit 0
end

pr_title, pr_body = nil, nil
release_pr = nil

if create_mode
created_pr = client.create_pull_request(
repository, production_branch, staging_branch, 'Preparing release pull request...', ''
)
unless created_pr
say 'Failed to create a new pull request', :error
exit 1
end
pr_title, pr_body = build_pr_title_and_body created_pr, merged_prs
release_pr = created_pr
if @release
create_release(client, repository, production_branch, @release)
else
pr_title, new_body = build_pr_title_and_body found_release_pr, merged_prs
pr_body = merge_pr_body(found_release_pr.body, new_body)
release_pr = found_release_pr
create_release_pr(client, repository, production_branch, staging_branch)
end

say 'Pull request body:', :debug
say pr_body, :debug

updated_pull_request = client.update_pull_request(
repository, release_pr.number, :title => pr_title, :body => pr_body
)

unless updated_pull_request
say 'Failed to update a pull request', :error
exit 1
end

say "#{create_mode ? 'Created' : 'Updated'} pull request: #{updated_pull_request.rels[:html].href}", :notice
dump_result_as_json( release_pr, merged_prs ) if @json