keep_up 0.6.3 → 0.9.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c6e9711c520dce8201a771fec4b0b972817ab8b9
4
- data.tar.gz: 2cbdba7b2d01263a405bc8baee64fb587dc28fd7
2
+ SHA256:
3
+ metadata.gz: d9a8cf50656d55403aaae2319218ce2801bcd9dffdea0151d86fc90f7051de27
4
+ data.tar.gz: ea53a52e56e04b9dba8134bdf86c99713847ef2158181a557f93165e326177b9
5
5
  SHA512:
6
- metadata.gz: 994a91e4ed5c541a0c594cc41868b19be18d5d50dd1c62ca011e947116c64377f24e1ce641b770fb4836db68f5f5bca0aa158f737476a88a79a63dfe166397d4
7
- data.tar.gz: bc4867a1518651a0715ab59c9c418c1ea1a1d822d89235c665232e6c4db1e6c4ca86f741a0472ab4881446631be87dbdc58cd28c1363f409fbade67e8f5cd551
6
+ metadata.gz: 251e0b63416c6b937831aac2b65cccd5b6e3b38432af375d4210f52071e63edd4688767754c4d94ef8d540176d8fd8ee895ee51ae81926613c39fdc81e1c87cf
7
+ data.tar.gz: 0eab09bcbfc0c8472a511e9d10cf8132529a3801d5aadc68a138d7d26d6d34c636d8d6e0246ee255c72e2853ed5344019f2ec486fac0b370a4fdf4e31f75f534
@@ -0,0 +1,2 @@
1
+ rubocop:
2
+ config_file: .rubocop.yml
@@ -1,21 +1,41 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ require:
4
+ - rubocop-performance
5
+ - rubocop-rspec
6
+
1
7
  AllCops:
2
8
  Exclude:
3
9
  - 'tmp/**/*'
10
+ NewCops: enable
11
+ TargetRubyVersion: 2.5
4
12
 
5
- # Dot at end of line makes it clearer that the line is not done
6
- Layout/DotPosition:
7
- EnforcedStyle: trailing
13
+ # Be lenient with line length
14
+ Layout/LineLength:
15
+ Max: 92
8
16
 
9
- # Multi-line assignment should be simply indented. Aligning them makes it even
10
- # harder to keep a sane line length.
11
- Layout/MultilineOperationIndentation:
12
- EnforcedStyle: indented
17
+ # Don't force lonely closing parentheses
18
+ Layout/MultilineMethodCallBraceLayout:
19
+ EnforcedStyle: same_line
13
20
 
14
21
  # Multi-line method calls should be simply indented. Aligning them makes it
15
22
  # even harder to keep a sane line length.
16
23
  Layout/MultilineMethodCallIndentation:
17
24
  EnforcedStyle: indented
18
25
 
26
+ # Multi-line assignment should be simply indented. Aligning them makes it even
27
+ # harder to keep a sane line length.
28
+ Layout/MultilineOperationIndentation:
29
+ EnforcedStyle: indented
30
+
31
+ # Force consistent spacing independent of block contents
32
+ Layout/SpaceBeforeBlockBraces:
33
+ EnforcedStyleForEmptyBraces: space
34
+
35
+ # Assume the programmer knows how bracketed block syntax works
36
+ Lint/AmbiguousBlockAssociation:
37
+ Enabled: false
38
+
19
39
  # Allow if (foo = get_foo) style
20
40
  Lint/AssignmentInCondition:
21
41
  AllowSafeAssignment: true
@@ -25,9 +45,8 @@ Metrics/BlockLength:
25
45
  Exclude:
26
46
  - 'spec/**/*'
27
47
 
28
- # Require lines to fit in pull requests.
29
- Metrics/LineLength:
30
- Max: 92
48
+ Performance/StartWith:
49
+ AutoCorrect: true
31
50
 
32
51
  # Allow and/or for control flow only
33
52
  Style/AndOr:
@@ -37,18 +56,32 @@ Style/AndOr:
37
56
  Style/GuardClause:
38
57
  MinBodyLength: 2
39
58
 
40
- # Don't use if or unless as modifier if the line gets too long
41
- Style/IfUnlessModifier:
42
- MaxLineLength: 60
59
+ # Sometimes an if statement just looks better than next with a guard clause
60
+ Style/Next:
61
+ Enabled: false
43
62
 
44
63
  # Explicite numbers are often clearer, and more robust.
45
64
  Style/NumericPredicate:
46
65
  Enabled: false
47
66
 
67
+ # Use older RuboCop default
68
+ Style/PercentLiteralDelimiters:
69
+ PreferredDelimiters:
70
+ '%W': ()
71
+ '%w': ()
72
+
48
73
  # Allow explicit return with multiple return values
49
74
  Style/RedundantReturn:
50
75
  AllowMultipleReturnValues: true
51
76
 
