lita-jls 0.0.11

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.
@@ -0,0 +1,321 @@
1
+ require "cabin"
2
+ require "tmpdir"
3
+ require "tempfile"
4
+ require "fileutils"
5
+ require "insist"
6
+ require "uri"
7
+ require "lita-jls/bot_builder"
8
+ require "lita-jls/repository"
9
+ require "lita-jls/github_url_parser"
10
+ require "lita-jls/util"
11
+
12
+ # TODO(sissel): This code needs some suuuper serious refactoring and testing improvements.
13
+ # TODO(sissel): Remove any usage of Rugged. This library requires compile-time
14
+ # settings of libgit2 and that's an annoying battle.
15
+
16
+ module Lita
17
+ module Handlers
18
+ class Jls < Handler
19
+ include LitaJLS::Util
20
+
21
+ route /^merge(?<dry>\?)? (?<pr_url>[^ ]+) (?<branchspec>.*)$/, :merge,
22
+ :command => true,
23
+ :help => { "merge https://github.com/ORG/PROJECT/pull/NUMBER branch1 [branch2 ...]" => "merges a PR into one or more branches. To see if a merge is successful, use 'merge? project#pr branch1 [branch2 ...]" }
24
+
25
+ route /^cla(?<dry>\?)? (?<pr_url>[^ ]+)$/, :cla,
26
+ :command => true,
27
+ :help => { "cla https://github.com/ORG/PROJECT/pull/NUMBER" => "CLA check for a giaven PR" }
28
+
29
+ route /^\(tableflip\)$/, :tableflip,
30
+ :command => true,
31
+ :help => { "(tableflip)" => "Fix whatever just broke. Probably git is going funky, so I will purge my local git junk" }
32
+
33
+ route /^ping/, :ping, :command => true
34
+
35
+ route /^publish\s(?<git_url>[^ ]+)$/, :publish,
36
+ :command => true,
37
+ :restrict_to => :logstash,
38
+ :help => { 'publish https://github.com/ORG/project' => 'Install dependencies, Run test, build gem, publish and compare version on rubygems' }
39
+
40
+ route /^(why computer(s?) so bad\?|explain)/i, :pop_exception,
41
+ :command => true,
42
+ :help => { 'explain or why computers so bad?' => 'return the last exception from redis' }
43
+
44
+ route /^migrate_pr (?<source_url>[^ ]+) (?<destination_url>[^ ]+)$/, :migrate_pr,
45
+ :command => true,
46
+ :help => { 'migrate_pr https://github.com/elasticsearch/logstash/pull/1452 "\
47
+ + "https://github.com/logstash-plugins/logstash-codec-line' => 'migrate pr from one repo to another' }
48
+
49
+ REMOTE = "origin"
50
+ URLBASE = "https://github.com/"
51
+ LIMIT_EXCEPTIONS_HISTORY = 20
52
+
53
+ RUBY_VERSION = "jruby-1.7.16"
54
+
55
+ on :loaded, :setup
56
+
57
+ def self.default_config(config)
58
+ config.default_organization = nil
59
+ end
60
+
61
+ def pop_exception(msg)
62
+ public_response = ['Commencing automated assembly. Estimated completion time is five hours.',
63
+ 'That is the only way, sir.',
64
+ 'Sir, please may I request just a few hours to calibrate.' ]
65
+
66
+ e = @redis.lpop(:exception)
67
+
68
+ msg.reply(public_response.sample)
69
+
70
+ logger.debug("pop exception", :exception => e)
71
+
72
+ if e
73
+ e = JSON.parse(e)
74
+
75
+ msg.reply_privately("exception: #{e.delete('exception')}")
76
+ msg.reply_privately("message: #{e.delete('message')}")
77
+ msg.reply_privately("backtrace: #{e.delete('backtrace')}")
78
+
79
+ # Print the remaining context
80
+ e.each do |key, value|
81
+ msg.reply_privately("#{key}: #{value}")
82
+ end
83
+ else
84
+ msg.reply_privately("No exception saved.")
85
+ end
86
+ end
87
+
88
+ def push_exception(e, context = {})
89
+ error = {
90
+ "exception" => e.exception,
91
+ "message" => e.message,
92
+ "backtrace" => e.backtrace,
93
+ }.merge(context)
94
+
95
+ @redis.lpush(:exception, error.to_json)
96
+ @redis.ltrim(:exception, 0, LIMIT_EXCEPTIONS_HISTORY)
97
+ end
98
+
99
+ def publish(msg)
100
+ git_url = msg.match_data["git_url"]
101
+
102
+ logger.info('publish', :url => git_url)
103
+
104
+ github_parser = LitaJLS::GithubUrlParser.parse(git_url, :link => :repository)
105
+ github_parser.validate!
106
+
107
+ repository = LitaJLS::Repository.new(github_parser)
108
+ repository.clone
109
+ repository.switch_branch('master')
110
+
111
+ builder = LitaJLS::BotBuilder.new(repository.git_path, { :ruby_version => RUBY_VERSION })
112
+
113
+ msg.reply("publishing (allthethings) for project: #{builder.project_name} branch: master")
114
+
115
+ reporter = LitaJLS::Reporter::HipChat.new(builder.build)
116
+ reporter.format(msg)
117
+ rescue => e
118
+ push_exception(e, :project => "#{github_parser.user}/#{github_parser.project}")
119
+ msg.reply("(stare) Error: #{e.inspect}")
120
+ raise
121
+ end # def publish
122
+
123
+ def ping(msg)
124
+ msg.reply("(chompy)")
125
+ end
126
+
127
+ def setup(*args)
128
+ ENV["PAGER"] = "cat"
129
+ @@logger_subscription ||= logger.subscribe(STDOUT)
130
+
131
+ @redis ||= Redis::Namespace.new("opsbot", redis: Lita.redis)
132
+ end
133
+
134
+ def cla(msg)
135
+ @cla_uri = config.cla_uri
136
+ pull = msg.match_data["pr_url"]
137
+ pull_path = URI.parse(pull).path
138
+ _, user, project, _, pr = pull_path.split("/")
139
+ cla?("#{user}/#{project}", pr)
140
+ msg.reply("#{user}/#{project}##{pr} CLA OK (freddie)")
141
+ rescue => e
142
+ msg.reply("cla check error: #{e}")
143
+ push_exception(e, :project => "#{user}/#{project}", :pr => pr)
144
+ end
145
+
146
+ def migrate_pr(msg)
147
+ source_url = msg.match_data["source_url"]
148
+ destination_url = msg.match_data["destination_url"]
149
+ if source_url.nil? || destination_url.nil?
150
+ raise "Invalid paramaters provided #{msg}"
151
+ end
152
+
153
+ destination_github_parser = parse_github_url(destination_url)
154
+ source_github_parser = parse_github_url(source_url)
155
+
156
+ pr_num = source_github_parser.pr
157
+ source_github_pr = github_get_pr("#{source_github_parser.user}/#{source_github_parser.project}", pr_num)
158
+
159
+ # Clone destination dir, patch and then push branch
160
+ repository = LitaJLS::Repository.new(destination_github_parser)
161
+ repository.clone if Dir["#{repository.git_path}/*"].empty?
162
+ repository.switch_branch("master")
163
+
164
+ # create a branch like pr/1234
165
+ pr_branch = "bot-migrated-pr/#{pr_num}"
166
+ repository.delete_local_branch(pr_branch, true)
167
+ repository.switch_branch(pr_branch, true)
168
+
169
+ patch_file = download_patch(source_github_pr[:patch_url], pr_num)
170
+
171
+ # Apply patch on repo
172
+ begin
173
+ repository.git_patch(patch_file.path)
174
+ repository.git_push_branch(pr_branch)
175
+ rescue => e
176
+ msg.reply("Error while migrating pr: #{e}")
177
+ push_exception(e, :source_url => source_url,
178
+ :destination_url => destination_url)
179
+ ensure
180
+ patch_file.unlink
181
+ end
182
+
183
+ # create the migrated PR in the destination repo
184
+ github_create_pr("#{destination_github_parser.user}/#{destination_github_parser.project}",
185
+ pr_branch, source_github_pr[:title],
186
+ source_github_pr[:body] + "\nMoved from #{source_url}")
187
+ end
188
+
189
+ @private
190
+ # downloads the patch file in mail format and saves it to a file
191
+ def download_patch(pr_url, pr_num)
192
+ http = Faraday.new("https://github.com")
193
+ response = http.get(URI.parse(pr_url).path)
194
+ if response.status != 200
195
+ raise "Unable to fetch pull request #{pr_url}"
196
+ end
197
+
198
+ patch_file = Tempfile.new("#{pr_num}.patch")
199
+
200
+ begin
201
+ #TODO: Use chunked writes
202
+ patch_file.write(response.body)
203
+ patch_file.close
204
+ rescue => e
205
+ raise "Error while downloading pr: #{pr_url}, exception #{e}"
206
+ end
207
+
208
+ return patch_file
209
+ end
210
+
211
+ @private
212
+ def parse_github_url(url)
213
+ github_parser = LitaJLS::GithubUrlParser.parse(url, :link => :repository)
214
+ github_parser.validate!
215
+ return github_parser
216
+ end
217
+
218
+ def merge(msg)
219
+ @cla_uri = config.cla_uri
220
+ FileUtils.mkdir_p(workdir) unless File.directory?(workdir)
221
+ pull = msg.match_data["pr_url"]
222
+ pull_path = URI.parse(pull).path
223
+ _, user, project, _, pr = pull_path.split("/")
224
+
225
+ if user.nil? || project.nil? || pr.nil? || pull !~ /^https:\/\/github.com\//
226
+ raise "Invalid URL. Expected something like: https://github.com/elasticsearch/snacktime/pull/12345"
227
+ end
228
+
229
+ branchspec = msg.match_data["branchspec"]
230
+ dry_run = msg.match_data["dry"]
231
+
232
+ begin
233
+ cla?("#{user}/#{project}", pr)
234
+ rescue => e
235
+ msg.reply("(firstworldproblems) cla check failed for #{user}/#{project}##{pr}.\n #{e}")
236
+ push_exception(e, :project => "#{user}/#{project}", :pr => pr)
237
+ return
238
+ end
239
+
240
+ url = File.join(URLBASE, user, project)
241
+ #git_url = "git@github.com:/#{user}/#{project}.git"
242
+ git_url = url
243
+ pr_url = File.join(url, "pull", "#{pr}.patch")
244
+ gitpath = gitdir(project)
245
+ branches = branchspec.split(/\s+/)
246
+
247
+ logger.info("Cloning git repo", :url => git_url, :gitpath => gitpath)
248
+ repo = clone_at(git_url, gitpath)
249
+
250
+ git(gitpath, "am", "--abort") if File.directory?(".git/rebase-apply")
251
+
252
+ # TODO(sissel): Fetch the PR patch
253
+ logger.info("Fetching PR patch", :url => pr_url)
254
+ http = Faraday.new("https://github.com")
255
+ response = http.get(URI.parse(pr_url).path)
256
+ if !response.success?
257
+ logger.warn("Failed fetching patch", :url => pr_url, :status => response.status, :headers => response.headers)
258
+ msg.reply("(grumpycat) Failed fetching patch. Cannot continue!")
259
+ return
260
+ end
261
+
262
+ patch = response.body
263
+
264
+ # For each branch, try to merge
265
+ repo = gitpath
266
+ branches.each do |branch|
267
+ begin
268
+ logger.info("Switching branches", :branch => branch, :repo => gitpath)
269
+ git(gitpath, "checkout", branch)
270
+ git(gitpath, "reset", "--hard", "#{REMOTE}/#{branch}")
271
+ git(gitpath, "pull", "--ff-only")
272
+ apply_patch(repo, patch) do |commit|
273
+ # Append the PR number to commit message
274
+
275
+ # Use "Fixes #XYZ" to make the PR get closed upon commit.
276
+ # https://help.github.com/articles/closing-issues-via-commit-messages
277
+ commit[:message] += "\nFixes ##{pr}"
278
+ end
279
+ rescue => e
280
+ push_exception(e, :project => "#{user}/#{project}", :pr => pr, :branch => branch)
281
+ msg.reply("(jackie) Failed attempting to merge #{user}/#{project}##{pr} into #{branch}: #{e}")
282
+ raise
283
+ end
284
+ end
285
+
286
+ # At this point, all branches merged successfully. Time to push!
287
+ if dry_run
288
+ msg.reply("(success) Merging was successful #{user}/#{project}##{pr} into: #{branchspec}.\n(but I did not push it)")
289
+ else
290
+ msg.reply("(success) #{user}/#{project}##{pr} merged into: #{branchspec}")
291
+ git(gitpath, "push", REMOTE, *branches)
292
+
293
+ labels = branches.reject { |b| b == "master" }
294
+ github_issue_label("#{user}/#{project}", pr.to_i, labels)
295
+ end
296
+ github_issue_comment("#{user}/#{project}", pr.to_i, "Merged sucessfully into #{branchspec}!")
297
+ rescue => e
298
+ push_exception(e, :project => "#{user}/#{project}", :pr => pr, :branch => branches)
299
+ msg.reply("(stare) Error: #{e.inspect}")
300
+ raise
301
+ end # def merge
302
+
303
+ def tableflip(msg)
304
+ logger.debug("(fliptable), remove the git directory")
305
+
306
+ begin
307
+ dir = workdir("gitbase")
308
+ insist { dir } =~ /\/lita-jls/ # Just in case, before we go purging things...
309
+ FileUtils.rm_r(dir) if File.directory?(dir)
310
+ msg.reply("Git: (tableflip) (success)")
311
+ rescue => e
312
+ push_exception(e)
313
+ msg.reply("Git: (tableflip) (huh): #{e}")
314
+ raise e
315
+ end
316
+ end
317
+ end # class Jls
318
+
319
+ Lita.register_handler(Jls)
320
+ end
321
+ end
@@ -0,0 +1,41 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "lita-jls"
3
+ spec.version = "0.0.11"
4
+ spec.authors = ["Jordan Sissel"]
5
+ spec.email = ["jls@semicomplete.com"]
6
+ spec.description = %q{Some stuff for the lita.io bot}
7
+ spec.summary = spec.description
8
+ spec.homepage = "http://example.com/"
9
+ spec.license = "MIT"
10
+ spec.metadata = { "lita_plugin_type" => "handler" }
11
+
12
+ spec.files = `git ls-files`.split($/)
13
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
14
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
15
+ spec.require_paths = ["lib"]
16
+
17
+ spec.add_runtime_dependency "lita", ">= 3.3"
18
+ spec.add_runtime_dependency "cabin", ">= 0"
19
+ spec.add_runtime_dependency "faraday", ">= 0"
20
+
21
+ # For access to Github's api
22
+ spec.add_runtime_dependency "octokit", ">= 0"
23
+ # For netrc support in octokit
24
+ spec.add_runtime_dependency "netrc"
25
+
26
+ # For parsing github's .patch files (mbox format)
27
+ spec.add_runtime_dependency "mbox", ">= 0"
28
+ spec.add_runtime_dependency "insist"
29
+ spec.add_runtime_dependency 'gems', '~> 0.8.3'
30
+ spec.add_runtime_dependency 'semverly', '~> 1.0.0'
31
+
32
+ spec.add_development_dependency "bundler", "~> 1.3"
33
+ spec.add_development_dependency "rake"
34
+ spec.add_development_dependency "stud"
35
+ spec.add_development_dependency "rspec", ">= 3.0.0"
36
+ spec.add_development_dependency "simplecov"
37
+ spec.add_development_dependency "pry"
38
+ spec.add_development_dependency "coveralls"
39
+ spec.add_development_dependency "vcr"
40
+ spec.add_development_dependency "webmock"
41
+ end
@@ -0,0 +1,4 @@
1
+ en:
2
+ lita:
3
+ handlers:
4
+ jls:
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gem 'this-gem-doesnt-exist'
@@ -0,0 +1,29 @@
1
+ Gem::Specification.new do |s|
2
+
3
+ s.name = 'logstash-codec-edn'
4
+ s.version = '0.1.3'
5
+ s.licenses = ['Apache License (2.0)']
6
+ s.summary = "Codec to process EDN data"
7
+ s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
8
+ s.authors = ["Elasticsearch"]
9
+ s.email = 'info@elasticsearch.com'
10
+ s.homepage = "http://www.elasticsearch.org/guide/en/logstash/current/index.html"
11
+ s.require_paths = ["lib"]
12
+
13
+ # Files
14
+ s.files = `git ls-files`.split($\)
15
+
16
+ # Tests
17
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
+
19
+ # Special flag to let us know this is actually a logstash plugin
20
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "codec" }
21
+
22
+ # Gem dependencies
23
+ s.add_runtime_dependency 'logstash', '>= 1.4.0', '< 2.0.0'
24
+
25
+ s.add_runtime_dependency 'edn'
26
+
27
+ s.add_development_dependency 'logstash-devutils'
28
+ end
29
+
@@ -0,0 +1,3 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "logstash"
3
+ end
@@ -0,0 +1,3 @@
1
+ module Testmore
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,9 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'testmore/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dummy-gem-dont-publish"
8
+ spec.version = Testmore::VERSION
9
+ end
@@ -0,0 +1,96 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://rubygems.org/api/v1/versions/logstash-output-s3.yaml
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept-Encoding:
11
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
+ Accept:
13
+ - "*/*"
14
+ User-Agent:
15
+ - Gems 0.8.3
16
+ - Ruby
17
+ Connection:
18
+ - keep-alive
19
+ Keep-Alive:
20
+ - '30'
21
+ Content-Type:
22
+ - application/x-www-form-urlencoded
23
+ response:
24
+ status:
25
+ code: 200
26
+ message: OK
27
+ headers:
28
+ Server:
29
+ - nginx
30
+ Date:
31
+ - Tue, 16 Dec 2014 16:12:50 GMT
32
+ Content-Type:
33
+ - text/yaml
34
+ Transfer-Encoding:
35
+ - chunked
36
+ Connection:
37
+ - keep-alive
38
+ Status:
39
+ - 200 OK
40
+ Etag:
41
+ - '"7341898c9b8c18bb4a2f72cafa13f98e"'
42
+ Last-Modified:
43
+ - Thu, 06 Nov 2014 09:33:38 GMT
44
+ Content-Disposition:
45
+ - attachment
46
+ Content-Transfer-Encoding:
47
+ - binary
48
+ Cache-Control:
49
+ - private
50
+ X-Content-Type-Options:
51
+ - nosniff
52
+ X-Ua-Compatible:
53
+ - IE=Edge,chrome=1
54
+ - IE=Edge,chrome=1
55
+ X-Request-Id:
56
+ - e726d0892877a0b310402be784f88add
57
+ X-Runtime:
58
+ - '0.012082'
59
+ X-Rack-Cache:
60
+ - miss
61
+ body:
62
+ encoding: UTF-8
63
+ string: |
64
+ ---
65
+ - authors: Elasticsearch
66
+ built_at: '2014-11-19T00:00:00Z'
67
+ description: This gem is a logstash plugin required to be installed on top of the
68
+ Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is
69
+ not a stand-alone program
70
+ downloads_count: 479
71
+ number: 0.1.1
72
+ summary: This plugin was created for store the logstash's events into Amazon Simple
73
+ Storage Service (Amazon S3)
74
+ platform: ruby
75
+ ruby_version: '>= 0'
76
+ prerelease: false
77
+ licenses:
78
+ - Apache License (2.0)
79
+ requirements: []
80
+ - authors: Elasticsearch
81
+ built_at: '2014-11-06T00:00:00Z'
82
+ description: This plugin was created for store the logstash's events into Amazon
83
+ Simple Storage Service (Amazon S3)
84
+ downloads_count: 195
85
+ number: 0.1.0
86
+ summary: This plugin was created for store the logstash's events into Amazon Simple
87
+ Storage Service (Amazon S3)
88
+ platform: ruby
89
+ ruby_version: '>= 0'
90
+ prerelease: false
91
+ licenses:
92
+ - Apache License (2.0)
93
+ requirements: []
94
+ http_version:
95
+ recorded_at: Tue, 16 Dec 2014 16:12:51 GMT
96
+ recorded_with: VCR 2.9.3