lib/keep_up/bundle.rb in keep_up-0.6.3 vs lib/keep_up/bundle.rb in keep_up-0.7.0
- old
+ new
@@ -1,132 +1,121 @@
-require 'bundler'
+# frozen_string_literal: true
+
require_relative 'gemfile_filter'
require_relative 'gemspec_filter'
require_relative 'dependency'
-require_relative 'one'
module KeepUp
# A Gemfile with its current set of locked dependencies.
class Bundle
- def initialize(definition_builder:)
- @definition_builder = definition_builder
+ OUTDATED_MATCHER =
+ /([^ ]*) \(newest ([^,]*), installed ([^,]*)(?:, requested (.*))?\)/.freeze
+ UPDATE_MATCHER =
+ /(?:Using|Installing|Fetching) ([^ ]*) ([^ ]*)(?: \(was (.*))?\)/.freeze
+
+ def initialize(runner:, local:)
+ @runner = runner
+ @local = local
end
def dependencies
- gemspec_dependencies + gemfile_dependencies + transitive_dependencies
+ @dependencies ||=
+ begin
+ command = "bundle outdated --parseable#{' --local' if @local}"
+ lines = run_filtered command, OUTDATED_MATCHER
+ lines.map do |name, newest, version, requirement|
+ requirement_list = requirement&.split(/,\s*/)
+ requirement_list ||= fetch_gemspec_dependency_requirements(name)
+ version = version.split(' ').first
+ newest = newest.split(' ').first
+ Dependency.new(name: name,
+ locked_version: version,
+ newest_version: newest,
+ requirement_list: requirement_list)
+ end
+ end
end
- def apply_updated_dependency(dependency)
- report_intent dependency
- update_gemfile_contents(dependency)
- update_gemspec_contents(dependency)
- result = update_lockfile(dependency)
- report_result dependency, result
- result
- end
-
def check?
- bundler_definition.to_lock == File.read('Gemfile.lock')
+ _, status = @runner.run2 'bundle check'
+ status == 0
end
- private
+ def update_gemfile_contents(update)
+ update = find_specification_update(dependencies, update)
+ return unless update
- attr_reader :definition_builder
-
- def report_intent(dependency)
- print "Updating #{dependency.name}"
+ update_specification_contents(update, 'Gemfile', GemfileFilter)
end
- def report_result(dependency, result)
- if result
- puts " to #{result.version}"
- else
- puts " to #{dependency.version}"
- puts 'Update failed'
- end
- end
+ def update_gemspec_contents(update)
+ return unless gemspec_name
- def gemfile_dependencies
- raw = if Bundler::VERSION >= '1.15.'
- bundler_lockfile.dependencies.values
- else
- bundler_lockfile.dependencies
- end
- build_dependencies raw
- end
+ update = find_specification_update(dependencies, update)
+ return unless update
- def gemspec_dependencies
- gemspec_source = bundler_lockfile.sources.
- find { |it| it.is_a? Bundler::Source::Gemspec }
- return [] unless gemspec_source
- build_dependencies gemspec_source.gemspec.dependencies
+ update_specification_contents(update, gemspec_name, GemspecFilter)
end
- def transitive_dependencies
- build_dependencies bundler_lockfile.specs.flat_map(&:dependencies).uniq
- end
+ # Update lockfile and return resulting spec, or false in case of failure
+ def update_lockfile(update)
+ update_name = update.name
+ command = "bundle update#{' --local' if @local} --conservative #{update_name}"
+ lines = run_filtered command, UPDATE_MATCHER
+ lines.each do |name, version, old_version|
+ next unless name == update_name && old_version
- def build_dependencies(deps)
- deps.map { |dep| build_dependency dep }.compact
+ current = Gem::Specification.new(name, old_version)
+ result = Gem::Specification.new(name, version)
+ return result if result.version > current.version
+ end
+ nil
end
- def build_dependency(dep)
- spec = locked_spec dep
- return unless spec
- Dependency.new(name: dep.name,
- requirement_list: dep.requirement.as_list,
- locked_version: spec.version)
- end
+ private
- def locked_spec(dep)
- bundler_lockfile.specs.find { |it| it.name == dep.name }
+ def gemspec
+ @gemspec ||= eval File.read(gemspec_name) if gemspec_name
end
- def bundler_lockfile
- @bundler_lockfile ||= bundler_definition.locked_gems
+ def gemspec_dependencies
+ @gemspec_dependencies ||= if gemspec
+ gemspec.dependencies
+ else
+ []
+ end
end
- def bundler_definition
- @bundler_definition ||= definition_builder.build(false)
+ def fetch_gemspec_dependency_requirements(name)
+ dep = gemspec_dependencies.find { |it| it.name == name }
+ return unless dep
+
+ dep.requirements_list
end
- def update_gemfile_contents(update)
- current_dependency = gemfile_dependencies.find { |it| it.name == update.name }
- return unless current_dependency
- return if current_dependency.matches_spec?(update)
+ def find_specification_update(current_dependencies, update)
+ current_dependency = current_dependencies.find { |it| it.name == update.name }
+ return if !current_dependency || current_dependency.matches_spec?(update)
- update = current_dependency.generalize_specification(update)
-
- contents = File.read 'Gemfile'
- updated_contents = GemfileFilter.apply(contents, update)
- File.write 'Gemfile', updated_contents
+ current_dependency.generalize_specification(update)
end
- def update_gemspec_contents(update)
- current_dependency = gemspec_dependencies.find { |it| it.name == update.name }
- return unless current_dependency
- return if current_dependency.matches_spec?(update)
-
- update = current_dependency.generalize_specification(update)
-
- contents = File.read gemspec_name
- updated_contents = GemspecFilter.apply(contents, update)
- File.write gemspec_name, updated_contents
+ def update_specification_contents(update, file, filter)
+ File.write file, filter.apply(File.read(file), update)
end
def gemspec_name
- @gemspec_name ||= One.fetch(Dir.glob('*.gemspec'))
+ @gemspec_name ||= Dir.glob('*.gemspec').first
end
- # Update lockfile and return resulting spec, or false in case of failure
- def update_lockfile(update)
- Bundler.clear_gemspec_cache
- definition = definition_builder.build(gems: [update.name])
- definition.lock('Gemfile.lock')
- current = locked_spec(update)
- result = definition.specs.find { |it| it.name == update.name }
- result if result.version > current.version
- rescue Bundler::VersionConflict
- false
+ def run_filtered(command, regexp)
+ result = @runner.run command
+ lines = result.split("\n").reject(&:empty?)
+ lines.map do |line|
+ matchdata = regexp.match line
+ next unless matchdata
+
+ matchdata.to_a[1..-1]
+ end.compact
end
end
end