kettle-dev 2.1.0 → 2.2.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,8 +14,9 @@ module Kettle
14
14
  module Dev
15
15
  # PreReleaseCLI: run pre-release checks before invoking full release workflow.
16
16
  # Checks:
17
- # 1) Normalize Markdown image URLs using Addressable normalization.
18
- # 2) Validate Markdown image links resolve via HTTP(S) HEAD.
17
+ # 1) Ensure GitHub Actions workflow actions are pinned to current SHAs.
18
+ # 2) Normalize Markdown image URLs using Addressable normalization.
19
+ # 3) Validate Markdown image links resolve via HTTP(S) HEAD.
19
20
  #
20
21
  # Usage: Kettle::Dev::PreReleaseCLI.new(check_num: 1).run
21
22
  class PreReleaseCLI
@@ -151,6 +152,7 @@ module Kettle
151
152
  # @return [void]
152
153
  def run
153
154
  checks = []
155
+ checks << method(:check_github_actions_sha_pins!)
154
156
  checks << method(:check_markdown_uri_normalization!)
155
157
  checks << method(:check_markdown_images_http!)
156
158
 
@@ -166,11 +168,21 @@ module Kettle
166
168
  nil
167
169
  end
168
170
 
169
- # Check 1: Normalize Markdown image URLs
171
+ # Check 1: Ensure GitHub Actions workflow action refs are current SHA pins.
172
+ # @return [void]
173
+ def check_github_actions_sha_pins!
174
+ puts "[kettle-pre-release] Check 1: Validate GitHub Actions SHA pins"
175
+ status = Kettle::Dev::GhaShaPinsCLI.new(["--root", Dir.pwd, "--check"]).run!
176
+ return nil if status.zero?
177
+
178
+ Kettle::Dev::ExitAdapter.abort("GitHub Actions SHA pin validation failed")
179
+ end
180
+
181
+ # Check 2: Normalize Markdown image URLs
170
182
  # Compares URLs to Addressable-normalized form and rewrites Markdown when needed.
171
183
  # @return [void]
172
184
  def check_markdown_uri_normalization!
173
- puts "[kettle-pre-release] Check 1: Normalize Markdown image URLs"
185
+ puts "[kettle-pre-release] Check 2: Normalize Markdown image URLs"
174
186
  files = Dir.glob(["**/*.md", "**/*.md.example"])
175
187
  changed = []
176
188
  total_candidates = 0
@@ -216,10 +228,10 @@ module Kettle
216
228
  nil
217
229
  end
218
230
 
219
- # Check 2: Validate Markdown image links by HTTP HEAD (no rescue for parse failures)
231
+ # Check 3: Validate Markdown image links by HTTP HEAD (no rescue for parse failures)
220
232
  # @return [void]
221
233
  def check_markdown_images_http!
