gem-compare 0.0.2 → 0.0.3
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 +4 -4
- data/README.md +9 -0
- data/Rakefile +3 -1
- data/lib/rubygems/commands/compare_command.rb +7 -1
- data/lib/rubygems/comparator.rb +76 -13
- data/lib/rubygems/comparator/base.rb +17 -62
- data/lib/rubygems/comparator/dependency_comparator.rb +9 -12
- data/lib/rubygems/comparator/dir_utils.rb +51 -0
- data/lib/rubygems/comparator/file_list_comparator.rb +83 -154
- data/lib/rubygems/comparator/gemfile_comparator.rb +28 -31
- data/lib/rubygems/comparator/monitor.rb +106 -0
- data/lib/rubygems/comparator/report.rb +48 -39
- data/lib/rubygems/comparator/report/entry.rb +38 -0
- data/lib/rubygems/comparator/spec_comparator.rb +10 -23
- data/lib/rubygems/comparator/utils.rb +133 -0
- data/test/rubygems/comparator/test_dependency_comparator.rb +2 -1
- data/test/rubygems/comparator/test_dir_utils.rb +52 -0
- data/test/rubygems/comparator/test_file_list_comparator.rb +5 -2
- data/test/rubygems/comparator/test_monitor.rb +53 -0
- data/test/rubygems/comparator/test_report.rb +23 -0
- data/test/rubygems/comparator/test_utils.rb +33 -0
- data/test/test_helper.rb +12 -1
- metadata +38 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0bf945c951c897beae61add41492c92c95248fa
|
4
|
+
data.tar.gz: 1113168bb3eeb4cc7af658f2b8a7bd2665c771a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4da75c477e6784574cc96651df72e4aaa3621fac232170ace9634dce53192b88997cf42115285bad9a047fb69a0f217823f20e5a4da095ab85213f707ff0232
|
7
|
+
data.tar.gz: 0c0b71c3002837de7e87b096cd6c98189708baa56e9a478135974a35d7853c88620bdb145bf12321b11950bf9e92c3d3a962c032e1068cb4f904e7ea52dec24d
|
data/README.md
CHANGED
@@ -134,6 +134,15 @@ Compared versions: ["0.1.0", "0.1.1", "0.1.2", "0.2.0", "0.2.1", "0.2.2", "0.2.3
|
|
134
134
|
rspec from: [">= 0"] to: ["= 2.14.1"]
|
135
135
|
```
|
136
136
|
|
137
|
+
#### Platforms
|
138
|
+
|
139
|
+
*gem-compare* supports querying different gem platforms via standard `--platform` option. To compare
|
140
|
+
nokogiri gem on different platform run:
|
141
|
+
```
|
142
|
+
$ gem compare nokogiri 1.5.6 1.6.1 -ak --platform java # for JRuby
|
143
|
+
$ gem compare nokogiri 1.5.6 1.6.1 -ak --platform x86-mingw32 # on Windows
|
144
|
+
```
|
145
|
+
|
137
146
|
### Supported options
|
138
147
|
|
139
148
|
To see all possible options run:
|
data/Rakefile
CHANGED
@@ -4,7 +4,7 @@ require 'rdoc/task'
|
|
4
4
|
|
5
5
|
gemspec = Gem::Specification.new do |s|
|
6
6
|
s.name = 'gem-compare'
|
7
|
-
s.version = '0.0.
|
7
|
+
s.version = '0.0.3'
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.summary = 'RubyGems plugin for comparing gem versions'
|
10
10
|
s.description = <<-EOF
|
@@ -19,6 +19,8 @@ gemspec = Gem::Specification.new do |s|
|
|
19
19
|
'lib/**/*.rb', 'lib/**/**/*.rb', 'test/**/test*.rb']
|
20
20
|
s.required_ruby_version = '>= 2.0.0'
|
21
21
|
s.required_rubygems_version = '>= 2.0.0'
|
22
|
+
s.add_runtime_dependency 'json'
|
23
|
+
s.add_runtime_dependency 'curl'
|
22
24
|
s.add_runtime_dependency 'diffy'
|
23
25
|
s.add_runtime_dependency 'rainbow'
|
24
26
|
s.add_runtime_dependency 'gemnasium-parser'
|
@@ -1,7 +1,10 @@
|
|
1
1
|
require 'rubygems/command'
|
2
|
+
require 'rubygems/version_option'
|
2
3
|
require 'rubygems/comparator'
|
3
4
|
|
4
5
|
class Gem::Commands::CompareCommand < Gem::Command
|
6
|
+
include Gem::VersionOption
|
7
|
+
|
5
8
|
def initialize
|
6
9
|
super 'compare', 'Compare gem\'s versions and generate a report of changes',
|
7
10
|
:output => Dir.pwd
|
@@ -18,7 +21,10 @@ class Gem::Commands::CompareCommand < Gem::Command
|
|
18
21
|
options[:no_color] = true
|
19
22
|
end
|
20
23
|
|
21
|
-
|
24
|
+
# Speficy the platform using --platform=PLATFORM
|
25
|
+
add_platform_option
|
26
|
+
|
27
|
+
add_option('-pPARAM', '--param PARAM', 'Compare only a given paramater') do |param, options|
|
22
28
|
options[:param] = param
|
23
29
|
end
|
24
30
|
|
data/lib/rubygems/comparator.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'tmpdir'
|
2
2
|
require 'rbconfig'
|
3
3
|
require 'rainbow'
|
4
|
+
require 'curb'
|
5
|
+
require 'json'
|
4
6
|
require 'rubygems/package'
|
5
7
|
require 'rubygems/dependency'
|
6
8
|
require 'rubygems/spec_fetcher'
|
7
|
-
require 'rubygems/comparator/
|
9
|
+
require 'rubygems/comparator/utils'
|
8
10
|
require 'rubygems/comparator/report'
|
9
11
|
require 'rubygems/comparator/spec_comparator'
|
10
12
|
require 'rubygems/comparator/file_list_comparator'
|
@@ -17,7 +19,7 @@ require 'rubygems/comparator/gemfile_comparator'
|
|
17
19
|
# Gemfiles
|
18
20
|
|
19
21
|
class Gem::Comparator
|
20
|
-
include Gem::Comparator::
|
22
|
+
include Gem::Comparator::Utils
|
21
23
|
attr_accessor :options, :report
|
22
24
|
|
23
25
|
VERSION = '0.0.2'
|
@@ -28,6 +30,8 @@ class Gem::Comparator
|
|
28
30
|
# Creates temporal directory if the gem files shouldn't be kept
|
29
31
|
|
30
32
|
def initialize(options)
|
33
|
+
info "gem-compare in #{VERSION}"
|
34
|
+
|
31
35
|
unless options[:keep_all]
|
32
36
|
options[:output] = Dir.mktmpdir
|
33
37
|
end
|
@@ -40,6 +44,14 @@ class Gem::Comparator
|
|
40
44
|
Rainbow.enabled = false
|
41
45
|
end
|
42
46
|
|
47
|
+
# Let's override platforms with the latest one if
|
48
|
+
# a platform has been specified via --platform
|
49
|
+
if options[:added_platform]
|
50
|
+
Gem.platforms = [Gem.platforms.last]
|
51
|
+
options[:platform] = Gem.platforms.last.to_s
|
52
|
+
info "Overriding platform to: #{options[:platform]}"
|
53
|
+
end
|
54
|
+
|
43
55
|
@options = options
|
44
56
|
|
45
57
|
# Results from the comparison
|
@@ -52,18 +64,22 @@ class Gem::Comparator
|
|
52
64
|
# Compares file lists, requirements, other meta data
|
53
65
|
|
54
66
|
def compare_versions(gem_name, versions)
|
55
|
-
info "gem-compare in #{VERSION}"
|
56
67
|
# Expand versions (<=, >=, ~>) and sort them
|
57
68
|
versions = expand_versions(gem_name, versions)
|
58
69
|
|
59
70
|
error 'Only one version specified. Specify at lease two versions.' \
|
60
71
|
if versions.size == 1
|
61
72
|
|
73
|
+
# This should match the final versions that has been compared
|
74
|
+
@compared_versions = versions
|
75
|
+
|
62
76
|
versions.each do |version|
|
63
|
-
download_gems? ?
|
77
|
+
download_gems? ?
|
78
|
+
get_package(gem_name, version) :
|
79
|
+
get_specification(gem_name, version)
|
64
80
|
end
|
65
81
|
|
66
|
-
@report.set_header "Compared versions: #{
|
82
|
+
@report.set_header "Compared versions: #{@compared_versions}"
|
67
83
|
|
68
84
|
comparators = [SpecComparator,
|
69
85
|
FileListComparator,
|
@@ -72,7 +88,7 @@ class Gem::Comparator
|
|
72
88
|
|
73
89
|
comparators.each do |c|
|
74
90
|
comparator = c.new
|
75
|
-
cmp = (
|
91
|
+
cmp = (comparator.compares == :packages) ? gem_packages.values : gem_specs.values
|
76
92
|
@report = comparator.compare(cmp, @report, @options)
|
77
93
|
end
|
78
94
|
|
@@ -95,9 +111,10 @@ class Gem::Comparator
|
|
95
111
|
# Return list of expanded versions
|
96
112
|
|
97
113
|
def expand_versions(gem_name, versions)
|
98
|
-
info
|
114
|
+
info "Expanding versions #{versions}..."
|
99
115
|
expanded = []
|
100
116
|
versions.each do |version|
|
117
|
+
version = latest_gem_version(gem_name) if version == '_'
|
101
118
|
if version =~ VERSION_REGEX
|
102
119
|
expanded << version
|
103
120
|
next
|
@@ -121,22 +138,46 @@ class Gem::Comparator
|
|
121
138
|
|
122
139
|
error 'No versions found.' if versions.size == 0
|
123
140
|
|
124
|
-
info "
|
141
|
+
info "Expanded versions: #{versions}"
|
125
142
|
versions
|
126
143
|
end
|
127
144
|
|
145
|
+
def remote_gem_versions(gem_name)
|
146
|
+
client = Curl::Easy.new
|
147
|
+
client.url = "https://rubygems.org/api/v1/versions/#{gem_name}.json"
|
148
|
+
client.follow_location = true
|
149
|
+
client.http_get
|
150
|
+
json = JSON.parse(client.body_str)
|
151
|
+
gems = json.collect { |version| version['number'] }
|
152
|
+
info "Upstream versions: #{gems}"
|
153
|
+
gems
|
154
|
+
end
|
155
|
+
|
156
|
+
def latest_gem_version(gem_name)
|
157
|
+
remote_gem_versions(gem_name).map{ |v| Gem::Version.new v }.max.to_s
|
158
|
+
end
|
159
|
+
|
128
160
|
def gem_file_name(gem_name, version)
|
129
|
-
|
161
|
+
if @options[:platform]
|
162
|
+
"#{gem_name}-#{version}-#{@options[:platform]}.gem"
|
163
|
+
else
|
164
|
+
"#{gem_name}-#{version}.gem"
|
165
|
+
end
|
130
166
|
end
|
131
167
|
|
132
|
-
def
|
168
|
+
def get_package(gem_name, version)
|
133
169
|
gem_file = gem_file_name(gem_name, version)
|
134
170
|
return gem_packages["#{gem_file}"] if gem_packages["#{gem_file}"]
|
135
171
|
|
136
172
|
find_downloaded_gem(gem_file)
|
137
173
|
return gem_packages["#{gem_file}"] if gem_packages["#{gem_file}"]
|
138
174
|
|
139
|
-
|
175
|
+
download_package(gem_name, version)
|
176
|
+
end
|
177
|
+
|
178
|
+
def download_package(gem_name, version)
|
179
|
+
spec, source = get_specification(gem_name, version)
|
180
|
+
gem_file = gem_file_name(gem_name, spec.version.to_s)
|
140
181
|
|
141
182
|
Dir.chdir @options[:output] do
|
142
183
|
source.download spec
|
@@ -149,22 +190,44 @@ class Gem::Comparator
|
|
149
190
|
package
|
150
191
|
end
|
151
192
|
|
152
|
-
def
|
193
|
+
def get_specification(gem_name, version)
|
153
194
|
gem_file = gem_file_name(gem_name, version)
|
154
195
|
return gem_specs["#{gem_file}"] if gem_specs["#{gem_file}"]
|
155
196
|
|
156
197
|
find_downloaded_gem(gem_file)
|
157
198
|
return gem_specs["#{gem_file}"] if gem_specs["#{gem_file}"]
|
158
199
|
|
200
|
+
download_specification(gem_name, version)
|
201
|
+
end
|
202
|
+
|
203
|
+
def download_specification(gem_name, version)
|
159
204
|
dep = Gem::Dependency.new gem_name, version
|
160
205
|
specs_and_sources, _errors = Gem::SpecFetcher.fetcher.spec_for_dependency dep
|
161
206
|
spec, source = specs_and_sources.max_by { |s,| s.version }
|
162
207
|
error "Gem #{gem_name} in #{version} doesn't exist." if spec.nil?
|
208
|
+
|
209
|
+
fix_comparing_version(version, spec.version.to_s)
|
210
|
+
gem_file = gem_file_name(gem_name, spec.version.to_s)
|
211
|
+
|
163
212
|
gem_specs["#{gem_file}"] = spec
|
164
213
|
|
165
214
|
[spec, source]
|
166
215
|
end
|
167
216
|
|
217
|
+
##
|
218
|
+
# Ensure the right version is referenced
|
219
|
+
|
220
|
+
def fix_comparing_version(version, spec_version)
|
221
|
+
if spec_version != version
|
222
|
+
@compared_versions.each do |v|
|
223
|
+
if v == version
|
224
|
+
@compared_versions[@compared_versions.index(version)] = spec_version
|
225
|
+
return
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
168
231
|
def find_downloaded_gem(gem_file)
|
169
232
|
if File.exist? File.join(@options[:output], gem_file)
|
170
233
|
info "#{gem_file} exists, using already downloaded file."
|
@@ -184,7 +247,7 @@ class Gem::Comparator
|
|
184
247
|
|
185
248
|
def download_gems?
|
186
249
|
return true if @options[:keep_all]
|
187
|
-
@options[:param] ?
|
250
|
+
@options[:param] ? !param_available_in_marshal?(@options[:param]) : true
|
188
251
|
end
|
189
252
|
|
190
253
|
def gem_packages
|
@@ -1,65 +1,28 @@
|
|
1
1
|
require 'rainbow'
|
2
|
+
require 'rubygems/comparator/utils'
|
3
|
+
require 'rubygems/comparator/report'
|
2
4
|
|
3
5
|
class Gem::Comparator
|
4
|
-
|
5
|
-
include Gem::
|
6
|
+
class Base
|
7
|
+
include Gem::Comparator::Utils
|
8
|
+
include Gem::Comparator::Report::Signs
|
6
9
|
|
7
|
-
|
10
|
+
attr_accessor :compares
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
SPEC_PARAMS = %w[ author authors name platform require_paths rubygems_version summary
|
15
|
-
license licenses bindir cert_chain description email executables
|
16
|
-
extensions homepage metadata post_install_message rdoc_options
|
17
|
-
required_ruby_version required_rubygems_version requirements
|
18
|
-
signing_key has_rdoc date version ].sort
|
19
|
-
SPEC_FILES_PARAMS = %w[ files test_files extra_rdoc_files ]
|
20
|
-
DEPENDENCY_PARAMS = %w[ runtime_dependency development_dependency ]
|
21
|
-
GEMFILE_PARAMS = %w[ gemfiles ]
|
12
|
+
##
|
13
|
+
# Compare Gem::Specification objects by default
|
14
|
+
#
|
15
|
+
# To override create your own initialize method and
|
16
|
+
# set expect(:packages) to expect Gem::Package objects.
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
(SPEC_PARAMS.include? param) ||
|
27
|
-
(SPEC_FILES_PARAMS.include? param) ||
|
28
|
-
(DEPENDENCY_PARAMS.include? param) ||
|
29
|
-
(GEMFILE_PARAMS.include? param)
|
30
|
-
end
|
31
|
-
|
32
|
-
def filter_params(params, param)
|
33
|
-
if param
|
34
|
-
if params.include? param
|
35
|
-
return [param]
|
36
|
-
else
|
37
|
-
return []
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
params
|
42
|
-
end
|
43
|
-
|
44
|
-
def same
|
45
|
-
Rainbow('SAME').green.bright
|
46
|
-
end
|
18
|
+
def initialize
|
19
|
+
expect(:specs)
|
20
|
+
end
|
47
21
|
|
48
|
-
|
49
|
-
Rainbow('DIFFERENT').red.bright
|
50
|
-
end
|
51
|
-
|
52
|
-
def info(msg)
|
53
|
-
say msg if Gem.configuration.really_verbose
|
54
|
-
end
|
55
|
-
|
56
|
-
def warn(msg)
|
57
|
-
say Rainbow("WARNING: #{msg}").red
|
58
|
-
end
|
22
|
+
private
|
59
23
|
|
60
|
-
def
|
61
|
-
|
62
|
-
exit 1
|
24
|
+
def expect(what)
|
25
|
+
@compares = what
|
63
26
|
end
|
64
27
|
|
65
28
|
def extract_gem(package, target_dir)
|
@@ -76,13 +39,5 @@ class Gem::Comparator
|
|
76
39
|
gem_dir
|
77
40
|
end
|
78
41
|
|
79
|
-
def check_diff_command_is_installed
|
80
|
-
begin
|
81
|
-
IO.popen('diff --version')
|
82
|
-
rescue Exception
|
83
|
-
raise DiffCommandMissing, \
|
84
|
-
'Calling `diff` command failed. Do you have it installed?'
|
85
|
-
end
|
86
|
-
end
|
87
42
|
end
|
88
43
|
end
|
@@ -7,15 +7,12 @@ class Gem::Comparator
|
|
7
7
|
# compare dependencies between gem's versions
|
8
8
|
# based on the given Gem::Specification objects
|
9
9
|
|
10
|
-
class DependencyComparator
|
11
|
-
include Gem::Comparator::Base
|
12
|
-
|
13
|
-
COMPARES = :specs
|
10
|
+
class DependencyComparator < Gem::Comparator::Base
|
14
11
|
|
15
12
|
##
|
16
13
|
# Compare dependencies in given +specs+ and
|
17
14
|
# write the changes to the +report+
|
18
|
-
#
|
15
|
+
#
|
19
16
|
# If +options[:param]+ is set, it compares only
|
20
17
|
# those dependencies
|
21
18
|
|
@@ -25,12 +22,11 @@ class Gem::Comparator
|
|
25
22
|
filter_params(DEPENDENCY_PARAMS, options[:param]).each do |param|
|
26
23
|
all_same = true
|
27
24
|
type = param.gsub('_dependency', '').to_sym
|
28
|
-
|
29
25
|
specs.each_with_index do |s, index|
|
30
26
|
next if index == 0
|
31
27
|
|
32
|
-
prev_deps = specs[index-1].dependencies.
|
33
|
-
curr_deps = specs[index].dependencies.
|
28
|
+
prev_deps = specs[index-1].dependencies.select { |d| d.type == type }
|
29
|
+
curr_deps = specs[index].dependencies.select { |d| d.type == type }
|
34
30
|
added, deleted, updated = resolve_dependencies(prev_deps, curr_deps)
|
35
31
|
|
36
32
|
if (!deleted.empty? || !added.empty? || !updated.empty?)
|
@@ -60,20 +56,21 @@ class Gem::Comparator
|
|
60
56
|
end
|
61
57
|
if all_same && options[:log_all]
|
62
58
|
report[param].set_header "#{same} #{type} dependencies:" if options[:log_all]
|
63
|
-
deps = specs[0].dependencies.
|
59
|
+
deps = specs[0].dependencies.select{ |d| d.type == type }.map{ |d| "#{d.name}: #{d.requirements_list}" }
|
64
60
|
deps = '[]' if deps.empty?
|
65
61
|
report[param] << deps
|
66
62
|
elsif !all_same
|
67
63
|
report[param].set_header "#{different} #{type} dependencies:"
|
68
64
|
end
|
69
65
|
end
|
66
|
+
|
70
67
|
report
|
71
68
|
end
|
72
69
|
|
73
70
|
private
|
74
|
-
|
71
|
+
|
75
72
|
##
|
76
|
-
# Find dependencies between +prev_deps+ and +curr_deps+
|
73
|
+
# Find dependencies between +prev_deps+ and +curr_deps+
|
77
74
|
#
|
78
75
|
# Return [added, deleted, updated] deps
|
79
76
|
|
@@ -82,7 +79,7 @@ class Gem::Comparator
|
|
82
79
|
deleted = prev_deps - curr_deps
|
83
80
|
split_dependencies(added, deleted)
|
84
81
|
end
|
85
|
-
|
82
|
+
|
86
83
|
##
|
87
84
|
# Find updated dependencies between +added+ and
|
88
85
|
# +deleted+ deps and move them out to +updated+.
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module DirUtils
|
4
|
+
SHEBANG_REGEX = /\A#!.*/
|
5
|
+
|
6
|
+
attr_accessor :files_first_line
|
7
|
+
|
8
|
+
def self.file_first_line(file)
|
9
|
+
File.open(file){ |f| f.readline }.gsub(/(.*)\n/, '\1')
|
10
|
+
rescue
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.file_has_shebang?(file)
|
14
|
+
file_first_line(file) =~ SHEBANG_REGEX
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.files_same_first_line?(file1, file2)
|
18
|
+
file_first_line(file1) == file_first_line(file2)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.file_permissions(file)
|
22
|
+
sprintf("%o", File.stat(file).mode)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.gem_bin_file?(file)
|
26
|
+
file =~ /(\A|.*\/)bin\/.*/
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Returns a unique list of directories and top level files
|
31
|
+
# out of an array of files
|
32
|
+
|
33
|
+
def self.dirs_of_files(file_list)
|
34
|
+
dirs_of_files = []
|
35
|
+
file_list.each do |file|
|
36
|
+
unless Pathname.new(file).dirname.to_s == '.'
|
37
|
+
dirs_of_files << "#{Pathname.new(file).dirname.to_s}/"
|
38
|
+
else
|
39
|
+
dirs_of_files << file
|
40
|
+
end
|
41
|
+
end
|
42
|
+
dirs_of_files.uniq
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.remove_subdirs(dirs)
|
46
|
+
dirs.dup.sort_by(&:length).reverse.each do |dir|
|
47
|
+
dirs.delete_if{ |d| d =~ /#{dir}\/.+/ }
|
48
|
+
end
|
49
|
+
dirs
|
50
|
+
end
|
51
|
+
end
|