52
- # I prefer to have symbols look like symbols
77
+ # Do not commit to use of interpolation
78
+ Style/StringLiterals:
79
+ EnforcedStyle: double_quotes
80
+
81
+ # Prefer symbols to look like symbols
53
82
  Style/SymbolArray:
54
83
  EnforcedStyle: brackets
84
+
85
+ # Accessors are only trivial if they match the ivar name
86
+ Style/TrivialAccessors:
87
+ ExactNameMatch: true
@@ -0,0 +1,22 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2020-08-28 11:02:57 UTC using RuboCop version 0.89.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ # Configuration parameters: CountComments, CountAsOne, ExcludedMethods.
11
+ Metrics/MethodLength:
12
+ Max: 15
13
+
14
+ # Offense count: 8
15
+ # Configuration parameters: AllowSubject.
16
+ RSpec/MultipleMemoizedHelpers:
17
+ Max: 10
18
+
19
+ # Offense count: 1
20
+ Security/Eval:
21
+ Exclude:
22
+ - 'lib/keep_up/bundle.rb'
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ SimpleCov.start do
4
+ add_group "Main", "lib"
5
+ add_group "Specs", "spec"
6
+ enable_coverage :branch
7
+ end
8
+
9
+ SimpleCov.at_exit do
10
+ SimpleCov.result.format!
11
+ end
@@ -1,18 +1,24 @@
1
- sudo: false
2
1
  language: ruby
3
- rvm:
4
- - 2.2.6
5
- - 2.3.3
6
- - 2.4.0
7
2
 
8
- before_install:
9
- - rvm use @global
10
- - gem uninstall bundler -x
11
- - gem install bundler --version=1.13.7
12
- - bundler --version
3
+ dist: xenial
4
+
5
+ before_script:
13
6
  - git config --global user.email "you@example.com"
14
7
  - git config --global user.name "Your Name"
15
8
 
9
+ cache:
10
+ bundler: true
11
+
12
+ rvm:
13
+ - 2.5
14
+ - 2.6
15
+ - 2.7
16
+ - ruby-head
17
+
18
+ matrix:
19
+ allow_failures:
20
+ - rvm: ruby-head
21
+
16
22
  branches:
17
23
  only:
18
24
  - master
@@ -1,5 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.0 / 2020-09-18
4
+
5
+ * Drop support for Ruby 2.4
6
+ * Improve wording for commit messages
7
+
8
+ ## 0.8.1 / 2019-12-30
9
+
10
+ * Silence keyword arguments deprecation on Ruby 2.7
11
+
12
+ ## 0.8.0 / 2019-10-16
13
+
14
+ * Drop support for Ruby 2.3
15
+ * Handle use of `require_relative` in gemspec files
16
+
17
+ ## 0.7.1 / 2019-01-16
18
+
19
+ * Support Ruby 2.6
20
+ * Support Bundler 2.x
21
+
22
+ ## 0.7.0 / 2018-12-10
23
+
24
+ * Interface with Bundler via its CLI
25
+ * Perform a real `bundle update` for each update dependency. This means gems
26
+ will actually be installed in each step.
27
+ * Drop support for Bundler versions below 1.15.
28
+ * Delegate finding available update candidates to Bundler.
29
+ * Gracefully handle git dependencies when collecting update candidates
30
+ * Do not attempt to update requirements with more than one element
31
+ * Restore effect of the `--local` flag.
32
+
3
33
  ## 0.6.3 / 2017-10-27
4
34
 
5
35
  * Filter out pre-releases when searching for dependencies to support
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
- source 'https://rubygems.org'
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
2
4
 
3
5
  # Specify your gem's dependencies in keep_up.gemspec
4
6
  gemspec
data/README.md CHANGED
@@ -17,7 +17,7 @@ Before running KeepUp, it's probably nice to start a new branch. Also, KeepUp
17
17
  will refuse to run if your checkout directory is not clean, or if your
18
18
  Gemfile.lock is not up-to-date.
19
19
 
20
- Run keep_up in your project directory:
20
+ Run `keep_up` in your project directory:
21
21
 
22
22
  $ keep_up
23
23
 
@@ -64,8 +64,9 @@ git commits and tags, and push the `.gem` file to
64
64
 
65
65
  ## Contributing
66
66
 
