committer-tools 0.1.0
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 +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +37 -0
- data/README.md +21 -0
- data/Rakefile +3 -0
- data/committer-tools.gemspec +20 -0
- data/exe/committer-tools +14 -0
- data/lib/committer-tools.rb +184 -0
- metadata +65 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fcb0ca6adc4041de06bee3d441c7b50e83aee16a
|
4
|
+
data.tar.gz: f760a7d9bbebf9615d2580984650cb5eab6b0dee
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 32fa3be15f3190091b8ca272f05c7f7b4a1edf2d76032f089a344feccd41b60781b3dada491db2154c6b3c2573c46be9d4a43a1bc31212a92e566b0268aac7c6
|
7
|
+
data.tar.gz: 821cf9d1df32651e37680dc047b133733c8738e73ac13e4194af287d8b9a1e08c5d005f42b32d6f597d2f689944eb104272f31f271373b93b643f7456d97cc1b
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
coderay (1.1.2)
|
5
|
+
domain_name (0.5.20170404)
|
6
|
+
unf (>= 0.0.5, < 1.0.0)
|
7
|
+
http-cookie (1.0.3)
|
8
|
+
domain_name (~> 0.5)
|
9
|
+
method_source (0.9.0)
|
10
|
+
mime-types (3.1)
|
11
|
+
mime-types-data (~> 3.2015)
|
12
|
+
mime-types-data (3.2016.0521)
|
13
|
+
netrc (0.11.0)
|
14
|
+
pry (0.11.2)
|
15
|
+
coderay (~> 1.1.0)
|
16
|
+
method_source (~> 0.9.0)
|
17
|
+
rake (12.0.0)
|
18
|
+
rb-readline (0.5.4)
|
19
|
+
rest-client (2.0.2)
|
20
|
+
http-cookie (>= 1.0.2, < 2.0)
|
21
|
+
mime-types (>= 1.16, < 4.0)
|
22
|
+
netrc (~> 0.8)
|
23
|
+
unf (0.1.4)
|
24
|
+
unf_ext
|
25
|
+
unf_ext (0.0.7.4)
|
26
|
+
|
27
|
+
PLATFORMS
|
28
|
+
ruby
|
29
|
+
|
30
|
+
DEPENDENCIES
|
31
|
+
pry
|
32
|
+
rake
|
33
|
+
rb-readline
|
34
|
+
rest-client
|
35
|
+
|
36
|
+
BUNDLED WITH
|
37
|
+
1.14.6
|
data/README.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# committer-tools-rb
|
2
|
+
|
3
|
+
Working on [committer-tools](https://github.com/maclover7/committer-tools) for right now because Promises are givin me a huge headache :)
|
4
|
+
|
5
|
+
### Usage
|
6
|
+
|
7
|
+
Globally:
|
8
|
+
|
9
|
+
```bash
|
10
|
+
gem install committer-tools
|
11
|
+
```
|
12
|
+
|
13
|
+
In actual working directory repo:
|
14
|
+
|
15
|
+
```
|
16
|
+
GH_TOKEN=mytoken123 committer-tools land
|
17
|
+
```
|
18
|
+
|
19
|
+
### License
|
20
|
+
|
21
|
+
Copyright (c) 2017+ Jon Moss under the MIT License.
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'committer-tools'
|
5
|
+
spec.version = '0.1.0'
|
6
|
+
spec.authors = 'Jon Moss'
|
7
|
+
spec.email = 'me@jonathanmoss.me'
|
8
|
+
|
9
|
+
spec.summary = 'A Node.js collaborator CLI utility.'
|
10
|
+
spec.description = 'A Node.js collaborator CLI utility.'
|
11
|
+
spec.homepage = 'https://github.com/maclover7/committer-tools-rb'
|
12
|
+
spec.license = 'MIT'
|
13
|
+
|
14
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
15
|
+
spec.bindir = 'exe'
|
16
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
17
|
+
spec.require_paths = ['lib']
|
18
|
+
|
19
|
+
spec.add_runtime_dependency 'rest-client'
|
20
|
+
end
|
data/exe/committer-tools
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
4
|
+
require 'committer-tools'
|
5
|
+
|
6
|
+
trap("SIGINT") { exit 0 }
|
7
|
+
|
8
|
+
if ARGV.shift == 'land'
|
9
|
+
Preparer.new.run
|
10
|
+
else
|
11
|
+
puts "Welcome to committer-tools!"
|
12
|
+
puts "Currently the only command is 'land', which will ready a Node.js Foundation pull request to be landed."
|
13
|
+
exit 0
|
14
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rest-client'
|
3
|
+
|
4
|
+
class Lander
|
5
|
+
def run(pr, metadata)
|
6
|
+
introduce_commit(pr, metadata)
|
7
|
+
|
8
|
+
puts "[\u{2714}] Commit(s) applied locally. Please update to your liking, and then type 'continue'."
|
9
|
+
continue = gets.strip!
|
10
|
+
|
11
|
+
while !continue do
|
12
|
+
sleep
|
13
|
+
end
|
14
|
+
|
15
|
+
if continue && continue == 'continue'
|
16
|
+
add_metadata_to_commit(metadata)
|
17
|
+
validate_commit
|
18
|
+
|
19
|
+
puts "\n[\u{2714}] committer-tool is done! Edit away to your content, and then push away :)"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def add_metadata_to_commit(metadata)
|
26
|
+
msg = `git log --format=%B -n 1` + [metadata[:pr_url], metadata[:reviewers]].compact.join("\n")
|
27
|
+
`git commit --amend -m '#{msg}'`
|
28
|
+
end
|
29
|
+
|
30
|
+
def introduce_commit(pr, metadata)
|
31
|
+
# Clear current status
|
32
|
+
`git am --abort`
|
33
|
+
`git rebase --abort`
|
34
|
+
`git checkout master`
|
35
|
+
|
36
|
+
# Update from upstream
|
37
|
+
`git fetch upstream`
|
38
|
+
`git merge --ff-only upstream/master`
|
39
|
+
|
40
|
+
# Download and apply patch
|
41
|
+
`curl -L https://github.com/#{pr[:org]}/#{pr[:repo]}/pull/#{pr[:id]}.patch | git am --whitespace=fix`
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_commit
|
45
|
+
puts "Running core-validate-commit..."
|
46
|
+
system('core-validate-commit 6b050dc70bd4aa4e7914f694927ef01b7b1d48a9')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Preparer
|
51
|
+
class MetadataCollector
|
52
|
+
NODE_README_URL = 'https://raw.githubusercontent.com/nodejs/node/master/README.md'
|
53
|
+
REVIEWER_REGEX = /\* \[(.+?)\]\(.+?\) -\s\*\*(.+?)\*\* <(.+?)>/m
|
54
|
+
|
55
|
+
def initialize(github_pr)
|
56
|
+
@github_pr = github_pr
|
57
|
+
end
|
58
|
+
|
59
|
+
def collect
|
60
|
+
{
|
61
|
+
pr_url: collect_pr_url,
|
62
|
+
reviewers: collect_reviewers,
|
63
|
+
ci_statuses: collect_ci_statuses
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def collect_ci_statuses
|
70
|
+
JSON.parse(
|
71
|
+
RestClient.get(
|
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
|
78
|
+
end
|
79
|
+
|
80
|
+
def collect_pr_url
|
81
|
+
"PR-URL: #{@github_pr['html_url']}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def collect_reviewers
|
85
|
+
# Collect a list of all possible reviewers
|
86
|
+
possible_reviewers = {}
|
87
|
+
readme = RestClient.get(NODE_README_URL, { params: { access_token: ENV['GH_TOKEN'] } }).body
|
88
|
+
|
89
|
+
# GitHub being stupid...
|
90
|
+
# Treat every two lines as one...
|
91
|
+
readme.split("\n").each_slice(2).to_a.each do |a, b|
|
92
|
+
if (m = REVIEWER_REGEX.match("#{a} #{b}"))
|
93
|
+
possible_reviewers[m[1]] = {
|
94
|
+
name: m[2],
|
95
|
+
email: m[3]
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Use this list to identify reviewers for the current PR!
|
101
|
+
reviewer_usernames = JSON.parse(RestClient.get("#{@github_pr['url']}/reviews", { params: { access_token: ENV['GH_TOKEN'] } })).map do |review|
|
102
|
+
next unless review['state'] == 'APPROVED'
|
103
|
+
review['user']['login']
|
104
|
+
end.compact.uniq
|
105
|
+
|
106
|
+
reviewer_usernames.map do |reviewer_username|
|
107
|
+
user = possible_reviewers[reviewer_username]
|
108
|
+
|
109
|
+
"Reviewed-By: #{user[:name]} <#{user[:email]}>"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def initialize
|
115
|
+
@pr = {}
|
116
|
+
@github_pr = {}
|
117
|
+
@metadata = {}
|
118
|
+
end
|
119
|
+
|
120
|
+
def run
|
121
|
+
@pr = get_pr()
|
122
|
+
@github_pr = get_github_pr(@pr)
|
123
|
+
@metadata = get_metadata(@github_pr)
|
124
|
+
check_to_land(@github_pr, @metadata)
|
125
|
+
|
126
|
+
Lander.new.run(@pr, @metadata)
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
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
|
+
def get_github_pr(pr)
|
150
|
+
JSON.parse(
|
151
|
+
RestClient.get(
|
152
|
+
"https://api.github.com/repos/#{pr[:org]}/#{pr[:repo]}/pulls/#{pr[:id]}",
|
153
|
+
{ params: { access_token: ENV['GH_TOKEN'] } }
|
154
|
+
)
|
155
|
+
)
|
156
|
+
end
|
157
|
+
|
158
|
+
def get_metadata(github_pr)
|
159
|
+
MetadataCollector.new(github_pr).collect
|
160
|
+
end
|
161
|
+
|
162
|
+
def get_pr
|
163
|
+
puts "Please enter PR ID:"
|
164
|
+
pr_id = gets.strip!
|
165
|
+
|
166
|
+
org, repo_and_id = pr_id.split('/')
|
167
|
+
repo, id = repo_and_id.split('#')
|
168
|
+
|
169
|
+
{ org: org, repo: repo, id: id }
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
###
|
174
|
+
# What this tool does:
|
175
|
+
###
|
176
|
+
# * get pr id
|
177
|
+
# * get pr from github api
|
178
|
+
# * put together metadata for commit
|
179
|
+
# * do a few checks, make sure ok to land
|
180
|
+
# * figure out squashing commit, make sure final message ok
|
181
|
+
##
|
182
|
+
# What you have to do:
|
183
|
+
##
|
184
|
+
# * push commit
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: committer-tools
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jon Moss
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-11-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rest-client
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: A Node.js collaborator CLI utility.
|
28
|
+
email: me@jonathanmoss.me
|
29
|
+
executables:
|
30
|
+
- committer-tools
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- Gemfile
|
35
|
+
- Gemfile.lock
|
36
|
+
- README.md
|
37
|
+
- Rakefile
|
38
|
+
- committer-tools.gemspec
|
39
|
+
- exe/committer-tools
|
40
|
+
- lib/committer-tools.rb
|
41
|
+
homepage: https://github.com/maclover7/committer-tools-rb
|
42
|
+
licenses:
|
43
|
+
- MIT
|
44
|
+
metadata: {}
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 2.6.14
|
62
|
+
signing_key:
|
63
|
+
specification_version: 4
|
64
|
+
summary: A Node.js collaborator CLI utility.
|
65
|
+
test_files: []
|