bundle-patch 0.3.0 β†’ 0.4.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
2
  SHA256:
3
- metadata.gz: 4e963f237bf1d991d0654cc32c1b70cecc98029e3a553cd0b5aeeb183aeac80c
4
- data.tar.gz: f84762b82d02ff168f56b82de3ea4680eac772f8f937caace1f5be38ea70e59a
3
+ metadata.gz: e0903e6a807f6bdab47f3b7b4872424fd5bad54c4c84b66033c2d398f793dbb5
4
+ data.tar.gz: b9956c73baf2f0fe592b348af500641e00f540faeb00c91f00d35fe5cfa4c432
5
5
  SHA512:
6
- metadata.gz: cbf5c83c3e1e2a4ac162203cc06a9ed04e8eb0bac24f4afa2ef5dc024c85ef6bd6a66cae6b846c2880dc3921cbfdbe6913e0ba7963541a33bfc5634f0f7c9c5f
7
- data.tar.gz: fae818ce5b5bc0cf74a095911beeee2d80edac1b474fbad89c0d12c4d08aca50407a587d9fc5ec763e805b8fa9c10b1f618cd7c42bc927e9882d0602b34e7183
6
+ metadata.gz: 6bb2038b986f0552bfaa5b7c3ec5f205ba590ecd9e3940270e535a88ab790a9a4374ce6c24298f0024dc6e96c53eecbc9a99d6608e662386e960cc2e64b88d7b
7
+ data.tar.gz: 1cf395cc171ff075e7665db75d2196638e2cdb17867c6cdf68c565ac9e00419afd538f5ac7739d63e348143a5d36af840a6a27768e08ce38735e836461ca7107
data/README.md CHANGED
@@ -14,17 +14,56 @@ It parses audit output, finds the **best patchable version** for each vulnerable
14
14
  - Supports patch/minor/major upgrade strategies
15
15
  - Handles indirect dependencies by explicitly adding them
16
16
  - Has a dry-run mode
17
+ - Creates backup of your Gemfile before changes
17
18
 
18
19
  ---
19
20
 
20
- ## πŸ’‘ Example
21
+ ## πŸ“‹ Requirements
22
+
23
+ - Ruby 2.6 or later
24
+ - Bundler installed
25
+ - bundler-audit installed (will be installed automatically if missing)
26
+
27
+ ---
28
+
29
+ ## πŸ“¦ Installation
30
+
31
+ Add this gem to your system:
21
32
 
22
33
  ```bash
23
- bundle-patch --mode=minor
34
+ gem install bundle-patch
24
35
  ```
25
36
 
26
- Example output
37
+ Or add it to your project's Gemfile for use in development:
27
38
 
39
+ ```ruby
40
+ # Gemfile
41
+ group :development do
42
+ gem 'bundle-patch'
43
+ end
44
+ ```
45
+
46
+ And then:
47
+
48
+ ```bash
49
+ bundle install
50
+ ```
51
+
52
+ ---
53
+
54
+ ## πŸ’‘ Examples
55
+
56
+ ### Basic Usage
57
+ ```bash
58
+ bundle-patch
59
+ ```
60
+ This will run in patch mode (default) and update only patch versions.
61
+
62
+ ### Minor Version Updates
63
+ ```bash
64
+ bundle-patch --mode=minor
65
+ ```
66
+ Example output:
28
67
  ```
29
68
  πŸ” Running `bundle-audit check --format json`...
30
69
  πŸ”’ Found 2 vulnerabilities:
@@ -40,43 +79,77 @@ Example output
40
79
  βœ… bundle install completed successfully
41
80
  ```
42
81
 
43
- ## βš™οΈ Options
44
-
45
- | Option | Description |
46
- | ----------------------- | ------------------------------------------------------------------------- |
47
- | `--mode=patch` | Only allow patch-level updates (default) |
48
- | `--mode=minor` | Allow minor version updates |
49
- | `--mode=all` | Allow all updates including major versions |
50
- | `--dry-run` | Only print what would be changed, don’t touch the Gemfile or install gems |
51
- | `--skip_bundle_install` | Modify the Gemfile, but skip `bundle install` |
52
-
53
- ## πŸ“¦ Installation
54
-
55
- Add this gem to your system:
56
-
82
+ ### Dry Run Mode
57
83
  ```bash
58
- gem install bundle-patch
84
+ bundle-patch --dry-run
59
85
  ```
86
+ This will show what would be changed without making any actual changes.
60
87
 
