bundler-audited_update 0.1.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 +7 -0
- data/lib/bundler/audited_update.rb +252 -0
- metadata +101 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bbfc1d502ed22c2bc57ce8560f3851f6dae3cb95914c3499cac9759ad689abfa
|
|
4
|
+
data.tar.gz: 93ab57d94ee1ef17af96febfe7310cd790028d573ed652336087fe8bd525a05c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 102338a0fc8098a5acac9cd047928ee09b92be8b70ae80fa5ff18222c52a7f28d37096cbeb6577b28cff190c71c79f3ff17aab57abaf3054a8721198ad89c2b9
|
|
7
|
+
data.tar.gz: ac8ef5414c618a802943495281dcf204d945ac362cbe318fb93e91094863dbb208dad34db8ca24571e79f78f13f216816fd8381f8b2194dfd6edcdee9ecf4ba1
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
require 'bundler'
|
|
2
|
+
require 'bundler/lockfile_parser'
|
|
3
|
+
require "bundler/cli"
|
|
4
|
+
require "bundler/cli/update"
|
|
5
|
+
require "open-uri"
|
|
6
|
+
require 'net/http'
|
|
7
|
+
require 'json'
|
|
8
|
+
require 'versionomy'
|
|
9
|
+
require 'launchy'
|
|
10
|
+
|
|
11
|
+
module Bundler
|
|
12
|
+
class AuditedUpdate
|
|
13
|
+
def run!
|
|
14
|
+
@before_specs = gem_specs
|
|
15
|
+
bundle_update
|
|
16
|
+
@after_specs = gem_specs
|
|
17
|
+
|
|
18
|
+
@output = ""
|
|
19
|
+
@output += "# Gem Changes\n"
|
|
20
|
+
@output += "\n"
|
|
21
|
+
|
|
22
|
+
output_gems("Added Gems", added_gems)
|
|
23
|
+
output_gems("Removed Gems", removed_gems)
|
|
24
|
+
output_changed_gems(changed_gems)
|
|
25
|
+
|
|
26
|
+
puts "\n\n\n\n\n"
|
|
27
|
+
|
|
28
|
+
puts "--------------------------------"
|
|
29
|
+
puts "Audited Bundle Upgrade Text"
|
|
30
|
+
puts "--------------------------------"
|
|
31
|
+
|
|
32
|
+
puts @output
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def output_gems(title, gems)
|
|
36
|
+
return if gems.empty?
|
|
37
|
+
|
|
38
|
+
@output += "## #{title}\n"
|
|
39
|
+
@output += "\n"
|
|
40
|
+
gems.each { |the_gem| gem_output(the_gem.name, the_gem.version) }
|
|
41
|
+
|
|
42
|
+
@output += "\n"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def output_changed_gems(gems)
|
|
46
|
+
return if gems.empty?
|
|
47
|
+
|
|
48
|
+
major_upgrades = gems.select {|_, versions| versions[:before].major != versions[:after].major }
|
|
49
|
+
minor_upgrades = gems.select {|name, versions| !major_upgrades.keys.include?(name) && versions[:before].minor != versions[:after].minor }
|
|
50
|
+
point_upgrades = gems.reject { |name, _| major_upgrades.keys.include?(name) || minor_upgrades.keys.include?(name) }
|
|
51
|
+
|
|
52
|
+
@output += "## Upgraded Gems\n"
|
|
53
|
+
@output += "\n"
|
|
54
|
+
|
|
55
|
+
output_changed_gems_section("Major", major_upgrades)
|
|
56
|
+
output_changed_gems_section("Minor", minor_upgrades)
|
|
57
|
+
output_changed_gems_section("Point", point_upgrades)
|
|
58
|
+
|
|
59
|
+
@output += "\n"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def output_changed_gems_section(title, gems)
|
|
63
|
+
@output += "### #{title} Upgrades\n"
|
|
64
|
+
@output += "\n"
|
|
65
|
+
gems.each { |name, versions| gem_output(name, versions) }
|
|
66
|
+
|
|
67
|
+
@output += "\n"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def gem_output(name, version)
|
|
71
|
+
if version.is_a? Hash
|
|
72
|
+
version_string = "#{version[:before]} -> #{version[:after]}"
|
|
73
|
+
info = gem_info(name, version[:after])
|
|
74
|
+
|
|
75
|
+
guessed_source = gem_source_url(info)
|
|
76
|
+
|
|
77
|
+
if guessed_source
|
|
78
|
+
changelog_text, changelog_url = guess_changelog(guessed_source)
|
|
79
|
+
|
|
80
|
+
if changelog_text && !changelog_text.empty?
|
|
81
|
+
puts "\n\n\n"
|
|
82
|
+
puts "--------------------------------"
|
|
83
|
+
puts "#{name} changes from #{version_string}"
|
|
84
|
+
puts "--------------------------------"
|
|
85
|
+
# Output the changelog text from top until the line that contains the previous version
|
|
86
|
+
changelog_output = changelog_text.split(/^.*#{version[:before]}/, 2).first
|
|
87
|
+
# Max 200 lines
|
|
88
|
+
changelog_output = changelog_output.lines.to_a[0...200].join
|
|
89
|
+
puts changelog_output
|
|
90
|
+
impact = nil
|
|
91
|
+
while impact.nil?
|
|
92
|
+
puts "Does this impact your application? (y/n/[o]pen in browser)"
|
|
93
|
+
answer = gets
|
|
94
|
+
answer = answer.downcase.strip
|
|
95
|
+
if answer == "y"
|
|
96
|
+
puts "What's a short description of the impact?"
|
|
97
|
+
impact = gets
|
|
98
|
+
elsif answer == "n"
|
|
99
|
+
impact = "No impact"
|
|
100
|
+
elsif answer == "o"
|
|
101
|
+
Launchy.open(changelog_url)
|
|
102
|
+
else
|
|
103
|
+
puts "Invalid answer"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
change_detail = impact
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
else
|
|
112
|
+
version_string = version
|
|
113
|
+
info = gem_info(name, version)
|
|
114
|
+
guessed_source = gem_source_url(info)
|
|
115
|
+
change_detail = guessed_source
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
change_detail ||= "Unsupported source URL, cannot search for changelog"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@output += "* #{name} (#{version_string}): #{change_detail}\n"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def guess_changelog(root_url)
|
|
125
|
+
filenames = %w{
|
|
126
|
+
CHANGELOG
|
|
127
|
+
CHANGELOG.md
|
|
128
|
+
Changelog
|
|
129
|
+
Changelog.md
|
|
130
|
+
History
|
|
131
|
+
History.md
|
|
132
|
+
History.rdoc
|
|
133
|
+
Changes
|
|
134
|
+
CHANGES
|
|
135
|
+
CHANGES.md
|
|
136
|
+
NEWS
|
|
137
|
+
}
|
|
138
|
+
changelog_text = nil
|
|
139
|
+
changelog_url = nil
|
|
140
|
+
|
|
141
|
+
filenames.each do |filename|
|
|
142
|
+
changelog_text = try_changelog_url(root_url, filename)
|
|
143
|
+
if changelog_text
|
|
144
|
+
changelog_url = changelog_url_for(root_url, filename)
|
|
145
|
+
break
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
unless changelog_text
|
|
150
|
+
changelog_text = github_releases_bodies(root_url)
|
|
151
|
+
changelog_url = github_releases_url(root_url) if changelog_text
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
unless changelog_text
|
|
155
|
+
changelog_text = "Could not find changelog URL, try manually"
|
|
156
|
+
changelog_url = root_url
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
[changelog_text, changelog_url]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def gem_source_url(info)
|
|
163
|
+
source_url_guess = info["source_code_uri"] || info["homepage_uri"]
|
|
164
|
+
if source_url_guess&.include?("github.com")
|
|
165
|
+
source_url_guess
|
|
166
|
+
else
|
|
167
|
+
# Unsupported source URL
|
|
168
|
+
nil
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def added_gems
|
|
173
|
+
@after_specs.reject {|spec| @before_specs.map(&:name).include?(spec.name) }
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def removed_gems
|
|
177
|
+
@before_specs.reject {|spec| @after_specs.map(&:name).include?(spec.name) }
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def changed_gems
|
|
181
|
+
gems = @after_specs.reject do |after_spec|
|
|
182
|
+
next unless after_spec
|
|
183
|
+
before_spec = @before_specs.find {|before_spec| before_spec && before_spec.name == after_spec.name }
|
|
184
|
+
next unless before_spec # new gem
|
|
185
|
+
before_spec.version == after_spec.version
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
gems.map! do |the_gem|
|
|
189
|
+
before_gem = @before_specs.find {|before_spec| before_spec.name == the_gem.name }
|
|
190
|
+
after_gem = @after_specs.find {|after_spec| after_spec.name == the_gem.name }
|
|
191
|
+
versions = {
|
|
192
|
+
before: Versionomy.parse(before_gem.version.to_s),
|
|
193
|
+
after: Versionomy.parse(after_gem.version.to_s)
|
|
194
|
+
}
|
|
195
|
+
[the_gem.name, versions]
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
gems.to_h
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def github_releases_url(source_root)
|
|
202
|
+
api_source_root = source_root.gsub("github.com/", "api.github.com/repos/")
|
|
203
|
+
"#{api_source_root}/releases"
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def github_releases_bodies(source_root)
|
|
207
|
+
response = URI.parse(github_releases_url(source_root)).read
|
|
208
|
+
releases = JSON.parse(response)
|
|
209
|
+
release_notes = ""
|
|
210
|
+
releases.each do |release|
|
|
211
|
+
next unless release["body"]
|
|
212
|
+
release_notes += release["name"]
|
|
213
|
+
release_notes += "\n"
|
|
214
|
+
release_notes += release["body"]
|
|
215
|
+
release_notes += "\n"
|
|
216
|
+
release_notes += "\n"
|
|
217
|
+
end
|
|
218
|
+
release_notes
|
|
219
|
+
rescue OpenURI::HTTPError
|
|
220
|
+
return nil
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def changelog_url_for(source_root, filename)
|
|
224
|
+
raw_source_root = source_root.gsub("github.com", "raw.githubusercontent.com")
|
|
225
|
+
url = "#{raw_source_root}/master/#{filename}"
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def try_changelog_url(source_root, filename)
|
|
229
|
+
URI.parse(changelog_url_for(source_root, filename)).read
|
|
230
|
+
rescue OpenURI::HTTPError
|
|
231
|
+
return nil
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def gem_info(name, version)
|
|
235
|
+
gem_url = "https://rubygems.org/api/v2/rubygems/#{name}/versions/#{version}"
|
|
236
|
+
response = URI.parse(gem_url).read
|
|
237
|
+
JSON.parse(response)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def gem_specs
|
|
241
|
+
root = File.expand_path(Dir.pwd)
|
|
242
|
+
lockfile = Bundler::LockfileParser.new(
|
|
243
|
+
File.read(File.join(root, 'Gemfile.lock'))
|
|
244
|
+
)
|
|
245
|
+
lockfile.specs
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def bundle_update
|
|
249
|
+
Bundler::CLI::Update.new({}, []).run
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: bundler-audited_update
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Brendan Mulholland
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-02-20 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: json
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: versionomy
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: launchy
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
description: Runs a bundle upgrade, shows the changelog for each gem that was upgraded,
|
|
70
|
+
and outputs a summary view of gem changes plus their impact.
|
|
71
|
+
email: audited_bundle_update@bmulholland.ca
|
|
72
|
+
executables: []
|
|
73
|
+
extensions: []
|
|
74
|
+
extra_rdoc_files: []
|
|
75
|
+
files:
|
|
76
|
+
- lib/bundler/audited_update.rb
|
|
77
|
+
homepage: http://rubygems.org/gems/bundler-audited_update
|
|
78
|
+
licenses:
|
|
79
|
+
- MIT
|
|
80
|
+
metadata: {}
|
|
81
|
+
post_install_message:
|
|
82
|
+
rdoc_options: []
|
|
83
|
+
require_paths:
|
|
84
|
+
- lib
|
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - ">="
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '0'
|
|
95
|
+
requirements: []
|
|
96
|
+
rubyforge_project:
|
|
97
|
+
rubygems_version: 2.7.6
|
|
98
|
+
signing_key:
|
|
99
|
+
specification_version: 4
|
|
100
|
+
summary: Streamlined bundler audit with Changelog detection and summary ouput
|
|
101
|
+
test_files: []
|