committer-tools 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/Rakefile +8 -0
- data/committer-tools.gemspec +1 -1
- data/lib/committer-tools.rb +86 -91
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f27491ec9dd9013f8a904670616d75b24104ed0
|
4
|
+
data.tar.gz: 0abe91d59fad9d7bf04198a39a89bdaa9ad612b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2d65c2d115afcb9b730523dee35d2b5abc2683c06efa9499097b12a4f2bf519392cd58623117e7531b838085c7d8217a64a3d41271c69b9cc43b1ae523dd94d
|
7
|
+
data.tar.gz: 8c19598e5de009b554f6cf655e76954e85f4a8c1a6cf58155f61292cde40827cf8187d0027ba7152d0ca07df8c254845a32849942c729f0580590923854ab743
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -10,6 +10,7 @@ GEM
|
|
10
10
|
mime-types (3.1)
|
11
11
|
mime-types-data (~> 3.2015)
|
12
12
|
mime-types-data (3.2016.0521)
|
13
|
+
minitest (5.10.3)
|
13
14
|
netrc (0.11.0)
|
14
15
|
pry (0.11.2)
|
15
16
|
coderay (~> 1.1.0)
|
@@ -28,6 +29,7 @@ PLATFORMS
|
|
28
29
|
ruby
|
29
30
|
|
30
31
|
DEPENDENCIES
|
32
|
+
minitest
|
31
33
|
pry
|
32
34
|
rake
|
33
35
|
rb-readline
|
data/Rakefile
CHANGED
data/committer-tools.gemspec
CHANGED
data/lib/committer-tools.rb
CHANGED
@@ -1,8 +1,19 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'rest-client'
|
3
3
|
|
4
|
+
class HTTPHelper
|
5
|
+
def self.get(url)
|
6
|
+
RestClient.get(url, { params: { access_token: ENV['GH_TOKEN'] } }).body
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.get_json(url)
|
10
|
+
JSON.parse(get(url))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
4
14
|
class Lander
|
5
|
-
def run(pr, metadata)
|
15
|
+
def run(pr, github_pr, metadata)
|
16
|
+
check_to_land(github_pr, metadata)
|
6
17
|
introduce_commit(pr, metadata)
|
7
18
|
|
8
19
|
puts "[\u{2714}] Commit(s) applied locally. Please update to your liking, and then type 'continue'."
|
@@ -27,6 +38,24 @@ class Lander
|
|
27
38
|
`git commit --amend -m '#{msg}'`
|
28
39
|
end
|
29
40
|
|
41
|
+
def check_to_land(github_pr, metadata)
|
42
|
+
# At least 48 hours of review time
|
43
|
+
if Time.parse(github_pr['created_at']) > (Date.today - 2).to_time
|
44
|
+
puts "[✘] PR must remain open for at least 48 hours"
|
45
|
+
end
|
46
|
+
|
47
|
+
# At least two approvals
|
48
|
+
if (metadata[:reviewers].length < 2)
|
49
|
+
puts "[✘] PR must have at least two reviewers"
|
50
|
+
end
|
51
|
+
|
52
|
+
# No failing CI builds
|
53
|
+
failing_statuses = metadata[:ci_statuses].select { |job| job[:status] == 'failure' }
|
54
|
+
if (failing_statuses.length > 0)
|
55
|
+
puts "[✘] Failing builds on #{failing_statuses.map { |s| s[:name] }.join(', ')}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
30
59
|
def introduce_commit(pr, metadata)
|
31
60
|
# Clear current status
|
32
61
|
`git am --abort`
|
@@ -47,125 +76,91 @@ class Lander
|
|
47
76
|
end
|
48
77
|
end
|
49
78
|
|
50
|
-
class
|
51
|
-
|
52
|
-
|
53
|
-
REVIEWER_REGEX = /\* \[(.+?)\]\(.+?\) -\s\*\*(.+?)\*\* <(.+?)>/m
|
54
|
-
|
55
|
-
def initialize(github_pr)
|
56
|
-
@github_pr = github_pr
|
57
|
-
end
|
79
|
+
class MetadataCollector
|
80
|
+
NODE_README_URL = 'https://raw.githubusercontent.com/nodejs/node/master/README.md'
|
81
|
+
REVIEWER_REGEX = /\* \[(.+?)\]\(.+?\) -\s\*\*(.+?)\*\* <(.+?)>/m
|
58
82
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
83
|
+
def collect(github_pr)
|
84
|
+
{
|
85
|
+
pr_url: collect_pr_url(github_pr),
|
86
|
+
reviewers: collect_reviewers(github_pr),
|
87
|
+
ci_statuses: collect_ci_statuses(github_pr)
|
88
|
+
}
|
89
|
+
end
|
66
90
|
|
67
|
-
|
91
|
+
private
|
68
92
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
@github_pr['statuses_url'],
|
73
|
-
{ params: { access_token: ENV['GH_TOKEN'] } }
|
74
|
-
)
|
75
|
-
).map do |status|
|
76
|
-
{ name: status['context'], status: status['state'] }
|
77
|
-
end
|
93
|
+
def collect_ci_statuses(github_pr)
|
94
|
+
HTTPHelper.get_json(github_pr['statuses_url']).map do |status|
|
95
|
+
{ name: status['context'], status: status['state'] }
|
78
96
|
end
|
97
|
+
end
|
79
98
|
|
80
|
-
|
81
|
-
|
82
|
-
|
99
|
+
def collect_pr_url(github_pr)
|
100
|
+
"PR-URL: #{github_pr['html_url']}"
|
101
|
+
end
|
83
102
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
103
|
+
def collect_reviewers(github_pr)
|
104
|
+
# Collect a list of all possible reviewers
|
105
|
+
possible_reviewers = {}
|
106
|
+
readme = HTTPHelper.get(NODE_README_URL)
|
107
|
+
|
108
|
+
# GitHub being stupid...
|
109
|
+
# Treat every two lines as one...
|
110
|
+
readme.split("\n").each_slice(2).to_a.each do |a, b|
|
111
|
+
if (m = REVIEWER_REGEX.match("#{a} #{b}"))
|
112
|
+
possible_reviewers[m[1]] = {
|
113
|
+
name: m[2],
|
114
|
+
email: m[3]
|
115
|
+
}
|
98
116
|
end
|
117
|
+
end
|
99
118
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
119
|
+
# Use this list to identify reviewers for the current PR!
|
120
|
+
reviewer_usernames = HTTPHelper.get_json("#{github_pr['url']}/reviews").map do |review|
|
121
|
+
next unless review['state'] == 'APPROVED'
|
122
|
+
review['user']['login']
|
123
|
+
end.compact.uniq
|
105
124
|
|
106
|
-
|
107
|
-
|
125
|
+
reviewer_usernames.map do |reviewer_username|
|
126
|
+
user = possible_reviewers[reviewer_username]
|
108
127
|
|
109
|
-
|
110
|
-
end
|
128
|
+
"Reviewed-By: #{user[:name]} <#{user[:email]}>"
|
111
129
|
end
|
112
130
|
end
|
131
|
+
end
|
113
132
|
|
114
|
-
|
115
|
-
@pr = {}
|
116
|
-
@github_pr = {}
|
117
|
-
@metadata = {}
|
118
|
-
end
|
119
|
-
|
133
|
+
class Preparer
|
120
134
|
def run
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
check_to_land(@github_pr, @metadata)
|
135
|
+
pr = get_pr()
|
136
|
+
github_pr = get_github_pr(pr)
|
137
|
+
metadata = get_metadata(github_pr)
|
125
138
|
|
126
|
-
Lander.new.run(
|
139
|
+
Lander.new.run(pr, github_pr, metadata)
|
127
140
|
end
|
128
141
|
|
129
142
|
private
|
130
143
|
|
131
|
-
def check_to_land(github_pr, metadata)
|
132
|
-
# At least 48 hours of review time
|
133
|
-
if Time.parse(github_pr['created_at']) > (Date.today - 2).to_time
|
134
|
-
puts "[✘] PR must remain open for at least 48 hours"
|
135
|
-
end
|
136
|
-
|
137
|
-
# At least two approvals
|
138
|
-
if (metadata[:reviewers].length < 2)
|
139
|
-
puts "[✘] PR must have at least two reviewers"
|
140
|
-
end
|
141
|
-
|
142
|
-
# No failing CI builds
|
143
|
-
failing_statuses = metadata[:ci_statuses].select { |job| job[:status] == 'failure' }
|
144
|
-
if (failing_statuses.length > 0)
|
145
|
-
puts "[✘] Failing builds on #{failing_statuses.map { |s| s[:name] }.join(', ')}"
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
144
|
def get_github_pr(pr)
|
150
|
-
|
151
|
-
|
152
|
-
"https://api.github.com/repos/#{pr[:org]}/#{pr[:repo]}/pulls/#{pr[:id]}",
|
153
|
-
{ params: { access_token: ENV['GH_TOKEN'] } }
|
154
|
-
)
|
145
|
+
HTTPHelper.get_json(
|
146
|
+
"https://api.github.com/repos/#{pr[:org]}/#{pr[:repo]}/pulls/#{pr[:id]}"
|
155
147
|
)
|
156
148
|
end
|
157
149
|
|
158
150
|
def get_metadata(github_pr)
|
159
|
-
MetadataCollector.new(github_pr)
|
151
|
+
MetadataCollector.new.collect(github_pr)
|
160
152
|
end
|
161
153
|
|
162
154
|
def get_pr
|
163
155
|
puts "Please enter PR ID:"
|
164
156
|
pr_id = gets.strip!
|
165
157
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
158
|
+
begin
|
159
|
+
org, repo_and_id = pr_id.split('/')
|
160
|
+
repo, id = repo_and_id.split('#')
|
161
|
+
{ org: org, repo: repo, id: id }
|
162
|
+
rescue
|
163
|
+
raise "Invalid PR ID: #{pr_id}"
|
164
|
+
end
|
170
165
|
end
|
171
166
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: committer-tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Moss
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-11-
|
11
|
+
date: 2017-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|