61
- Or add it to your project's Gemfile for use in development:
88
+ ### Skip Bundle Install
89
+ ```bash
90
+ bundle-patch --skip-bundle-install
91
+ ```
92
+ This will update the Gemfile but skip running `bundle install`.
62
93
 
94
+ ### Major Version Updates
63
95
  ```bash
64
- # Gemfile
65
- group :development do
66
- gem 'bundle-patch'
67
- end
96
+ bundle-patch --mode=all
68
97
  ```
98
+ This will allow updates to any version that fixes the vulnerability.
69
99
 
70
- And then:
100
+ ---
71
101
 
72
- ```
73
- bundle install
74
- ```
102
+ ## βš™οΈ Options
103
+
104
+ | Option | Description | Default |
105
+ | ----------------------- | ------------------------------------------------------------------------- | ------- |
106
+ | `--mode=patch` | Only allow patch-level updates (e.g., 1.0.0 β†’ 1.0.1) | βœ“ |
107
+ | `--mode=minor` | Allow minor version updates (e.g., 1.0.0 β†’ 1.1.0) | |
108
+ | `--mode=all` | Allow all updates including major versions (e.g., 1.0.0 β†’ 2.0.0) | |
109
+ | `--dry-run` | Only print what would be changed, don't touch the Gemfile or install gems | false |
110
+ | `--skip-bundle-install` | Modify the Gemfile, but skip `bundle install` | false |
111
+
112
+ ---
75
113
 
76
114
  ## 🧼 How it works
77
115
 
78
116
  1. Runs `bundle audit check --format json`
79
117
  2. Groups advisories by gem
80
118
  3. Determines the best patchable version for each gem based on `--mode`
81
- 4. Ensures the gem is either updated or explicitly added to the `Gemfile`
82
- 5. Optionally runs `bundle install` (unless `--skip_bundle_install` or `--dry-run` is used)
119
+ 4. Creates a backup of your Gemfile (Gemfile.bak)
120
+ 5. Ensures the gem is either updated or explicitly added to the `Gemfile`
121
+ 6. Optionally runs `bundle install` (unless `--skip-bundle-install` or `--dry-run` is used)
122
+
123
+ ---
124
+
125
+ ## πŸ” Troubleshooting
126
+
127
+ ### Bundle Install Fails
128
+ If `bundle install` fails after updating:
129
+ 1. Check the error message
130
+ 2. You can revert to the backup: `cp Gemfile.bak Gemfile`
131
+ 3. Try running `bundle install` manually to see more detailed errors
132
+
133
+ ### Gem Can't Be Patched
134
+ If a gem can't be patched in your chosen mode:
135
+ 1. Try running with `--mode=all` to see all possible updates
136
+ 2. Check if there are any version conflicts in your Gemfile
137
+ 3. Consider manually updating the gem to a specific version
138
+
139
+ ### Security Considerations
140
+ - Always review the changes made to your Gemfile
141
+ - Test your application after applying updates
142
+ - Consider running your test suite after updates
143
+ - Check the changelog of updated gems for breaking changes
144
+
145
+ ---
146
+
147
+ ## 🀝 Contributing
148
+
149
+ Bug reports and pull requests are welcome on GitHub at https://github.com/yourusername/bundle-patch.
150
+
151
+ ---
152
+
153
+ ## πŸ“„ License
154
+
155
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Bundle
4
4
  module Patch
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
data/lib/bundle/patch.rb CHANGED
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require_relative "patch/version"
5
- require_relative "patch/bundler_audit_installer"
6
5
  require_relative "patch/audit/parser"
7
6
  require_relative "patch/gemfile_editor"
8
7
  require_relative "patch/gemfile_updater"
@@ -11,7 +10,6 @@ require_relative "patch/config"
11
10
  module Bundle
12
11
  module Patch
13
12
  def self.start(config = Config.new)
14
- BundlerAuditInstaller.ensure_installed!
15
13
  advisories = Audit::Parser.run
16
14
 
17
15
  if advisories.empty?
@@ -23,34 +21,16 @@ module Bundle
23
21
  patchable = []
24
22
 
25
23
  advisories.group_by { |adv| adv.to_h.dig("gem", "name") }.each do |name, gem_advisories|
26
- current = gem_advisories.first.to_h.dig("gem", "version")
27
- current_version = Gem::Version.new(current)
28
-
29
- # Collect all requirements from advisories
30
- all_requirements = gem_advisories.flat_map do |adv|
31
- adv.to_h.dig("advisory", "patched_versions").map do |req|
32
- Gem::Requirement.new(req) rescue nil
33
- end
34
- end.compact
35
-
36
- # Find versions that satisfy all requirements
37
- candidate_versions = all_requirements
38
- .map { |req| best_version_matching(req) }
39
- .compact
40
- .uniq
41
- .select { |v| config.allow_update?(current_version, v) }
42
- .sort
43
-
44
- if candidate_versions.any?
45
- best_patch = candidate_versions.first
24
+ result = process_gem_advisories(name, gem_advisories, config)
25
+
26
+ if result
46
27
  title_list = gem_advisories.map { |a| a.to_h.dig("advisory", "title") }.uniq
