pr-merger 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 07bcad60e1e3ea9859a006c2d128d9d6064b1e28d203af88807dea4085e19d70
4
+ data.tar.gz: cf613b6b664e8000846cbd262237a926911360927aa3420c1809bb6ea5b0e360
5
+ SHA512:
6
+ metadata.gz: 848dd327551abe6cd100640ef9b9844f7838e6c308b5a2a165e250e1564c33783e28edebdb8ec349b8cc7b9c3cb191ea665c67dc6a0216bf1026805ec4d1db0c
7
+ data.tar.gz: 3f15cdf97645f1d4ec9582154b660f3df64cf1412fb492b780dcd8c8cf491603aaa4c23255c943dc68c5f4523a22faacd46afe771b15cc00da4216f2c18eed64
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pr-merger.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016+ Cloudaper
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
13
+ all 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
21
+ THE SOFTWARE.
@@ -0,0 +1,67 @@
1
+ # Pull Request Merger
2
+
3
+ Merge open pull requests on GitHub all together to create a new branch with all changes.
4
+
5
+ Read more about the workflow we use the PR Merger for at [@cloudaper](https://github.com/cloudaper) in our [Medium story](https://medium.com/cloudaper/devops-hack-make-all-the-work-in-progress-accessible-with-pull-request-merger-47b58dc51207).
6
+
7
+ Any feedback or even a pull request welcomed!
8
+
9
+ ## Installation
10
+
11
+ You need Ruby to use PR Merger.
12
+
13
+ Run this command:
14
+
15
+ ```shell
16
+ gem install pr-merger
17
+ ```
18
+
19
+ or add
20
+
21
+ ```ruby
22
+ gem 'pr-merger'
23
+ ```
24
+
25
+ to your Gemfile.
26
+
27
+ ## Usage
28
+
29
+ ```shell
30
+ $ pr-merger --help
31
+ Usage: pr-merger --access-token TOKEN --base-repo REPO --base-branch BRANCH --merge-branch BRANCH [--fork-repo]
32
+ ```
33
+
34
+ You have to provide several arguments to PR Merger:
35
+
36
+ - **`--access-token`**
37
+ This is GitHub personal access token to access the repository details and update [statuses](https://help.github.com/articles/about-status-checks). You can generate one in [user's settings](https://github.com/settings/tokens); select `repo` scope. Please read the [know issues](#known-issues) section below.
38
+ E.g.: `472a3a8f5315a3435a295091a365d5f9fb736d84`.
39
+
40
+ - **`--base-repo`**
41
+ This is the name of the base repository.
42
+ E.g.: `cloudaper/pr-merger`.
43
+
44
+ - **`--base-branch`**
45
+ This is the name of the base branch, where the pull requests are merged to – usually master.
46
+ E.g.: `master`.
47
+
48
+ - **`--merge-branch`**
49
+ This is the name of newly created branch with merged pull requests.
50
+ E.g.: `merged-prs`
51
+
52
+ - **`--fork-repo`**
53
+ Add this option if merging from forked repository: this means the base repository will be used instead of fork for base branch.
54
+
55
+ The assembled command should look like this:
56
+
57
+ ```shell
58
+ pr-merger --access-token 2a3a8f5315a3435a295091a365d5f9fb736d84 --base-repo "cloudaper/pr-merger" --base-branch master --merge-branch merged-prs
59
+ ```
60
+
61
+ If there is any pull request you don't want to merge, just add `[skip merge]` after the pull request title.
62
+
63
+ ## Known issues
64
+
65
+ Currently there are two possible security issues, which you should take into account before using PR Merger. First, PR Merger is using _Personal access token_, which basically equals to your GitHub password, it can therefore access all the repositories the user has access to. Second, if you want to merge PRs from forked repositories, the machine you're running PR Merger at has to have access to all those repositories – this means [SSH deploy key](https://developer.github.com/v3/guides/managing-deploy-keys/#deploy-keys) cannot be used.
66
+
67
+ Recommended way to solve both those issues is to create a separate [machine user](https://developer.github.com/v3/guides/managing-deploy-keys/#machine-users) with access only to the repositories in question. However, the token still enables a full control of those repositories.
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.expand_path('lib')
3
+
4
+ require 'optparse'
5
+ require 'pr-merger'
6
+
7
+ def show_version
8
+ puts "pr-merger v#{PrMerger::VERSION}"
9
+
10
+ exit 0
11
+ end
12
+
13
+ def show_help
14
+ puts <<~HELP
15
+ Usage: pr-merger --access-token TOKEN --base-repo REPO --base-branch BRANCH --merge-branch BRANCH [--fork-repo]
16
+ HELP
17
+
18
+ exit 0
19
+ end
20
+
21
+ options = {}
22
+
23
+ parser = OptionParser.new do|opts|
24
+ opts.banner = "Usage: pr-merger [options]"
25
+
26
+ opts.on('--access-token TOKEN') do |option|
27
+ options[:access_token] = option
28
+ end
29
+
30
+ opts.on('--base-repo REPO') do |option|
31
+ options[:base_repo] = option
32
+ end
33
+
34
+ opts.on('--base-branch BRANCH') do |option|
35
+ options[:base_branch] = option
36
+ end
37
+
38
+ opts.on('--merge-branch BRANCH') do |option|
39
+ options[:merge_branch] = option
40
+ end
41
+
42
+ opts.on('--fork-repo') do
43
+ options[:fork_repo] = true
44
+ end
45
+
46
+ opts.on('-v', '--version') do
47
+ show_version
48
+ end
49
+
50
+ opts.on('--debug') do
51
+ options[:debug] = true
52
+ end
53
+
54
+ opts.on('-h', '--help') do
55
+ show_help
56
+ end
57
+ end
58
+
59
+ begin
60
+ parser.parse!
61
+
62
+ show_help if options.empty?
63
+
64
+ merger = PrMerger::Merger.new(access_token: options[:access_token])
65
+
66
+ exit merger.run(base_repo: options[:base_repo],
67
+ base_branch: options[:base_branch],
68
+ merge_branch: options[:merge_branch],
69
+ fork_repo: options[:fork_repo])
70
+
71
+ rescue => e
72
+ STDERR.puts "ERROR: #{e.message}"
73
+
74
+ if options[:debug]
75
+ STDERR.puts
76
+ STDERR.puts e.backtrace
77
+ end
78
+
79
+ exit 1
80
+ end
@@ -0,0 +1,122 @@
1
+ require 'pr-merger/version'
2
+ require 'tty-command'
3
+ require 'octokit'
4
+
5
+ Octokit.auto_paginate = true
6
+
7
+ # Merges all opened GitHub PRs to a new branch
8
+ module PrMerger
9
+ class Merger
10
+ APP_CONTEXT = 'pr-merger'.freeze
11
+ SKIP_MERGE = '[skip merge]'.freeze
12
+
13
+ def initialize(access_token:)
14
+ @gh = Octokit::Client.new(access_token: access_token)
15
+ end
16
+
17
+ def run(base_repo:, base_branch:, merge_branch:, fork_repo:)
18
+ @base_repo = base_repo
19
+ @base_branch = base_branch
20
+
21
+ pull_requests = @gh.pull_requests(@base_repo, state: 'open').reverse
22
+
23
+ cmd = TTY::Command.new
24
+ active_branch_result = cmd.run! 'git rev-parse --abbrev-ref HEAD'
25
+
26
+ if active_branch_result.success?
27
+ previous_branch = active_branch_result.out.strip
28
+ else
29
+ previous_branch = cmd.run('cat .git/HEAD').strip
30
+ end
31
+
32
+ cmd.run 'git checkout', @base_branch
33
+
34
+ if fork_repo
35
+ cmd.run! 'git remote add upstream', "git@github.com:#{@base_repo}.git"
36
+ cmd.run 'git fetch upstream', @base_branch
37
+ cmd.run 'git reset --hard', "upstream/#{@base_branch}"
38
+ else
39
+ cmd.run 'git reset --hard', "origin/#{@base_branch}"
40
+ end
41
+
42
+ cmd.run 'git checkout -b', merge_branch
43
+
44
+ merge_statuses = pull_requests.map do |pr|
45
+ process_pr(pr, cmd)
46
+ end
47
+
48
+ cmd.run 'git checkout', previous_branch
49
+
50
+ merge_statuses.all? { |status| status }
51
+ end
52
+
53
+ private
54
+
55
+ # Process a given pull request
56
+ def process_pr(pr, cmd)
57
+ head = pr[:head]
58
+ repo = head[:repo]
59
+
60
+ pending_status(pr, 'Merge in progress.')
61
+
62
+ return true if skip_pr?(pr)
63
+
64
+ begin
65
+ cmd.run 'git fetch', repo[:ssh_url], head[:ref] if repo
66
+
67
+ merge_status = cmd.run! 'git merge --no-ff --no-edit', head[:sha]
68
+
69
+ if merge_status.success?
70
+ success_status(pr, "Merge with '#{@base_branch}' was successful.")
71
+ else
72
+ cmd.run 'git merge --abort'
73
+
74
+ message = "Failed to merge '#{head[:ref]} with #{@base_branch}."
75
+ failure_status(pr, message)
76
+ end
77
+ rescue => e
78
+ failure_status(pr, "Merge encountered an error: #{e.message}.")
79
+
80
+ return false
81
+ end
82
+
83
+ true
84
+ end
85
+
86
+ # Skip a given pull request if it includes skip merge message
87
+ def skip_pr?(pr)
88
+ return false unless pr[:title].include?(SKIP_MERGE)
89
+
90
+ failure_status(pr, "Skipping #{pr[:head][:ref]}.")
91
+
92
+ true
93
+ end
94
+
95
+ def pending_status(pr, message)
96
+ send_status(pr, 'pending', message)
97
+ end
98
+
99
+ def error_status(pr, message)
100
+ send_status(pr, 'error', message)
101
+ end
102
+
103
+ def failure_status(pr, message)
104
+ send_status(pr, 'failure', message)
105
+ end
106
+
107
+ def success_status(pr, message)
108
+ send_status(pr, 'success', message)
109
+ end
110
+
111
+ # Send Octokit status
112
+ def send_status(pr, status, message)
113
+ @gh.create_status(
114
+ @base_repo,
115
+ pr[:head][:sha],
116
+ status,
117
+ context: APP_CONTEXT,
118
+ description: message[0..140]
119
+ )
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,3 @@
1
+ module PrMerger
2
+ VERSION = '1.0.0'.freeze
3
+ end
@@ -0,0 +1,26 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'pr-merger/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'pr-merger'
7
+ spec.version = PrMerger::VERSION
8
+ spec.authors = ['Tibor Szolár', 'Josef Strzibny']
9
+ spec.email = ['tibor.szolar@seznam.cz', 'strzibny@strzibny.name']
10
+
11
+ spec.summary = %q{Merges all opened GitHub PRs to a new branch.}
12
+ spec.description = %q{}
13
+ spec.homepage = 'https://github.com/cloudaper/pr-merger'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = 'bin'
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.8'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+
24
+ spec.add_runtime_dependency 'octokit', '~> 4.0'
25
+ spec.add_runtime_dependency 'tty-command', '~> 0.2'
26
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pr-merger
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Tibor Szolár
8
+ - Josef Strzibny
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-09-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.8'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.8'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: octokit
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '4.0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '4.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: tty-command
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0.2'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.2'
70
+ description: ''
71
+ email:
72
+ - tibor.szolar@seznam.cz
73
+ - strzibny@strzibny.name
74
+ executables:
75
+ - pr-merger
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".gitignore"
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - bin/pr-merger
85
+ - lib/pr-merger.rb
86
+ - lib/pr-merger/version.rb
87
+ - pr-merger.gemspec
88
+ homepage: https://github.com/cloudaper/pr-merger
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.7.7
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Merges all opened GitHub PRs to a new branch.
112
+ test_files: []