├── .rspec
├── Rakefile
├── spec
├── fixtures
│ └── file
│ │ ├── template_1.erb
│ │ ├── template_2.erb
│ │ ├── pr_1_files.yml
│ │ ├── pr_1.yml
│ │ ├── pr_4.yml
│ │ ├── pr_3.yml
│ │ ├── pr_6.yml
│ │ └── pr_7.yml
├── support
│ ├── capture_support.rb
│ └── file_fixture_support.rb
├── spec_helper.rb
└── git
│ └── pr
│ ├── release
│ ├── pull_request_spec.rb
│ └── cli_spec.rb
│ └── release_spec.rb
├── Gemfile
├── .gitignore
├── exe
└── git-pr-release
├── lib
└── git
│ └── pr
│ ├── release.rb
│ └── release
│ ├── dummy_pull_request.rb
│ ├── pull_request.rb
│ ├── util.rb
│ └── cli.rb
├── .github
└── workflows
│ ├── release.yml
│ └── test.yml
├── LICENSE
├── git-pr-release.gemspec
├── CHANGELOG.md
└── README.md
/.rspec:
--------------------------------------------------------------------------------
1 | --require spec_helper
2 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'bundler/gem_tasks'
2 |
--------------------------------------------------------------------------------
/spec/fixtures/file/template_1.erb:
--------------------------------------------------------------------------------
1 | a
2 | b
3 |
--------------------------------------------------------------------------------
/spec/fixtures/file/template_2.erb:
--------------------------------------------------------------------------------
1 | c
2 | d
3 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gemspec
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | *.gem
3 | Gemfile.lock
4 | /.rspec_status
5 |
--------------------------------------------------------------------------------
/exe/git-pr-release:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "git/pr/release"
4 |
5 | Git::Pr::Release::CLI.start
6 |
--------------------------------------------------------------------------------
/lib/git/pr/release.rb:
--------------------------------------------------------------------------------
1 | require "git/pr/release/util"
2 | require "git/pr/release/pull_request"
3 | require "git/pr/release/dummy_pull_request"
4 | require "git/pr/release/cli"
5 |
6 | module Git
7 | module Pr
8 | module Release
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/support/capture_support.rb:
--------------------------------------------------------------------------------
1 | module CaptureSupport
2 | def capture(stream)
3 | begin
4 | eval "$#{stream} = StringIO.new"
5 | yield
6 | result = eval("$#{stream}").string
7 | ensure
8 | eval("$#{stream} = #{stream.upcase}")
9 | end
10 |
11 | result
12 | end
13 | end
14 |
15 | RSpec.configure do |config|
16 | config.include CaptureSupport
17 | end
18 |
--------------------------------------------------------------------------------
/lib/git/pr/release/dummy_pull_request.rb:
--------------------------------------------------------------------------------
1 | module Git
2 | module Pr
3 | module Release
4 | class DummyPullRequest
5 | def initialize
6 | # nop
7 | end
8 |
9 | def to_checklist_item
10 | "- [ ] #??? THIS IS DUMMY PULL REQUEST"
11 | end
12 |
13 | def html_link
14 | 'http://github.com/DUMMY/DUMMY/issues/?'
15 | end
16 |
17 | def to_hash
18 | { :data => {} }
19 | end
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release new version to RubyGems.org
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | release:
8 | runs-on: ubuntu-slim
9 |
10 | permissions:
11 | contents: write
12 | id-token: write
13 |
14 | steps:
15 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
16 | with:
17 | persist-credentials: false
18 |
19 | - uses: ruby/setup-ruby@ac793fdd38cc468a4dd57246fa9d0e868aba9085 # v1.270.0
20 | with:
21 | bundler-cache: true
22 | ruby-version: ruby
23 |
24 | - uses: rubygems/release-gem@1c162a739e8b4cb21a676e97b087e8268d8fc40b # v1.1.2
25 |
--------------------------------------------------------------------------------
/spec/support/file_fixture_support.rb:
--------------------------------------------------------------------------------
1 | module FileFixtureSupport
2 | def load_yaml(fixture_name)
3 | if YAML.respond_to?(:unsafe_load_file)
4 | YAML.unsafe_load_file(file_fixture(fixture_name))
5 | else
6 | YAML.load_file(file_fixture(fixture_name))
7 | end
8 | end
9 |
10 | def file_fixture(fixture_name)
11 | file_fixture_path = RSpec.configuration.file_fixture_path
12 | path = Pathname.new(File.join(file_fixture_path, fixture_name))
13 |
14 | if path.exist?
15 | path
16 | else
17 | msg = "the directory '%s' does not contain a file named '%s'"
18 | raise ArgumentError, msg % [file_fixture_path, fixture_name]
19 | end
20 | end
21 | end
22 |
23 | RSpec.configure do |config|
24 | config.add_setting :file_fixture_path
25 | config.file_fixture_path = File.expand_path("../fixtures/file", __dir__)
26 | config.include FileFixtureSupport
27 | end
28 |
--------------------------------------------------------------------------------
/spec/fixtures/file/pr_1_files.yml:
--------------------------------------------------------------------------------
1 | ---
2 | :sha: 2bcc5ec915521df50825ad08118665c66ad072be
3 | :filename: bin/git-pr-release
4 | :status: modified
5 | :additions: 1
6 | :deletions: 1
7 | :changes: 2
8 | :blob_url: https://github.com/motemen/git-pr-release/blob/bbcd2a04ef394e91be44c24e93e52fdbca944060/bin/git-pr-release
9 | :raw_url: https://github.com/motemen/git-pr-release/raw/bbcd2a04ef394e91be44c24e93e52fdbca944060/bin/git-pr-release
10 | :contents_url: https://api.github.com/repos/motemen/git-pr-release/contents/bin/git-pr-release?ref=bbcd2a04ef394e91be44c24e93e52fdbca944060
11 | :patch: "@@ -87,7 +87,7 @@ def build_pr_title_and_body(prs)\n \n template = DEFAULT_PR_TEMPLATE\n
12 | \n- if path = git_config('pr-release.template')\n+ if path = git_config('template')\n
13 | \ template_path = File.join(git('rev-parse', '--show-toplevel').first.chomp,
14 | path)\n template = File.read(template_path)\n end"
15 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require "timecop"
2 | require "yaml"
3 | require "git/pr/release"
4 | require "webmock/rspec"
5 |
6 | RSpec.configure do |config|
7 | config.expect_with :rspec do |expectations|
8 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true
9 | end
10 | config.mock_with :rspec do |mocks|
11 | mocks.verify_partial_doubles = true
12 | end
13 | config.shared_context_metadata_behavior = :apply_to_host_groups
14 |
15 | config.filter_run_when_matching :focus
16 | config.example_status_persistence_file_path = ".rspec_status"
17 | config.disable_monkey_patching!
18 | config.warnings = true
19 | config.order = :random
20 | Kernel.srand config.seed
21 |
22 | config.around(:each) do |example|
23 | begin
24 | ENV['TZ'], old = 'Asia/Tokyo', ENV['TZ']
25 | example.run
26 | ensure
27 | ENV['TZ'] = old
28 | end
29 | end
30 |
31 | config.after do
32 | Timecop.return
33 | end
34 |
35 | # Trigger Autoload
36 | Octokit::Client
37 | end
38 |
39 | Dir[File.expand_path("support/**/*.rb", __dir__)].each {|f| require f }
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 motemen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/git-pr-release.gemspec:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | lib = File.expand_path('../lib', __FILE__)
3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4 |
5 | Gem::Specification.new do |spec|
6 | spec.name = "git-pr-release"
7 | spec.version = '2.5.0'
8 | spec.authors = ["motemen"]
9 | spec.email = ["motemen@gmail.com"]
10 | spec.summary = 'Creates a release pull request'
11 | spec.description = 'git-pr-release creates a pull request which summarizes feature branches that are to be released into production'
12 | spec.homepage = 'https://github.com/x-motemen/git-pr-release'
13 |
14 | spec.files = `git ls-files`.split($/)
15 | spec.bindir = "exe"
16 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
17 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18 | spec.require_paths = ["lib"]
19 |
20 | spec.required_ruby_version = '>= 3.0'
21 |
22 | spec.add_dependency 'octokit'
23 | spec.add_dependency 'highline'
24 | spec.add_dependency 'colorize'
25 | spec.add_dependency 'diff-lcs'
26 |
27 | spec.add_development_dependency 'rake'
28 | spec.add_development_dependency 'rspec'
29 | spec.add_development_dependency 'timecop'
30 | spec.add_development_dependency 'webmock'
31 |
32 | spec.license = 'MIT'
33 | end
34 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | schedule:
9 | - cron: "0 0 * * *" # JST 09:00
10 |
11 | jobs:
12 | test:
13 | runs-on: ubuntu-latest
14 | strategy:
15 | fail-fast: false
16 | matrix:
17 | ruby:
18 | - "3.0"
19 | - "3.1"
20 | - "3.2"
21 | - "3.3"
22 | - "3.4"
23 | - head
24 |
25 | steps:
26 | - uses: actions/checkout@v4
27 | - uses: ruby/setup-ruby@v1
28 | with:
29 | ruby-version: ${{ matrix.ruby }}
30 | bundler-cache: true
31 | - name: Cache vendor/bundle
32 | uses: actions/cache@v4
33 | with:
34 | path: vendor/bundle
35 | key: v1-gem-${{ runner.os }}-${{ matrix.ruby }}-${{ github.sha }}
36 | restore-keys: |
37 | v1-gem-${{ runner.os }}-${{ matrix.ruby }}-
38 | continue-on-error: ${{ matrix.allow_failures == 'true' }}
39 | - name: bundle update
40 | run: |
41 | set -xe
42 | bundle config path vendor/bundle
43 | bundle update --jobs $(nproc) --retry 3
44 | continue-on-error: ${{ matrix.allow_failures == 'true' }}
45 | - name: Run ${{ matrix.test_framework }}
46 | run: |
47 | set -xe
48 | bundle exec rspec
49 | continue-on-error: ${{ matrix.allow_failures == 'true' }}
50 |
--------------------------------------------------------------------------------
/spec/git/pr/release/pull_request_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec.describe Git::Pr::Release::PullRequest do
4 | let(:pr_data) {
5 | agent = Sawyer::Agent.new "http://foo.com/a/" do |conn|
6 | conn.builder.handlers.delete(Faraday::Adapter::NetHttp)
7 | conn.adapter :test, Faraday::Adapter::Test::Stubs.new
8 | end
9 | Sawyer::Resource.new(agent, load_yaml(pr_data_yaml))
10 | }
11 | subject { described_class.new(pr_data) }
12 |
13 | describe "#mention" do
14 | context "with multiple assignees" do
15 | let(:pr_data_yaml) { "pr_7.yml" }
16 |
17 | it "returns all assignees as mentions" do
18 | expect(subject.mention).to eq " @hakobe @toshimaru @Copilot"
19 | end
20 | end
21 |
22 | context "with no assignees" do
23 | let(:pr_data_yaml) { "pr_3.yml" }
24 |
25 | it "returns PR creator as mention" do
26 | expect(subject.mention).to eq " @hakobe"
27 | end
28 | end
29 |
30 | context "when mention_type is 'author'" do
31 | let(:pr_data_yaml) { "pr_7.yml" }
32 |
33 | before do
34 | allow(Git::Pr::Release::PullRequest).to receive(:mention_type).and_return('author')
35 | end
36 |
37 | it "returns PR author regardless of assignees" do
38 | expect(subject.mention).to eq " @hakobe"
39 | end
40 | end
41 | end
42 |
43 | describe "#to_checklist_item" do
44 | context "with multiple assignees" do
45 | let(:pr_data_yaml) { "pr_7.yml" }
46 |
47 | it "includes all assignees in the checklist item" do
48 | expect(subject.to_checklist_item).to eq "- [ ] #7 @hakobe @toshimaru @Copilot"
49 | end
50 |
51 | it "includes title when print_title is true" do
52 | expect(subject.to_checklist_item(true)).to eq "- [ ] #7 Support multiple assignees @hakobe @toshimaru @Copilot"
53 | end
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/lib/git/pr/release/pull_request.rb:
--------------------------------------------------------------------------------
1 | module Git
2 | module Pr
3 | module Release
4 | class PullRequest
5 | include Git::Pr::Release::Util
6 | extend Git::Pr::Release::Util
7 | attr_reader :pr
8 |
9 | def initialize(pr)
10 | @pr = pr
11 | end
12 |
13 | def to_checklist_item(print_title = false)
14 | if print_title
15 | "- [ ] ##{pr.number} #{pr.title}" + mention
16 | else
17 | "- [ ] ##{pr.number}" + mention
18 | end
19 | end
20 |
21 | def html_link
22 | pr.rels[:html].href
23 | end
24 |
25 | def to_hash
26 | { :data => @pr.to_hash }
27 | end
28 |
29 | def mention
30 | mention = target_user_login_names.map { |login_name| "@#{login_name}" }.join(" ")
31 | mention.empty? ? "" : " #{mention}"
32 | end
33 |
34 | def target_user_login_names
35 | case PullRequest.mention_type
36 | when 'author'
37 | pr.user ? [pr.user.login] : []
38 | else
39 | if pr.assignees&.any? && pr.assignees.length > 1
40 | pr.assignees.map(&:login)
41 | elsif pr.assignee
42 | [pr.assignee.login]
43 | elsif pr.user
44 | [pr.user.login]
45 | else
46 | []
47 | end
48 | end
49 | end
50 |
51 | def self.mention_type
52 | @mention_type ||= (ENV.fetch('GIT_PR_RELEASE_MENTION') { git_config('mention') } || 'default')
53 | end
54 |
55 | def method_missing(name, *args, &block)
56 | @pr.public_send name, *args, &block
57 | end
58 |
59 | def respond_to_missing?(name, include_private = false)
60 | @pr.respond_to?(name, include_private)
61 | end
62 | end
63 | end
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # git-pr-release
2 |
3 | ## v2.5.0 (2025-12-16)
4 |
5 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v2.4.0...v2.5.0)
6 |
7 | * (#116) Make it backward compatible for Ruby 3.0 (@moznion)
8 |
9 | ## v2.4.0 (2025-12-16)
10 |
11 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v2.3.0...v2.4.0)
12 |
13 | * (#112) Add release workflow using trusted publishing (@ohbarye)
14 | * (#110) Support `pr-release.assign-pr-author` and `pr-release.request-pr-author-review` options (@moznion)
15 |
16 | ## v2.3.0 (2025-12-15)
17 |
18 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v2.2.0...v2.3.0)
19 |
20 | * (#108) Support multiple PR assignees (@toshimaru)
21 | * (#107) Update CI matrix (@onk)
22 | * (#106) Use setup-ruby instead of container-based setup (@onk)
23 | * (#100) Allow big bang release PR (@secobaka)
24 | * (#95) Bump actions/cache from v2 to v4 (@ytkg)
25 | * (#94) Bump actions/checkout from v2 to v4 (@ytkg)
26 | * (#91) Test against Ruby v3.1 and Ruby v3.2 (@toshimaru)
27 | * (#90) updated docs to include --squashed flag (@petercorrea)
28 |
29 | ## v2.2.0 (2022-08-17)
30 |
31 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v2.1.2...v2.2.0)
32 |
33 | * (#88) unshallow if a shallow repository (@Songmu)
34 | * (#89) Add overwrite-description option (@onk)
35 |
36 | ## v2.1.2 (2022-07-29)
37 |
38 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v2.1.1...v2.1.2)
39 |
40 | * (#87) delegate to `@pr` when `method_missing` in PullRequest (@Songmu)
41 |
42 | ## v2.1.1 (2022-03-09)
43 |
44 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v2.1.0...v2.1.1)
45 |
46 | * (#81) fix forbidden git config name (#80) (@mtgto)
47 |
48 | ## v2.1.0 (2022-03-03)
49 |
50 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v2.0.0...v2.1.0)
51 |
52 | * (#75) reduce GitHub search API calls when the squashed option is specified (@Songmu)
53 | * (#76) use bulk issue search to reduce API calls (@Songmu)
54 | * (#77) Add option "ssl_no_verify" to skip verifying ssl certificate (@mtgto)
55 | * (#78) add an argument to to_checklist_item to print pr title (@mtgto)
56 |
57 | ## v2.0.0 (2022-02-17)
58 |
59 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v1.9.0...v2.0.0)
60 |
61 | * (#69) remove duplicated PR entries at squash (@Yuki-Inoue)
62 | * (#70) [Spec] Fix spec for build_pr_title_and_body (@yutailang0119)
63 | * (#71) Introduce CI (@ohbarye)
64 | * (#73) (#74) Use `YAML.unsafe_load_file` instead of `YAML.load_file` (@ohbarye)
65 |
66 | ## v1.9.0 (2021-08-04)
67 |
68 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v1.8.0...v1.9.0)
69 |
70 | * (#68) Add nil check for release\_pr.body (@w1mvy)
71 |
72 | ## v1.8.0 (2021-06-24)
73 |
74 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v1.7.0...v1.8.0)
75 |
76 | * (#66) Exclude titles from checklist items (@nhosoya)
77 |
78 | ## v1.7.0 (2021-05-24)
79 |
80 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v1.6.0...v1.7.0)
81 |
82 | * (#64) fix wrong pr number due to sleep (@mpon)
83 |
84 | ## v1.6.0 (2021-05-15)
85 |
86 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v1.5.0...v1.6.0)
87 |
88 | * (#63) Sort merged_pull_request_numbers numerically by default (@yutailang0119)
89 |
90 | ## v1.5.0 (2021-04-02)
91 |
92 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v1.4.0...v1.5.0)
93 |
94 | * (#60, #61) Get issue number from GitHub API for squashed PR (@yuuan)
95 | * (#58) Make stable test (@kachick)
96 | * (#55) Suppress warning for ERB (@ohbarye)
97 | * (#50) support `GIT_PR_RELEASE_MENTION` environment variable (@dabutvin)
98 | * (#49) Transfer repository to x-motemen organization (@onk)
99 |
100 | ## v1.4.0 (2020-02-22)
101 |
102 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v1.3.0...v1.4.0)
103 |
104 | * (#48) List PR API needs head user or head organization and branch name (@sasasin)
105 |
106 | ## v1.3.0 (2020-02-19)
107 |
108 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v1.2.0...v1.3.0)
109 |
110 | * (#47) Fix Errno::ENOENT when finding the specified template (@onk)
111 | * (#45) Fix "warning: instance variable @xxx not initialized" (@onk)
112 |
113 | ## v1.2.0 (2020-02-07)
114 |
115 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v1.1.0...v1.2.0)
116 |
117 | * (#44) Use API option when detecting existing release PR (@onk)
118 | * (#41, #42) Refactor (@onk)
119 | - Some local variables are removed. This will break if you have customized the template ERB.
120 |
121 | ## v1.1.0 (2020-01-02)
122 |
123 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v1.0.1...v1.1.0)
124 |
125 | * (#38) Fetch changed files as many as possible (@shibayu36)
126 |
127 | ## v1.0.1 (2019-12-17)
128 |
129 | [full changelog](https://github.com/x-motemen/git-pr-release/compare/v1.0.0...v1.0.1)
130 |
131 | * (#37) Fix NameError (@onk)
132 |
133 | ## v1.0.0 (2019-12-14)
134 |
135 | * (#35) Do not define classes and methods at the top level (@onk)
136 | * (#30) Extract logic from bin/git-pr-release (@banyan)
137 |
138 | ...
139 |
140 | ## v0.0.1 (2014-01-21)
141 |
142 | Initial Release
143 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # git-pr-release
2 |
3 | Creates a "release pull request", whose body consists of features list or
4 | pull requests that are to be released into production. It's especially useful for QA and
5 | pre-release checks. `git-pr-release` automatically collect pull requests
6 | merged into master branch and generates the content of the release
7 | pull request.
8 |
9 | 
10 |
11 | Suitable for branching strategy like below (similar to git-flow):
12 |
13 | - Feature branches are first merged into "staging" (or release, development)
14 | branch.
15 | - Then the staging branch is merged into "production" branch, which is for
16 | production release.
17 |
18 | ## Usage
19 |
20 | `git-pr-release`: default
21 |
22 | `git-pr-release --squashed`: include PRs containing squashed commits.
23 |
24 | `git-pr-release --overwrite-description`: generate a fresh PR description.
25 |
26 | `git-pr-release -n` | `git-pr-release --dry-run`: perform a dry run; does not update PR.
27 |
28 | `git-pr-release --no-fetch`: Do not fetch from remote repo before determining target PRs (CI friendly).
29 |
30 | `git-pr-release --json`: Show data of target PRs in JSON format.
31 |
32 | ## Configuration
33 |
34 | All configuration are taken using `git config`. You can write these variables
35 | in file `.git-pr-release` (instead of `.git/config` or `~/.gitconfig`) to share project-wise configuration to other
36 | collaborators.
37 |
38 | ### `pr-release.token`
39 |
40 | Token for GitHub API.
41 |
42 | If not set, you will be asked to input username/password for one time only,
43 | and this configuration variable will be stored.
44 |
45 | You can specify this value by `GIT_PR_RELEASE_TOKEN` environment variable.
46 |
47 | ### `pr-release.branch.production`
48 |
49 | The branch name that is deployed in production environment.
50 |
51 | You can specify this value by `GIT_PR_RELEASE_BRANCH_PRODUCTION` environment variable.
52 |
53 | Default value: `master`.
54 |
55 | ### `pr-release.branch.staging`
56 |
57 | The branch name that the feature branches are merged into and is going to be
58 | merged into the "production" branch.
59 |
60 | You can specify this value by `GIT_PR_RELEASE_BRANCH_STAGING` environment variable.
61 |
62 | Default value: `staging`.
63 |
64 | ### `pr-release.template`
65 |
66 | The template file path (relative to the workidir top) for pull requests
67 | created. Its first line is used for the PR title, the rest for the body. This
68 | is an ERB template.
69 |
70 | You can specify this value by `GIT_PR_RELEASE_TEMPLATE` environment variable.
71 |
72 | If not specified, the content below is used as the template (embedded in the code):
73 |
74 | ```erb
75 | Release <%= Time.now %>
76 | <% pull_requests.each do |pr| -%>
77 | <%= pr.to_checklist_item %>
78 | <% end -%>
79 | ```
80 |
81 | ### `pr-release.labels`
82 |
83 | The labels list for adding to pull requests created.
84 | This value should be comma-separated strings.
85 |
86 | You can specify this value by `GIT_PR_RELEASE_LABELS` environment variable.
87 |
88 | If not specified, any labels will not be added for PRs.
89 |
90 | ### `pr-release.mention`
91 |
92 | The name that is listed next to each PR title.
93 | Accepted values: `author`
94 |
95 | You can specify this value by `GIT_PR_RELEASE_MENTION` environment variable.
96 |
97 | If not specified, the mention will be the PR assignee
98 |
99 | ### `pr-release.assign-pr-author`
100 |
101 | Whether to assign the related users of the merged pull requests to the release pull request.
102 | Accepted values: `true` | `false`
103 |
104 | You can specify this value by `GIT_PR_RELEASE_ASSIGN_PR_AUTHOR` environment variable.
105 |
106 | If not specified, no assignees will be added.
107 |
108 | Note: The user selection follows the same logic as `pr-release.mention`. If `mention` is set to `author`, the PR author will be assigned. Otherwise, the PR assignee will be used (falling back to the author if no assignee exists).
109 |
110 | ### `pr-release.request-pr-author-review`
111 |
112 | Whether to request review from the related users of the merged pull requests on the release pull request.
113 | Accepted values: `true` | `false`
114 |
115 | You can specify this value by `GIT_PR_RELEASE_REQUEST_PR_AUTHOR_REVIEW` environment variable.
116 |
117 | If not specified, no review requests will be made.
118 |
119 | Note: The user selection follows the same logic as `pr-release.mention`. If `mention` is set to `author`, the PR author will be requested for review. Otherwise, the PR assignee will be used (falling back to the author if no assignee exists).
120 |
121 | ### `pr-release.ssl-no-verify`
122 |
123 | Whether to verify SSL certificate or not.
124 | Accepted values: `true` | `false`
125 |
126 | This option might be useful when self-hosted GitHub enterprise server is using self-signed certificate.
127 |
128 | You can specify this value by `GIT_PR_RELEASE_SSL_NO_VERIFY` to `1`.
129 |
130 | If not specified, verify SSL certificate always.
131 |
132 | ## Errors and exit statuses
133 |
134 | ### No pull requests to be released
135 |
136 | exit status is 1.
137 |
138 | ## Author
139 |
140 | motemen , original in-house version written by @hitode909.
141 |
--------------------------------------------------------------------------------
/spec/git/pr/release_spec.rb:
--------------------------------------------------------------------------------
1 | RSpec.describe Git::Pr::Release do
2 | include Git::Pr::Release::Util
3 | before do
4 | Timecop.freeze(Time.parse("2019-02-20 22:58:35"))
5 |
6 | @stubs = Faraday::Adapter::Test::Stubs.new
7 | @agent = Sawyer::Agent.new "http://foo.com/a/" do |conn|
8 | conn.builder.handlers.delete(Faraday::Adapter::NetHttp)
9 | conn.adapter :test, @stubs
10 | end
11 | @release_pr = Sawyer::Resource.new(@agent, load_yaml("pr_1.yml"))
12 | @merged_prs = [
13 | Sawyer::Resource.new(@agent, load_yaml("pr_3.yml")),
14 | Sawyer::Resource.new(@agent, load_yaml("pr_6.yml")),
15 | ]
16 | @changed_files = [
17 | Sawyer::Resource.new(@agent, load_yaml("pr_1_files.yml")),
18 | ]
19 | end
20 |
21 | describe "#build_pr_title_and_body" do
22 | context "without template_path" do
23 | it {
24 | pr_title, new_body = build_pr_title_and_body(@release_pr, @merged_prs, @changed_files, nil)
25 | expect(pr_title).to eq "Release 2019-02-20 22:58:35 +0900"
26 | expect(new_body).to eq <<~MARKDOWN
27 | - [ ] #3 @hakobe
28 | - [ ] #6 @ninjinkun
29 | MARKDOWN
30 | }
31 | end
32 |
33 | context "with template_path" do
34 | it {
35 | pr_title, new_body = build_pr_title_and_body(@release_pr, @merged_prs, @changed_files, "spec/fixtures/file/template_1.erb")
36 | expect(pr_title).to eq "a"
37 | expect(new_body).to eq <<~MARKDOWN
38 | b
39 | MARKDOWN
40 | }
41 | end
42 | end
43 |
44 | describe "#dump_result_as_json" do
45 | it {
46 | output = capture(:stdout) { dump_result_as_json(@release_pr, @merged_prs, @changed_files) }
47 | parsed_output = JSON.parse(output)
48 |
49 | expect(parsed_output.keys).to eq %w[release_pull_request merged_pull_requests changed_files]
50 | expect(parsed_output["release_pull_request"]).to eq({ "data" => JSON.parse(@release_pr.to_hash.to_json) })
51 | expect(parsed_output["merged_pull_requests"]).to eq @merged_prs.map {|e| JSON.parse(Git::Pr::Release::PullRequest.new(e).to_hash.to_json) }
52 | expect(parsed_output["changed_files"]).to eq @changed_files.map {|e| JSON.parse(e.to_hash.to_json) }
53 | }
54 | end
55 |
56 | describe "#merge_pr_body" do
57 | context "new pr added" do
58 | it {
59 | actual = merge_pr_body(<<~OLD_BODY, <<~NEW_BODY)
60 | - [x] #3 Provides a creating release pull-request object for template @hakobe
61 | - [ ] #6 Support two factor auth @ninjinkun
62 | OLD_BODY
63 | - [ ] #3 Provides a creating release pull-request object for template @hakobe
64 | - [ ] #4 use user who create PR if there is no assignee @hakobe
65 | - [ ] #6 Support two factor auth @ninjinkun
66 | NEW_BODY
67 |
68 | expect(actual).to eq <<~MARKDOWN.chomp
69 | - [x] #3 Provides a creating release pull-request object for template @hakobe
70 | - [ ] #4 use user who create PR if there is no assignee @hakobe
71 | - [ ] #6 Support two factor auth @ninjinkun
72 | MARKDOWN
73 | }
74 | end
75 | context "new pr added and keeping task status" do
76 | it {
77 | actual = merge_pr_body(<<~OLD_BODY, <<~NEW_BODY)
78 | - [x] #4 use user who create PR if there is no assignee @hakobe
79 | - [x] #6 Support two factor auth @ninjinkun
80 | OLD_BODY
81 | - [ ] #3 Provides a creating release pull-request object for template @hakobe
82 | - [ ] #4 use user who create PR if there is no assignee @hakobe
83 | - [ ] #6 Support two factor auth @ninjinkun
84 | NEW_BODY
85 |
86 | expect(actual).to eq <<~MARKDOWN.chomp
87 | - [ ] #3 Provides a creating release pull-request object for template @hakobe
88 | - [x] #4 use user who create PR if there is no assignee @hakobe
89 | - [x] #6 Support two factor auth @ninjinkun
90 | MARKDOWN
91 | }
92 | end
93 | context "new pr added when the same numbers are included in a forward match" do
94 | it {
95 | actual = merge_pr_body(<<~OLD_BODY, <<~NEW_BODY)
96 | - [x] #3 Provides a creating release pull-request object for template @hakobe
97 | - [ ] #6 Support two factor auth @ninjinkun
98 | OLD_BODY
99 | - [ ] #3 Provides a creating release pull-request object for template @hakobe
100 | - [ ] #4 use user who create PR if there is no assignee @hakobe
101 | - [ ] #6 Support two factor auth @ninjinkun
102 | - [ ] #30 Extract logic from bin/git-pr-release @banyan
103 | NEW_BODY
104 |
105 | expect(actual).to eq <<~MARKDOWN.chomp
106 | - [x] #3 Provides a creating release pull-request object for template @hakobe
107 | - [ ] #4 use user who create PR if there is no assignee @hakobe
108 | - [ ] #6 Support two factor auth @ninjinkun
109 | - [ ] #30 Extract logic from bin/git-pr-release @banyan
110 | MARKDOWN
111 | }
112 | end
113 | end
114 |
115 | describe "#host_and_repository_and_scheme" do
116 | it {
117 | expect(self).to receive(:git).with(:config, "remote.origin.url") { ["https://github.com/motemen/git-pr-release\n"] }
118 | expect(host_and_repository_and_scheme).to eq [nil, "motemen/git-pr-release", "https"]
119 | }
120 | it {
121 | expect(self).to receive(:git).with(:config, "remote.origin.url") { ["ssh://git@github.com/motemen/git-pr-release.git\n"] }
122 | expect(host_and_repository_and_scheme).to eq [nil, "motemen/git-pr-release", "https"]
123 | }
124 | it {
125 | expect(self).to receive(:git).with(:config, "remote.origin.url") { ["http://ghe.example.com/motemen/git-pr-release\n"] }
126 | expect(host_and_repository_and_scheme).to eq ["ghe.example.com", "motemen/git-pr-release", "http"]
127 | }
128 | it {
129 | expect(self).to receive(:git).with(:config, "remote.origin.url") { ["ssh://git@ghe.example.com/motemen/git-pr-release.git\n"] }
130 | expect(host_and_repository_and_scheme).to eq ["ghe.example.com", "motemen/git-pr-release", "https"]
131 | }
132 | end
133 | end
134 |
--------------------------------------------------------------------------------
/lib/git/pr/release/util.rb:
--------------------------------------------------------------------------------
1 | require 'erb'
2 | require 'uri'
3 | require 'open3'
4 | require 'json'
5 | require 'colorize'
6 | require 'diff/lcs'
7 |
8 | module Git
9 | module Pr
10 | module Release
11 | module Util
12 | def host_and_repository_and_scheme
13 | @host_and_repository_and_scheme ||= begin
14 | remote = git(:config, 'remote.origin.url').first.chomp
15 | unless %r(^\w+://) === remote
16 | remote = "ssh://#{remote.sub(':', '/')}"
17 | end
18 |
19 | remote_url = URI.parse(remote)
20 | repository = remote_url.path.sub(%r(^/), '').sub(/\.git$/, '')
21 |
22 | host = remote_url.host == 'github.com' ? nil : remote_url.host
23 | [ host, repository, remote_url.scheme === 'http' ? 'http' : 'https' ]
24 | end
25 | end
26 |
27 | def say(message, level)
28 | color = case level
29 | when :trace
30 | return unless ENV['DEBUG']
31 | nil
32 | when :debug
33 | return unless ENV['DEBUG']
34 | :blue
35 | when :info
36 | :green
37 | when :notice
38 | :yellow
39 | when :warn
40 | :magenta
41 | when :error
42 | :red
43 | end
44 |
45 | STDERR.puts message.colorize(color)
46 | end
47 |
48 | def git(*command)
49 | command = [ 'git', *command.map(&:to_s) ]
50 | say "Executing `#{command.join(' ')}`", :trace
51 | out, status = Open3.capture2(*command)
52 | unless status.success?
53 | raise "Executing `#{command.join(' ')}` failed: #{status}"
54 | end
55 | out.each_line
56 | end
57 |
58 | def git_config(key)
59 | host, _ = host_and_repository_and_scheme()
60 |
61 | plain_key = [ 'pr-release', key ].join('.')
62 | host_aware_key = [ 'pr-release', host, key ].compact.join('.')
63 |
64 | begin
65 | git(:config, '-f', '.git-pr-release', plain_key).first.chomp
66 | rescue
67 | git(:config, host_aware_key).first.chomp rescue nil
68 | end
69 | end
70 |
71 | def git_config_set(key, value)
72 | host, _ = host_and_repository_and_scheme()
73 | host_aware_key = [ 'pr-release', host, key ].compact.join('.')
74 |
75 | git :config, '--global', host_aware_key, value
76 | end
77 |
78 | # First line will be the title of the PR
79 | DEFAULT_PR_TEMPLATE = <
81 | <% pull_requests.each do |pr| -%>
82 | <%= pr.to_checklist_item %>
83 | <% end -%>
84 | ERB
85 |
86 | def build_pr_title_and_body(release_pr, merged_prs, changed_files, template_path)
87 | release_pull_request = target_pull_request = release_pr ? PullRequest.new(release_pr) : DummyPullRequest.new
88 | merged_pull_requests = pull_requests = merged_prs.map { |pr| PullRequest.new(pr) }
89 |
90 | template = if template_path
91 | template_fullpath = File.join(git('rev-parse', '--show-toplevel').first.chomp, template_path)
92 | File.read(template_fullpath)
93 | else
94 | DEFAULT_PR_TEMPLATE
95 | end
96 |
97 | erb = if RUBY_VERSION >= '2.6'
98 | ERB.new template, trim_mode: '-'
99 | else
100 | ERB.new template, nil, '-'
101 | end
102 | content = erb.result binding
103 | content.split(/\n/, 2)
104 | end
105 |
106 | def dump_result_as_json(release_pr, merged_prs, changed_files)
107 | puts( {
108 | :release_pull_request => (release_pr ? PullRequest.new(release_pr) : DummyPullRequest.new).to_hash,
109 | :merged_pull_requests => merged_prs.map { |pr| PullRequest.new(pr).to_hash },
110 | :changed_files => changed_files.map { |file| file.to_hash }
111 | }.to_json )
112 | end
113 |
114 | def merge_pr_body(old_body, new_body)
115 | # Try to take over checklist statuses
116 | pr_body_lines = []
117 |
118 | check_status = {}
119 | old_body.split(/\r?\n/).each { |line|
120 | line.match(/^- \[(?[ x])\] #(?\d+)/) { |m|
121 | say "Found pull-request checkbox \##{m[:issue_number]} is #{m[:check_value]}.", :trace
122 | check_status[m[:issue_number]] = m[:check_value]
123 | }
124 | }
125 | old_body_unchecked = old_body.gsub /^- \[[ x]\] \#(\d+)/, '- [ ] #\1'
126 |
127 | Diff::LCS.traverse_balanced(old_body_unchecked.split(/\r?\n/), new_body.split(/\r?\n/)) do |event|
128 | say "diff: #{event.inspect}", :trace
129 | action, old, new = *event
130 | old_nr, old_line = *old
131 | new_nr, new_line = *new
132 |
133 | case action
134 | when '=', '+'
135 | say "Use line as is: #{new_line}", :trace
136 | pr_body_lines << new_line
137 | when '-'
138 | say "Use old line: #{old_line}", :trace
139 | pr_body_lines << old_line
140 | when '!'
141 | if [ old_line, new_line ].all? { |line| /^- \[ \]/ === line }
142 | say "Found checklist diff; use old one: #{old_line}", :trace
143 | pr_body_lines << old_line
144 | else
145 | # not a checklist diff, use both line
146 | say "Use line as is: #{old_line}", :trace
147 | pr_body_lines << old_line
148 |
149 | say "Use line as is: #{new_line}", :trace
150 | pr_body_lines << new_line
151 | end
152 | else
153 | say "Unknown diff event: #{event}", :warn
154 | end
155 | end
156 |
157 | merged_body = pr_body_lines.join("\n")
158 | check_status.each { |issue_number, check_value|
159 | say "Update pull-request checkbox \##{issue_number} to #{check_value}.", :trace
160 | merged_body.gsub! /^- \[ \] \##{issue_number}\b/, "- [#{check_value}] \##{issue_number}"
161 | }
162 |
163 | merged_body
164 | end
165 |
166 | def obtain_token!
167 | token = ENV.fetch('GIT_PR_RELEASE_TOKEN') { git_config('token') }
168 |
169 | unless token
170 | require 'highline/import'
171 | STDERR.puts 'Could not obtain GitHub API token.'
172 | STDERR.puts 'Trying to generate token...'
173 |
174 | username = ask('username? ') { |q| q.default = ENV['USER'] }
175 | password = ask('password? (not saved) ') { |q| q.echo = '*' }
176 |
177 | temporary_client = Octokit::Client.new :login => username, :password => password
178 |
179 | auth = request_authorization(temporary_client, nil)
180 |
181 | token = auth.token
182 | git_config_set 'token', token
183 | end
184 |
185 | token
186 | end
187 |
188 | def request_authorization(client, two_factor_code)
189 | params = { :scopes => [ 'public_repo', 'repo' ], :note => 'git-pr-release' }
190 | params[:headers] = { "X-GitHub-OTP" => two_factor_code} if two_factor_code
191 |
192 | auth = nil
193 | begin
194 | auth = client.create_authorization(params)
195 | rescue Octokit::OneTimePasswordRequired
196 | two_factor_code = ask('two-factor authentication code? ')
197 | auth = request_authorization(client, two_factor_code)
198 | end
199 |
200 | auth
201 | end
202 | end
203 | end
204 | end
205 | end
206 |
--------------------------------------------------------------------------------
/lib/git/pr/release/cli.rb:
--------------------------------------------------------------------------------
1 | require 'octokit'
2 | require 'optparse'
3 |
4 | module Git
5 | module Pr
6 | module Release
7 | class CLI
8 | include Git::Pr::Release::Util
9 | attr_reader :repository, :production_branch, :staging_branch, :template_path, :labels, :assign_pr_author, :request_pr_author_review
10 |
11 | def self.start
12 | result = self.new.start
13 | exit result
14 | end
15 |
16 | def initialize
17 | @dry_run = false
18 | @json = false
19 | @no_fetch = false
20 | @squashed = false
21 | end
22 |
23 | def start
24 | OptionParser.new do |opts|
25 | opts.on('-n', '--dry-run', 'Do not create/update a PR. Just prints out') do |v|
26 | @dry_run = v
27 | end
28 | opts.on('--json', 'Show data of target PRs in JSON format') do |v|
29 | @json = v
30 | end
31 | opts.on('--no-fetch', 'Do not fetch from remote repo before determining target PRs (CI friendly)') do |v|
32 | @no_fetch = v
33 | end
34 | opts.on('--squashed', 'Handle squash merged PRs') do |v|
35 | @squashed = v
36 | end
37 | opts.on('--overwrite-description', 'Force overwrite PR description') do |v|
38 | @overwrite_description = v
39 | end
40 | end.parse!
41 |
42 | ### Set up configuration
43 | configure
44 |
45 | ### Fetch merged PRs
46 | merged_prs = fetch_merged_prs
47 | if merged_prs.empty?
48 | say 'No pull requests to be released', :error
49 | return 1
50 | end
51 |
52 | ### Create a release PR
53 | create_release_pr(merged_prs)
54 | return 0
55 | end
56 |
57 | def client
58 | @client ||= Octokit::Client.new :access_token => obtain_token!
59 | end
60 |
61 | def configure
62 | host, @repository, scheme = host_and_repository_and_scheme
63 |
64 | if host
65 | if scheme == 'https' # GitHub Enterprise
66 | ssl_no_verify = %w[true 1].include? ENV.fetch('GIT_PR_RELEASE_SSL_NO_VERIFY') { git_config('ssl-no-verify') }
67 | if ssl_no_verify
68 | OpenSSL::SSL.const_set :VERIFY_PEER, OpenSSL::SSL::VERIFY_NONE
69 | end
70 | end
71 |
72 | Octokit.configure do |c|
73 | c.api_endpoint = "#{scheme}://#{host}/api/v3"
74 | c.web_endpoint = "#{scheme}://#{host}/"
75 | end
76 | end
77 |
78 | @production_branch = ENV.fetch('GIT_PR_RELEASE_BRANCH_PRODUCTION') { git_config('branch.production') } || 'master'
79 | @staging_branch = ENV.fetch('GIT_PR_RELEASE_BRANCH_STAGING') { git_config('branch.staging') } || 'staging'
80 | @template_path = ENV.fetch('GIT_PR_RELEASE_TEMPLATE') { git_config('template') }
81 |
82 | _labels = ENV.fetch('GIT_PR_RELEASE_LABELS') { git_config('labels') }
83 | @labels = _labels && _labels.split(/\s*,\s*/) || []
84 |
85 | _assign_pr_author = ENV.fetch('GIT_PR_RELEASE_ASSIGN_PR_AUTHOR') { git_config('assign-pr-author') }
86 | @assign_pr_author = %w[true 1].include?(_assign_pr_author)
87 |
88 | _request_pr_author_review = ENV.fetch('GIT_PR_RELEASE_REQUEST_PR_AUTHOR_REVIEW') { git_config('request-pr-author-review') }
89 | @request_pr_author_review = %w[true 1].include?(_request_pr_author_review)
90 |
91 | say "Repository: #{repository}", :debug
92 | say "Production branch: #{production_branch}", :debug
93 | say "Staging branch: #{staging_branch}", :debug
94 | say "Template path: #{template_path}", :debug
95 | say "Labels #{labels}", :debug
96 | say "Assign PR author: #{assign_pr_author}", :debug
97 | say "Request PR author review: #{request_pr_author_review}", :debug
98 | end
99 |
100 | def fetch_merged_prs
101 | bool = git(:'rev-parse', '--is-shallow-repository').first.chomp
102 | if bool == 'true'
103 | git(:fetch, '--unshallow')
104 | end
105 | git :remote, 'update', 'origin' unless @no_fetch
106 |
107 | merged_pull_request_numbers = fetch_merged_pr_numbers_from_git_remote
108 | if @squashed
109 | merged_pull_request_numbers.concat(fetch_squash_merged_pr_numbers_from_github)
110 | end
111 |
112 | merged_prs = merged_pull_request_numbers.uniq.sort.map do |nr|
113 | pr = client.pull_request repository, nr
114 | say "To be released: ##{pr.number} #{pr.title}", :notice
115 | pr
116 | end
117 |
118 | merged_prs
119 | end
120 |
121 | def fetch_merged_pr_numbers_from_git_remote
122 | merged_feature_head_sha1s = git(:log, '--merges', '--pretty=format:%P', "origin/#{production_branch}..origin/#{staging_branch}").map do |line|
123 | main_sha1, feature_sha1 = line.chomp.split /\s+/
124 | feature_sha1
125 | end
126 |
127 | git('ls-remote', 'origin', 'refs/pull/*/head').map do |line|
128 | sha1, ref = line.chomp.split /\s+/
129 |
130 | if merged_feature_head_sha1s.include? sha1
131 | if %r<^refs/pull/(\d+)/head$>.match ref
132 | pr_number = $1.to_i
133 |
134 | if git('merge-base', sha1, "origin/#{production_branch}").first.chomp == sha1
135 | say "##{pr_number} (#{sha1}) is already merged into #{production_branch}", :debug
136 | else
137 | pr_number
138 | end
139 | else
140 | say "Bad pull request head ref format: #{ref}", :warn
141 | nil
142 | end
143 | end
144 | end.compact
145 | end
146 |
147 | def search_issue_numbers(query)
148 | sleep 1
149 | say "search issues with query:#{query}", :debug
150 | # Fortunately, we don't need to take care of the page count in response, because
151 | # the default value of per_page is 30 and we can't specify more than 30 commits due to
152 | # the length limit specification of the query string.
153 | client.search_issues("#{query}")[:items].map(&:number)
154 | end
155 |
156 | def fetch_squash_merged_pr_numbers_from_github
157 | # When "--abbrev" is specified, the length of the each line of the stdout isn't fixed.
158 | # It is just a minimum length, and if the commit cannot be uniquely identified with
159 | # that length, a longer commit hash will be displayed.
160 | # We specify this option to minimize the length of the query string, but we use
161 | # "--abbrev=7" because the SHA syntax of the search API requires a string of at
162 | # least 7 characters.
163 | # ref. https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests#search-by-commit-sha
164 | # This is done because there is a length limit on the API query string, and we want
165 | # to create a string with the minimum possible length.
166 | shas = git(:log, '--pretty=format:%h', "--abbrev=7", "--no-merges", "--first-parent",
167 | "origin/#{production_branch}..origin/#{staging_branch}").map(&:chomp)
168 |
169 | pr_nums = []
170 | query_base = "repo:#{repository} is:pr is:closed"
171 | query = query_base
172 | # Make bulk requests with multiple SHAs of the maximum possible length.
173 | # If multiple SHAs are specified, the issue search API will treat it like an OR search,
174 | # and all the pull requests will be searched.
175 | # This is difficult to read from the current documentation, but that is the current
176 | # behavior and GitHub support has responded that this is the spec.
177 | shas.each do |sha|
178 | # Longer than 256 characters are not supported in the query.
179 | # ref. https://docs.github.com/en/rest/reference/search#limitations-on-query-length
180 | if query.length + 1 + sha.length >= 256
181 | pr_nums.concat(search_issue_numbers(query))
182 | query = query_base
183 | end
184 | query += " " + sha
185 | end
186 | if query != query_base
187 | pr_nums.concat(search_issue_numbers(query))
188 | end
189 | pr_nums
190 | end
191 |
192 | def create_release_pr(merged_prs)
193 | found_release_pr = detect_existing_release_pr
194 | create_mode = found_release_pr.nil?
195 |
196 | if create_mode
197 | if @dry_run
198 | release_pr = nil
199 | changed_files = []
200 | else
201 | release_pr = prepare_release_pr
202 | changed_files = pull_request_files(release_pr)
203 | end
204 | else
205 | release_pr = found_release_pr
206 | changed_files = pull_request_files(release_pr)
207 | end
208 |
209 | pr_title, pr_body = if @overwrite_description
210 | build_pr_title_and_body(release_pr, merged_prs, changed_files, template_path)
211 | else
212 | build_and_merge_pr_title_and_body(release_pr, merged_prs, changed_files)
213 | end
214 |
215 | if @dry_run
216 | say 'Dry-run. Not updating PR', :info
217 | say pr_title, :notice
218 | say pr_body, :notice
219 | dump_result_as_json( release_pr, merged_prs, changed_files ) if @json
220 | return
221 | end
222 |
223 | update_release_pr(release_pr, pr_title, pr_body, merged_prs)
224 |
225 | say "#{create_mode ? 'Created' : 'Updated'} pull request: #{release_pr.rels[:html].href}", :notice
226 | dump_result_as_json( release_pr, merged_prs, changed_files ) if @json
227 | end
228 |
229 | def detect_existing_release_pr
230 | say 'Searching for existing release pull requests...', :info
231 | user=repository.split("/")[0]
232 | client.pull_requests(repository, head: "#{user}:#{staging_branch}", base: production_branch).first
233 | end
234 |
235 | def prepare_release_pr
236 | client.create_pull_request(
237 | repository, production_branch, staging_branch, 'Preparing release pull request...', ''
238 | )
239 | end
240 |
241 | def build_and_merge_pr_title_and_body(release_pr, merged_prs, changed_files)
242 | # release_pr is nil when dry_run && create_mode
243 | old_body = (release_pr && release_pr.body != nil) ? release_pr.body : ""
244 | pr_title, new_body = build_pr_title_and_body(release_pr, merged_prs, changed_files, template_path)
245 |
246 | [pr_title, merge_pr_body(old_body, new_body)]
247 | end
248 |
249 | def update_release_pr(release_pr, pr_title, pr_body, merged_prs)
250 | say 'Pull request body:', :debug
251 | say pr_body, :debug
252 |
253 | client.update_pull_request(
254 | repository, release_pr.number, :title => pr_title, :body => pr_body
255 | )
256 |
257 | unless labels.empty?
258 | client.add_labels_to_an_issue(
259 | repository, release_pr.number, labels
260 | )
261 | end
262 |
263 | if assign_pr_author
264 | assignees = merged_prs.map { |pr| PullRequest.new(pr).target_user_login_names }.flatten.compact.uniq
265 | unless assignees.empty?
266 | client.add_assignees(
267 | repository, release_pr.number, assignees
268 | )
269 | end
270 | end
271 |
272 | if request_pr_author_review
273 | reviewers = merged_prs.map { |pr| PullRequest.new(pr).target_user_login_names }.flatten.compact.uniq
274 | unless reviewers.empty?
275 | client.request_pull_request_review(
276 | repository, release_pr.number, reviewers: reviewers
277 | )
278 | end
279 | end
280 | end
281 |
282 | # Fetch PR files of specified pull_request
283 | def pull_request_files(pull_request)
284 | return [] if pull_request.nil?
285 |
286 | # Fetch files as many as possible
287 | client.auto_paginate = true
288 | files = client.pull_request_files repository, pull_request.number
289 | client.auto_paginate = false
290 | return files
291 | end
292 | end
293 | end
294 | end
295 | end
296 |
--------------------------------------------------------------------------------
/spec/fixtures/file/pr_1.yml:
--------------------------------------------------------------------------------
1 | ---
2 | :url: https://api.github.com/repos/motemen/git-pr-release/pulls/1
3 | :id: 13194147
4 | :node_id: MDExOlB1bGxSZXF1ZXN0MTMxOTQxNDc=
5 | :html_url: https://github.com/motemen/git-pr-release/pull/1
6 | :diff_url: https://github.com/motemen/git-pr-release/pull/1.diff
7 | :patch_url: https://github.com/motemen/git-pr-release/pull/1.patch
8 | :issue_url: https://api.github.com/repos/motemen/git-pr-release/issues/1
9 | :number: 1
10 | :state: closed
11 | :locked: false
12 | :title: No need to append key prefix for template config
13 | :user:
14 | :login: hakobe
15 | :id: 6882
16 | :node_id: MDQ6VXNlcjY4ODI=
17 | :avatar_url: https://avatars0.githubusercontent.com/u/6882?v=4
18 | :gravatar_id: ''
19 | :url: https://api.github.com/users/hakobe
20 | :html_url: https://github.com/hakobe
21 | :followers_url: https://api.github.com/users/hakobe/followers
22 | :following_url: https://api.github.com/users/hakobe/following{/other_user}
23 | :gists_url: https://api.github.com/users/hakobe/gists{/gist_id}
24 | :starred_url: https://api.github.com/users/hakobe/starred{/owner}{/repo}
25 | :subscriptions_url: https://api.github.com/users/hakobe/subscriptions
26 | :organizations_url: https://api.github.com/users/hakobe/orgs
27 | :repos_url: https://api.github.com/users/hakobe/repos
28 | :events_url: https://api.github.com/users/hakobe/events{/privacy}
29 | :received_events_url: https://api.github.com/users/hakobe/received_events
30 | :type: User
31 | :site_admin: false
32 | :body: "`git_config('pr-release.template')` loads config value from key `pr-release.pr-release.template`.\n"
33 | :created_at: 2014-03-05 02:48:51.000000000 Z
34 | :updated_at: 2014-07-10 03:13:44.000000000 Z
35 | :closed_at: 2014-03-05 02:59:52.000000000 Z
36 | :merged_at: 2014-03-05 02:59:52.000000000 Z
37 | :merge_commit_sha: d40cebeaa7cd69c5eabb6e76de7314891d9cd743
38 | :assignee:
39 | :assignees: []
40 | :requested_reviewers: []
41 | :requested_teams: []
42 | :labels: []
43 | :milestone:
44 | :commits_url: https://api.github.com/repos/motemen/git-pr-release/pulls/1/commits
45 | :review_comments_url: https://api.github.com/repos/motemen/git-pr-release/pulls/1/comments
46 | :review_comment_url: https://api.github.com/repos/motemen/git-pr-release/pulls/comments{/number}
47 | :comments_url: https://api.github.com/repos/motemen/git-pr-release/issues/1/comments
48 | :statuses_url: https://api.github.com/repos/motemen/git-pr-release/statuses/bbcd2a04ef394e91be44c24e93e52fdbca944060
49 | :head:
50 | :label: hakobe:fix/template-config-key
51 | :ref: fix/template-config-key
52 | :sha: bbcd2a04ef394e91be44c24e93e52fdbca944060
53 | :user:
54 | :login: hakobe
55 | :id: 6882
56 | :node_id: MDQ6VXNlcjY4ODI=
57 | :avatar_url: https://avatars0.githubusercontent.com/u/6882?v=4
58 | :gravatar_id: ''
59 | :url: https://api.github.com/users/hakobe
60 | :html_url: https://github.com/hakobe
61 | :followers_url: https://api.github.com/users/hakobe/followers
62 | :following_url: https://api.github.com/users/hakobe/following{/other_user}
63 | :gists_url: https://api.github.com/users/hakobe/gists{/gist_id}
64 | :starred_url: https://api.github.com/users/hakobe/starred{/owner}{/repo}
65 | :subscriptions_url: https://api.github.com/users/hakobe/subscriptions
66 | :organizations_url: https://api.github.com/users/hakobe/orgs
67 | :repos_url: https://api.github.com/users/hakobe/repos
68 | :events_url: https://api.github.com/users/hakobe/events{/privacy}
69 | :received_events_url: https://api.github.com/users/hakobe/received_events
70 | :type: User
71 | :site_admin: false
72 | :repo:
73 | :id: 17425170
74 | :node_id: MDEwOlJlcG9zaXRvcnkxNzQyNTE3MA==
75 | :name: git-pr-release
76 | :full_name: hakobe/git-pr-release
77 | :private: false
78 | :owner:
79 | :login: hakobe
80 | :id: 6882
81 | :node_id: MDQ6VXNlcjY4ODI=
82 | :avatar_url: https://avatars0.githubusercontent.com/u/6882?v=4
83 | :gravatar_id: ''
84 | :url: https://api.github.com/users/hakobe
85 | :html_url: https://github.com/hakobe
86 | :followers_url: https://api.github.com/users/hakobe/followers
87 | :following_url: https://api.github.com/users/hakobe/following{/other_user}
88 | :gists_url: https://api.github.com/users/hakobe/gists{/gist_id}
89 | :starred_url: https://api.github.com/users/hakobe/starred{/owner}{/repo}
90 | :subscriptions_url: https://api.github.com/users/hakobe/subscriptions
91 | :organizations_url: https://api.github.com/users/hakobe/orgs
92 | :repos_url: https://api.github.com/users/hakobe/repos
93 | :events_url: https://api.github.com/users/hakobe/events{/privacy}
94 | :received_events_url: https://api.github.com/users/hakobe/received_events
95 | :type: User
96 | :site_admin: false
97 | :html_url: https://github.com/hakobe/git-pr-release
98 | :description: Creates a pull request which summarizes feature branches that are
99 | to be released into production
100 | :fork: true
101 | :url: https://api.github.com/repos/hakobe/git-pr-release
102 | :forks_url: https://api.github.com/repos/hakobe/git-pr-release/forks
103 | :keys_url: https://api.github.com/repos/hakobe/git-pr-release/keys{/key_id}
104 | :collaborators_url: https://api.github.com/repos/hakobe/git-pr-release/collaborators{/collaborator}
105 | :teams_url: https://api.github.com/repos/hakobe/git-pr-release/teams
106 | :hooks_url: https://api.github.com/repos/hakobe/git-pr-release/hooks
107 | :issue_events_url: https://api.github.com/repos/hakobe/git-pr-release/issues/events{/number}
108 | :events_url: https://api.github.com/repos/hakobe/git-pr-release/events
109 | :assignees_url: https://api.github.com/repos/hakobe/git-pr-release/assignees{/user}
110 | :branches_url: https://api.github.com/repos/hakobe/git-pr-release/branches{/branch}
111 | :tags_url: https://api.github.com/repos/hakobe/git-pr-release/tags
112 | :blobs_url: https://api.github.com/repos/hakobe/git-pr-release/git/blobs{/sha}
113 | :git_tags_url: https://api.github.com/repos/hakobe/git-pr-release/git/tags{/sha}
114 | :git_refs_url: https://api.github.com/repos/hakobe/git-pr-release/git/refs{/sha}
115 | :trees_url: https://api.github.com/repos/hakobe/git-pr-release/git/trees{/sha}
116 | :statuses_url: https://api.github.com/repos/hakobe/git-pr-release/statuses/{sha}
117 | :languages_url: https://api.github.com/repos/hakobe/git-pr-release/languages
118 | :stargazers_url: https://api.github.com/repos/hakobe/git-pr-release/stargazers
119 | :contributors_url: https://api.github.com/repos/hakobe/git-pr-release/contributors
120 | :subscribers_url: https://api.github.com/repos/hakobe/git-pr-release/subscribers
121 | :subscription_url: https://api.github.com/repos/hakobe/git-pr-release/subscription
122 | :commits_url: https://api.github.com/repos/hakobe/git-pr-release/commits{/sha}
123 | :git_commits_url: https://api.github.com/repos/hakobe/git-pr-release/git/commits{/sha}
124 | :comments_url: https://api.github.com/repos/hakobe/git-pr-release/comments{/number}
125 | :issue_comment_url: https://api.github.com/repos/hakobe/git-pr-release/issues/comments{/number}
126 | :contents_url: https://api.github.com/repos/hakobe/git-pr-release/contents/{+path}
127 | :compare_url: https://api.github.com/repos/hakobe/git-pr-release/compare/{base}...{head}
128 | :merges_url: https://api.github.com/repos/hakobe/git-pr-release/merges
129 | :archive_url: https://api.github.com/repos/hakobe/git-pr-release/{archive_format}{/ref}
130 | :downloads_url: https://api.github.com/repos/hakobe/git-pr-release/downloads
131 | :issues_url: https://api.github.com/repos/hakobe/git-pr-release/issues{/number}
132 | :pulls_url: https://api.github.com/repos/hakobe/git-pr-release/pulls{/number}
133 | :milestones_url: https://api.github.com/repos/hakobe/git-pr-release/milestones{/number}
134 | :notifications_url: https://api.github.com/repos/hakobe/git-pr-release/notifications{?since,all,participating}
135 | :labels_url: https://api.github.com/repos/hakobe/git-pr-release/labels{/name}
136 | :releases_url: https://api.github.com/repos/hakobe/git-pr-release/releases{/id}
137 | :deployments_url: https://api.github.com/repos/hakobe/git-pr-release/deployments
138 | :created_at: 2014-03-05 02:20:59.000000000 Z
139 | :updated_at: 2014-05-13 01:42:58.000000000 Z
140 | :pushed_at: 2014-05-12 11:42:34.000000000 Z
141 | :git_url: git://github.com/hakobe/git-pr-release.git
142 | :ssh_url: git@github.com:hakobe/git-pr-release.git
143 | :clone_url: https://github.com/hakobe/git-pr-release.git
144 | :svn_url: https://github.com/hakobe/git-pr-release
145 | :homepage: https://rubygems.org/gems/git-pr-release
146 | :size: 236
147 | :stargazers_count: 0
148 | :watchers_count: 0
149 | :language: Ruby
150 | :has_issues: false
151 | :has_projects: true
152 | :has_downloads: true
153 | :has_wiki: true
154 | :has_pages: false
155 | :forks_count: 0
156 | :mirror_url:
157 | :archived: false
158 | :open_issues_count: 0
159 | :license:
160 | :forks: 0
161 | :open_issues: 0
162 | :watchers: 0
163 | :default_branch: master
164 | :base:
165 | :label: motemen:master
166 | :ref: master
167 | :sha: adcd1d55438db0116f2583368466f1bf101ac0d0
168 | :user:
169 | :login: motemen
170 | :id: 8465
171 | :node_id: MDQ6VXNlcjg0NjU=
172 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
173 | :gravatar_id: ''
174 | :url: https://api.github.com/users/motemen
175 | :html_url: https://github.com/motemen
176 | :followers_url: https://api.github.com/users/motemen/followers
177 | :following_url: https://api.github.com/users/motemen/following{/other_user}
178 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
179 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
180 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
181 | :organizations_url: https://api.github.com/users/motemen/orgs
182 | :repos_url: https://api.github.com/users/motemen/repos
183 | :events_url: https://api.github.com/users/motemen/events{/privacy}
184 | :received_events_url: https://api.github.com/users/motemen/received_events
185 | :type: User
186 | :site_admin: false
187 | :repo:
188 | :id: 15468156
189 | :node_id: MDEwOlJlcG9zaXRvcnkxNTQ2ODE1Ng==
190 | :name: git-pr-release
191 | :full_name: motemen/git-pr-release
192 | :private: false
193 | :owner:
194 | :login: motemen
195 | :id: 8465
196 | :node_id: MDQ6VXNlcjg0NjU=
197 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
198 | :gravatar_id: ''
199 | :url: https://api.github.com/users/motemen
200 | :html_url: https://github.com/motemen
201 | :followers_url: https://api.github.com/users/motemen/followers
202 | :following_url: https://api.github.com/users/motemen/following{/other_user}
203 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
204 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
205 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
206 | :organizations_url: https://api.github.com/users/motemen/orgs
207 | :repos_url: https://api.github.com/users/motemen/repos
208 | :events_url: https://api.github.com/users/motemen/events{/privacy}
209 | :received_events_url: https://api.github.com/users/motemen/received_events
210 | :type: User
211 | :site_admin: false
212 | :html_url: https://github.com/motemen/git-pr-release
213 | :description: Release pull request generator
214 | :fork: false
215 | :url: https://api.github.com/repos/motemen/git-pr-release
216 | :forks_url: https://api.github.com/repos/motemen/git-pr-release/forks
217 | :keys_url: https://api.github.com/repos/motemen/git-pr-release/keys{/key_id}
218 | :collaborators_url: https://api.github.com/repos/motemen/git-pr-release/collaborators{/collaborator}
219 | :teams_url: https://api.github.com/repos/motemen/git-pr-release/teams
220 | :hooks_url: https://api.github.com/repos/motemen/git-pr-release/hooks
221 | :issue_events_url: https://api.github.com/repos/motemen/git-pr-release/issues/events{/number}
222 | :events_url: https://api.github.com/repos/motemen/git-pr-release/events
223 | :assignees_url: https://api.github.com/repos/motemen/git-pr-release/assignees{/user}
224 | :branches_url: https://api.github.com/repos/motemen/git-pr-release/branches{/branch}
225 | :tags_url: https://api.github.com/repos/motemen/git-pr-release/tags
226 | :blobs_url: https://api.github.com/repos/motemen/git-pr-release/git/blobs{/sha}
227 | :git_tags_url: https://api.github.com/repos/motemen/git-pr-release/git/tags{/sha}
228 | :git_refs_url: https://api.github.com/repos/motemen/git-pr-release/git/refs{/sha}
229 | :trees_url: https://api.github.com/repos/motemen/git-pr-release/git/trees{/sha}
230 | :statuses_url: https://api.github.com/repos/motemen/git-pr-release/statuses/{sha}
231 | :languages_url: https://api.github.com/repos/motemen/git-pr-release/languages
232 | :stargazers_url: https://api.github.com/repos/motemen/git-pr-release/stargazers
233 | :contributors_url: https://api.github.com/repos/motemen/git-pr-release/contributors
234 | :subscribers_url: https://api.github.com/repos/motemen/git-pr-release/subscribers
235 | :subscription_url: https://api.github.com/repos/motemen/git-pr-release/subscription
236 | :commits_url: https://api.github.com/repos/motemen/git-pr-release/commits{/sha}
237 | :git_commits_url: https://api.github.com/repos/motemen/git-pr-release/git/commits{/sha}
238 | :comments_url: https://api.github.com/repos/motemen/git-pr-release/comments{/number}
239 | :issue_comment_url: https://api.github.com/repos/motemen/git-pr-release/issues/comments{/number}
240 | :contents_url: https://api.github.com/repos/motemen/git-pr-release/contents/{+path}
241 | :compare_url: https://api.github.com/repos/motemen/git-pr-release/compare/{base}...{head}
242 | :merges_url: https://api.github.com/repos/motemen/git-pr-release/merges
243 | :archive_url: https://api.github.com/repos/motemen/git-pr-release/{archive_format}{/ref}
244 | :downloads_url: https://api.github.com/repos/motemen/git-pr-release/downloads
245 | :issues_url: https://api.github.com/repos/motemen/git-pr-release/issues{/number}
246 | :pulls_url: https://api.github.com/repos/motemen/git-pr-release/pulls{/number}
247 | :milestones_url: https://api.github.com/repos/motemen/git-pr-release/milestones{/number}
248 | :notifications_url: https://api.github.com/repos/motemen/git-pr-release/notifications{?since,all,participating}
249 | :labels_url: https://api.github.com/repos/motemen/git-pr-release/labels{/name}
250 | :releases_url: https://api.github.com/repos/motemen/git-pr-release/releases{/id}
251 | :deployments_url: https://api.github.com/repos/motemen/git-pr-release/deployments
252 | :created_at: 2013-12-27 06:35:52.000000000 Z
253 | :updated_at: 2019-02-18 01:40:52.000000000 Z
254 | :pushed_at: 2018-11-15 10:45:07.000000000 Z
255 | :git_url: git://github.com/motemen/git-pr-release.git
256 | :ssh_url: git@github.com:motemen/git-pr-release.git
257 | :clone_url: https://github.com/motemen/git-pr-release.git
258 | :svn_url: https://github.com/motemen/git-pr-release
259 | :homepage: https://rubygems.org/gems/git-pr-release
260 | :size: 62
261 | :stargazers_count: 359
262 | :watchers_count: 359
263 | :language: Ruby
264 | :has_issues: true
265 | :has_projects: true
266 | :has_downloads: true
267 | :has_wiki: true
268 | :has_pages: false
269 | :forks_count: 28
270 | :mirror_url:
271 | :archived: false
272 | :open_issues_count: 6
273 | :license:
274 | :key: mit
275 | :name: MIT License
276 | :spdx_id: MIT
277 | :url: https://api.github.com/licenses/mit
278 | :node_id: MDc6TGljZW5zZTEz
279 | :forks: 28
280 | :open_issues: 6
281 | :watchers: 359
282 | :default_branch: master
283 | :_links:
284 | :self:
285 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/1
286 | :html:
287 | :href: https://github.com/motemen/git-pr-release/pull/1
288 | :issue:
289 | :href: https://api.github.com/repos/motemen/git-pr-release/issues/1
290 | :comments:
291 | :href: https://api.github.com/repos/motemen/git-pr-release/issues/1/comments
292 | :review_comments:
293 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/1/comments
294 | :review_comment:
295 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/comments{/number}
296 | :commits:
297 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/1/commits
298 | :statuses:
299 | :href: https://api.github.com/repos/motemen/git-pr-release/statuses/bbcd2a04ef394e91be44c24e93e52fdbca944060
300 | :author_association: COLLABORATOR
301 | :merged: true
302 | :mergeable:
303 | :rebaseable:
304 | :mergeable_state: unknown
305 | :merged_by:
306 | :login: motemen
307 | :id: 8465
308 | :node_id: MDQ6VXNlcjg0NjU=
309 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
310 | :gravatar_id: ''
311 | :url: https://api.github.com/users/motemen
312 | :html_url: https://github.com/motemen
313 | :followers_url: https://api.github.com/users/motemen/followers
314 | :following_url: https://api.github.com/users/motemen/following{/other_user}
315 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
316 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
317 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
318 | :organizations_url: https://api.github.com/users/motemen/orgs
319 | :repos_url: https://api.github.com/users/motemen/repos
320 | :events_url: https://api.github.com/users/motemen/events{/privacy}
321 | :received_events_url: https://api.github.com/users/motemen/received_events
322 | :type: User
323 | :site_admin: false
324 | :comments: 2
325 | :review_comments: 0
326 | :maintainer_can_modify: false
327 | :commits: 1
328 | :additions: 1
329 | :deletions: 1
330 | :changed_files: 1
331 |
--------------------------------------------------------------------------------
/spec/fixtures/file/pr_4.yml:
--------------------------------------------------------------------------------
1 | ---
2 | :url: https://api.github.com/repos/motemen/git-pr-release/pulls/4
3 | :id: 13997501
4 | :node_id: MDExOlB1bGxSZXF1ZXN0MTM5OTc1MDE=
5 | :html_url: https://github.com/motemen/git-pr-release/pull/4
6 | :diff_url: https://github.com/motemen/git-pr-release/pull/4.diff
7 | :patch_url: https://github.com/motemen/git-pr-release/pull/4.patch
8 | :issue_url: https://api.github.com/repos/motemen/git-pr-release/issues/4
9 | :number: 4
10 | :state: closed
11 | :locked: false
12 | :title: use user who create PR if there is no assignee
13 | :user:
14 | :login: hakobe
15 | :id: 6882
16 | :node_id: MDQ6VXNlcjY4ODI=
17 | :avatar_url: https://avatars0.githubusercontent.com/u/6882?v=4
18 | :gravatar_id: ''
19 | :url: https://api.github.com/users/hakobe
20 | :html_url: https://github.com/hakobe
21 | :followers_url: https://api.github.com/users/hakobe/followers
22 | :following_url: https://api.github.com/users/hakobe/following{/other_user}
23 | :gists_url: https://api.github.com/users/hakobe/gists{/gist_id}
24 | :starred_url: https://api.github.com/users/hakobe/starred{/owner}{/repo}
25 | :subscriptions_url: https://api.github.com/users/hakobe/subscriptions
26 | :organizations_url: https://api.github.com/users/hakobe/orgs
27 | :repos_url: https://api.github.com/users/hakobe/repos
28 | :events_url: https://api.github.com/users/hakobe/events{/privacy}
29 | :received_events_url: https://api.github.com/users/hakobe/received_events
30 | :type: User
31 | :site_admin: false
32 | :body: ''
33 | :created_at: 2014-03-26 10:17:02.000000000 Z
34 | :updated_at: 2014-07-13 11:45:52.000000000 Z
35 | :closed_at: 2014-03-26 10:49:03.000000000 Z
36 | :merged_at: 2014-03-26 10:49:03.000000000 Z
37 | :merge_commit_sha: 19dd4309bfac0b0358e7e87f48a007ef54634140
38 | :assignee:
39 | :assignees: []
40 | :requested_reviewers: []
41 | :requested_teams: []
42 | :labels: []
43 | :milestone:
44 | :commits_url: https://api.github.com/repos/motemen/git-pr-release/pulls/4/commits
45 | :review_comments_url: https://api.github.com/repos/motemen/git-pr-release/pulls/4/comments
46 | :review_comment_url: https://api.github.com/repos/motemen/git-pr-release/pulls/comments{/number}
47 | :comments_url: https://api.github.com/repos/motemen/git-pr-release/issues/4/comments
48 | :statuses_url: https://api.github.com/repos/motemen/git-pr-release/statuses/42bd43b80c973c8f348df3521745201be05bf194
49 | :head:
50 | :label: motemen:use-pr-cretor
51 | :ref: use-pr-cretor
52 | :sha: 42bd43b80c973c8f348df3521745201be05bf194
53 | :user:
54 | :login: motemen
55 | :id: 8465
56 | :node_id: MDQ6VXNlcjg0NjU=
57 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
58 | :gravatar_id: ''
59 | :url: https://api.github.com/users/motemen
60 | :html_url: https://github.com/motemen
61 | :followers_url: https://api.github.com/users/motemen/followers
62 | :following_url: https://api.github.com/users/motemen/following{/other_user}
63 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
64 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
65 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
66 | :organizations_url: https://api.github.com/users/motemen/orgs
67 | :repos_url: https://api.github.com/users/motemen/repos
68 | :events_url: https://api.github.com/users/motemen/events{/privacy}
69 | :received_events_url: https://api.github.com/users/motemen/received_events
70 | :type: User
71 | :site_admin: false
72 | :repo:
73 | :id: 15468156
74 | :node_id: MDEwOlJlcG9zaXRvcnkxNTQ2ODE1Ng==
75 | :name: git-pr-release
76 | :full_name: motemen/git-pr-release
77 | :private: false
78 | :owner:
79 | :login: motemen
80 | :id: 8465
81 | :node_id: MDQ6VXNlcjg0NjU=
82 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
83 | :gravatar_id: ''
84 | :url: https://api.github.com/users/motemen
85 | :html_url: https://github.com/motemen
86 | :followers_url: https://api.github.com/users/motemen/followers
87 | :following_url: https://api.github.com/users/motemen/following{/other_user}
88 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
89 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
90 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
91 | :organizations_url: https://api.github.com/users/motemen/orgs
92 | :repos_url: https://api.github.com/users/motemen/repos
93 | :events_url: https://api.github.com/users/motemen/events{/privacy}
94 | :received_events_url: https://api.github.com/users/motemen/received_events
95 | :type: User
96 | :site_admin: false
97 | :html_url: https://github.com/motemen/git-pr-release
98 | :description: Release pull request generator
99 | :fork: false
100 | :url: https://api.github.com/repos/motemen/git-pr-release
101 | :forks_url: https://api.github.com/repos/motemen/git-pr-release/forks
102 | :keys_url: https://api.github.com/repos/motemen/git-pr-release/keys{/key_id}
103 | :collaborators_url: https://api.github.com/repos/motemen/git-pr-release/collaborators{/collaborator}
104 | :teams_url: https://api.github.com/repos/motemen/git-pr-release/teams
105 | :hooks_url: https://api.github.com/repos/motemen/git-pr-release/hooks
106 | :issue_events_url: https://api.github.com/repos/motemen/git-pr-release/issues/events{/number}
107 | :events_url: https://api.github.com/repos/motemen/git-pr-release/events
108 | :assignees_url: https://api.github.com/repos/motemen/git-pr-release/assignees{/user}
109 | :branches_url: https://api.github.com/repos/motemen/git-pr-release/branches{/branch}
110 | :tags_url: https://api.github.com/repos/motemen/git-pr-release/tags
111 | :blobs_url: https://api.github.com/repos/motemen/git-pr-release/git/blobs{/sha}
112 | :git_tags_url: https://api.github.com/repos/motemen/git-pr-release/git/tags{/sha}
113 | :git_refs_url: https://api.github.com/repos/motemen/git-pr-release/git/refs{/sha}
114 | :trees_url: https://api.github.com/repos/motemen/git-pr-release/git/trees{/sha}
115 | :statuses_url: https://api.github.com/repos/motemen/git-pr-release/statuses/{sha}
116 | :languages_url: https://api.github.com/repos/motemen/git-pr-release/languages
117 | :stargazers_url: https://api.github.com/repos/motemen/git-pr-release/stargazers
118 | :contributors_url: https://api.github.com/repos/motemen/git-pr-release/contributors
119 | :subscribers_url: https://api.github.com/repos/motemen/git-pr-release/subscribers
120 | :subscription_url: https://api.github.com/repos/motemen/git-pr-release/subscription
121 | :commits_url: https://api.github.com/repos/motemen/git-pr-release/commits{/sha}
122 | :git_commits_url: https://api.github.com/repos/motemen/git-pr-release/git/commits{/sha}
123 | :comments_url: https://api.github.com/repos/motemen/git-pr-release/comments{/number}
124 | :issue_comment_url: https://api.github.com/repos/motemen/git-pr-release/issues/comments{/number}
125 | :contents_url: https://api.github.com/repos/motemen/git-pr-release/contents/{+path}
126 | :compare_url: https://api.github.com/repos/motemen/git-pr-release/compare/{base}...{head}
127 | :merges_url: https://api.github.com/repos/motemen/git-pr-release/merges
128 | :archive_url: https://api.github.com/repos/motemen/git-pr-release/{archive_format}{/ref}
129 | :downloads_url: https://api.github.com/repos/motemen/git-pr-release/downloads
130 | :issues_url: https://api.github.com/repos/motemen/git-pr-release/issues{/number}
131 | :pulls_url: https://api.github.com/repos/motemen/git-pr-release/pulls{/number}
132 | :milestones_url: https://api.github.com/repos/motemen/git-pr-release/milestones{/number}
133 | :notifications_url: https://api.github.com/repos/motemen/git-pr-release/notifications{?since,all,participating}
134 | :labels_url: https://api.github.com/repos/motemen/git-pr-release/labels{/name}
135 | :releases_url: https://api.github.com/repos/motemen/git-pr-release/releases{/id}
136 | :deployments_url: https://api.github.com/repos/motemen/git-pr-release/deployments
137 | :created_at: 2013-12-27 06:35:52.000000000 Z
138 | :updated_at: 2019-02-18 01:40:52.000000000 Z
139 | :pushed_at: 2018-11-15 10:45:07.000000000 Z
140 | :git_url: git://github.com/motemen/git-pr-release.git
141 | :ssh_url: git@github.com:motemen/git-pr-release.git
142 | :clone_url: https://github.com/motemen/git-pr-release.git
143 | :svn_url: https://github.com/motemen/git-pr-release
144 | :homepage: https://rubygems.org/gems/git-pr-release
145 | :size: 62
146 | :stargazers_count: 359
147 | :watchers_count: 359
148 | :language: Ruby
149 | :has_issues: true
150 | :has_projects: true
151 | :has_downloads: true
152 | :has_wiki: true
153 | :has_pages: false
154 | :forks_count: 28
155 | :mirror_url:
156 | :archived: false
157 | :open_issues_count: 6
158 | :license:
159 | :key: mit
160 | :name: MIT License
161 | :spdx_id: MIT
162 | :url: https://api.github.com/licenses/mit
163 | :node_id: MDc6TGljZW5zZTEz
164 | :forks: 28
165 | :open_issues: 6
166 | :watchers: 359
167 | :default_branch: master
168 | :base:
169 | :label: motemen:master
170 | :ref: master
171 | :sha: ad694b9c2b868e8801f9209f0ad5dd5458c49854
172 | :user:
173 | :login: motemen
174 | :id: 8465
175 | :node_id: MDQ6VXNlcjg0NjU=
176 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
177 | :gravatar_id: ''
178 | :url: https://api.github.com/users/motemen
179 | :html_url: https://github.com/motemen
180 | :followers_url: https://api.github.com/users/motemen/followers
181 | :following_url: https://api.github.com/users/motemen/following{/other_user}
182 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
183 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
184 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
185 | :organizations_url: https://api.github.com/users/motemen/orgs
186 | :repos_url: https://api.github.com/users/motemen/repos
187 | :events_url: https://api.github.com/users/motemen/events{/privacy}
188 | :received_events_url: https://api.github.com/users/motemen/received_events
189 | :type: User
190 | :site_admin: false
191 | :repo:
192 | :id: 15468156
193 | :node_id: MDEwOlJlcG9zaXRvcnkxNTQ2ODE1Ng==
194 | :name: git-pr-release
195 | :full_name: motemen/git-pr-release
196 | :private: false
197 | :owner:
198 | :login: motemen
199 | :id: 8465
200 | :node_id: MDQ6VXNlcjg0NjU=
201 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
202 | :gravatar_id: ''
203 | :url: https://api.github.com/users/motemen
204 | :html_url: https://github.com/motemen
205 | :followers_url: https://api.github.com/users/motemen/followers
206 | :following_url: https://api.github.com/users/motemen/following{/other_user}
207 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
208 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
209 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
210 | :organizations_url: https://api.github.com/users/motemen/orgs
211 | :repos_url: https://api.github.com/users/motemen/repos
212 | :events_url: https://api.github.com/users/motemen/events{/privacy}
213 | :received_events_url: https://api.github.com/users/motemen/received_events
214 | :type: User
215 | :site_admin: false
216 | :html_url: https://github.com/motemen/git-pr-release
217 | :description: Release pull request generator
218 | :fork: false
219 | :url: https://api.github.com/repos/motemen/git-pr-release
220 | :forks_url: https://api.github.com/repos/motemen/git-pr-release/forks
221 | :keys_url: https://api.github.com/repos/motemen/git-pr-release/keys{/key_id}
222 | :collaborators_url: https://api.github.com/repos/motemen/git-pr-release/collaborators{/collaborator}
223 | :teams_url: https://api.github.com/repos/motemen/git-pr-release/teams
224 | :hooks_url: https://api.github.com/repos/motemen/git-pr-release/hooks
225 | :issue_events_url: https://api.github.com/repos/motemen/git-pr-release/issues/events{/number}
226 | :events_url: https://api.github.com/repos/motemen/git-pr-release/events
227 | :assignees_url: https://api.github.com/repos/motemen/git-pr-release/assignees{/user}
228 | :branches_url: https://api.github.com/repos/motemen/git-pr-release/branches{/branch}
229 | :tags_url: https://api.github.com/repos/motemen/git-pr-release/tags
230 | :blobs_url: https://api.github.com/repos/motemen/git-pr-release/git/blobs{/sha}
231 | :git_tags_url: https://api.github.com/repos/motemen/git-pr-release/git/tags{/sha}
232 | :git_refs_url: https://api.github.com/repos/motemen/git-pr-release/git/refs{/sha}
233 | :trees_url: https://api.github.com/repos/motemen/git-pr-release/git/trees{/sha}
234 | :statuses_url: https://api.github.com/repos/motemen/git-pr-release/statuses/{sha}
235 | :languages_url: https://api.github.com/repos/motemen/git-pr-release/languages
236 | :stargazers_url: https://api.github.com/repos/motemen/git-pr-release/stargazers
237 | :contributors_url: https://api.github.com/repos/motemen/git-pr-release/contributors
238 | :subscribers_url: https://api.github.com/repos/motemen/git-pr-release/subscribers
239 | :subscription_url: https://api.github.com/repos/motemen/git-pr-release/subscription
240 | :commits_url: https://api.github.com/repos/motemen/git-pr-release/commits{/sha}
241 | :git_commits_url: https://api.github.com/repos/motemen/git-pr-release/git/commits{/sha}
242 | :comments_url: https://api.github.com/repos/motemen/git-pr-release/comments{/number}
243 | :issue_comment_url: https://api.github.com/repos/motemen/git-pr-release/issues/comments{/number}
244 | :contents_url: https://api.github.com/repos/motemen/git-pr-release/contents/{+path}
245 | :compare_url: https://api.github.com/repos/motemen/git-pr-release/compare/{base}...{head}
246 | :merges_url: https://api.github.com/repos/motemen/git-pr-release/merges
247 | :archive_url: https://api.github.com/repos/motemen/git-pr-release/{archive_format}{/ref}
248 | :downloads_url: https://api.github.com/repos/motemen/git-pr-release/downloads
249 | :issues_url: https://api.github.com/repos/motemen/git-pr-release/issues{/number}
250 | :pulls_url: https://api.github.com/repos/motemen/git-pr-release/pulls{/number}
251 | :milestones_url: https://api.github.com/repos/motemen/git-pr-release/milestones{/number}
252 | :notifications_url: https://api.github.com/repos/motemen/git-pr-release/notifications{?since,all,participating}
253 | :labels_url: https://api.github.com/repos/motemen/git-pr-release/labels{/name}
254 | :releases_url: https://api.github.com/repos/motemen/git-pr-release/releases{/id}
255 | :deployments_url: https://api.github.com/repos/motemen/git-pr-release/deployments
256 | :created_at: 2013-12-27 06:35:52.000000000 Z
257 | :updated_at: 2019-02-18 01:40:52.000000000 Z
258 | :pushed_at: 2018-11-15 10:45:07.000000000 Z
259 | :git_url: git://github.com/motemen/git-pr-release.git
260 | :ssh_url: git@github.com:motemen/git-pr-release.git
261 | :clone_url: https://github.com/motemen/git-pr-release.git
262 | :svn_url: https://github.com/motemen/git-pr-release
263 | :homepage: https://rubygems.org/gems/git-pr-release
264 | :size: 62
265 | :stargazers_count: 359
266 | :watchers_count: 359
267 | :language: Ruby
268 | :has_issues: true
269 | :has_projects: true
270 | :has_downloads: true
271 | :has_wiki: true
272 | :has_pages: false
273 | :forks_count: 28
274 | :mirror_url:
275 | :archived: false
276 | :open_issues_count: 6
277 | :license:
278 | :key: mit
279 | :name: MIT License
280 | :spdx_id: MIT
281 | :url: https://api.github.com/licenses/mit
282 | :node_id: MDc6TGljZW5zZTEz
283 | :forks: 28
284 | :open_issues: 6
285 | :watchers: 359
286 | :default_branch: master
287 | :_links:
288 | :self:
289 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/4
290 | :html:
291 | :href: https://github.com/motemen/git-pr-release/pull/4
292 | :issue:
293 | :href: https://api.github.com/repos/motemen/git-pr-release/issues/4
294 | :comments:
295 | :href: https://api.github.com/repos/motemen/git-pr-release/issues/4/comments
296 | :review_comments:
297 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/4/comments
298 | :review_comment:
299 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/comments{/number}
300 | :commits:
301 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/4/commits
302 | :statuses:
303 | :href: https://api.github.com/repos/motemen/git-pr-release/statuses/42bd43b80c973c8f348df3521745201be05bf194
304 | :author_association: COLLABORATOR
305 | :merged: true
306 | :mergeable:
307 | :rebaseable:
308 | :mergeable_state: unknown
309 | :merged_by:
310 | :login: motemen
311 | :id: 8465
312 | :node_id: MDQ6VXNlcjg0NjU=
313 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
314 | :gravatar_id: ''
315 | :url: https://api.github.com/users/motemen
316 | :html_url: https://github.com/motemen
317 | :followers_url: https://api.github.com/users/motemen/followers
318 | :following_url: https://api.github.com/users/motemen/following{/other_user}
319 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
320 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
321 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
322 | :organizations_url: https://api.github.com/users/motemen/orgs
323 | :repos_url: https://api.github.com/users/motemen/repos
324 | :events_url: https://api.github.com/users/motemen/events{/privacy}
325 | :received_events_url: https://api.github.com/users/motemen/received_events
326 | :type: User
327 | :site_admin: false
328 | :comments: 1
329 | :review_comments: 0
330 | :maintainer_can_modify: false
331 | :commits: 1
332 | :additions: 2
333 | :deletions: 1
334 | :changed_files: 1
335 |
--------------------------------------------------------------------------------
/spec/fixtures/file/pr_3.yml:
--------------------------------------------------------------------------------
1 | ---
2 | :url: https://api.github.com/repos/motemen/git-pr-release/pulls/3
3 | :id: 13365285
4 | :node_id: MDExOlB1bGxSZXF1ZXN0MTMzNjUyODU=
5 | :html_url: https://github.com/motemen/git-pr-release/pull/3
6 | :diff_url: https://github.com/motemen/git-pr-release/pull/3.diff
7 | :patch_url: https://github.com/motemen/git-pr-release/pull/3.patch
8 | :issue_url: https://api.github.com/repos/motemen/git-pr-release/issues/3
9 | :number: 3
10 | :state: closed
11 | :locked: false
12 | :title: Provides a creating release pull-request object for template
13 | :user:
14 | :login: hakobe
15 | :id: 6882
16 | :node_id: MDQ6VXNlcjY4ODI=
17 | :avatar_url: https://avatars0.githubusercontent.com/u/6882?v=4
18 | :gravatar_id: ''
19 | :url: https://api.github.com/users/hakobe
20 | :html_url: https://github.com/hakobe
21 | :followers_url: https://api.github.com/users/hakobe/followers
22 | :following_url: https://api.github.com/users/hakobe/following{/other_user}
23 | :gists_url: https://api.github.com/users/hakobe/gists{/gist_id}
24 | :starred_url: https://api.github.com/users/hakobe/starred{/owner}{/repo}
25 | :subscriptions_url: https://api.github.com/users/hakobe/subscriptions
26 | :organizations_url: https://api.github.com/users/hakobe/orgs
27 | :repos_url: https://api.github.com/users/hakobe/repos
28 | :events_url: https://api.github.com/users/hakobe/events{/privacy}
29 | :received_events_url: https://api.github.com/users/hakobe/received_events
30 | :type: User
31 | :site_admin: false
32 | :body: "### Problem\n\nCreating release pull-request info(e.g. url of pull-request)
33 | is not available in template. \n### Solution\n\nEnsures a release pull-request has
34 | already been created before generating a pull-request body.\n"
35 | :created_at: 2014-03-10 10:25:33.000000000 Z
36 | :updated_at: 2014-07-03 00:16:55.000000000 Z
37 | :closed_at: 2014-03-13 11:42:18.000000000 Z
38 | :merged_at: 2014-03-13 11:42:18.000000000 Z
39 | :merge_commit_sha: 2ff5c670d9bebde490a6651f185ecba6cb09b48d
40 | :assignee:
41 | :assignees: []
42 | :requested_reviewers: []
43 | :requested_teams: []
44 | :labels: []
45 | :milestone:
46 | :commits_url: https://api.github.com/repos/motemen/git-pr-release/pulls/3/commits
47 | :review_comments_url: https://api.github.com/repos/motemen/git-pr-release/pulls/3/comments
48 | :review_comment_url: https://api.github.com/repos/motemen/git-pr-release/pulls/comments{/number}
49 | :comments_url: https://api.github.com/repos/motemen/git-pr-release/issues/3/comments
50 | :statuses_url: https://api.github.com/repos/motemen/git-pr-release/statuses/5c977a1827387ac7b7a85c7b827ee119165f1823
51 | :head:
52 | :label: hakobe:prepare-pr-for-template
53 | :ref: prepare-pr-for-template
54 | :sha: 5c977a1827387ac7b7a85c7b827ee119165f1823
55 | :user:
56 | :login: hakobe
57 | :id: 6882
58 | :node_id: MDQ6VXNlcjY4ODI=
59 | :avatar_url: https://avatars0.githubusercontent.com/u/6882?v=4
60 | :gravatar_id: ''
61 | :url: https://api.github.com/users/hakobe
62 | :html_url: https://github.com/hakobe
63 | :followers_url: https://api.github.com/users/hakobe/followers
64 | :following_url: https://api.github.com/users/hakobe/following{/other_user}
65 | :gists_url: https://api.github.com/users/hakobe/gists{/gist_id}
66 | :starred_url: https://api.github.com/users/hakobe/starred{/owner}{/repo}
67 | :subscriptions_url: https://api.github.com/users/hakobe/subscriptions
68 | :organizations_url: https://api.github.com/users/hakobe/orgs
69 | :repos_url: https://api.github.com/users/hakobe/repos
70 | :events_url: https://api.github.com/users/hakobe/events{/privacy}
71 | :received_events_url: https://api.github.com/users/hakobe/received_events
72 | :type: User
73 | :site_admin: false
74 | :repo:
75 | :id: 17425170
76 | :node_id: MDEwOlJlcG9zaXRvcnkxNzQyNTE3MA==
77 | :name: git-pr-release
78 | :full_name: hakobe/git-pr-release
79 | :private: false
80 | :owner:
81 | :login: hakobe
82 | :id: 6882
83 | :node_id: MDQ6VXNlcjY4ODI=
84 | :avatar_url: https://avatars0.githubusercontent.com/u/6882?v=4
85 | :gravatar_id: ''
86 | :url: https://api.github.com/users/hakobe
87 | :html_url: https://github.com/hakobe
88 | :followers_url: https://api.github.com/users/hakobe/followers
89 | :following_url: https://api.github.com/users/hakobe/following{/other_user}
90 | :gists_url: https://api.github.com/users/hakobe/gists{/gist_id}
91 | :starred_url: https://api.github.com/users/hakobe/starred{/owner}{/repo}
92 | :subscriptions_url: https://api.github.com/users/hakobe/subscriptions
93 | :organizations_url: https://api.github.com/users/hakobe/orgs
94 | :repos_url: https://api.github.com/users/hakobe/repos
95 | :events_url: https://api.github.com/users/hakobe/events{/privacy}
96 | :received_events_url: https://api.github.com/users/hakobe/received_events
97 | :type: User
98 | :site_admin: false
99 | :html_url: https://github.com/hakobe/git-pr-release
100 | :description: Creates a pull request which summarizes feature branches that are
101 | to be released into production
102 | :fork: true
103 | :url: https://api.github.com/repos/hakobe/git-pr-release
104 | :forks_url: https://api.github.com/repos/hakobe/git-pr-release/forks
105 | :keys_url: https://api.github.com/repos/hakobe/git-pr-release/keys{/key_id}
106 | :collaborators_url: https://api.github.com/repos/hakobe/git-pr-release/collaborators{/collaborator}
107 | :teams_url: https://api.github.com/repos/hakobe/git-pr-release/teams
108 | :hooks_url: https://api.github.com/repos/hakobe/git-pr-release/hooks
109 | :issue_events_url: https://api.github.com/repos/hakobe/git-pr-release/issues/events{/number}
110 | :events_url: https://api.github.com/repos/hakobe/git-pr-release/events
111 | :assignees_url: https://api.github.com/repos/hakobe/git-pr-release/assignees{/user}
112 | :branches_url: https://api.github.com/repos/hakobe/git-pr-release/branches{/branch}
113 | :tags_url: https://api.github.com/repos/hakobe/git-pr-release/tags
114 | :blobs_url: https://api.github.com/repos/hakobe/git-pr-release/git/blobs{/sha}
115 | :git_tags_url: https://api.github.com/repos/hakobe/git-pr-release/git/tags{/sha}
116 | :git_refs_url: https://api.github.com/repos/hakobe/git-pr-release/git/refs{/sha}
117 | :trees_url: https://api.github.com/repos/hakobe/git-pr-release/git/trees{/sha}
118 | :statuses_url: https://api.github.com/repos/hakobe/git-pr-release/statuses/{sha}
119 | :languages_url: https://api.github.com/repos/hakobe/git-pr-release/languages
120 | :stargazers_url: https://api.github.com/repos/hakobe/git-pr-release/stargazers
121 | :contributors_url: https://api.github.com/repos/hakobe/git-pr-release/contributors
122 | :subscribers_url: https://api.github.com/repos/hakobe/git-pr-release/subscribers
123 | :subscription_url: https://api.github.com/repos/hakobe/git-pr-release/subscription
124 | :commits_url: https://api.github.com/repos/hakobe/git-pr-release/commits{/sha}
125 | :git_commits_url: https://api.github.com/repos/hakobe/git-pr-release/git/commits{/sha}
126 | :comments_url: https://api.github.com/repos/hakobe/git-pr-release/comments{/number}
127 | :issue_comment_url: https://api.github.com/repos/hakobe/git-pr-release/issues/comments{/number}
128 | :contents_url: https://api.github.com/repos/hakobe/git-pr-release/contents/{+path}
129 | :compare_url: https://api.github.com/repos/hakobe/git-pr-release/compare/{base}...{head}
130 | :merges_url: https://api.github.com/repos/hakobe/git-pr-release/merges
131 | :archive_url: https://api.github.com/repos/hakobe/git-pr-release/{archive_format}{/ref}
132 | :downloads_url: https://api.github.com/repos/hakobe/git-pr-release/downloads
133 | :issues_url: https://api.github.com/repos/hakobe/git-pr-release/issues{/number}
134 | :pulls_url: https://api.github.com/repos/hakobe/git-pr-release/pulls{/number}
135 | :milestones_url: https://api.github.com/repos/hakobe/git-pr-release/milestones{/number}
136 | :notifications_url: https://api.github.com/repos/hakobe/git-pr-release/notifications{?since,all,participating}
137 | :labels_url: https://api.github.com/repos/hakobe/git-pr-release/labels{/name}
138 | :releases_url: https://api.github.com/repos/hakobe/git-pr-release/releases{/id}
139 | :deployments_url: https://api.github.com/repos/hakobe/git-pr-release/deployments
140 | :created_at: 2014-03-05 02:20:59.000000000 Z
141 | :updated_at: 2014-05-13 01:42:58.000000000 Z
142 | :pushed_at: 2014-05-12 11:42:34.000000000 Z
143 | :git_url: git://github.com/hakobe/git-pr-release.git
144 | :ssh_url: git@github.com:hakobe/git-pr-release.git
145 | :clone_url: https://github.com/hakobe/git-pr-release.git
146 | :svn_url: https://github.com/hakobe/git-pr-release
147 | :homepage: https://rubygems.org/gems/git-pr-release
148 | :size: 236
149 | :stargazers_count: 0
150 | :watchers_count: 0
151 | :language: Ruby
152 | :has_issues: false
153 | :has_projects: true
154 | :has_downloads: true
155 | :has_wiki: true
156 | :has_pages: false
157 | :forks_count: 0
158 | :mirror_url:
159 | :archived: false
160 | :open_issues_count: 0
161 | :license:
162 | :forks: 0
163 | :open_issues: 0
164 | :watchers: 0
165 | :default_branch: master
166 | :base:
167 | :label: motemen:master
168 | :ref: master
169 | :sha: b620bead10831d2e4e15be392e0a435d3470a0ad
170 | :user:
171 | :login: motemen
172 | :id: 8465
173 | :node_id: MDQ6VXNlcjg0NjU=
174 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
175 | :gravatar_id: ''
176 | :url: https://api.github.com/users/motemen
177 | :html_url: https://github.com/motemen
178 | :followers_url: https://api.github.com/users/motemen/followers
179 | :following_url: https://api.github.com/users/motemen/following{/other_user}
180 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
181 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
182 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
183 | :organizations_url: https://api.github.com/users/motemen/orgs
184 | :repos_url: https://api.github.com/users/motemen/repos
185 | :events_url: https://api.github.com/users/motemen/events{/privacy}
186 | :received_events_url: https://api.github.com/users/motemen/received_events
187 | :type: User
188 | :site_admin: false
189 | :repo:
190 | :id: 15468156
191 | :node_id: MDEwOlJlcG9zaXRvcnkxNTQ2ODE1Ng==
192 | :name: git-pr-release
193 | :full_name: motemen/git-pr-release
194 | :private: false
195 | :owner:
196 | :login: motemen
197 | :id: 8465
198 | :node_id: MDQ6VXNlcjg0NjU=
199 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
200 | :gravatar_id: ''
201 | :url: https://api.github.com/users/motemen
202 | :html_url: https://github.com/motemen
203 | :followers_url: https://api.github.com/users/motemen/followers
204 | :following_url: https://api.github.com/users/motemen/following{/other_user}
205 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
206 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
207 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
208 | :organizations_url: https://api.github.com/users/motemen/orgs
209 | :repos_url: https://api.github.com/users/motemen/repos
210 | :events_url: https://api.github.com/users/motemen/events{/privacy}
211 | :received_events_url: https://api.github.com/users/motemen/received_events
212 | :type: User
213 | :site_admin: false
214 | :html_url: https://github.com/motemen/git-pr-release
215 | :description: Release pull request generator
216 | :fork: false
217 | :url: https://api.github.com/repos/motemen/git-pr-release
218 | :forks_url: https://api.github.com/repos/motemen/git-pr-release/forks
219 | :keys_url: https://api.github.com/repos/motemen/git-pr-release/keys{/key_id}
220 | :collaborators_url: https://api.github.com/repos/motemen/git-pr-release/collaborators{/collaborator}
221 | :teams_url: https://api.github.com/repos/motemen/git-pr-release/teams
222 | :hooks_url: https://api.github.com/repos/motemen/git-pr-release/hooks
223 | :issue_events_url: https://api.github.com/repos/motemen/git-pr-release/issues/events{/number}
224 | :events_url: https://api.github.com/repos/motemen/git-pr-release/events
225 | :assignees_url: https://api.github.com/repos/motemen/git-pr-release/assignees{/user}
226 | :branches_url: https://api.github.com/repos/motemen/git-pr-release/branches{/branch}
227 | :tags_url: https://api.github.com/repos/motemen/git-pr-release/tags
228 | :blobs_url: https://api.github.com/repos/motemen/git-pr-release/git/blobs{/sha}
229 | :git_tags_url: https://api.github.com/repos/motemen/git-pr-release/git/tags{/sha}
230 | :git_refs_url: https://api.github.com/repos/motemen/git-pr-release/git/refs{/sha}
231 | :trees_url: https://api.github.com/repos/motemen/git-pr-release/git/trees{/sha}
232 | :statuses_url: https://api.github.com/repos/motemen/git-pr-release/statuses/{sha}
233 | :languages_url: https://api.github.com/repos/motemen/git-pr-release/languages
234 | :stargazers_url: https://api.github.com/repos/motemen/git-pr-release/stargazers
235 | :contributors_url: https://api.github.com/repos/motemen/git-pr-release/contributors
236 | :subscribers_url: https://api.github.com/repos/motemen/git-pr-release/subscribers
237 | :subscription_url: https://api.github.com/repos/motemen/git-pr-release/subscription
238 | :commits_url: https://api.github.com/repos/motemen/git-pr-release/commits{/sha}
239 | :git_commits_url: https://api.github.com/repos/motemen/git-pr-release/git/commits{/sha}
240 | :comments_url: https://api.github.com/repos/motemen/git-pr-release/comments{/number}
241 | :issue_comment_url: https://api.github.com/repos/motemen/git-pr-release/issues/comments{/number}
242 | :contents_url: https://api.github.com/repos/motemen/git-pr-release/contents/{+path}
243 | :compare_url: https://api.github.com/repos/motemen/git-pr-release/compare/{base}...{head}
244 | :merges_url: https://api.github.com/repos/motemen/git-pr-release/merges
245 | :archive_url: https://api.github.com/repos/motemen/git-pr-release/{archive_format}{/ref}
246 | :downloads_url: https://api.github.com/repos/motemen/git-pr-release/downloads
247 | :issues_url: https://api.github.com/repos/motemen/git-pr-release/issues{/number}
248 | :pulls_url: https://api.github.com/repos/motemen/git-pr-release/pulls{/number}
249 | :milestones_url: https://api.github.com/repos/motemen/git-pr-release/milestones{/number}
250 | :notifications_url: https://api.github.com/repos/motemen/git-pr-release/notifications{?since,all,participating}
251 | :labels_url: https://api.github.com/repos/motemen/git-pr-release/labels{/name}
252 | :releases_url: https://api.github.com/repos/motemen/git-pr-release/releases{/id}
253 | :deployments_url: https://api.github.com/repos/motemen/git-pr-release/deployments
254 | :created_at: 2013-12-27 06:35:52.000000000 Z
255 | :updated_at: 2019-02-18 01:40:52.000000000 Z
256 | :pushed_at: 2018-11-15 10:45:07.000000000 Z
257 | :git_url: git://github.com/motemen/git-pr-release.git
258 | :ssh_url: git@github.com:motemen/git-pr-release.git
259 | :clone_url: https://github.com/motemen/git-pr-release.git
260 | :svn_url: https://github.com/motemen/git-pr-release
261 | :homepage: https://rubygems.org/gems/git-pr-release
262 | :size: 62
263 | :stargazers_count: 359
264 | :watchers_count: 359
265 | :language: Ruby
266 | :has_issues: true
267 | :has_projects: true
268 | :has_downloads: true
269 | :has_wiki: true
270 | :has_pages: false
271 | :forks_count: 28
272 | :mirror_url:
273 | :archived: false
274 | :open_issues_count: 6
275 | :license:
276 | :key: mit
277 | :name: MIT License
278 | :spdx_id: MIT
279 | :url: https://api.github.com/licenses/mit
280 | :node_id: MDc6TGljZW5zZTEz
281 | :forks: 28
282 | :open_issues: 6
283 | :watchers: 359
284 | :default_branch: master
285 | :_links:
286 | :self:
287 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/3
288 | :html:
289 | :href: https://github.com/motemen/git-pr-release/pull/3
290 | :issue:
291 | :href: https://api.github.com/repos/motemen/git-pr-release/issues/3
292 | :comments:
293 | :href: https://api.github.com/repos/motemen/git-pr-release/issues/3/comments
294 | :review_comments:
295 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/3/comments
296 | :review_comment:
297 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/comments{/number}
298 | :commits:
299 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/3/commits
300 | :statuses:
301 | :href: https://api.github.com/repos/motemen/git-pr-release/statuses/5c977a1827387ac7b7a85c7b827ee119165f1823
302 | :author_association: COLLABORATOR
303 | :merged: true
304 | :mergeable:
305 | :rebaseable:
306 | :mergeable_state: unknown
307 | :merged_by:
308 | :login: motemen
309 | :id: 8465
310 | :node_id: MDQ6VXNlcjg0NjU=
311 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
312 | :gravatar_id: ''
313 | :url: https://api.github.com/users/motemen
314 | :html_url: https://github.com/motemen
315 | :followers_url: https://api.github.com/users/motemen/followers
316 | :following_url: https://api.github.com/users/motemen/following{/other_user}
317 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
318 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
319 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
320 | :organizations_url: https://api.github.com/users/motemen/orgs
321 | :repos_url: https://api.github.com/users/motemen/repos
322 | :events_url: https://api.github.com/users/motemen/events{/privacy}
323 | :received_events_url: https://api.github.com/users/motemen/received_events
324 | :type: User
325 | :site_admin: false
326 | :comments: 3
327 | :review_comments: 0
328 | :maintainer_can_modify: false
329 | :commits: 3
330 | :additions: 54
331 | :deletions: 38
332 | :changed_files: 1
333 |
--------------------------------------------------------------------------------
/spec/fixtures/file/pr_6.yml:
--------------------------------------------------------------------------------
1 | ---
2 | :url: https://api.github.com/repos/motemen/git-pr-release/pulls/6
3 | :id: 16585502
4 | :node_id: MDExOlB1bGxSZXF1ZXN0MTY1ODU1MDI=
5 | :html_url: https://github.com/motemen/git-pr-release/pull/6
6 | :diff_url: https://github.com/motemen/git-pr-release/pull/6.diff
7 | :patch_url: https://github.com/motemen/git-pr-release/pull/6.patch
8 | :issue_url: https://api.github.com/repos/motemen/git-pr-release/issues/6
9 | :number: 6
10 | :state: closed
11 | :locked: false
12 | :title: Support two factor auth
13 | :user:
14 | :login: ninjinkun
15 | :id: 113420
16 | :node_id: MDQ6VXNlcjExMzQyMA==
17 | :avatar_url: https://avatars1.githubusercontent.com/u/113420?v=4
18 | :gravatar_id: ''
19 | :url: https://api.github.com/users/ninjinkun
20 | :html_url: https://github.com/ninjinkun
21 | :followers_url: https://api.github.com/users/ninjinkun/followers
22 | :following_url: https://api.github.com/users/ninjinkun/following{/other_user}
23 | :gists_url: https://api.github.com/users/ninjinkun/gists{/gist_id}
24 | :starred_url: https://api.github.com/users/ninjinkun/starred{/owner}{/repo}
25 | :subscriptions_url: https://api.github.com/users/ninjinkun/subscriptions
26 | :organizations_url: https://api.github.com/users/ninjinkun/orgs
27 | :repos_url: https://api.github.com/users/ninjinkun/repos
28 | :events_url: https://api.github.com/users/ninjinkun/events{/privacy}
29 | :received_events_url: https://api.github.com/users/ninjinkun/received_events
30 | :type: User
31 | :site_admin: false
32 | :body: 'Currently `git-pr-release` don''t support two factor auth in interactive configuration
33 | mode. This patch shows prompt `two-factor authentication code?` and try to request
34 | again.
35 |
36 | '
37 | :created_at: 2014-06-02 06:55:44.000000000 Z
38 | :updated_at: 2014-06-21 05:22:22.000000000 Z
39 | :closed_at: 2014-06-02 08:02:02.000000000 Z
40 | :merged_at: 2014-06-02 08:02:02.000000000 Z
41 | :merge_commit_sha: c66dfa90eed853507ae0e2876df3cb4dc7ce5286
42 | :assignee:
43 | :assignees: []
44 | :requested_reviewers: []
45 | :requested_teams: []
46 | :labels: []
47 | :milestone:
48 | :commits_url: https://api.github.com/repos/motemen/git-pr-release/pulls/6/commits
49 | :review_comments_url: https://api.github.com/repos/motemen/git-pr-release/pulls/6/comments
50 | :review_comment_url: https://api.github.com/repos/motemen/git-pr-release/pulls/comments{/number}
51 | :comments_url: https://api.github.com/repos/motemen/git-pr-release/issues/6/comments
52 | :statuses_url: https://api.github.com/repos/motemen/git-pr-release/statuses/67d936fb57851f85db28249e91dfa0d8ccfe530b
53 | :head:
54 | :label: ninjinkun:support-two-factor-auth
55 | :ref: support-two-factor-auth
56 | :sha: 67d936fb57851f85db28249e91dfa0d8ccfe530b
57 | :user:
58 | :login: ninjinkun
59 | :id: 113420
60 | :node_id: MDQ6VXNlcjExMzQyMA==
61 | :avatar_url: https://avatars1.githubusercontent.com/u/113420?v=4
62 | :gravatar_id: ''
63 | :url: https://api.github.com/users/ninjinkun
64 | :html_url: https://github.com/ninjinkun
65 | :followers_url: https://api.github.com/users/ninjinkun/followers
66 | :following_url: https://api.github.com/users/ninjinkun/following{/other_user}
67 | :gists_url: https://api.github.com/users/ninjinkun/gists{/gist_id}
68 | :starred_url: https://api.github.com/users/ninjinkun/starred{/owner}{/repo}
69 | :subscriptions_url: https://api.github.com/users/ninjinkun/subscriptions
70 | :organizations_url: https://api.github.com/users/ninjinkun/orgs
71 | :repos_url: https://api.github.com/users/ninjinkun/repos
72 | :events_url: https://api.github.com/users/ninjinkun/events{/privacy}
73 | :received_events_url: https://api.github.com/users/ninjinkun/received_events
74 | :type: User
75 | :site_admin: false
76 | :repo:
77 | :id: 20395231
78 | :node_id: MDEwOlJlcG9zaXRvcnkyMDM5NTIzMQ==
79 | :name: git-pr-release
80 | :full_name: ninjinkun/git-pr-release
81 | :private: false
82 | :owner:
83 | :login: ninjinkun
84 | :id: 113420
85 | :node_id: MDQ6VXNlcjExMzQyMA==
86 | :avatar_url: https://avatars1.githubusercontent.com/u/113420?v=4
87 | :gravatar_id: ''
88 | :url: https://api.github.com/users/ninjinkun
89 | :html_url: https://github.com/ninjinkun
90 | :followers_url: https://api.github.com/users/ninjinkun/followers
91 | :following_url: https://api.github.com/users/ninjinkun/following{/other_user}
92 | :gists_url: https://api.github.com/users/ninjinkun/gists{/gist_id}
93 | :starred_url: https://api.github.com/users/ninjinkun/starred{/owner}{/repo}
94 | :subscriptions_url: https://api.github.com/users/ninjinkun/subscriptions
95 | :organizations_url: https://api.github.com/users/ninjinkun/orgs
96 | :repos_url: https://api.github.com/users/ninjinkun/repos
97 | :events_url: https://api.github.com/users/ninjinkun/events{/privacy}
98 | :received_events_url: https://api.github.com/users/ninjinkun/received_events
99 | :type: User
100 | :site_admin: false
101 | :html_url: https://github.com/ninjinkun/git-pr-release
102 | :description: Creates a pull request which summarizes feature branches that are
103 | to be released into production
104 | :fork: true
105 | :url: https://api.github.com/repos/ninjinkun/git-pr-release
106 | :forks_url: https://api.github.com/repos/ninjinkun/git-pr-release/forks
107 | :keys_url: https://api.github.com/repos/ninjinkun/git-pr-release/keys{/key_id}
108 | :collaborators_url: https://api.github.com/repos/ninjinkun/git-pr-release/collaborators{/collaborator}
109 | :teams_url: https://api.github.com/repos/ninjinkun/git-pr-release/teams
110 | :hooks_url: https://api.github.com/repos/ninjinkun/git-pr-release/hooks
111 | :issue_events_url: https://api.github.com/repos/ninjinkun/git-pr-release/issues/events{/number}
112 | :events_url: https://api.github.com/repos/ninjinkun/git-pr-release/events
113 | :assignees_url: https://api.github.com/repos/ninjinkun/git-pr-release/assignees{/user}
114 | :branches_url: https://api.github.com/repos/ninjinkun/git-pr-release/branches{/branch}
115 | :tags_url: https://api.github.com/repos/ninjinkun/git-pr-release/tags
116 | :blobs_url: https://api.github.com/repos/ninjinkun/git-pr-release/git/blobs{/sha}
117 | :git_tags_url: https://api.github.com/repos/ninjinkun/git-pr-release/git/tags{/sha}
118 | :git_refs_url: https://api.github.com/repos/ninjinkun/git-pr-release/git/refs{/sha}
119 | :trees_url: https://api.github.com/repos/ninjinkun/git-pr-release/git/trees{/sha}
120 | :statuses_url: https://api.github.com/repos/ninjinkun/git-pr-release/statuses/{sha}
121 | :languages_url: https://api.github.com/repos/ninjinkun/git-pr-release/languages
122 | :stargazers_url: https://api.github.com/repos/ninjinkun/git-pr-release/stargazers
123 | :contributors_url: https://api.github.com/repos/ninjinkun/git-pr-release/contributors
124 | :subscribers_url: https://api.github.com/repos/ninjinkun/git-pr-release/subscribers
125 | :subscription_url: https://api.github.com/repos/ninjinkun/git-pr-release/subscription
126 | :commits_url: https://api.github.com/repos/ninjinkun/git-pr-release/commits{/sha}
127 | :git_commits_url: https://api.github.com/repos/ninjinkun/git-pr-release/git/commits{/sha}
128 | :comments_url: https://api.github.com/repos/ninjinkun/git-pr-release/comments{/number}
129 | :issue_comment_url: https://api.github.com/repos/ninjinkun/git-pr-release/issues/comments{/number}
130 | :contents_url: https://api.github.com/repos/ninjinkun/git-pr-release/contents/{+path}
131 | :compare_url: https://api.github.com/repos/ninjinkun/git-pr-release/compare/{base}...{head}
132 | :merges_url: https://api.github.com/repos/ninjinkun/git-pr-release/merges
133 | :archive_url: https://api.github.com/repos/ninjinkun/git-pr-release/{archive_format}{/ref}
134 | :downloads_url: https://api.github.com/repos/ninjinkun/git-pr-release/downloads
135 | :issues_url: https://api.github.com/repos/ninjinkun/git-pr-release/issues{/number}
136 | :pulls_url: https://api.github.com/repos/ninjinkun/git-pr-release/pulls{/number}
137 | :milestones_url: https://api.github.com/repos/ninjinkun/git-pr-release/milestones{/number}
138 | :notifications_url: https://api.github.com/repos/ninjinkun/git-pr-release/notifications{?since,all,participating}
139 | :labels_url: https://api.github.com/repos/ninjinkun/git-pr-release/labels{/name}
140 | :releases_url: https://api.github.com/repos/ninjinkun/git-pr-release/releases{/id}
141 | :deployments_url: https://api.github.com/repos/ninjinkun/git-pr-release/deployments
142 | :created_at: 2014-06-02 06:41:45.000000000 Z
143 | :updated_at: 2014-06-03 06:26:14.000000000 Z
144 | :pushed_at: 2014-06-03 06:26:13.000000000 Z
145 | :git_url: git://github.com/ninjinkun/git-pr-release.git
146 | :ssh_url: git@github.com:ninjinkun/git-pr-release.git
147 | :clone_url: https://github.com/ninjinkun/git-pr-release.git
148 | :svn_url: https://github.com/ninjinkun/git-pr-release
149 | :homepage: https://rubygems.org/gems/git-pr-release
150 | :size: 192
151 | :stargazers_count: 0
152 | :watchers_count: 0
153 | :language: Ruby
154 | :has_issues: false
155 | :has_projects: true
156 | :has_downloads: true
157 | :has_wiki: true
158 | :has_pages: false
159 | :forks_count: 0
160 | :mirror_url:
161 | :archived: false
162 | :open_issues_count: 0
163 | :license:
164 | :key: mit
165 | :name: MIT License
166 | :spdx_id: MIT
167 | :url: https://api.github.com/licenses/mit
168 | :node_id: MDc6TGljZW5zZTEz
169 | :forks: 0
170 | :open_issues: 0
171 | :watchers: 0
172 | :default_branch: master
173 | :base:
174 | :label: motemen:master
175 | :ref: master
176 | :sha: a0d1fd72732d705270878d22601c44e0533641e1
177 | :user:
178 | :login: motemen
179 | :id: 8465
180 | :node_id: MDQ6VXNlcjg0NjU=
181 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
182 | :gravatar_id: ''
183 | :url: https://api.github.com/users/motemen
184 | :html_url: https://github.com/motemen
185 | :followers_url: https://api.github.com/users/motemen/followers
186 | :following_url: https://api.github.com/users/motemen/following{/other_user}
187 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
188 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
189 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
190 | :organizations_url: https://api.github.com/users/motemen/orgs
191 | :repos_url: https://api.github.com/users/motemen/repos
192 | :events_url: https://api.github.com/users/motemen/events{/privacy}
193 | :received_events_url: https://api.github.com/users/motemen/received_events
194 | :type: User
195 | :site_admin: false
196 | :repo:
197 | :id: 15468156
198 | :node_id: MDEwOlJlcG9zaXRvcnkxNTQ2ODE1Ng==
199 | :name: git-pr-release
200 | :full_name: motemen/git-pr-release
201 | :private: false
202 | :owner:
203 | :login: motemen
204 | :id: 8465
205 | :node_id: MDQ6VXNlcjg0NjU=
206 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
207 | :gravatar_id: ''
208 | :url: https://api.github.com/users/motemen
209 | :html_url: https://github.com/motemen
210 | :followers_url: https://api.github.com/users/motemen/followers
211 | :following_url: https://api.github.com/users/motemen/following{/other_user}
212 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
213 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
214 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
215 | :organizations_url: https://api.github.com/users/motemen/orgs
216 | :repos_url: https://api.github.com/users/motemen/repos
217 | :events_url: https://api.github.com/users/motemen/events{/privacy}
218 | :received_events_url: https://api.github.com/users/motemen/received_events
219 | :type: User
220 | :site_admin: false
221 | :html_url: https://github.com/motemen/git-pr-release
222 | :description: Release pull request generator
223 | :fork: false
224 | :url: https://api.github.com/repos/motemen/git-pr-release
225 | :forks_url: https://api.github.com/repos/motemen/git-pr-release/forks
226 | :keys_url: https://api.github.com/repos/motemen/git-pr-release/keys{/key_id}
227 | :collaborators_url: https://api.github.com/repos/motemen/git-pr-release/collaborators{/collaborator}
228 | :teams_url: https://api.github.com/repos/motemen/git-pr-release/teams
229 | :hooks_url: https://api.github.com/repos/motemen/git-pr-release/hooks
230 | :issue_events_url: https://api.github.com/repos/motemen/git-pr-release/issues/events{/number}
231 | :events_url: https://api.github.com/repos/motemen/git-pr-release/events
232 | :assignees_url: https://api.github.com/repos/motemen/git-pr-release/assignees{/user}
233 | :branches_url: https://api.github.com/repos/motemen/git-pr-release/branches{/branch}
234 | :tags_url: https://api.github.com/repos/motemen/git-pr-release/tags
235 | :blobs_url: https://api.github.com/repos/motemen/git-pr-release/git/blobs{/sha}
236 | :git_tags_url: https://api.github.com/repos/motemen/git-pr-release/git/tags{/sha}
237 | :git_refs_url: https://api.github.com/repos/motemen/git-pr-release/git/refs{/sha}
238 | :trees_url: https://api.github.com/repos/motemen/git-pr-release/git/trees{/sha}
239 | :statuses_url: https://api.github.com/repos/motemen/git-pr-release/statuses/{sha}
240 | :languages_url: https://api.github.com/repos/motemen/git-pr-release/languages
241 | :stargazers_url: https://api.github.com/repos/motemen/git-pr-release/stargazers
242 | :contributors_url: https://api.github.com/repos/motemen/git-pr-release/contributors
243 | :subscribers_url: https://api.github.com/repos/motemen/git-pr-release/subscribers
244 | :subscription_url: https://api.github.com/repos/motemen/git-pr-release/subscription
245 | :commits_url: https://api.github.com/repos/motemen/git-pr-release/commits{/sha}
246 | :git_commits_url: https://api.github.com/repos/motemen/git-pr-release/git/commits{/sha}
247 | :comments_url: https://api.github.com/repos/motemen/git-pr-release/comments{/number}
248 | :issue_comment_url: https://api.github.com/repos/motemen/git-pr-release/issues/comments{/number}
249 | :contents_url: https://api.github.com/repos/motemen/git-pr-release/contents/{+path}
250 | :compare_url: https://api.github.com/repos/motemen/git-pr-release/compare/{base}...{head}
251 | :merges_url: https://api.github.com/repos/motemen/git-pr-release/merges
252 | :archive_url: https://api.github.com/repos/motemen/git-pr-release/{archive_format}{/ref}
253 | :downloads_url: https://api.github.com/repos/motemen/git-pr-release/downloads
254 | :issues_url: https://api.github.com/repos/motemen/git-pr-release/issues{/number}
255 | :pulls_url: https://api.github.com/repos/motemen/git-pr-release/pulls{/number}
256 | :milestones_url: https://api.github.com/repos/motemen/git-pr-release/milestones{/number}
257 | :notifications_url: https://api.github.com/repos/motemen/git-pr-release/notifications{?since,all,participating}
258 | :labels_url: https://api.github.com/repos/motemen/git-pr-release/labels{/name}
259 | :releases_url: https://api.github.com/repos/motemen/git-pr-release/releases{/id}
260 | :deployments_url: https://api.github.com/repos/motemen/git-pr-release/deployments
261 | :created_at: 2013-12-27 06:35:52.000000000 Z
262 | :updated_at: 2019-02-18 01:40:52.000000000 Z
263 | :pushed_at: 2018-11-15 10:45:07.000000000 Z
264 | :git_url: git://github.com/motemen/git-pr-release.git
265 | :ssh_url: git@github.com:motemen/git-pr-release.git
266 | :clone_url: https://github.com/motemen/git-pr-release.git
267 | :svn_url: https://github.com/motemen/git-pr-release
268 | :homepage: https://rubygems.org/gems/git-pr-release
269 | :size: 62
270 | :stargazers_count: 359
271 | :watchers_count: 359
272 | :language: Ruby
273 | :has_issues: true
274 | :has_projects: true
275 | :has_downloads: true
276 | :has_wiki: true
277 | :has_pages: false
278 | :forks_count: 28
279 | :mirror_url:
280 | :archived: false
281 | :open_issues_count: 6
282 | :license:
283 | :key: mit
284 | :name: MIT License
285 | :spdx_id: MIT
286 | :url: https://api.github.com/licenses/mit
287 | :node_id: MDc6TGljZW5zZTEz
288 | :forks: 28
289 | :open_issues: 6
290 | :watchers: 359
291 | :default_branch: master
292 | :_links:
293 | :self:
294 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/6
295 | :html:
296 | :href: https://github.com/motemen/git-pr-release/pull/6
297 | :issue:
298 | :href: https://api.github.com/repos/motemen/git-pr-release/issues/6
299 | :comments:
300 | :href: https://api.github.com/repos/motemen/git-pr-release/issues/6/comments
301 | :review_comments:
302 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/6/comments
303 | :review_comment:
304 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/comments{/number}
305 | :commits:
306 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/6/commits
307 | :statuses:
308 | :href: https://api.github.com/repos/motemen/git-pr-release/statuses/67d936fb57851f85db28249e91dfa0d8ccfe530b
309 | :author_association: CONTRIBUTOR
310 | :merged: true
311 | :mergeable:
312 | :rebaseable:
313 | :mergeable_state: unknown
314 | :merged_by:
315 | :login: motemen
316 | :id: 8465
317 | :node_id: MDQ6VXNlcjg0NjU=
318 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
319 | :gravatar_id: ''
320 | :url: https://api.github.com/users/motemen
321 | :html_url: https://github.com/motemen
322 | :followers_url: https://api.github.com/users/motemen/followers
323 | :following_url: https://api.github.com/users/motemen/following{/other_user}
324 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
325 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
326 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
327 | :organizations_url: https://api.github.com/users/motemen/orgs
328 | :repos_url: https://api.github.com/users/motemen/repos
329 | :events_url: https://api.github.com/users/motemen/events{/privacy}
330 | :received_events_url: https://api.github.com/users/motemen/received_events
331 | :type: User
332 | :site_admin: false
333 | :comments: 2
334 | :review_comments: 2
335 | :maintainer_can_modify: false
336 | :commits: 2
337 | :additions: 17
338 | :deletions: 1
339 | :changed_files: 1
340 |
--------------------------------------------------------------------------------
/spec/fixtures/file/pr_7.yml:
--------------------------------------------------------------------------------
1 | ---
2 | :url: https://api.github.com/repos/motemen/git-pr-release/pulls/7
3 | :id: 16585507
4 | :node_id: MDExOlB1bGxSZXF1ZXN0MTY1ODU1MDc=
5 | :html_url: https://github.com/motemen/git-pr-release/pull/7
6 | :diff_url: https://github.com/motemen/git-pr-release/pull/7.diff
7 | :patch_url: https://github.com/motemen/git-pr-release/pull/7.patch
8 | :issue_url: https://api.github.com/repos/motemen/git-pr-release/issues/7
9 | :number: 7
10 | :state: closed
11 | :locked: false
12 | :title: Support multiple assignees
13 | :user:
14 | :login: hakobe
15 | :id: 6882
16 | :node_id: MDQ6VXNlcjY4ODI=
17 | :avatar_url: https://avatars0.githubusercontent.com/u/6882?v=4
18 | :gravatar_id: ''
19 | :url: https://api.github.com/users/hakobe
20 | :html_url: https://github.com/hakobe
21 | :followers_url: https://api.github.com/users/hakobe/followers
22 | :following_url: https://api.github.com/users/hakobe/following{/other_user}
23 | :gists_url: https://api.github.com/users/hakobe/gists{/gist_id}
24 | :starred_url: https://api.github.com/users/hakobe/starred{/owner}{/repo}
25 | :subscriptions_url: https://api.github.com/users/hakobe/subscriptions
26 | :organizations_url: https://api.github.com/users/hakobe/orgs
27 | :repos_url: https://api.github.com/users/hakobe/repos
28 | :events_url: https://api.github.com/users/hakobe/events{/privacy}
29 | :received_events_url: https://api.github.com/users/hakobe/received_events
30 | :type: User
31 | :site_admin: false
32 | :body: 'This PR adds support for multiple assignees in git-pr-release.
33 |
34 | '
35 | :created_at: 2014-06-02 06:55:44.000000000 Z
36 | :updated_at: 2014-06-21 05:22:22.000000000 Z
37 | :closed_at: 2014-06-02 08:02:02.000000000 Z
38 | :merged_at: 2014-06-02 08:02:02.000000000 Z
39 | :merge_commit_sha: c66dfa90eed853507ae0e2876df3cb4dc7ce5286
40 | :assignee:
41 | :login: hakobe
42 | :id: 6882
43 | :node_id: MDQ6VXNlcjY4ODI=
44 | :avatar_url: https://avatars0.githubusercontent.com/u/6882?v=4
45 | :gravatar_id: ''
46 | :url: https://api.github.com/users/hakobe
47 | :html_url: https://github.com/hakobe
48 | :followers_url: https://api.github.com/users/hakobe/followers
49 | :following_url: https://api.github.com/users/hakobe/following{/other_user}
50 | :gists_url: https://api.github.com/users/hakobe/gists{/gist_id}
51 | :starred_url: https://api.github.com/users/hakobe/starred{/owner}{/repo}
52 | :subscriptions_url: https://api.github.com/users/hakobe/subscriptions
53 | :organizations_url: https://api.github.com/users/hakobe/orgs
54 | :repos_url: https://api.github.com/users/hakobe/repos
55 | :events_url: https://api.github.com/users/hakobe/events{/privacy}
56 | :received_events_url: https://api.github.com/users/hakobe/received_events
57 | :type: User
58 | :site_admin: false
59 | :assignees:
60 | - :login: hakobe
61 | :id: 6882
62 | :node_id: MDQ6VXNlcjY4ODI=
63 | :avatar_url: https://avatars0.githubusercontent.com/u/6882?v=4
64 | :gravatar_id: ''
65 | :url: https://api.github.com/users/hakobe
66 | :html_url: https://github.com/hakobe
67 | :followers_url: https://api.github.com/users/hakobe/followers
68 | :following_url: https://api.github.com/users/hakobe/following{/other_user}
69 | :gists_url: https://api.github.com/users/hakobe/gists{/gist_id}
70 | :starred_url: https://api.github.com/users/hakobe/starred{/owner}{/repo}
71 | :subscriptions_url: https://api.github.com/users/hakobe/subscriptions
72 | :organizations_url: https://api.github.com/users/hakobe/orgs
73 | :repos_url: https://api.github.com/users/hakobe/repos
74 | :events_url: https://api.github.com/users/hakobe/events{/privacy}
75 | :received_events_url: https://api.github.com/users/hakobe/received_events
76 | :type: User
77 | :site_admin: false
78 | - :login: toshimaru
79 | :id: 803398
80 | :node_id: MDQ6VXNlcjgwMzM5OA==
81 | :avatar_url: https://avatars.githubusercontent.com/u/803398?v=4
82 | :gravatar_id: ''
83 | :url: https://api.github.com/users/toshimaru
84 | :html_url: https://github.com/toshimaru
85 | :followers_url: https://api.github.com/users/toshimaru/followers
86 | :following_url: https://api.github.com/users/toshimaru/following{/other_user}
87 | :gists_url: https://api.github.com/users/toshimaru/gists{/gist_id}
88 | :starred_url: https://api.github.com/users/toshimaru/starred{/owner}{/repo}
89 | :subscriptions_url: https://api.github.com/users/toshimaru/subscriptions
90 | :organizations_url: https://api.github.com/users/toshimaru/orgs
91 | :repos_url: https://api.github.com/users/toshimaru/repos
92 | :events_url: https://api.github.com/users/toshimaru/events{/privacy}
93 | :received_events_url: https://api.github.com/users/toshimaru/received_events
94 | :type: User
95 | :site_admin: false
96 | - :login: Copilot
97 | :id: 198982749
98 | :node_id: BOT_kgDOC9w8XQ
99 | :avatar_url: https://avatars.githubusercontent.com/in/1143301?v=4
100 | :gravatar_id: ''
101 | :url: https://api.github.com/users/Copilot
102 | :html_url: https://github.com/apps/copilot-swe-agent
103 | :followers_url: https://api.github.com/users/Copilot/followers
104 | :following_url: https://api.github.com/users/Copilot/following{/other_user}
105 | :gists_url: https://api.github.com/users/Copilot/gists{/gist_id}
106 | :starred_url: https://api.github.com/users/Copilot/starred{/owner}{/repo}
107 | :subscriptions_url: https://api.github.com/users/Copilot/subscriptions
108 | :organizations_url: https://api.github.com/users/Copilot/orgs
109 | :repos_url: https://api.github.com/users/Copilot/repos
110 | :events_url: https://api.github.com/users/Copilot/events{/privacy}
111 | :received_events_url: https://api.github.com/users/Copilot/received_events
112 | :type: Bot
113 | :site_admin: false
114 | :requested_reviewers: []
115 | :requested_teams: []
116 | :labels: []
117 | :milestone:
118 | :commits_url: https://api.github.com/repos/motemen/git-pr-release/pulls/7/commits
119 | :review_comments_url: https://api.github.com/repos/motemen/git-pr-release/pulls/7/comments
120 | :review_comment_url: https://api.github.com/repos/motemen/git-pr-release/pulls/comments{/number}
121 | :comments_url: https://api.github.com/repos/motemen/git-pr-release/issues/7/comments
122 | :statuses_url: https://api.github.com/repos/motemen/git-pr-release/statuses/67d936fb57851f85db28249e91dfa0d8ccfe530b
123 | :head:
124 | :label: hakobe:support-multiple-assignees
125 | :ref: support-multiple-assignees
126 | :sha: 67d936fb57851f85db28249e91dfa0d8ccfe530b
127 | :user:
128 | :login: hakobe
129 | :id: 6882
130 | :node_id: MDQ6VXNlcjY4ODI=
131 | :avatar_url: https://avatars0.githubusercontent.com/u/6882?v=4
132 | :gravatar_id: ''
133 | :url: https://api.github.com/users/hakobe
134 | :html_url: https://github.com/hakobe
135 | :followers_url: https://api.github.com/users/hakobe/followers
136 | :following_url: https://api.github.com/users/hakobe/following{/other_user}
137 | :gists_url: https://api.github.com/users/hakobe/gists{/gist_id}
138 | :starred_url: https://api.github.com/users/hakobe/starred{/owner}{/repo}
139 | :subscriptions_url: https://api.github.com/users/hakobe/subscriptions
140 | :organizations_url: https://api.github.com/users/hakobe/orgs
141 | :repos_url: https://api.github.com/users/hakobe/repos
142 | :events_url: https://api.github.com/users/hakobe/events{/privacy}
143 | :received_events_url: https://api.github.com/users/hakobe/received_events
144 | :type: User
145 | :site_admin: false
146 | :repo:
147 | :id: 17425170
148 | :node_id: MDEwOlJlcG9zaXRvcnkxNzQyNTE3MA==
149 | :name: git-pr-release
150 | :full_name: hakobe/git-pr-release
151 | :private: false
152 | :owner:
153 | :login: hakobe
154 | :id: 6882
155 | :node_id: MDQ6VXNlcjY4ODI=
156 | :avatar_url: https://avatars0.githubusercontent.com/u/6882?v=4
157 | :gravatar_id: ''
158 | :url: https://api.github.com/users/hakobe
159 | :html_url: https://github.com/hakobe
160 | :followers_url: https://api.github.com/users/hakobe/followers
161 | :following_url: https://api.github.com/users/hakobe/following{/other_user}
162 | :gists_url: https://api.github.com/users/hakobe/gists{/gist_id}
163 | :starred_url: https://api.github.com/users/hakobe/starred{/owner}{/repo}
164 | :subscriptions_url: https://api.github.com/users/hakobe/subscriptions
165 | :organizations_url: https://api.github.com/users/hakobe/orgs
166 | :repos_url: https://api.github.com/users/hakobe/repos
167 | :events_url: https://api.github.com/users/hakobe/events{/privacy}
168 | :received_events_url: https://api.github.com/users/hakobe/received_events
169 | :type: User
170 | :site_admin: false
171 | :html_url: https://github.com/hakobe/git-pr-release
172 | :description: Creates a pull request which summarizes feature branches that are
173 | to be released into production
174 | :fork: true
175 | :url: https://api.github.com/repos/hakobe/git-pr-release
176 | :forks_url: https://api.github.com/repos/hakobe/git-pr-release/forks
177 | :keys_url: https://api.github.com/repos/hakobe/git-pr-release/keys{/key_id}
178 | :collaborators_url: https://api.github.com/repos/hakobe/git-pr-release/collaborators{/collaborator}
179 | :teams_url: https://api.github.com/repos/hakobe/git-pr-release/teams
180 | :hooks_url: https://api.github.com/repos/hakobe/git-pr-release/hooks
181 | :issue_events_url: https://api.github.com/repos/hakobe/git-pr-release/issues/events{/number}
182 | :events_url: https://api.github.com/repos/hakobe/git-pr-release/events
183 | :assignees_url: https://api.github.com/repos/hakobe/git-pr-release/assignees{/user}
184 | :branches_url: https://api.github.com/repos/hakobe/git-pr-release/branches{/branch}
185 | :tags_url: https://api.github.com/repos/hakobe/git-pr-release/tags
186 | :blobs_url: https://api.github.com/repos/hakobe/git-pr-release/git/blobs{/sha}
187 | :git_tags_url: https://api.github.com/repos/hakobe/git-pr-release/git/tags{/sha}
188 | :git_refs_url: https://api.github.com/repos/hakobe/git-pr-release/git/refs{/sha}
189 | :trees_url: https://api.github.com/repos/hakobe/git-pr-release/git/trees{/sha}
190 | :statuses_url: https://api.github.com/repos/hakobe/git-pr-release/statuses/{sha}
191 | :languages_url: https://api.github.com/repos/hakobe/git-pr-release/languages
192 | :stargazers_url: https://api.github.com/repos/hakobe/git-pr-release/stargazers
193 | :contributors_url: https://api.github.com/repos/hakobe/git-pr-release/contributors
194 | :subscribers_url: https://api.github.com/repos/hakobe/git-pr-release/subscribers
195 | :subscription_url: https://api.github.com/repos/hakobe/git-pr-release/subscription
196 | :commits_url: https://api.github.com/repos/hakobe/git-pr-release/commits{/sha}
197 | :git_commits_url: https://api.github.com/repos/hakobe/git-pr-release/git/commits{/sha}
198 | :comments_url: https://api.github.com/repos/hakobe/git-pr-release/comments{/number}
199 | :issue_comment_url: https://api.github.com/repos/hakobe/git-pr-release/issues/comments{/number}
200 | :contents_url: https://api.github.com/repos/hakobe/git-pr-release/contents/{+path}
201 | :compare_url: https://api.github.com/repos/hakobe/git-pr-release/compare/{base}...{head}
202 | :merges_url: https://api.github.com/repos/hakobe/git-pr-release/merges
203 | :archive_url: https://api.github.com/repos/hakobe/git-pr-release/{archive_format}{/ref}
204 | :downloads_url: https://api.github.com/repos/hakobe/git-pr-release/downloads
205 | :issues_url: https://api.github.com/repos/hakobe/git-pr-release/issues{/number}
206 | :pulls_url: https://api.github.com/repos/hakobe/git-pr-release/pulls{/number}
207 | :milestones_url: https://api.github.com/repos/hakobe/git-pr-release/milestones{/number}
208 | :notifications_url: https://api.github.com/repos/hakobe/git-pr-release/notifications{?since,all,participating}
209 | :labels_url: https://api.github.com/repos/hakobe/git-pr-release/labels{/name}
210 | :releases_url: https://api.github.com/repos/hakobe/git-pr-release/releases{/id}
211 | :deployments_url: https://api.github.com/repos/hakobe/git-pr-release/deployments
212 | :created_at: 2014-03-05 02:20:59.000000000 Z
213 | :updated_at: 2014-05-13 01:42:58.000000000 Z
214 | :pushed_at: 2014-05-12 11:42:34.000000000 Z
215 | :git_url: git://github.com/hakobe/git-pr-release.git
216 | :ssh_url: git@github.com:hakobe/git-pr-release.git
217 | :clone_url: https://github.com/hakobe/git-pr-release.git
218 | :svn_url: https://github.com/hakobe/git-pr-release
219 | :homepage: https://rubygems.org/gems/git-pr-release
220 | :size: 236
221 | :stargazers_count: 0
222 | :watchers_count: 0
223 | :language: Ruby
224 | :has_issues: false
225 | :has_projects: true
226 | :has_downloads: true
227 | :has_wiki: true
228 | :has_pages: false
229 | :forks_count: 0
230 | :mirror_url:
231 | :archived: false
232 | :open_issues_count: 0
233 | :license:
234 | :forks: 0
235 | :open_issues: 0
236 | :watchers: 0
237 | :default_branch: master
238 | :base:
239 | :label: motemen:master
240 | :ref: master
241 | :sha: a0d1fd72732d705270878d22601c44e0533641e1
242 | :user:
243 | :login: motemen
244 | :id: 8465
245 | :node_id: MDQ6VXNlcjg0NjU=
246 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
247 | :gravatar_id: ''
248 | :url: https://api.github.com/users/motemen
249 | :html_url: https://github.com/motemen
250 | :followers_url: https://api.github.com/users/motemen/followers
251 | :following_url: https://api.github.com/users/motemen/following{/other_user}
252 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
253 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
254 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
255 | :organizations_url: https://api.github.com/users/motemen/orgs
256 | :repos_url: https://api.github.com/users/motemen/repos
257 | :events_url: https://api.github.com/users/motemen/events{/privacy}
258 | :received_events_url: https://api.github.com/users/motemen/received_events
259 | :type: User
260 | :site_admin: false
261 | :repo:
262 | :id: 15468156
263 | :node_id: MDEwOlJlcG9zaXRvcnkxNTQ2ODE1Ng==
264 | :name: git-pr-release
265 | :full_name: motemen/git-pr-release
266 | :private: false
267 | :owner:
268 | :login: motemen
269 | :id: 8465
270 | :node_id: MDQ6VXNlcjg0NjU=
271 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
272 | :gravatar_id: ''
273 | :url: https://api.github.com/users/motemen
274 | :html_url: https://github.com/motemen
275 | :followers_url: https://api.github.com/users/motemen/followers
276 | :following_url: https://api.github.com/users/motemen/following{/other_user}
277 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
278 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
279 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
280 | :organizations_url: https://api.github.com/users/motemen/orgs
281 | :repos_url: https://api.github.com/users/motemen/repos
282 | :events_url: https://api.github.com/users/motemen/events{/privacy}
283 | :received_events_url: https://api.github.com/users/motemen/received_events
284 | :type: User
285 | :site_admin: false
286 | :html_url: https://github.com/motemen/git-pr-release
287 | :description: Release pull request generator
288 | :fork: false
289 | :url: https://api.github.com/repos/motemen/git-pr-release
290 | :forks_url: https://api.github.com/repos/motemen/git-pr-release/forks
291 | :keys_url: https://api.github.com/repos/motemen/git-pr-release/keys{/key_id}
292 | :collaborators_url: https://api.github.com/repos/motemen/git-pr-release/collaborators{/collaborator}
293 | :teams_url: https://api.github.com/repos/motemen/git-pr-release/teams
294 | :hooks_url: https://api.github.com/repos/motemen/git-pr-release/hooks
295 | :issue_events_url: https://api.github.com/repos/motemen/git-pr-release/issues/events{/number}
296 | :events_url: https://api.github.com/repos/motemen/git-pr-release/events
297 | :assignees_url: https://api.github.com/repos/motemen/git-pr-release/assignees{/user}
298 | :branches_url: https://api.github.com/repos/motemen/git-pr-release/branches{/branch}
299 | :tags_url: https://api.github.com/repos/motemen/git-pr-release/tags
300 | :blobs_url: https://api.github.com/repos/motemen/git-pr-release/git/blobs{/sha}
301 | :git_tags_url: https://api.github.com/repos/motemen/git-pr-release/git/tags{/sha}
302 | :git_refs_url: https://api.github.com/repos/motemen/git-pr-release/git/refs{/sha}
303 | :trees_url: https://api.github.com/repos/motemen/git-pr-release/git/trees{/sha}
304 | :statuses_url: https://api.github.com/repos/motemen/git-pr-release/statuses/{sha}
305 | :languages_url: https://api.github.com/repos/motemen/git-pr-release/languages
306 | :stargazers_url: https://api.github.com/repos/motemen/git-pr-release/stargazers
307 | :contributors_url: https://api.github.com/repos/motemen/git-pr-release/contributors
308 | :subscribers_url: https://api.github.com/repos/motemen/git-pr-release/subscribers
309 | :subscription_url: https://api.github.com/repos/motemen/git-pr-release/subscription
310 | :commits_url: https://api.github.com/repos/motemen/git-pr-release/commits{/sha}
311 | :git_commits_url: https://api.github.com/repos/motemen/git-pr-release/git/commits{/sha}
312 | :comments_url: https://api.github.com/repos/motemen/git-pr-release/comments{/number}
313 | :issue_comment_url: https://api.github.com/repos/motemen/git-pr-release/issues/comments{/number}
314 | :contents_url: https://api.github.com/repos/motemen/git-pr-release/contents/{+path}
315 | :compare_url: https://api.github.com/repos/motemen/git-pr-release/compare/{base}...{head}
316 | :merges_url: https://api.github.com/repos/motemen/git-pr-release/merges
317 | :archive_url: https://api.github.com/repos/motemen/git-pr-release/{archive_format}{/ref}
318 | :downloads_url: https://api.github.com/repos/motemen/git-pr-release/downloads
319 | :issues_url: https://api.github.com/repos/motemen/git-pr-release/issues{/number}
320 | :pulls_url: https://api.github.com/repos/motemen/git-pr-release/pulls{/number}
321 | :milestones_url: https://api.github.com/repos/motemen/git-pr-release/milestones{/number}
322 | :notifications_url: https://api.github.com/repos/motemen/git-pr-release/notifications{?since,all,participating}
323 | :labels_url: https://api.github.com/repos/motemen/git-pr-release/labels{/name}
324 | :releases_url: https://api.github.com/repos/motemen/git-pr-release/releases{/id}
325 | :deployments_url: https://api.github.com/repos/motemen/git-pr-release/deployments
326 | :created_at: 2013-12-27 06:35:52.000000000 Z
327 | :updated_at: 2019-02-18 01:40:52.000000000 Z
328 | :pushed_at: 2018-11-15 10:45:07.000000000 Z
329 | :git_url: git://github.com/motemen/git-pr-release.git
330 | :ssh_url: git@github.com:motemen/git-pr-release.git
331 | :clone_url: https://github.com/motemen/git-pr-release.git
332 | :svn_url: https://github.com/motemen/git-pr-release
333 | :homepage: https://rubygems.org/gems/git-pr-release
334 | :size: 62
335 | :stargazers_count: 359
336 | :watchers_count: 359
337 | :language: Ruby
338 | :has_issues: true
339 | :has_projects: true
340 | :has_downloads: true
341 | :has_wiki: true
342 | :has_pages: false
343 | :forks_count: 28
344 | :mirror_url:
345 | :archived: false
346 | :open_issues_count: 6
347 | :license:
348 | :key: mit
349 | :name: MIT License
350 | :spdx_id: MIT
351 | :url: https://api.github.com/licenses/mit
352 | :node_id: MDc6TGljZW5zZTEz
353 | :forks: 28
354 | :open_issues: 6
355 | :watchers: 359
356 | :default_branch: master
357 | :_links:
358 | :self:
359 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/7
360 | :html:
361 | :href: https://github.com/motemen/git-pr-release/pull/7
362 | :issue:
363 | :href: https://api.github.com/repos/motemen/git-pr-release/issues/7
364 | :comments:
365 | :href: https://api.github.com/repos/motemen/git-pr-release/issues/7/comments
366 | :review_comments:
367 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/7/comments
368 | :review_comment:
369 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/comments{/number}
370 | :commits:
371 | :href: https://api.github.com/repos/motemen/git-pr-release/pulls/7/commits
372 | :statuses:
373 | :href: https://api.github.com/repos/motemen/git-pr-release/statuses/67d936fb57851f85db28249e91dfa0d8ccfe530b
374 | :author_association: CONTRIBUTOR
375 | :merged: true
376 | :mergeable:
377 | :rebaseable:
378 | :mergeable_state: unknown
379 | :merged_by:
380 | :login: motemen
381 | :id: 8465
382 | :node_id: MDQ6VXNlcjg0NjU=
383 | :avatar_url: https://avatars2.githubusercontent.com/u/8465?v=4
384 | :gravatar_id: ''
385 | :url: https://api.github.com/users/motemen
386 | :html_url: https://github.com/motemen
387 | :followers_url: https://api.github.com/users/motemen/followers
388 | :following_url: https://api.github.com/users/motemen/following{/other_user}
389 | :gists_url: https://api.github.com/users/motemen/gists{/gist_id}
390 | :starred_url: https://api.github.com/users/motemen/starred{/owner}{/repo}
391 | :subscriptions_url: https://api.github.com/users/motemen/subscriptions
392 | :organizations_url: https://api.github.com/users/motemen/orgs
393 | :repos_url: https://api.github.com/users/motemen/repos
394 | :events_url: https://api.github.com/users/motemen/events{/privacy}
395 | :received_events_url: https://api.github.com/users/motemen/received_events
396 | :type: User
397 | :site_admin: false
398 | :comments: 2
399 | :review_comments: 2
400 | :maintainer_can_modify: false
401 | :commits: 2
402 | :additions: 17
403 | :deletions: 1
404 | :changed_files: 1
405 |
--------------------------------------------------------------------------------
/spec/git/pr/release/cli_spec.rb:
--------------------------------------------------------------------------------
1 | RSpec.describe Git::Pr::Release::CLI do
2 | let(:configured_cli) {
3 | cli = Git::Pr::Release::CLI.new
4 | allow(cli).to receive(:host_and_repository_and_scheme) {
5 | [nil, "motemen/git-pr-release", "https"]
6 | }
7 | allow(cli).to receive(:git_config).with(anything) { nil }
8 | cli.configure
9 | cli
10 | }
11 |
12 | describe "#start" do
13 | subject { @cli.start }
14 |
15 | before {
16 | @cli = Git::Pr::Release::CLI.new
17 |
18 | allow(@cli).to receive(:configure)
19 | allow(@cli).to receive(:fetch_merged_prs) { merged_prs }
20 | allow(@cli).to receive(:create_release_pr)
21 | }
22 |
23 | context "When merged_prs is empty" do
24 | let(:merged_prs) { [] }
25 | it {
26 | is_expected.to eq 1
27 | expect(@cli).to have_received(:configure)
28 | expect(@cli).to have_received(:fetch_merged_prs)
29 | expect(@cli).not_to have_received(:create_release_pr)
30 | }
31 | end
32 |
33 | context "When merged_prs exists" do
34 | let(:merged_prs) {
35 | agent = Sawyer::Agent.new("http://example.com/") do |conn|
36 | conn.builder.handlers.delete(Faraday::Adapter::NetHttp)
37 | conn.adapter(:test, Faraday::Adapter::Test::Stubs.new)
38 | end
39 | pr_3 = Sawyer::Resource.new(agent, load_yaml("pr_3.yml"))
40 | pr_4 = Sawyer::Resource.new(agent, load_yaml("pr_4.yml"))
41 | [pr_3, pr_4]
42 | }
43 | it {
44 | is_expected.to eq 0
45 | expect(@cli).to have_received(:configure)
46 | expect(@cli).to have_received(:fetch_merged_prs)
47 | expect(@cli).to have_received(:create_release_pr).with(merged_prs)
48 | }
49 | end
50 | end
51 |
52 | describe "#configure" do
53 | subject { @cli.configure }
54 |
55 | before {
56 | @cli = Git::Pr::Release::CLI.new
57 |
58 | allow(@cli).to receive(:git_config).with(anything) { nil }
59 | }
60 |
61 | context "When default" do
62 | before {
63 | allow(@cli).to receive(:host_and_repository_and_scheme) {
64 | [nil, "motemen/git-pr-release", "https"]
65 | }
66 | }
67 |
68 | it "configured as default" do
69 | subject
70 |
71 | expect(Octokit.api_endpoint).to eq "https://api.github.com/"
72 | expect(Octokit.web_endpoint).to eq "https://github.com/"
73 |
74 | expect(@cli.repository).to eq "motemen/git-pr-release"
75 | expect(@cli.production_branch).to eq "master"
76 | expect(@cli.staging_branch).to eq "staging"
77 | expect(@cli.template_path).to eq nil
78 | expect(@cli.labels).to eq []
79 | expect(@cli.assign_pr_author).to eq false
80 | expect(@cli.request_pr_author_review).to eq false
81 | end
82 | end
83 |
84 | context "When GitHub Enterprise Server" do
85 | before {
86 | allow(@cli).to receive(:host_and_repository_and_scheme) {
87 | ["example.com", "motemen/git-pr-release", "https"]
88 | }
89 | }
90 | after {
91 | Octokit.reset!
92 | }
93 |
94 | it "octokit is configured" do
95 | subject
96 |
97 | expect(Octokit.api_endpoint).to eq "https://example.com/api/v3/"
98 | expect(Octokit.web_endpoint).to eq "https://example.com/"
99 | end
100 | end
101 |
102 | describe "branches" do
103 | context "When branches are set by ENV" do
104 | around do |example|
105 | original = ENV.to_hash
106 | begin
107 | ENV["GIT_PR_RELEASE_BRANCH_PRODUCTION"] = "prod"
108 | ENV["GIT_PR_RELEASE_BRANCH_STAGING"] = "dev"
109 | example.run
110 | ensure
111 | ENV.replace(original)
112 | end
113 | end
114 |
115 | it "branches are configured" do
116 | subject
117 |
118 | expect(@cli.production_branch).to eq "prod"
119 | expect(@cli.staging_branch).to eq "dev"
120 | end
121 | end
122 |
123 | context "When branches are set by git_config" do
124 | before {
125 | allow(@cli).to receive(:git_config).with("branch.production") { "production" }
126 | allow(@cli).to receive(:git_config).with("branch.staging") { "develop" }
127 | }
128 |
129 | it "branches are configured" do
130 | subject
131 |
132 | expect(@cli.production_branch).to eq "production"
133 | expect(@cli.staging_branch).to eq "develop"
134 | end
135 | end
136 | end
137 |
138 | describe "labels" do
139 | context "With ENV" do
140 | around do |example|
141 | original = ENV.to_hash
142 | begin
143 | ENV["GIT_PR_RELEASE_LABELS"] = env_labels
144 | example.run
145 | ensure
146 | ENV.replace(original)
147 | end
148 | end
149 |
150 | context "string" do
151 | let(:env_labels) { "release" }
152 | it "set labels" do
153 | subject
154 | expect(@cli.labels).to eq ["release"]
155 | end
156 | end
157 |
158 | context "comma separated string" do
159 | let(:env_labels) { "release,release2" }
160 | it "set labels" do
161 | subject
162 | expect(@cli.labels).to eq ["release", "release2"]
163 | end
164 | end
165 |
166 | context "empty string" do
167 | let(:env_labels) { "" }
168 | it "set labels as default" do
169 | subject
170 | expect(@cli.labels).to eq []
171 | end
172 | end
173 | end
174 |
175 | context "With git_config" do
176 | before {
177 | allow(@cli).to receive(:git_config).with("labels") { "release" }
178 | }
179 |
180 | it "set labels" do
181 | subject
182 | expect(@cli.labels).to eq ["release"]
183 | end
184 | end
185 | end
186 |
187 | describe "assign_pr_author" do
188 | context "With ENV set to true" do
189 | around do |example|
190 | original = ENV.to_hash
191 | begin
192 | ENV["GIT_PR_RELEASE_ASSIGN_PR_AUTHOR"] = "true"
193 | example.run
194 | ensure
195 | ENV.replace(original)
196 | end
197 | end
198 |
199 | it "enables assign_pr_author" do
200 | subject
201 | expect(@cli.assign_pr_author).to eq true
202 | end
203 | end
204 |
205 | context "With ENV set to 1" do
206 | around do |example|
207 | original = ENV.to_hash
208 | begin
209 | ENV["GIT_PR_RELEASE_ASSIGN_PR_AUTHOR"] = "1"
210 | example.run
211 | ensure
212 | ENV.replace(original)
213 | end
214 | end
215 |
216 | it "enables assign_pr_author" do
217 | subject
218 | expect(@cli.assign_pr_author).to eq true
219 | end
220 | end
221 |
222 | context "With ENV set to false" do
223 | around do |example|
224 | original = ENV.to_hash
225 | begin
226 | ENV["GIT_PR_RELEASE_ASSIGN_PR_AUTHOR"] = "false"
227 | example.run
228 | ensure
229 | ENV.replace(original)
230 | end
231 | end
232 |
233 | it "disables assign_pr_author" do
234 | subject
235 | expect(@cli.assign_pr_author).to eq false
236 | end
237 | end
238 |
239 | context "With git_config" do
240 | before {
241 | allow(@cli).to receive(:git_config).with("assign-pr-author") { "true" }
242 | }
243 |
244 | it "enables assign_pr_author" do
245 | subject
246 | expect(@cli.assign_pr_author).to eq true
247 | end
248 | end
249 | end
250 |
251 | describe "request_pr_author_review" do
252 | context "With ENV set to true" do
253 | around do |example|
254 | original = ENV.to_hash
255 | begin
256 | ENV["GIT_PR_RELEASE_REQUEST_PR_AUTHOR_REVIEW"] = "true"
257 | example.run
258 | ensure
259 | ENV.replace(original)
260 | end
261 | end
262 |
263 | it "enables request_pr_author_review" do
264 | subject
265 | expect(@cli.request_pr_author_review).to eq true
266 | end
267 | end
268 |
269 | context "With ENV set to 1" do
270 | around do |example|
271 | original = ENV.to_hash
272 | begin
273 | ENV["GIT_PR_RELEASE_REQUEST_PR_AUTHOR_REVIEW"] = "1"
274 | example.run
275 | ensure
276 | ENV.replace(original)
277 | end
278 | end
279 |
280 | it "enables request_pr_author_review" do
281 | subject
282 | expect(@cli.request_pr_author_review).to eq true
283 | end
284 | end
285 |
286 | context "With ENV set to false" do
287 | around do |example|
288 | original = ENV.to_hash
289 | begin
290 | ENV["GIT_PR_RELEASE_REQUEST_PR_AUTHOR_REVIEW"] = "false"
291 | example.run
292 | ensure
293 | ENV.replace(original)
294 | end
295 | end
296 |
297 | it "disables request_pr_author_review" do
298 | subject
299 | expect(@cli.request_pr_author_review).to eq false
300 | end
301 | end
302 |
303 | context "With git_config" do
304 | before {
305 | allow(@cli).to receive(:git_config).with("request-pr-author-review") { "true" }
306 | }
307 |
308 | it "enables request_pr_author_review" do
309 | subject
310 | expect(@cli.request_pr_author_review).to eq true
311 | end
312 | end
313 | end
314 | end
315 |
316 | describe "#fetch_merged_prs" do
317 | subject { @cli.fetch_merged_prs }
318 |
319 | before {
320 | @cli = configured_cli
321 |
322 | agent = Sawyer::Agent.new("http://example.com/") do |conn|
323 | conn.builder.handlers.delete(Faraday::Adapter::NetHttp)
324 | conn.adapter(:test, Faraday::Adapter::Test::Stubs.new)
325 | end
326 |
327 | expect(@cli).to receive(:git).with(:'rev-parse', "--is-shallow-repository") { ["false\n"] }
328 | expect(@cli).to receive(:git).with(:remote, "update", "origin") {
329 | []
330 | }
331 |
332 | expect(@cli).to receive(:git).with(:log, "--merges", "--pretty=format:%P", "origin/master..origin/staging") {
333 | <<~GIT_LOG.each_line
334 | ad694b9c2b868e8801f9209f0ad5dd5458c49854 42bd43b80c973c8f348df3521745201be05bf194
335 | b620bead10831d2e4e15be392e0a435d3470a0ad 5c977a1827387ac7b7a85c7b827ee119165f1823
336 | GIT_LOG
337 | }
338 | expect(@cli).to receive(:git).with("ls-remote", "origin", "refs/pull/*/head") {
339 | <<~GIT_LS_REMOTE.each_line
340 | bbcd2a04ef394e91be44c24e93e52fdbca944060 refs/pull/1/head
341 | 5c977a1827387ac7b7a85c7b827ee119165f1823 refs/pull/3/head
342 | 42bd43b80c973c8f348df3521745201be05bf194 refs/pull/4/head
343 | GIT_LS_REMOTE
344 | }
345 | expect(@cli).to receive(:git).with("merge-base", "5c977a1827387ac7b7a85c7b827ee119165f1823", "origin/master") {
346 | "b620bead10831d2e4e15be392e0a435d3470a0ad".each_line
347 | }
348 | expect(@cli).to receive(:git).with("merge-base", "42bd43b80c973c8f348df3521745201be05bf194", "origin/master") {
349 | "b620bead10831d2e4e15be392e0a435d3470a0ad".each_line
350 | }
351 |
352 | client = double(Octokit::Client)
353 | @pr_3 = Sawyer::Resource.new(agent, load_yaml("pr_3.yml"))
354 | @pr_4 = Sawyer::Resource.new(agent, load_yaml("pr_4.yml"))
355 | expect(client).to receive(:pull_request).with("motemen/git-pr-release", 3) { @pr_3 }
356 | expect(client).to receive(:pull_request).with("motemen/git-pr-release", 4) { @pr_4 }
357 | allow(@cli).to receive(:client).with(no_args) { client }
358 | }
359 |
360 | it { is_expected.to eq [@pr_3, @pr_4] }
361 | end
362 |
363 | describe "#create_release_pr" do
364 | subject { @cli.create_release_pr(@merged_prs) }
365 |
366 | before {
367 | @cli = configured_cli
368 |
369 | @agent = Sawyer::Agent.new("http://example.com/") do |conn|
370 | conn.builder.handlers.delete(Faraday::Adapter::NetHttp)
371 | conn.adapter(:test, Faraday::Adapter::Test::Stubs.new)
372 | end
373 |
374 | @merged_prs = [
375 | Sawyer::Resource.new(@agent, load_yaml("pr_3.yml")),
376 | Sawyer::Resource.new(@agent, load_yaml("pr_4.yml")),
377 | ]
378 |
379 | allow(@cli).to receive(:detect_existing_release_pr) { existing_release_pr }
380 | @pr_title = "Release 2020-01-04 16:51:09 +0900"
381 | @pr_body = <<~BODY.chomp
382 | - [ ] #3 Provides a creating release pull-request object for template @hakobe
383 | - [ ] #4 use user who create PR if there is no assignee @motemen
384 | BODY
385 | allow(@cli).to receive(:build_and_merge_pr_title_and_body) {
386 | [@pr_title, @pr_body]
387 | }
388 | allow(@cli).to receive(:update_release_pr)
389 | @changed_files = [double(Sawyer::Resource)]
390 | allow(@cli).to receive(:pull_request_files) { @changed_files }
391 | }
392 |
393 | context "When create_mode" do
394 | before {
395 | @created_pr = Sawyer::Resource.new(@agent, load_yaml("pr_1.yml"))
396 | allow(@cli).to receive(:prepare_release_pr) { @created_pr }
397 | }
398 |
399 | let(:existing_release_pr) { nil }
400 |
401 | it {
402 | subject
403 |
404 | expect(@cli).to have_received(:detect_existing_release_pr)
405 | expect(@cli).to have_received(:prepare_release_pr)
406 | expect(@cli).to have_received(:pull_request_files)
407 | expect(@cli).to have_received(:build_and_merge_pr_title_and_body).with(@created_pr, @merged_prs, @changed_files)
408 | expect(@cli).to have_received(:update_release_pr).with(@created_pr, @pr_title, @pr_body, @merged_prs)
409 | }
410 | end
411 |
412 | context "When not create_mode" do
413 | before {
414 | allow(@cli).to receive(:prepare_release_pr)
415 | }
416 |
417 | let(:existing_release_pr) { double(
418 | number: 1023,
419 | rels: { html: double(href: "https://github.com/motemen/git-pr-release/pull/1023") },
420 | )}
421 |
422 | it {
423 | subject
424 |
425 | expect(@cli).to have_received(:detect_existing_release_pr)
426 | expect(@cli).to have_received(:pull_request_files).with(existing_release_pr)
427 | expect(@cli).not_to have_received(:prepare_release_pr)
428 | expect(@cli).to have_received(:build_and_merge_pr_title_and_body).with(existing_release_pr, @merged_prs, @changed_files)
429 | expect(@cli).to have_received(:update_release_pr).with(existing_release_pr, @pr_title, @pr_body, @merged_prs)
430 | }
431 | end
432 |
433 | context "When dry_run with create_mode" do
434 | before {
435 | @created_pr = Sawyer::Resource.new(@agent, load_yaml("pr_1.yml"))
436 | allow(@cli).to receive(:prepare_release_pr) { @created_pr }
437 |
438 | @cli.instance_variable_set(:@dry_run, true)
439 | }
440 |
441 | let(:existing_release_pr) { nil }
442 |
443 | it {
444 | subject
445 |
446 | expect(@cli).to have_received(:detect_existing_release_pr)
447 | expect(@cli).not_to have_received(:prepare_release_pr)
448 | expect(@cli).not_to have_received(:pull_request_files)
449 | expect(@cli).to have_received(:build_and_merge_pr_title_and_body).with(nil, @merged_prs, [])
450 | expect(@cli).not_to have_received(:update_release_pr).with(@created_pr, @pr_title, @pr_body, @merged_prs)
451 | }
452 | end
453 |
454 | context "When overwrite_description" do
455 | before {
456 | @cli.instance_variable_set(:@overwrite_description, true)
457 | @new_pr_title = "2022-08-17 12:34:58 +0900"
458 | @new_pr_body = <<~BODY.chomp
459 | - [ ] #3 @hakobe
460 | - [ ] #4 @hakobe
461 | BODY
462 | allow(@cli).to receive(:build_pr_title_and_body) {
463 | [@new_pr_title, @new_pr_body]
464 | }
465 | }
466 |
467 | let(:existing_release_pr) { double(
468 | number: 1023,
469 | rels: { html: double(href: "https://github.com/motemen/git-pr-release/pull/1023") },
470 | )}
471 |
472 | it {
473 | subject
474 |
475 | expect(@cli).not_to have_received(:build_and_merge_pr_title_and_body)
476 | expect(@cli).to have_received(:update_release_pr).with(existing_release_pr, @new_pr_title, @new_pr_body, @merged_prs)
477 | }
478 | end
479 | end
480 |
481 | describe "#prepare_release_pr" do
482 | subject { @cli.prepare_release_pr }
483 |
484 | before {
485 | @cli = configured_cli
486 |
487 | @client = double(Octokit::Client)
488 | allow(@client).to receive(:create_pull_request)
489 | allow(@cli).to receive(:client) { @client }
490 | }
491 |
492 | it {
493 | subject
494 |
495 | expect(@client).to have_received(:create_pull_request).with(
496 | "motemen/git-pr-release",
497 | "master",
498 | "staging",
499 | "Preparing release pull request...",
500 | "", # empby body
501 | )
502 | }
503 | end
504 |
505 | describe "#build_and_merge_pr_title_and_body" do
506 | subject { @cli.build_and_merge_pr_title_and_body(release_pr, @merged_prs, changed_files) }
507 |
508 | before {
509 | @cli = configured_cli
510 |
511 | @merged_prs = [double(Sawyer::Resource)]
512 | allow(@cli).to receive(:build_pr_title_and_body) { ["PR Title", "PR Body"] }
513 | allow(@cli).to receive(:merge_pr_body) { "Merged Body" }
514 | }
515 |
516 | context "When release_pr exists" do
517 | let(:release_pr) { double(body: "Old Body") }
518 | let(:changed_files) { [double(Sawyer::Resource)] }
519 |
520 | it {
521 | is_expected.to eq ["PR Title", "Merged Body"]
522 |
523 | expect(@cli).to have_received(:build_pr_title_and_body).with(release_pr, @merged_prs, changed_files, nil)
524 | expect(@cli).to have_received(:merge_pr_body).with("Old Body", "PR Body")
525 | }
526 | end
527 |
528 | context "When release_pr is nil" do
529 | # = When dry_run with create_mode
530 | let(:release_pr) { nil }
531 | let(:changed_files) { [] }
532 |
533 | it {
534 | is_expected.to eq ["PR Title", "Merged Body"]
535 |
536 | expect(@cli).to have_received(:build_pr_title_and_body).with(release_pr, @merged_prs, changed_files, nil)
537 | expect(@cli).to have_received(:merge_pr_body).with("", "PR Body")
538 | }
539 | end
540 | end
541 |
542 | describe "#update_release_pr" do
543 | subject { @cli.update_release_pr(@release_pr, "PR Title", "PR Body", @merged_prs) }
544 |
545 | before {
546 | @cli = configured_cli
547 |
548 | @release_pr = double(number: 1023)
549 |
550 | @agent = Sawyer::Agent.new("http://example.com/") do |conn|
551 | conn.builder.handlers.delete(Faraday::Adapter::NetHttp)
552 | conn.adapter(:test, Faraday::Adapter::Test::Stubs.new)
553 | end
554 | @merged_prs = [
555 | Sawyer::Resource.new(@agent, load_yaml("pr_3.yml")),
556 | Sawyer::Resource.new(@agent, load_yaml("pr_4.yml")),
557 | Sawyer::Resource.new(@agent, load_yaml("pr_6.yml")),
558 | ]
559 |
560 | @client = double(Octokit::Client)
561 | allow(@client).to receive(:update_pull_request) { @release_pr }
562 | allow(@client).to receive(:add_labels_to_an_issue)
563 | allow(@client).to receive(:add_assignees)
564 | allow(@client).to receive(:request_pull_request_review)
565 | allow(@cli).to receive(:client) { @client }
566 | }
567 |
568 | context "Without labels and without assign_pr_author" do
569 | it {
570 | subject
571 |
572 | expect(@client).to have_received(:update_pull_request).with(
573 | "motemen/git-pr-release",
574 | 1023,
575 | {
576 | title: "PR Title",
577 | body: "PR Body",
578 | }
579 | )
580 | expect(@client).not_to have_received(:add_labels_to_an_issue)
581 | expect(@client).not_to have_received(:add_assignees)
582 | expect(@client).not_to have_received(:request_pull_request_review)
583 | }
584 | end
585 |
586 | context "With labels" do
587 | before {
588 | allow(@cli).to receive(:labels) { ["release"] }
589 | }
590 | it {
591 | subject
592 |
593 | expect(@client).to have_received(:update_pull_request).with(
594 | "motemen/git-pr-release",
595 | 1023,
596 | {
597 | title: "PR Title",
598 | body: "PR Body",
599 | }
600 | )
601 | expect(@client).to have_received(:add_labels_to_an_issue).with(
602 | "motemen/git-pr-release", 1023, ["release"]
603 | )
604 | expect(@client).not_to have_received(:add_assignees)
605 | expect(@client).not_to have_received(:request_pull_request_review)
606 | }
607 | end
608 |
609 | context "With assign_pr_author enabled" do
610 | before {
611 | allow(@cli).to receive(:assign_pr_author) { true }
612 | }
613 |
614 | it {
615 | subject
616 |
617 | expect(@client).to have_received(:update_pull_request).with(
618 | "motemen/git-pr-release",
619 | 1023,
620 | {
621 | title: "PR Title",
622 | body: "PR Body",
623 | }
624 | )
625 | # Both pr_3.yml and pr_4.yml have hakobe as the author, so only one unique assignee
626 | # And the author of pr_6.yml is ninjinkun.
627 | expect(@client).to have_received(:add_assignees).with(
628 | "motemen/git-pr-release", 1023, ["hakobe", "ninjinkun"]
629 | )
630 | expect(@client).not_to have_received(:request_pull_request_review)
631 | }
632 | end
633 |
634 | context "With request_pr_author_review enabled" do
635 | before {
636 | allow(@cli).to receive(:request_pr_author_review) { true }
637 | }
638 |
639 | it {
640 | subject
641 |
642 | expect(@client).to have_received(:update_pull_request).with(
643 | "motemen/git-pr-release",
644 | 1023,
645 | {
646 | title: "PR Title",
647 | body: "PR Body",
648 | }
649 | )
650 | expect(@client).not_to have_received(:add_assignees)
651 | # Both pr_3.yml and pr_4.yml have hakobe as the author
652 | # And the author of pr_6.yml is ninjinkun.
653 | expect(@client).to have_received(:request_pull_request_review).with(
654 | "motemen/git-pr-release", 1023, reviewers: ["hakobe", "ninjinkun"]
655 | )
656 | }
657 | end
658 | end
659 |
660 | describe "#detect_existing_release_pr" do
661 | subject { @cli.detect_existing_release_pr }
662 |
663 | before {
664 | @cli = configured_cli
665 |
666 | @client = double(Octokit::Client)
667 | allow(@cli).to receive(:client).with(no_args) { @client }
668 | }
669 |
670 | context "When exists" do
671 | before {
672 | @release_pr = double(head: double(ref: "staging"), base: double(ref: "master"))
673 | allow(@client).to receive(:pull_requests) { [@release_pr] }
674 | }
675 |
676 | it { is_expected.to eq @release_pr }
677 | end
678 |
679 | context "When not exists" do
680 | before {
681 | allow(@client).to receive(:pull_requests) { [] }
682 | }
683 |
684 | it { is_expected.to be_nil }
685 | end
686 | end
687 |
688 | describe "#pull_request_files" do
689 | subject { @cli.pull_request_files(@release_pr) }
690 |
691 | before {
692 | @cli = configured_cli
693 |
694 | @release_pr = double(number: 1023)
695 | @client = double(Octokit::Client)
696 | @changed_files = [double(Sawyer::Resource)]
697 | allow(@client).to receive(:pull_request_files) { @changed_files }
698 | allow(@client).to receive(:auto_paginate=)
699 | allow(@cli).to receive(:client) { @client }
700 | }
701 |
702 | it {
703 | is_expected.to eq @changed_files
704 |
705 | expect(@client).to have_received(:auto_paginate=).with(true)
706 | expect(@client).to have_received(:pull_request_files).with("motemen/git-pr-release", 1023)
707 | expect(@client).to have_received(:auto_paginate=).with(false)
708 | }
709 | end
710 | end
711 |
--------------------------------------------------------------------------------