47
- puts "- #{name} (#{current}):"
28
+ puts "- #{name} (#{gem_advisories.first.to_h.dig("gem", "version")}):"
48
29
  title_list.each { |t| puts " β€’ #{t}" }
49
- puts " βœ… Patchable β†’ #{best_patch}"
50
-
51
- patchable << { "name" => name, "required_version" => best_patch.to_s }
30
+ puts " βœ… Patchable β†’ #{result[:required_version]}"
31
+ patchable << { "name" => name, "required_version" => result[:required_version] }
52
32
  else
53
- puts "- #{name} (#{current}):"
33
+ puts "- #{name} (#{gem_advisories.first.to_h.dig("gem", "version")}):"
54
34
  gem_advisories.each do |adv|
55
35
  puts " β€’ #{adv.to_h.dig("advisory", "title")}"
56
36
  end
@@ -79,9 +59,49 @@ module Bundle
79
59
  end
80
60
  end
81
61
 
82
- def self.best_version_matching(req)
83
- # Approximate best patch version using upper bound from requirement
84
- req.requirements.map { |_, v| v }.compact.min rescue nil
62
+ def self.process_gem_advisories(name, advisories, config)
63
+ current = advisories.first.to_h.dig("gem", "version")
64
+ current_version = Gem::Version.new(current)
65
+
66
+ # Collect all requirements from advisories
67
+ all_requirements = advisories.flat_map do |adv|
68
+ adv.to_h.dig("advisory", "patched_versions").map do |req|
69
+ Gem::Requirement.new(req) rescue nil
70
+ end
71
+ end.compact
72
+
73
+ # Find versions that satisfy all requirements
74
+ candidate_versions = all_requirements
75
+ .flat_map { |req| versions_satisfying(req) }
76
+ .compact
77
+ .uniq
78
+ .select { |v| config.allow_update?(current_version, v) }
79
+ .sort
80
+
81
+ if candidate_versions.any?
82
+ {
83
+ name: name,
84
+ required_version: candidate_versions.last.to_s
85
+ }
86
+ end
87
+ end
88
+
89
+ def self.versions_satisfying(req)
90
+ # Get all versions that satisfy the requirement
91
+ req.requirements.map do |op, version|
92
+ case op
93
+ when ">="
94
+ # For >= requirements, we need to find all versions >= this version
95
+ # We'll approximate by using the version itself
96
+ version
97
+ when "~>"
98
+ # For ~> requirements, we need to find all versions in the range
99
+ # We'll approximate by using the upper bound
100
+ version
101
+ else
102
+ version
103
+ end
104
+ end.compact
85
105
  end
86
106
  end
87
107
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bundle-patch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rishijain
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-04-12 00:00:00.000000000 Z
10
+ date: 2025-04-18 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: bundler-audit
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0.9'
26
+ - !ruby/object:Gem::Dependency
27
+ name: minitest
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '5.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '5.0'
26
40
  description: bundle-patch is a CLI tool that detects vulnerable gems in your Gemfile
27
41
  and automatically upgrades them to a patchable version based on your configured
28
42
  strategy (patch/minor/all). Uses bundler-audit under the hood.
@@ -44,7 +58,6 @@ files:
44
58
  - lib/bundle/patch.rb
45
59
  - lib/bundle/patch/audit/advisory.rb
46
60
  - lib/bundle/patch/audit/parser.rb
47
- - lib/bundle/patch/bundler_audit_installer.rb
48
61
  - lib/bundle/patch/config.rb
49
62
  - lib/bundle/patch/gemfile_editor.rb
50
63
  - lib/bundle/patch/gemfile_updater.rb
@@ -1,16 +0,0 @@
1
- module Bundle
2
- module Patch
3
- class BundlerAuditInstaller
4
- def self.ensure_installed!
5
- return if system("bundle-audit --version > /dev/null 2>&1")
6
-
7
- puts "πŸ” bundler-audit not found. Installing..."
8
- success = system("gem install bundler-audit")
9
-
10
- unless success
11
- abort "❌ Failed to install bundler-audit. Please check your RubyGems setup."
12
- end
13
- end
14
- end
15
- end
16
- end