222
- puts "[kettle-pre-release] Check 2: Validate Markdown image links (HTTP HEAD)"
234
+ puts "[kettle-pre-release] Check 3: Validate Markdown image links (HTTP HEAD)"
223
235
  urls = [
224
236
  Markdown.extract_image_urls_from_files("**/*.md"),
225
237
  Markdown.extract_image_urls_from_files("**/*.md.example")
@@ -3,14 +3,19 @@
3
3
  require "fileutils"
4
4
 
5
5
  # rubocop:disable Rake/DuplicateTask
6
+ minitest_files = FileList["test/*test*.rb", "test/**/*test*.rb"].to_a.uniq
7
+ rspec_files = FileList["spec/*_spec.rb", "spec/**/*_spec.rb"].to_a.uniq
8
+ minitest_project = !minitest_files.empty?
9
+ rspec_project = !rspec_files.empty?
10
+
6
11
  # Set up MiniTest
7
12
  begin
8
13
  require "rake/testtask"
9
14
 
10
- unless Rake::Task.task_defined?(:test)
15
+ if minitest_project && !Rake::Task.task_defined?(:test)
11
16
  Rake::TestTask.new(:test) do |t|
12
17
  t.libs << "test"
13
- t.test_files = FileList["test/**/*test*.rb"]
18
+ t.test_files = minitest_files
14
19
  t.verbose = true
15
20
  end
16
21
  # The test task is invoked by the coverage task, so of the two, (i.e., when outside CI),
@@ -44,20 +49,23 @@ setup_spec_task = ->(default:) {
44
49
  end
45
50
  }
46
51
 
47
- # Setup RSpec
48
- if defined?(Kettle::Dev::IS_CI)
49
- if Kettle::Dev::IS_CI
50
- # then we should not have a coverage task, but do want a spec test.
51
- setup_spec_task.call(default: true)
52
+ # Setup RSpec only when this project has specs, or when no MiniTest suite exists
53
+ # and the historical kettle-test/RSpec default remains the best available task.
54
+ if rspec_project || !minitest_project
55
+ if defined?(Kettle::Dev::IS_CI)
56
+ if Kettle::Dev::IS_CI
57
+ # then we should not have a coverage task, but do want a spec test.
58
+ setup_spec_task.call(default: true)
59
+ else
60
+ # then we should have a coverage task.
61
+ # The coverage task will invoke the "test" task, which will invoke the spec task.
62
+ setup_spec_task.call(default: false)
63
+ end
52
64
  else
53
- # then we should have a coverage task.
54
- # The coverage task will invoke the "test" task, which will invoke the spec task.
55
- setup_spec_task.call(default: false)
65
+ # then we do not have a coverage task setup by this gem, and are not in a coverage context.
66
+ # So setup a spec test.
67
+ setup_spec_task.call(default: true)
56
68
  end
57
- else
58
- # then we do not have a coverage task setup by this gem, and are not in a coverage context.
59
- # So setup a spec test.
60
- setup_spec_task.call(default: true)
61
69
  end
62
70
 
63
71
  spec_registered = Kettle::Dev.default_registered?("spec")
@@ -70,14 +70,17 @@ module Kettle
70
70
 
71
71
  public
72
72
 
73
- def initialize(start_step: 1)
73
+ def initialize(start_step: 1, local_ci: false)
74
74
  @root = Kettle::Dev::CIHelpers.project_root
75
75
  @git = Kettle::Dev::GitAdapter.new
76
76
  @start_step = (start_step || 1).to_i
77
77
  @start_step = 1 if @start_step < 1
78
+ @local_ci = !!local_ci
78
79
  end
79
80
 
80
81
  def run
82
+ run_pre_release_checks! if @start_step <= 1
83
+
81
84
  # 1. Ensure Bundler version ✓
82
85
  ensure_bundler_2_7_plus!
83
86
 
@@ -206,31 +209,33 @@ module Kettle
206
209
  end
207
210
 
208
211
  # 7. optional local CI via act
209
- maybe_run_local_ci_before_push!(committed) if @start_step <= 7
212
+ maybe_run_local_ci_before_push!(committed, force: local_ci?) if @start_step <= 7
210
213
 
211
214
  # 8. ensure trunk synced
212
- if @start_step <= 8
215
+ if @start_step <= 8 && !local_ci?
213
216
  trunk = detect_trunk_branch
214
217
  feature = current_branch
215
218
  puts "Trunk branch detected: #{trunk}"
216
219
  ensure_trunk_synced_before_push!(trunk, feature)
220
+ elsif @start_step <= 8
221
+ puts "Local CI release mode: skipping remote trunk sync before publishing."
217
222
  end
218
223
 
219
224
  # 9. push branches
220
- push! if @start_step <= 9
225
+ push! if @start_step <= 9 && !local_ci?
221
226
 
222
227
  # 10. monitor CI after push
223
- monitor_workflows_after_push! if @start_step <= 10
228
+ monitor_workflows_after_push! if @start_step <= 10 && !local_ci?
224
229
 
225
230
  # 11. merge feature into trunk and push
226
- if @start_step <= 11
231
+ if @start_step <= 11 && !local_ci?
227
232
  trunk ||= detect_trunk_branch
228
233
  feature ||= current_branch
229
234
  merge_feature_into_trunk_and_push!(trunk, feature)
230
235
  end
231
236
 
232
237
  # 12. checkout trunk and pull
233
- if @start_step <= 12
238
+ if @start_step <= 12 && !local_ci?
234
239
  trunk ||= detect_trunk_branch
235
240
  checkout!(trunk)
236
241
  pull!(trunk)
@@ -265,8 +270,13 @@ module Kettle
265
270
 
266
271
  # 15. release and tag
267
272
  if @start_step <= 15
268
- puts "Running release (you may be prompted for signing key password and RubyGems MFA OTP)..."
269
- run_cmd!("bundle exec rake release")
273
+ if local_ci?
274
+ version ||= detect_version
275
+ release_gem_and_tag_locally!(version)
276
+ else
277
+ puts "Running release (you may be prompted for signing key password and RubyGems MFA OTP)..."
278
+ run_cmd!("bundle exec rake release")
279
+ end
270
280
  end
271
281
 
272
282
  # 16. generate checksums
@@ -281,7 +291,10 @@ module Kettle
281
291
  end
282
292
 
283
293
  # 17. push checksum commit (gem_checksums already commits)
284
- push! if @start_step <= 17
294
+ if @start_step <= 17
295
+ push!
296
+ push_tags! if local_ci?
297
+ end
285
298
 
286
299
  # 18. create GitHub release (optional)
287
300
  if @start_step <= 18
@@ -290,7 +303,7 @@ module Kettle
290
303
  end
291
304
 
292
305
  # 19. push tags to remotes (final step)
293
- push_tags! if @start_step <= 19
306
+ push_tags! if @start_step <= 19 && !local_ci?
294
307
 
295
308
  # Final success message
296
309
  begin
@@ -306,6 +319,15 @@ module Kettle
306
319
 
307
320
  private
308
321
 
322
+ def local_ci?
323
+ @local_ci
324
+ end
325
+
326
+ def run_pre_release_checks!
327
+ puts "Running pre-release checks via kettle-pre-release..."
328
+ Kettle::Dev::PreReleaseCLI.new(check_num: 1).run
329
+ end
330
+
309
331
  # Update the README KLOC badge number based on the denominator in the current version's COVERAGE line in CHANGELOG.md.
310
332
  # - Parses the current version section of CHANGELOG.md
311
333
  # - Finds a line matching: "- COVERAGE: ... -- <tested>/<total> lines ..."
@@ -552,17 +574,22 @@ module Kettle
552
574
  end
553
575
  end
554
576
 
555
- def maybe_run_local_ci_before_push!(committed)
577
+ def maybe_run_local_ci_before_push!(committed, force: false)
556
578
  mode = (ENV["K_RELEASE_LOCAL_CI"] || "").strip.downcase
557
- run_it = case mode
558
- when "true", "1", "yes", "y" then true
559
- when "ask"
560
- print("Run local CI with 'act' before pushing? [Y/n] ")
561
- ans = Kettle::Dev::InputAdapter.gets&.strip
562
- ans.nil? || ans.empty? || /\Ay(es)?\z/i.match?(ans)
563
- else
564
- false
565
- end
579
+ run_it =
580
+ if force
581
+ true
582
+ else
583
+ case mode
584
+ when "true", "1", "yes", "y" then true
585
+ when "ask"
586
+ print("Run local CI with 'act' before pushing? [Y/n] ")
587
+ ans = Kettle::Dev::InputAdapter.gets&.strip
588
+ ans.nil? || ans.empty? || /\Ay(es)?\z/i.match?(ans)
589
+ else
590
+ false
591
+ end
592
+ end
566
593
  return unless run_it
567
594
 
568
595
  act_ok = begin
@@ -572,6 +599,8 @@ module Kettle
572
599
  false
573
600
  end
574
601
  unless act_ok
602
+ msg = "Local CI requires 'act'. Install https://github.com/nektos/act to enable."
603
+ abort(msg) if force
575
604
  puts "Skipping local CI: 'act' command not found. Install https://github.com/nektos/act to enable."
576
605
  return
577
606
  end
@@ -594,12 +623,16 @@ module Kettle
594
623
  end
595
624
 
596
625
  unless chosen
626
+ msg = "Local CI requires at least one workflow under .github/workflows."
627
+ abort(msg) if force
597
628
  puts "Skipping local CI: no workflows found under .github/workflows."
598
629
  return
599
630
  end
600
631
 
601
632
  file_path = File.join(workflows_dir, chosen)
602
633
  unless File.file?(file_path)
634
+ msg = "Local CI selected workflow not found: #{Kettle::Dev.display_path(file_path)}"
635
+ abort(msg) if force
603
636
  puts "Skipping local CI: selected workflow not found: #{Kettle::Dev.display_path(file_path)}"
604
637
  return
605
638
  end
@@ -618,6 +651,25 @@ module Kettle
618
651
  end
619
652
  end
620
653
 
654
+ def release_gem_and_tag_locally!(version)
655
+ tag = "v#{version}"
656
+ gem_path = gem_file_for_version(version)
657
+ unless gem_path && File.file?(gem_path)
658
+ abort("Unable to locate built gem for version #{version} in pkg/. Did the build succeed?")
659
+ end
660
+
661
+ _out, tag_exists = git_output(["rev-parse", "-q", "--verify", "refs/tags/#{tag}"])
662
+ if tag_exists
663
+ puts "Local tag #{tag} already exists."
664
+ else
665
+ puts "Creating local git tag #{tag} without pushing it."
666
+ run_cmd!("git tag -a #{Shellwords.escape(tag)} -m #{Shellwords.escape(tag)}")
667
+ end
668
+
669
+ puts "Publishing #{File.basename(gem_path)} to RubyGems without pushing git refs..."
670
+ run_cmd!("gem push #{Shellwords.escape(gem_path)}")
671
+ end
672
+
621
673
  def detect_version
622
674
  Kettle::Dev::Versioning.detect_version(@root)
623
675
  end
@@ -3,7 +3,7 @@
3
3
  module Kettle
4
4
  module Dev
5
5
  module Version
6
- VERSION = "2.1.0"
6
+ VERSION = "2.2.0"
7
7
  end
8
8
  VERSION = Version::VERSION # Traditional Constant Location
9
9
  end
data/lib/kettle/dev.rb CHANGED
@@ -3,6 +3,16 @@
3
3
  require "version_gem"
4
4
  require_relative "dev/version"
5
5
 
6
+ if Gem.ruby_version < Gem::Version.new("2.7")
7
+ begin
8
+ require "backports/2.7.0/enumerable/filter_map"
9
+ rescue LoadError => error
10
+ message = "kettle-dev on Ruby <= 2.6 requires the backports gem; " \
11
+ "add `gem \"backports\", \">= 3.25\"` to your Gemfile (#{error.message})"
12
+ raise LoadError, message
13
+ end
14
+ end
15
+
6
16
  Kettle::Dev::Version.class_eval do
7
17
  extend VersionGem::Basic
8
18
  end
@@ -19,6 +29,7 @@ module Kettle
19
29
  autoload :EmojiRegex, "kettle/emoji_regex"
20
30
  module Dev
21
31
  autoload :ChangelogCLI, "kettle/dev/changelog_cli"
32
+ autoload :BumpCLI, "kettle/dev/bump_cli"
22
33
  autoload :CIHelpers, "kettle/dev/ci_helpers"
23
34
  autoload :CIMonitor, "kettle/dev/ci_monitor"
24
35
  autoload :CommitMsg, "kettle/dev/commit_msg"
@@ -27,6 +38,7 @@ module Kettle
27
38
  autoload :GemSpecReader, "kettle/dev/gem_spec_reader"
28
39
  autoload :GitAdapter, "kettle/dev/git_adapter"
29
40
  autoload :GitCommitFooter, "kettle/dev/git_commit_footer"
41
+ autoload :GhaShaPinsCLI, "kettle/dev/gha_sha_pins_cli"
30
42
  autoload :InputAdapter, "kettle/dev/input_adapter"
31
43
  autoload :ReadmeBackers, "kettle/dev/readme_backers"
32
44
  autoload :OpenCollectiveConfig, "kettle/dev/open_collective_config"
@@ -5,6 +5,7 @@ module Kettle
5
5
  def self.project_root: () -> String
6
6
  def self.repo_info: () -> [String, String]?
7
7
  def self.current_branch: () -> String?
8
+ def self.current_head_sha: () -> String?
8
9
  def self.workflows_list: (?String root) -> Array[String]
9
10
  def self.exclusions: () -> Array[String]
10
11
 
@@ -13,8 +14,10 @@ module Kettle
13
14
  repo: String,
14
15
  workflow_file: String,
15
16
  ?branch: String?,
16
- ?token: String?
17
- ) -> { "status" => String, "conclusion" => String?, "html_url" => String, "id" => Integer }?
17
+ ?token: String?,
18
+ ?require_head: bool,
19
+ ?head_sha: String?
20
+ ) -> { "status" => String, "conclusion" => String?, "html_url" => String, "id" => Integer, "head_sha" => String? }?
18
21
 
