bundleup 2.0.0 → 2.2.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
2
  SHA256:
3
- metadata.gz: 82ad26069759aeaa4516bb5305da2d0d75a38e1c30d8efd275b3fd09df6f2697
4
- data.tar.gz: 1cec30ec08cf62b3332c6b8b6539c4231fbf6b1bc29f2c8a4069b5afff2d0b83
3
+ metadata.gz: '02583d0ea6a0b389400ed3c24f312effae81880bbf2409ac25136681f6cd0028'
4
+ data.tar.gz: e1ffadae2408a04df7d336531b41329e9de416d4b3149644768ef526658561d0
5
5
  SHA512:
6
- metadata.gz: 388e5e6c1ca95b073319c7cd5954348190c2ff1a2e63836659a966eebc7c4da8583474434c33a979d0f70bc69e02686d8a0c4829fcba4123e25b675665af2ee4
7
- data.tar.gz: 651c38cc47925f3bafbbc30b50d777c58dfc2918ca3a1329c182ae38735d6e33fb287cb6ba9096b878c4c137746cdc6baa18726362a02eb036ab7a63db9e77a7
6
+ metadata.gz: e36564061fd34a361512543e26ded1f2158beb57f019eb3c67e8e1f626f00f1af02d83df19626c36632e07d0ffc1163507a006dacc2080a42fa1c522d6cfdeaa
7
+ data.tar.gz: db630caef95d92d94c68e83dbd82a9cd1bec082943f377efb1655361802e2f11c86f7800956ae71f7748caa5d22347ee90d5a9d87e83c51087a26bdef7727772
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2020 Matt Brictson
3
+ Copyright (c) 2021 Matt Brictson
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # bundleup
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/bundleup.svg)](http://badge.fury.io/rb/bundleup)
4
- [![Build Status](https://travis-ci.org/mattbrictson/bundleup.svg?branch=main)](https://travis-ci.org/mattbrictson/bundleup)
4
+ [![Build Status](https://circleci.com/gh/mattbrictson/bundleup/tree/main.svg?style=shield)](https://app.circleci.com/pipelines/github/mattbrictson/bundleup?branch=main)
5
5
 
6
6
  **Run `bundleup` on a Ruby project containing a Gemfile to see what gem dependencies need updating.** It is a friendlier command-line interface to [Bundler’s][bundler] `bundle update` and `bundle outdated`.
7
7
 
@@ -19,7 +19,7 @@ Here it is in action:
19
19
  ## Requirements
20
20
 
21
21
  - Bundler 1.16 or later
22
- - Ruby 2.5 or later
22
+ - Ruby 2.6 or later
23
23
 
24
24
  ## Usage
25
25
 
@@ -44,7 +44,37 @@ Protip: Any extra command-line arguments will be passed along to `bundle update`
44
44
  bundleup --group=development
45
45
  ```
46
46
 
47
- ## How it works
47
+ ### Experimental: `--update-gemfile`
48
+
49
+ > 💡 This is an experimental feature that may be removed or changed in future versions.
50
+
51
+ Normally bundleup only makes changes to your Gemfile.lock. It honors the version restrictions ("pins") in your Gemfile and will not update your Gemfile.lock to have versions that are not allowed. However with the `--update-gemfile` flag, bundleup can update the version pins in your Gemfile as well. Consider the following Gemfile:
52
+
53
+ ```ruby
54
+ gem 'sidekiq', '~> 5.2'
55
+ gem 'rubocop', '0.89.0'
56
+ ```
57
+
58
+ Normally running `bundleup` will report that these gems are pinned and therefore cannot be updated to the latest versions. However, if you pass the `--update-gemfile` option like this:
59
+
60
+ ```
61
+ $ bundleup --update-gemfile
62
+ ```
63
+
64
+ Now bundleup will automatically edit your Gemfile pins as needed to bring those gems up to date. For example, bundleup would change the Gemfile to look like this:
65
+
66
+ ```ruby
67
+ gem 'sidekiq', '~> 6.1'
68
+ gem 'rubocop', '0.90.0'
69
+ ```
70
+
71
+ Note that `--update-gemfile` will _not_ modify Gemfile entries that contain a comment, like this:
72
+
73
+ ```ruby
74
+ gem 'sidekiq', '~> 5.2' # our monkey patch doesn't work on 6.0+
75
+ ```
76
+
77
+ ## How bundleup works
48
78
 
49
79
  bundleup starts by making a backup copy of your Gemfile.lock. Next it runs `bundle check` (and `bundle install` if any gems are missing in your local environment), `bundle list`, then `bundle update` and `bundle list` again to find what gems versions are being used before and after Bundler does its updating magic. (Since gems are actually being installed into your Ruby environment during these steps, the process may take a few moments to complete, especially if gems with native extensions need to be compiled.)
50
80
 
data/lib/bundleup.rb CHANGED
@@ -9,6 +9,7 @@ require "bundleup/report"
9
9
  require "bundleup/shell"
10
10
  require "bundleup/pin_report"
11
11
  require "bundleup/update_report"
12
+ require "bundleup/version_spec"
12
13
 
13
14
  module Bundleup
14
15
  class << self
@@ -1,7 +1,7 @@
1
1
  module Bundleup
2
2
  class Backup
3
- def self.restore_on_error(path)
4
- backup = new(path)
3
+ def self.restore_on_error(*paths)
4
+ backup = new(*paths)
5
5
  begin
6
6
  yield(backup)
7
7
  rescue StandardError, Interrupt
@@ -10,17 +10,20 @@ module Bundleup
10
10
  end
11
11
  end
12
12
 
13
- def initialize(path)
14
- @path = path
15
- @original_contents = IO.read(path)
13
+ def initialize(*paths)
14
+ @original_contents = paths.each_with_object({}) do |path, hash|
15
+ hash[path] = IO.read(path)
16
+ end
16
17
  end
17
18
 
18
19
  def restore
19
- IO.write(path, original_contents)
20
+ original_contents.each do |path, contents|
21
+ IO.write(path, contents)
22
+ end
20
23
  end
21
24
 
22
25
  private
23
26
 
24
- attr_reader :path, :original_contents
27
+ attr_reader :original_contents
25
28
  end
26
29
  end
data/lib/bundleup/cli.rb CHANGED
@@ -9,7 +9,8 @@ module Bundleup
9
9
  def_delegators :Bundleup, :commands, :logger
10
10
 
11
11
  def initialize(args)
12
- @args = args
12
+ @args = args.dup
13
+ @update_gemfile = @args.delete("--update-gemfile")
13
14
  end
14
15
 
15
16
  def run # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
@@ -18,24 +19,24 @@ module Bundleup
18
19
  assert_gemfile_and_lock_exist!
19
20
 
20
21
  logger.puts "Please wait a moment while I upgrade your Gemfile.lock..."
21
- Backup.restore_on_error("Gemfile.lock") do |backup|
22
- update_report, pin_report = perform_analysis
23
-
24
- if update_report.empty?
25
- logger.ok "Nothing to update."
26
- logger.puts "\n#{pin_report}" unless pin_report.empty?
27
- break
28
- end
29
-
30
- logger.puts
31
- logger.puts update_report
32
- logger.puts pin_report unless pin_report.empty?
33
-
34
- if logger.confirm?("Do you want to apply these changes?")
35
- logger.ok "Done!"
36
- else
37
- backup.restore
38
- logger.puts "Your original Gemfile.lock has been restored."
22
+ Backup.restore_on_error("Gemfile", "Gemfile.lock") do |backup|
23
+ perform_analysis_and_optionally_bump_gemfile_versions do |update_report, pin_report|
24
+ if update_report.empty?
25
+ logger.ok "Nothing to update."
26
+ logger.puts "\n#{pin_report}" unless pin_report.empty?
27
+ break
28
+ end
29
+
30
+ logger.puts
31
+ logger.puts update_report
32
+ logger.puts pin_report unless pin_report.empty?
33
+
34
+ if logger.confirm?("Do you want to apply these changes?")
35
+ logger.ok "Done!"
36
+ else
37
+ backup.restore
38
+ logger.puts "Your original Gemfile.lock has been restored."
39
+ end
39
40
  end
40
41
  end
41
42
  end
@@ -80,6 +81,13 @@ module Bundleup
80
81
  these will be passed through to bundler. See #{blue('bundle update --help')} for the
81
82
  full list of the options that bundler supports.
82
83
 
84
+ Finally, bundleup also supports an experimental #{yellow('--update-gemfile')} option.
85
+ If specified, bundleup with modify the version restrictions specified in
86
+ your Gemfile so that it can install the latest version of each gem. For
87
+ instance, if your Gemfile specifies #{yellow('gem "sidekiq", "~> 5.2"')} but an update
88
+ to version 6.1.2 is available, bundleup will modify the Gemfile entry to
89
+ be #{yellow('gem "sidekiq", "~> 6.1"')} in order to permit the update.
90
+
83
91
  Examples:
84
92
 
85
93
  #{gray('# Update all gems')}
@@ -91,6 +99,9 @@ module Bundleup
91
99
  #{gray('# Only update the rake gem')}
92
100
  #{blue('$ bundleup rake')}
93
101
 
102
+ #{gray('# Experimental: modify Gemfile to allow the latest gem versions')}
103
+ #{blue('$ bundleup --update-gemfile')}
104
+
94
105
  USAGE
95
106
  true
96
107
  end
@@ -101,6 +112,25 @@ module Bundleup
101
112
  raise Error, "Gemfile and Gemfile.lock must both be present."
102
113
  end
103
114
 
115
+ def perform_analysis_and_optionally_bump_gemfile_versions # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
116
+ gemfile = Gemfile.new
117
+ lockfile_backup = Backup.new("Gemfile.lock")
118
+ update_report, pin_report, _, outdated_gems = perform_analysis
119
+ updatable_gems = gemfile.gem_pins_without_comments.slice(*outdated_gems.keys)
120
+
121
+ if updatable_gems.any? && @update_gemfile
122
+ lockfile_backup.restore
123
+ orig_gemfile = Gemfile.new
124
+ gemfile.relax_gem_pins!(updatable_gems.keys)
125
+ update_report, pin_report, new_versions, = perform_analysis
126
+ orig_gemfile.shift_gem_pins!(new_versions.slice(*updatable_gems.keys))
127
+ commands.install
128
+ end
129
+
130
+ logger.clear_line
131
+ yield(update_report, pin_report)
132
+ end
133
+
104
134
  def perform_analysis # rubocop:disable Metrics/AbcSize
105
135
  gem_comments = Gemfile.new.gem_comments
106
136
  commands.check? || commands.install
@@ -109,12 +139,10 @@ module Bundleup
109
139
  new_versions = commands.list
110
140
  outdated_gems = commands.outdated
111
141
 
112
- logger.clear_line
113
-
114
142
  update_report = UpdateReport.new(old_versions: old_versions, new_versions: new_versions)
115
143
  pin_report = PinReport.new(gem_versions: new_versions, outdated_gems: outdated_gems, gem_comments: gem_comments)
116
144
 
117
- [update_report, pin_report]
145
+ [update_report, pin_report, new_versions, outdated_gems]
118
146
  end
119
147
  end
120
148
  end
@@ -1,18 +1,53 @@
1
1
  module Bundleup
2
2
  class Gemfile
3
+ attr_reader :path
4
+
3
5
  def initialize(path="Gemfile")
6
+ @path = path
4
7
  @contents = IO.read(path)
5
8
  end
6
9
 
7
10
  def gem_comments
8
- gem_names.each_with_object({}) do |gem, hash|
9
- comment = inline_comment(gem) || prefix_comment(gem)
10
- hash[gem] = comment unless comment.nil?
11
+ gem_names.each_with_object({}) do |gem_name, hash|
12
+ comment = inline_comment(gem_name) || prefix_comment(gem_name)
13
+ hash[gem_name] = comment unless comment.nil?
14
+ end
15
+ end
16
+
17
+ def gem_pins_without_comments
18
+ (gem_names - gem_comments.keys).each_with_object({}) do |gem_name, hash|
19
+ next unless (match = gem_declaration_with_pinned_version_re(gem_name).match(contents))
20
+
21
+ version = match[1]
22
+ hash[gem_name] = VersionSpec.parse(version)
23
+ end
24
+ end
25
+
26
+ def relax_gem_pins!(gem_names)
27
+ gem_names.each do |gem_name|
28
+ rewrite_gem_version!(gem_name, &:relax)
29
+ end
30
+ end
31
+
32
+ def shift_gem_pins!(new_gem_versions)
33
+ new_gem_versions.each do |gem_name, new_version|
34
+ rewrite_gem_version!(gem_name) { |version_spec| version_spec.shift(new_version) }
11
35
  end
12
36
  end
13
37
 
14
38
  private
15
39
 
40
+ def rewrite_gem_version!(gem_name)
41
+ found = contents.sub!(gem_declaration_with_pinned_version_re(gem_name)) do |match|
42
+ version = Regexp.last_match[1]
43
+ match[Regexp.last_match.regexp, 1] = yield(VersionSpec.parse(version)).to_s
44
+ match
45
+ end
46
+ raise "Can't rewrite version for #{gem_name}; it does not have a pin" unless found
47
+
48
+ IO.write(path, contents)
49
+ end
50
+
16
51
  attr_reader :contents
17
52
 
18
53
  def gem_names
@@ -30,5 +65,9 @@ module Bundleup
30
65
  def gem_declaration_re(gem_name)
31
66
  /^\s*gem\s+["']#{Regexp.escape(gem_name)}["']/
32
67
  end
68
+
69
+ def gem_declaration_with_pinned_version_re(gem_name)
70
+ /#{gem_declaration_re(gem_name)},\s*["']([^'"]+)["']\s*$/
71
+ end
33
72
  end
34
73
  end
@@ -3,7 +3,11 @@ require "forwardable"
3
3
  module Bundleup
4
4
  class Report
5
5
  extend Forwardable
6
- def_delegators :rows, :empty?
6
+ def_delegators :rows, :empty?, :one?
7
+
8
+ def many?
9
+ rows.length > 1
10
+ end
7
11
 
8
12
  def to_s
9
13
  [
@@ -1,3 +1,3 @@
1
1
  module Bundleup
2
- VERSION = "2.0.0".freeze
2
+ VERSION = "2.2.0".freeze
3
3
  end
@@ -0,0 +1,54 @@
1
+ module Bundleup
2
+ class VersionSpec
3
+ def self.parse(version)
4
+ return version if version.is_a?(VersionSpec)
5
+
6
+ version = version.strip
7
+ _, operator, number = version.match(/^([^\d\s]*)\s*(.+)/).to_a
8
+ operator = nil if operator.empty?
9
+
10
+ new(parts: number.split("."), operator: operator)
11
+ end
12
+
13
+ attr_reader :parts, :operator
14
+
15
+ def initialize(parts:, operator: nil)
16
+ @parts = parts
17
+ @operator = operator
18
+ end
19
+
20
+ def exact?
21
+ operator.nil?
22
+ end
23
+
24
+ def relax
25
+ return self if %w[!= > >=].include?(operator)
26
+ return self.class.parse(">= 0") if %w[< <=].include?(operator)
27
+
28
+ self.class.new(parts: parts, operator: ">=")
29
+ end
30
+
31
+ def shift(new_version) # rubocop:disable Metrics/AbcSize
32
+ return self.class.parse(new_version) if exact?
33
+ return self if Gem::Requirement.new(to_s).satisfied_by?(Gem::Version.new(new_version))
34
+ return self.class.new(parts: self.class.parse(new_version).parts, operator: "<=") if %w[< <=].include?(operator)
35
+
36
+ new_slice = self.class.parse(new_version).slice(parts.length)
37
+ self.class.new(parts: new_slice.parts, operator: "~>")
38
+ end
39
+
40
+ def slice(amount)
41
+ self.class.new(parts: parts[0, amount], operator: operator)
42
+ end
43
+
44
+ def to_s
45
+ [operator, parts.join(".")].compact.join(" ")
46
+ end
47
+
48
+ def ==(other)
49
+ return false unless other.is_a?(VersionSpec)
50
+
51
+ to_s == other.to_s
52
+ end
53
+ end
54
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bundleup
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Brictson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-03 00:00:00.000000000 Z
11
+ date: 2021-07-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Use `bundleup` whenever you want to update the locked Gemfile dependencies
14
14
  of a Ruby project. It shows exactly what gems will be updated with color output
@@ -38,6 +38,7 @@ files:
38
38
  - lib/bundleup/shell.rb
39
39
  - lib/bundleup/update_report.rb
40
40
  - lib/bundleup/version.rb
41
+ - lib/bundleup/version_spec.rb
41
42
  homepage: https://github.com/mattbrictson/bundleup
42
43
  licenses:
43
44
  - MIT
@@ -54,14 +55,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
54
55
  requirements:
55
56
  - - ">="
56
57
  - !ruby/object:Gem::Version
57
- version: 2.5.0
58
+ version: 2.6.0
58
59
  required_rubygems_version: !ruby/object:Gem::Requirement
59
60
  requirements:
60
61
  - - ">="
61
62
  - !ruby/object:Gem::Version
62
63
  version: '0'
63
64
  requirements: []
64
- rubygems_version: 3.1.4
65
+ rubygems_version: 3.2.22
65
66
  signing_key:
66
67
  specification_version: 4
67
68
  summary: A friendlier command-line interface for Bundler’s `update` and `outdated`