committer-tools 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|