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
data/lib/toys/release/version.rb
CHANGED
|
@@ -14,12 +14,12 @@ module Toys
|
|
|
14
14
|
##
|
|
15
15
|
# Create a new ChangeSet
|
|
16
16
|
#
|
|
17
|
-
# @param
|
|
17
|
+
# @param component_settings [ComponentSettings] the component settings
|
|
18
18
|
#
|
|
19
|
-
def initialize(
|
|
20
|
-
@
|
|
21
|
-
@breaking_change_header =
|
|
22
|
-
@no_significant_updates_notice =
|
|
19
|
+
def initialize(component_settings)
|
|
20
|
+
@commit_tags = component_settings.commit_tags
|
|
21
|
+
@breaking_change_header = component_settings.breaking_change_header
|
|
22
|
+
@no_significant_updates_notice = component_settings.no_significant_updates_notice
|
|
23
23
|
@semver = Semver::NONE
|
|
24
24
|
@change_groups = nil
|
|
25
25
|
@inputs = []
|
|
@@ -49,7 +49,7 @@ module Toys
|
|
|
49
49
|
raise "ChangeSet locked" if finished?
|
|
50
50
|
@semver = Semver::NONE
|
|
51
51
|
change_groups = {breaking: Group.new(@breaking_change_header)}
|
|
52
|
-
@
|
|
52
|
+
@commit_tags.each do |tag_info|
|
|
53
53
|
tag_info.all_headers.each { |header| change_groups[header] = Group.new(header) }
|
|
54
54
|
end
|
|
55
55
|
@inputs.each do |input|
|
|
@@ -187,7 +187,7 @@ module Toys
|
|
|
187
187
|
when /^revert-commit$/i
|
|
188
188
|
@inputs.delete_if { |elem| elem.sha.start_with?(match[:content].split.first) }
|
|
189
189
|
else
|
|
190
|
-
tag_info = @
|
|
190
|
+
tag_info = @commit_tags.find { |tag| tag.tag == match[:tag] }
|
|
191
191
|
input.apply_commit(tag_info, match[:scope], match[:bang], match[:content])
|
|
192
192
|
end
|
|
193
193
|
end
|
|
@@ -11,25 +11,15 @@ module Toys
|
|
|
11
11
|
#
|
|
12
12
|
class Component
|
|
13
13
|
##
|
|
14
|
-
#
|
|
14
|
+
# Constructor
|
|
15
15
|
#
|
|
16
16
|
# @param repo_settings [Toys::Release::RepoSettings] the repo settings
|
|
17
17
|
# @param name [String] The component name
|
|
18
18
|
# @param environment_utils [Toys::Release::EnvironmentUtils] env utils
|
|
19
19
|
#
|
|
20
|
-
def
|
|
21
|
-
settings = repo_settings.component_settings(name)
|
|
22
|
-
if settings.type == "gem"
|
|
23
|
-
GemComponent.new(repo_settings, settings, environment_utils)
|
|
24
|
-
else
|
|
25
|
-
Component.new(repo_settings, settings, environment_utils)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# @private
|
|
30
|
-
def initialize(repo_settings, settings, environment_utils)
|
|
20
|
+
def initialize(repo_settings, name, environment_utils)
|
|
31
21
|
@repo_settings = repo_settings
|
|
32
|
-
@settings =
|
|
22
|
+
@settings = repo_settings.component_settings(name)
|
|
33
23
|
@utils = environment_utils
|
|
34
24
|
@changelog_file = ChangelogFile.new(changelog_path(from: :absolute), @utils)
|
|
35
25
|
@version_rb_file = VersionRbFile.new(version_rb_path(from: :absolute), @utils, @settings.version_constant)
|
|
@@ -267,7 +257,7 @@ module Toys
|
|
|
267
257
|
to ||= "HEAD"
|
|
268
258
|
from = latest_tag(ref: to) if from == :default
|
|
269
259
|
commits = from ? "#{from}..#{to}" : to
|
|
270
|
-
changeset = ChangeSet.new(@
|
|
260
|
+
changeset = ChangeSet.new(@settings)
|
|
271
261
|
shas = @utils.capture(["git", "log", commits, "--format=%H"], e: true).split("\n")
|
|
272
262
|
shas.reverse_each do |sha|
|
|
273
263
|
message = touched_message(sha)
|
|
@@ -279,12 +269,14 @@ module Toys
|
|
|
279
269
|
##
|
|
280
270
|
# Run bundler
|
|
281
271
|
#
|
|
272
|
+
# @return [Toys::Utils::Exec::Result]
|
|
273
|
+
#
|
|
282
274
|
def bundle
|
|
283
275
|
cd do
|
|
284
276
|
exec_result = @utils.exec(["bundle", "install"])
|
|
285
277
|
@utils.error("Bundle install failed for #{name}.") unless exec_result.success?
|
|
278
|
+
exec_result
|
|
286
279
|
end
|
|
287
|
-
self
|
|
288
280
|
end
|
|
289
281
|
|
|
290
282
|
##
|
|
@@ -331,58 +323,5 @@ module Toys
|
|
|
331
323
|
name.hash
|
|
332
324
|
end
|
|
333
325
|
end
|
|
334
|
-
|
|
335
|
-
##
|
|
336
|
-
# Subclass for Gem components
|
|
337
|
-
#
|
|
338
|
-
class GemComponent < Component
|
|
339
|
-
##
|
|
340
|
-
# Returns the path to the gemspec. It can be returned as a relative path
|
|
341
|
-
# from the component directory, a relative path from the repo root
|
|
342
|
-
# directory, or an absolute path.
|
|
343
|
-
#
|
|
344
|
-
# @param from [:directory,:repo_root,:absolute] From where (defaults to
|
|
345
|
-
# `:directory`)
|
|
346
|
-
# @return [String] The path to the gemspec file
|
|
347
|
-
#
|
|
348
|
-
def gemspec_path(from: :directory)
|
|
349
|
-
file_path("#{name}.gemspec", from: from)
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
##
|
|
353
|
-
# Validates the component and reports any errors.
|
|
354
|
-
# Includes both errors from the base class and gem-specific errors.
|
|
355
|
-
#
|
|
356
|
-
def validate
|
|
357
|
-
super do
|
|
358
|
-
path = gemspec_path(from: :absolute)
|
|
359
|
-
@utils.error("Missing gemspec #{path} for #{name}") unless ::File.file?(path)
|
|
360
|
-
end
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
##
|
|
364
|
-
# Return a list of released versions
|
|
365
|
-
#
|
|
366
|
-
# @return [Array<::Gem::Version>]
|
|
367
|
-
#
|
|
368
|
-
def released_versions
|
|
369
|
-
content = @utils.capture(["gem", "info", "-r", "-a", name], e: true)
|
|
370
|
-
match = /#{name} \(([\w., ]+)\)/.match(content)
|
|
371
|
-
return [] unless match
|
|
372
|
-
match[1].split(/,\s+/).map { |str| ::Gem::Version.new(str) }
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
##
|
|
376
|
-
# Determines if a version has been released
|
|
377
|
-
#
|
|
378
|
-
# @param version [::Gem::Version,String] The version to check
|
|
379
|
-
# @return [boolean] Whether the version has been released
|
|
380
|
-
#
|
|
381
|
-
def version_released?(version)
|
|
382
|
-
cmd = ["gem", "search", name, "--exact", "--remote", "--version", version.to_s]
|
|
383
|
-
content = @utils.capture(cmd)
|
|
384
|
-
content.include?("#{name} (#{version})")
|
|
385
|
-
end
|
|
386
|
-
end
|
|
387
326
|
end
|
|
388
327
|
end
|
|
@@ -241,6 +241,19 @@ module Toys
|
|
|
241
241
|
tool_context.exec_separate_tool(cmd, **opts, &block)
|
|
242
242
|
end
|
|
243
243
|
|
|
244
|
+
##
|
|
245
|
+
# Returns the URL for the current GitHub Actions workflow run.
|
|
246
|
+
#
|
|
247
|
+
# @return [String] Current workflow run URL
|
|
248
|
+
# @return [nil] If not called from within a workflow run
|
|
249
|
+
#
|
|
250
|
+
def current_workflow_run_url
|
|
251
|
+
return nil unless (server_url = ::ENV["GITHUB_SERVER_URL"])
|
|
252
|
+
return nil unless (repo = ::ENV["GITHUB_REPOSITORY"])
|
|
253
|
+
return nil unless (run_id = ::ENV["GITHUB_RUN_ID"])
|
|
254
|
+
"#{server_url}/#{repo}/actions/runs/#{run_id}"
|
|
255
|
+
end
|
|
256
|
+
|
|
244
257
|
private
|
|
245
258
|
|
|
246
259
|
def modify_exec_opts(opts, cmd)
|
|
@@ -260,13 +260,8 @@ module Toys
|
|
|
260
260
|
else
|
|
261
261
|
"* **All releases completed successfully.**"
|
|
262
262
|
end
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (run_id = ::ENV["GITHUB_RUN_ID"])
|
|
266
|
-
lines << "* Run logs: #{server_url}/#{repo}/actions/runs/#{run_id}"
|
|
267
|
-
end
|
|
268
|
-
end
|
|
269
|
-
end
|
|
263
|
+
url = @utils.current_workflow_run_url
|
|
264
|
+
lines << "* Run logs: #{url}" if url
|
|
270
265
|
lines
|
|
271
266
|
end
|
|
272
267
|
|
|
@@ -236,9 +236,13 @@ module Toys
|
|
|
236
236
|
# @param source_step [String] Name of the source step
|
|
237
237
|
# @param source_path [String] Path to the file or directory to copy
|
|
238
238
|
# @param dest [:component,:repo_root,:temp,:output] Symbolic destination
|
|
239
|
-
# @param dest_path [String] Path in the destination
|
|
239
|
+
# @param dest_path [String,nil] Path in the destination, if different
|
|
240
|
+
# from the source
|
|
241
|
+
# @param collisions [:error,:replace,:keep] What to do if a collision
|
|
242
|
+
# occurs
|
|
240
243
|
#
|
|
241
|
-
def copy_from_input(source_step, source_path: nil, dest: :component, dest_path: nil)
|
|
244
|
+
def copy_from_input(source_step, source_path: nil, dest: :component, dest_path: nil, collisions: nil)
|
|
245
|
+
collisions ||= "error"
|
|
242
246
|
source_dir = output_dir(source_step)
|
|
243
247
|
source_path ||= "."
|
|
244
248
|
dest_path ||= source_path
|
|
@@ -258,16 +262,21 @@ module Toys
|
|
|
258
262
|
source = ::File.expand_path(source_path, source_dir)
|
|
259
263
|
dest = ::File.expand_path(dest_path, dest_dir)
|
|
260
264
|
utils.log("Copying #{source_path.inspect} from step #{source_step.inspect}")
|
|
261
|
-
@pipeline.copy_tree(self, source, dest, source_path)
|
|
265
|
+
@pipeline.copy_tree(self, source, dest, source_path, collisions.to_s)
|
|
262
266
|
end
|
|
263
267
|
|
|
264
268
|
##
|
|
265
269
|
# Copy the given item to the output directory
|
|
266
270
|
#
|
|
267
|
-
# @param source_path [String] Path to the file or directory to copy
|
|
268
271
|
# @param source [:component,:repo_root,:temp] Symbolic source
|
|
272
|
+
# @param source_path [String] Path to the file or directory to copy
|
|
273
|
+
# @param dest_path [String,nil] Path in the destination, if different
|
|
274
|
+
# from the source
|
|
275
|
+
# @param collisions [:error,:replace,:keep] What to do if a collision
|
|
276
|
+
# occurs
|
|
269
277
|
#
|
|
270
|
-
def copy_to_output(source: :component, source_path: nil, dest_path: nil)
|
|
278
|
+
def copy_to_output(source: :component, source_path: nil, dest_path: nil, collisions: nil)
|
|
279
|
+
collisions ||= "error"
|
|
271
280
|
source_path ||= "."
|
|
272
281
|
dest_path ||= source_path
|
|
273
282
|
source_dir =
|
|
@@ -284,7 +293,7 @@ module Toys
|
|
|
284
293
|
source = ::File.expand_path(source_path, source_dir)
|
|
285
294
|
dest = ::File.expand_path(dest_path, output_dir)
|
|
286
295
|
utils.log("Copying #{source_path.inspect} to output")
|
|
287
|
-
@pipeline.copy_tree(self, source, dest, source_path)
|
|
296
|
+
@pipeline.copy_tree(self, source, dest, source_path, collisions.to_s)
|
|
288
297
|
end
|
|
289
298
|
|
|
290
299
|
# ---- called internally from the pipeline ----
|
|
@@ -367,6 +376,7 @@ module Toys
|
|
|
367
376
|
mark_step_index(index)
|
|
368
377
|
end
|
|
369
378
|
end
|
|
379
|
+
self
|
|
370
380
|
end
|
|
371
381
|
|
|
372
382
|
##
|
|
@@ -393,23 +403,25 @@ module Toys
|
|
|
393
403
|
return nil
|
|
394
404
|
end
|
|
395
405
|
end
|
|
406
|
+
self
|
|
396
407
|
end
|
|
397
408
|
|
|
398
409
|
##
|
|
399
410
|
# @private
|
|
400
411
|
#
|
|
401
|
-
def copy_tree(step, src, dest, src_name)
|
|
412
|
+
def copy_tree(step, src, dest, src_name, collisions)
|
|
402
413
|
if ::File.directory?(src)
|
|
403
414
|
if ::File.exist?(dest) && !::File.directory?(dest)
|
|
404
|
-
step
|
|
415
|
+
return if handle_copy_collision(step, collisions, dest, src_name) == :keep
|
|
405
416
|
end
|
|
406
417
|
::FileUtils.mkdir_p(dest)
|
|
407
418
|
::Dir.children(src).each do |child|
|
|
408
|
-
copy_tree(step, ::File.join(src, child), ::File.join(dest, child),
|
|
419
|
+
copy_tree(step, ::File.join(src, child), ::File.join(dest, child),
|
|
420
|
+
::File.join(src_name, child), collisions)
|
|
409
421
|
end
|
|
410
422
|
elsif ::File.exist?(src)
|
|
411
423
|
if ::File.exist?(dest)
|
|
412
|
-
step
|
|
424
|
+
return if handle_copy_collision(step, collisions, dest, src_name) == :keep
|
|
413
425
|
end
|
|
414
426
|
::FileUtils.copy_entry(src, dest)
|
|
415
427
|
else
|
|
@@ -514,7 +526,7 @@ module Toys
|
|
|
514
526
|
end
|
|
515
527
|
dest = ::File.expand_path(dest_path, dest_dir)
|
|
516
528
|
@utils.log("Copying #{source_path.inspect} from step #{input.step_name.inspect}")
|
|
517
|
-
copy_tree(step, source, dest, source_path)
|
|
529
|
+
copy_tree(step, source, dest, source_path, input.collisions)
|
|
518
530
|
end
|
|
519
531
|
end
|
|
520
532
|
|
|
@@ -540,7 +552,24 @@ module Toys
|
|
|
540
552
|
source = ::File.expand_path(source_path, source_dir)
|
|
541
553
|
dest = ::File.expand_path(dest_path, step.output_dir)
|
|
542
554
|
@utils.log("Copying #{source_path.inspect} to output")
|
|
543
|
-
copy_tree(step, source, dest, source_path)
|
|
555
|
+
copy_tree(step, source, dest, source_path, output.collisions)
|
|
556
|
+
end
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
##
|
|
560
|
+
# @private
|
|
561
|
+
# Handle a collision during copy_tree.
|
|
562
|
+
# Returns :keep or :replace
|
|
563
|
+
#
|
|
564
|
+
def handle_copy_collision(step, collisions, dest, src_name)
|
|
565
|
+
case collisions
|
|
566
|
+
when "keep"
|
|
567
|
+
:keep
|
|
568
|
+
when "replace"
|
|
569
|
+
::FileUtils.remove_entry(dest)
|
|
570
|
+
:replace
|
|
571
|
+
else
|
|
572
|
+
step.abort_pipeline("Unable to copy #{src_name} because it already exists at the destination")
|
|
544
573
|
end
|
|
545
574
|
end
|
|
546
575
|
end
|