committer-tools 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'pry'
4
+ gem 'rake'
5
+ gem 'rb-readline'
6
+ gem 'rest-client'
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,3 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'bundler/gem_tasks'
@@ -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
@@ -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\*\*(.+?)\*\* &lt;(.+?)&gt;/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: []