toys-release 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 +7 -0
- data/.yardopts +11 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.md +21 -0
- data/README.md +87 -0
- data/docs/guide.md +7 -0
- data/lib/toys/release/version.rb +11 -0
- data/lib/toys-release.rb +23 -0
- data/toys/.data/templates/gh-pages-404.html.erb +25 -0
- data/toys/.data/templates/gh-pages-empty.html.erb +11 -0
- data/toys/.data/templates/gh-pages-gitignore.erb +1 -0
- data/toys/.data/templates/gh-pages-index.html.erb +15 -0
- data/toys/.data/templates/release-hook-on-closed.yml.erb +34 -0
- data/toys/.data/templates/release-hook-on-open.yml.erb +30 -0
- data/toys/.data/templates/release-hook-on-push.yml.erb +32 -0
- data/toys/.data/templates/release-perform.yml.erb +46 -0
- data/toys/.data/templates/release-request.yml.erb +37 -0
- data/toys/.data/templates/release-retry.yml.erb +42 -0
- data/toys/.lib/toys/release/artifact_dir.rb +70 -0
- data/toys/.lib/toys/release/change_set.rb +259 -0
- data/toys/.lib/toys/release/changelog_file.rb +136 -0
- data/toys/.lib/toys/release/component.rb +388 -0
- data/toys/.lib/toys/release/environment_utils.rb +246 -0
- data/toys/.lib/toys/release/performer.rb +346 -0
- data/toys/.lib/toys/release/pull_request.rb +154 -0
- data/toys/.lib/toys/release/repo_settings.rb +855 -0
- data/toys/.lib/toys/release/repository.rb +661 -0
- data/toys/.lib/toys/release/request_logic.rb +217 -0
- data/toys/.lib/toys/release/request_spec.rb +188 -0
- data/toys/.lib/toys/release/semver.rb +112 -0
- data/toys/.lib/toys/release/steps.rb +580 -0
- data/toys/.lib/toys/release/version_rb_file.rb +91 -0
- data/toys/.toys.rb +5 -0
- data/toys/_onclosed.rb +113 -0
- data/toys/_onopen.rb +158 -0
- data/toys/_onpush.rb +57 -0
- data/toys/create-labels.rb +115 -0
- data/toys/gen-gh-pages.rb +146 -0
- data/toys/gen-settings.rb +46 -0
- data/toys/gen-workflows.rb +70 -0
- data/toys/perform.rb +152 -0
- data/toys/request.rb +162 -0
- data/toys/retry.rb +133 -0
- metadata +106 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
require_relative "artifact_dir"
|
|
7
|
+
require_relative "steps"
|
|
8
|
+
|
|
9
|
+
module Toys
|
|
10
|
+
module Release
|
|
11
|
+
##
|
|
12
|
+
# Performs releases
|
|
13
|
+
#
|
|
14
|
+
class Performer
|
|
15
|
+
##
|
|
16
|
+
# Results to report for a component release
|
|
17
|
+
#
|
|
18
|
+
class Result
|
|
19
|
+
##
|
|
20
|
+
# Create a new result
|
|
21
|
+
#
|
|
22
|
+
# @param component_name [String] The name of the component
|
|
23
|
+
# @param version [::Gem::Version] The version to release
|
|
24
|
+
#
|
|
25
|
+
def initialize(component_name, version)
|
|
26
|
+
@component_name = component_name
|
|
27
|
+
@version = version
|
|
28
|
+
@successes = []
|
|
29
|
+
@errors = []
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# @return [boolean] Whether the result is capturing errors
|
|
34
|
+
#
|
|
35
|
+
def capture_errors?
|
|
36
|
+
!@errors.nil?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# @return [boolean] True if there is no content (neither success nor
|
|
41
|
+
# error) for this result.
|
|
42
|
+
#
|
|
43
|
+
def empty?
|
|
44
|
+
successes.empty? && errors.empty?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
# @return [String] The name of the component
|
|
49
|
+
#
|
|
50
|
+
attr_reader :component_name
|
|
51
|
+
|
|
52
|
+
##
|
|
53
|
+
# @return [::Gem::Version] The version to release
|
|
54
|
+
#
|
|
55
|
+
attr_reader :version
|
|
56
|
+
|
|
57
|
+
##
|
|
58
|
+
# @return [Array<String>] The success messages
|
|
59
|
+
#
|
|
60
|
+
attr_reader :successes
|
|
61
|
+
|
|
62
|
+
##
|
|
63
|
+
# @return [Array<String>] The error messages
|
|
64
|
+
#
|
|
65
|
+
attr_reader :errors
|
|
66
|
+
|
|
67
|
+
##
|
|
68
|
+
# @return [Array<String>] The success messages, formatted for output.
|
|
69
|
+
#
|
|
70
|
+
def formatted_successes
|
|
71
|
+
successes.map { |line| "* #{line}" }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
##
|
|
75
|
+
# @return [Array<String>] The error messages, formatted for output.
|
|
76
|
+
# Returns the empty array if this Result is not capturing errors.
|
|
77
|
+
#
|
|
78
|
+
def formatted_errors
|
|
79
|
+
errors.map { |line| "* ERROR: #{line}" }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @private
|
|
83
|
+
attr_writer :version
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
##
|
|
87
|
+
# Create a release performer.
|
|
88
|
+
#
|
|
89
|
+
# @param repository [Repository]
|
|
90
|
+
# @param release_ref [String] Git ref or SHA for the release.
|
|
91
|
+
# @param release_pr [Integer,PullRequest] Pull request for the release.
|
|
92
|
+
# @param enable_prechecks [boolean]
|
|
93
|
+
# @param git_remote [String] Git remote.
|
|
94
|
+
# @param work_dir [String,nil] Directory for temporary artifacts. Uses an
|
|
95
|
+
# ephemeral temporary directory if not provided.
|
|
96
|
+
# @param dry_run [boolean] If true, doesn't perform permanent operations
|
|
97
|
+
# such as pushing gems or creating github releases.
|
|
98
|
+
#
|
|
99
|
+
def initialize(repository,
|
|
100
|
+
release_ref: nil,
|
|
101
|
+
release_pr: nil,
|
|
102
|
+
enable_prechecks: true,
|
|
103
|
+
git_remote: nil,
|
|
104
|
+
work_dir: nil,
|
|
105
|
+
dry_run: false)
|
|
106
|
+
@enable_prechecks = enable_prechecks
|
|
107
|
+
@repository = repository
|
|
108
|
+
@settings = repository.settings
|
|
109
|
+
@utils = repository.utils
|
|
110
|
+
@dry_run = dry_run
|
|
111
|
+
@gh_token = ENV["GITHUB_TOKEN"]
|
|
112
|
+
@git_remote = git_remote || "origin"
|
|
113
|
+
@work_dir = work_dir
|
|
114
|
+
@release_sha = @pull_request = @pr_components = nil
|
|
115
|
+
@component_results = []
|
|
116
|
+
@init_result = Result.new(nil, nil)
|
|
117
|
+
@start_time = ::Time.now.utc
|
|
118
|
+
@utils.capture_errors(@init_result.errors) do
|
|
119
|
+
resolve_ref_and_pr(release_ref, release_pr)
|
|
120
|
+
repo_prechecks if @enable_prechecks
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
##
|
|
125
|
+
# @return [PullRequest] the GitHub pull request
|
|
126
|
+
# @return [nil] if this performer was not configured with a pull request
|
|
127
|
+
#
|
|
128
|
+
attr_reader :pull_request
|
|
129
|
+
|
|
130
|
+
##
|
|
131
|
+
# @return [Result] the results of initialization and prechecks
|
|
132
|
+
#
|
|
133
|
+
attr_reader :init_result
|
|
134
|
+
|
|
135
|
+
##
|
|
136
|
+
# @return [Array<Result>] the results of the various releases done
|
|
137
|
+
#
|
|
138
|
+
attr_reader :component_results
|
|
139
|
+
|
|
140
|
+
##
|
|
141
|
+
# Perform a release without pull request direction. Stores the result in a
|
|
142
|
+
# new result appended to the component results.
|
|
143
|
+
#
|
|
144
|
+
# @param component_name [String] the name of the component to release
|
|
145
|
+
# @param assert_version [String] (optional) if provided, asserts that the
|
|
146
|
+
# released version is the same as what is given
|
|
147
|
+
#
|
|
148
|
+
def perform_adhoc_release(component_name, assert_version: nil)
|
|
149
|
+
@repository.at_sha(@release_sha) do
|
|
150
|
+
result = Result.new(component_name, assert_version)
|
|
151
|
+
@component_results << result
|
|
152
|
+
@utils.capture_errors(result.errors) do
|
|
153
|
+
component = @repository.component_named(component_name)
|
|
154
|
+
version = component.current_changelog_version
|
|
155
|
+
if !component
|
|
156
|
+
@utils.error("Component #{component_name.inspect} not found.")
|
|
157
|
+
elsif assert_version && assert_version != version
|
|
158
|
+
@utils.error("Asserted version #{assert_version} does not match version " \
|
|
159
|
+
"#{version} found in the changelog for #{component_name.inspect}.")
|
|
160
|
+
else
|
|
161
|
+
result.version = version
|
|
162
|
+
internal_perform_release(component, version, result)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
self
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
##
|
|
170
|
+
# Perform all releases associated with the configured pull request
|
|
171
|
+
#
|
|
172
|
+
def perform_pr_releases
|
|
173
|
+
if @pr_components.nil?
|
|
174
|
+
@utils.capture_errors(@init_result.errors) do
|
|
175
|
+
@utils.error("Cannot perform PR releases because no pull request was found.")
|
|
176
|
+
end
|
|
177
|
+
return self
|
|
178
|
+
end
|
|
179
|
+
@pr_components.each do |component_name, version|
|
|
180
|
+
perform_adhoc_release(component_name, assert_version: version)
|
|
181
|
+
end
|
|
182
|
+
self
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
##
|
|
186
|
+
# Returns true if any errors happened in any of the releases
|
|
187
|
+
#
|
|
188
|
+
# @return [boolean]
|
|
189
|
+
#
|
|
190
|
+
def error?
|
|
191
|
+
!@init_result.errors.empty? || @component_results.any? { |result| !result.errors.empty? }
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
##
|
|
195
|
+
# @return [String] The pull request URL
|
|
196
|
+
# @return [nil] if there is no pull request configured for this performer
|
|
197
|
+
#
|
|
198
|
+
def pr_url
|
|
199
|
+
@pull_request&.url
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
##
|
|
203
|
+
# Updates the pull request (if any) with the release results.
|
|
204
|
+
# Also opens an issue if any failures happened.
|
|
205
|
+
#
|
|
206
|
+
def report_results
|
|
207
|
+
report_text = build_report_text
|
|
208
|
+
if @pull_request
|
|
209
|
+
@utils.log("Updating release pull request #{@pull_request.url} ...")
|
|
210
|
+
label = error? ? @settings.release_error_label : @settings.release_complete_label
|
|
211
|
+
@pull_request.update(labels: label)
|
|
212
|
+
@pull_request.add_comment(report_text)
|
|
213
|
+
@utils.log("Updated release pull request #{@pull_request.url}")
|
|
214
|
+
end
|
|
215
|
+
if error?
|
|
216
|
+
@utils.log("Opening a new issue to report the failure ...")
|
|
217
|
+
body = <<~STR
|
|
218
|
+
A release job failed.
|
|
219
|
+
|
|
220
|
+
Release PR: #{@pull_request&.url || 'unknown'}
|
|
221
|
+
Commit: https://github.com/#{@settings.repo_path}/commit/#{@release_sha}
|
|
222
|
+
|
|
223
|
+
----
|
|
224
|
+
|
|
225
|
+
#{report_text}
|
|
226
|
+
STR
|
|
227
|
+
title = "Release PR ##{@pull_request&.number || 'unknown'} failed with errors"
|
|
228
|
+
issue_number = @repository.open_issue(title, body)["number"]
|
|
229
|
+
@utils.log("Issue ##{issue_number} opened")
|
|
230
|
+
end
|
|
231
|
+
self
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
##
|
|
235
|
+
# Builds a report of the release results
|
|
236
|
+
#
|
|
237
|
+
# @return [String]
|
|
238
|
+
#
|
|
239
|
+
def build_report_text
|
|
240
|
+
lines = [
|
|
241
|
+
"## Release job results",
|
|
242
|
+
"",
|
|
243
|
+
]
|
|
244
|
+
lines.concat(main_report_lines)
|
|
245
|
+
unless @init_result.empty?
|
|
246
|
+
lines << ""
|
|
247
|
+
lines << "### Setup"
|
|
248
|
+
lines << ""
|
|
249
|
+
lines.concat(@init_result.formatted_errors)
|
|
250
|
+
lines.concat(@init_result.formatted_successes)
|
|
251
|
+
end
|
|
252
|
+
@component_results.each do |result|
|
|
253
|
+
next if result.empty?
|
|
254
|
+
lines << ""
|
|
255
|
+
lines << "### #{result.component_name} #{result.version}"
|
|
256
|
+
lines << ""
|
|
257
|
+
lines.concat(result.formatted_errors)
|
|
258
|
+
lines.concat(result.formatted_successes)
|
|
259
|
+
end
|
|
260
|
+
lines.join("\n")
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
private
|
|
264
|
+
|
|
265
|
+
def main_report_lines
|
|
266
|
+
lines = [
|
|
267
|
+
"* Job started #{@start_time.strftime('%Y-%m-%d %H:%M:%S')} UTC",
|
|
268
|
+
"* Job finished #{::Time.now.utc.strftime('%Y-%m-%d %H:%M:%S')} UTC",
|
|
269
|
+
]
|
|
270
|
+
lines << "* Release SHA: #{@release_sha}" if @release_sha
|
|
271
|
+
lines << "* Release pull request: #{@pull_request.url}" if @pull_request
|
|
272
|
+
lines << if error?
|
|
273
|
+
"* **Release job completed with errors.**"
|
|
274
|
+
else
|
|
275
|
+
"* **All releases completed successfully.**"
|
|
276
|
+
end
|
|
277
|
+
if (server_url = ::ENV["GITHUB_SERVER_URL"])
|
|
278
|
+
if (repo = ::ENV["GITHUB_REPOSITORY"])
|
|
279
|
+
if (run_id = ::ENV["GITHUB_RUN_ID"])
|
|
280
|
+
lines << "* Run logs: #{server_url}/#{repo}/actions/runs/#{run_id}"
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
lines
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def resolve_ref_and_pr(ref, pr_info)
|
|
288
|
+
@pull_request = nil
|
|
289
|
+
case pr_info
|
|
290
|
+
when ::Integer, ::String
|
|
291
|
+
@pull_request = @repository.load_pr(pr_info.to_i)
|
|
292
|
+
@utils.error("Pull request number #{pr_info} not found.") if @pull_request.nil?
|
|
293
|
+
when PullRequest
|
|
294
|
+
@pull_request = pr_info
|
|
295
|
+
end
|
|
296
|
+
ref = @pull_request.merge_commit_sha if @pull_request && !ref
|
|
297
|
+
@release_sha = @repository.current_sha(ref)
|
|
298
|
+
@utils.log("Release SHA set to #{@release_sha}")
|
|
299
|
+
if @pull_request
|
|
300
|
+
@utils.log("Release pull request is #{@pull_request.url}")
|
|
301
|
+
else
|
|
302
|
+
@utils.warning("Pull request not provided, and will not be updated with the release info.")
|
|
303
|
+
end
|
|
304
|
+
@pr_components = @pull_request ? @repository.released_components_and_versions(@pull_request) : nil
|
|
305
|
+
@utils.exec(["git", "fetch", @git_remote, @release_sha], e: true)
|
|
306
|
+
self
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def repo_prechecks
|
|
310
|
+
@utils.log("Performing repo-level prechecks ...")
|
|
311
|
+
@repository.verify_git_clean
|
|
312
|
+
@repository.verify_repo_identity(remote: @git_remote)
|
|
313
|
+
@repository.verify_github_checks(ref: @release_sha)
|
|
314
|
+
@utils.log("Repo-level prechecks succeeded.")
|
|
315
|
+
self
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def internal_perform_release(component, version, result)
|
|
319
|
+
component_prechecks(component, version) if @enable_prechecks
|
|
320
|
+
artifact_dir = Toys::Release::ArtifactDir.new(@work_dir)
|
|
321
|
+
begin
|
|
322
|
+
component.cd do
|
|
323
|
+
component.settings.steps.each do |step_settings|
|
|
324
|
+
result_code = Toys::Release::Steps.run(
|
|
325
|
+
type: step_settings.type, name: step_settings.name, options: step_settings.options,
|
|
326
|
+
repository: @repository, component: component, version: version, performer_result: result,
|
|
327
|
+
artifact_dir: artifact_dir, dry_run: @dry_run, git_remote: @git_remote
|
|
328
|
+
)
|
|
329
|
+
break if result_code == :abort
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
ensure
|
|
333
|
+
artifact_dir.cleanup
|
|
334
|
+
end
|
|
335
|
+
self
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def component_prechecks(component, version)
|
|
339
|
+
@utils.log("Running prechecks for #{component.name.inspect} ...")
|
|
340
|
+
component.verify_version(version)
|
|
341
|
+
@utils.log("Completed prechecks for #{component.name.inspect}")
|
|
342
|
+
self
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
require_relative "semver"
|
|
6
|
+
|
|
7
|
+
module Toys
|
|
8
|
+
module Release
|
|
9
|
+
##
|
|
10
|
+
# Represents a release pull request
|
|
11
|
+
#
|
|
12
|
+
class PullRequest
|
|
13
|
+
##
|
|
14
|
+
# Create a pull request object
|
|
15
|
+
#
|
|
16
|
+
# @param repository [Repository]
|
|
17
|
+
# @param resource [Hash] The resource hash describing the pull request
|
|
18
|
+
#
|
|
19
|
+
def initialize(repository, resource)
|
|
20
|
+
@repository = repository
|
|
21
|
+
@resource = resource
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# @return [Repository]
|
|
26
|
+
#
|
|
27
|
+
attr_reader :repository
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# @return [Hash] The resource hash describing the pull request
|
|
31
|
+
#
|
|
32
|
+
attr_reader :resource
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
# @return [Integer] The pull request number
|
|
36
|
+
#
|
|
37
|
+
def number
|
|
38
|
+
resource["number"].to_i
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
##
|
|
42
|
+
# @return [String] The pull request title
|
|
43
|
+
#
|
|
44
|
+
def title
|
|
45
|
+
resource["title"]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# @return [String] The pull request state
|
|
50
|
+
#
|
|
51
|
+
def state
|
|
52
|
+
resource["state"]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# @return [Array<String>] The current label names
|
|
57
|
+
#
|
|
58
|
+
def labels
|
|
59
|
+
resource["labels"].map { |label_info| label_info["name"] }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
##
|
|
63
|
+
# @return [String] The pull request URL
|
|
64
|
+
#
|
|
65
|
+
def url
|
|
66
|
+
"https://github.com/#{repository.settings.repo_path}/pull/#{number}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
##
|
|
70
|
+
# @return [String] The SHA of the merge commit
|
|
71
|
+
#
|
|
72
|
+
def merge_commit_sha
|
|
73
|
+
resource["merge_commit_sha"]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
##
|
|
77
|
+
# @return [String] The SHA of the pull request head
|
|
78
|
+
#
|
|
79
|
+
def head_sha
|
|
80
|
+
resource["head"]["sha"]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
##
|
|
84
|
+
# @return [String] The SHA of the pull request base
|
|
85
|
+
#
|
|
86
|
+
def base_sha
|
|
87
|
+
resource["base"]["sha"]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
##
|
|
91
|
+
# @return [String] The ref of the pull request head
|
|
92
|
+
#
|
|
93
|
+
def head_ref
|
|
94
|
+
resource["head"]["ref"]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
##
|
|
98
|
+
# @return [String] The ref of the pull request base
|
|
99
|
+
#
|
|
100
|
+
def base_ref
|
|
101
|
+
resource["base"]["ref"]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
##
|
|
105
|
+
# @return [boolean] Whether the pull request has been merged
|
|
106
|
+
#
|
|
107
|
+
def merged?
|
|
108
|
+
resource["merged_at"] ? true : false
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
##
|
|
112
|
+
# Perform various updates to a pull request
|
|
113
|
+
#
|
|
114
|
+
# @param labels [String,Array<String>,nil] One or more release-related
|
|
115
|
+
# labels that should be applied. All existing release-related labels
|
|
116
|
+
# are replaced with this list. Optional; no label updates are applied
|
|
117
|
+
# if not present.
|
|
118
|
+
# @param state [String,nil] New pull request state. Optional; the state is
|
|
119
|
+
# not modified if not present.
|
|
120
|
+
# @return [self]
|
|
121
|
+
#
|
|
122
|
+
def update(labels: nil, state: nil)
|
|
123
|
+
body = {}
|
|
124
|
+
body[:state] = state if state && self.state != state
|
|
125
|
+
if labels
|
|
126
|
+
labels = Array(labels)
|
|
127
|
+
release_labels, other_labels = self.labels.partition do |label|
|
|
128
|
+
repository.release_related_label?(label)
|
|
129
|
+
end
|
|
130
|
+
body[:labels] = other_labels + labels unless release_labels.sort == labels.sort
|
|
131
|
+
end
|
|
132
|
+
unless body.empty?
|
|
133
|
+
cmd = ["gh", "api", "-XPATCH", "repos/#{repository.settings.repo_path}/issues/#{number}",
|
|
134
|
+
"--input", "-", "-H", "Accept: application/vnd.github.v3+json"]
|
|
135
|
+
repository.utils.exec(cmd, in: [:string, ::JSON.dump(body)], out: :null, e: true)
|
|
136
|
+
end
|
|
137
|
+
self
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
##
|
|
141
|
+
# Add a comment to a pull request
|
|
142
|
+
#
|
|
143
|
+
# @param message [String] A comment to add to the pull request.
|
|
144
|
+
# @return [self]
|
|
145
|
+
#
|
|
146
|
+
def add_comment(message)
|
|
147
|
+
cmd = ["gh", "api", "repos/#{repository.settings.repo_path}/issues/#{number}/comments",
|
|
148
|
+
"--input", "-", "-H", "Accept: application/vnd.github.v3+json"]
|
|
149
|
+
repository.utils.exec(cmd, in: [:string, ::JSON.dump(body: message)], out: :null, e: true)
|
|
150
|
+
self
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|