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 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: []