67
- Bug reports and pull requests are welcome on GitHub at
68
- https://github.com/mvz/keep_up. This project is intended to be a safe,
67
+ Bug reports and pull requests are welcome at
68
+ [`keep_up`'s repository](https://github.com/mvz/keep_up) on GitHub.
69
+ This project is intended to be a safe,
69
70
  welcoming space for collaboration, and contributors are expected to adhere to
70
71
  the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
71
72
 
data/Rakefile CHANGED
@@ -1,8 +1,13 @@
1
- require 'bundler/gem_tasks'
2
- require 'rspec/core/rake_task'
3
- require 'cucumber/rake/task'
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+ require "cucumber/rake/task"
6
+
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.ruby_opts = ["-rbundler/setup -rsimplecov -w"]
9
+ end
4
10
 
5
- RSpec::Core::RakeTask.new(:spec)
6
11
  Cucumber::Rake::Task.new(:features)
7
12
 
8
13
  task default: [:spec, :features]
@@ -1,29 +1,39 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require 'optparse'
4
- require_relative '../lib/keep_up'
4
+ require "optparse"
5
+ require_relative "../lib/keep_up"
5
6
 
6
7
  options = {
7
8
  local: false,
8
- test_command: 'bundle exec rake',
9
+ test_command: "bundle exec rake",
9
10
  skip: []
10
11
  }
11
- OptionParser.new do |parser|
12
- parser.on('--[no-]local') do |local|
12
+
13
+ opt_parser = OptionParser.new do |parser|
14
+ parser.on("--[no-]local", "Only consider locally installed gems") do |local|
13
15
  options[:local] = local
14
16
  end
15
-
16
- parser.on('--test-command=COMMAND') do |command|
17
+ parser.on("--test-command=COMMAND", "Run COMMAND to test each update") do |command|
17
18
  options[:test_command] = command
18
19
  end
19
-
20
- parser.on('--skip=SKIPPED_GEM') do |gemname|
20
+ parser.on("--skip=SKIPPED_GEM", "Do not consider SKIPPED_GEM for updating") do |gemname|
21
21
  options[:skip] << gemname
22
22
  end
23
- end.parse!
23
+ parser.on_tail("-h", "--help", "Show this message") do
24
+ puts parser
25
+ exit
26
+ end
27
+ parser.on_tail("-v", "--version", "Show version") do
28
+ puts "#{parser.program_name} #{KeepUp::VERSION}\n"
29
+ exit
30
+ end
31
+ end
32
+
33
+ opt_parser.parse!
24
34
 
25
35
  begin
26
- KeepUp::Application.new(options).run
36
+ KeepUp::Application.new(**options).run
27
37
  rescue KeepUp::BailOut => e
28
38
  warn e.message
29
39
  exit 1
@@ -1,31 +1,36 @@
1
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'keep_up/version'
5
+ require "keep_up/version"
4
6
 
5
7
  Gem::Specification.new do |spec|
6
- spec.name = 'keep_up'
8
+ spec.name = "keep_up"
7
9
  spec.version = KeepUp::VERSION
8
- spec.authors = ['Matijs van Zuijlen']
9
- spec.email = ['matijs@matijs.net']
10
+ spec.authors = ["Matijs van Zuijlen"]
11
+ spec.email = ["matijs@matijs.net"]
10
12
 
11
- spec.summary = 'Automatically update your dependencies'
13
+ spec.summary = "Automatically update your dependencies"
12
14
  spec.description =
13
- 'Automatically update the dependencies listed in your Gemfile,' \
14
- ' Gemfile.lock, and gemspec.'
15
- spec.homepage = 'https://github.com/mvz/keep_up'
16
- spec.license = 'MIT'
15
+ "Automatically update the dependencies listed in your Gemfile," \
16
+ " Gemfile.lock, and gemspec."
17
+ spec.homepage = "https://github.com/mvz/keep_up"
18
+ spec.license = "MIT"
17
19
 
18
- spec.files = `git ls-files -z`.split("\x0").
19
- reject { |f| f.match(%r{^(test|script|spec|features)/}) }
20
+ spec.files = `git ls-files -z`.split("\x0")
21
+ .reject { |f| f.match(%r{^(test|script|spec|features)/}) }
20
22
 
21
- spec.bindir = 'bin'
23
+ spec.bindir = "bin"
22
24
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
23
- spec.require_paths = ['lib']
25
+ spec.require_paths = ["lib"]
26
+
27
+ spec.required_ruby_version = ">= 2.5.0"
24
28
 
25
- spec.add_runtime_dependency 'bundler', '~> 1.13'
29
+ spec.add_runtime_dependency "bundler", [">= 1.15", "< 3.0"]
26
30
 
27
- spec.add_development_dependency 'aruba', '~> 0.14.2'
28
- spec.add_development_dependency 'pry'
29
- spec.add_development_dependency 'rake', '~> 12.0'
30
- spec.add_development_dependency 'rspec', '~> 3.0'
31
+ spec.add_development_dependency "aruba", "~> 1.0"
32
+ spec.add_development_dependency "cucumber", "~> 5.0"
33
+ spec.add_development_dependency "rake", "~> 13.0"
34
+ spec.add_development_dependency "rspec", "~> 3.0"
35
+ spec.add_development_dependency "simplecov", "~> 0.19.0"
31
36
  end
@@ -1,5 +1,7 @@
1
- require_relative 'keep_up/version'
2
- require_relative 'keep_up/application'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "keep_up/version"
4
+ require_relative "keep_up/application"
3
5
 
4
6
  # Main KeepUp namespace
5
7
  module KeepUp
@@ -1,13 +1,11 @@
1
- require 'bundler'
2
- require 'open3'
3
- require_relative 'bundle'
4
- require_relative 'bundler_definition_builder'
5
- require_relative 'gem_index'
6
- require_relative 'null_filter'
7
- require_relative 'repository'
8
- require_relative 'skip_filter'
9
- require_relative 'updater'
10
- require_relative 'version_control'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "runner"
4
+ require_relative "bundle"
5
+ require_relative "null_filter"
6
+ require_relative "skip_filter"
7
+ require_relative "updater"
8
+ require_relative "version_control"
11
9
 
12
10
  module KeepUp
13
11
  # Error thrown when we can't go any further.
@@ -16,50 +14,53 @@ module KeepUp
16
14
 
17
15
  # Main application
18
16
  class Application
19
- def initialize(local:, test_command:, skip:)
17
+ def initialize(local:, test_command:, skip:, runner: Runner)
20
18
  @test_command = test_command
21
19
  @local = local
22
20
  @skip = skip
21
+ @runner = runner
23
22
  end
24
23
 
25
24
  def run
26
- sanity_check
25
+ check_version_control_clean
26
+ check_bundle_lockfile
27
27
  update_all_dependencies
28
28
  report_done
29
29
  end
30
30
 
31
31
  private
32
32
 
33
- attr_reader :skip, :local
33
+ attr_reader :skip, :local, :runner
34
+
35
+ def check_version_control_clean
36
+ return if version_control.clean?
37
+
38
+ raise BailOut, "Commit or stash your work before running 'keep_up'"
39
+ end
40
+
41
+ def check_bundle_lockfile
42
+ return if bundle.check? && version_control.clean?
34
43
 
35
- def sanity_check
36
- version_control.clean? or
37
- raise BailOut, "Commit or stash your work before running 'keep_up'"
38
- bundle.check? or
39
- raise BailOut, "Make sure your Gemfile.lock is up-to-date before running 'keep_up'"
44
+ version_control.revert_changes
45
+ raise BailOut, "Make sure your Gemfile.lock is up-to-date before running 'keep_up'"
40
46
  end
41
47
 
42
48
  def update_all_dependencies
43
49
  Updater.new(bundle: bundle,
44
- repository: Repository.new(index: index),
45
50
  version_control: version_control,
46
51
  filter: filter).run
47
52
  end
48
53
 
49
54
  def version_control
50
- @version_control ||= VersionControl.new
55
+ @version_control ||= VersionControl.new(runner: runner)
51
56
  end
52
57
 
53
58
  def bundle
54
- @bundle ||= Bundle.new(definition_builder: definition_builder)
59
+ @bundle ||= Bundle.new(runner: runner, local: local)
55
60
  end
56
61
 
57
62
  def report_done
58
- puts 'All done!'
59
- end
60
-
61
- def definition_builder
62
- @definition_builder ||= BundlerDefinitionBuilder.new(local: local)
63
+ puts "All done!"
63
64
  end
64
65
 
65
66
  def filter
@@ -69,9 +70,5 @@ module KeepUp
69
70
  NullFilter.new
70
71
  end
71
72
  end
72
-
73
- def index
74
- GemIndex.new(definition_builder: definition_builder)
75
- end
76
73
  end
77
74
  end
@@ -1,132 +1,124 @@
1
- require 'bundler'
2
- require_relative 'gemfile_filter'
3
- require_relative 'gemspec_filter'
4
- require_relative 'dependency'
5
- require_relative 'one'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "gemfile_filter"
4
+ require_relative "gemspec_filter"
5
+ require_relative "dependency"
6
6
 
7
7
  module KeepUp
8
8
  # A Gemfile with its current set of locked dependencies.
9
9
  class Bundle
10
- def initialize(definition_builder:)
11
- @definition_builder = definition_builder
12
- end
10
+ OUTDATED_MATCHER =
11
+ /([^ ]*) \(newest ([^,]*), installed ([^,]*)(?:, requested (.*))?\)/.freeze
12
+ UPDATE_MATCHER =
13
+ /(?:Using|Installing|Fetching) ([^ ]*) ([^ ]*)(?: \(was (.*))?\)/.freeze
13
14
 
14
- def dependencies
15
- gemspec_dependencies + gemfile_dependencies + transitive_dependencies
15
+ def initialize(runner:, local:)
16
+ @runner = runner
17
+ @local = local
16
18
  end
17
19
 
18
- def apply_updated_dependency(dependency)
19
- report_intent dependency
20
- update_gemfile_contents(dependency)
21
- update_gemspec_contents(dependency)
22
- result = update_lockfile(dependency)
23
- report_result dependency, result
24
- result
20
+ def dependencies
21
+ @dependencies ||=
22
+ begin
23
+ command = "bundle outdated --parseable#{' --local' if @local}"
24
+ lines = run_filtered command, OUTDATED_MATCHER
25
+ lines.map do |name, newest, version, requirement|
26
+ requirement_list = requirement&.split(/,\s*/)
27
+ requirement_list ||= fetch_gemspec_dependency_requirements(name)
28
+ version = version.split(" ").first
29
+ newest = newest.split(" ").first
30
+ Dependency.new(name: name,
31
+ locked_version: version,
32
+ newest_version: newest,
33
+ requirement_list: requirement_list)
34
+ end
35
+ end
25
36
  end
26
37
 
27
38
  def check?
28
- bundler_definition.to_lock == File.read('Gemfile.lock')
39
+ _, status = @runner.run2 "bundle check"
40
+ status == 0
29
41
  end
30
42
 
31
- private
32
-
33
- attr_reader :definition_builder
43
+ def update_gemfile_contents(update)
44
+ update = find_specification_update(dependencies, update)
45
+ return unless update
34
46
 
35
- def report_intent(dependency)
36
- print "Updating #{dependency.name}"
47
+ update_specification_contents(update, "Gemfile", GemfileFilter)
37
48
  end
38
49
 
39
- def report_result(dependency, result)
40
- if result
41
- puts " to #{result.version}"
42
- else
43
- puts " to #{dependency.version}"
44
- puts 'Update failed'
45
- end
46
- end
50
+ def update_gemspec_contents(update)
51
+ return unless gemspec_name
47
52
 
48
- def gemfile_dependencies
49
- raw = if Bundler::VERSION >= '1.15.'
50
- bundler_lockfile.dependencies.values
51
- else
52
- bundler_lockfile.dependencies
53
- end
54
- build_dependencies raw
55
- end
53
+ update = find_specification_update(dependencies, update)
54
+ return unless update
56
55
 
57
- def gemspec_dependencies
58
- gemspec_source = bundler_lockfile.sources.
59
- find { |it| it.is_a? Bundler::Source::Gemspec }
60
- return [] unless gemspec_source
61
- build_dependencies gemspec_source.gemspec.dependencies
56
+ update_specification_contents(update, gemspec_name, GemspecFilter)
62
57
  end
63
58
 
64
- def transitive_dependencies
65
- build_dependencies bundler_lockfile.specs.flat_map(&:dependencies).uniq
59
+ # Update lockfile and return resulting spec, or false in case of failure
60
+ def update_lockfile(update)
61
+ update_name = update.name
62
+ command = "bundle update#{' --local' if @local} --conservative #{update_name}"
63
+ lines = run_filtered command, UPDATE_MATCHER
64
+ lines.each do |name, version, old_version|
65
+ next unless name == update_name && old_version
66
+
67
+ current = Gem::Specification.new(name, old_version)
68
+ result = Gem::Specification.new(name, version)
69
+ return result if result.version > current.version
70
+ end
71
+ nil
66
72
  end
67
73
 
68
- def build_dependencies(deps)
69
- deps.map { |dep| build_dependency dep }.compact
70
- end
74
+ private
71
75
 
72
- def build_dependency(dep)
73
- spec = locked_spec dep
74
- return unless spec
75
- Dependency.new(name: dep.name,
76
- requirement_list: dep.requirement.as_list,
77
- locked_version: spec.version)
76
+ def gemspec
77
+ @gemspec ||= if gemspec_name
78
+ gemspec_path = File.expand_path(gemspec_name)
79
+ eval File.read(gemspec_name), nil, gemspec_path
80
+ end
78
81
  end
79
82
 
80
- def locked_spec(dep)
81
- bundler_lockfile.specs.find { |it| it.name == dep.name }
83
+ def gemspec_dependencies
84
+ @gemspec_dependencies ||= if gemspec
85
+ gemspec.dependencies
86
+ else
87
+ []
88
+ end
82
89
  end
83
90
 
84
- def bundler_lockfile
85
- @bundler_lockfile ||= bundler_definition.locked_gems
86
- end
91
+ def fetch_gemspec_dependency_requirements(name)
92
+ dep = gemspec_dependencies.find { |it| it.name == name }
93
+ return unless dep
87
94
 
88
- def bundler_definition
89
- @bundler_definition ||= definition_builder.build(false)
95
+ dep.requirements_list
90
96
  end
91
97
 
92
- def update_gemfile_contents(update)
93
- current_dependency = gemfile_dependencies.find { |it| it.name == update.name }
94
- return unless current_dependency
95
- return if current_dependency.matches_spec?(update)
96
-
97
- update = current_dependency.generalize_specification(update)
98
+ def find_specification_update(current_dependencies, update)
99
+ current_dependency = current_dependencies.find { |it| it.name == update.name }
100
+ return if !current_dependency || current_dependency.matches_spec?(update)
98
101
 
99
- contents = File.read 'Gemfile'
100
- updated_contents = GemfileFilter.apply(contents, update)
101
- File.write 'Gemfile', updated_contents
102
+ current_dependency.generalize_specification(update)
102
103
  end
103
104
 
104
- def update_gemspec_contents(update)
105
- current_dependency = gemspec_dependencies.find { |it| it.name == update.name }
106
- return unless current_dependency
107
- return if current_dependency.matches_spec?(update)
108
-
109
- update = current_dependency.generalize_specification(update)
110
-
111
- contents = File.read gemspec_name
112
- updated_contents = GemspecFilter.apply(contents, update)
113
- File.write gemspec_name, updated_contents
105
+ def update_specification_contents(update, file, filter)
106
+ File.write file, filter.apply(File.read(file), update)
114
107
  end
115
108
 
116
109
  def gemspec_name
117
- @gemspec_name ||= One.fetch(Dir.glob('*.gemspec'))
110
+ @gemspec_name ||= Dir.glob("*.gemspec").first
118
111
  end
119
112
 
120
- # Update lockfile and return resulting spec, or false in case of failure
121
- def update_lockfile(update)
122
- Bundler.clear_gemspec_cache
123
- definition = definition_builder.build(gems: [update.name])
124
- definition.lock('Gemfile.lock')
125
- current = locked_spec(update)
126
- result = definition.specs.find { |it| it.name == update.name }
127
- result if result.version > current.version
128
- rescue Bundler::VersionConflict
129
- false
113
+ def run_filtered(command, regexp)
114
+ result = @runner.run command
115
+ lines = result.split("\n").reject(&:empty?)
116
+ lines.map do |line|
117
+ matchdata = regexp.match line
118
+ next unless matchdata
119
+
120
+ matchdata.to_a[1..-1]
121
+ end.compact
130
122
  end
131
123
  end
132
124
  end
@@ -1,13 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KeepUp
2
4
  # Single dependency with its current locked version.
3
5
  class Dependency
4
- def initialize(name:, requirement_list:, locked_version:)
6
+ def initialize(name:, requirement_list:, locked_version:, newest_version:)
5
7
  @name = name
6
8
  @requirement_list = requirement_list
7
9
  @locked_version = Gem::Version.new locked_version
10
+ @newest_version = Gem::Version.new newest_version
8
11
  end
9
12
 
10
- attr_reader :name, :locked_version
13
+ attr_reader :name, :locked_version, :newest_version
11
14
 
12
15
  def requirement
13
16
  @requirement ||= Gem::Requirement.new @requirement_list
@@ -19,15 +22,18 @@ module KeepUp
19
22
 
20
23
  def generalize_specification(specification)
21
24
  return specification if requirement.exact?
25
+
22
26
  segments = specification.version.segments
23
27
  return specification if segments.count <= segment_count
24
- version = segments.take(segment_count).join('.')
28
+
29
+ version = segments.take(segment_count).join(".")
25
30
  Gem::Specification.new(specification.name, version)
26
31
  end
27
32
 
28
33
  def ==(other)
29
34
  other.name == name &&
30
35
  other.locked_version == locked_version &&
36
+ other.newest_version == newest_version &&
31
37
  other.requirement == requirement
32
38
  end
33
39
 
@@ -1,15 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KeepUp
2
4
  # Filter to update dependency information in a Gemfile.
3
5
  module GemfileFilter
4
6
  def self.apply(contents, dependency)
7
+ matcher = dependency_matcher(dependency)
5
8
  contents.each_line.map do |line|
6
- if line =~ /^(\s*gem\s+['"]#{dependency.name}['"],\s+['"](~> *)?)[^'"]*(['"].*)/m
9
+ if line =~ matcher
7
10
  match = Regexp.last_match
8
- "#{match[1]}#{dependency.version}#{match[3]}"
11
+ "#{match[1]}#{dependency.version}#{match[2]}"
9
12
  else
10
13
  line
11
14
  end
12
15
  end.join
13
16
  end
17
+
18
+ def self.dependency_matcher(dependency)
19
+ /
20
+ ^(\s*gem\s+['"]#{dependency.name}['"],
21
+ \s+\[?['"](?:~>|=)?\ *)
22
+ [^'"]*
23
+ (['"]\]?[^\]]*)
24
+ $
25
+ /mx
26
+ end
14
27
  end
15
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KeepUp
2
4
  # Filter to update dependency information in a Gemspec.
3
5
  module GemspecFilter
@@ -18,7 +20,9 @@ module KeepUp
18
20
  ^(.*_dependency
19
21
  \s*(?:\(\s*)?
20
22
  (?:['"]|%q.)#{dependency.name}.(?:\.freeze)?,
21
- \s+\[?['"](?:~>|=)?\ *)[^'"]*(['"].*)
23
+ \s+\[?['"](?:~>|=)?\ *)
24
+ [^'"]*
25
+ (['"]\]?[^'"]*)$
22
26
  /mx
23
27
  end
24
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KeepUp
2
4
  # Simple filter that acccepts everything.
3
5
  class NullFilter
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module KeepUp
6
+ # Encapsulate knowledge of running external commands
7
+ module Runner
8
+ module_function
9
+
10
+ def run(command)
11
+ stdout, = run2 command
12
+ stdout
13
+ end
14
+
15
+ def run2(command)
16
+ Open3.capture2 command
17
+ end
18
+ end
19
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KeepUp
2
4
  # Filter that skips dependencies if their name is on the list of things to be skipped.
3
5
  class SkipFilter
@@ -1,20 +1,23 @@
1
- require_relative 'null_filter'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "null_filter"
2
4
 
3
5
  module KeepUp
4
6
  # Apply potential updates to a Gemfile.
5
7
  class Updater
6
- attr_reader :bundle, :repository, :version_control, :filter
8
+ attr_reader :bundle, :version_control, :filter
7
9
 
8
- def initialize(bundle:, repository:, version_control:, filter: NullFilter.new)
10
+ def initialize(bundle:, version_control:,
11
+ filter: NullFilter.new, out: $stdout)
9
12
  @bundle = bundle
10
- @repository = repository
11
13
  @version_control = version_control
12
14
  @filter = filter
15
+ @out = out
13
16
  end
14
17
 
15
18
  def run
16
19
  possible_updates.each do |update|
17
- result = bundle.apply_updated_dependency update
20
+ result = apply_updated_dependency update
18
21
  if result
19
22
  version_control.commit_changes result
20
23
  else
@@ -24,9 +27,41 @@ module KeepUp
24
27
  end
25
28
 
26
29
  def possible_updates
27
- bundle.dependencies.
28
- select { |dep| filter.call dep }.
29
- map { |dep| repository.updated_dependency_for dep }.compact.uniq
30
+ bundle.dependencies
31
+ .select { |dep| filter.call dep }
32
+ .map { |dep| updated_dependency_for dep }.compact.uniq
33
+ end
34
+
35
+ private
36
+
37
+ def apply_updated_dependency(dependency)
38
+ report_intent dependency
39
+ bundle.update_gemfile_contents(dependency)
40
+ bundle.update_gemspec_contents(dependency)
41
+ result = bundle.update_lockfile(dependency)
42
+ report_result dependency, result
43
+ result
44
+ end
45
+
46
+ def report_intent(dependency)
47
+ @out.print "Updating #{dependency.name}"
48
+ end
49
+
50
+ def report_result(dependency, result)
51
+ if result
52
+ @out.puts " to #{result.version}"
53
+ else
54
+ @out.puts " to #{dependency.version}"
55
+ @out.puts "Update failed"
56
+ end
57
+ end
58
+
59
+ def updated_dependency_for(dependency)
60
+ locked_version = dependency.locked_version
61
+ newest_version = dependency.newest_version
62
+ return unless newest_version > locked_version
63
+
64
+ Gem::Specification.new(dependency.name, newest_version)
30
65
  end
31
66
  end
32
67
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KeepUp
2
- VERSION = '0.6.3'.freeze
4
+ VERSION = "0.9.0"
3
5
  end
@@ -1,16 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KeepUp
2
4
  # Interface to the version control system (only Git is supported).
3
5
  class VersionControl
6
+ def initialize(runner:)
7
+ @runner = runner
8
+ end
9
+
4
10
  def commit_changes(dependency)
5
- `git commit -am "Auto-update #{dependency.name} to #{dependency.version}"`
11
+ @runner.run "git commit -am 'Update #{dependency.name} to version #{dependency.version}'"
6
12
  end
7
13
 
8
14
  def revert_changes
9
- `git reset --hard`
15
+ @runner.run "git reset --hard"
10
16
  end
11
17
 
12
18
  def clean?
13
- `git status -s` == ''
19
+ @runner.run("git status -s") == ""
14
20
  end
15
21
  end
16
22
  end
metadata CHANGED
@@ -1,71 +1,77 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keep_up
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matijs van Zuijlen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-27 00:00:00.000000000 Z
11
+ date: 2020-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ - - "<"
18
21
  - !ruby/object:Gem::Version
19
- version: '1.13'
22
+ version: '3.0'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: '1.13'
29
+ version: '1.15'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: aruba
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
37
  - - "~>"
32
38
  - !ruby/object:Gem::Version
33
- version: 0.14.2
39
+ version: '1.0'
34
40
  type: :development
35
41
  prerelease: false
36
42
  version_requirements: !ruby/object:Gem::Requirement
37
43
  requirements:
38
44
  - - "~>"
39
45
  - !ruby/object:Gem::Version
40
- version: 0.14.2
46
+ version: '1.0'
41
47
  - !ruby/object:Gem::Dependency
42
- name: pry
48
+ name: cucumber
43
49
  requirement: !ruby/object:Gem::Requirement
44
50
  requirements:
45
- - - ">="
51
+ - - "~>"
46
52
  - !ruby/object:Gem::Version
47
- version: '0'
53
+ version: '5.0'
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
- - - ">="
58
+ - - "~>"
53
59
  - !ruby/object:Gem::Version
54
- version: '0'
60
+ version: '5.0'
55
61
  - !ruby/object:Gem::Dependency
56
62
  name: rake
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
65
  - - "~>"
60
66
  - !ruby/object:Gem::Version
61
- version: '12.0'
67
+ version: '13.0'
62
68
  type: :development
63
69
  prerelease: false
64
70
  version_requirements: !ruby/object:Gem::Requirement
65
71
  requirements:
66
72
  - - "~>"
67
73
  - !ruby/object:Gem::Version
68
- version: '12.0'
74
+ version: '13.0'
69
75
  - !ruby/object:Gem::Dependency
70
76
  name: rspec
71
77
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +86,20 @@ dependencies:
80
86
  - - "~>"
81
87
  - !ruby/object:Gem::Version
82
88
  version: '3.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: simplecov
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: 0.19.0
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: 0.19.0
83
103
  description: Automatically update the dependencies listed in your Gemfile, Gemfile.lock,
84
104
  and gemspec.
85
105
  email:
@@ -91,8 +111,11 @@ extra_rdoc_files: []
91
111
  files:
92
112
  - ".dockerignore"
93
113
  - ".gitignore"
114
+ - ".hound.yml"
94
115
  - ".rspec"
95
116
  - ".rubocop.yml"
117
+ - ".rubocop_todo.yml"
118
+ - ".simplecov"
96
119
  - ".travis.yml"
97
120
  - CHANGELOG.md
98
121
  - CODE_OF_CONDUCT.md
@@ -106,14 +129,11 @@ files:
106
129
  - lib/keep_up.rb
107
130
  - lib/keep_up/application.rb
108
131
  - lib/keep_up/bundle.rb
109
- - lib/keep_up/bundler_definition_builder.rb
110
132
  - lib/keep_up/dependency.rb
111
- - lib/keep_up/gem_index.rb
112
133
  - lib/keep_up/gemfile_filter.rb
113
134
  - lib/keep_up/gemspec_filter.rb
114
135
  - lib/keep_up/null_filter.rb
115
- - lib/keep_up/one.rb
116
- - lib/keep_up/repository.rb
136
+ - lib/keep_up/runner.rb
117
137
  - lib/keep_up/skip_filter.rb
118
138
  - lib/keep_up/updater.rb
119
139
  - lib/keep_up/version.rb
@@ -130,15 +150,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
130
150
  requirements:
131
151
  - - ">="
132
152
  - !ruby/object:Gem::Version
133
- version: '0'
153
+ version: 2.5.0
134
154
  required_rubygems_version: !ruby/object:Gem::Requirement
135
155
  requirements:
136
156
  - - ">="
137
157
  - !ruby/object:Gem::Version
138
158
  version: '0'
139
159
  requirements: []
140
- rubyforge_project:
141
- rubygems_version: 2.6.13
160
+ rubygems_version: 3.1.2
142
161
  signing_key:
143
162
  specification_version: 4
144
163
  summary: Automatically update your dependencies
@@ -1,26 +0,0 @@
1
- require 'bundler'
2
-
3
- module KeepUp
4
- # Creates Bunder::Definition objects.
5
- class BundlerDefinitionBuilder
6
- def initialize(local: false)
7
- @local = local
8
- end
9
-
10
- def build(lock)
11
- definition = Bundler::Definition.build('Gemfile', 'Gemfile.lock', lock)
12
- if lock
13
- if local
14
- definition.resolve_with_cache!
15
- else
16
- definition.resolve_remotely!
17
- end
18
- end
19
- definition
20
- end
21
-
22
- private
23
-
24
- attr_reader :local
25
- end
26
- end
@@ -1,24 +0,0 @@
1
- module KeepUp
2
- # Searches possibly remote gem index to find potential dependency updates.
3
- class GemIndex
4
- def initialize(definition_builder:)
5
- @definition_builder = definition_builder
6
- end
7
-
8
- def search(dependency)
9
- index.search(Bundler::Dependency.new(dependency.name, nil))
10
- end
11
-
12
- private
13
-
14
- attr_reader :definition_builder
15
-
16
- def definition
17
- @definition ||= definition_builder.build(true)
18
- end
19
-
20
- def index
21
- @index ||= definition.index
22
- end
23
- end
24
- end
@@ -1,13 +0,0 @@
1
- module KeepUp
2
- # Fetch value from an array with exactly one element
3
- module One
4
- def self.fetch(arr)
5
- case arr.count
6
- when 1
7
- arr.first
8
- else
9
- raise 'Expected exactly one element'
10
- end
11
- end
12
- end
13
- end
@@ -1,16 +0,0 @@
1
- module KeepUp
2
- # Picks updated versions for dependencies.
3
- class Repository
4
- attr_reader :index
5
-
6
- def initialize(index:)
7
- @index = index
8
- end
9
-
10
- def updated_dependency_for(dependency)
11
- candidates = index.search(dependency).reject { |it| it.version.prerelease? }
12
- latest = candidates.sort_by(&:version).last
13
- latest unless latest.version <= dependency.locked_version
14
- end
15
- end
16
- end