lita-jls 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -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