Class: Kettle::Dev::LockfileReset
- Inherits:
-
Object
- Object
- Kettle::Dev::LockfileReset
- Defined in:
- lib/kettle/dev/lockfile_reset.rb
Constant Summary collapse
- DEFAULT_DISABLED_ENV =
{ "KETTLE_DEV_DEV" => "false", "K_JEM_TEMPLATING" => "false", "STRUCTUREDMERGE_DEV" => "false", "TREE_SITTER_LANGUAGE_PACK_DEV" => "false", "RUBOCOP_LTS_LOCAL" => "false", "GALTZO_FLOSS_DEV" => "false", "UR_BRAIN_DEV" => "false" }.freeze
- RELEASE_LOCKFILES_TARGET =
"release-lockfiles"- SUPPORTED_TARGETS =
["Gemfile.lock", "Appraisal.root.gemfile.lock", RELEASE_LOCKFILES_TARGET].freeze
- UNBUNDLED_ENV_KEYS =
%w[ BUNDLE_BIN_PATH BUNDLE_FROZEN BUNDLER_VERSION RUBYOPT ].freeze
Instance Method Summary collapse
- #diagnostics(path) ⇒ Object
- #display_path(path) ⇒ Object
- #dynamic_local_path_env ⇒ Object
- #empty_registry_checksums(path) ⇒ Object
- #gemfile_for_lockfile(path) ⇒ Object
- #has_any_sha_checksum?(path) ⇒ Boolean
- #has_local_path_remote?(path) ⇒ Boolean
-
#initialize(root:, command_runner:) ⇒ LockfileReset
constructor
A new instance of LockfileReset.
- #local_path_env_value?(value) ⇒ Boolean
- #local_path_remote_lines(path) ⇒ Object
- #lockfile_path_for(target) ⇒ Object
- #lockfile_paths ⇒ Object
- #lockfile_paths_for(target) ⇒ Object
- #normalization_env ⇒ Object
- #normalization_needed?(path) ⇒ Boolean
- #path_dependency_gems(path) ⇒ Object
- #path_source_gems(path) ⇒ Object
- #release_lockfiles_target?(target) ⇒ Boolean
- #reset(target) ⇒ Object
- #reset_command(path:, gemfile:, full_update: false) ⇒ Object
- #reset_lockfile!(path, full_update: false) ⇒ Object
- #reset_update_gems(path) ⇒ Object
- #self_path_source_gems(path) ⇒ Object
Constructor Details
#initialize(root:, command_runner:) ⇒ LockfileReset
Returns a new instance of LockfileReset.
32 33 34 35 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 32 def initialize(root:, command_runner:) @root = root @command_runner = command_runner end |
Instance Method Details
#diagnostics(path) ⇒ Object
102 103 104 105 106 107 108 109 110 111 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 102 def diagnostics(path) diagnostics = [] local_path_remote_lines(path).each do |line_number| diagnostics << "#{display_path(path)} has local path remote at line #{line_number}" end empty_registry_checksums(path).each do |name, version, line_number| diagnostics << "#{display_path(path)} CHECKSUMS has no sha256 for #{name} #{version} at line #{line_number}" end diagnostics end |
#display_path(path) ⇒ Object
261 262 263 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 261 def display_path(path) Kettle::Dev.display_path(path) end |
#dynamic_local_path_env ⇒ Object
161 162 163 164 165 166 167 168 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 161 def dynamic_local_path_env ENV.each_with_object({}) do |(key, value), env| next unless key.end_with?("_DEV", "_LOCAL") next unless local_path_env_value?(value) env[key] = "false" end end |
#empty_registry_checksums(path) ⇒ Object
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 199 def empty_registry_checksums(path) path_gems = path_source_gems(path) | path_dependency_gems(path) | self_path_source_gems(path) in_checksums = false entries = File.readlines(path).filter_map.with_index(1) do |line, index| stripped = line.strip if stripped == "CHECKSUMS" in_checksums = true next end next unless in_checksums next if stripped.empty? next if stripped == "BUNDLED WITH" match = stripped.match(/\A([A-Za-z0-9_.-]+) \(([^)]+)\)(?:\s+(sha256=.*))?\z/) next unless match name = match[1] checksum = match[3].to_s next unless checksum.empty? next if path_gems.include?(name) [name, match[2], index] end return [] unless has_any_sha_checksum?(path) entries end |
#gemfile_for_lockfile(path) ⇒ Object
150 151 152 153 154 155 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 150 def gemfile_for_lockfile(path) basename = File.basename(path) return File.join(root, "Gemfile") if basename == "Gemfile.lock" path.delete_suffix(".lock") end |
#has_any_sha_checksum?(path) ⇒ Boolean
227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 227 def has_any_sha_checksum?(path) in_checksums = false File.readlines(path).any? do |line| stripped = line.strip if stripped == "CHECKSUMS" in_checksums = true next false end next false unless in_checksums stripped.include?("sha256=") end end |
#has_local_path_remote?(path) ⇒ Boolean
178 179 180 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 178 def has_local_path_remote?(path) !local_path_remote_lines(path).empty? end |
#local_path_env_value?(value) ⇒ Boolean
170 171 172 173 174 175 176 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 170 def local_path_env_value?(value) text = value.to_s.strip return false if text.empty? || text.casecmp("false").zero? return true if text.start_with?("/", "./", "../", "~") text.include?(File::SEPARATOR) end |
#local_path_remote_lines(path) ⇒ Object
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 182 def local_path_remote_lines(path) in_path = false File.readlines(path).filter_map.with_index(1) do |line, index| stripped = line.strip if stripped.match?(/\A[A-Z][A-Z ]*\z/) in_path = stripped == "PATH" next end next unless in_path next unless stripped.start_with?("remote:") next if stripped == "remote: ." next unless stripped.start_with?("remote: /", "remote: ./", "remote: ../") index end end |
#lockfile_path_for(target) ⇒ Object
121 122 123 124 125 126 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 121 def lockfile_path_for(target) paths = lockfile_paths_for(target) raise Error, "reset target #{target.inspect} resolves to multiple lockfiles; use lockfile_paths_for" if paths.length != 1 paths.first end |
#lockfile_paths ⇒ Object
113 114 115 116 117 118 119 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 113 def lockfile_paths candidates = [ File.join(root, "Gemfile.lock"), File.join(root, "Appraisal.root.gemfile.lock") ] candidates.select { |path| File.file?(path) }.sort end |
#lockfile_paths_for(target) ⇒ Object
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 128 def lockfile_paths_for(target) normalized = target.to_s.strip raise Error, "reset requires TARGET" if normalized.empty? if release_lockfiles_target?(normalized) paths = lockfile_paths raise Error, "no release lockfiles found" if paths.empty? return paths end supported = SUPPORTED_TARGETS.reject { |candidate| candidate == RELEASE_LOCKFILES_TARGET } match = supported.find { |candidate| candidate.casecmp(normalized).zero? } unless match raise Error, "reset target #{normalized.inspect} is not supported; supported targets: #{SUPPORTED_TARGETS.join(", ")}" end [File.join(root, match)] end |
#normalization_env ⇒ Object
157 158 159 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 157 def normalization_env DEFAULT_DISABLED_ENV.merge(dynamic_local_path_env) end |
#normalization_needed?(path) ⇒ Boolean
98 99 100 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 98 def normalization_needed?(path) has_local_path_remote?(path) || !empty_registry_checksums(path).empty? end |
#path_dependency_gems(path) ⇒ Object
248 249 250 251 252 253 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 248 def path_dependency_gems(path) lockfile_parser(path).dependencies.each_value.each_with_object(Set.new) do |dependency, gems| source = dependency.source gems << dependency.name if path_source?(source) && !self_path_source?(source) end end |
#path_source_gems(path) ⇒ Object
241 242 243 244 245 246 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 241 def path_source_gems(path) lockfile_parser(path).specs.each_with_object(Set.new) do |spec, gems| source = spec.source gems << spec.name if path_source?(source) && !self_path_source?(source) end end |
#release_lockfiles_target?(target) ⇒ Boolean
146 147 148 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 146 def release_lockfiles_target?(target) target.to_s.strip.casecmp(RELEASE_LOCKFILES_TARGET).zero? end |
#reset(target) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 37 def reset(target) paths = lockfile_paths_for(target) force_full_update = release_lockfiles_target?(target) uninstall_unreleased_local_gems(paths) if force_full_update paths.each do |path| reset_lockfile!(path, full_update: force_full_update) if force_full_update || normalization_needed?(path) end if force_full_update && uninstall_unreleased_local_gems(paths) paths.each do |path| reset_lockfile!(path, full_update: true) end end diagnostics = paths.flat_map { |path| diagnostics(path) } raise Error, (target, diagnostics) unless diagnostics.empty? paths end |
#reset_command(path:, gemfile:, full_update: false) ⇒ Object
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 70 def reset_command(path:, gemfile:, full_update: false) update_gems = (full_update || has_local_path_remote?(path)) ? [] : reset_update_gems(path) env = normalization_env.merge( "BUNDLE_GEMFILE" => gemfile, "BUNDLE_LOCKFILE" => path ) command = +"env" UNBUNDLED_ENV_KEYS.each do |key| command << " -u #{key}" end env.each do |key, value| command << " #{key}=#{Shellwords.escape(value)}" end command << " bundle lock" command << " --update" command << " #{update_gems.map { |gem_name| Shellwords.escape(gem_name) }.join(" ")}" unless update_gems.empty? command << " --add-checksums" command end |
#reset_lockfile!(path, full_update: false) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 55 def reset_lockfile!(path, full_update: false) gemfile = gemfile_for_lockfile(path) unless gemfile && File.file?(gemfile) warn("Cannot reset #{display_path(path)} because its Gemfile was not found.") return end command = reset_command(path: path, gemfile: gemfile, full_update: full_update) if full_update || has_local_path_remote?(path) rebuild_lockfile(path) { command_runner.call(command) } else command_runner.call(command) end end |
#reset_update_gems(path) ⇒ Object
90 91 92 93 94 95 96 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 90 def reset_update_gems(path) ( path_source_gems(path).to_a | path_dependency_gems(path).to_a | empty_registry_checksums(path).map(&:first) ).uniq.sort end |
#self_path_source_gems(path) ⇒ Object
255 256 257 258 259 |
# File 'lib/kettle/dev/lockfile_reset.rb', line 255 def self_path_source_gems(path) lockfile_parser(path).specs.each_with_object(Set.new) do |spec, gems| gems << spec.name if self_path_source?(spec.source) end end |