toys-release 0.1.1 → 0.2.2
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 +18 -0
- data/README.md +3 -3
- data/lib/toys/release/version.rb +1 -1
- data/toys/.lib/toys/release/artifact_dir.rb +21 -1
- data/toys/.lib/toys/release/change_set.rb +1 -1
- data/toys/.lib/toys/release/component.rb +19 -19
- data/toys/.lib/toys/release/environment_utils.rb +16 -6
- data/toys/.lib/toys/release/performer.rb +46 -36
- data/toys/.lib/toys/release/pipeline.rb +548 -0
- data/toys/.lib/toys/release/pull_request.rb +0 -2
- data/toys/.lib/toys/release/repo_settings.rb +250 -114
- data/toys/.lib/toys/release/repository.rb +4 -5
- data/toys/.lib/toys/release/request_spec.rb +1 -1
- data/toys/.lib/toys/release/steps.rb +265 -428
- data/toys/_onclosed.rb +1 -1
- data/toys/perform.rb +7 -5
- data/toys/retry.rb +20 -14
- metadata +4 -5
- data/toys/.data/templates/release-hook-on-open.yml.erb +0 -30
- data/toys/_onopen.rb +0 -158
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "yaml"
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
require "toys/release/semver"
|
|
6
6
|
|
|
7
7
|
module Toys
|
|
8
8
|
module Release
|
|
@@ -14,6 +14,7 @@ module Toys
|
|
|
14
14
|
ScopeInfo = ::Struct.new(:semver, :header)
|
|
15
15
|
|
|
16
16
|
##
|
|
17
|
+
# @private
|
|
17
18
|
# Create a CommitTagSettings from either a tag name string (which will
|
|
18
19
|
# default to patch releases) or a hash with fields.
|
|
19
20
|
#
|
|
@@ -160,6 +161,7 @@ module Toys
|
|
|
160
161
|
#
|
|
161
162
|
class ComponentSettings
|
|
162
163
|
##
|
|
164
|
+
# @private
|
|
163
165
|
# Create a ComponentSettings from input data structures
|
|
164
166
|
#
|
|
165
167
|
# @param info [Hash] Nested hash input
|
|
@@ -299,13 +301,134 @@ module Toys
|
|
|
299
301
|
end
|
|
300
302
|
|
|
301
303
|
##
|
|
304
|
+
# Configuration of input settings for a step.
|
|
305
|
+
# An input declares a dependency on a step, and copies any files output by
|
|
306
|
+
# that dependency.
|
|
307
|
+
#
|
|
308
|
+
class InputSettings
|
|
309
|
+
##
|
|
310
|
+
# @private
|
|
311
|
+
# Construct input settings
|
|
312
|
+
#
|
|
313
|
+
# @param info [Hash,String] Config data
|
|
314
|
+
#
|
|
315
|
+
def initialize(info)
|
|
316
|
+
@step_name = @dest = @source_path = @dest_path = nil
|
|
317
|
+
case info
|
|
318
|
+
when ::String
|
|
319
|
+
@step_name = info
|
|
320
|
+
@dest = "component"
|
|
321
|
+
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"]
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
##
|
|
331
|
+
# @return [String] Name of the step to copy data from.
|
|
332
|
+
#
|
|
333
|
+
attr_reader :step_name
|
|
334
|
+
|
|
335
|
+
##
|
|
336
|
+
# @return [String,false] Where to copy data to. Possible values are
|
|
337
|
+
# "component", "repo_root", "output", "temp", and "none". If "none",
|
|
338
|
+
# no copying is performed and this input declares a dependency only.
|
|
339
|
+
#
|
|
340
|
+
attr_reader :dest
|
|
341
|
+
|
|
342
|
+
##
|
|
343
|
+
# @return [String,nil] Path in the source to copy from. Can be a path to
|
|
344
|
+
# a file or a directory. If nil, copy everything from the input.
|
|
345
|
+
#
|
|
346
|
+
attr_reader :source_path
|
|
347
|
+
|
|
348
|
+
##
|
|
349
|
+
# @return [String,nil] Path in the destination to copy to, relative to
|
|
350
|
+
# the destination. If nil, uses the source path.
|
|
351
|
+
#
|
|
352
|
+
attr_reader :dest_path
|
|
353
|
+
|
|
354
|
+
##
|
|
355
|
+
# @return [Hash] the hash representation
|
|
356
|
+
#
|
|
357
|
+
def to_h
|
|
358
|
+
{
|
|
359
|
+
"name" => step_name,
|
|
360
|
+
"dest" => dest,
|
|
361
|
+
"source_path" => source_path,
|
|
362
|
+
"dest_path" => dest_path,
|
|
363
|
+
}
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
##
|
|
368
|
+
# Configuration of output info for a step.
|
|
369
|
+
# An output automatically copies files from the repo directory to this
|
|
370
|
+
# step's output where they can be imported by another step.
|
|
371
|
+
#
|
|
372
|
+
class OutputSettings
|
|
373
|
+
##
|
|
374
|
+
# @private
|
|
375
|
+
# Construct output settings
|
|
376
|
+
#
|
|
377
|
+
# @param info [Hash,String] Config data
|
|
378
|
+
#
|
|
379
|
+
def initialize(info)
|
|
380
|
+
@source = @source_path = @dest_path = nil
|
|
381
|
+
case info
|
|
382
|
+
when ::String
|
|
383
|
+
@source_path = info
|
|
384
|
+
@source = "component"
|
|
385
|
+
when ::Hash
|
|
386
|
+
@source = info.fetch("source", "component")
|
|
387
|
+
@source_path = info["source_path"]
|
|
388
|
+
@dest_path = info["dest_path"]
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
##
|
|
393
|
+
# @return [String] Where to copy data from. Possible values are
|
|
394
|
+
# "component", "repo_root", and "temp".
|
|
395
|
+
#
|
|
396
|
+
attr_reader :source
|
|
397
|
+
|
|
398
|
+
##
|
|
399
|
+
# @return [String,nil] Path to copy from, relative to the source. Can be
|
|
400
|
+
# a file or a directory. If nil, copy everything in the source.
|
|
401
|
+
#
|
|
402
|
+
attr_reader :source_path
|
|
403
|
+
|
|
404
|
+
##
|
|
405
|
+
# @return [String,nil] Path in the step's output to copy to.
|
|
406
|
+
# If nil, uses the source path.
|
|
407
|
+
#
|
|
408
|
+
attr_reader :dest_path
|
|
409
|
+
|
|
410
|
+
##
|
|
411
|
+
# @return [Hash] the hash representation
|
|
412
|
+
#
|
|
413
|
+
def to_h
|
|
414
|
+
{
|
|
415
|
+
"source" => source,
|
|
416
|
+
"source_path" => source_path,
|
|
417
|
+
"dest_path" => dest_path,
|
|
418
|
+
}
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
##
|
|
423
|
+
# @private
|
|
302
424
|
# Configuration of a step
|
|
303
425
|
#
|
|
304
426
|
class StepSettings
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
427
|
+
##
|
|
428
|
+
# Create a StepSettings
|
|
429
|
+
#
|
|
430
|
+
def initialize(info)
|
|
431
|
+
from_h(info.dup)
|
|
309
432
|
end
|
|
310
433
|
|
|
311
434
|
##
|
|
@@ -318,18 +441,62 @@ module Toys
|
|
|
318
441
|
#
|
|
319
442
|
attr_reader :type
|
|
320
443
|
|
|
444
|
+
##
|
|
445
|
+
# @return [boolean] Whether this step is explicitly requested
|
|
446
|
+
#
|
|
447
|
+
def requested?
|
|
448
|
+
@requested
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
##
|
|
452
|
+
# @return [Array<InputSettings>] Inputs for this step
|
|
453
|
+
#
|
|
454
|
+
attr_reader :inputs
|
|
455
|
+
|
|
456
|
+
##
|
|
457
|
+
# @return [Array<OutputSettings>] Extra outputs for this step
|
|
458
|
+
#
|
|
459
|
+
attr_reader :outputs
|
|
460
|
+
|
|
321
461
|
##
|
|
322
462
|
# @return [Hash{String=>Object}] Options for this step
|
|
323
463
|
#
|
|
324
464
|
attr_reader :options
|
|
325
465
|
|
|
466
|
+
##
|
|
467
|
+
# @return [Hash] the hash representation
|
|
468
|
+
#
|
|
469
|
+
def to_h
|
|
470
|
+
{
|
|
471
|
+
"name" => name,
|
|
472
|
+
"type" => type,
|
|
473
|
+
"run" => requested?,
|
|
474
|
+
"inputs" => inputs.map(&:to_h),
|
|
475
|
+
"outputs" => outputs.map(&:to_h),
|
|
476
|
+
}.merge(RepoSettings.deep_copy(options))
|
|
477
|
+
end
|
|
478
|
+
|
|
326
479
|
##
|
|
327
480
|
# Make a deep copy
|
|
328
481
|
#
|
|
329
482
|
# @return [StepSettings] A deep copy
|
|
330
483
|
#
|
|
331
484
|
def deep_copy
|
|
332
|
-
StepSettings.new(
|
|
485
|
+
StepSettings.new(to_h)
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
##
|
|
489
|
+
# @private
|
|
490
|
+
# Initialize the step from the given hash.
|
|
491
|
+
# The hash will be deconstructed in place.
|
|
492
|
+
#
|
|
493
|
+
def from_h(info)
|
|
494
|
+
@type = info.delete("type") || info["name"] || "noop"
|
|
495
|
+
@name = info.delete("name") || "_anon_#{@type}_#{object_id}"
|
|
496
|
+
@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) }
|
|
499
|
+
@options = info
|
|
333
500
|
end
|
|
334
501
|
end
|
|
335
502
|
|
|
@@ -368,6 +535,7 @@ module Toys
|
|
|
368
535
|
end
|
|
369
536
|
|
|
370
537
|
##
|
|
538
|
+
# @private
|
|
371
539
|
# Create a repo configuration object.
|
|
372
540
|
#
|
|
373
541
|
# @param info [Hash] Configuration hash read from JSON.
|
|
@@ -378,7 +546,6 @@ module Toys
|
|
|
378
546
|
@default_component_name = nil
|
|
379
547
|
read_global_info(info)
|
|
380
548
|
read_label_info(info)
|
|
381
|
-
read_commit_lint_info(info)
|
|
382
549
|
read_commit_tag_info(info)
|
|
383
550
|
read_default_step_info(info)
|
|
384
551
|
read_component_info(info)
|
|
@@ -452,18 +619,6 @@ module Toys
|
|
|
452
619
|
#
|
|
453
620
|
attr_reader :gh_pages_enabled
|
|
454
621
|
|
|
455
|
-
##
|
|
456
|
-
# @return [Array<String>] The merge strategies allowed when linting
|
|
457
|
-
# commit messages.
|
|
458
|
-
#
|
|
459
|
-
attr_reader :commit_lint_merge
|
|
460
|
-
|
|
461
|
-
##
|
|
462
|
-
# @return [Array<String>] The allowed conventional commit types when
|
|
463
|
-
# linting commit messages.
|
|
464
|
-
#
|
|
465
|
-
attr_reader :commit_lint_allowed_types
|
|
466
|
-
|
|
467
622
|
##
|
|
468
623
|
# @return [Hash{String=>CommitTagSettings}] The conventional commit types
|
|
469
624
|
# recognized as release-triggering, along with the type of change they
|
|
@@ -535,21 +690,6 @@ module Toys
|
|
|
535
690
|
@enable_release_automation
|
|
536
691
|
end
|
|
537
692
|
|
|
538
|
-
##
|
|
539
|
-
# @return [boolean] Whether conventional commit linting errors should fail
|
|
540
|
-
# GitHub checks.
|
|
541
|
-
#
|
|
542
|
-
def commit_lint_fail_checks?
|
|
543
|
-
@commit_lint_fail_checks
|
|
544
|
-
end
|
|
545
|
-
|
|
546
|
-
##
|
|
547
|
-
# @return [boolean] Whether to perform conventional commit linting.
|
|
548
|
-
#
|
|
549
|
-
def commit_lint_active?
|
|
550
|
-
@commit_lint_active
|
|
551
|
-
end
|
|
552
|
-
|
|
553
693
|
##
|
|
554
694
|
# @return [Array<String>] A list of all component names.
|
|
555
695
|
#
|
|
@@ -587,18 +727,7 @@ module Toys
|
|
|
587
727
|
|
|
588
728
|
# @private
|
|
589
729
|
def read_steps(info)
|
|
590
|
-
|
|
591
|
-
info.each do |step_info|
|
|
592
|
-
step_info = step_info.dup
|
|
593
|
-
name = step_info.delete("name")
|
|
594
|
-
type = step_info.delete("type")
|
|
595
|
-
if type
|
|
596
|
-
steps << StepSettings.new(name, type, step_info)
|
|
597
|
-
else
|
|
598
|
-
@errors << "No step type provided for step #{name.inspect}"
|
|
599
|
-
end
|
|
600
|
-
end
|
|
601
|
-
steps
|
|
730
|
+
info.map { |step_info| StepSettings.new(step_info) }
|
|
602
731
|
end
|
|
603
732
|
|
|
604
733
|
# @private
|
|
@@ -610,14 +739,15 @@ module Toys
|
|
|
610
739
|
steps.each do |step|
|
|
611
740
|
next if (mod_name && step.name != mod_name) || (mod_type && step.type != mod_type)
|
|
612
741
|
count += 1
|
|
613
|
-
|
|
742
|
+
modified_info = step.to_h
|
|
614
743
|
mod_data.each do |key, value|
|
|
615
744
|
if value.nil?
|
|
616
|
-
|
|
745
|
+
modified_info.delete(key)
|
|
617
746
|
else
|
|
618
|
-
|
|
747
|
+
modified_info[key] = value
|
|
619
748
|
end
|
|
620
749
|
end
|
|
750
|
+
step.from_h(modified_info)
|
|
621
751
|
end
|
|
622
752
|
if count.zero?
|
|
623
753
|
@errors << "Unable to find step to modify for name=#{mod_name.inspect} and type=#{mod_type.inspect}."
|
|
@@ -628,14 +758,60 @@ module Toys
|
|
|
628
758
|
|
|
629
759
|
# @private
|
|
630
760
|
def prepend_steps(steps, info)
|
|
631
|
-
|
|
632
|
-
|
|
761
|
+
before = []
|
|
762
|
+
insert = []
|
|
763
|
+
after = steps
|
|
764
|
+
case info
|
|
765
|
+
when ::Hash
|
|
766
|
+
if (before_name = info["before"])
|
|
767
|
+
before_index = steps.find_index { |step| step.name == before_name }
|
|
768
|
+
if before_index
|
|
769
|
+
before = steps[...before_index]
|
|
770
|
+
after = steps[before_index..]
|
|
771
|
+
else
|
|
772
|
+
@errors << "Unable to find step named #{before_name} in prepend_steps.before"
|
|
773
|
+
end
|
|
774
|
+
end
|
|
775
|
+
if (steps_info = info["steps"]).is_a?(::Array)
|
|
776
|
+
insert = read_steps(steps_info)
|
|
777
|
+
else
|
|
778
|
+
@errors << "steps expected in prepend_steps"
|
|
779
|
+
end
|
|
780
|
+
when ::Array
|
|
781
|
+
insert = read_steps(info)
|
|
782
|
+
else
|
|
783
|
+
@errors << "prepend_steps expected a hash or array"
|
|
784
|
+
end
|
|
785
|
+
before + insert + after
|
|
633
786
|
end
|
|
634
787
|
|
|
635
788
|
# @private
|
|
636
789
|
def append_steps(steps, info)
|
|
637
|
-
|
|
638
|
-
|
|
790
|
+
before = steps
|
|
791
|
+
insert = []
|
|
792
|
+
after = []
|
|
793
|
+
case info
|
|
794
|
+
when ::Hash
|
|
795
|
+
if (after_name = info["after"])
|
|
796
|
+
after_index = steps.find_index { |step| step.name == after_name }
|
|
797
|
+
if after_index
|
|
798
|
+
before = steps[..after_index]
|
|
799
|
+
after = steps[(after_index + 1)..]
|
|
800
|
+
else
|
|
801
|
+
@errors << "Unable to find step named #{after_name} in append_steps.after"
|
|
802
|
+
end
|
|
803
|
+
end
|
|
804
|
+
if (steps_info = info["steps"]).is_a?(::Array)
|
|
805
|
+
insert = read_steps(steps_info)
|
|
806
|
+
else
|
|
807
|
+
@errors << "steps expected in append_steps"
|
|
808
|
+
end
|
|
809
|
+
when ::Array
|
|
810
|
+
insert = read_steps(info)
|
|
811
|
+
else
|
|
812
|
+
@errors << "append_steps expected a hash or array"
|
|
813
|
+
end
|
|
814
|
+
before + insert + after
|
|
639
815
|
end
|
|
640
816
|
|
|
641
817
|
# @private
|
|
@@ -656,57 +832,29 @@ module Toys
|
|
|
656
832
|
DEFAULT_MAIN_BRAMCH = "main"
|
|
657
833
|
private_constant :DEFAULT_MAIN_BRAMCH
|
|
658
834
|
|
|
659
|
-
DEFAULT_RELEASE_COMMIT_TAGS =
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
"header" => "FIXED",
|
|
668
|
-
}.freeze,
|
|
669
|
-
"docs",
|
|
670
|
-
].freeze
|
|
835
|
+
DEFAULT_RELEASE_COMMIT_TAGS = ::YAML.load(<<~STRING) # rubocop:disable Security/YAMLLoad
|
|
836
|
+
- tag: feat
|
|
837
|
+
header: ADDED
|
|
838
|
+
semver: minor
|
|
839
|
+
- tag: fix
|
|
840
|
+
header: FIXED
|
|
841
|
+
- docs
|
|
842
|
+
STRING
|
|
671
843
|
private_constant :DEFAULT_RELEASE_COMMIT_TAGS
|
|
672
844
|
|
|
673
|
-
DEFAULT_STEPS =
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
"name" => "build-gem",
|
|
687
|
-
"type" => "BuildGem",
|
|
688
|
-
}.freeze,
|
|
689
|
-
{
|
|
690
|
-
"name" => "build-yard",
|
|
691
|
-
"type" => "BuildYard",
|
|
692
|
-
"require_gh_pages_enabled" => true,
|
|
693
|
-
}.freeze,
|
|
694
|
-
{
|
|
695
|
-
"name" => "github-release",
|
|
696
|
-
"type" => "GitHubRelease",
|
|
697
|
-
}.freeze,
|
|
698
|
-
{
|
|
699
|
-
"name" => "release-gem",
|
|
700
|
-
"type" => "ReleaseGem",
|
|
701
|
-
"input" => "build-gem",
|
|
702
|
-
}.freeze,
|
|
703
|
-
{
|
|
704
|
-
"name" => "push-gh-pages",
|
|
705
|
-
"type" => "PushGhPages",
|
|
706
|
-
"input" => "build-yard",
|
|
707
|
-
}.freeze,
|
|
708
|
-
].freeze,
|
|
709
|
-
}.freeze
|
|
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
|
|
857
|
+
STRING
|
|
710
858
|
private_constant :DEFAULT_STEPS
|
|
711
859
|
|
|
712
860
|
DEFAULT_BREAKING_CHANGE_HEADER = "BREAKING CHANGE"
|
|
@@ -750,18 +898,6 @@ module Toys
|
|
|
750
898
|
@release_complete_label = info["release_complete_label"] || DEFAULT_RELEASE_COMPLETE_LABEL
|
|
751
899
|
end
|
|
752
900
|
|
|
753
|
-
def read_commit_lint_info(info)
|
|
754
|
-
info = info["commit_lint"]
|
|
755
|
-
@commit_lint_active = !info.nil?
|
|
756
|
-
info = {} unless info.is_a?(::Hash)
|
|
757
|
-
@commit_lint_fail_checks = info["fail_checks"] ? true : false
|
|
758
|
-
@commit_lint_merge = Array(info["merge"] || ["squash", "merge", "rebase"])
|
|
759
|
-
@commit_lint_allowed_types = info["allowed_types"]
|
|
760
|
-
if @commit_lint_allowed_types
|
|
761
|
-
@commit_lint_allowed_types = Array(@commit_lint_allowed_types).map(&:downcase)
|
|
762
|
-
end
|
|
763
|
-
end
|
|
764
|
-
|
|
765
901
|
def read_commit_tag_info(info)
|
|
766
902
|
@release_commit_tags = read_commit_tag_info_set(info["release_commit_tags"] || DEFAULT_RELEASE_COMMIT_TAGS)
|
|
767
903
|
info["modify_release_commit_tags"]&.each do |tag, data|
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
require "base64"
|
|
4
4
|
require "fileutils"
|
|
5
5
|
require "json"
|
|
6
|
-
require "tmpdir"
|
|
7
6
|
require "yaml"
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
require "toys/release/artifact_dir"
|
|
9
|
+
require "toys/release/component"
|
|
10
|
+
require "toys/release/pull_request"
|
|
11
11
|
|
|
12
12
|
module Toys
|
|
13
13
|
module Release
|
|
@@ -493,8 +493,7 @@ module Toys
|
|
|
493
493
|
::FileUtils.remove_entry(dir, true)
|
|
494
494
|
::FileUtils.mkdir_p(dir)
|
|
495
495
|
else
|
|
496
|
-
dir =
|
|
497
|
-
at_exit { ::FileUtils.remove_entry(dir, true) }
|
|
496
|
+
dir = ArtifactDir.new(auto_cleanup: true).get
|
|
498
497
|
end
|
|
499
498
|
dir
|
|
500
499
|
end
|