kettle-jem 7.0.0 → 7.1.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.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +1505 -0
  4. data/LICENSE.md +13 -0
  5. data/README.md +794 -0
  6. data/exe/kettle-jem +20 -0
  7. data/lib/kettle/jem/cli.rb +334 -0
  8. data/lib/kettle/jem/license_txt_migrator.rb +33 -0
  9. data/lib/kettle/jem/rakelib/install.rake +10 -0
  10. data/lib/kettle/jem/rakelib/prepare.rake +10 -0
  11. data/lib/kettle/jem/rakelib/selftest.rake +10 -0
  12. data/lib/kettle/jem/rakelib/template.rake +10 -0
  13. data/lib/kettle/jem/tasks/install_task.rb +2178 -0
  14. data/lib/kettle/jem/tasks/prepare_task.rb +274 -0
  15. data/lib/kettle/jem/tasks/self_test_task.rb +256 -0
  16. data/lib/kettle/jem/tasks/template_task.rb +124 -0
  17. data/lib/kettle/jem/tasks.rb +6 -0
  18. data/lib/kettle/jem/templates/.aiignore.example +19 -0
  19. data/lib/kettle/jem/templates/.config/mise/env.sh +26 -0
  20. data/lib/kettle/jem/templates/.devcontainer/apt-install/devcontainer-feature.json.example +9 -0
  21. data/lib/kettle/jem/templates/.devcontainer/apt-install/install.sh.example +19 -0
  22. data/lib/kettle/jem/templates/.devcontainer/devcontainer.json.example +27 -0
  23. data/lib/kettle/jem/templates/.devcontainer/scripts/setup-tree-sitter.sh.example +210 -0
  24. data/lib/kettle/jem/templates/.env.local.example +67 -0
  25. data/lib/kettle/jem/templates/.envrc.example +4 -0
  26. data/lib/kettle/jem/templates/.envrc.no-osc.example +4 -0
  27. data/lib/kettle/jem/templates/.gemrc.example +1 -0
  28. data/lib/kettle/jem/templates/.git-hooks/commit-msg.example +60 -0
  29. data/lib/kettle/jem/templates/.git-hooks/commit-subjects-goalie.txt.example +8 -0
  30. data/lib/kettle/jem/templates/.git-hooks/footer-template.erb.txt.example +16 -0
  31. data/lib/kettle/jem/templates/.git-hooks/prepare-commit-msg.example +9 -0
  32. data/lib/kettle/jem/templates/.github/.codecov.yml.example +14 -0
  33. data/lib/kettle/jem/templates/.github/FUNDING.yml.example +10 -0
  34. data/lib/kettle/jem/templates/.github/FUNDING.yml.no-osc.example +10 -0
  35. data/lib/kettle/jem/templates/.github/copilot_instructions.md.example +1 -0
  36. data/lib/kettle/jem/templates/.github/dependabot.yml.example +13 -0
  37. data/lib/kettle/jem/templates/.github/workflows/auto-assign.yml.example +21 -0
  38. data/lib/kettle/jem/templates/.github/workflows/coverage.yml.example +102 -0
  39. data/lib/kettle/jem/templates/.github/workflows/current.yml.example +86 -0
  40. data/lib/kettle/jem/templates/.github/workflows/dep-heads.yml.example +249 -0
  41. data/lib/kettle/jem/templates/.github/workflows/dependency-review.yml.example +22 -0
  42. data/lib/kettle/jem/templates/.github/workflows/discord-notifier.yml.example +39 -0
  43. data/lib/kettle/jem/templates/.github/workflows/framework-ci.yml.example +81 -0
  44. data/lib/kettle/jem/templates/.github/workflows/heads.yml.example +212 -0
  45. data/lib/kettle/jem/templates/.github/workflows/jruby-10.0.yml.example +91 -0
  46. data/lib/kettle/jem/templates/.github/workflows/jruby-9.1.yml.example +90 -0
  47. data/lib/kettle/jem/templates/.github/workflows/jruby-9.2.yml.example +95 -0
  48. data/lib/kettle/jem/templates/.github/workflows/jruby-9.3.yml.example +95 -0
  49. data/lib/kettle/jem/templates/.github/workflows/jruby-9.4.yml.example +95 -0
  50. data/lib/kettle/jem/templates/.github/workflows/jruby.yml.example +91 -0
  51. data/lib/kettle/jem/templates/.github/workflows/license-eye.yml.example +43 -0
  52. data/lib/kettle/jem/templates/.github/workflows/locked_deps.yml.example +89 -0
  53. data/lib/kettle/jem/templates/.github/workflows/opencollective.yml.example +44 -0
  54. data/lib/kettle/jem/templates/.github/workflows/ruby-2.3.yml.example +80 -0
  55. data/lib/kettle/jem/templates/.github/workflows/ruby-2.4.yml.example +84 -0
  56. data/lib/kettle/jem/templates/.github/workflows/ruby-2.5.yml.example +84 -0
  57. data/lib/kettle/jem/templates/.github/workflows/ruby-2.6.yml.example +84 -0
  58. data/lib/kettle/jem/templates/.github/workflows/ruby-2.7.yml.example +84 -0
  59. data/lib/kettle/jem/templates/.github/workflows/ruby-3.0.yml.example +85 -0
  60. data/lib/kettle/jem/templates/.github/workflows/ruby-3.1.yml.example +83 -0
  61. data/lib/kettle/jem/templates/.github/workflows/ruby-3.2.yml.example +80 -0
  62. data/lib/kettle/jem/templates/.github/workflows/ruby-3.3.yml.example +80 -0
  63. data/lib/kettle/jem/templates/.github/workflows/ruby-3.4.yml.example +80 -0
  64. data/lib/kettle/jem/templates/.github/workflows/style.yml.example +78 -0
  65. data/lib/kettle/jem/templates/.github/workflows/templating.yml.example +86 -0
  66. data/lib/kettle/jem/templates/.github/workflows/truffle.yml.example +91 -0
  67. data/lib/kettle/jem/templates/.github/workflows/truffleruby-22.3.yml.example +92 -0
  68. data/lib/kettle/jem/templates/.github/workflows/truffleruby-23.0.yml.example +92 -0
  69. data/lib/kettle/jem/templates/.github/workflows/truffleruby-23.1.yml.example +78 -0
  70. data/lib/kettle/jem/templates/.github/workflows/truffleruby-24.2.yml.example +91 -0
  71. data/lib/kettle/jem/templates/.github/workflows/truffleruby-25.0.yml.example +96 -0
  72. data/lib/kettle/jem/templates/.github/workflows/truffleruby-33.0.yml.example +91 -0
  73. data/lib/kettle/jem/templates/.github/workflows/unlocked_deps.yml.example +93 -0
  74. data/lib/kettle/jem/templates/.gitignore.example +53 -0
  75. data/lib/kettle/jem/templates/.gitlab-ci.yml.example +134 -0
  76. data/lib/kettle/jem/templates/.idea/.gitignore.example +44 -0
  77. data/lib/kettle/jem/templates/.licenserc.yaml.example +8 -0
  78. data/lib/kettle/jem/templates/.opencollective.yml.example +3 -0
  79. data/lib/kettle/jem/templates/.qlty/qlty.toml.example +79 -0
  80. data/lib/kettle/jem/templates/.rspec.example +10 -0
  81. data/lib/kettle/jem/templates/.rubocop.example +1 -0
  82. data/lib/kettle/jem/templates/.rubocop.yml.example +47 -0
  83. data/lib/kettle/jem/templates/.rubocop_rspec.yml.example +55 -0
  84. data/lib/kettle/jem/templates/.simplecov.example +18 -0
  85. data/lib/kettle/jem/templates/.structuredmerge/git-drivers.toml.example +86 -0
  86. data/lib/kettle/jem/templates/.structuredmerge/kettle-jem.yml.example +519 -0
  87. data/lib/kettle/jem/templates/.yard-lint.yml.example +54 -0
  88. data/lib/kettle/jem/templates/.yardignore.example +13 -0
  89. data/lib/kettle/jem/templates/.yardopts.example +14 -0
  90. data/lib/kettle/jem/templates/AGENTS.md.example +283 -0
  91. data/lib/kettle/jem/templates/AGPL-3.0-only.md.example +235 -0
  92. data/lib/kettle/jem/templates/Appraisal.root.gemfile.example +26 -0
  93. data/lib/kettle/jem/templates/Appraisals.example +150 -0
  94. data/lib/kettle/jem/templates/Big-Time-Public-License.md.example +99 -0
  95. data/lib/kettle/jem/templates/CHANGELOG.md.example +33 -0
  96. data/lib/kettle/jem/templates/CHANGELOG.transfer.md +109 -0
  97. data/lib/kettle/jem/templates/CITATION.cff.example +20 -0
  98. data/lib/kettle/jem/templates/CODE_OF_CONDUCT.md.example +134 -0
  99. data/lib/kettle/jem/templates/CONTRIBUTING.md.example +292 -0
  100. data/lib/kettle/jem/templates/FUNDING.md.example +74 -0
  101. data/lib/kettle/jem/templates/FUNDING.md.no-osc.example +63 -0
  102. data/lib/kettle/jem/templates/Gemfile.example +52 -0
  103. data/lib/kettle/jem/templates/IRP.md.example +34 -0
  104. data/lib/kettle/jem/templates/LICENSE.md.example +3 -0
  105. data/lib/kettle/jem/templates/MIT.md.example +21 -0
  106. data/lib/kettle/jem/templates/PolyForm-Noncommercial-1.0.0.md.example +131 -0
  107. data/lib/kettle/jem/templates/PolyForm-Small-Business-1.0.0.md.example +121 -0
  108. data/lib/kettle/jem/templates/README.md.example +544 -0
  109. data/lib/kettle/jem/templates/REEK +0 -0
  110. data/lib/kettle/jem/templates/RUBOCOP.md.example +78 -0
  111. data/lib/kettle/jem/templates/Rakefile.example +163 -0
  112. data/lib/kettle/jem/templates/SECURITY.md.example +21 -0
  113. data/lib/kettle/jem/templates/bin/setup.example +36 -0
  114. data/lib/kettle/jem/templates/certs/pboling.pem.example +27 -0
  115. data/lib/kettle/jem/templates/gem.gemspec.example +161 -0
  116. data/lib/kettle/jem/templates/gemfiles/modular/benchmark/r4/v0.5.gemfile.example +2 -0
  117. data/lib/kettle/jem/templates/gemfiles/modular/benchmark/vHEAD.gemfile.example +2 -0
  118. data/lib/kettle/jem/templates/gemfiles/modular/cgi/r4/v0.5.gemfile.example +1 -0
  119. data/lib/kettle/jem/templates/gemfiles/modular/cgi/vHEAD.gemfile.example +1 -0
  120. data/lib/kettle/jem/templates/gemfiles/modular/changelog.gemfile.example +6 -0
  121. data/lib/kettle/jem/templates/gemfiles/modular/coverage.gemfile.example +14 -0
  122. data/lib/kettle/jem/templates/gemfiles/modular/coverage_local.gemfile.example +34 -0
  123. data/lib/kettle/jem/templates/gemfiles/modular/debug.gemfile.example +22 -0
  124. data/lib/kettle/jem/templates/gemfiles/modular/documentation.gemfile.example +24 -0
  125. data/lib/kettle/jem/templates/gemfiles/modular/documentation_local.gemfile.example +20 -0
  126. data/lib/kettle/jem/templates/gemfiles/modular/erb/r2/v3.0.gemfile.example +1 -0
  127. data/lib/kettle/jem/templates/gemfiles/modular/erb/r2.3/default.gemfile.example +6 -0
  128. data/lib/kettle/jem/templates/gemfiles/modular/erb/r2.6/v2.2.gemfile.example +3 -0
  129. data/lib/kettle/jem/templates/gemfiles/modular/erb/r3/v5.0.gemfile.example +1 -0
  130. data/lib/kettle/jem/templates/gemfiles/modular/erb/r3.1/v4.0.gemfile.example +2 -0
  131. data/lib/kettle/jem/templates/gemfiles/modular/erb/r4/v5.0.gemfile.example +1 -0
  132. data/lib/kettle/jem/templates/gemfiles/modular/erb/vHEAD.gemfile.example +2 -0
  133. data/lib/kettle/jem/templates/gemfiles/modular/json/truffleruby_22_3.gemfile.example +6 -0
  134. data/lib/kettle/jem/templates/gemfiles/modular/json/truffleruby_23_0.gemfile.example +6 -0
  135. data/lib/kettle/jem/templates/gemfiles/modular/json/truffleruby_23_1.gemfile.example +6 -0
  136. data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/r2/v0.3.gemfile.example +2 -0
  137. data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/r2.4/v0.1.gemfile.example +3 -0
  138. data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/r3/v0.3.gemfile.example +2 -0
  139. data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/r4/v0.3.gemfile.example +2 -0
  140. data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/vHEAD.gemfile.example +2 -0
  141. data/lib/kettle/jem/templates/gemfiles/modular/optional.gemfile.example +16 -0
  142. data/lib/kettle/jem/templates/gemfiles/modular/recording/r2.3/recording.gemfile.example +16 -0
  143. data/lib/kettle/jem/templates/gemfiles/modular/recording/r2.4/recording.gemfile.example +18 -0
  144. data/lib/kettle/jem/templates/gemfiles/modular/recording/r2.5/recording.gemfile.example +16 -0
  145. data/lib/kettle/jem/templates/gemfiles/modular/recording/r3/recording.gemfile.example +16 -0
  146. data/lib/kettle/jem/templates/gemfiles/modular/recording/r4/recording.gemfile.example +16 -0
  147. data/lib/kettle/jem/templates/gemfiles/modular/recording/vHEAD.gemfile.example +2 -0
  148. data/lib/kettle/jem/templates/gemfiles/modular/rspec.gemfile.example +4 -0
  149. data/lib/kettle/jem/templates/gemfiles/modular/runtime_heads.gemfile.example +14 -0
  150. data/lib/kettle/jem/templates/gemfiles/modular/shunted.gemfile.example +21 -0
  151. data/lib/kettle/jem/templates/gemfiles/modular/stringio/r2/v3.0.gemfile.example +5 -0
  152. data/lib/kettle/jem/templates/gemfiles/modular/stringio/r2.4/v0.0.2.gemfile.example +4 -0
  153. data/lib/kettle/jem/templates/gemfiles/modular/stringio/r3/v3.0.gemfile.example +5 -0
  154. data/lib/kettle/jem/templates/gemfiles/modular/stringio/r4/v3.0.gemfile.example +5 -0
  155. data/lib/kettle/jem/templates/gemfiles/modular/stringio/vHEAD.gemfile.example +2 -0
  156. data/lib/kettle/jem/templates/gemfiles/modular/style.gemfile.example +33 -0
  157. data/lib/kettle/jem/templates/gemfiles/modular/style_local.gemfile.example +26 -0
  158. data/lib/kettle/jem/templates/gemfiles/modular/templating.gemfile.example +34 -0
  159. data/lib/kettle/jem/templates/gemfiles/modular/templating_local.gemfile.example +73 -0
  160. data/lib/kettle/jem/templates/gemfiles/modular/webrick/r4/v1.9.gemfile.example +1 -0
  161. data/lib/kettle/jem/templates/gemfiles/modular/webrick/vHEAD.gemfile.example +1 -0
  162. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r2/libs.gemfile.example +3 -0
  163. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r2.3/libs.gemfile.example +3 -0
  164. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r2.4/libs.gemfile.example +3 -0
  165. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r2.6/libs.gemfile.example +3 -0
  166. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r3/libs.gemfile.example +3 -0
  167. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r3.1/libs.gemfile.example +5 -0
  168. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r4/libs.gemfile.example +6 -0
  169. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/vHEAD.gemfile.example +6 -0
  170. data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs.gemfile.example +12 -0
  171. data/lib/kettle/jem/templates/lib/gem/version.rb.example +1 -0
  172. data/lib/kettle/jem/templates/lib/gem/version_gem.rb.example +1 -0
  173. data/lib/kettle/jem/templates/mise.toml.example +25 -0
  174. data/lib/kettle/jem/templates/mise.toml.no-osc.example +25 -0
  175. data/lib/kettle/jem/templates/parsers.toml.example +19 -0
  176. data/lib/kettle/jem/templates/readme/partials/basic_usage.md.example +28 -0
  177. data/lib/kettle/jem/templates/readme/partials/configuration.md.example +112 -0
  178. data/lib/kettle/jem/templates/readme/partials/synopsis.md.example +3 -0
  179. data/lib/kettle/jem/templates/shim/.github/workflows/current.yml.example +40 -0
  180. data/lib/kettle/jem/templates/shim/.rspec.example +5 -0
  181. data/lib/kettle/jem/templates/shim/.structuredmerge/kettle-jem.yml.example +28 -0
  182. data/lib/kettle/jem/templates/shim/CHANGELOG.md.example +9 -0
  183. data/lib/kettle/jem/templates/shim/Gemfile.example +14 -0
  184. data/lib/kettle/jem/templates/shim/README.md.example +25 -0
  185. data/lib/kettle/jem/templates/shim/Rakefile.example +8 -0
  186. data/lib/kettle/jem/templates/shim/gem.gemspec.example +45 -0
  187. data/lib/kettle/jem/templates/shim/lib/compat_require.rb.example +3 -0
  188. data/lib/kettle/jem/templates/shim/lib/entrypoint.rb.example +3 -0
  189. data/lib/kettle/jem/templates/shim/lib/version.rb.example +1 -0
  190. data/lib/kettle/jem/templates/shim/spec/shim_spec.rb.example +15 -0
  191. data/lib/kettle/jem/templates/shim/spec/spec_helper.rb.example +23 -0
  192. data/lib/kettle/jem/templates/sig/gem.rbs.example +1 -0
  193. data/lib/kettle/jem/templates/spec/README.md.example +28 -0
  194. data/lib/kettle/jem/templates/spec/spec_helper.rb.example +39 -0
  195. data/lib/kettle/jem/version.rb +5 -3
  196. data/lib/kettle/jem/version_gem.rb +8 -0
  197. data/lib/kettle/jem.rb +18540 -1517
  198. data/lib/kettle-jem.rb +2 -1
  199. data/sig/kettle/jem.rbs +8 -0
  200. data.tar.gz.sig +0 -0
  201. metadata +870 -32
  202. metadata.gz.sig +0 -0
