kettle-dev 1.1.11 → 1.1.13
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
- checksums.yaml.gz.sig +0 -0
- data/.envrc +1 -1
- data/.rubocop_rspec.yml +30 -0
- data/Appraisals +1 -1
- data/CHANGELOG.md +23 -1
- data/README.md +1 -1
- data/README.md.example +1 -1
- data/Rakefile.example +1 -1
- data/gemfiles/modular/injected.gemfile +1 -1
- data/lib/kettle/dev/changelog_cli.rb +5 -0
- data/lib/kettle/dev/ci_helpers.rb +11 -0
- data/lib/kettle/dev/ci_monitor.rb +8 -2
- data/lib/kettle/dev/commit_msg.rb +1 -0
- data/lib/kettle/dev/dvcs_cli.rb +10 -1
- data/lib/kettle/dev/git_adapter.rb +1 -0
- data/lib/kettle/dev/git_commit_footer.rb +1 -0
- data/lib/kettle/dev/input_adapter.rb +1 -0
- data/lib/kettle/dev/modular_gemfiles.rb +110 -0
- data/lib/kettle/dev/pre_release_cli.rb +2 -0
- data/lib/kettle/dev/readme_backers.rb +14 -0
- data/lib/kettle/dev/release_cli.rb +19 -0
- data/lib/kettle/dev/setup_cli.rb +162 -1
- data/lib/kettle/dev/tasks/install_task.rb +31 -18
- data/lib/kettle/dev/tasks/template_task.rb +16 -85
- data/lib/kettle/dev/template_helpers.rb +4 -0
- data/lib/kettle/dev/version.rb +1 -1
- data/lib/kettle/dev/versioning.rb +2 -0
- data/lib/kettle/dev.rb +1 -0
- data/sig/kettle/dev/modular_gemfiles.rbs +12 -0
- data.tar.gz.sig +0 -0
- metadata +8 -5
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b2236329716c5ee01b5cf04dd99ee6b0595a7fa95a76c8ab959c04130aaada7
|
4
|
+
data.tar.gz: 9ea676b1fd79e9247c56bf3bb5c584870e572ed6ed462ba399227c3e03047ea5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2d83150965bedec8642543e4c818447203c0c4d5de60d41a41b2bf0672995a1e80c8501c463d448746e9d76622553df389a6fecf1a1313358c1c203c2ea29a0
|
7
|
+
data.tar.gz: ba80dea5b32c6ae569e63ef2f5134683d6867c0857ed988c2bfe243076b02728c9ca12a5ae874cd94459794af63163ef215753243256c7d6157d91251f19e8ca
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/.envrc
CHANGED
@@ -21,7 +21,7 @@ export K_SOUP_COV_DO=true # Means you want code coverage
|
|
21
21
|
export K_SOUP_COV_COMMAND_NAME="Test Coverage"
|
22
22
|
# Available formats are html, xml, rcov, lcov, json, tty
|
23
23
|
export K_SOUP_COV_FORMATTERS="html,xml,rcov,lcov,json,tty"
|
24
|
-
export K_SOUP_COV_MIN_BRANCH=
|
24
|
+
export K_SOUP_COV_MIN_BRANCH=80 # Means you want to enforce X% branch coverage
|
25
25
|
export K_SOUP_COV_MIN_LINE=96 # Means you want to enforce X% line coverage
|
26
26
|
export K_SOUP_COV_MIN_HARD=true # Means you want the build to fail if the coverage thresholds are not met
|
27
27
|
export K_SOUP_COV_MULTI_FORMATTERS=true
|
data/.rubocop_rspec.yml
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
RSpec/MultipleExpectations:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
RSpec/NamedSubject:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
RSpec/ExampleLength:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
RSpec/VerifiedDoubles:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
RSpec/MessageSpies:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
RSpec/InstanceVariable:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
RSpec/NestedGroups:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
RSpec/ExpectInHook:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
RSpec/DescribeClass:
|
26
|
+
Exclude:
|
27
|
+
- 'spec/examples/*'
|
28
|
+
|
29
|
+
RSpec/MultipleMemoizedHelpers:
|
30
|
+
Enabled: false
|
data/Appraisals
CHANGED
@@ -143,7 +143,7 @@ end
|
|
143
143
|
# Only run linter on the latest version of Ruby (but, in support of oldest supported Ruby version)
|
144
144
|
appraise "style" do
|
145
145
|
eval_gemfile "modular/style.gemfile"
|
146
|
-
eval_gemfile "modular/x_std_libs
|
146
|
+
eval_gemfile "modular/x_std_libs.gemfile"
|
147
147
|
# Dependencies injected by the kettle-dev-setup script & kettle:dev:install rake task
|
148
148
|
# eval_gemfile "modular/injected.gemfile"
|
149
149
|
end
|
data/CHANGELOG.md
CHANGED
@@ -24,6 +24,24 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
24
24
|
### Fixed
|
25
25
|
### Security
|
26
26
|
|
27
|
+
## [1.1.13] - 2025-09-09
|
28
|
+
- TAG: [v1.1.13][1.1.13t]
|
29
|
+
- COVERAGE: 96.29% -- 3479/3613 lines in 25 files
|
30
|
+
- BRANCH COVERAGE: 80.96% -- 1424/1759 branches in 25 files
|
31
|
+
- 76.88% documented
|
32
|
+
### Fixed
|
33
|
+
- include .rubocop_rspec.yml during install / template task's file copy
|
34
|
+
- kettle-dev-setup now honors `--force` option
|
35
|
+
|
36
|
+
## [1.1.12] - 2025-09-09
|
37
|
+
- TAG: [v1.1.12][1.1.12t]
|
38
|
+
- COVERAGE: 94.84% -- 3422/3608 lines in 25 files
|
39
|
+
- BRANCH COVERAGE: 78.97% -- 1386/1755 branches in 25 files
|
40
|
+
- 76.88% documented
|
41
|
+
### Changed
|
42
|
+
- improve Gemfile updates during kettle-dev-setup
|
43
|
+
- git origin-based funding_org derivation during setup
|
44
|
+
|
27
45
|
## [1.1.11] - 2025-09-08
|
28
46
|
- TAG: [v1.1.11][1.1.11t]
|
29
47
|
- COVERAGE: 96.56% -- 3396/3517 lines in 24 files
|
@@ -581,7 +599,11 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
581
599
|
- Selecting will run the selected workflow via `act`
|
582
600
|
- This may move to its own gem in the future.
|
583
601
|
|
584
|
-
[Unreleased]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.
|
602
|
+
[Unreleased]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.13...HEAD
|
603
|
+
[1.1.13]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.12...v1.1.13
|
604
|
+
[1.1.13t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.1.13
|
605
|
+
[1.1.12]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.11...v1.1.12
|
606
|
+
[1.1.12t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.1.12
|
585
607
|
[1.1.11]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.10...v1.1.11
|
586
608
|
[1.1.11t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.1.11
|
587
609
|
[1.1.10]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.9...v1.1.10
|
data/README.md
CHANGED
@@ -907,7 +907,7 @@ Thanks for RTFM. ☺️
|
|
907
907
|
[📌gitmoji]:https://gitmoji.dev
|
908
908
|
[📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
909
909
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
910
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-3.
|
910
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-3.613-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
911
911
|
[🔐security]: SECURITY.md
|
912
912
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
913
913
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
data/README.md.example
CHANGED
@@ -507,7 +507,7 @@ Thanks for RTFM. ☺️
|
|
507
507
|
[📌gitmoji]:https://gitmoji.dev
|
508
508
|
[📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
509
509
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
510
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-3.
|
510
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-3.613-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
511
511
|
[🔐security]: SECURITY.md
|
512
512
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
513
513
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
data/Rakefile.example
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# visibility and discoverability on RubyGems.org.
|
3
3
|
# However, this gem sits underneath all my other gems, and also "depends on" many of them.
|
4
4
|
# So instead of depending on them directly it injects them into the other gem's gemspec on install.
|
5
|
-
# This gem its injected dev dependencies, will install on Ruby down to 2.3.x.
|
5
|
+
# This gem, and its injected dev dependencies, will install on Ruby down to 2.3.x.
|
6
6
|
# This gem does not inject runtime dependencies.
|
7
7
|
# Thus, dev dependencies injected into gemspecs must have
|
8
8
|
#
|
@@ -106,6 +106,7 @@ module Kettle
|
|
106
106
|
content = File.read(path)
|
107
107
|
m = content.match(/VERSION\s*=\s*(["'])([^"']+)\1/)
|
108
108
|
next unless m
|
109
|
+
|
109
110
|
m[2]
|
110
111
|
end.compact
|
111
112
|
abort("VERSION constant not found in #{@root}/lib/**/version.rb") if versions.none?
|
@@ -117,6 +118,7 @@ module Kettle
|
|
117
118
|
lines = content.lines
|
118
119
|
start_i = lines.index { |l| l.start_with?("## [Unreleased]") }
|
119
120
|
return [nil, nil, nil] unless start_i
|
121
|
+
|
120
122
|
# Find the next version heading after Unreleased
|
121
123
|
next_i = (start_i + 1)
|
122
124
|
while next_i < lines.length && !lines[next_i].start_with?("## [")
|
@@ -133,6 +135,7 @@ module Kettle
|
|
133
135
|
# after_text begins with the first released section following Unreleased
|
134
136
|
m = after_text.match(/^## \[(\d+\.\d+\.\d+)\]/)
|
135
137
|
return m[1] if m
|
138
|
+
|
136
139
|
nil
|
137
140
|
end
|
138
141
|
|
@@ -199,8 +202,10 @@ module Kettle
|
|
199
202
|
branches = h["branches"] || []
|
200
203
|
branches.each do |b|
|
201
204
|
next unless b.is_a?(Hash)
|
205
|
+
|
202
206
|
cov = b["coverage"]
|
203
207
|
next unless cov.is_a?(Numeric)
|
208
|
+
|
204
209
|
total_branches += 1
|
205
210
|
covered_branches += 1 if cov > 0
|
206
211
|
end
|
@@ -35,6 +35,7 @@ module Kettle
|
|
35
35
|
def repo_info
|
36
36
|
out, status = Open3.capture2("git", "config", "--get", "remote.origin.url")
|
37
37
|
return unless status.success?
|
38
|
+
|
38
39
|
url = out.strip
|
39
40
|
if url =~ %r{git@github.com:(.+?)/(.+?)(\.git)?$}
|
40
41
|
[Regexp.last_match(1), Regexp.last_match(2).sub(/\.git\z/, "")]
|
@@ -88,8 +89,10 @@ module Kettle
|
|
88
89
|
# @return [Hash{String=>String,Integer}, nil] minimal run info or nil on error/none
|
89
90
|
def latest_run(owner:, repo:, workflow_file:, branch: nil, token: default_token)
|
90
91
|
return unless owner && repo
|
92
|
+
|
91
93
|
b = branch || current_branch
|
92
94
|
return unless b
|
95
|
+
|
93
96
|
# Scope to the exact commit SHA when available to avoid picking up a previous run on the same branch.
|
94
97
|
sha_out, status = Open3.capture2("git", "rev-parse", "HEAD")
|
95
98
|
sha = status.success? ? sha_out.strip : nil
|
@@ -100,6 +103,7 @@ module Kettle
|
|
100
103
|
req["Authorization"] = "token #{token}" if token && !token.empty?
|
101
104
|
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
|
102
105
|
return unless res.is_a?(Net::HTTPSuccess)
|
106
|
+
|
103
107
|
data = JSON.parse(res.body)
|
104
108
|
runs = Array(data["workflow_runs"]) || []
|
105
109
|
# Try to match by head_sha first; fall back to first run (branch-scoped) if none matches yet.
|
@@ -109,6 +113,7 @@ module Kettle
|
|
109
113
|
runs.first
|
110
114
|
end
|
111
115
|
return unless run
|
116
|
+
|
112
117
|
{
|
113
118
|
"status" => run["status"],
|
114
119
|
"conclusion" => run["conclusion"],
|
@@ -154,6 +159,7 @@ module Kettle
|
|
154
159
|
def repo_info_gitlab
|
155
160
|
url = origin_url
|
156
161
|
return unless url
|
162
|
+
|
157
163
|
if url =~ %r{git@gitlab.com:(.+?)/(.+?)(\.git)?$}
|
158
164
|
[Regexp.last_match(1), Regexp.last_match(2).sub(/\.git\z/, "")]
|
159
165
|
elsif url =~ %r{https://gitlab.com/(.+?)/(.+?)(\.git)?$}
|
@@ -176,8 +182,10 @@ module Kettle
|
|
176
182
|
# @return [Hash{String=>String,Integer}, nil]
|
177
183
|
def gitlab_latest_pipeline(owner:, repo:, branch: nil, host: "gitlab.com", token: default_gitlab_token)
|
178
184
|
return unless owner && repo
|
185
|
+
|
179
186
|
b = branch || current_branch
|
180
187
|
return unless b
|
188
|
+
|
181
189
|
project = URI.encode_www_form_component("#{owner}/#{repo}")
|
182
190
|
uri = URI("https://#{host}/api/v4/projects/#{project}/pipelines?ref=#{URI.encode_www_form_component(b)}&per_page=1")
|
183
191
|
req = Net::HTTP::Get.new(uri)
|
@@ -185,10 +193,13 @@ module Kettle
|
|
185
193
|
req["PRIVATE-TOKEN"] = token if token && !token.empty?
|
186
194
|
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
|
187
195
|
return unless res.is_a?(Net::HTTPSuccess)
|
196
|
+
|
188
197
|
data = JSON.parse(res.body)
|
189
198
|
return unless data.is_a?(Array)
|
199
|
+
|
190
200
|
pipe = data.first
|
191
201
|
return unless pipe.is_a?(Hash)
|
202
|
+
|
192
203
|
# Attempt to enrich with failure_reason by querying the single pipeline endpoint
|
193
204
|
begin
|
194
205
|
if pipe["id"]
|
@@ -36,6 +36,7 @@ module Kettle
|
|
36
36
|
# Some APIs report only a final state string like "success"/"failed"
|
37
37
|
return "✅" if conclusion.to_s == "success" || status.to_s == "success"
|
38
38
|
return "🍅" if conclusion.to_s == "failure" || status.to_s == "failed"
|
39
|
+
|
39
40
|
"⏳️"
|
40
41
|
end
|
41
42
|
end
|
@@ -100,7 +101,7 @@ module Kettle
|
|
100
101
|
emoji = status_emoji(it[:status], it[:conclusion])
|
101
102
|
details = [it[:status], it[:conclusion]].compact.join("/")
|
102
103
|
wf = it[:workflow]
|
103
|
-
puts " - #{wf}: #{emoji} (#{details}) #{
|
104
|
+
puts " - #{wf}: #{emoji} (#{details}) #{"-> #{it[:url]}" if it[:url]}"
|
104
105
|
all_ok &&= (it[:conclusion] == "success")
|
105
106
|
end
|
106
107
|
end
|
@@ -113,7 +114,7 @@ module Kettle
|
|
113
114
|
end
|
114
115
|
emoji = status_emoji(gl[:status], status)
|
115
116
|
details = gl[:status].to_s
|
116
|
-
puts "GitLab Pipeline: #{emoji} (#{details}) #{
|
117
|
+
puts "GitLab Pipeline: #{emoji} (#{details}) #{"-> #{gl[:url]}" if gl[:url]}"
|
117
118
|
all_ok &&= (gl[:status] != "failed")
|
118
119
|
end
|
119
120
|
all_ok
|
@@ -209,6 +210,7 @@ module Kettle
|
|
209
210
|
end
|
210
211
|
end
|
211
212
|
break if results.size == total
|
213
|
+
|
212
214
|
idx = (idx + 1) % total
|
213
215
|
sleep(1)
|
214
216
|
end
|
@@ -313,6 +315,7 @@ module Kettle
|
|
313
315
|
end
|
314
316
|
end
|
315
317
|
break if passed.size == total
|
318
|
+
|
316
319
|
idx = (idx + 1) % total
|
317
320
|
sleep(1)
|
318
321
|
end
|
@@ -395,15 +398,18 @@ module Kettle
|
|
395
398
|
def preferred_github_remote
|
396
399
|
cands = github_remote_candidates
|
397
400
|
return if cands.empty?
|
401
|
+
|
398
402
|
explicit = cands.find { |n| n == "github" } || cands.find { |n| n == "gh" }
|
399
403
|
return explicit if explicit
|
400
404
|
return "origin" if cands.include?("origin")
|
405
|
+
|
401
406
|
cands.first
|
402
407
|
end
|
403
408
|
module_function :preferred_github_remote
|
404
409
|
|
405
410
|
def parse_github_owner_repo(url)
|
406
411
|
return [nil, nil] unless url
|
412
|
+
|
407
413
|
if url =~ %r{git@github.com:(.+?)/(.+?)(\.git)?$}
|
408
414
|
[Regexp.last_match(1), Regexp.last_match(2).sub(/\.git\z/, "")]
|
409
415
|
elsif url =~ %r{https://github.com/(.+?)/(.+?)(\.git)?$}
|
@@ -18,6 +18,7 @@ module Kettle
|
|
18
18
|
validate = ENV.fetch("GIT_HOOK_BRANCH_VALIDATE", "false")
|
19
19
|
branch_rule_type = (!validate.casecmp("false").zero? && validate) || nil
|
20
20
|
return unless branch_rule_type
|
21
|
+
|
21
22
|
branch_rule = BRANCH_RULES[branch_rule_type]
|
22
23
|
return unless branch_rule
|
23
24
|
|
data/lib/kettle/dev/dvcs_cli.rb
CHANGED
@@ -69,6 +69,7 @@ module Kettle
|
|
69
69
|
%i[github gitlab codeberg].each do |forge|
|
70
70
|
r = names[forge]
|
71
71
|
next unless r && r != names[:origin]
|
72
|
+
|
72
73
|
git.fetch(r)
|
73
74
|
end
|
74
75
|
show_status!(git, names, branch)
|
@@ -106,8 +107,10 @@ module Kettle
|
|
106
107
|
def detect_default_branch!(git)
|
107
108
|
_out, ok = git.capture(["rev-parse", "--verify", "origin/main"])
|
108
109
|
return "main" if ok
|
110
|
+
|
109
111
|
_out2, ok2 = git.capture(["rev-parse", "--verify", "origin/master"])
|
110
112
|
return "master" if ok2
|
113
|
+
|
111
114
|
# Default to main if neither verifies
|
112
115
|
"main"
|
113
116
|
end
|
@@ -125,6 +128,7 @@ module Kettle
|
|
125
128
|
next unless remote
|
126
129
|
next if remote == names[:origin]
|
127
130
|
next unless existing.include?(remote)
|
131
|
+
|
128
132
|
ref = "#{remote}/#{branch}"
|
129
133
|
out, ok = git.capture(["rev-list", "--left-right", "--count", "#{base}...#{ref}"])
|
130
134
|
if ok && !out.to_s.strip.empty?
|
@@ -232,6 +236,7 @@ module Kettle
|
|
232
236
|
if org && repo
|
233
237
|
return [org, repo]
|
234
238
|
end
|
239
|
+
|
235
240
|
# Try to infer from any existing remote url
|
236
241
|
urls = git.remotes_with_urls
|
237
242
|
sample = urls["origin"] || urls.values.first
|
@@ -252,7 +257,8 @@ module Kettle
|
|
252
257
|
|
253
258
|
def prompt(label, default: nil)
|
254
259
|
return default if @opts[:force]
|
255
|
-
|
260
|
+
|
261
|
+
print("#{label}#{" [#{default}]" if default}: ")
|
256
262
|
ans = $stdin.gets&.strip
|
257
263
|
ans = nil if ans == ""
|
258
264
|
ans || default || abort!("#{label} is required")
|
@@ -341,6 +347,7 @@ module Kettle
|
|
341
347
|
codeberg: names[:codeberg],
|
342
348
|
}.each do |forge, remote_name|
|
343
349
|
next unless remote_name
|
350
|
+
|
344
351
|
ok = git.fetch(remote_name)
|
345
352
|
results[forge] = !!ok
|
346
353
|
say("Fetched from #{forge} (remote: #{remote_name}) => #{ok ? "OK" : "FAILED"}")
|
@@ -352,6 +359,7 @@ module Kettle
|
|
352
359
|
def update_readme_federation_status!(org, repo, results)
|
353
360
|
readme_path = File.join(Dir.pwd, "README.md")
|
354
361
|
return unless File.exist?(readme_path)
|
362
|
+
|
355
363
|
content = File.read(readme_path)
|
356
364
|
# Determine if all succeeded
|
357
365
|
forges = [:github, :gitlab, :codeberg]
|
@@ -376,6 +384,7 @@ module Kettle
|
|
376
384
|
say("\nSome forges are not yet available. Use these import links to create mirrors:")
|
377
385
|
[:github, :gitlab, :codeberg].each do |forge|
|
378
386
|
next if results[forge]
|
387
|
+
|
379
388
|
say(" - #{forge.capitalize} import: #{FORGE_MIGRATION_TOOLS[forge]}")
|
380
389
|
end
|
381
390
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kettle
|
4
|
+
module Dev
|
5
|
+
# Utilities for copying modular Gemfiles and related directories
|
6
|
+
# in a DRY fashion. Used by both the template rake task and the
|
7
|
+
# setup CLI to ensure gemfiles/modular/* are present before use.
|
8
|
+
module ModularGemfiles
|
9
|
+
MODULAR_GEMFILE_DIR = "gemfiles/modular"
|
10
|
+
|
11
|
+
module_function
|
12
|
+
|
13
|
+
# Copy the modular gemfiles and nested directories from the gem
|
14
|
+
# checkout into the target project, prompting where appropriate
|
15
|
+
# via the provided helpers.
|
16
|
+
#
|
17
|
+
# @param helpers [Kettle::Dev::TemplateHelpers] helper API
|
18
|
+
# @param project_root [String] destination project root
|
19
|
+
# @param gem_checkout_root [String] kettle-dev checkout root (source)
|
20
|
+
# @param min_ruby [Gem::Version, nil] minimum Ruby version (for style.gemfile tuning)
|
21
|
+
# @return [void]
|
22
|
+
def sync!(helpers:, project_root:, gem_checkout_root:, min_ruby: nil)
|
23
|
+
# 4a) gemfiles/modular/*.gemfile except style.gemfile (handled below)
|
24
|
+
modular_gemfiles = %w[
|
25
|
+
coverage
|
26
|
+
debug
|
27
|
+
documentation
|
28
|
+
injected
|
29
|
+
optional
|
30
|
+
runtime_heads
|
31
|
+
x_std_libs
|
32
|
+
]
|
33
|
+
modular_gemfiles.each do |base|
|
34
|
+
modular_gemfile = "#{base}.gemfile"
|
35
|
+
src = helpers.prefer_example(File.join(gem_checkout_root, MODULAR_GEMFILE_DIR, modular_gemfile))
|
36
|
+
dest = File.join(project_root, MODULAR_GEMFILE_DIR, modular_gemfile)
|
37
|
+
helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true)
|
38
|
+
end
|
39
|
+
|
40
|
+
# 4b) gemfiles/modular/style.gemfile with dynamic rubocop constraints
|
41
|
+
modular_gemfile = "style.gemfile"
|
42
|
+
src = helpers.prefer_example(File.join(gem_checkout_root, MODULAR_GEMFILE_DIR, modular_gemfile))
|
43
|
+
dest = File.join(project_root, MODULAR_GEMFILE_DIR, modular_gemfile)
|
44
|
+
if File.basename(src).sub(/\.example\z/, "") == "style.gemfile"
|
45
|
+
helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true) do |content|
|
46
|
+
# Adjust rubocop-lts constraint based on min_ruby
|
47
|
+
version_map = [
|
48
|
+
[Gem::Version.new("1.8"), "~> 0.1"],
|
49
|
+
[Gem::Version.new("1.9"), "~> 2.0"],
|
50
|
+
[Gem::Version.new("2.0"), "~> 4.0"],
|
51
|
+
[Gem::Version.new("2.1"), "~> 6.0"],
|
52
|
+
[Gem::Version.new("2.2"), "~> 8.0"],
|
53
|
+
[Gem::Version.new("2.3"), "~> 10.0"],
|
54
|
+
[Gem::Version.new("2.4"), "~> 12.0"],
|
55
|
+
[Gem::Version.new("2.5"), "~> 14.0"],
|
56
|
+
[Gem::Version.new("2.6"), "~> 16.0"],
|
57
|
+
[Gem::Version.new("2.7"), "~> 18.0"],
|
58
|
+
[Gem::Version.new("3.0"), "~> 20.0"],
|
59
|
+
[Gem::Version.new("3.1"), "~> 22.0"],
|
60
|
+
[Gem::Version.new("3.2"), "~> 24.0"],
|
61
|
+
[Gem::Version.new("3.3"), "~> 26.0"],
|
62
|
+
[Gem::Version.new("3.4"), "~> 28.0"],
|
63
|
+
]
|
64
|
+
new_constraint = nil
|
65
|
+
rubocop_ruby_gem_version = nil
|
66
|
+
ruby1_8 = version_map.first
|
67
|
+
begin
|
68
|
+
if min_ruby
|
69
|
+
version_map.reverse_each do |min, req|
|
70
|
+
if min_ruby >= min
|
71
|
+
new_constraint = req
|
72
|
+
rubocop_ruby_gem_version = min.segments.join("_")
|
73
|
+
break
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
if !new_constraint || !rubocop_ruby_gem_version
|
78
|
+
# A gem with no declared minimum ruby is effectively >= 1.8.7
|
79
|
+
new_constraint = ruby1_8[1]
|
80
|
+
rubocop_ruby_gem_version = ruby1_8[0].segments.join("_")
|
81
|
+
end
|
82
|
+
rescue StandardError => e
|
83
|
+
Kettle::Dev.debug_error(e, __method__) if defined?(Kettle::Dev.debug_error)
|
84
|
+
# ignore, use default
|
85
|
+
ensure
|
86
|
+
new_constraint ||= ruby1_8[1]
|
87
|
+
rubocop_ruby_gem_version ||= ruby1_8[0].segments.join("_")
|
88
|
+
end
|
89
|
+
if new_constraint && rubocop_ruby_gem_version
|
90
|
+
token = "{RUBOCOP|LTS|CONSTRAINT}"
|
91
|
+
content.gsub!(token, new_constraint) if content.include?(token)
|
92
|
+
token = "{RUBOCOP|RUBY|GEM}"
|
93
|
+
content.gsub!(token, "rubocop-ruby#{rubocop_ruby_gem_version}") if content.include?(token)
|
94
|
+
end
|
95
|
+
content
|
96
|
+
end
|
97
|
+
else
|
98
|
+
helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true)
|
99
|
+
end
|
100
|
+
|
101
|
+
# 4c) Copy modular directories with nested/versioned files
|
102
|
+
%w[erb mutex_m stringio x_std_libs].each do |dir|
|
103
|
+
src_dir = File.join(gem_checkout_root, MODULAR_GEMFILE_DIR, dir)
|
104
|
+
dest_dir = File.join(project_root, MODULAR_GEMFILE_DIR, dir)
|
105
|
+
helpers.copy_dir_with_prompt(src_dir, dest_dir)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -71,6 +71,7 @@ module Kettle
|
|
71
71
|
when Net::HTTPRedirection
|
72
72
|
location = response["location"]
|
73
73
|
return false unless location
|
74
|
+
|
74
75
|
new_uri = parse_http_uri(location)
|
75
76
|
new_uri = uri + location if new_uri.relative?
|
76
77
|
head_ok?(new_uri.to_s, limit: limit - 1, timeout: timeout)
|
@@ -155,6 +156,7 @@ module Kettle
|
|
155
156
|
|
156
157
|
start = @check_num
|
157
158
|
raise ArgumentError, "check_num must be >= 1" if start < 1
|
159
|
+
|
158
160
|
begin_idx = start - 1
|
159
161
|
checks[begin_idx..-1].each_with_index do |check, i|
|
160
162
|
idx = begin_idx + i + 1
|
@@ -119,6 +119,7 @@ module Kettle
|
|
119
119
|
def readme_osc_tag
|
120
120
|
env = ENV["KETTLE_DEV_BACKER_README_OSC_TAG"].to_s
|
121
121
|
return env unless env.strip.empty?
|
122
|
+
|
122
123
|
if File.file?(OC_YML_PATH)
|
123
124
|
begin
|
124
125
|
yml = YAML.safe_load(File.read(OC_YML_PATH))
|
@@ -149,6 +150,7 @@ module Kettle
|
|
149
150
|
def resolve_handle
|
150
151
|
env = ENV["OPENCOLLECTIVE_HANDLE"]
|
151
152
|
return env unless env.nil? || env.strip.empty?
|
153
|
+
|
152
154
|
if File.file?(OC_YML_PATH)
|
153
155
|
yml = YAML.safe_load(File.read(OC_YML_PATH))
|
154
156
|
handle = yml.is_a?(Hash) ? yml["collective"] || yml[:collective] : nil
|
@@ -167,6 +169,7 @@ module Kettle
|
|
167
169
|
conn.request(req)
|
168
170
|
end
|
169
171
|
return [] unless response.is_a?(Net::HTTPSuccess)
|
172
|
+
|
170
173
|
parsed = JSON.parse(response.body)
|
171
174
|
Array(parsed).map do |h|
|
172
175
|
Backer.new(
|
@@ -186,6 +189,7 @@ module Kettle
|
|
186
189
|
|
187
190
|
def generate_markdown(members, empty_message:, default_name:)
|
188
191
|
return empty_message if members.nil? || members.empty?
|
192
|
+
|
189
193
|
members.map do |m|
|
190
194
|
image_url = m.image || DEFAULT_AVATAR
|
191
195
|
link = m.website || m.profile || "#"
|
@@ -196,14 +200,17 @@ module Kettle
|
|
196
200
|
|
197
201
|
def replace_between_tags(content, start_tag, end_tag, new_content)
|
198
202
|
return :not_found if start_tag == :not_found || end_tag == :not_found
|
203
|
+
|
199
204
|
start_index = content.index(start_tag)
|
200
205
|
end_index = content.index(end_tag)
|
201
206
|
return :not_found if start_index.nil? || end_index.nil? || end_index < start_index
|
207
|
+
|
202
208
|
before = content[0..start_index + start_tag.length - 1]
|
203
209
|
after = content[end_index..-1]
|
204
210
|
replacement = "#{start_tag}\n#{new_content}\n#{end_tag}"
|
205
211
|
current_block = content[start_index..end_index + end_tag.length - 1]
|
206
212
|
return :no_change if current_block == replacement
|
213
|
+
|
207
214
|
trailing = after[end_tag.length..-1] || ""
|
208
215
|
"#{before}\n#{new_content}\n#{end_tag}#{trailing}"
|
209
216
|
end
|
@@ -230,9 +237,11 @@ module Kettle
|
|
230
237
|
|
231
238
|
def extract_section_identities(content, start_tag, end_tag)
|
232
239
|
return Set.new unless start_tag && end_tag && start_tag != :not_found && end_tag != :not_found
|
240
|
+
|
233
241
|
start_index = content.index(start_tag)
|
234
242
|
end_index = content.index(end_tag)
|
235
243
|
return Set.new if start_index.nil? || end_index.nil? || end_index < start_index
|
244
|
+
|
236
245
|
block = content[(start_index + start_tag.length)...end_index]
|
237
246
|
identities = Set.new
|
238
247
|
block.to_s.scan(/\[!\[[^\]]*\]\([^\)]*\)\]\(([^\)]+)\)/) do |m|
|
@@ -269,6 +278,7 @@ module Kettle
|
|
269
278
|
def mention_for_member(m, default_name: "Member")
|
270
279
|
handle = github_handle_from_urls(m.profile, m.website)
|
271
280
|
return "@#{handle}" if handle
|
281
|
+
|
272
282
|
name = (m.name && !m.name.strip.empty?) ? m.name.strip : default_name
|
273
283
|
name
|
274
284
|
end
|
@@ -281,8 +291,10 @@ module Kettle
|
|
281
291
|
next
|
282
292
|
end
|
283
293
|
next unless uri&.host&.downcase&.end_with?("github.com")
|
294
|
+
|
284
295
|
path = (uri.path || "").sub(%r{^/}, "").sub(%r{/$}, "")
|
285
296
|
next if path.empty?
|
297
|
+
|
286
298
|
parts = path.split("/")
|
287
299
|
candidate = if parts[0].downcase == "sponsors" && parts[1]
|
288
300
|
parts[1]
|
@@ -308,12 +320,14 @@ module Kettle
|
|
308
320
|
if system("git", "diff", "--cached", "--quiet")
|
309
321
|
return
|
310
322
|
end
|
323
|
+
|
311
324
|
system("git", "commit", "-m", message)
|
312
325
|
end
|
313
326
|
|
314
327
|
def commit_subject
|
315
328
|
env = ENV["KETTLE_README_BACKERS_COMMIT_SUBJECT"].to_s
|
316
329
|
return env unless env.strip.empty?
|
330
|
+
|
317
331
|
if File.file?(OC_YML_PATH)
|
318
332
|
begin
|
319
333
|
yml = YAML.safe_load(File.read(OC_YML_PATH))
|