bundle_update_interactive 0.1.2 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +87 -2
- data/lib/bundle_update_interactive/changelog_locator.rb +2 -2
- data/lib/bundle_update_interactive/cli/multi_select.rb +9 -0
- data/lib/bundle_update_interactive/cli/table.rb +1 -1
- data/lib/bundle_update_interactive/outdated_gem.rb +19 -30
- data/lib/bundle_update_interactive/report.rb +23 -12
- data/lib/bundle_update_interactive/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24de9c6dde2b3514ffad21a64256359200e2bb717b1a0385caa8d032776d5fe6
|
4
|
+
data.tar.gz: 4a1a1c65bd89e4c7e17d563c2a854c80e07edeb7d04e6b4ffdf3cbef1d47ddee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 493421b5621ff128b56ed96041fd4c1e776de8c0160f330d8458d40963248e2c2e779dc64c7f7afd390c2f98d62a6203b3495534e7b57051f79add6edc729715
|
7
|
+
data.tar.gz: 638b235f21664177f8b9de29a0b69b9229ef1b57fc985c4ba6c73678ca2142a926caff18626a809b4ab4fdd2aeff4573e7a4137c471ca569d62b95e55322e0ec
|
data/README.md
CHANGED
@@ -5,13 +5,15 @@
|
|
5
5
|
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/mattbrictson/bundle_update_interactive/ci.yml)](https://github.com/mattbrictson/bundle_update_interactive/actions/workflows/ci.yml)
|
6
6
|
[![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/mattbrictson/bundle_update_interactive)](https://codeclimate.com/github/mattbrictson/bundle_update_interactive)
|
7
7
|
|
8
|
-
This gem adds an `update-interactive` command to [Bundler](https://bundler.io).
|
8
|
+
**This gem adds an `update-interactive` command to [Bundler](https://bundler.io).** Run it to see what gems can be updated, then pick and choose which ones to update. If you've used `yarn upgrade-interactive`, the interface should be very familiar.
|
9
9
|
|
10
|
-
|
10
|
+
<img src="images/update-interactive.png" alt="Screenshot of update-interactive UI" width="1154" />
|
11
11
|
|
12
12
|
---
|
13
13
|
|
14
14
|
- [Quick start](#quick-start)
|
15
|
+
- [Features](#features)
|
16
|
+
- [Prior art](#prior-art)
|
15
17
|
- [Support](#support)
|
16
18
|
- [License](#license)
|
17
19
|
- [Code of conduct](#code-of-conduct)
|
@@ -37,6 +39,85 @@ Or the shorthand:
|
|
37
39
|
bundle ui
|
38
40
|
```
|
39
41
|
|
42
|
+
## Features
|
43
|
+
|
44
|
+
### Semver highlighting
|
45
|
+
|
46
|
+
`bundle update-interactive` highlights each gem according the severity of its version upgrade.
|
47
|
+
|
48
|
+
<img src="images/semver.png" alt="Severities are in red, yellow, and green" width="480" />
|
49
|
+
|
50
|
+
Gems sourced from Git repositories are highlighted in cyan, regardless of the semver change, due to the fact that new commits pulled from the Git repo may not yet be officially released. In this case the semver information is unknown.
|
51
|
+
|
52
|
+
`bundle update-interactive` also highlights the exact portion of the version number that has changed, so you can quickly scan gem versions for important differences.
|
53
|
+
|
54
|
+
<img src="images/version-highlight.png" alt="Screenshot of highlighted version numbers" width="70" />
|
55
|
+
|
56
|
+
### Security vulnerabilities
|
57
|
+
|
58
|
+
`bundle update-interactive` uses [bundler-audit](https://github.com/rubysec/bundler-audit) internally to search for outdated gems that have known security vulnerabilities. These gems are highlighted prominently with white text on a red background.
|
59
|
+
|
60
|
+
<img src="images/security.png" alt="Screenshot of security vulnerability highlighted in red" width="402" />
|
61
|
+
|
62
|
+
Some gems, notably `rails`, are composed of smaller gems like `actionpack`, `activesupport`, `railties`, etc. Because of how these component gem versions are constrained, you cannot update just one of them; they all must be updated together.
|
63
|
+
|
64
|
+
Therefore, if any Rails component has a security vulnerability, `bundle update-interactive` will automatically roll up that information into a single `rails` line item, so you can select it and upgrade all of its components in one shot.
|
65
|
+
|
66
|
+
### Changelogs
|
67
|
+
|
68
|
+
`bundle update-interactive` will do its best to find an appropriate changelog for each gem.
|
69
|
+
|
70
|
+
It prefers the `changelog_uri` [metadata](https://guides.rubygems.org/specification-reference/#metadata) published in the gem itself. However, this metadata field is optional, and many gem authors do not provide it.
|
71
|
+
|
72
|
+
As a fallback, `bundle update-interactive` will check if the gem's source code is hosted on GitHub, and scans the GitHub repo for obvious changelog files like `CHANGELOG.md`, `NEWS`, etc. Finally, if the project is actively documenting versions using GitHub Releases, the Releases URL will be used.
|
73
|
+
|
74
|
+
If you discover a gem that is missing a changelog in `bundle update-interactive`, [log an issue](https://github.com/mattbrictson/bundle_update_interactive/issues) and I'll see if the algorithm can be improved.
|
75
|
+
|
76
|
+
### Git diffs
|
77
|
+
|
78
|
+
If your `Gemfile` sources a gem from a GitHub repo like this:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
gem "rails", github: "rails/rails", branch: "7-1-stable"
|
82
|
+
```
|
83
|
+
|
84
|
+
Then `bundle update-interactive` will show a GitHub diff link instead of a changelog, so you can see exactly what changed when the gem is updated. For example:
|
85
|
+
|
86
|
+
https://github.com/rails/rails/compare/5a8d894...77dfa65
|
87
|
+
|
88
|
+
Currently only GitHub repos are supported, but I'm considering adding GitLab and BitBucket as well.
|
89
|
+
|
90
|
+
### Conservative updates
|
91
|
+
|
92
|
+
`bundle update-interactive` updates the gems you select by running `bundle update --conservative [GEMS...]`. This means that only those specific gems will be updated. Indirect dependencies shared with other gems will not be affected.
|
93
|
+
|
94
|
+
<img src="images/conservative.png" alt="Screenshot of gems being updated" width="762" />
|
95
|
+
|
96
|
+
An exception is made for "meta gems" like `rails` that are composed of dependencies locked at exact versions. For example, if you chose to upgrade `rails`, the actual command issued to Bundler will be:
|
97
|
+
|
98
|
+
```
|
99
|
+
bundle update --conservative \
|
100
|
+
rails \
|
101
|
+
actioncable \
|
102
|
+
actionmailbox \
|
103
|
+
actionmailer \
|
104
|
+
actionpack \
|
105
|
+
actiontext \
|
106
|
+
actionview \
|
107
|
+
activejob \
|
108
|
+
activemodel \
|
109
|
+
activerecord \
|
110
|
+
activestorage \
|
111
|
+
activesupport \
|
112
|
+
railties
|
113
|
+
```
|
114
|
+
|
115
|
+
## Prior art
|
116
|
+
|
117
|
+
This project was inspired by [yarn upgrade-interactive](https://classic.yarnpkg.com/lang/en/docs/cli/upgrade-interactive/), and borrows many of its interface ideas.
|
118
|
+
|
119
|
+
Before creating `bundle update-interactive`, I published [bundleup](https://github.com/mattbrictson/bundleup), a gem that serves a similar purpose but with a simpler, non-interactive approach.
|
120
|
+
|
40
121
|
## Support
|
41
122
|
|
42
123
|
If you want to report a bug, or have ideas, feedback or questions about the gem, [let me know via GitHub issues](https://github.com/mattbrictson/bundle_update_interactive/issues/new) and I will do my best to provide a helpful answer. Happy hacking!
|
@@ -52,3 +133,7 @@ Everyone interacting in this project’s codebases, issue trackers, chat rooms a
|
|
52
133
|
## Contribution guide
|
53
134
|
|
54
135
|
Pull requests are welcome!
|
136
|
+
|
137
|
+
To test your locally cloned version of `bundle update-interactive`, run `rake install`. This will install the gem and its executable so that you can try it out on other local projects.
|
138
|
+
|
139
|
+
Before submitting a PR, make sure to run `rake` to see if there are any RuboCop or test failures.
|
@@ -5,8 +5,8 @@ require "json"
|
|
5
5
|
|
6
6
|
GITHUB_PATTERN = %r{^(?:https?://)?github\.com/([^/]+/[^/]+)(?:\.git)?/?}.freeze
|
7
7
|
URI_KEYS = %w[source_code_uri homepage_uri bug_tracker_uri wiki_uri].freeze
|
8
|
-
FILE_PATTERN = /
|
9
|
-
EXT_PATTERN = /
|
8
|
+
FILE_PATTERN = /changelog|changes|history|news|release/i.freeze
|
9
|
+
EXT_PATTERN = /md|txt|rdoc/i.freeze
|
10
10
|
|
11
11
|
module BundleUpdateInteractive
|
12
12
|
class ChangelogLocator
|
@@ -39,6 +39,8 @@ class BundleUpdateInteractive::CLI
|
|
39
39
|
exit(130)
|
40
40
|
}
|
41
41
|
)
|
42
|
+
add_keybindings
|
43
|
+
|
42
44
|
@pastel = BundleUpdateInteractive.pastel
|
43
45
|
end
|
44
46
|
|
@@ -51,6 +53,13 @@ class BundleUpdateInteractive::CLI
|
|
51
53
|
|
52
54
|
attr_reader :pastel, :table, :tty_prompt, :title
|
53
55
|
|
56
|
+
def add_keybindings
|
57
|
+
tty_prompt.on(:keypress) do |event|
|
58
|
+
tty_prompt.trigger(:keyup) if %w[k p].include?(event.value)
|
59
|
+
tty_prompt.trigger(:keydown) if %w[j n].include?(event.value)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
54
63
|
def help
|
55
64
|
[
|
56
65
|
pastel.dim("\nPress <space> to select, ↑/↓ move, <ctrl-a> all, <ctrl-r> reverse, <enter> to finish."),
|
@@ -2,19 +2,21 @@
|
|
2
2
|
|
3
3
|
module BundleUpdateInteractive
|
4
4
|
class OutdatedGem
|
5
|
-
|
6
|
-
|
5
|
+
attr_accessor :name,
|
6
|
+
:gemfile_groups,
|
7
|
+
:git_source_uri,
|
8
|
+
:current_version,
|
9
|
+
:current_git_version,
|
10
|
+
:updated_version,
|
11
|
+
:updated_git_version
|
7
12
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@gemfile_groups = gemfile_groups
|
12
|
-
@changelog_locator = ChangelogLocator.new
|
13
|
+
attr_writer :rubygems_source, :vulnerable
|
14
|
+
|
15
|
+
def initialize(**attrs)
|
13
16
|
@vulnerable = nil
|
14
|
-
|
17
|
+
@changelog_locator = ChangelogLocator.new
|
15
18
|
|
16
|
-
|
17
|
-
current_lockfile_entry.name
|
19
|
+
attrs.each { |name, value| public_send(:"#{name}=", value) }
|
18
20
|
end
|
19
21
|
|
20
22
|
def semver_change
|
@@ -25,13 +27,17 @@ module BundleUpdateInteractive
|
|
25
27
|
@vulnerable
|
26
28
|
end
|
27
29
|
|
30
|
+
def rubygems_source?
|
31
|
+
@rubygems_source
|
32
|
+
end
|
33
|
+
|
28
34
|
def changelog_uri
|
29
35
|
return @changelog_uri if defined?(@changelog_uri)
|
30
36
|
|
31
37
|
@changelog_uri =
|
32
|
-
if git_version_changed?
|
38
|
+
if git_version_changed? && github_repo
|
33
39
|
"https://github.com/#{github_repo}/compare/#{current_git_version}...#{updated_git_version}"
|
34
|
-
elsif
|
40
|
+
elsif rubygems_source?
|
35
41
|
changelog_locator.find_changelog_uri(name: name, version: updated_version.to_s)
|
36
42
|
else
|
37
43
|
begin
|
@@ -42,22 +48,6 @@ module BundleUpdateInteractive
|
|
42
48
|
end
|
43
49
|
end
|
44
50
|
|
45
|
-
def current_version
|
46
|
-
current_lockfile_entry.version
|
47
|
-
end
|
48
|
-
|
49
|
-
def updated_version
|
50
|
-
updated_lockfile_entry.version
|
51
|
-
end
|
52
|
-
|
53
|
-
def current_git_version
|
54
|
-
current_lockfile_entry.git_version
|
55
|
-
end
|
56
|
-
|
57
|
-
def updated_git_version
|
58
|
-
updated_lockfile_entry.git_version
|
59
|
-
end
|
60
|
-
|
61
51
|
def git_version_changed?
|
62
52
|
current_git_version && updated_git_version && current_git_version != updated_git_version
|
63
53
|
end
|
@@ -69,8 +59,7 @@ module BundleUpdateInteractive
|
|
69
59
|
def github_repo
|
70
60
|
return nil unless updated_git_version
|
71
61
|
|
72
|
-
|
73
|
-
1]
|
62
|
+
git_source_uri.to_s[%r{^(?:git@github.com:|https://github.com/)([^/]+/[^/]+?)(:?\.git)?(?:$|/)}i, 1]
|
74
63
|
end
|
75
64
|
end
|
76
65
|
end
|
@@ -21,16 +21,12 @@ module BundleUpdateInteractive
|
|
21
21
|
|
22
22
|
def initialize(gemfile:, current_lockfile:, updated_lockfile:)
|
23
23
|
@current_lockfile = current_lockfile
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
hash[name] =
|
30
|
-
current_lockfile_entry: current_lockfile[name],
|
31
|
-
updated_lockfile_entry: updated_lockfile[name],
|
32
|
-
gemfile_groups: gemfile[name]&.groups
|
33
|
-
)
|
24
|
+
@outdated_gems ||= current_lockfile.entries.each_with_object({}) do |current_lockfile_entry, hash|
|
25
|
+
name = current_lockfile_entry.name
|
26
|
+
updated_lockfile_entry = updated_lockfile[name]
|
27
|
+
next unless current_lockfile_entry.older_than?(updated_lockfile_entry)
|
28
|
+
|
29
|
+
hash[name] = build_outdated_gem(current_lockfile_entry, updated_lockfile_entry, gemfile[name]&.groups)
|
34
30
|
end.freeze
|
35
31
|
end
|
36
32
|
|
@@ -39,12 +35,14 @@ module BundleUpdateInteractive
|
|
39
35
|
end
|
40
36
|
|
41
37
|
def updateable_gems
|
42
|
-
outdated_gems.reject
|
38
|
+
@updateable_gems ||= outdated_gems.reject do |name, _|
|
39
|
+
current_lockfile[name].exact_dependency?
|
40
|
+
end.freeze
|
43
41
|
end
|
44
42
|
|
45
43
|
def expand_gems_with_exact_dependencies(*gem_names)
|
46
44
|
gem_names.flatten!
|
47
|
-
gem_names.flat_map { [
|
45
|
+
gem_names.flat_map { |name| [name, *current_lockfile[name].exact_dependencies] }.uniq
|
48
46
|
end
|
49
47
|
|
50
48
|
def scan_for_vulnerabilities!
|
@@ -68,5 +66,18 @@ module BundleUpdateInteractive
|
|
68
66
|
private
|
69
67
|
|
70
68
|
attr_reader :current_lockfile
|
69
|
+
|
70
|
+
def build_outdated_gem(current_lockfile_entry, updated_lockfile_entry, gemfile_groups)
|
71
|
+
OutdatedGem.new(
|
72
|
+
name: current_lockfile_entry.name,
|
73
|
+
gemfile_groups: gemfile_groups,
|
74
|
+
rubygems_source: updated_lockfile_entry.rubygems_source?,
|
75
|
+
git_source_uri: updated_lockfile_entry.git_source_uri&.to_s,
|
76
|
+
current_version: current_lockfile_entry.version.to_s,
|
77
|
+
current_git_version: current_lockfile_entry.git_version&.strip,
|
78
|
+
updated_version: updated_lockfile_entry.version.to_s,
|
79
|
+
updated_git_version: updated_lockfile_entry.git_version&.strip
|
80
|
+
)
|
81
|
+
end
|
71
82
|
end
|
72
83
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bundle_update_interactive
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Brictson
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-07-
|
11
|
+
date: 2024-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|