data/exe/kettle-jem ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ script_basename = File.basename(__FILE__)
5
+ repo_lib = File.expand_path("../lib", __dir__)
6
+ $LOAD_PATH.unshift(repo_lib) if File.directory?(repo_lib) && !$LOAD_PATH.include?(repo_lib)
7
+
8
+ require "kettle/jem/version"
9
+
10
+ if ARGV.first == "version" || ARGV.any? { |arg| arg == "-v" || arg == "--version" }
11
+ puts Kettle::Jem::Version::VERSION
12
+ exit(0)
13
+ end
14
+
15
+ require "kettle-jem"
16
+
17
+ machine_output = ARGV.any? { |arg| arg == "--json" || arg.start_with?("--events") }
18
+ puts "== #{script_basename} v#{Kettle::Jem::Version::VERSION} ==" if ARGV.include?("--verbose") && !machine_output
19
+
20
+ exit(Kettle::Jem::CLI.run(ARGV, env: ENV, out: $stdout, err: $stderr))
@@ -0,0 +1,334 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "fileutils"
5
+ require "optparse"
6
+
7
+ module Kettle
8
+ module Jem
9
+ module CLI
10
+ USAGE = <<~USAGE
11
+ Usage:
12
+ kettle-jem [PROJECT_ROOT] [--accept-config] [--bootstrap-mode] [--quiet|--verbose]
13
+ kettle-jem setup [PROJECT_ROOT] [--accept-config] [--bootstrap-mode] [--quiet|--verbose]
14
+ kettle-jem prepare [PROJECT_ROOT] [--json|--events[=TYPE,...]] [--report PATH] [--accept|--force] [--quiet|--verbose]
15
+ kettle-jem plan [PROJECT_ROOT] [--json|--events[=TYPE,...]] [--report PATH] [--accept|--force|--interactive] [--failure-mode MODE] [--prompt-answer ID=ACTION]
16
+ kettle-jem apply [PROJECT_ROOT] [--json|--events[=TYPE,...]] [--report PATH] [--accept|--force|--interactive] [--failure-mode MODE] [--prompt-answer ID=ACTION]
17
+ kettle-jem template [PROJECT_ROOT] [--json|--events[=TYPE,...]] [--report PATH] [--accept|--force|--interactive] [--failure-mode MODE] [--prompt-answer ID=ACTION]
18
+ kettle-jem install [PROJECT_ROOT] [--json|--events[=TYPE,...]] [--report PATH] [--accept|--force|--interactive] [--failure-mode MODE] [--prompt-answer ID=ACTION]
19
+ kettle-jem manifest [PROJECT_ROOT] [--json]
20
+ kettle-jem selftest [PROJECT_ROOT] [--json] [--report PATH] [--destination PATH] [--template-root PATH] [--selftest-output PATH]
21
+ kettle-jem version
22
+ USAGE
23
+
24
+ module_function
25
+
26
+ def run(argv = ARGV, env: ENV, out: $stdout, err: $stderr)
27
+ command, args = normalize_command(argv)
28
+ return print_help(out) if command == "help"
29
+ return print_version(out) if command == "version"
30
+
31
+ options = parse_options(args)
32
+ project_root = File.expand_path(options.fetch(:project_root) || Dir.pwd)
33
+ ensure_not_running_from_own_context!(command, project_root)
34
+ print_debug_snapshot(command, project_root: project_root, env: env, err: err) if debug_enabled?(env)
35
+ options[:event_io] = out
36
+ result = execute(command, project_root: project_root, env: env, options: options)
37
+ write_report(options[:report_path], result) if options[:report_path]
38
+ print_result(command, result, options: options, out: out)
39
+ 0
40
+ rescue OptionParser::ParseError, ArgumentError => error
41
+ err.puts(error.message)
42
+ err.puts(USAGE)
43
+ 2
44
+ rescue => error
45
+ err.puts("#{error.class}: #{error.message}")
46
+ err.puts(error.backtrace.join("\n")) if debug_enabled?(env)
47
+ 1
48
+ end
49
+
50
+ def normalize_command(argv)
51
+ args = Array(argv).dup
52
+ command = args.shift
53
+ return ["help", []] if command == "help" || command == "--help" || command == "-h"
54
+ return ["setup", []] if command.nil?
55
+
56
+ unless command_allowed?(command)
57
+ args.unshift(command)
58
+ command = "setup"
59
+ end
60
+ command = "version" if command == "version" || command == "--version" || command == "-v"
61
+ raise ArgumentError, "Unsupported kettle-jem command #{command.inspect}" unless command_allowed?(command)
62
+
63
+ [command, args]
64
+ end
65
+
66
+ def command_allowed?(command)
67
+ %w[setup prepare plan apply template install manifest selftest help version].include?(command)
68
+ end
69
+
70
+ def parse_options(args)
71
+ options = {
72
+ json: false,
73
+ events: false,
74
+ run_options: {}
75
+ }
76
+ parser = OptionParser.new do |opts|
77
+ opts.banner = USAGE
78
+ opts.on("--json", "Print the full machine-readable result as JSON.") { options[:json] = true }
79
+ opts.on("--events[=TYPES]", "Print newline-delimited JSON progress events. Optional comma-separated TYPES filter.") do |value|
80
+ options[:events] = true
81
+ options[:event_types] = value if value
82
+ end
83
+ opts.on("--report PATH", "Write the full machine-readable result to PATH as JSON.") do |path|
84
+ options[:report_path] = path
85
+ end
86
+ opts.on("--accept", "Use non-interactive default decisions.") { options[:run_options][:accept] = true }
87
+ opts.on("--force", "Alias for --accept.") { options[:run_options][:force] = true }
88
+ opts.on("--interactive", "Use interactive decision mode when supported.") do
89
+ options[:run_options][:interactive] = true
90
+ end
91
+ opts.on("--quiet", "Suppress normal text output.") do
92
+ options[:run_options][:quiet] = true
93
+ end
94
+ opts.on("--verbose", "Request verbose diagnostics where supported.") do
95
+ options[:run_options][:verbose] = true
96
+ end
97
+ opts.on("--failure-mode MODE", "Set the template failure mode.") do |mode|
98
+ options[:run_options][:failure_mode] = mode
99
+ end
100
+ opts.on("--prompt-answer ID=ACTION", "Answer an interactive decision prompt.") do |value|
101
+ id, action = value.to_s.split("=", 2)
102
+ raise OptionParser::InvalidArgument, "--prompt-answer must use ID=ACTION" if id.to_s.empty? || action.to_s.empty?
103
+
104
+ (options[:run_options][:prompt_answers] ||= {})[id] = action
105
+ options[:run_options][:interactive] = true
106
+ end
107
+ opts.on("--allowed VALUE", "Set the env-file change policy.") do |value|
108
+ options[:run_options][:allowed] = value
109
+ end
110
+ opts.on("--hook-templates VALUE", "Set hook template handling.") do |value|
111
+ options[:run_options][:hook_templates] = value
112
+ end
113
+ opts.on("--hook_templates VALUE", "Alias for --hook-templates.") do |value|
114
+ options[:run_options][:hook_templates] = value
115
+ end
116
+ opts.on("--git-drivers VALUE", "Set Git diff/merge driver setup handling.") do |value|
117
+ options[:run_options][:git_drivers] = value
118
+ end
119
+ opts.on("--git_drivers VALUE", "Alias for --git-drivers.") do |value|
120
+ options[:run_options][:git_drivers] = value
121
+ end
122
+ opts.on("--dry-run", "Plan supported post-template actions without applying them.") do
123
+ options[:run_options][:dry_run] = true
124
+ end
125
+ opts.on("--check", "Verify supported post-template setup without changing files.") do
126
+ options[:run_options][:git_drivers] = "check"
127
+ end
128
+ opts.on("--undo", "Undo supported post-template setup.") do
129
+ options[:run_options][:git_drivers] = "undo"
130
+ end
131
+ opts.on("--only PATHS", "Restrict templating to comma-separated paths or patterns.") do |value|
132
+ (options[:run_options][:only] ||= []) << value
133
+ end
134
+ opts.on("--include PATHS", "Include comma-separated paths or patterns.") do |value|
135
+ (options[:run_options][:include] ||= []) << value
136
+ end
137
+ opts.on("--skip-commit", "Skip bootstrap commit behavior.") do
138
+ options[:run_options][:skip_commit] = true
139
+ end
140
+ opts.on("--skip-drift-check", "Skip post-apply duplicate drift checking.") do
141
+ options[:run_options][:skip_drift_check] = true
142
+ end
143
+ opts.on("--skip-duplicate-drift", "Alias for --skip-drift-check.") do
144
+ options[:run_options][:skip_drift_check] = true
145
+ end
146
+ opts.on("--skip-rubocop-gradual", "Skip post-template RuboCop Gradual autocorrect.") do
147
+ options[:run_options][:skip_rubocop_gradual] = true
148
+ end
149
+ opts.on("--skip-binstubs", "Skip post-template curated Bundler binstub generation.") do
150
+ options[:run_options][:skip_binstubs] = true
151
+ end
152
+ opts.on("--checksums VALUE", "Set checksum skip mode: template, dest, ignore-template, ignore-dest, or off.") do |value|
153
+ options[:run_options][:checksums] = value
154
+ end
155
+ opts.on("--ignore-checksums", "Alias for --checksums off.") do
156
+ options[:run_options][:checksums] = "off"
157
+ end
158
+ opts.on("--accept-config", "Accept first-run template config bootstrap.") do
159
+ options[:run_options][:accept_config] = true
160
+ end
161
+ opts.on("--bootstrap-mode", "Force first-run bootstrap mode.") do
162
+ options[:run_options][:bootstrap_mode] = true
163
+ end
164
+ opts.on("--template-profile PROFILE", "Use a packaged template profile.") do |value|
165
+ options[:run_options][:template_profile] = value
166
+ end
167
+ opts.on("--shimmed-gem GEM", "Runtime gem required by the shim profile.") do |value|
168
+ options[:run_options][:shimmed_gem] = value
169
+ end
170
+ opts.on("--shimmed-require REQUIRE", "Require path loaded by the shim profile.") do |value|
171
+ options[:run_options][:shimmed_require] = value
172
+ end
173
+ opts.on("--destination PATH", "Selftest destination root.") do |path|
174
+ options[:destination_root] = path
175
+ end
176
+ opts.on("--template-root PATH", "Selftest template root.") do |path|
177
+ options[:template_root] = path
178
+ end
179
+ opts.on("--selftest-output PATH", "Selftest output root.") do |path|
180
+ options[:selftest_output_root] = path
181
+ end
182
+ opts.on("--min-divergence-threshold PERCENT", "Fail selftest when divergence exceeds PERCENT.") do |value|
183
+ options[:min_divergence_threshold] = Float(value)
184
+ end
185
+ end
186
+ remaining = parser.parse(args)
187
+ raise ArgumentError, "Expected at most one PROJECT_ROOT" if remaining.length > 1
188
+ raise OptionParser::InvalidOption, "--events cannot be combined with --json" if options[:events] && options[:json]
189
+
190
+ options[:project_root] = remaining.first
191
+ options
192
+ end
193
+
194
+ def execute(command, project_root:, env:, options:)
195
+ run_options = options.fetch(:run_options)
196
+ if options[:events]
197
+ run_options[:event_stream] = Kettle::Jem.event_stream(
198
+ options.fetch(:event_io),
199
+ types: options[:event_types]
200
+ )
201
+ end
202
+ case command
203
+ when "setup"
204
+ Kettle::Jem.setup_project(project_root, env: env, run_options: run_options)
205
+ when "prepare"
206
+ Kettle::Jem::Tasks::PrepareTask.run(project_root: project_root, env: env, run_options: run_options)
207
+ when "plan"
208
+ Kettle::Jem.plan_project(project_root, env: env, run_options: run_options)
209
+ when "apply"
210
+ Kettle::Jem.apply_project(project_root, env: env, run_options: run_options)
211
+ when "template"
212
+ if scoped_template_run?(run_options)
213
+ Kettle::Jem::Tasks::TemplateTask.run(project_root: project_root, env: env, run_options: run_options)
214
+ else
215
+ Kettle::Jem::Tasks::InstallTask.run(project_root: project_root, env: env, run_options: run_options)
216
+ end
217
+ when "install"
218
+ Kettle::Jem::Tasks::InstallTask.run(project_root: project_root, env: env, run_options: run_options)
219
+ when "manifest"
220
+ Kettle::Jem.template_manifest(project_root: project_root)
221
+ when "selftest"
222
+ Kettle::Jem::Tasks::SelfTestTask.run(
223
+ project_root: project_root,
224
+ destination_root: options[:destination_root] || project_root,
225
+ template_root: options[:template_root],
226
+ output_root: options[:selftest_output_root],
227
+ min_divergence_threshold: options[:min_divergence_threshold]
228
+ )
229
+ else
230
+ raise ArgumentError, "Unsupported kettle-jem command #{command.inspect}"
231
+ end
232
+ end
233
+
234
+ def scoped_template_run?(run_options)
235
+ run_options = run_options.to_h
236
+ run_options.key?(:only) || run_options.key?(:include)
237
+ end
238
+
239
+ def ensure_not_running_from_own_context!(command, project_root)
240
+ return if %w[help version].include?(command)
241
+
242
+ current_root = canonical_path(Dir.pwd)
243
+ own_root = canonical_path(File.expand_path("../../..", __dir__))
244
+ target_root = canonical_path(project_root)
245
+ return unless current_root == own_root && target_root != own_root
246
+
247
+ raise ArgumentError,
248
+ "Refusing to run kettle-jem from its own project root against #{project_root}; " \
249
+ "run from the destination repository so its environment is loaded, or target kettle-jem itself."
250
+ end
251
+
252
+ def canonical_path(path)
253
+ File.realpath(path)
254
+ rescue Errno::ENOENT
255
+ File.expand_path(path)
256
+ end
257
+
258
+ def print_result(command, result, options:, out:)
259
+ return if options[:events]
260
+ return if options.fetch(:run_options, {})[:quiet] && !options[:json]
261
+
262
+ if options[:json]
263
+ out.puts(JSON.pretty_generate(result))
264
+ return
265
+ end
266
+
267
+ case command
268
+ when "setup"
269
+ out.puts("setup: #{result.fetch(:setup_status)}")
270
+ result.fetch(:changed_files, []).each { |path| out.puts(" #{path}") }
271
+ result.fetch(:diagnostics, []).each do |diagnostic|
272
+ message = diagnostic.is_a?(Hash) ? diagnostic[:message] || diagnostic["message"] : diagnostic
273
+ out.puts(" #{message}") unless message.to_s.empty?
274
+ end
275
+ when "manifest"
276
+ entries = result.fetch(:entries, [])
277
+ out.puts("template manifest: #{entries.length} entr#{(entries.length == 1) ? "y" : "ies"}")
278
+ when "selftest"
279
+ comparison = result.fetch(:comparison, {})
280
+ divergent = comparison.fetch(:changed, []).size +
281
+ comparison.fetch(:added, []).size +
282
+ comparison.fetch(:removed, []).size
283
+ out.puts("selftest: #{divergent} divergent file#{"s" unless divergent == 1}")
284
+ out.puts(" report: #{result.fetch(:report_path)}") if result[:report_path]
285
+ else
286
+ changed_files = result.fetch(:changed_files, [])
287
+ out.puts("#{result.fetch(:mode)}: #{changed_files.length} changed file#{"s" unless changed_files.length == 1}")
288
+ changed_files.each { |path| out.puts(" #{path}") }
289
+ Array(result[:warnings]).map(&:to_s).reject(&:empty?).uniq.each do |warning|
290
+ out.puts(" warning: #{warning}")
291
+ end
292
+ end
293
+ end
294
+
295
+ def write_report(path, result)
296
+ FileUtils.mkdir_p(File.dirname(File.expand_path(path)))
297
+ File.write(path, "#{JSON.pretty_generate(result)}\n")
298
+ end
299
+
300
+ def print_debug_snapshot(command, project_root:, env:, err:)
301
+ err.puts("[kettle-jem] DEBUG: early environment snapshot")
302
+ err.puts(" command=#{command.inspect}")
303
+ err.puts(" project_root=#{project_root.inspect}")
304
+ %w[DEBUG KETTLE_JEM_DEBUG KETTLE_DEV_DEBUG KETTLE_DEV_DEV BUNDLE_GEMFILE BUNDLE_PATH GEM_HOME GEM_PATH RUBYOPT RUBYLIB PWD].each do |key|
305
+ err.puts(" #{key}=#{env_value(env, key).inspect}")
306
+ end
307
+ end
308
+
309
+ def debug_enabled?(env)
310
+ %w[DEBUG KETTLE_JEM_DEBUG KETTLE_DEV_DEBUG].any? { |key| env_true?(env_value(env, key)) }
311
+ end
312
+
313
+ def env_true?(value)
314
+ /\A(?:true|t|yes|y|on|1)\z/i.match?(value.to_s.strip)
315
+ end
316
+
317
+ def env_value(env, key)
318
+ env.fetch(key, nil)
319
+ rescue KeyError
320
+ nil
321
+ end
322
+
323
+ def print_help(out)
324
+ out.puts(USAGE)
325
+ 0
326
+ end
327
+
328
+ def print_version(out)
329
+ out.puts(Kettle::Jem::Version::VERSION)
330
+ 0
331
+ end
332
+ end
333
+ end
334
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kettle
4
+ module Jem
5
+ # Recognizes MIT license text and extracts copyright lines from its preamble.
6
+ class LicenseTxtMigrator
7
+ MIT_PHRASES = [
8
+ "permission is hereby granted",
9
+ "without restriction"
10
+ ].freeze
11
+
12
+ def initialize(content)
13
+ @content = content.to_s
14
+ @analysis = Ast::Merge::Text::FileAnalysis.new(@content)
15
+ end
16
+
17
+ def mit_license?
18
+ text = @analysis.searchable_text.downcase
19
+ MIT_PHRASES.all? { |phrase| text.include?(phrase) }
20
+ end
21
+
22
+ def copyright_lines
23
+ lines = @analysis.statements.select { |node| node.is_a?(Ast::Merge::Text::LineNode) }
24
+ boundary = lines.index do |node|
25
+ @analysis.searchable_text(nodes: [node]).downcase.include?("permission is hereby granted")
26
+ end
27
+ lines.first(boundary || lines.length)
28
+ .select { |node| node.content.match?(/copyright/i) }
29
+ .map(&:content)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :kettle do
4
+ namespace :jem do
5
+ desc "Prepare the current project to run kettle-jem inside its own bundle"
6
+ task :install do
7
+ Kettle::Jem::Tasks::InstallTask.run
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :kettle do
4
+ namespace :jem do
5
+ desc "Prepare .kettle-jem.yml for templating by validating project/template facts"
6
+ task :prepare do
7
+ Kettle::Jem::Tasks::PrepareTask.run
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :kettle do
4
+ namespace :jem do
5
+ desc "Validate a destination project against the kettle-jem template"
6
+ task :selftest do
7
+ Kettle::Jem::Tasks::SelfTestTask.run
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :kettle do
4
+ namespace :jem do
5
+ desc "Apply the kettle-jem template to the current project"
6
+ task :template do
7
+ Kettle::Jem::Tasks::TemplateTask.run
8
+ end
9
+ end
10
+ end