19
22
  def self.success?: ({ "status" => String, "conclusion" => String? }?) -> bool
20
23
  def self.failed?: ({ "status" => String, "conclusion" => String? }?) -> bool
@@ -9,6 +9,9 @@ module Kettle
9
9
  def self.monitor_and_prompt_for_release!: (?restart_hint: String) -> void
10
10
  def self.collect_github: () -> Array[untyped]?
11
11
  def self.collect_gitlab: () -> { status: String, url: String? }?
12
+ def self.monotonic_time: () -> Float
13
+ def self.github_start_timeout: () -> Integer
14
+ def self.github_poll_interval: () -> Float
12
15
  end
13
16
  end
14
17
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kettle-dev
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -46,7 +46,7 @@ dependencies:
46
46
  version: '2.0'
47
47
  - - ">="
48
48
  - !ruby/object:Gem::Version
49
- version: 2.0.2
49
+ version: 2.0.4
50
50
  type: :runtime
51
51
  prerelease: false
52
52
  version_requirements: !ruby/object:Gem::Requirement
@@ -56,7 +56,7 @@ dependencies:
56
56
  version: '2.0'
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
- version: 2.0.2
59
+ version: 2.0.4
60
60
  - !ruby/object:Gem::Dependency
61
61
  name: version_gem
