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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +1505 -0
- data/LICENSE.md +13 -0
- data/README.md +794 -0
- data/exe/kettle-jem +20 -0
- data/lib/kettle/jem/cli.rb +334 -0
- data/lib/kettle/jem/license_txt_migrator.rb +33 -0
- data/lib/kettle/jem/rakelib/install.rake +10 -0
- data/lib/kettle/jem/rakelib/prepare.rake +10 -0
- data/lib/kettle/jem/rakelib/selftest.rake +10 -0
- data/lib/kettle/jem/rakelib/template.rake +10 -0
- data/lib/kettle/jem/tasks/install_task.rb +2178 -0
- data/lib/kettle/jem/tasks/prepare_task.rb +274 -0
- data/lib/kettle/jem/tasks/self_test_task.rb +256 -0
- data/lib/kettle/jem/tasks/template_task.rb +124 -0
- data/lib/kettle/jem/tasks.rb +6 -0
- data/lib/kettle/jem/templates/.aiignore.example +19 -0
- data/lib/kettle/jem/templates/.config/mise/env.sh +26 -0
- data/lib/kettle/jem/templates/.devcontainer/apt-install/devcontainer-feature.json.example +9 -0
- data/lib/kettle/jem/templates/.devcontainer/apt-install/install.sh.example +19 -0
- data/lib/kettle/jem/templates/.devcontainer/devcontainer.json.example +27 -0
- data/lib/kettle/jem/templates/.devcontainer/scripts/setup-tree-sitter.sh.example +210 -0
- data/lib/kettle/jem/templates/.env.local.example +67 -0
- data/lib/kettle/jem/templates/.envrc.example +4 -0
- data/lib/kettle/jem/templates/.envrc.no-osc.example +4 -0
- data/lib/kettle/jem/templates/.gemrc.example +1 -0
- data/lib/kettle/jem/templates/.git-hooks/commit-msg.example +60 -0
- data/lib/kettle/jem/templates/.git-hooks/commit-subjects-goalie.txt.example +8 -0
- data/lib/kettle/jem/templates/.git-hooks/footer-template.erb.txt.example +16 -0
- data/lib/kettle/jem/templates/.git-hooks/prepare-commit-msg.example +9 -0
- data/lib/kettle/jem/templates/.github/.codecov.yml.example +14 -0
- data/lib/kettle/jem/templates/.github/FUNDING.yml.example +10 -0
- data/lib/kettle/jem/templates/.github/FUNDING.yml.no-osc.example +10 -0
- data/lib/kettle/jem/templates/.github/copilot_instructions.md.example +1 -0
- data/lib/kettle/jem/templates/.github/dependabot.yml.example +13 -0
- data/lib/kettle/jem/templates/.github/workflows/auto-assign.yml.example +21 -0
- data/lib/kettle/jem/templates/.github/workflows/coverage.yml.example +102 -0
- data/lib/kettle/jem/templates/.github/workflows/current.yml.example +86 -0
- data/lib/kettle/jem/templates/.github/workflows/dep-heads.yml.example +249 -0
- data/lib/kettle/jem/templates/.github/workflows/dependency-review.yml.example +22 -0
- data/lib/kettle/jem/templates/.github/workflows/discord-notifier.yml.example +39 -0
- data/lib/kettle/jem/templates/.github/workflows/framework-ci.yml.example +81 -0
- data/lib/kettle/jem/templates/.github/workflows/heads.yml.example +212 -0
- data/lib/kettle/jem/templates/.github/workflows/jruby-10.0.yml.example +91 -0
- data/lib/kettle/jem/templates/.github/workflows/jruby-9.1.yml.example +90 -0
- data/lib/kettle/jem/templates/.github/workflows/jruby-9.2.yml.example +95 -0
- data/lib/kettle/jem/templates/.github/workflows/jruby-9.3.yml.example +95 -0
- data/lib/kettle/jem/templates/.github/workflows/jruby-9.4.yml.example +95 -0
- data/lib/kettle/jem/templates/.github/workflows/jruby.yml.example +91 -0
- data/lib/kettle/jem/templates/.github/workflows/license-eye.yml.example +43 -0
- data/lib/kettle/jem/templates/.github/workflows/locked_deps.yml.example +89 -0
- data/lib/kettle/jem/templates/.github/workflows/opencollective.yml.example +44 -0
- data/lib/kettle/jem/templates/.github/workflows/ruby-2.3.yml.example +80 -0
- data/lib/kettle/jem/templates/.github/workflows/ruby-2.4.yml.example +84 -0
- data/lib/kettle/jem/templates/.github/workflows/ruby-2.5.yml.example +84 -0
- data/lib/kettle/jem/templates/.github/workflows/ruby-2.6.yml.example +84 -0
- data/lib/kettle/jem/templates/.github/workflows/ruby-2.7.yml.example +84 -0
- data/lib/kettle/jem/templates/.github/workflows/ruby-3.0.yml.example +85 -0
- data/lib/kettle/jem/templates/.github/workflows/ruby-3.1.yml.example +83 -0
- data/lib/kettle/jem/templates/.github/workflows/ruby-3.2.yml.example +80 -0
- data/lib/kettle/jem/templates/.github/workflows/ruby-3.3.yml.example +80 -0
- data/lib/kettle/jem/templates/.github/workflows/ruby-3.4.yml.example +80 -0
- data/lib/kettle/jem/templates/.github/workflows/style.yml.example +78 -0
- data/lib/kettle/jem/templates/.github/workflows/templating.yml.example +86 -0
- data/lib/kettle/jem/templates/.github/workflows/truffle.yml.example +91 -0
- data/lib/kettle/jem/templates/.github/workflows/truffleruby-22.3.yml.example +92 -0
- data/lib/kettle/jem/templates/.github/workflows/truffleruby-23.0.yml.example +92 -0
- data/lib/kettle/jem/templates/.github/workflows/truffleruby-23.1.yml.example +78 -0
- data/lib/kettle/jem/templates/.github/workflows/truffleruby-24.2.yml.example +91 -0
- data/lib/kettle/jem/templates/.github/workflows/truffleruby-25.0.yml.example +96 -0
- data/lib/kettle/jem/templates/.github/workflows/truffleruby-33.0.yml.example +91 -0
- data/lib/kettle/jem/templates/.github/workflows/unlocked_deps.yml.example +93 -0
- data/lib/kettle/jem/templates/.gitignore.example +53 -0
- data/lib/kettle/jem/templates/.gitlab-ci.yml.example +134 -0
- data/lib/kettle/jem/templates/.idea/.gitignore.example +44 -0
- data/lib/kettle/jem/templates/.licenserc.yaml.example +8 -0
- data/lib/kettle/jem/templates/.opencollective.yml.example +3 -0
- data/lib/kettle/jem/templates/.qlty/qlty.toml.example +79 -0
- data/lib/kettle/jem/templates/.rspec.example +10 -0
- data/lib/kettle/jem/templates/.rubocop.example +1 -0
- data/lib/kettle/jem/templates/.rubocop.yml.example +47 -0
- data/lib/kettle/jem/templates/.rubocop_rspec.yml.example +55 -0
- data/lib/kettle/jem/templates/.simplecov.example +18 -0
- data/lib/kettle/jem/templates/.structuredmerge/git-drivers.toml.example +86 -0
- data/lib/kettle/jem/templates/.structuredmerge/kettle-jem.yml.example +519 -0
- data/lib/kettle/jem/templates/.yard-lint.yml.example +54 -0
- data/lib/kettle/jem/templates/.yardignore.example +13 -0
- data/lib/kettle/jem/templates/.yardopts.example +14 -0
- data/lib/kettle/jem/templates/AGENTS.md.example +283 -0
- data/lib/kettle/jem/templates/AGPL-3.0-only.md.example +235 -0
- data/lib/kettle/jem/templates/Appraisal.root.gemfile.example +26 -0
- data/lib/kettle/jem/templates/Appraisals.example +150 -0
- data/lib/kettle/jem/templates/Big-Time-Public-License.md.example +99 -0
- data/lib/kettle/jem/templates/CHANGELOG.md.example +33 -0
- data/lib/kettle/jem/templates/CHANGELOG.transfer.md +109 -0
- data/lib/kettle/jem/templates/CITATION.cff.example +20 -0
- data/lib/kettle/jem/templates/CODE_OF_CONDUCT.md.example +134 -0
- data/lib/kettle/jem/templates/CONTRIBUTING.md.example +292 -0
- data/lib/kettle/jem/templates/FUNDING.md.example +74 -0
- data/lib/kettle/jem/templates/FUNDING.md.no-osc.example +63 -0
- data/lib/kettle/jem/templates/Gemfile.example +52 -0
- data/lib/kettle/jem/templates/IRP.md.example +34 -0
- data/lib/kettle/jem/templates/LICENSE.md.example +3 -0
- data/lib/kettle/jem/templates/MIT.md.example +21 -0
- data/lib/kettle/jem/templates/PolyForm-Noncommercial-1.0.0.md.example +131 -0
- data/lib/kettle/jem/templates/PolyForm-Small-Business-1.0.0.md.example +121 -0
- data/lib/kettle/jem/templates/README.md.example +544 -0
- data/lib/kettle/jem/templates/REEK +0 -0
- data/lib/kettle/jem/templates/RUBOCOP.md.example +78 -0
- data/lib/kettle/jem/templates/Rakefile.example +163 -0
- data/lib/kettle/jem/templates/SECURITY.md.example +21 -0
- data/lib/kettle/jem/templates/bin/setup.example +36 -0
- data/lib/kettle/jem/templates/certs/pboling.pem.example +27 -0
- data/lib/kettle/jem/templates/gem.gemspec.example +161 -0
- data/lib/kettle/jem/templates/gemfiles/modular/benchmark/r4/v0.5.gemfile.example +2 -0
- data/lib/kettle/jem/templates/gemfiles/modular/benchmark/vHEAD.gemfile.example +2 -0
- data/lib/kettle/jem/templates/gemfiles/modular/cgi/r4/v0.5.gemfile.example +1 -0
- data/lib/kettle/jem/templates/gemfiles/modular/cgi/vHEAD.gemfile.example +1 -0
- data/lib/kettle/jem/templates/gemfiles/modular/changelog.gemfile.example +6 -0
- data/lib/kettle/jem/templates/gemfiles/modular/coverage.gemfile.example +14 -0
- data/lib/kettle/jem/templates/gemfiles/modular/coverage_local.gemfile.example +34 -0
- data/lib/kettle/jem/templates/gemfiles/modular/debug.gemfile.example +22 -0
- data/lib/kettle/jem/templates/gemfiles/modular/documentation.gemfile.example +24 -0
- data/lib/kettle/jem/templates/gemfiles/modular/documentation_local.gemfile.example +20 -0
- data/lib/kettle/jem/templates/gemfiles/modular/erb/r2/v3.0.gemfile.example +1 -0
- data/lib/kettle/jem/templates/gemfiles/modular/erb/r2.3/default.gemfile.example +6 -0
- data/lib/kettle/jem/templates/gemfiles/modular/erb/r2.6/v2.2.gemfile.example +3 -0
- data/lib/kettle/jem/templates/gemfiles/modular/erb/r3/v5.0.gemfile.example +1 -0
- data/lib/kettle/jem/templates/gemfiles/modular/erb/r3.1/v4.0.gemfile.example +2 -0
- data/lib/kettle/jem/templates/gemfiles/modular/erb/r4/v5.0.gemfile.example +1 -0
- data/lib/kettle/jem/templates/gemfiles/modular/erb/vHEAD.gemfile.example +2 -0
- data/lib/kettle/jem/templates/gemfiles/modular/json/truffleruby_22_3.gemfile.example +6 -0
- data/lib/kettle/jem/templates/gemfiles/modular/json/truffleruby_23_0.gemfile.example +6 -0
- data/lib/kettle/jem/templates/gemfiles/modular/json/truffleruby_23_1.gemfile.example +6 -0
- data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/r2/v0.3.gemfile.example +2 -0
- data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/r2.4/v0.1.gemfile.example +3 -0
- data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/r3/v0.3.gemfile.example +2 -0
- data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/r4/v0.3.gemfile.example +2 -0
- data/lib/kettle/jem/templates/gemfiles/modular/mutex_m/vHEAD.gemfile.example +2 -0
- data/lib/kettle/jem/templates/gemfiles/modular/optional.gemfile.example +16 -0
- data/lib/kettle/jem/templates/gemfiles/modular/recording/r2.3/recording.gemfile.example +16 -0
- data/lib/kettle/jem/templates/gemfiles/modular/recording/r2.4/recording.gemfile.example +18 -0
- data/lib/kettle/jem/templates/gemfiles/modular/recording/r2.5/recording.gemfile.example +16 -0
- data/lib/kettle/jem/templates/gemfiles/modular/recording/r3/recording.gemfile.example +16 -0
- data/lib/kettle/jem/templates/gemfiles/modular/recording/r4/recording.gemfile.example +16 -0
- data/lib/kettle/jem/templates/gemfiles/modular/recording/vHEAD.gemfile.example +2 -0
- data/lib/kettle/jem/templates/gemfiles/modular/rspec.gemfile.example +4 -0
- data/lib/kettle/jem/templates/gemfiles/modular/runtime_heads.gemfile.example +14 -0
- data/lib/kettle/jem/templates/gemfiles/modular/shunted.gemfile.example +21 -0
- data/lib/kettle/jem/templates/gemfiles/modular/stringio/r2/v3.0.gemfile.example +5 -0
- data/lib/kettle/jem/templates/gemfiles/modular/stringio/r2.4/v0.0.2.gemfile.example +4 -0
- data/lib/kettle/jem/templates/gemfiles/modular/stringio/r3/v3.0.gemfile.example +5 -0
- data/lib/kettle/jem/templates/gemfiles/modular/stringio/r4/v3.0.gemfile.example +5 -0
- data/lib/kettle/jem/templates/gemfiles/modular/stringio/vHEAD.gemfile.example +2 -0
- data/lib/kettle/jem/templates/gemfiles/modular/style.gemfile.example +33 -0
- data/lib/kettle/jem/templates/gemfiles/modular/style_local.gemfile.example +26 -0
- data/lib/kettle/jem/templates/gemfiles/modular/templating.gemfile.example +34 -0
- data/lib/kettle/jem/templates/gemfiles/modular/templating_local.gemfile.example +73 -0
- data/lib/kettle/jem/templates/gemfiles/modular/webrick/r4/v1.9.gemfile.example +1 -0
- data/lib/kettle/jem/templates/gemfiles/modular/webrick/vHEAD.gemfile.example +1 -0
- data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r2/libs.gemfile.example +3 -0
- data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r2.3/libs.gemfile.example +3 -0
- data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r2.4/libs.gemfile.example +3 -0
- data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r2.6/libs.gemfile.example +3 -0
- data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r3/libs.gemfile.example +3 -0
- data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r3.1/libs.gemfile.example +5 -0
- data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/r4/libs.gemfile.example +6 -0
- data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs/vHEAD.gemfile.example +6 -0
- data/lib/kettle/jem/templates/gemfiles/modular/x_std_libs.gemfile.example +12 -0
- data/lib/kettle/jem/templates/lib/gem/version.rb.example +1 -0
- data/lib/kettle/jem/templates/lib/gem/version_gem.rb.example +1 -0
- data/lib/kettle/jem/templates/mise.toml.example +25 -0
- data/lib/kettle/jem/templates/mise.toml.no-osc.example +25 -0
- data/lib/kettle/jem/templates/parsers.toml.example +19 -0
- data/lib/kettle/jem/templates/readme/partials/basic_usage.md.example +28 -0
- data/lib/kettle/jem/templates/readme/partials/configuration.md.example +112 -0
- data/lib/kettle/jem/templates/readme/partials/synopsis.md.example +3 -0
- data/lib/kettle/jem/templates/shim/.github/workflows/current.yml.example +40 -0
- data/lib/kettle/jem/templates/shim/.rspec.example +5 -0
- data/lib/kettle/jem/templates/shim/.structuredmerge/kettle-jem.yml.example +28 -0
- data/lib/kettle/jem/templates/shim/CHANGELOG.md.example +9 -0
- data/lib/kettle/jem/templates/shim/Gemfile.example +14 -0
- data/lib/kettle/jem/templates/shim/README.md.example +25 -0
- data/lib/kettle/jem/templates/shim/Rakefile.example +8 -0
- data/lib/kettle/jem/templates/shim/gem.gemspec.example +45 -0
- data/lib/kettle/jem/templates/shim/lib/compat_require.rb.example +3 -0
- data/lib/kettle/jem/templates/shim/lib/entrypoint.rb.example +3 -0
- data/lib/kettle/jem/templates/shim/lib/version.rb.example +1 -0
- data/lib/kettle/jem/templates/shim/spec/shim_spec.rb.example +15 -0
- data/lib/kettle/jem/templates/shim/spec/spec_helper.rb.example +23 -0
- data/lib/kettle/jem/templates/sig/gem.rbs.example +1 -0
- data/lib/kettle/jem/templates/spec/README.md.example +28 -0
- data/lib/kettle/jem/templates/spec/spec_helper.rb.example +39 -0
- data/lib/kettle/jem/version.rb +5 -3
- data/lib/kettle/jem/version_gem.rb +8 -0
- data/lib/kettle/jem.rb +18540 -1517
- data/lib/kettle-jem.rb +2 -1
- data/sig/kettle/jem.rbs +8 -0
- data.tar.gz.sig +0 -0
- metadata +870 -32
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler"
|
|
4
|
+
require "kettle/dev/lockfile_reset"
|
|
5
|
+
require "pathname"
|
|
6
|
+
require "shellwords"
|
|
7
|
+
|
|
8
|
+
module Kettle
|
|
9
|
+
module Jem
|
|
10
|
+
module Tasks
|
|
11
|
+
module PrepareTask
|
|
12
|
+
PREPARE_ONLY_PATHS = [
|
|
13
|
+
Kettle::Jem::KETTLE_CONFIG_PATH,
|
|
14
|
+
"Gemfile",
|
|
15
|
+
# Gemfile evaluates these fragments immediately. Preparing only the
|
|
16
|
+
# templating pair leaves a newly migrated project with references to
|
|
17
|
+
# files that do not exist yet, so Bundler cannot perform the bootstrap.
|
|
18
|
+
"gemfiles/modular/**",
|
|
19
|
+
"mise.toml"
|
|
20
|
+
].freeze
|
|
21
|
+
CRITICAL_TEMPLATING_GEMS = %w[nomono].freeze
|
|
22
|
+
LOCKED_TEMPLATING_GEMS = %w[tree_sitter_language_pack].freeze
|
|
23
|
+
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
def run(project_root: Dir.pwd, env: ENV, run_options: {}, command_runner: Kettle::Jem::Tasks::InstallTask.method(:run_system_command))
|
|
27
|
+
effective_run_options = Kettle::Jem::Tasks::TemplateTask.env_run_options(env || {}).merge(run_options || {})
|
|
28
|
+
prepare_run_options = effective_run_options.merge(
|
|
29
|
+
only: PREPARE_ONLY_PATHS,
|
|
30
|
+
skip_lock_normalization: true
|
|
31
|
+
)
|
|
32
|
+
events = Kettle::Jem.event_stream_from_options(effective_run_options)
|
|
33
|
+
report = Kettle::Jem.apply_project(project_root, env: env, run_options: prepare_run_options)
|
|
34
|
+
profile_sync_step = synchronize_monorepo_subgem_profile_step(project_root, report)
|
|
35
|
+
supplemental_report = if profile_sync_step && profile_sync_step.fetch(:status) == "applied"
|
|
36
|
+
Kettle::Jem.apply_project(project_root, env: env, run_options: prepare_run_options)
|
|
37
|
+
end
|
|
38
|
+
report = merge_supplemental_prepare_report(report, supplemental_report)
|
|
39
|
+
nomono_bootstrap_step = normalize_existing_local_gemfile_bootstraps_step(project_root, events: events)
|
|
40
|
+
setup_env = Kettle::Jem::Tasks::InstallTask.setup_command_env(project_root, env)
|
|
41
|
+
setup_env["BUNDLE_DISABLE_CHECKSUM_VALIDATION"] = "true"
|
|
42
|
+
setup_env["K_JEM_TEMPLATING"] = "true" if local_path_development_env?(env)
|
|
43
|
+
reset_step = reset_release_lockfiles_step(
|
|
44
|
+
project_root: project_root,
|
|
45
|
+
setup_env: setup_env,
|
|
46
|
+
quiet: Kettle::Jem::DecisionPolicy.value_to_boolean(effective_run_options[:quiet]),
|
|
47
|
+
command_runner: command_runner,
|
|
48
|
+
events: events
|
|
49
|
+
)
|
|
50
|
+
Kettle::Jem.emit_step_event(events, "command_step", reset_step, phase: "prepare")
|
|
51
|
+
bootstrap_name = templating_bootstrap_step_name(project_root)
|
|
52
|
+
bootstrap_command = templating_bootstrap_command(project_root)
|
|
53
|
+
Kettle::Jem.emit_step_event(
|
|
54
|
+
events,
|
|
55
|
+
"command_step",
|
|
56
|
+
{name: bootstrap_name, status: "started", command: bootstrap_command},
|
|
57
|
+
phase: "prepare"
|
|
58
|
+
)
|
|
59
|
+
bootstrap_step = Kettle::Jem::Tasks::InstallTask.run_command_step(
|
|
60
|
+
bootstrap_name,
|
|
61
|
+
bootstrap_command,
|
|
62
|
+
project_root: project_root,
|
|
63
|
+
env: setup_env,
|
|
64
|
+
quiet: Kettle::Jem::DecisionPolicy.value_to_boolean(effective_run_options[:quiet]),
|
|
65
|
+
command_runner: command_runner
|
|
66
|
+
)
|
|
67
|
+
Kettle::Jem.emit_step_event(events, "command_step", bootstrap_step, phase: "prepare")
|
|
68
|
+
bundle_step = bundle_install_after_bootstrap_step(
|
|
69
|
+
project_root: project_root,
|
|
70
|
+
setup_env: setup_env,
|
|
71
|
+
quiet: Kettle::Jem::DecisionPolicy.value_to_boolean(effective_run_options[:quiet]),
|
|
72
|
+
command_runner: command_runner,
|
|
73
|
+
events: events,
|
|
74
|
+
bootstrap_command: bootstrap_command
|
|
75
|
+
)
|
|
76
|
+
Kettle::Jem.emit_step_event(events, "command_step", bundle_step, phase: "prepare")
|
|
77
|
+
|
|
78
|
+
final_report = report.merge(
|
|
79
|
+
mode: "prepare",
|
|
80
|
+
prepared: bootstrap_step.fetch(:status) == "succeeded" &&
|
|
81
|
+
%w[skipped succeeded].include?(reset_step.fetch(:status)) &&
|
|
82
|
+
%w[skipped succeeded].include?(bundle_step.fetch(:status)),
|
|
83
|
+
prepare_only: PREPARE_ONLY_PATHS,
|
|
84
|
+
prepare_steps: [profile_sync_step, nomono_bootstrap_step, reset_step, bootstrap_step, bundle_step].compact,
|
|
85
|
+
changed_files: (
|
|
86
|
+
report.fetch(:changed_files, []) +
|
|
87
|
+
nomono_bootstrap_step.fetch(:changed_files, []) +
|
|
88
|
+
reset_step.fetch(:changed_files, []) +
|
|
89
|
+
bootstrap_step.fetch(:changed_files, []) +
|
|
90
|
+
bundle_step.fetch(:changed_files, [])
|
|
91
|
+
).uniq.sort,
|
|
92
|
+
diagnostics: report.fetch(:diagnostics, []) + [{
|
|
93
|
+
severity: "advisory",
|
|
94
|
+
message: "kettle:jem:prepare applied the templating dependency bootstrap payload, " \
|
|
95
|
+
"updated critical templating gems, and ran bundle install."
|
|
96
|
+
}]
|
|
97
|
+
)
|
|
98
|
+
Kettle::Jem.emit_summary_event(events, final_report)
|
|
99
|
+
final_report
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def synchronize_monorepo_subgem_profile_step(project_root, report)
|
|
103
|
+
facts = report.fetch(:facts, {})
|
|
104
|
+
return unless Kettle::Jem.send(:monorepo_subgem_template_profile?, facts)
|
|
105
|
+
|
|
106
|
+
Kettle::Jem.send(:monorepo_subgem_kettle_config_profile_sync_step, project_root, report)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def merge_supplemental_prepare_report(report, supplemental_report)
|
|
110
|
+
return report unless supplemental_report
|
|
111
|
+
|
|
112
|
+
report.merge(
|
|
113
|
+
changed_files: (
|
|
114
|
+
report.fetch(:changed_files, []) + supplemental_report.fetch(:changed_files, [])
|
|
115
|
+
).uniq.sort,
|
|
116
|
+
diagnostics: report.fetch(:diagnostics, []) + supplemental_report.fetch(:diagnostics, [])
|
|
117
|
+
)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def normalize_existing_local_gemfile_bootstraps_step(project_root, events:)
|
|
121
|
+
paths = Dir.glob(File.join(project_root.to_s, "gemfiles/modular/**/*_local.gemfile")).sort
|
|
122
|
+
changed_files = paths.filter_map do |path|
|
|
123
|
+
before = File.read(path)
|
|
124
|
+
after = Kettle::Jem.normalize_local_gemfile_nomono_bootstrap(before)
|
|
125
|
+
next if after == before
|
|
126
|
+
|
|
127
|
+
File.write(path, after)
|
|
128
|
+
Pathname.new(path).relative_path_from(Pathname.new(project_root.to_s)).to_s
|
|
129
|
+
end
|
|
130
|
+
step = {
|
|
131
|
+
name: "normalize_local_gemfile_bootstraps",
|
|
132
|
+
status: changed_files.empty? ? "already_current" : "applied",
|
|
133
|
+
changed_files: changed_files
|
|
134
|
+
}
|
|
135
|
+
Kettle::Jem.emit_step_event(events, "command_step", step, phase: "prepare")
|
|
136
|
+
step
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def reset_release_lockfiles_step(project_root:, setup_env:, quiet:, command_runner:, events:)
|
|
140
|
+
if local_path_development_env?(setup_env)
|
|
141
|
+
return {
|
|
142
|
+
name: "reset_release_lockfiles",
|
|
143
|
+
command: %w[kettle-reset release-lockfiles],
|
|
144
|
+
status: "skipped",
|
|
145
|
+
reason: "local_path_development_env"
|
|
146
|
+
}
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
reset_command_runner = lambda do |command|
|
|
150
|
+
command_runner.call(Shellwords.split(command), chdir: project_root, env: setup_env, quiet: quiet)
|
|
151
|
+
end
|
|
152
|
+
resetter = Kettle::Dev::LockfileReset.new(
|
|
153
|
+
root: project_root,
|
|
154
|
+
command_runner: reset_command_runner
|
|
155
|
+
)
|
|
156
|
+
paths = resetter.lockfile_paths
|
|
157
|
+
if paths.empty?
|
|
158
|
+
return {
|
|
159
|
+
name: "reset_release_lockfiles",
|
|
160
|
+
command: %w[kettle-reset release-lockfiles],
|
|
161
|
+
status: "skipped",
|
|
162
|
+
reason: "no_release_lockfiles"
|
|
163
|
+
}
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
Kettle::Jem.emit_step_event(
|
|
167
|
+
events,
|
|
168
|
+
"command_step",
|
|
169
|
+
{name: "reset_release_lockfiles", status: "started", command: %w[kettle-reset release-lockfiles]},
|
|
170
|
+
phase: "prepare"
|
|
171
|
+
)
|
|
172
|
+
before = snapshot_files(paths)
|
|
173
|
+
resetter.reset(Kettle::Dev::LockfileReset::RELEASE_LOCKFILES_TARGET)
|
|
174
|
+
changed_files = changed_files_since(project_root, before)
|
|
175
|
+
{
|
|
176
|
+
name: "reset_release_lockfiles",
|
|
177
|
+
command: %w[kettle-reset release-lockfiles],
|
|
178
|
+
status: "succeeded",
|
|
179
|
+
changed_files: changed_files,
|
|
180
|
+
quiet: quiet
|
|
181
|
+
}
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def local_path_development_env?(env)
|
|
185
|
+
(env || {}).any? do |key, raw_value|
|
|
186
|
+
next false unless key.to_s.end_with?("_DEV")
|
|
187
|
+
|
|
188
|
+
value = raw_value.to_s.strip
|
|
189
|
+
!value.empty? && !Kettle::Jem::DecisionPolicy.falsey?(value)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def bundle_update_templating_bootstrap_command(project_root = Dir.pwd)
|
|
194
|
+
%w[bundle update] + CRITICAL_TEMPLATING_GEMS + locked_templating_gems(project_root)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def templating_bootstrap_command(project_root = Dir.pwd)
|
|
198
|
+
return %w[bundle install] unless templating_bootstrap_lockfile_ready?(project_root)
|
|
199
|
+
|
|
200
|
+
bundle_update_templating_bootstrap_command(project_root)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def templating_bootstrap_step_name(project_root = Dir.pwd)
|
|
204
|
+
return "bundle_install_templating_bootstrap" unless templating_bootstrap_lockfile_ready?(project_root)
|
|
205
|
+
|
|
206
|
+
"bundle_update_templating_bootstrap"
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def templating_bootstrap_lockfile_ready?(project_root)
|
|
210
|
+
lock_path = File.join(project_root.to_s, "Gemfile.lock")
|
|
211
|
+
return false unless File.file?(lock_path)
|
|
212
|
+
|
|
213
|
+
locked_gem_names(project_root).then do |names|
|
|
214
|
+
CRITICAL_TEMPLATING_GEMS.all? { |gem_name| names.include?(gem_name) }
|
|
215
|
+
end
|
|
216
|
+
rescue Bundler::LockfileError
|
|
217
|
+
false
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def bundle_install_after_bootstrap_step(project_root:, setup_env:, quiet:, command_runner:, events:, bootstrap_command:)
|
|
221
|
+
if bootstrap_command == %w[bundle install]
|
|
222
|
+
return {
|
|
223
|
+
name: "bundle_install",
|
|
224
|
+
command: %w[bundle install],
|
|
225
|
+
status: "skipped",
|
|
226
|
+
reason: "already_ran_as_templating_bootstrap"
|
|
227
|
+
}
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
Kettle::Jem.emit_step_event(
|
|
231
|
+
events,
|
|
232
|
+
"command_step",
|
|
233
|
+
{name: "bundle_install", status: "started", command: %w[bundle install]},
|
|
234
|
+
phase: "prepare"
|
|
235
|
+
)
|
|
236
|
+
Kettle::Jem::Tasks::InstallTask.run_command_step(
|
|
237
|
+
"bundle_install",
|
|
238
|
+
%w[bundle install],
|
|
239
|
+
project_root: project_root,
|
|
240
|
+
env: setup_env,
|
|
241
|
+
quiet: quiet,
|
|
242
|
+
command_runner: command_runner
|
|
243
|
+
)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def locked_templating_gems(project_root)
|
|
247
|
+
LOCKED_TEMPLATING_GEMS & locked_gem_names(project_root)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def locked_gem_names(project_root)
|
|
251
|
+
lock_path = File.join(project_root.to_s, "Gemfile.lock")
|
|
252
|
+
return [] unless File.file?(lock_path)
|
|
253
|
+
|
|
254
|
+
Bundler::LockfileParser.new(Bundler.read_file(lock_path)).specs.map(&:name)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def snapshot_files(paths)
|
|
258
|
+
paths.each_with_object({}) do |path, snapshot|
|
|
259
|
+
snapshot[path] = File.file?(path) ? File.binread(path) : nil
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def changed_files_since(project_root, snapshot)
|
|
264
|
+
snapshot.filter_map do |path, before|
|
|
265
|
+
after = File.file?(path) ? File.binread(path) : nil
|
|
266
|
+
next if before == after
|
|
267
|
+
|
|
268
|
+
Pathname.new(path).relative_path_from(Pathname.new(project_root)).to_s
|
|
269
|
+
end.sort
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tmpdir"
|
|
4
|
+
|
|
5
|
+
module Kettle
|
|
6
|
+
module Jem
|
|
7
|
+
module Tasks
|
|
8
|
+
module SelfTestTask
|
|
9
|
+
SKIPPED_PREFIXES = %w[
|
|
10
|
+
examples/
|
|
11
|
+
exe/
|
|
12
|
+
lib/
|
|
13
|
+
sig/
|
|
14
|
+
spec/
|
|
15
|
+
template/
|
|
16
|
+
].freeze
|
|
17
|
+
SKIPPED_FILES = %w[
|
|
18
|
+
.kettle-jem.yml
|
|
19
|
+
.rubocop_gradual.lock
|
|
20
|
+
Gemfile.lock
|
|
21
|
+
].freeze
|
|
22
|
+
GENERATED_RUNTIME_PREFIXES = %w[
|
|
23
|
+
tmp/kettle-jem/templating-report-
|
|
24
|
+
].freeze
|
|
25
|
+
SELFTEST_IGNORED_FILES = %w[
|
|
26
|
+
gemfiles/modular/shunted.gemfile
|
|
27
|
+
results/test_results.html
|
|
28
|
+
tmp/.gitignore
|
|
29
|
+
].freeze
|
|
30
|
+
|
|
31
|
+
module_function
|
|
32
|
+
|
|
33
|
+
def run(project_root: Dir.pwd, destination_root: project_root, template_root: nil, output_root: nil, min_divergence_threshold: nil)
|
|
34
|
+
root = File.expand_path(destination_root)
|
|
35
|
+
base_dir = output_root || File.join(root, "tmp", "template_test")
|
|
36
|
+
before_dir = File.join(base_dir, "destination")
|
|
37
|
+
after_dir = File.join(base_dir, "output")
|
|
38
|
+
report_dir = File.join(base_dir, "report")
|
|
39
|
+
diffs_dir = File.join(report_dir, "diffs")
|
|
40
|
+
|
|
41
|
+
FileUtils.rm_rf(base_dir)
|
|
42
|
+
[before_dir, after_dir, report_dir, diffs_dir].each { |path| FileUtils.mkdir_p(path) }
|
|
43
|
+
copy_tree(root, before_dir)
|
|
44
|
+
copy_tree(root, after_dir)
|
|
45
|
+
|
|
46
|
+
before = Kettle::Jem::SelfTest::Manifest.generate(before_dir)
|
|
47
|
+
File.write(File.join(report_dir, "before.json"), "#{JSON.pretty_generate(before)}\n")
|
|
48
|
+
|
|
49
|
+
write_template_root_override(after_dir, template_root) if template_root
|
|
50
|
+
Kettle::Jem.apply_project(after_dir, env: ENV, run_options: {accept: true, force: true, skip_commit: true})
|
|
51
|
+
|
|
52
|
+
after = Kettle::Jem::SelfTest::Manifest.generate(after_dir)
|
|
53
|
+
File.write(File.join(report_dir, "after.json"), "#{JSON.pretty_generate(after)}\n")
|
|
54
|
+
|
|
55
|
+
comparison = classify_comparison(Kettle::Jem::SelfTest::Manifest.compare(before, after))
|
|
56
|
+
diff_count = write_diffs(comparison, before_dir: before_dir, after_dir: after_dir, diffs_dir: diffs_dir)
|
|
57
|
+
drift = drift_report(after_dir: after_dir, template_root: template_root || Kettle::Jem.template_root_path(after_dir))
|
|
58
|
+
summary = Kettle::Jem::SelfTest::Reporter.summary(
|
|
59
|
+
comparison,
|
|
60
|
+
output_dir: after_dir,
|
|
61
|
+
templating_environment: Kettle::Jem::TemplatingReport.snapshot,
|
|
62
|
+
diff_count: diff_count
|
|
63
|
+
)
|
|
64
|
+
summary = append_drift_summary(summary, drift)
|
|
65
|
+
summary_path = File.join(report_dir, "summary.md")
|
|
66
|
+
File.write(summary_path, summary)
|
|
67
|
+
score, divergence = score_and_divergence(comparison)
|
|
68
|
+
threshold = selftest_threshold(min_divergence_threshold, root)
|
|
69
|
+
|
|
70
|
+
result = {
|
|
71
|
+
mode: "selftest",
|
|
72
|
+
destination_root: root,
|
|
73
|
+
template_root: template_root || Kettle::Jem.template_root_path(root),
|
|
74
|
+
output_root: after_dir,
|
|
75
|
+
report_path: summary_path,
|
|
76
|
+
comparison: comparison,
|
|
77
|
+
score: score,
|
|
78
|
+
divergence: divergence,
|
|
79
|
+
min_divergence_threshold: threshold,
|
|
80
|
+
drift: drift
|
|
81
|
+
}
|
|
82
|
+
if threshold && divergence > threshold
|
|
83
|
+
raise Kettle::Jem::Error, "selftest divergence #{divergence}% exceeds threshold #{threshold}%"
|
|
84
|
+
end
|
|
85
|
+
result
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def copy_tree(source, destination)
|
|
89
|
+
Find.find(source) do |path|
|
|
90
|
+
relative = path.sub(%r{\A#{Regexp.escape(source)}/?}, "")
|
|
91
|
+
next if relative.empty?
|
|
92
|
+
next Find.prune if ignored_tree_entry?(relative, path)
|
|
93
|
+
|
|
94
|
+
target = File.join(destination, relative)
|
|
95
|
+
if File.directory?(path)
|
|
96
|
+
FileUtils.mkdir_p(target)
|
|
97
|
+
else
|
|
98
|
+
FileUtils.mkdir_p(File.dirname(target))
|
|
99
|
+
FileUtils.cp(path, target)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def ignored_tree_entry?(relative, path)
|
|
105
|
+
return false unless File.directory?(path)
|
|
106
|
+
|
|
107
|
+
%w[.git .yardoc coverage docs node_modules pkg tmp].include?(relative.split("/").first)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def write_diffs(comparison, before_dir:, after_dir:, diffs_dir:)
|
|
111
|
+
count = 0
|
|
112
|
+
comparison.fetch(:changed, []).each do |relative|
|
|
113
|
+
diff = Kettle::Jem::SelfTest::Reporter.diff(
|
|
114
|
+
File.join(before_dir, relative),
|
|
115
|
+
File.join(after_dir, relative)
|
|
116
|
+
)
|
|
117
|
+
next if diff.empty?
|
|
118
|
+
|
|
119
|
+
diff_path = File.join(diffs_dir, "#{relative}.diff")
|
|
120
|
+
FileUtils.mkdir_p(File.dirname(diff_path))
|
|
121
|
+
File.write(diff_path, diff)
|
|
122
|
+
count += 1
|
|
123
|
+
end
|
|
124
|
+
count
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def classify_comparison(comparison)
|
|
128
|
+
changed = comparison.fetch(:changed, []).reject { |relative| ignored_selftest_artifact?(relative) }
|
|
129
|
+
added = comparison.fetch(:added, []).reject { |relative| ignored_selftest_artifact?(relative) }
|
|
130
|
+
skipped, removed = comparison.fetch(:removed, []).partition do |relative|
|
|
131
|
+
ignored_selftest_artifact?(relative) || expected_non_templated_path?(relative)
|
|
132
|
+
end
|
|
133
|
+
comparison.merge(changed: changed, added: added, removed: removed, skipped: skipped)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def expected_non_templated_path?(relative_path)
|
|
137
|
+
path_parts = relative_path.to_s.split("/")
|
|
138
|
+
SKIPPED_FILES.include?(relative_path) ||
|
|
139
|
+
SKIPPED_PREFIXES.any? { |prefix| relative_path.start_with?(prefix) } ||
|
|
140
|
+
(path_parts.length == 2 && path_parts.first == "gemfiles" && path_parts.last.end_with?(".gemfile")) ||
|
|
141
|
+
relative_path.to_s.end_with?(".gemspec")
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def ignored_selftest_artifact?(relative_path)
|
|
145
|
+
generated_runtime_artifact?(relative_path) || SELFTEST_IGNORED_FILES.include?(relative_path)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def generated_runtime_artifact?(relative_path)
|
|
149
|
+
GENERATED_RUNTIME_PREFIXES.any? { |prefix| relative_path.start_with?(prefix.to_s) }
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def score_and_divergence(comparison)
|
|
153
|
+
total = comparison.fetch(:matched, []).size +
|
|
154
|
+
comparison.fetch(:changed, []).size +
|
|
155
|
+
comparison.fetch(:added, []).size +
|
|
156
|
+
comparison.fetch(:removed, []).size
|
|
157
|
+
score = total.zero? ? 0.0 : (comparison.fetch(:matched, []).size.to_f / total * 100).round(1)
|
|
158
|
+
[score, (100.0 - score).round(1)]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def selftest_threshold(explicit, root)
|
|
162
|
+
return explicit.to_f if explicit
|
|
163
|
+
|
|
164
|
+
env_threshold = ENV["KJ_MIN_DIVERGENCE_THRESHOLD"].to_s.strip
|
|
165
|
+
return env_threshold.to_f unless env_threshold.empty?
|
|
166
|
+
|
|
167
|
+
config_path = File.join(root, ".kettle-jem.yml")
|
|
168
|
+
return unless File.file?(config_path)
|
|
169
|
+
|
|
170
|
+
config = YAML.load_file(config_path)
|
|
171
|
+
value = config["min_divergence_threshold"] if config.is_a?(Hash)
|
|
172
|
+
return if value.nil? || value.to_s.strip.empty?
|
|
173
|
+
|
|
174
|
+
Float(value)
|
|
175
|
+
rescue ArgumentError, TypeError
|
|
176
|
+
raise Kettle::Jem::Error, "Invalid selftest min_divergence_threshold"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def write_template_root_override(root, template_root)
|
|
180
|
+
config_path = File.join(root, ".kettle-jem.yml")
|
|
181
|
+
content = File.file?(config_path) ? File.read(config_path) : ""
|
|
182
|
+
File.write(config_path, upsert_template_root_override(content, File.expand_path(template_root)))
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def upsert_template_root_override(content, template_root)
|
|
186
|
+
# This is line-oriented instead of YAML round-tripping because the self-test fixture config must preserve
|
|
187
|
+
# comments, key order, and scalar spellings while injecting a temporary local template root.
|
|
188
|
+
lines = content.to_s.lines
|
|
189
|
+
template_index = lines.index { |line| line.match?(/\Atemplates:\s*(?:#.*)?\n?\z/) }
|
|
190
|
+
if template_index
|
|
191
|
+
root_index = ((template_index + 1)...lines.length).find do |index|
|
|
192
|
+
line = lines[index]
|
|
193
|
+
break nil unless line.strip.empty? || line.start_with?(" ")
|
|
194
|
+
|
|
195
|
+
line.match?(/\A\s+root\s*:/)
|
|
196
|
+
end
|
|
197
|
+
if root_index
|
|
198
|
+
lines[root_index] = " root: #{template_root}\n"
|
|
199
|
+
else
|
|
200
|
+
lines.insert(template_index + 1, " root: #{template_root}\n")
|
|
201
|
+
end
|
|
202
|
+
else
|
|
203
|
+
lines << "\n" unless lines.empty? || lines.last.to_s.strip.empty?
|
|
204
|
+
lines << "templates:\n"
|
|
205
|
+
lines << " root: #{template_root}\n"
|
|
206
|
+
end
|
|
207
|
+
output = lines.join
|
|
208
|
+
output.end_with?("\n") ? output : "#{output}\n"
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def drift_report(after_dir:, template_root:)
|
|
212
|
+
unless defined?(Kettle::Drift)
|
|
213
|
+
return {
|
|
214
|
+
available: false,
|
|
215
|
+
reason: "kettle-drift is not available"
|
|
216
|
+
}
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
outcome = Kettle::Drift.run(
|
|
220
|
+
project_root: after_dir,
|
|
221
|
+
template_dir: template_root,
|
|
222
|
+
lock_path: File.join("tmp", "template_test", ".kettle-drift.lock"),
|
|
223
|
+
mode: :force_update,
|
|
224
|
+
printer_class: nil
|
|
225
|
+
)
|
|
226
|
+
{
|
|
227
|
+
available: true,
|
|
228
|
+
warning_count: outcome.warning_count,
|
|
229
|
+
json_path: outcome.json_path,
|
|
230
|
+
lock_path: outcome.lock_path,
|
|
231
|
+
exit_code: outcome.exit_code
|
|
232
|
+
}
|
|
233
|
+
rescue => error
|
|
234
|
+
{
|
|
235
|
+
available: false,
|
|
236
|
+
reason: "#{error.class}: #{error.message}"
|
|
237
|
+
}
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def append_drift_summary(summary, drift)
|
|
241
|
+
return summary unless drift
|
|
242
|
+
|
|
243
|
+
lines = [summary, "", "## Drift Analysis", ""]
|
|
244
|
+
if drift[:available]
|
|
245
|
+
lines << "**Duplicate drift warnings**: #{drift.fetch(:warning_count)}"
|
|
246
|
+
lines << "**Drift report**: `#{drift[:json_path]}`" if drift[:json_path]
|
|
247
|
+
else
|
|
248
|
+
lines << "Drift analysis unavailable: #{drift[:reason]}"
|
|
249
|
+
end
|
|
250
|
+
lines << ""
|
|
251
|
+
lines.join("\n")
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "etc"
|
|
4
|
+
|
|
5
|
+
module Kettle
|
|
6
|
+
module Jem
|
|
7
|
+
module Tasks
|
|
8
|
+
module TemplateTask
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def run(project_root: Dir.pwd, env: ENV, run_options: nil, command_runner: Kettle::Jem::Tasks::InstallTask.method(:run_system_command))
|
|
12
|
+
effective_run_options = templating_run_options(env, run_options)
|
|
13
|
+
report = if Dir.glob(File.join(project_root.to_s, "*.gemspec")).empty? && !monorepo_root_profile?(project_root, env, effective_run_options)
|
|
14
|
+
{
|
|
15
|
+
mode: "apply",
|
|
16
|
+
changed_files: [],
|
|
17
|
+
diagnostics: [],
|
|
18
|
+
recipe_reports: []
|
|
19
|
+
}
|
|
20
|
+
else
|
|
21
|
+
Kettle::Jem.apply_project(project_root, env: env, run_options: effective_run_options)
|
|
22
|
+
end
|
|
23
|
+
setup_env = Kettle::Jem::Tasks::InstallTask.setup_command_env(project_root, env)
|
|
24
|
+
hook_step = Kettle::Jem::Tasks::InstallTask.hook_templates_step(project_root, effective_run_options)
|
|
25
|
+
git_drivers_step = Kettle::Jem::Tasks::InstallTask.git_drivers_step(project_root, effective_run_options)
|
|
26
|
+
lock_step = Kettle::Jem::Tasks::InstallTask.normalize_lockfile_step(project_root, env: setup_env, run_options: effective_run_options)
|
|
27
|
+
template_steps = Kettle::Jem::Tasks::InstallTask.execute_orchestration_steps(
|
|
28
|
+
[hook_step, git_drivers_step, lock_step],
|
|
29
|
+
project_root: project_root,
|
|
30
|
+
env: setup_env,
|
|
31
|
+
run_options: effective_run_options,
|
|
32
|
+
command_runner: command_runner,
|
|
33
|
+
event_phase: "template"
|
|
34
|
+
)
|
|
35
|
+
final_report = report.merge(mode: "template", template_steps: template_steps)
|
|
36
|
+
Kettle::Jem.emit_summary_event(Kettle::Jem.event_stream_from_options(effective_run_options), final_report)
|
|
37
|
+
final_report
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def templating_run_options(env, run_options)
|
|
41
|
+
options = env_run_options(env || {}).merge(run_options || {})
|
|
42
|
+
return options if worker_or_strategy_option_provided?(env || {}, options)
|
|
43
|
+
|
|
44
|
+
workers = default_thread_worker_count
|
|
45
|
+
options.merge(
|
|
46
|
+
recipe_planning_strategy: "classified",
|
|
47
|
+
recipe_planning_thread_workers: workers,
|
|
48
|
+
file_work_thread_workers: workers
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def env_run_options(env)
|
|
53
|
+
{
|
|
54
|
+
accept: truthy?(env["accept"]) || truthy?(env["force"]),
|
|
55
|
+
force: truthy?(env["force"]),
|
|
56
|
+
interactive: falsey?(env["force"]),
|
|
57
|
+
failure_mode: env["FAILURE_MODE"] || env["failure_mode"],
|
|
58
|
+
allowed: env["allowed"],
|
|
59
|
+
hook_templates: env["hook_templates"],
|
|
60
|
+
git_drivers: env["git_drivers"] || env["KETTLE_JEM_GIT_DRIVERS"],
|
|
61
|
+
only: env["only"],
|
|
62
|
+
include: env["include"],
|
|
63
|
+
template_profile: env["KETTLE_JEM_TEMPLATE_PROFILE"],
|
|
64
|
+
dry_run: truthy?(env["KETTLE_JEM_DRY_RUN"]),
|
|
65
|
+
skip_commit: truthy?(env["KETTLE_JEM_SKIP_COMMIT"]),
|
|
66
|
+
skip_drift_check: truthy?(env["KETTLE_JEM_SKIP_DRIFT_CHECK"]),
|
|
67
|
+
skip_rubocop_gradual: truthy?(env["KETTLE_JEM_SKIP_RUBOCOP_GRADUAL"]),
|
|
68
|
+
skip_binstubs: truthy?(env["KETTLE_JEM_SKIP_BINSTUBS"]),
|
|
69
|
+
skip_appraisal_generate: truthy?(env["KETTLE_JEM_SKIP_APPRAISAL_GENERATE"]),
|
|
70
|
+
skip_lock_normalization: truthy?(env["KETTLE_JEM_SKIP_LOCK_NORMALIZATION"]),
|
|
71
|
+
checksums: env["KETTLE_JEM_CHECKSUMS"],
|
|
72
|
+
accept_config: truthy?(env["KETTLE_JEM_ACCEPT_CONFIG"]),
|
|
73
|
+
bootstrap_mode: truthy?(env["KETTLE_JEM_BOOTSTRAP_MODE"]),
|
|
74
|
+
quiet: truthy?(env["KETTLE_JEM_QUIET"]),
|
|
75
|
+
verbose: truthy?(env["KETTLE_JEM_VERBOSE"])
|
|
76
|
+
}.compact
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def default_thread_worker_count
|
|
80
|
+
[1, Etc.nprocessors / 2].max
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def worker_or_strategy_option_provided?(env, run_options)
|
|
84
|
+
option_keys = %i[
|
|
85
|
+
ractor_workers
|
|
86
|
+
recipe_planning_workers
|
|
87
|
+
thread_workers
|
|
88
|
+
recipe_planning_thread_workers
|
|
89
|
+
ractor_file_workers
|
|
90
|
+
file_work_workers
|
|
91
|
+
thread_file_workers
|
|
92
|
+
file_work_thread_workers
|
|
93
|
+
recipe_planning_strategy
|
|
94
|
+
]
|
|
95
|
+
string_option_keys = option_keys.map(&:to_s)
|
|
96
|
+
env_keys = %w[
|
|
97
|
+
KETTLE_JEM_RACTOR_WORKERS
|
|
98
|
+
KETTLE_JEM_THREAD_WORKERS
|
|
99
|
+
KETTLE_JEM_RACTOR_FILE_WORKERS
|
|
100
|
+
KETTLE_JEM_THREAD_FILE_WORKERS
|
|
101
|
+
KETTLE_JEM_RECIPE_PLANNING_STRATEGY
|
|
102
|
+
]
|
|
103
|
+
option_keys.any? { |key| run_options.key?(key) } ||
|
|
104
|
+
string_option_keys.any? { |key| run_options.key?(key) } ||
|
|
105
|
+
env_keys.any? { |key| env.key?(key) && !env[key].to_s.strip.empty? }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def truthy?(value)
|
|
109
|
+
Kettle::Jem::DecisionPolicy.truthy?(value)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def falsey?(value)
|
|
113
|
+
Kettle::Jem::DecisionPolicy.falsey?(value)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def monorepo_root_profile?(project_root, env, run_options)
|
|
117
|
+
profile = (run_options || {})[:template_profile] || (run_options || {})["template_profile"] || (env || {})["KETTLE_JEM_TEMPLATE_PROFILE"]
|
|
118
|
+
profile = Kettle::Jem.kettle_jem_config(project_root).dig("templates", "profile") if profile.to_s.empty?
|
|
119
|
+
Kettle::Jem.normalize_template_profile(profile) == Kettle::Jem::MONOREPO_ROOT_TEMPLATE_PROFILE
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# An .aiignore file follows the same syntax as a .gitignore file.
|
|
2
|
+
# .gitignore documentation: https://git-scm.com/docs/gitignore
|
|
3
|
+
|
|
4
|
+
# you can ignore files
|
|
5
|
+
.DS_Store
|
|
6
|
+
*.log
|
|
7
|
+
*.tmp
|
|
8
|
+
|
|
9
|
+
# or folders
|
|
10
|
+
.devcontainer/
|
|
11
|
+
.qlty/
|
|
12
|
+
.yardoc/
|
|
13
|
+
dist/
|
|
14
|
+
build/
|
|
15
|
+
out/
|
|
16
|
+
coverage/
|
|
17
|
+
docs/
|
|
18
|
+
pkg/
|
|
19
|
+
results/
|