keep_up 0.6.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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