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 +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +33 -3
- data/lib/bundleup.rb +1 -0
- data/lib/bundleup/backup.rb +10 -7
- data/lib/bundleup/cli.rb +50 -22
- data/lib/bundleup/gemfile.rb +42 -3
- data/lib/bundleup/report.rb +5 -1
- data/lib/bundleup/version.rb +1 -1
- data/lib/bundleup/version_spec.rb +54 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '02583d0ea6a0b389400ed3c24f312effae81880bbf2409ac25136681f6cd0028'
|
4
|
+
data.tar.gz: e1ffadae2408a04df7d336531b41329e9de416d4b3149644768ef526658561d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e36564061fd34a361512543e26ded1f2158beb57f019eb3c67e8e1f626f00f1af02d83df19626c36632e07d0ffc1163507a006dacc2080a42fa1c522d6cfdeaa
|
7
|
+
data.tar.gz: db630caef95d92d94c68e83dbd82a9cd1bec082943f377efb1655361802e2f11c86f7800956ae71f7748caa5d22347ee90d5a9d87e83c51087a26bdef7727772
|
data/LICENSE.txt
CHANGED
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://
|
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.
|
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
|
-
|
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
data/lib/bundleup/backup.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Bundleup
|
2
2
|
class Backup
|
3
|
-
def self.restore_on_error(
|
4
|
-
backup = new(
|
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(
|
14
|
-
@
|
15
|
-
|
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
|
-
|
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 :
|
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
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
data/lib/bundleup/gemfile.rb
CHANGED
@@ -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 |
|
9
|
-
comment = inline_comment(
|
10
|
-
hash[
|
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
|
data/lib/bundleup/report.rb
CHANGED
data/lib/bundleup/version.rb
CHANGED
@@ -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.
|
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:
|
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.
|
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.
|
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`
|