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.
@@ -14,17 +14,31 @@ module Toys
14
14
  ScopeInfo = ::Struct.new(:semver, :header)
15
15
 
16
16
  ##
17
- # @private
18
- # Create a CommitTagSettings from either a tag name string (which will
19
- # default to patch releases) or a hash with fields.
17
+ # Create an empty settings for an unknown tag
18
+ #
19
+ # @param tag [String] Conventional commit tag
20
+ # @return [CommitTagSettings]
20
21
  #
21
- def initialize(input)
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
- case input
24
- when ::String
25
- init_from_string(input)
26
- when ::Hash
27
- init_from_hash(input)
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 init_from_string(input)
99
- @tag = input
100
- @header = @tag.upcase
101
- @semver = Semver::PATCH
102
- end
103
-
104
- def init_from_hash(input)
105
- if input.size == 1
106
- key = input.keys.first
107
- value = input.values.first
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 load_hash(input)
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
- raise "Unknown semver: #{value} for tag #{tag}"
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["name"]
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["directory"] || (has_multiple_components ? name : ".")
261
- @include_globs = Array(info["include_globs"])
262
- @exclude_globs = Array(info["exclude_globs"])
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 = info["name"].split("-")
243
+ segments = @name.split("-")
267
244
  name_path = segments.join("/")
268
- @version_rb_path = info["version_rb_path"] || "lib/#{name_path}/version.rb"
269
- @version_constant = info["version_constant"] ||
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["changelog_path"] || "CHANGELOG.md"
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 = info["steps"] ? repo_settings.read_steps(info["steps"]) : repo_settings.default_steps(@type)
288
- @steps = repo_settings.modify_steps(@steps, info["modify_steps"] || [])
289
- @steps = repo_settings.prepend_steps(@steps, info["prepend_steps"] || [])
290
- @steps = repo_settings.append_steps(@steps, info["append_steps"] || [])
291
- @steps = repo_settings.delete_steps(@steps, info["delete_steps"] || [])
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["name"]
323
- @dest = info.fetch("dest", "component")
324
- @dest = "none" if @dest == false
325
- @source_path = info["source_path"]
326
- @dest_path = info["dest_path"]
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.fetch("source", "component")
387
- @source_path = info["source_path"]
388
- @dest_path = info["dest_path"]
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 { |input_info| InputSettings.new(input_info) }
498
- @outputs = Array(info.delete("outputs")).map { |output_info| OutputSettings.new(output_info) }
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
- read_commit_tag_info(info)
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 [Hash{String=>CommitTagSettings}] The conventional commit types
624
- # recognized as release-triggering, along with the type of change they
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
- attr_reader :release_commit_tags
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] No significant updates notice
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.each do |del_name|
820
- index = steps.find_index { |step| step.name == del_name }
821
- if index
822
- steps.delete_at(index)
823
- else
824
- @errors << "Unable to find step named #{del_name} to delete."
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
- DEFAULT_RELEASE_COMMIT_TAGS = ::YAML.load(<<~STRING) # rubocop:disable Security/YAMLLoad
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 :DEFAULT_RELEASE_COMMIT_TAGS
844
-
845
- DEFAULT_STEPS = ::YAML.load(<<~STRING) # rubocop:disable Security/YAMLLoad
846
- component:
847
- - name: release_github
848
- gem:
849
- - name: bundle
850
- - name: build_gem
851
- - name: build_yard
852
- - name: release_github
853
- - name: release_gem
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 :DEFAULT_STEPS
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["main_branch"] || DEFAULT_MAIN_BRAMCH
880
- @repo_path = info["repo"]
881
- @signoff_commits = info["signoff_commits"] ? true : false
882
- @gh_pages_enabled = info["gh_pages_enabled"] ? true : false
883
- @enable_release_automation = info["enable_release_automation"] != false
884
- required_checks = info["required_checks"]
885
- @required_checks_regexp = required_checks == false ? nil : ::Regexp.new(required_checks.to_s)
886
- @required_checks_timeout = info["required_checks_timeout"] || 900
887
- @release_jobs_regexp = ::Regexp.new(info["release_jobs_regexp"] || "^release-")
888
- @release_branch_prefix = info["release_branch_prefix"] || "release"
889
- @git_user_name = info["git_user_name"]
890
- @git_user_email = info["git_user_email"]
891
- @errors << "Repo key missing from releases.yml" unless @repo_path
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["release_pending_label"] || DEFAULT_RELEASE_PENDING_LABEL
896
- @release_error_label = info["release_error_label"] || DEFAULT_RELEASE_ERROR_LABEL
897
- @release_aborted_label = info["release_aborted_label"] || DEFAULT_RELEASE_ABORTED_LABEL
898
- @release_complete_label = info["release_complete_label"] || DEFAULT_RELEASE_COMPLETE_LABEL
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 read_commit_tag_info_set(input)
917
- input.to_h do |value|
918
- settings = CommitTagSettings.new(value)
919
- [settings.tag, settings]
920
- end
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) # rubocop:disable Metrics/AbcSize
924
- default_step_data = info["default_steps"] || DEFAULT_STEPS
925
- @default_steps = {}
926
- default_step_data.each do |key, data|
927
- @default_steps[key] = read_steps(data)
928
- end
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
- @default_component_name = nil
946
- @has_multiple_components = (info["components"]&.size.to_i + info["gems"]&.size.to_i) > 1
947
- info["gems"]&.each do |component_info|
948
- component_info["type"] = "gem"
949
- read_component_settings(component_info)
950
- end
951
- info["components"]&.each do |component_info|
952
- read_component_settings(component_info)
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
- if info["coordinate_versions"]
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