62
62
  requirement: !ruby/object:Gem::Requirement
@@ -66,7 +66,7 @@ dependencies:
66
66
  version: '1.1'
67
67
  - - ">="
68
68
  - !ruby/object:Gem::Version
69
- version: 1.1.10
69
+ version: 1.1.11
70
70
  type: :runtime
71
71
  prerelease: false
72
72
  version_requirements: !ruby/object:Gem::Requirement
@@ -76,7 +76,7 @@ dependencies:
76
76
  version: '1.1'
77
77
  - - ">="
78
78
  - !ruby/object:Gem::Version
79
- version: 1.1.10
79
+ version: 1.1.11
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: bundler-audit
82
82
  requirement: !ruby/object:Gem::Requirement
@@ -134,7 +134,7 @@ dependencies:
134
134
  version: '3.1'
135
135
  - - ">="
136
136
  - !ruby/object:Gem::Version
137
- version: 3.1.0
137
+ version: 3.1.1
138
138
  type: :development
139
139
  prerelease: false
140
140
  version_requirements: !ruby/object:Gem::Requirement
@@ -144,7 +144,7 @@ dependencies:
144
144
  version: '3.1'
145
145
  - - ">="
146
146
  - !ruby/object:Gem::Version
147
- version: 3.1.0
147
+ version: 3.1.1
148
148
  - !ruby/object:Gem::Dependency
