toys-release 0.2.2 → 0.3.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 +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +1 -1
- data/docs/guide.md +826 -1
- data/lib/toys/release/version.rb +1 -1
- data/toys/.data/templates/release-request.yml.erb +1 -1
- data/toys/.lib/toys/release/change_set.rb +7 -7
- data/toys/.lib/toys/release/component.rb +7 -68
- data/toys/.lib/toys/release/environment_utils.rb +13 -0
- data/toys/.lib/toys/release/performer.rb +2 -7
- data/toys/.lib/toys/release/pipeline.rb +41 -12
- data/toys/.lib/toys/release/repo_settings.rb +282 -256
- data/toys/.lib/toys/release/repository.rb +3 -3
- data/toys/.lib/toys/release/steps.rb +39 -22
- data/toys/.toys.rb +2 -2
- data/toys/_onclosed.rb +8 -0
- data/toys/gen-config.rb +137 -0
- data/toys/gen-workflows.rb +23 -6
- metadata +4 -4
- data/toys/gen-settings.rb +0 -46
|
@@ -14,17 +14,31 @@ module Toys
|
|
|
14
14
|
ScopeInfo = ::Struct.new(:semver, :header)
|
|
15
15
|
|
|
16
16
|
##
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
17
|
+
# Create an empty settings for an unknown tag
|
|
18
|
+
#
|
|
19
|
+
# @param tag [String] Conventional commit tag
|
|
20
|
+
# @return [CommitTagSettings]
|
|
20
21
|
#
|
|
21
|
-
def
|
|
22
|
+
def self.empty(tag)
|
|
23
|
+
new({"tag" => tag, "header" => nil}, [])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# @private
|
|
28
|
+
# Create a CommitTagSettings from an input hash.
|
|
29
|
+
#
|
|
30
|
+
def initialize(info, errors)
|
|
31
|
+
@tag = info.delete("tag").to_s
|
|
32
|
+
errors << "Commit tag missing : #{info}" if @tag.empty?
|
|
33
|
+
@header = info.fetch("header", @tag.upcase) || :hidden
|
|
34
|
+
info.delete("header")
|
|
35
|
+
@semver = load_semver(info.delete("semver"), errors)
|
|
22
36
|
@scopes = {}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
info.delete("scopes")&.each do |scope_info|
|
|
38
|
+
load_scope(scope_info, errors)
|
|
39
|
+
end
|
|
40
|
+
info.each_key do |key|
|
|
41
|
+
errors << "Unknown key #{key.inspect} in configuration of tag #{@tag.inspect}"
|
|
28
42
|
end
|
|
29
43
|
end
|
|
30
44
|
|
|
@@ -71,86 +85,27 @@ module Toys
|
|
|
71
85
|
end
|
|
72
86
|
end
|
|
73
87
|
|
|
74
|
-
##
|
|
75
|
-
# Make specified modifications to the settings
|
|
76
|
-
#
|
|
77
|
-
# @param input [Hash] Modifications
|
|
78
|
-
#
|
|
79
|
-
def modify(input)
|
|
80
|
-
if input.key?("header") || input.key?("label")
|
|
81
|
-
@header = input.fetch("header", input["label"]) || :hidden
|
|
82
|
-
end
|
|
83
|
-
if input.key?("semver")
|
|
84
|
-
@semver = load_semver(input["semver"])
|
|
85
|
-
end
|
|
86
|
-
input["scopes"]&.each do |key, value|
|
|
87
|
-
if value.nil?
|
|
88
|
-
@scopes.delete(key)
|
|
89
|
-
else
|
|
90
|
-
scope_info = load_scope(key, value)
|
|
91
|
-
@scopes[key] = scope_info if scope_info
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
88
|
private
|
|
97
89
|
|
|
98
|
-
def
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if value.is_a?(::Hash)
|
|
109
|
-
@tag = key
|
|
110
|
-
load_hash(value)
|
|
111
|
-
elsif key == "tag"
|
|
112
|
-
@tag = value
|
|
113
|
-
@header = @tag.upcase
|
|
114
|
-
@semver = Semver::PATCH
|
|
115
|
-
else
|
|
116
|
-
@tag = key
|
|
117
|
-
@header = @tag.upcase
|
|
118
|
-
@semver = load_semver(value)
|
|
119
|
-
end
|
|
120
|
-
else
|
|
121
|
-
@tag = input["tag"]
|
|
122
|
-
raise "tag missing in #{input}" unless @tag
|
|
123
|
-
load_hash(input)
|
|
90
|
+
def load_scope(info, errors)
|
|
91
|
+
scope = info.delete("scope").to_s
|
|
92
|
+
errors << "Commit tag scope missing under tag #{@tag.inspect} : #{info}" if scope.empty?
|
|
93
|
+
scope_semver = load_semver(info.delete("semver"), errors, scope) if info.key?("semver")
|
|
94
|
+
scope_header = info.fetch("header", :inherit) || :hidden
|
|
95
|
+
info.delete("header")
|
|
96
|
+
scope_header = nil if scope_header == :inherit
|
|
97
|
+
@scopes[scope] = ScopeInfo.new(scope_semver, scope_header)
|
|
98
|
+
info.each_key do |key|
|
|
99
|
+
errors << "Unknown key #{key.inspect} in configuration of tag \"#{@tag}(#{scope})\""
|
|
124
100
|
end
|
|
125
101
|
end
|
|
126
102
|
|
|
127
|
-
def
|
|
128
|
-
@header = input.fetch("header", input.fetch("label", @tag.upcase)) || :hidden
|
|
129
|
-
@semver = load_semver(input.fetch("semver", "patch"))
|
|
130
|
-
input["scopes"]&.each do |key, value|
|
|
131
|
-
scope_info = load_scope(key, value)
|
|
132
|
-
@scopes[key] = scope_info if scope_info
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def load_scope(key, value)
|
|
137
|
-
case value
|
|
138
|
-
when ::String
|
|
139
|
-
semver = load_semver(value, key)
|
|
140
|
-
ScopeInfo.new(semver, nil)
|
|
141
|
-
when ::Hash
|
|
142
|
-
semver = load_semver(value["semver"], key) if value.key?("semver")
|
|
143
|
-
header = value.fetch("header", value.fetch("label", :inherit)) || :hidden
|
|
144
|
-
header = nil if header == :inherit
|
|
145
|
-
ScopeInfo.new(semver, header)
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def load_semver(value, scope = nil)
|
|
103
|
+
def load_semver(value, errors, scope = nil)
|
|
150
104
|
result = Semver.for_name(value || "none")
|
|
151
105
|
unless result
|
|
152
106
|
tag = scope ? "#{@tag}(#{scope})" : @tag
|
|
153
|
-
|
|
107
|
+
errors << "Unknown semver: #{value} for tag #{tag}"
|
|
108
|
+
result = Semver::NONE
|
|
154
109
|
end
|
|
155
110
|
result
|
|
156
111
|
end
|
|
@@ -169,13 +124,13 @@ module Toys
|
|
|
169
124
|
# components
|
|
170
125
|
#
|
|
171
126
|
def initialize(repo_settings, info, has_multiple_components)
|
|
172
|
-
@name = info
|
|
173
|
-
@type = info["type"] || "component"
|
|
174
|
-
|
|
127
|
+
@name = info.delete("name").to_s
|
|
175
128
|
read_path_info(info, has_multiple_components)
|
|
176
129
|
read_file_modification_info(info)
|
|
177
130
|
read_gh_pages_info(repo_settings, info, has_multiple_components)
|
|
178
131
|
read_steps_info(repo_settings, info)
|
|
132
|
+
read_commit_tag_info(repo_settings, info)
|
|
133
|
+
check_problems(repo_settings, info)
|
|
179
134
|
end
|
|
180
135
|
|
|
181
136
|
##
|
|
@@ -183,12 +138,6 @@ module Toys
|
|
|
183
138
|
#
|
|
184
139
|
attr_reader :name
|
|
185
140
|
|
|
186
|
-
##
|
|
187
|
-
# @return [String] The type of component. Default is `"component"`.
|
|
188
|
-
# Subclasses may define other types.
|
|
189
|
-
#
|
|
190
|
-
attr_reader :type
|
|
191
|
-
|
|
192
141
|
##
|
|
193
142
|
# @return [String] The directory within the repo in which the component
|
|
194
143
|
# is located
|
|
@@ -247,6 +196,24 @@ module Toys
|
|
|
247
196
|
#
|
|
248
197
|
attr_reader :steps
|
|
249
198
|
|
|
199
|
+
##
|
|
200
|
+
# @return [Array<CommitTagSettings>] The conventional commit types
|
|
201
|
+
# recognized as release-triggering, along with information on the
|
|
202
|
+
# change they map to.
|
|
203
|
+
#
|
|
204
|
+
attr_reader :commit_tags
|
|
205
|
+
|
|
206
|
+
##
|
|
207
|
+
# @return [String] Header for breaking changes in a changelog
|
|
208
|
+
#
|
|
209
|
+
attr_reader :breaking_change_header
|
|
210
|
+
|
|
211
|
+
##
|
|
212
|
+
# @return [String] Notice displayed in the changelog when there are
|
|
213
|
+
# otherwise no significant updates in the release
|
|
214
|
+
#
|
|
215
|
+
attr_reader :no_significant_updates_notice
|
|
216
|
+
|
|
250
217
|
##
|
|
251
218
|
# @return [StepSettings,nil] The unique step with the given name
|
|
252
219
|
#
|
|
@@ -254,41 +221,69 @@ module Toys
|
|
|
254
221
|
steps.find { |t| t.name == name }
|
|
255
222
|
end
|
|
256
223
|
|
|
224
|
+
##
|
|
225
|
+
# Look up the settings for the given named tag.
|
|
226
|
+
#
|
|
227
|
+
# @param tag [String] Conventional commit tag to look up
|
|
228
|
+
# @return [CommitTagSettings] The commit tag settings for the given tag
|
|
229
|
+
#
|
|
230
|
+
def commit_tag_named(tag)
|
|
231
|
+
commit_tags.find { |elem| elem.tag == tag } || CommitTagSettings.empty(tag)
|
|
232
|
+
end
|
|
233
|
+
|
|
257
234
|
private
|
|
258
235
|
|
|
259
236
|
def read_path_info(info, has_multiple_components)
|
|
260
|
-
@directory = info
|
|
261
|
-
@include_globs = Array(info
|
|
262
|
-
@exclude_globs = Array(info
|
|
237
|
+
@directory = info.delete("directory") || (has_multiple_components ? name : ".")
|
|
238
|
+
@include_globs = Array(info.delete("include_globs"))
|
|
239
|
+
@exclude_globs = Array(info.delete("exclude_globs"))
|
|
263
240
|
end
|
|
264
241
|
|
|
265
242
|
def read_file_modification_info(info)
|
|
266
|
-
segments =
|
|
243
|
+
segments = @name.split("-")
|
|
267
244
|
name_path = segments.join("/")
|
|
268
|
-
@version_rb_path = info
|
|
269
|
-
@version_constant = info
|
|
245
|
+
@version_rb_path = info.delete("version_rb_path") || "lib/#{name_path}/version.rb"
|
|
246
|
+
@version_constant = info.delete("version_constant") ||
|
|
270
247
|
(segments.map { |seg| camelize(seg) } + ["VERSION"])
|
|
271
248
|
@version_constant = @version_constant.split("::") if @version_constant.is_a?(::String)
|
|
272
|
-
@changelog_path = info
|
|
249
|
+
@changelog_path = info.delete("changelog_path") || "CHANGELOG.md"
|
|
273
250
|
end
|
|
274
251
|
|
|
275
252
|
def read_gh_pages_info(repo_settings, info, has_multiple_components)
|
|
276
|
-
@gh_pages_directory = info["gh_pages_directory"] || (has_multiple_components ? name : ".")
|
|
277
|
-
@gh_pages_version_var = info["gh_pages_version_var"] ||
|
|
278
|
-
(has_multiple_components ? "version_#{name}".tr("-", "_") : "version")
|
|
279
253
|
@gh_pages_enabled = info.fetch("gh_pages_enabled") do |_key|
|
|
280
254
|
repo_settings.gh_pages_enabled ||
|
|
281
255
|
info.key?("gh_pages_directory") ||
|
|
282
256
|
info.key?("gh_pages_version_var")
|
|
283
257
|
end
|
|
258
|
+
info.delete("gh_pages_enabled")
|
|
259
|
+
@gh_pages_directory = info.delete("gh_pages_directory") || (has_multiple_components ? name : ".")
|
|
260
|
+
@gh_pages_version_var = info.delete("gh_pages_version_var") ||
|
|
261
|
+
(has_multiple_components ? "version_#{name}".tr("-", "_") : "version")
|
|
284
262
|
end
|
|
285
263
|
|
|
286
264
|
def read_steps_info(repo_settings, info)
|
|
287
|
-
@steps =
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
265
|
+
@steps =
|
|
266
|
+
if info.key?("steps")
|
|
267
|
+
repo_settings.read_steps(info.delete("steps"))
|
|
268
|
+
else
|
|
269
|
+
repo_settings.steps.map(&:deep_copy)
|
|
270
|
+
end
|
|
271
|
+
@steps = repo_settings.modify_steps(@steps, info.delete("modify_steps") || [])
|
|
272
|
+
@steps = repo_settings.prepend_steps(@steps, info.delete("prepend_steps") || [])
|
|
273
|
+
@steps = repo_settings.append_steps(@steps, info.delete("append_steps") || [])
|
|
274
|
+
@steps = repo_settings.delete_steps(@steps, info.delete("delete_steps") || [])
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def read_commit_tag_info(repo_settings, info)
|
|
278
|
+
@commit_tags =
|
|
279
|
+
if info.key?("commit_tags")
|
|
280
|
+
repo_settings.read_commit_tags(info.delete("commit_tags"))
|
|
281
|
+
else
|
|
282
|
+
repo_settings.commit_tags.dup
|
|
283
|
+
end
|
|
284
|
+
@breaking_change_header = info.delete("breaking_change_header") || repo_settings.breaking_change_header
|
|
285
|
+
@no_significant_updates_notice =
|
|
286
|
+
info.delete("no_significant_updates_notice") || repo_settings.no_significant_updates_notice
|
|
292
287
|
end
|
|
293
288
|
|
|
294
289
|
def camelize(str)
|
|
@@ -298,6 +293,13 @@ module Toys
|
|
|
298
293
|
.gsub(/_+/, "_")
|
|
299
294
|
.gsub(/(?:^|_)([a-zA-Z])/) { ::Regexp.last_match(1).upcase }
|
|
300
295
|
end
|
|
296
|
+
|
|
297
|
+
def check_problems(repo_settings, info)
|
|
298
|
+
info.each_key do |key|
|
|
299
|
+
repo_settings.errors << "Unknown key #{key.inspect} in component #{@name.inspect}"
|
|
300
|
+
end
|
|
301
|
+
repo_settings.errors << 'Component is missing required key "name"' if @name.empty?
|
|
302
|
+
end
|
|
301
303
|
end
|
|
302
304
|
|
|
303
305
|
##
|
|
@@ -312,18 +314,29 @@ module Toys
|
|
|
312
314
|
#
|
|
313
315
|
# @param info [Hash,String] Config data
|
|
314
316
|
#
|
|
315
|
-
def initialize(info)
|
|
317
|
+
def initialize(info, errors, containing_step_name)
|
|
316
318
|
@step_name = @dest = @source_path = @dest_path = nil
|
|
317
319
|
case info
|
|
318
320
|
when ::String
|
|
319
321
|
@step_name = info
|
|
320
322
|
@dest = "component"
|
|
321
323
|
when ::Hash
|
|
322
|
-
@step_name = info
|
|
323
|
-
@
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
@
|
|
324
|
+
@step_name = info.delete("name").to_s
|
|
325
|
+
if @step_name.empty?
|
|
326
|
+
errors << "Missing required key \"name\" in input for step #{containing_step_name.inspect}"
|
|
327
|
+
end
|
|
328
|
+
@dest = info.delete("dest")
|
|
329
|
+
if @dest == false
|
|
330
|
+
@dest = "none"
|
|
331
|
+
elsif @dest.nil?
|
|
332
|
+
@dest = "component"
|
|
333
|
+
end
|
|
334
|
+
@source_path = info.delete("source_path")
|
|
335
|
+
@dest_path = info.delete("dest_path")
|
|
336
|
+
@collisions = info.delete("collisions") || "error"
|
|
337
|
+
info.each_key do |key|
|
|
338
|
+
errors << "Unknown key #{key.inspect} in input for step #{containing_step_name.inspect}"
|
|
339
|
+
end
|
|
327
340
|
end
|
|
328
341
|
end
|
|
329
342
|
|
|
@@ -351,6 +364,12 @@ module Toys
|
|
|
351
364
|
#
|
|
352
365
|
attr_reader :dest_path
|
|
353
366
|
|
|
367
|
+
##
|
|
368
|
+
# @return [String] What to do if a collision occurs. Possible values are
|
|
369
|
+
# "error", "replace", and "keep".
|
|
370
|
+
#
|
|
371
|
+
attr_reader :collisions
|
|
372
|
+
|
|
354
373
|
##
|
|
355
374
|
# @return [Hash] the hash representation
|
|
356
375
|
#
|
|
@@ -360,6 +379,7 @@ module Toys
|
|
|
360
379
|
"dest" => dest,
|
|
361
380
|
"source_path" => source_path,
|
|
362
381
|
"dest_path" => dest_path,
|
|
382
|
+
"collisions" => collisions,
|
|
363
383
|
}
|
|
364
384
|
end
|
|
365
385
|
end
|
|
@@ -376,16 +396,20 @@ module Toys
|
|
|
376
396
|
#
|
|
377
397
|
# @param info [Hash,String] Config data
|
|
378
398
|
#
|
|
379
|
-
def initialize(info)
|
|
399
|
+
def initialize(info, errors, containing_step_name)
|
|
380
400
|
@source = @source_path = @dest_path = nil
|
|
381
401
|
case info
|
|
382
402
|
when ::String
|
|
383
403
|
@source_path = info
|
|
384
404
|
@source = "component"
|
|
385
405
|
when ::Hash
|
|
386
|
-
@source = info.
|
|
387
|
-
@source_path = info
|
|
388
|
-
@dest_path = info
|
|
406
|
+
@source = info.delete("source") || "component"
|
|
407
|
+
@source_path = info.delete("source_path")
|
|
408
|
+
@dest_path = info.delete("dest_path")
|
|
409
|
+
@collisions = info.delete("collisions") || "error"
|
|
410
|
+
info.each_key do |key|
|
|
411
|
+
errors << "Unknown key #{key.inspect} in output for step #{containing_step_name.inspect}"
|
|
412
|
+
end
|
|
389
413
|
end
|
|
390
414
|
end
|
|
391
415
|
|
|
@@ -407,6 +431,12 @@ module Toys
|
|
|
407
431
|
#
|
|
408
432
|
attr_reader :dest_path
|
|
409
433
|
|
|
434
|
+
##
|
|
435
|
+
# @return [String] What to do if a collision occurs. Possible values are
|
|
436
|
+
# "error", "replace", and "keep".
|
|
437
|
+
#
|
|
438
|
+
attr_reader :collisions
|
|
439
|
+
|
|
410
440
|
##
|
|
411
441
|
# @return [Hash] the hash representation
|
|
412
442
|
#
|
|
@@ -415,6 +445,7 @@ module Toys
|
|
|
415
445
|
"source" => source,
|
|
416
446
|
"source_path" => source_path,
|
|
417
447
|
"dest_path" => dest_path,
|
|
448
|
+
"collisions" => collisions,
|
|
418
449
|
}
|
|
419
450
|
end
|
|
420
451
|
end
|
|
@@ -427,8 +458,8 @@ module Toys
|
|
|
427
458
|
##
|
|
428
459
|
# Create a StepSettings
|
|
429
460
|
#
|
|
430
|
-
def initialize(info)
|
|
431
|
-
from_h(info.dup)
|
|
461
|
+
def initialize(info, errors)
|
|
462
|
+
from_h(info.dup, errors)
|
|
432
463
|
end
|
|
433
464
|
|
|
434
465
|
##
|
|
@@ -482,7 +513,7 @@ module Toys
|
|
|
482
513
|
# @return [StepSettings] A deep copy
|
|
483
514
|
#
|
|
484
515
|
def deep_copy
|
|
485
|
-
StepSettings.new(to_h)
|
|
516
|
+
StepSettings.new(to_h, [])
|
|
486
517
|
end
|
|
487
518
|
|
|
488
519
|
##
|
|
@@ -490,12 +521,16 @@ module Toys
|
|
|
490
521
|
# Initialize the step from the given hash.
|
|
491
522
|
# The hash will be deconstructed in place.
|
|
492
523
|
#
|
|
493
|
-
def from_h(info)
|
|
524
|
+
def from_h(info, errors)
|
|
494
525
|
@type = info.delete("type") || info["name"] || "noop"
|
|
495
526
|
@name = info.delete("name") || "_anon_#{@type}_#{object_id}"
|
|
496
527
|
@requested = info.delete("run") ? true : false
|
|
497
|
-
@inputs = Array(info.delete("inputs")).map
|
|
498
|
-
|
|
528
|
+
@inputs = Array(info.delete("inputs")).map do |input_info|
|
|
529
|
+
InputSettings.new(input_info, errors, @name)
|
|
530
|
+
end
|
|
531
|
+
@outputs = Array(info.delete("outputs")).map do |output_info|
|
|
532
|
+
OutputSettings.new(output_info, errors, @name)
|
|
533
|
+
end
|
|
499
534
|
@options = info
|
|
500
535
|
end
|
|
501
536
|
end
|
|
@@ -543,13 +578,14 @@ module Toys
|
|
|
543
578
|
def initialize(info)
|
|
544
579
|
@warnings = []
|
|
545
580
|
@errors = []
|
|
546
|
-
@default_component_name = nil
|
|
547
581
|
read_global_info(info)
|
|
582
|
+
read_required_checks_info(info)
|
|
548
583
|
read_label_info(info)
|
|
549
|
-
|
|
584
|
+
read_default_commit_tag_info(info)
|
|
550
585
|
read_default_step_info(info)
|
|
551
586
|
read_component_info(info)
|
|
552
587
|
read_coordination_info(info)
|
|
588
|
+
check_global_problems(info)
|
|
553
589
|
end
|
|
554
590
|
|
|
555
591
|
##
|
|
@@ -584,11 +620,6 @@ module Toys
|
|
|
584
620
|
#
|
|
585
621
|
attr_reader :git_user_email
|
|
586
622
|
|
|
587
|
-
##
|
|
588
|
-
# @return [String] The name of the default component to release
|
|
589
|
-
#
|
|
590
|
-
attr_reader :default_component_name
|
|
591
|
-
|
|
592
623
|
##
|
|
593
624
|
# @return [Array<Array<String>>] An array of groups of component names
|
|
594
625
|
# whose releases should be coordinated.
|
|
@@ -602,12 +633,6 @@ module Toys
|
|
|
602
633
|
#
|
|
603
634
|
attr_reader :required_checks_regexp
|
|
604
635
|
|
|
605
|
-
##
|
|
606
|
-
# @return [Regexp,nil] A regular expression identifying all the
|
|
607
|
-
# release-related GitHub checks
|
|
608
|
-
#
|
|
609
|
-
attr_reader :release_jobs_regexp
|
|
610
|
-
|
|
611
636
|
##
|
|
612
637
|
# @return [Numeric] The number of seconds that releases will wait for
|
|
613
638
|
# checks to complete.
|
|
@@ -620,11 +645,18 @@ module Toys
|
|
|
620
645
|
attr_reader :gh_pages_enabled
|
|
621
646
|
|
|
622
647
|
##
|
|
623
|
-
# @return [
|
|
624
|
-
# recognized as release-triggering, along with
|
|
625
|
-
# map to.
|
|
648
|
+
# @return [Array<CommitTagSettings>] The conventional commit types
|
|
649
|
+
# recognized as release-triggering, along with information on the
|
|
650
|
+
# change they map to.
|
|
651
|
+
#
|
|
652
|
+
attr_reader :commit_tags
|
|
653
|
+
|
|
654
|
+
##
|
|
655
|
+
# Get the build step pipeline
|
|
626
656
|
#
|
|
627
|
-
|
|
657
|
+
# @return [Array<StepSettings>] Step pipeline
|
|
658
|
+
#
|
|
659
|
+
attr_reader :steps
|
|
628
660
|
|
|
629
661
|
##
|
|
630
662
|
# @return [String] Header for breaking changes in a changelog
|
|
@@ -632,7 +664,8 @@ module Toys
|
|
|
632
664
|
attr_reader :breaking_change_header
|
|
633
665
|
|
|
634
666
|
##
|
|
635
|
-
# @return [String]
|
|
667
|
+
# @return [String] Notice displayed in the changelog when there are
|
|
668
|
+
# otherwise no significant updates in the release
|
|
636
669
|
#
|
|
637
670
|
attr_reader :no_significant_updates_notice
|
|
638
671
|
|
|
@@ -661,6 +694,16 @@ module Toys
|
|
|
661
694
|
#
|
|
662
695
|
attr_reader :release_branch_prefix
|
|
663
696
|
|
|
697
|
+
##
|
|
698
|
+
# Look up the settings for the given named tag.
|
|
699
|
+
#
|
|
700
|
+
# @param tag [String] Conventional commit tag to look up
|
|
701
|
+
# @return [CommitTagSettings] The commit tag settings for the given tag
|
|
702
|
+
#
|
|
703
|
+
def commit_tag_named(tag)
|
|
704
|
+
commit_tags.find { |elem| elem.tag == tag } || CommitTagSettings.empty(tag)
|
|
705
|
+
end
|
|
706
|
+
|
|
664
707
|
##
|
|
665
708
|
# @return [String] The owner of the repo
|
|
666
709
|
#
|
|
@@ -715,23 +758,17 @@ module Toys
|
|
|
715
758
|
@components[name]
|
|
716
759
|
end
|
|
717
760
|
|
|
718
|
-
##
|
|
719
|
-
# Get the default step pipeline settings for a component type
|
|
720
|
-
#
|
|
721
|
-
# @param component_type [String] Type of component
|
|
722
|
-
# @return [Array<StepSettings>] Step pipeline
|
|
723
|
-
#
|
|
724
|
-
def default_steps(component_type)
|
|
725
|
-
(@default_steps[component_type] || @default_steps["component"]).map(&:deep_copy)
|
|
726
|
-
end
|
|
727
|
-
|
|
728
761
|
# @private
|
|
729
762
|
def read_steps(info)
|
|
730
|
-
info.map { |step_info| StepSettings.new(step_info) }
|
|
763
|
+
Array(info).map { |step_info| StepSettings.new(step_info, @errors) }
|
|
731
764
|
end
|
|
732
765
|
|
|
733
766
|
# @private
|
|
734
|
-
def modify_steps(steps, modifications)
|
|
767
|
+
def modify_steps(steps, modifications) # rubocop:disable Metrics/MethodLength
|
|
768
|
+
unless modifications.is_a?(::Array)
|
|
769
|
+
@errors << "modify_steps expected an array of modification dictionaries"
|
|
770
|
+
return steps
|
|
771
|
+
end
|
|
735
772
|
modifications.each do |mod_data|
|
|
736
773
|
mod_name = mod_data.delete("name")
|
|
737
774
|
mod_type = mod_data.delete("type")
|
|
@@ -747,7 +784,7 @@ module Toys
|
|
|
747
784
|
modified_info[key] = value
|
|
748
785
|
end
|
|
749
786
|
end
|
|
750
|
-
step.from_h(modified_info)
|
|
787
|
+
step.from_h(modified_info, @errors)
|
|
751
788
|
end
|
|
752
789
|
if count.zero?
|
|
753
790
|
@errors << "Unable to find step to modify for name=#{mod_name.inspect} and type=#{mod_type.inspect}."
|
|
@@ -816,46 +853,54 @@ module Toys
|
|
|
816
853
|
|
|
817
854
|
# @private
|
|
818
855
|
def delete_steps(steps, info)
|
|
819
|
-
info.
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
856
|
+
if info.is_a?(::Array)
|
|
857
|
+
info.each do |del_name|
|
|
858
|
+
index = steps.find_index { |step| step.name == del_name }
|
|
859
|
+
if index
|
|
860
|
+
steps.delete_at(index)
|
|
861
|
+
else
|
|
862
|
+
@errors << "Unable to find step named #{del_name} to delete."
|
|
863
|
+
end
|
|
825
864
|
end
|
|
865
|
+
else
|
|
866
|
+
@errors << "delete_steps expected an array of names"
|
|
826
867
|
end
|
|
827
868
|
steps
|
|
828
869
|
end
|
|
829
870
|
|
|
871
|
+
# @private
|
|
872
|
+
def read_commit_tags(info)
|
|
873
|
+
Array(info).map { |tag_info| CommitTagSettings.new(tag_info, @errors) }
|
|
874
|
+
end
|
|
875
|
+
|
|
830
876
|
private
|
|
831
877
|
|
|
832
878
|
DEFAULT_MAIN_BRAMCH = "main"
|
|
833
879
|
private_constant :DEFAULT_MAIN_BRAMCH
|
|
834
880
|
|
|
835
|
-
|
|
881
|
+
DEFAULT_COMMIT_TAGS_YAML = <<~STRING
|
|
836
882
|
- tag: feat
|
|
837
|
-
header: ADDED
|
|
838
883
|
semver: minor
|
|
884
|
+
header: ADDED
|
|
839
885
|
- tag: fix
|
|
886
|
+
semver: patch
|
|
840
887
|
header: FIXED
|
|
841
|
-
- docs
|
|
888
|
+
- tag: docs
|
|
889
|
+
semver: patch
|
|
842
890
|
STRING
|
|
843
|
-
private_constant :
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
source: build_gem
|
|
855
|
-
- name: push_gh_pages
|
|
856
|
-
source: build_yard
|
|
891
|
+
private_constant :DEFAULT_COMMIT_TAGS_YAML
|
|
892
|
+
|
|
893
|
+
DEFAULT_STEPS_YAML = <<~STRING
|
|
894
|
+
- name: bundle
|
|
895
|
+
- name: build_gem
|
|
896
|
+
- name: build_yard
|
|
897
|
+
- name: release_github
|
|
898
|
+
- name: release_gem
|
|
899
|
+
source: build_gem
|
|
900
|
+
- name: push_gh_pages
|
|
901
|
+
source: build_yard
|
|
857
902
|
STRING
|
|
858
|
-
private_constant :
|
|
903
|
+
private_constant :DEFAULT_STEPS_YAML
|
|
859
904
|
|
|
860
905
|
DEFAULT_BREAKING_CHANGE_HEADER = "BREAKING CHANGE"
|
|
861
906
|
private_constant :DEFAULT_BREAKING_CHANGE_HEADER
|
|
@@ -876,102 +921,71 @@ module Toys
|
|
|
876
921
|
private_constant :DEFAULT_RELEASE_COMPLETE_LABEL
|
|
877
922
|
|
|
878
923
|
def read_global_info(info)
|
|
879
|
-
@main_branch = info
|
|
880
|
-
@repo_path = info
|
|
881
|
-
@signoff_commits = info
|
|
882
|
-
@gh_pages_enabled = info
|
|
883
|
-
@enable_release_automation = info
|
|
884
|
-
|
|
885
|
-
@
|
|
886
|
-
@
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
@
|
|
924
|
+
@main_branch = info.delete("main_branch") || DEFAULT_MAIN_BRAMCH
|
|
925
|
+
@repo_path = info.delete("repo")
|
|
926
|
+
@signoff_commits = info.delete("signoff_commits") ? true : false
|
|
927
|
+
@gh_pages_enabled = info.delete("gh_pages_enabled") ? true : false
|
|
928
|
+
@enable_release_automation = info.delete("enable_release_automation") != false
|
|
929
|
+
@release_branch_prefix = info.delete("release_branch_prefix") || "release"
|
|
930
|
+
@git_user_name = info.delete("git_user_name")
|
|
931
|
+
@git_user_email = info.delete("git_user_email")
|
|
932
|
+
end
|
|
933
|
+
|
|
934
|
+
def read_required_checks_info(info)
|
|
935
|
+
required_checks = info.delete("required_checks")
|
|
936
|
+
@required_checks_regexp =
|
|
937
|
+
case required_checks
|
|
938
|
+
when false
|
|
939
|
+
nil
|
|
940
|
+
when true
|
|
941
|
+
//
|
|
942
|
+
else
|
|
943
|
+
::Regexp.new(required_checks.to_s)
|
|
944
|
+
end
|
|
945
|
+
@required_checks_timeout = info.delete("required_checks_timeout") || 900
|
|
892
946
|
end
|
|
893
947
|
|
|
894
948
|
def read_label_info(info)
|
|
895
|
-
@release_pending_label = info
|
|
896
|
-
@release_error_label = info
|
|
897
|
-
@release_aborted_label = info
|
|
898
|
-
@release_complete_label = info
|
|
899
|
-
end
|
|
900
|
-
|
|
901
|
-
def read_commit_tag_info(info)
|
|
902
|
-
@release_commit_tags = read_commit_tag_info_set(info["release_commit_tags"] || DEFAULT_RELEASE_COMMIT_TAGS)
|
|
903
|
-
info["modify_release_commit_tags"]&.each do |tag, data|
|
|
904
|
-
if data.nil?
|
|
905
|
-
@release_commit_tags.delete(tag)
|
|
906
|
-
elsif (tag_settings = @release_commit_tags[tag])
|
|
907
|
-
tag_settings.modify(data)
|
|
908
|
-
end
|
|
909
|
-
end
|
|
910
|
-
@release_commit_tags = read_commit_tag_info_set(info["prepend_release_commit_tags"]).merge(@release_commit_tags)
|
|
911
|
-
@release_commit_tags.merge!(read_commit_tag_info_set(info["append_release_commit_tags"]))
|
|
912
|
-
@breaking_change_header = info["breaking_change_header"] || DEFAULT_BREAKING_CHANGE_HEADER
|
|
913
|
-
@no_significant_updates_notice = info["no_significant_updates_notice"] || DEFAULT_NO_SIGNIFICANT_UPDATES_NOTICE
|
|
949
|
+
@release_pending_label = info.delete("release_pending_label") || DEFAULT_RELEASE_PENDING_LABEL
|
|
950
|
+
@release_error_label = info.delete("release_error_label") || DEFAULT_RELEASE_ERROR_LABEL
|
|
951
|
+
@release_aborted_label = info.delete("release_aborted_label") || DEFAULT_RELEASE_ABORTED_LABEL
|
|
952
|
+
@release_complete_label = info.delete("release_complete_label") || DEFAULT_RELEASE_COMPLETE_LABEL
|
|
914
953
|
end
|
|
915
954
|
|
|
916
|
-
def
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
955
|
+
def read_default_commit_tag_info(info)
|
|
956
|
+
@commit_tags = read_commit_tags(info.delete("commit_tags") || ::YAML.load(DEFAULT_COMMIT_TAGS_YAML))
|
|
957
|
+
@breaking_change_header = info.delete("breaking_change_header") || DEFAULT_BREAKING_CHANGE_HEADER
|
|
958
|
+
@no_significant_updates_notice =
|
|
959
|
+
info.delete("no_significant_updates_notice") || DEFAULT_NO_SIGNIFICANT_UPDATES_NOTICE
|
|
921
960
|
end
|
|
922
961
|
|
|
923
|
-
def read_default_step_info(info)
|
|
924
|
-
|
|
925
|
-
@
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
(info["modify_default_steps"] || {}).each do |key, data|
|
|
930
|
-
@default_steps[key] = modify_steps(@default_steps[key], data)
|
|
931
|
-
end
|
|
932
|
-
(info["append_default_steps"] || {}).each do |key, data|
|
|
933
|
-
@default_steps[key] = append_steps(@default_steps[key], data)
|
|
934
|
-
end
|
|
935
|
-
(info["prepend_default_steps"] || {}).each do |key, data|
|
|
936
|
-
@default_steps[key] = prepend_steps(@default_steps[key], data)
|
|
937
|
-
end
|
|
938
|
-
(info["delete_default_steps"] || {}).each do |key, data|
|
|
939
|
-
@default_steps[key] = delete_steps(@default_steps[key], data)
|
|
940
|
-
end
|
|
962
|
+
def read_default_step_info(info)
|
|
963
|
+
@steps = read_steps(info.delete("steps") || ::YAML.load(DEFAULT_STEPS_YAML))
|
|
964
|
+
@steps = modify_steps(@steps, info.delete("modify_steps") || [])
|
|
965
|
+
@steps = prepend_steps(@steps, info.delete("prepend_steps") || [])
|
|
966
|
+
@steps = append_steps(@steps, info.delete("append_steps") || [])
|
|
967
|
+
@steps = delete_steps(@steps, info.delete("delete_steps") || [])
|
|
941
968
|
end
|
|
942
969
|
|
|
943
970
|
def read_component_info(info)
|
|
944
971
|
@components = {}
|
|
945
|
-
|
|
946
|
-
@has_multiple_components =
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
972
|
+
component_info_array = Array(info.delete("components")) + Array(info.delete("gems"))
|
|
973
|
+
@has_multiple_components = component_info_array.size > 1
|
|
974
|
+
component_info_array.each do |component_info|
|
|
975
|
+
component = ComponentSettings.new(self, component_info, @has_multiple_components)
|
|
976
|
+
if component.name.empty?
|
|
977
|
+
@errors << "A component is missing a name"
|
|
978
|
+
elsif @components[component.name]
|
|
979
|
+
@errors << "Duplicate component #{component.name.inspect}"
|
|
980
|
+
else
|
|
981
|
+
@components[component.name] = component
|
|
982
|
+
end
|
|
953
983
|
end
|
|
954
984
|
@errors << "No components found" if @components.empty?
|
|
955
985
|
end
|
|
956
986
|
|
|
957
|
-
def read_component_settings(component_info)
|
|
958
|
-
component = ComponentSettings.new(self, component_info, @has_multiple_components)
|
|
959
|
-
if component.name.empty?
|
|
960
|
-
@errors << "A component is missing a name"
|
|
961
|
-
elsif @components[component.name]
|
|
962
|
-
@errors << "Duplicate component #{component.name.inspect}"
|
|
963
|
-
else
|
|
964
|
-
@components[component.name] = component
|
|
965
|
-
@default_component_name ||= component.name
|
|
966
|
-
end
|
|
967
|
-
end
|
|
968
|
-
|
|
969
987
|
def read_coordination_info(info)
|
|
970
|
-
|
|
971
|
-
@coordination_groups = [@components.keys]
|
|
972
|
-
return
|
|
973
|
-
end
|
|
974
|
-
@coordination_groups = Array(info["coordination_groups"])
|
|
988
|
+
@coordination_groups = Array(info.delete("coordination_groups"))
|
|
975
989
|
@coordination_groups = [@coordination_groups] if @coordination_groups.first.is_a?(::String)
|
|
976
990
|
seen = {}
|
|
977
991
|
@coordination_groups.each do |group|
|
|
@@ -985,6 +999,18 @@ module Toys
|
|
|
985
999
|
end
|
|
986
1000
|
end
|
|
987
1001
|
end
|
|
1002
|
+
if info.delete("coordinate_versions") && @coordination_groups.empty?
|
|
1003
|
+
@coordination_groups = [@components.keys]
|
|
1004
|
+
end
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
def check_global_problems(info)
|
|
1008
|
+
info.each_key do |key|
|
|
1009
|
+
@errors << "Unknown top level key #{key.inspect} in releases.yml"
|
|
1010
|
+
end
|
|
1011
|
+
@errors << 'Required key "repo" missing from releases.yml' unless @repo_path
|
|
1012
|
+
@errors << 'Required key "git_user_name" missing from releases.yml' unless @git_user_name
|
|
1013
|
+
@errors << 'Required key "git_user_email" missing from releases.yml' unless @git_user_email
|
|
988
1014
|
end
|
|
989
1015
|
end
|
|
990
1016
|
end
|