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.
@@ -6,6 +6,6 @@ module Toys
6
6
  # Current version of the Toys release system.
7
7
  # @return [String]
8
8
  #
9
- VERSION = "0.2.2"
9
+ VERSION = "0.3.0"
10
10
  end
11
11
  end
@@ -24,7 +24,7 @@ jobs:
24
24
  with:
25
25
  ruby-version: ${{ env.ruby_version }}
26
26
  - name: Checkout repo
27
- uses: actions/checkout@v4
27
+ uses: actions/checkout@v5
28
28
  - name: Install Toys
29
29
  run: "gem install --no-document toys"
30
30
  - name: Open release pull request
@@ -14,12 +14,12 @@ module Toys
14
14
  ##
15
15
  # Create a new ChangeSet
16
16
  #
17
- # @param settings [RepoSettings] the repo settings
17
+ # @param component_settings [ComponentSettings] the component settings
18
18
  #
19
- def initialize(settings)
20
- @release_commit_tags = settings.release_commit_tags
21
- @breaking_change_header = settings.breaking_change_header
22
- @no_significant_updates_notice = settings.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
- @release_commit_tags.each_value do |tag_info|
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 = @release_commit_tags[match[:tag]]
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
- # Factory method
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 self.build(repo_settings, name, environment_utils)
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 = 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(@repo_settings)
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
- if (server_url = ::ENV["GITHUB_SERVER_URL"])
264
- if (repo = ::ENV["GITHUB_REPOSITORY"])
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.abort_pipeline("Unable to copy #{src_name} because a non-directory exists at the destination")
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), ::File.join(src_name, 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.abort_pipeline("Unable to copy #{src_name} because something already exists at the destination")
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