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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 82fcfffe36fd74ada588e3e69026933c65307904
4
+ data.tar.gz: e7e5e78740affb2ec4e9de2eec9345eb84f77da3
5
+ SHA512:
6
+ metadata.gz: aab3bdc7a67337e64cac4acd57de18f70ba3713adc38915462dca2e60e85b97c41f95c40e8616763992e098d6d1c82b7fa7ce07ba80931a33f2cd026cf84397d
7
+ data.tar.gz: 78b56db4fe290dd3ae558cdc3c84a3c0a99d0cc6fd54050c22de2fa83746c334191a697e25250462b730e0031ecfe0fab459edbb421ee01752eacfd3a86782f7
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ spec/fixtures/test-opsbots
19
+ .idea/*
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2014 Jordan Sissel
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,24 @@
1
+ # lita-jls
2
+
3
+ TODO: Add a description of the plugin.
4
+
5
+ ## Installation
6
+
7
+ Add lita-jls to your Lita instance's Gemfile:
8
+
9
+ ``` ruby
10
+ gem "lita-jls"
11
+ ```
12
+
13
+
14
+ ## Configuration
15
+
16
+ TODO: Describe any configuration attributes the plugin exposes.
17
+
18
+ ## Usage
19
+
20
+ TODO: Describe the plugin's features and how to use them.
21
+
22
+ ## License
23
+
24
+ [MIT](http://opensource.org/licenses/MIT)
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,8 @@
1
+ require "lita"
2
+
3
+ Lita.load_locales Dir[File.expand_path(
4
+ File.join("..", "..", "locales", "*.yml"), __FILE__
5
+ )]
6
+
7
+ require "lita/handlers/jls"
8
+ require "lita-jls/github_url_parser"
@@ -0,0 +1,240 @@
1
+ require 'lita-jls/util'
2
+ require 'rubygems'
3
+ require 'open3'
4
+ require 'gems'
5
+ require 'semverly'
6
+
7
+ module LitaJLS
8
+ module Reporter
9
+ class HipChat
10
+ def initialize(build_results)
11
+ @build_results = build_results
12
+ end
13
+
14
+ def format(message)
15
+ formatted_message = []
16
+
17
+ @build_results.each do |build_result|
18
+ if build_result.status == :ok
19
+ formatted_message << " - (success) #{build_result.message}"
20
+ else
21
+ if build_result.full_message
22
+ formatted_message << " - (stare) #{build_result.message} \nstacktrace: #{build_result.error || build_result.full_message}"
23
+ else
24
+ formatted_message << " - (stare) #{build_result.message}"
25
+ end
26
+ end
27
+ end
28
+
29
+ message.reply(formatted_message.join("\n"))
30
+ end
31
+ end
32
+ end
33
+
34
+ class BuildResult
35
+ attr_reader :status, :full_message, :error, :message
36
+
37
+ def initialize(options = {})
38
+ @status = options[:status]
39
+ @full_message = options[:full_message]
40
+ @error = options[:error]
41
+ @message = options[:message]
42
+ end
43
+ end
44
+
45
+ class BotBuilder
46
+ include LitaJLS::Logger
47
+
48
+ class ConfigurationError < StandardError; end
49
+
50
+ GEM_TO_EXCLUDE = ["logstash"].freeze
51
+
52
+ TASKS_ORDER = ['bundle install',
53
+ 'bundle exec rake vendor',
54
+ 'bundle exec rspec',
55
+ 'bundle exec rake publish_gem'].freeze
56
+
57
+ GEM_CREDENTIALS_FILE = '~/.gem/credentials'
58
+
59
+ attr_reader :current_path, :project_name, :ruby_version
60
+
61
+ def initialize(path, options = {})
62
+ @cache_commands = {}
63
+ @current_path = File.expand_path(path)
64
+ @project_name = File.basename(current_path)
65
+ @ruby_version = options.fetch(:ruby_version, nil)
66
+ @tasks_order = options[:tasks_order] || TASKS_ORDER
67
+ end
68
+
69
+ def is_gem?
70
+ File.exists?(find_gemspec)
71
+ end
72
+
73
+ def find_gemspec
74
+ file = [project_name, "gemspec"].join('.')
75
+ File.join(current_path, file)
76
+ end
77
+
78
+ def gem_specification
79
+ # HACK: if you are using the `real` bundler way of creating gem
80
+ # You have to create a version.rb file containing the version number
81
+ # and require the file in the gemspec.
82
+ # Ruby will cache this require and not reload it again in a long running
83
+ # process like the bot.
84
+ cmd = "ruby -e \"spec = Gem::Specification.load('#{find_gemspec}'); puts [spec.name, spec.version].join(',')\""
85
+ results = execute_command_with_ruby(cmd)
86
+
87
+ if run_successfully?(results)
88
+ name, version = results.stdout.strip.split(',')
89
+ gemspec = Struct.new(:name, :version)
90
+ return gemspec.new(name, version)
91
+ else
92
+ raise results.stderr
93
+ end
94
+ end
95
+
96
+ def publishable?
97
+ if is_gem?
98
+ return !GEM_TO_EXCLUDE.include?(gem_specification.name)
99
+ else
100
+ return false
101
+ end
102
+ end
103
+
104
+ def run_successfully?(task_result)
105
+ logger.debug("Check if run run_successfully", :exit_code => task_result.status.inspect, :task_result => task_result.inspect)
106
+ task_result.status.success?
107
+ end
108
+
109
+ def execution_report(task_result)
110
+ if run_successfully?(task_result)
111
+ report_ok(task_result.cmd, task_result.stdout)
112
+ else
113
+ report_error(task_result.cmd, task_result.stdout, task_result.stderr)
114
+ end
115
+ end
116
+
117
+ def cache_command(cmd, options = {})
118
+ @cache_commands[cmd] ||= execute_command(cmd, options)
119
+ end
120
+
121
+ def report_error(message, full_message = nil, error = nil)
122
+ BuildResult.new(:status => :error, :message => message, :full_message => full_message, :error => error)
123
+ end
124
+
125
+ def report_ok(message, full_message = nil)
126
+ BuildResult.new(:status => :ok, :message => message, :full_message => full_message)
127
+ end
128
+
129
+ def fetch_last_released_version(name)
130
+ # Assume you have correctly configured the ~/gem/credentials file
131
+ credentials_file = File.expand_path(GEM_CREDENTIALS_FILE)
132
+
133
+ if File.exist?(credentials_file)
134
+ response = Gems.versions(name)
135
+ if response != 'This rubygem could not be found.'
136
+ return response.first.fetch('number', nil)
137
+ else
138
+ return nil
139
+ end
140
+ else
141
+ raise ConfigurationError.new("Missing rubygems credentials in #{credentials_file}")
142
+ end
143
+ end
144
+
145
+ def local_version
146
+ SemVer.parse(gem_specification.version.to_s)
147
+ end
148
+
149
+ def rubygems_version
150
+ rubygems_version = fetch_last_released_version(project_name)
151
+
152
+ if rubygems_version.nil?
153
+ return SemVer.new(0, 0, 0)
154
+ else
155
+ return SemVer.parse(rubygems_version)
156
+ end
157
+ end
158
+
159
+ def execute_command_with_ruby(cmd)
160
+ Dir.chdir(current_path) do
161
+ Bundler.with_clean_env do
162
+ environment_variables = {}
163
+
164
+ if ruby_version
165
+ if using_rvm?
166
+ cmd = "rvm #{ruby_version} do #{cmd}"
167
+ elsif using_rbenv?
168
+ raise ConfigurationError.new('RBENV is currently not supported')
169
+ end
170
+ end
171
+
172
+ return cache_command(cmd, environment_variables)
173
+ end
174
+ end
175
+ end
176
+
177
+ def execute_command(cmd, environment_variables = {})
178
+ logger.debug("Running command", :cmd => cmd, :path => current_path)
179
+ Open3.popen3(environment_variables, cmd, :chdir => current_path) do |input, stdout, stderr, thr|
180
+ return OpenStruct.new(:stdout => stdout.read,
181
+ :status => thr.value,
182
+ :stderr => stderr.read,
183
+ :cmd => cmd)
184
+ end
185
+ end
186
+
187
+ def using_rvm?
188
+ run_successfully?(cache_command('which rvm'))
189
+ end
190
+
191
+ def using_rbenv?
192
+ run_successfully?(cache_command('which rbenv'))
193
+ end
194
+
195
+ def run_tasks
196
+ messages = []
197
+
198
+ @tasks_order.each do |task|
199
+ result = execute_command_with_ruby(task)
200
+ messages << execution_report(result)
201
+ break unless run_successfully?(result)
202
+ end
203
+ messages
204
+ end
205
+
206
+ def build
207
+ messages = []
208
+
209
+ if is_gem?
210
+ if publishable?
211
+ if local_version < rubygems_version
212
+ logger.debug("Remote version is higher on rubygems, we dont do anything")
213
+
214
+ messages << report_error("Higher version on rubygems (#{rubygems_version}) than the local version (#{local_version}), see http://rubygems.org/gems/#{project_name}")
215
+ elsif local_version == rubygems_version
216
+ logger.debug("Same version on rubygems", :local_version => local_version, :rubygems_version => rubygems_version)
217
+
218
+ messages << report_error("Local version and rubygems version are the same (#{local_version}|#{rubygems_version}), see http://rubygems.org/gems/#{project_name}")
219
+ else
220
+ logger.debug("Start the build process")
221
+
222
+ messages.concat(run_tasks)
223
+
224
+ if local_version == rubygems_version
225
+ messages << report_ok("version on rubygems match local version, published #{local_version} see http://rubygems.org/gems/#{project_name}")
226
+ else
227
+ messages << report_error("versions on rubygems doesn't match see http://rubygems.org/gems/#{project_name}")
228
+ end
229
+ end
230
+ else
231
+ messages << report_error("#{project_name} is blacklisted, you cannot deploy it with this tool.")
232
+ end
233
+ else
234
+ messages << report_error("#{project_name} doesn't have a gemspec")
235
+ end
236
+
237
+ messages
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,43 @@
1
+ module LitaJLS
2
+ class GithubUrlParser
3
+ attr_reader :user, :project, :pr, :url
4
+
5
+ URL_BASE = "https://github.com"
6
+
7
+ def initialize(git_url, options)
8
+ @url = git_url
9
+ full_path = URI.parse(@url).path
10
+ _, @user, @project, _, @pr = full_path.split('/')
11
+
12
+ @options = { :link => :repository }.merge(options)
13
+ end
14
+
15
+ def valid_url?
16
+ url =~ /^#{URL_BASE.gsub('/', '\/')}/
17
+ end
18
+
19
+ def validate_repository!
20
+ if user.nil? || project.nil? || !valid_url?
21
+ raise "Invalid URL. Expected something like: #{URL_BASE}/elasticsearch/snacktime/"
22
+ end
23
+ end
24
+
25
+ def validate_pull_request!
26
+ if user.nil? || project.nil? || pr.nil? || !valid_url?
27
+ raise "Invalid URL. Expected something like: #{URL_BASE}/elasticsearch/snacktime/pull/12345"
28
+ end
29
+ end
30
+
31
+ def validate!
32
+ send("validate_#{@options[:link]}!")
33
+ end
34
+
35
+ def self.parse(git_url, options = {})
36
+ new(git_url, options)
37
+ end
38
+
39
+ def git_url
40
+ "#{URL_BASE}/#{user}/#{project}"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,57 @@
1
+ require 'lita-jls/util'
2
+
3
+ module LitaJLS
4
+ class Repository
5
+ include LitaJLS::Util
6
+ include LitaJLS::Logger
7
+
8
+ REMOTE = 'origin'
9
+
10
+ def initialize(parsed_url)
11
+ @parsed_url = parsed_url
12
+ end
13
+
14
+ def clone
15
+ clone_at(@parsed_url.git_url, git_path)
16
+ git(git_path, "am", "--abort") if unfinished_rebase?
17
+ end
18
+
19
+ def unfinished_rebase?
20
+ File.directory?(".git/rebase-apply")
21
+ end
22
+
23
+ def git_path
24
+ @git_path ||= gitdir(@parsed_url.project)
25
+ end
26
+
27
+ def switch_branch(branch, create_new=false)
28
+ if create_new
29
+ logger.info("Creating and switching branches", :branch => branch, :repo => git_path)
30
+ git(git_path, "checkout", "-b", branch)
31
+ git(git_path, "reset", "--hard", "#{REMOTE}/master")
32
+ else
33
+ logger.info("Switching branches", :branch => branch, :repo => git_path)
34
+ git(git_path, "checkout", branch)
35
+ git(git_path, "reset", "--hard", "#{REMOTE}/#{branch}")
36
+ git(git_path, "pull", "--ff-only")
37
+ end
38
+ end
39
+
40
+ def git_patch(patch_file)
41
+ raise "Previous patch apply had failed. Please resolve it before continuing" if File.directory?(".git/rebase-apply")
42
+ git(git_path, "am", "--3way", patch_file)
43
+ end
44
+
45
+ def git_push_branch(local_branch)
46
+ git(git_path, "push", "origin", local_branch)
47
+ end
48
+
49
+ def delete_local_branch(branch, ignore_error=false)
50
+ begin
51
+ git(git_path, "branch", "-D", branch)
52
+ rescue => e
53
+ raise e unless ignore_error
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,259 @@
1
+ require "cabin"
2
+ require "fileutils"
3
+ require "uri"
4
+ require "faraday" # for cla check
5
+ require "json" # for cla check
6
+ #
7
+ # TODO(sissel): This code needs some suuuper serious refactoring and testing improvements.
8
+
9
+ module LitaJLS
10
+ module Logger
11
+ def logger
12
+ return @logger if @logger
13
+ @logger = Cabin::Channel.get
14
+ @logger.level = :debug if ENV["DEBUG"]
15
+ @logger
16
+ end
17
+ end
18
+
19
+ module Util
20
+ include Logger
21
+
22
+ CLANotSigned = Class.new(StandardError)
23
+
24
+ private
25
+
26
+ # This method requires @cla_uri being set before it'll work.
27
+ def cla?(repository, pr)
28
+ raise "No @cla_uri set. Cannot check CLA signature." unless @cla_uri
29
+ uri = URI.parse(@cla_uri)
30
+ conn = Faraday.new(:url => "#{uri.scheme}://#{uri.host}")
31
+ conn.basic_auth(uri.user, uri.password)
32
+ response = conn.get(uri.path, :repository => repository, :number => pr)
33
+ check = JSON.parse(response.body)
34
+ # TODO(sissel): json exception? .get exception?
35
+
36
+ if check["status"] == "error"
37
+ raise CLANotSigned, check["message"]
38
+ end
39
+
40
+ true
41
+ end # cla?
42
+
43
+ def logstash_team?(user)
44
+ ["electrical",
45
+ "jordansissel",
46
+ "ph",
47
+ "colinsurprenant",
48
+ "jsvd",
49
+ "untergeek",
50
+ "talevy",
51
+ "kurtado",
52
+ "suyograo",
53
+ "purbon"].include?(user.downcase)
54
+ end
55
+
56
+ # Clone a git url into a local path.
57
+ #
58
+ # This caches a remote git repo and performs a clone against that in
59
+ # order to make subsequent clones faster. It will try to provide the
60
+ # latest (via 'git fetch') after cloning is complete.
61
+ def clone_at(url, gitpath)
62
+ # TODO(sissel): Refactor this into two 'clone' calls.
63
+ logger.debug("clone_at", :url => url, :gitpath => gitpath)
64
+
65
+ # Cache a remote git url so that we can clone it more quickly in the
66
+ # future.
67
+ cache = File.join(gitdir(File.join("_")), File.basename(gitpath))
68
+ FileUtils.mkdir_p(cache) unless File.directory?(cache)
69
+
70
+ logger.info("Cloning to cache", :url => url, :cache => cache)
71
+ begin
72
+ git(".", "clone", url, cache)
73
+ rescue => e
74
+ logger.debug("clone_at failed, trying to open repo instead", :cache => cache, :error => e)
75
+ git(cache, "log", "-n0") # Verify some kind of git works here
76
+ end
77
+ remote = "origin"
78
+
79
+ # Update from remote if already cloned
80
+ logger.debug("Fetching a remote", :cache => cache, :remote => remote)
81
+ git(cache, "fetch", remote)
82
+
83
+ # Clone from cache.
84
+ # This allows us to have multiple local working/clones and just keep
85
+ # cloning from the local cache. The alternative is to clone from the
86
+ # remote every time we do a new clone_at, and that would be slow through
87
+ # github or gitlab.
88
+ logger.info("Cloning from cache", :cache => cache, :gitpath => gitpath)
89
+ begin
90
+ git(".", "clone", cache, gitpath)
91
+ rescue => e
92
+ logger.info(e)
93
+ logger.debug("clone_at from cache failed, trying to open repo instead", :repo => gitpath, :cache => cache)
94
+ end
95
+ git(gitpath, "remote", "set-url", remote, url)
96
+
97
+ uri = URI.parse(url)
98
+ push_url = "git@github.com:#{uri.path}.git"
99
+ git(gitpath, "remote", "set-url", "--push", remote, push_url)
100
+ git(gitpath, "fetch")
101
+
102
+ # TODO(sissel): pull --ff-only?
103
+ gitpath
104
+ end # def clone_at
105
+
106
+ def gitdir(project)
107
+ if !@gitdir
108
+ @gitdir = workdir("gitbase")
109
+ FileUtils.mkdir_p(@gitdir) unless File.directory?(@gitdir)
110
+ logger.debug("Git dir", :path => @gitdir)
111
+ end
112
+ path = File.join(@gitdir, project)
113
+ Dir.mkdir(path) unless File.directory?(path)
114
+ path
115
+ end # def gitdir
116
+
117
+ def workdir(path=nil)
118
+ return File.join(@workdir, path) if @workdir
119
+ @workdir = File.join(Dir.tmpdir, "lita-jls")
120
+ Dir.mkdir(@workdir) unless File.directory?(@workdir)
121
+ if path.nil?
122
+ @workdir
123
+ else
124
+ File.join(@workdir, path)
125
+ end
126
+ end # def workdir
127
+
128
+ def apply_patch(repo, patch_body, &block)
129
+ require "mbox"
130
+ require "time"
131
+ # The github '.patch' format is an mbox containing one mail+patch per
132
+ # commit.
133
+ mbox = Mbox.new(patch_body)
134
+ mbox.each_with_index do |mail, i|
135
+ commit = apply_commit(repo, mail, &block)
136
+ logger.info("Created commit", :commit => commit, :patch => i)
137
+ end
138
+ end # def apply_patch
139
+
140
+ def apply_commit(repo, mail, &block)
141
+ from_re = /([^<]+) (<[^>]+>)/
142
+ match = from_re.match(mail.headers["from"])
143
+ name = match[1].gsub(/(^")|("$)/, "")
144
+ email = match[2].gsub(/^<|>$/, "")
145
+ if name.nil? || email.nil?
146
+ raise "Unable to parse name and email from '#{mail.headers["from"]}'. Cannot continue"
147
+ end
148
+ time = Time.parse(mail.headers["date"])
149
+ #File.write("/tmp/x", patch)
150
+
151
+ # Take the email subject but strip [PATCH] or [PATCH N/M] out.
152
+ subject = mail.headers["subject"].gsub(/^\[PATCH[^\]]*\] /, "")
153
+ # The email body (minus the patch itself) is the rest of the commit message
154
+ description = /^(?<description>.*\n)?---\n.*$/m.match(mail.content.first.content)["description"] || ""
155
+
156
+ if subject.empty? && description.empty?
157
+ raise "Empty commit message (no subject or description). Refusing to continue."
158
+ end
159
+
160
+ #patch = mail.content.first.content.gsub(/^(?:.*\n)?---\n.*?\n\n/m, "")
161
+ #patch += "\n" if patch[-1,1] != "\n"
162
+ # Patch must have a trailing newline.
163
+ patch = [mail.headers, mail.content, ""].join("\n")
164
+
165
+ # Apply the code change to the git index
166
+ Dir.chdir(repo) do
167
+ cmd = ["git", "am", "--3way"]
168
+ File.write("/tmp/patch", patch)
169
+ IO.popen(cmd, "w+") do |io|
170
+ io.write(patch)
171
+ io.close_write
172
+ logger.pipe(io => :debug)
173
+ end
174
+ status = $?
175
+ if !status.success?
176
+ logger.warn("Git am failed", :code => status.exitstatus, :command => cmd, :pwd => Dir.pwd)
177
+ git(repo, "am", "--abort")
178
+ raise "Git am failed: #{cmd.join(" ")}"
179
+ end
180
+ logger.info("Git am successful!", :code => status.exitstatus, :command => cmd, :pwd => Dir.pwd)
181
+ end
182
+
183
+ # Combine subject + description for the full commit message
184
+ message = "#{subject}\n\n#{description}"
185
+
186
+ commit_settings = {
187
+ # TODO(sissel): override the committer with whoever
188
+ #:author => { :email => email, :name => name, :time => time },
189
+ #:committer => { :email => "jls@semicomplete.com", :name => "Jordan Sissel", :time => Time.now },
190
+ :message => message
191
+ }
192
+
193
+ # Allow any modifications to the commit object itself.
194
+ block.call(commit_settings)
195
+
196
+ Dir.chdir(repo) do
197
+ cmd = ["git", "commit", "--amend", "-F-"]
198
+ IO.popen(cmd, "w+") do |io|
199
+ io.write(commit_settings[:message])
200
+ io.close_write
201
+ logger.pipe(io => :debug)
202
+ end
203
+ end
204
+
205
+ end # def apply_commit
206
+
207
+ def system!(*args)
208
+ logger.debug("Running command", :args => args)
209
+ # TODO(sissel): use Open4
210
+ IO.popen(args, "r") do |io|
211
+ logger.pipe(io => :debug)
212
+ end
213
+ status = $?
214
+ return if status.success?
215
+ raise "Command failed; #{args.inspect}"
216
+ end
217
+
218
+ def github_client
219
+ require "octokit"
220
+ # This requires you have ~/.netrc setup correctly
221
+ # I don't know if it works with 2FA
222
+ @client ||= Octokit::Client.new(:netrc => true).tap do |client|
223
+ client.login
224
+ client.auto_paginate = true
225
+ end
226
+ end # def client
227
+
228
+ def github_issue_label(project, issue, labels)
229
+ logger.debug('Adding label to a specific issue', :project => project, :issue => issue, :labels => labels)
230
+ github_client.add_labels_to_an_issue(project, issue, labels) unless labels.empty?
231
+ rescue => e
232
+ raise e.class, "Failed adding label '#{labels}' to issue #{issue} on #{project}: #{e}"
233
+ end # def github_issue_label
234
+
235
+ def github_issue_comment(project, issue, comment)
236
+ logger.debug('Adding a comment to an given issue', :project => project, :issue => issue, :comment => comment)
237
+ github_client.add_comment(project, issue, comment) unless comment.empty?
238
+ rescue => e
239
+ raise e.class, "Failed adding a comment #{comment} to issue #{issue} on #{project}: #{e}"
240
+
241
+ end # def github_issue_comment
242
+
243
+ def github_create_pr(project, branch, title, body)
244
+ logger.debug('Creating a pull request', :project => project, :branch => branch)
245
+ github_client.create_pull_request(project, "master", branch, title, body)
246
+ end
247
+
248
+ def github_get_pr(project, pr_num)
249
+ github_client.pull_request(project, pr_num)
250
+ end
251
+
252
+ def git(gitdir, *args)
253
+ Dir.chdir(gitdir) do
254
+ system!("git", *args)
255
+ end
256
+ end
257
+ end # module Util
258
+
259
+ end # module LitaJLS