kettle-dev 1.0.1 → 1.0.3
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/.devcontainer/devcontainer.json +26 -0
- data/.envrc +42 -0
- data/.git-hooks/commit-msg +41 -0
- data/.git-hooks/commit-subjects-goalie.txt +8 -0
- data/.git-hooks/footer-template.erb.txt +16 -0
- data/.git-hooks/prepare-commit-msg +20 -0
- data/.github/FUNDING.yml +13 -0
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/ancient.yml +80 -0
- data/.github/workflows/auto-assign.yml +21 -0
- data/.github/workflows/codeql-analysis.yml +70 -0
- data/.github/workflows/coverage.yml +130 -0
- data/.github/workflows/current.yml +88 -0
- data/.github/workflows/dependency-review.yml +20 -0
- data/.github/workflows/discord-notifier.yml +38 -0
- data/.github/workflows/heads.yml +87 -0
- data/.github/workflows/jruby.yml +79 -0
- data/.github/workflows/legacy.yml +70 -0
- data/.github/workflows/locked_deps.yml +88 -0
- data/.github/workflows/opencollective.yml +40 -0
- data/.github/workflows/style.yml +67 -0
- data/.github/workflows/supported.yml +85 -0
- data/.github/workflows/truffle.yml +78 -0
- data/.github/workflows/unlocked_deps.yml +87 -0
- data/.github/workflows/unsupported.yml +78 -0
- data/.gitignore +48 -0
- data/.gitlab-ci.yml +45 -0
- data/.junie/guidelines-rbs.md +49 -0
- data/.junie/guidelines.md +132 -0
- data/.opencollective.yml +3 -0
- data/.qlty/qlty.toml +79 -0
- data/.rspec +8 -0
- data/.rubocop.yml +13 -0
- data/.simplecov +7 -0
- data/.tool-versions +1 -0
- data/.yard_gfm_support.rb +22 -0
- data/.yardopts +11 -0
- data/Appraisal.root.gemfile +12 -0
- data/Appraisals +120 -0
- data/CHANGELOG.md +26 -1
- data/Gemfile +32 -0
- data/Rakefile +99 -0
- data/checksums/kettle-dev-1.0.2.gem.sha256 +1 -0
- data/checksums/kettle-dev-1.0.2.gem.sha512 +1 -0
- data/checksums/kettle-dev-1.0.3.gem.sha256 +1 -0
- data/checksums/kettle-dev-1.0.3.gem.sha512 +1 -0
- data/exe/kettle-release +2 -3
- data/gemfiles/modular/coverage.gemfile +6 -0
- data/gemfiles/modular/documentation.gemfile +11 -0
- data/gemfiles/modular/style.gemfile +16 -0
- data/lib/kettle/dev/rakelib/appraisal.rake +40 -0
- data/lib/kettle/dev/rakelib/bench.rake +58 -0
- data/lib/kettle/dev/rakelib/bundle_audit.rake +18 -0
- data/lib/kettle/dev/rakelib/ci.rake +348 -0
- data/lib/kettle/dev/rakelib/install.rake +304 -0
- data/lib/kettle/dev/rakelib/reek.rake +34 -0
- data/lib/kettle/dev/rakelib/require_bench.rake +7 -0
- data/lib/kettle/dev/rakelib/rubocop_gradual.rake +9 -0
- data/lib/kettle/dev/rakelib/spec_test.rake +42 -0
- data/lib/kettle/dev/rakelib/template.rake +461 -0
- data/lib/kettle/dev/rakelib/yard.rake +33 -0
- data/lib/kettle/dev/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +67 -4
- metadata.gz.sig +0 -0
@@ -0,0 +1,9 @@
|
|
1
|
+
begin
|
2
|
+
require "rubocop/gradual/rake_task"
|
3
|
+
|
4
|
+
RuboCop::Gradual::RakeTask.new(:rubocop_gradual_debug) do |t|
|
5
|
+
t.options = ["--debug"]
|
6
|
+
end
|
7
|
+
rescue LoadError
|
8
|
+
warn("[kettle-dev][rubocop_gradual.rake] failed to load rubocop/gradual/rake_task") if Kettle::Dev::DEBUGGING
|
9
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Setup RSpec
|
2
|
+
begin
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
# This takes the place of `coverage` task when running as CI=true
|
7
|
+
Kettle::Dev.register_default("spec") if Kettle::Dev::IS_CI
|
8
|
+
rescue LoadError
|
9
|
+
warn("[kettle-dev][spec_test.rake] failed to load rspec/core/rake_task") if Kettle::Dev::DEBUGGING
|
10
|
+
desc("spec task stub")
|
11
|
+
task(:spec) do
|
12
|
+
warn("NOTE: rspec isn't installed, or is disabled for #{RUBY_VERSION} in the current environment")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Setup MiniTest
|
17
|
+
begin
|
18
|
+
require "rake/testtask"
|
19
|
+
|
20
|
+
Rake::TestTask.new(:test) do |t|
|
21
|
+
t.test_files = FileList["tests/**/test_*.rb"]
|
22
|
+
end
|
23
|
+
rescue LoadError
|
24
|
+
warn("[kettle-dev][spec_test.rake] failed to load rake/testtask") if Kettle::Dev::DEBUGGING
|
25
|
+
desc("test task stub")
|
26
|
+
task(:test) do
|
27
|
+
warn("NOTE: minitest isn't installed, or is disabled for #{RUBY_VERSION} in the current environment")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# rubocop:disable Rake/DuplicateTask
|
32
|
+
if Rake::Task.task_defined?("spec") && !Rake::Task.task_defined?("test")
|
33
|
+
desc "run spec task with test task"
|
34
|
+
task test: :spec
|
35
|
+
elsif !Rake::Task.task_defined?("spec") && Rake::Task.task_defined?("test")
|
36
|
+
desc "run test task with spec task"
|
37
|
+
task spec: :test
|
38
|
+
else
|
39
|
+
# Add spec as pre-requisite to 'test'
|
40
|
+
Rake::Task[:test].enhance(["spec"])
|
41
|
+
end
|
42
|
+
# rubocop:enable Rake/DuplicateTask
|
@@ -0,0 +1,461 @@
|
|
1
|
+
namespace :kettle do
|
2
|
+
namespace :dev do
|
3
|
+
# New template task split from kettle:dev:install
|
4
|
+
# Copies and replaces files pulled from this gem checkout into the host project.
|
5
|
+
# This task can be run independently of `kettle:dev:install`.
|
6
|
+
desc "Template kettle-dev files into the current project"
|
7
|
+
task :template do
|
8
|
+
require "fileutils"
|
9
|
+
require "kettle/dev/template_helpers"
|
10
|
+
|
11
|
+
helpers = Kettle::Dev::TemplateHelpers
|
12
|
+
|
13
|
+
project_root = helpers.project_root
|
14
|
+
gem_checkout_root = helpers.gem_checkout_root
|
15
|
+
|
16
|
+
# Ensure git working tree is clean before making changes (when run standalone)
|
17
|
+
helpers.ensure_clean_git!(root: project_root, task_label: "kettle:dev:template")
|
18
|
+
|
19
|
+
meta = helpers.gemspec_metadata(project_root)
|
20
|
+
gem_name = meta[:gem_name]
|
21
|
+
min_ruby = meta[:min_ruby]
|
22
|
+
gh_org = meta[:gh_org]
|
23
|
+
entrypoint_require = meta[:entrypoint_require]
|
24
|
+
namespace = meta[:namespace]
|
25
|
+
namespace_shield = meta[:namespace_shield]
|
26
|
+
gem_shield = meta[:gem_shield]
|
27
|
+
|
28
|
+
# 1) .devcontainer directory
|
29
|
+
helpers.copy_dir_with_prompt(File.join(gem_checkout_root, ".devcontainer"), File.join(project_root, ".devcontainer"))
|
30
|
+
|
31
|
+
# 2) .github/**/*.yml with FUNDING.yml customizations
|
32
|
+
source_github_dir = File.join(gem_checkout_root, ".github")
|
33
|
+
if Dir.exist?(source_github_dir)
|
34
|
+
Dir.glob(File.join(source_github_dir, "**", "*.yml")).each do |src|
|
35
|
+
src = helpers.prefer_example(src)
|
36
|
+
rel = src.sub(/^#{Regexp.escape(gem_checkout_root)}\/?/, "")
|
37
|
+
dest = File.join(project_root, rel)
|
38
|
+
if File.basename(src).sub(/\.example\z/, "") == "FUNDING.yml"
|
39
|
+
helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true) do |content|
|
40
|
+
c = content.dup
|
41
|
+
c = c.gsub(/^open_collective:\s+.*$/i) { |line| gh_org ? "open_collective: #{gh_org}" : line }
|
42
|
+
if gem_name && !gem_name.empty?
|
43
|
+
c = c.gsub(/^tidelift:\s+.*$/i, "tidelift: rubygems/#{gem_name}")
|
44
|
+
end
|
45
|
+
# Also apply common replacements for org/gem/namespace/shields
|
46
|
+
helpers.apply_common_replacements(
|
47
|
+
c,
|
48
|
+
gh_org: gh_org,
|
49
|
+
gem_name: gem_name,
|
50
|
+
namespace: namespace,
|
51
|
+
namespace_shield: namespace_shield,
|
52
|
+
gem_shield: gem_shield,
|
53
|
+
)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true) do |content|
|
57
|
+
helpers.apply_common_replacements(
|
58
|
+
content,
|
59
|
+
gh_org: gh_org,
|
60
|
+
gem_name: gem_name,
|
61
|
+
namespace: namespace,
|
62
|
+
namespace_shield: namespace_shield,
|
63
|
+
gem_shield: gem_shield,
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# 3) .qlty/qlty.toml
|
71
|
+
helpers.copy_file_with_prompt(
|
72
|
+
helpers.prefer_example(File.join(gem_checkout_root, ".qlty/qlty.toml")),
|
73
|
+
File.join(project_root, ".qlty/qlty.toml"),
|
74
|
+
allow_create: true,
|
75
|
+
allow_replace: true,
|
76
|
+
)
|
77
|
+
|
78
|
+
# 4) gemfiles/modular/*.gemfile (from gem's gemfiles/modular)
|
79
|
+
[%w[coverage.gemfile], %w[documentation.gemfile], %w[style.gemfile]].each do |base|
|
80
|
+
src = helpers.prefer_example(File.join(gem_checkout_root, "gemfiles/modular", base[0]))
|
81
|
+
dest = File.join(project_root, "gemfiles/modular", base[0])
|
82
|
+
if File.basename(src).sub(/\.example\z/, "") == "style.gemfile"
|
83
|
+
helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true) do |content|
|
84
|
+
# Adjust rubocop-lts constraint based on min_ruby
|
85
|
+
version_map = [
|
86
|
+
[Gem::Version.new("1.8"), "~> 0.0"],
|
87
|
+
[Gem::Version.new("1.9"), "~> 2.0"],
|
88
|
+
[Gem::Version.new("2.0"), "~> 4.0"],
|
89
|
+
[Gem::Version.new("2.1"), "~> 6.0"],
|
90
|
+
[Gem::Version.new("2.2"), "~> 8.0"],
|
91
|
+
[Gem::Version.new("2.3"), "~> 10.0"],
|
92
|
+
[Gem::Version.new("2.4"), "~> 12.0"],
|
93
|
+
[Gem::Version.new("2.5"), "~> 14.0"],
|
94
|
+
[Gem::Version.new("2.6"), "~> 16.0"],
|
95
|
+
[Gem::Version.new("2.7"), "~> 18.0"],
|
96
|
+
[Gem::Version.new("3.0"), "~> 20.0"],
|
97
|
+
[Gem::Version.new("3.1"), "~> 22.0"],
|
98
|
+
[Gem::Version.new("3.2"), "~> 24.0"],
|
99
|
+
[Gem::Version.new("3.3"), "~> 26.0"],
|
100
|
+
[Gem::Version.new("3.4"), "~> 28.0"],
|
101
|
+
]
|
102
|
+
new_constraint = nil
|
103
|
+
begin
|
104
|
+
if min_ruby && !min_ruby.empty?
|
105
|
+
v = Gem::Version.new(min_ruby.split(".")[0, 2].join("."))
|
106
|
+
version_map.reverse_each do |min, req|
|
107
|
+
if v >= min
|
108
|
+
new_constraint = req
|
109
|
+
break
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
rescue StandardError
|
114
|
+
# leave nil
|
115
|
+
end
|
116
|
+
if new_constraint
|
117
|
+
content.gsub(/^gem\s+"rubocop-lts",\s*"[^"]+".*$/) do |line|
|
118
|
+
# Do not preserve whatever tail was there before
|
119
|
+
%(gem "rubocop-lts", "#{new_constraint}")
|
120
|
+
end
|
121
|
+
else
|
122
|
+
content
|
123
|
+
end
|
124
|
+
end
|
125
|
+
else
|
126
|
+
helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# 5) spec/spec_helper.rb (no create)
|
131
|
+
dest_spec_helper = File.join(project_root, "spec/spec_helper.rb")
|
132
|
+
if File.file?(dest_spec_helper)
|
133
|
+
old = File.read(dest_spec_helper)
|
134
|
+
if old.include?('require "kettle/dev"') || old.include?("require 'kettle/dev'")
|
135
|
+
replacement = %(require "#{entrypoint_require}")
|
136
|
+
new_content = old.gsub(/require\s+["']kettle\/dev["']/, replacement)
|
137
|
+
if new_content != old
|
138
|
+
if helpers.ask("Replace require \"kettle/dev\" in spec/spec_helper.rb with #{replacement}?", true)
|
139
|
+
helpers.write_file(dest_spec_helper, new_content)
|
140
|
+
puts "Updated require in spec/spec_helper.rb"
|
141
|
+
else
|
142
|
+
puts "Skipped modifying spec/spec_helper.rb"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# 6) .env.local special case: never overwrite project .env.local; copy template as .env.local.example
|
149
|
+
begin
|
150
|
+
envlocal_src = helpers.prefer_example(File.join(gem_checkout_root, ".env.local"))
|
151
|
+
envlocal_dest = File.join(project_root, ".env.local.example")
|
152
|
+
if File.exist?(envlocal_src)
|
153
|
+
helpers.copy_file_with_prompt(envlocal_src, envlocal_dest, allow_create: true, allow_replace: true)
|
154
|
+
end
|
155
|
+
rescue StandardError => e
|
156
|
+
puts "WARNING: Skipped .env.local example copy due to #{e.class}: #{e.message}"
|
157
|
+
end
|
158
|
+
|
159
|
+
# 7) Root and other files
|
160
|
+
files_to_copy = %w[
|
161
|
+
.envrc
|
162
|
+
.gitignore
|
163
|
+
.gitlab-ci.yml
|
164
|
+
.rspec
|
165
|
+
.rubocop.yml
|
166
|
+
.simplecov
|
167
|
+
.tool-versions
|
168
|
+
.yard_gfm_support.rb
|
169
|
+
.yardopts
|
170
|
+
.opencollective.yml
|
171
|
+
Appraisal.root.gemfile
|
172
|
+
Appraisals
|
173
|
+
CHANGELOG.md
|
174
|
+
CITATION.cff
|
175
|
+
CODE_OF_CONDUCT.md
|
176
|
+
CONTRIBUTING.md
|
177
|
+
Gemfile
|
178
|
+
Rakefile
|
179
|
+
README.md
|
180
|
+
RUBOCOP.md
|
181
|
+
SECURITY.md
|
182
|
+
.junie/guidelines.md
|
183
|
+
.junie/guidelines-rbs.md
|
184
|
+
]
|
185
|
+
|
186
|
+
files_to_copy.each do |rel|
|
187
|
+
src = helpers.prefer_example(File.join(gem_checkout_root, rel))
|
188
|
+
dest = File.join(project_root, rel)
|
189
|
+
next unless File.exist?(src)
|
190
|
+
if File.basename(rel) == "README.md"
|
191
|
+
helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true) do |content|
|
192
|
+
# 1) Do token replacements on the template content (org/gem/namespace/shields)
|
193
|
+
c = helpers.apply_common_replacements(
|
194
|
+
content,
|
195
|
+
gh_org: gh_org,
|
196
|
+
gem_name: gem_name,
|
197
|
+
namespace: namespace,
|
198
|
+
namespace_shield: namespace_shield,
|
199
|
+
gem_shield: gem_shield,
|
200
|
+
)
|
201
|
+
|
202
|
+
# 2) Merge specific sections from destination README, if present
|
203
|
+
begin
|
204
|
+
dest_existing = File.exist?(dest) ? File.read(dest) : nil
|
205
|
+
|
206
|
+
# Helper to parse markdown sections at any heading level (#, ##, ###, ...)
|
207
|
+
parse_sections = lambda do |md|
|
208
|
+
sections = []
|
209
|
+
return sections unless md
|
210
|
+
lines = md.split("\n", -1) # keep trailing empty lines
|
211
|
+
indices = []
|
212
|
+
lines.each_with_index do |ln, i|
|
213
|
+
indices << i if ln =~ /^#+\s+.+/
|
214
|
+
end
|
215
|
+
indices << lines.length
|
216
|
+
indices.each_cons(2) do |start_i, nxt|
|
217
|
+
heading = lines[start_i]
|
218
|
+
body_lines = lines[(start_i + 1)...nxt] || []
|
219
|
+
title = heading.sub(/^#+\s+/, "")
|
220
|
+
# Normalize by removing leading emoji/non-alnum and extra spaces
|
221
|
+
base = title.sub(/\A[^\p{Alnum}]+/u, "").strip.downcase
|
222
|
+
sections << {start: start_i, stop: nxt - 1, heading: heading, body: body_lines.join("\n"), base: base}
|
223
|
+
end
|
224
|
+
{lines: lines, sections: sections}
|
225
|
+
end
|
226
|
+
|
227
|
+
# Parse src (c) and dest
|
228
|
+
src_parsed = parse_sections.call(c)
|
229
|
+
dest_parsed = parse_sections.call(dest_existing)
|
230
|
+
|
231
|
+
# Build lookup for destination sections by base title
|
232
|
+
dest_lookup = {}
|
233
|
+
if dest_parsed && dest_parsed[:sections]
|
234
|
+
dest_parsed[:sections].each do |s|
|
235
|
+
dest_lookup[s[:base]] = s[:body]
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# Build targets to merge: existing curated list plus any NOTE sections at any level
|
240
|
+
note_bases = []
|
241
|
+
if src_parsed && src_parsed[:sections]
|
242
|
+
note_bases = src_parsed[:sections]
|
243
|
+
.select { |s| s[:heading] =~ /^#+\s+note:.*/i }
|
244
|
+
.map { |s| s[:base] }
|
245
|
+
end
|
246
|
+
targets = ["synopsis", "configuration", "basic usage"] + note_bases
|
247
|
+
|
248
|
+
# Replace matching sections in src
|
249
|
+
if src_parsed && src_parsed[:sections] && !src_parsed[:sections].empty?
|
250
|
+
lines = src_parsed[:lines].dup
|
251
|
+
# Iterate over src sections; when base is in targets, rewrite its body
|
252
|
+
src_parsed[:sections].reverse_each do |sec|
|
253
|
+
next unless targets.include?(sec[:base])
|
254
|
+
new_body = dest_lookup.fetch(sec[:base], "\n\n")
|
255
|
+
new_block = [sec[:heading], new_body].join("\n")
|
256
|
+
# Replace the range from start+0 to stop with new_block lines
|
257
|
+
range_start = sec[:start]
|
258
|
+
range_end = sec[:stop]
|
259
|
+
# Remove old range
|
260
|
+
lines.slice!(range_start..range_end)
|
261
|
+
# Insert new block (split preserves potential empty tail)
|
262
|
+
insert_lines = new_block.split("\n", -1)
|
263
|
+
lines.insert(range_start, *insert_lines)
|
264
|
+
end
|
265
|
+
c = lines.join("\n")
|
266
|
+
end
|
267
|
+
|
268
|
+
# 3) Preserve first H1 emojis from destination README, if any
|
269
|
+
begin
|
270
|
+
require "kettle/emoji_regex"
|
271
|
+
emoji_re = Kettle::EmojiRegex::REGEX
|
272
|
+
|
273
|
+
dest_emojis = nil
|
274
|
+
if dest_existing
|
275
|
+
first_h1_dest = dest_existing.lines.find { |ln| ln =~ /^#\s+/ }
|
276
|
+
if first_h1_dest
|
277
|
+
after = first_h1_dest.sub(/^#\s+/, "")
|
278
|
+
emojis = +""
|
279
|
+
while after =~ /\A#{emoji_re.source}/u
|
280
|
+
# Capture the entire grapheme cluster for the emoji (handles VS16/ZWJ sequences)
|
281
|
+
cluster = after[/\A\X/u]
|
282
|
+
emojis << cluster
|
283
|
+
after = after[cluster.length..-1].to_s
|
284
|
+
end
|
285
|
+
dest_emojis = emojis unless emojis.empty?
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
if dest_emojis && !dest_emojis.empty?
|
290
|
+
lines_new = c.split("\n", -1)
|
291
|
+
idx = lines_new.index { |ln| ln =~ /^#\s+/ }
|
292
|
+
if idx
|
293
|
+
rest = lines_new[idx].sub(/^#\s+/, "")
|
294
|
+
# Remove any leading emojis from the H1 by peeling full grapheme clusters
|
295
|
+
rest_wo_emoji = begin
|
296
|
+
tmp = rest.dup
|
297
|
+
while tmp =~ /\A#{emoji_re.source}/u
|
298
|
+
cluster = tmp[/\A\X/u]
|
299
|
+
tmp = tmp[cluster.length..-1].to_s
|
300
|
+
end
|
301
|
+
tmp.sub(/\A\s+/, "")
|
302
|
+
end
|
303
|
+
lines_new[idx] = ["#", dest_emojis, rest_wo_emoji].join(" ").gsub(/\s+/, " ").sub(/^#\s+/, "# ")
|
304
|
+
c = lines_new.join("\n")
|
305
|
+
end
|
306
|
+
end
|
307
|
+
rescue StandardError
|
308
|
+
# ignore emoji preservation errors
|
309
|
+
end
|
310
|
+
rescue StandardError
|
311
|
+
# Best effort; if anything fails, keep c as-is
|
312
|
+
end
|
313
|
+
|
314
|
+
c
|
315
|
+
end
|
316
|
+
elsif ["CHANGELOG.md", "CITATION.cff", "CONTRIBUTING.md", ".opencollective.yml", ".junie/guidelines.md"].include?(rel)
|
317
|
+
helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true) do |content|
|
318
|
+
helpers.apply_common_replacements(
|
319
|
+
content,
|
320
|
+
gh_org: gh_org,
|
321
|
+
gem_name: gem_name,
|
322
|
+
namespace: namespace,
|
323
|
+
namespace_shield: namespace_shield,
|
324
|
+
gem_shield: gem_shield,
|
325
|
+
)
|
326
|
+
end
|
327
|
+
else
|
328
|
+
helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# After creating or replacing .envrc or .env.local.example, require review and exit unless allowed
|
333
|
+
begin
|
334
|
+
envrc_path = File.join(project_root, ".envrc")
|
335
|
+
envlocal_example_path = File.join(project_root, ".env.local.example")
|
336
|
+
changed_env_files = []
|
337
|
+
changed_env_files << envrc_path if helpers.modified_by_template?(envrc_path)
|
338
|
+
changed_env_files << envlocal_example_path if helpers.modified_by_template?(envlocal_example_path)
|
339
|
+
if !changed_env_files.empty?
|
340
|
+
if ENV.fetch("allowed", "").to_s =~ /\A(1|true|y|yes)\z/i
|
341
|
+
puts "Detected updates to #{changed_env_files.map { |p| File.basename(p) }.join(" and ")}. Proceeding because allowed=true."
|
342
|
+
else
|
343
|
+
puts
|
344
|
+
puts "IMPORTANT: The following environment-related files were created/updated:"
|
345
|
+
changed_env_files.each { |p| puts " - #{p}" }
|
346
|
+
puts
|
347
|
+
puts "Please review these files. If .envrc changed, run:"
|
348
|
+
puts " direnv allow"
|
349
|
+
puts
|
350
|
+
puts "After that, re-run to resume:"
|
351
|
+
puts " bundle exec rake kettle:dev:template allowed=true"
|
352
|
+
puts " # or to run the full install afterwards:"
|
353
|
+
puts " bundle exec rake kettle:dev:install allowed=true"
|
354
|
+
abort("Aborting: review of environment files required before continuing.")
|
355
|
+
end
|
356
|
+
end
|
357
|
+
rescue StandardError => e
|
358
|
+
puts "WARNING: Could not determine env file changes: #{e.class}: #{e.message}"
|
359
|
+
end
|
360
|
+
|
361
|
+
# Handle .git-hooks files
|
362
|
+
# - Two template files (.txt) are offered with a location prompt (local/global/skip).
|
363
|
+
# - Two hook scripts are always copied to both local and global locations by default;
|
364
|
+
# if they already exist, ask before overwriting.
|
365
|
+
source_hooks_dir = File.join(gem_checkout_root, ".git-hooks")
|
366
|
+
if Dir.exist?(source_hooks_dir)
|
367
|
+
goalie_src = File.join(source_hooks_dir, "commit-subjects-goalie.txt")
|
368
|
+
footer_src = File.join(source_hooks_dir, "footer-template.erb.txt")
|
369
|
+
hook_ruby_src = File.join(source_hooks_dir, "commit-msg")
|
370
|
+
hook_sh_src = File.join(source_hooks_dir, "prepare-commit-msg")
|
371
|
+
|
372
|
+
# First: templates (.txt) — keep the existing single question which applies only to these
|
373
|
+
if File.file?(goalie_src) && File.file?(footer_src)
|
374
|
+
puts
|
375
|
+
puts "Git hooks templates found:"
|
376
|
+
puts " - #{goalie_src}"
|
377
|
+
puts " - #{footer_src}"
|
378
|
+
puts
|
379
|
+
puts "About these files:"
|
380
|
+
puts "- commit-subjects-goalie.txt:"
|
381
|
+
puts " Lists commit subject prefixes to look for; if a commit subject starts with any listed prefix,"
|
382
|
+
puts " kettle-commit-msg will append a footer to the commit message (when GIT_HOOK_FOOTER_APPEND=true)."
|
383
|
+
puts " Defaults include release prep (🔖 Prepare release v) and checksum commits (🔒️ Checksums for v)."
|
384
|
+
puts "- footer-template.erb.txt:"
|
385
|
+
puts " ERB template rendered to produce the footer. You can customize its contents and variables."
|
386
|
+
puts
|
387
|
+
puts "Where would you like to install these two templates?"
|
388
|
+
puts " [l] Local to this project (#{File.join(project_root, ".git-hooks")})"
|
389
|
+
puts " [g] Global for this user (#{File.join(ENV["HOME"], ".git-hooks")})"
|
390
|
+
puts " [s] Skip copying"
|
391
|
+
print "Choose (l/g/s) [l]: "
|
392
|
+
choice = $stdin.gets&.strip
|
393
|
+
choice = "l" if choice.nil? || choice.empty?
|
394
|
+
dest_dir = case choice.downcase
|
395
|
+
when "g", "global" then File.join(ENV["HOME"], ".git-hooks")
|
396
|
+
when "s", "skip" then nil
|
397
|
+
else File.join(project_root, ".git-hooks")
|
398
|
+
end
|
399
|
+
|
400
|
+
if dest_dir
|
401
|
+
FileUtils.mkdir_p(dest_dir)
|
402
|
+
[[goalie_src, "commit-subjects-goalie.txt"], [footer_src, "footer-template.erb.txt"]].each do |src, base|
|
403
|
+
dest = File.join(dest_dir, base)
|
404
|
+
# Use helpers to allow create/replace prompts for these files (question applies to them)
|
405
|
+
helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true)
|
406
|
+
# Ensure readable (0644). These are data/templates, not executables.
|
407
|
+
begin
|
408
|
+
File.chmod(0o644, dest) if File.exist?(dest)
|
409
|
+
rescue StandardError
|
410
|
+
# ignore permission issues
|
411
|
+
end
|
412
|
+
end
|
413
|
+
else
|
414
|
+
puts "Skipping copy of .git-hooks templates."
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
# Second: hook scripts — copy only to the local project; prompt only on overwrite
|
419
|
+
# Per requirements: do not install hook scripts to global ~/.git-hooks
|
420
|
+
hook_dests = [File.join(project_root, ".git-hooks")]
|
421
|
+
hook_pairs = [[hook_ruby_src, "commit-msg", 0o755], [hook_sh_src, "prepare-commit-msg", 0o755]]
|
422
|
+
hook_pairs.each do |src, base, mode|
|
423
|
+
next unless File.file?(src)
|
424
|
+
hook_dests.each do |dstdir|
|
425
|
+
begin
|
426
|
+
FileUtils.mkdir_p(dstdir)
|
427
|
+
dest = File.join(dstdir, base)
|
428
|
+
# Create without prompt if missing; if exists, ask to replace
|
429
|
+
if File.exist?(dest)
|
430
|
+
# ask to overwrite
|
431
|
+
if helpers.ask("Overwrite existing #{dest}?", true)
|
432
|
+
content = File.read(src)
|
433
|
+
helpers.write_file(dest, content)
|
434
|
+
begin
|
435
|
+
File.chmod(mode, dest)
|
436
|
+
rescue StandardError
|
437
|
+
# ignore permission issues
|
438
|
+
end
|
439
|
+
puts "Replaced #{dest}"
|
440
|
+
else
|
441
|
+
puts "Kept existing #{dest}"
|
442
|
+
end
|
443
|
+
else
|
444
|
+
content = File.read(src)
|
445
|
+
helpers.write_file(dest, content)
|
446
|
+
begin
|
447
|
+
File.chmod(mode, dest)
|
448
|
+
rescue StandardError
|
449
|
+
# ignore permission issues
|
450
|
+
end
|
451
|
+
puts "Installed #{dest}"
|
452
|
+
end
|
453
|
+
rescue StandardError => e
|
454
|
+
puts "WARNING: Could not install hook #{base} to #{dstdir}: #{e.class}: #{e.message}"
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Setup Yard
|
4
|
+
begin
|
5
|
+
require "yard"
|
6
|
+
|
7
|
+
YARD::Rake::YardocTask.new(:yard) do |t|
|
8
|
+
t.files = [
|
9
|
+
# Source Splats (alphabetical)
|
10
|
+
"lib/**/*.rb",
|
11
|
+
"-", # source and extra docs are separated by "-"
|
12
|
+
# Extra Files (alphabetical)
|
13
|
+
"*.cff",
|
14
|
+
"*.md",
|
15
|
+
"*.txt",
|
16
|
+
"checksums/**/*.sha256",
|
17
|
+
"checksums/**/*.sha512",
|
18
|
+
"REEK",
|
19
|
+
"sig/**/*.rbs",
|
20
|
+
]
|
21
|
+
|
22
|
+
# No need for this, due to plugin load in .yardopts
|
23
|
+
# require "yard-junk/rake"
|
24
|
+
# YardJunk::Rake.define_task
|
25
|
+
end
|
26
|
+
Kettle::Dev.register_default("yard")
|
27
|
+
rescue LoadError
|
28
|
+
warn("[kettle-dev][yard.rake] failed to load yard") if Kettle::Dev::DEBUGGING
|
29
|
+
desc("(stub) yard is unavailable")
|
30
|
+
task(:yard) do
|
31
|
+
warn("NOTE: yard isn't installed, or is disabled for #{RUBY_VERSION} in the current environment")
|
32
|
+
end
|
33
|
+
end
|
data/lib/kettle/dev/version.rb
CHANGED
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: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter H. Boling
|
@@ -207,26 +207,89 @@ extra_rdoc_files:
|
|
207
207
|
- checksums/kettle-dev-1.0.0.gem.sha512
|
208
208
|
- checksums/kettle-dev-1.0.1.gem.sha256
|
209
209
|
- checksums/kettle-dev-1.0.1.gem.sha512
|
210
|
+
- checksums/kettle-dev-1.0.2.gem.sha256
|
211
|
+
- checksums/kettle-dev-1.0.2.gem.sha512
|
212
|
+
- checksums/kettle-dev-1.0.3.gem.sha256
|
213
|
+
- checksums/kettle-dev-1.0.3.gem.sha512
|
210
214
|
files:
|
215
|
+
- ".devcontainer/devcontainer.json"
|
216
|
+
- ".envrc"
|
217
|
+
- ".git-hooks/commit-msg"
|
218
|
+
- ".git-hooks/commit-subjects-goalie.txt"
|
219
|
+
- ".git-hooks/footer-template.erb.txt"
|
220
|
+
- ".git-hooks/prepare-commit-msg"
|
221
|
+
- ".github/FUNDING.yml"
|
222
|
+
- ".github/dependabot.yml"
|
223
|
+
- ".github/workflows/ancient.yml"
|
224
|
+
- ".github/workflows/auto-assign.yml"
|
225
|
+
- ".github/workflows/codeql-analysis.yml"
|
226
|
+
- ".github/workflows/coverage.yml"
|
227
|
+
- ".github/workflows/current.yml"
|
228
|
+
- ".github/workflows/dependency-review.yml"
|
229
|
+
- ".github/workflows/discord-notifier.yml"
|
230
|
+
- ".github/workflows/heads.yml"
|
231
|
+
- ".github/workflows/jruby.yml"
|
232
|
+
- ".github/workflows/legacy.yml"
|
233
|
+
- ".github/workflows/locked_deps.yml"
|
234
|
+
- ".github/workflows/opencollective.yml"
|
235
|
+
- ".github/workflows/style.yml"
|
236
|
+
- ".github/workflows/supported.yml"
|
237
|
+
- ".github/workflows/truffle.yml"
|
238
|
+
- ".github/workflows/unlocked_deps.yml"
|
239
|
+
- ".github/workflows/unsupported.yml"
|
240
|
+
- ".gitignore"
|
241
|
+
- ".gitlab-ci.yml"
|
242
|
+
- ".junie/guidelines-rbs.md"
|
243
|
+
- ".junie/guidelines.md"
|
244
|
+
- ".opencollective.yml"
|
245
|
+
- ".qlty/qlty.toml"
|
246
|
+
- ".rspec"
|
247
|
+
- ".rubocop.yml"
|
248
|
+
- ".simplecov"
|
249
|
+
- ".tool-versions"
|
250
|
+
- ".yard_gfm_support.rb"
|
251
|
+
- ".yardopts"
|
252
|
+
- Appraisal.root.gemfile
|
253
|
+
- Appraisals
|
211
254
|
- CHANGELOG.md
|
212
255
|
- CITATION.cff
|
213
256
|
- CODE_OF_CONDUCT.md
|
214
257
|
- CONTRIBUTING.md
|
258
|
+
- Gemfile
|
215
259
|
- LICENSE.txt
|
216
260
|
- README.md
|
217
261
|
- REEK
|
218
262
|
- RUBOCOP.md
|
263
|
+
- Rakefile
|
219
264
|
- SECURITY.md
|
220
265
|
- checksums/kettle-dev-1.0.0.gem.sha256
|
221
266
|
- checksums/kettle-dev-1.0.0.gem.sha512
|
222
267
|
- checksums/kettle-dev-1.0.1.gem.sha256
|
223
268
|
- checksums/kettle-dev-1.0.1.gem.sha512
|
269
|
+
- checksums/kettle-dev-1.0.2.gem.sha256
|
270
|
+
- checksums/kettle-dev-1.0.2.gem.sha512
|
271
|
+
- checksums/kettle-dev-1.0.3.gem.sha256
|
272
|
+
- checksums/kettle-dev-1.0.3.gem.sha512
|
224
273
|
- exe/kettle-commit-msg
|
225
274
|
- exe/kettle-readme-backers
|
226
275
|
- exe/kettle-release
|
276
|
+
- gemfiles/modular/coverage.gemfile
|
277
|
+
- gemfiles/modular/documentation.gemfile
|
278
|
+
- gemfiles/modular/style.gemfile
|
227
279
|
- lib/kettle-dev.rb
|
228
280
|
- lib/kettle/dev.rb
|
229
281
|
- lib/kettle/dev/ci_helpers.rb
|
282
|
+
- lib/kettle/dev/rakelib/appraisal.rake
|
283
|
+
- lib/kettle/dev/rakelib/bench.rake
|
284
|
+
- lib/kettle/dev/rakelib/bundle_audit.rake
|
285
|
+
- lib/kettle/dev/rakelib/ci.rake
|
286
|
+
- lib/kettle/dev/rakelib/install.rake
|
287
|
+
- lib/kettle/dev/rakelib/reek.rake
|
288
|
+
- lib/kettle/dev/rakelib/require_bench.rake
|
289
|
+
- lib/kettle/dev/rakelib/rubocop_gradual.rake
|
290
|
+
- lib/kettle/dev/rakelib/spec_test.rake
|
291
|
+
- lib/kettle/dev/rakelib/template.rake
|
292
|
+
- lib/kettle/dev/rakelib/yard.rake
|
230
293
|
- lib/kettle/dev/tasks.rb
|
231
294
|
- lib/kettle/dev/template_helpers.rb
|
232
295
|
- lib/kettle/dev/version.rb
|
@@ -239,10 +302,10 @@ licenses:
|
|
239
302
|
- MIT
|
240
303
|
metadata:
|
241
304
|
homepage_uri: https://kettle-dev.galtzo.com/
|
242
|
-
source_code_uri: https://github.com/galtzo-floss/kettle-dev/tree/v1.0.
|
243
|
-
changelog_uri: https://github.com/galtzo-floss/kettle-dev/blob/v1.0.
|
305
|
+
source_code_uri: https://github.com/galtzo-floss/kettle-dev/tree/v1.0.3
|
306
|
+
changelog_uri: https://github.com/galtzo-floss/kettle-dev/blob/v1.0.3/CHANGELOG.md
|
244
307
|
bug_tracker_uri: https://github.com/galtzo-floss/kettle-dev/issues
|
245
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/1.0.
|
308
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/1.0.3
|
246
309
|
funding_uri: https://github.com/sponsors/pboling
|
247
310
|
wiki_uri: https://github.com/galtzo-floss/kettle-dev/wiki
|
248
311
|
news_uri: https://www.railsbling.com/tags/kettle-dev
|
metadata.gz.sig
CHANGED
Binary file
|