149
149
  name: turbo_tests2
150
150
  requirement: !ruby/object:Gem::Requirement
@@ -154,7 +154,7 @@ dependencies:
154
154
  version: '3.1'
155
155
  - - ">="
156
156
  - !ruby/object:Gem::Version
157
- version: 3.1.1
157
+ version: 3.1.2
158
158
  type: :development
159
159
  prerelease: false
160
160
  version_requirements: !ruby/object:Gem::Requirement
@@ -164,7 +164,7 @@ dependencies:
164
164
  version: '3.1'
165
165
  - - ">="
166
166
  - !ruby/object:Gem::Version
167
- version: 3.1.1
167
+ version: 3.1.2
168
168
  - !ruby/object:Gem::Dependency
169
169
  name: ruby-progressbar
170
170
  requirement: !ruby/object:Gem::Requirement
@@ -227,12 +227,14 @@ description: "\U0001F372 Kettle::Dev is a meta tool from kettle-rb to streamline
227
227
  email:
228
228
  - floss@galtzo.com
229
229
  executables:
230
+ - kettle-bump
230
231
  - kettle-changelog
231
232
  - kettle-check-eof
232
233
  - kettle-commit-msg
233
234
  - kettle-dev-setup
234
235
  - kettle-dvcs
235
236
  - kettle-gh-release
237
+ - kettle-gha-sha-pins
236
238
  - kettle-pre-release
237
239
  - kettle-readme-backers
238
240
  - kettle-release
@@ -258,6 +260,7 @@ files:
258
260
  - RUBOCOP.md
259
261
  - SECURITY.md
260
262
  - certs/pboling.pem
263
+ - exe/kettle-bump
261
264
  - exe/kettle-changelog
262
265
  - exe/kettle-check-eof
263
266
  - exe/kettle-check-eof.sh
@@ -265,11 +268,13 @@ files:
265
268
  - exe/kettle-dev-setup
266
269
  - exe/kettle-dvcs
267
270
  - exe/kettle-gh-release
271
+ - exe/kettle-gha-sha-pins
268
272
  - exe/kettle-pre-release
269
273
  - exe/kettle-readme-backers
270
274
  - exe/kettle-release
271
275
  - lib/kettle-dev.rb
272
276
  - lib/kettle/dev.rb
277
+ - lib/kettle/dev/bump_cli.rb
273
278
  - lib/kettle/dev/changelog_cli.rb
274
279
  - lib/kettle/dev/ci_helpers.rb
275
280
  - lib/kettle/dev/ci_monitor.rb
@@ -277,6 +282,7 @@ files:
277
282
  - lib/kettle/dev/dvcs_cli.rb
278
283
  - lib/kettle/dev/exit_adapter.rb
279
284
  - lib/kettle/dev/gem_spec_reader.rb
285
+ - lib/kettle/dev/gha_sha_pins_cli.rb
280
286
  - lib/kettle/dev/git_adapter.rb
281
287
  - lib/kettle/dev/git_commit_footer.rb
282
288
  - lib/kettle/dev/input_adapter.rb
@@ -332,10 +338,10 @@ licenses:
332
338
  - AGPL-3.0-only
333
339
  metadata:
334
340
  homepage_uri: https://kettle-dev.galtzo.com
335
- source_code_uri: https://github.com/kettle-rb/kettle-dev/tree/v2.1.0
336
- changelog_uri: https://github.com/kettle-rb/kettle-dev/blob/v2.1.0/CHANGELOG.md
341
+ source_code_uri: https://github.com/kettle-rb/kettle-dev/tree/v2.2.0
342
+ changelog_uri: https://github.com/kettle-rb/kettle-dev/blob/v2.2.0/CHANGELOG.md
337
343
  bug_tracker_uri: https://github.com/kettle-rb/kettle-dev/issues
338
- documentation_uri: https://www.rubydoc.info/gems/kettle-dev/2.1.0
344
+ documentation_uri: https://www.rubydoc.info/gems/kettle-dev/2.2.0
339
345
  funding_uri: https://github.com/sponsors/pboling
340
346
  wiki_uri: https://github.com/kettle-rb/kettle-dev/wiki
341
347
  news_uri: https://www.railsbling.com/tags/kettle-dev
metadata.gz.sig CHANGED
Binary file