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.
@@ -1,5 +1,7 @@
1
1
  require 'diffy'
2
2
  require 'rubygems/comparator/base'
3
+ require 'rubygems/comparator/dir_utils'
4
+ require 'rubygems/comparator/monitor'
3
5
 
4
6
  class Gem::Comparator
5
7
 
@@ -11,10 +13,18 @@ class Gem::Comparator
11
13
  # To compare the files it needs to extract
12
14
  # gem packages to +options[:output]+
13
15
 
14
- class FileListComparator
15
- include Gem::Comparator::Base
16
+ class FileListComparator < Gem::Comparator::Base
16
17
 
17
- COMPARES = :packages
18
+ def initialize
19
+ expect(:packages)
20
+
21
+ # We need diff
22
+ begin
23
+ IO.popen('diff --version')
24
+ rescue Exception
25
+ error('Calling `diff` command failed. Do you have it installed?')
26
+ end
27
+ end
18
28
 
19
29
  ##
20
30
  # Compare file lists for gem's Gem::Package objects
@@ -25,7 +35,6 @@ class Gem::Comparator
25
35
 
26
36
  def compare(packages, report, options = {})
27
37
  info 'Checking file lists...'
28
- check_diff_command_is_installed
29
38
 
30
39
  @packages = packages
31
40
 
@@ -44,38 +53,42 @@ class Gem::Comparator
44
53
 
45
54
  vers = "#{packages[index-1].spec.version}->#{packages[index].spec.version}"
46
55
 
47
- if previous == current && !all_same
48
- report[param][vers] << "#{Rainbow(packages[index-1].spec.version).blue}->" + \
49
- "#{Rainbow(packages[index].spec.version).blue}: No change"
56
+ deleted = previous - current
57
+ added = current - previous
58
+ same = current - added
59
+
60
+ if options[:brief]
61
+ deleted, dirs_added = dir_changed(previous, current)
50
62
  end
51
63
 
52
- unless previous == current
53
- deleted = previous - current
54
- added = current - previous
55
- same = current - added
64
+ report[param].set_header "#{different} #{param}:"
56
65
 
57
- if !added.empty? || !deleted.empty?
58
- report[param].set_header "#{different} #{param}:"
59
- all_same = false
66
+ report[param][vers].section do
67
+ set_header "#{Rainbow(packages[index-1].spec.version).blue}->" +
68
+ "#{Rainbow(packages[index].spec.version).blue}:"
69
+ nest('deleted').section do
70
+ set_header '* Deleted:'
71
+ puts deleted unless deleted.empty?
60
72
  end
61
73
 
62
- report[param][vers].section do
63
- set_header "#{Rainbow(packages[index-1].spec.version).blue}->" +
64
- "#{Rainbow(packages[index].spec.version).blue}:"
65
- nest('deleted').section do
66
- set_header '* Deleted:'
67
- puts deleted unless deleted.empty?
68
- end
69
-
70
- nest('added').section do
71
- set_header '* Added:'
72
- puts added unless added.empty?
73
- end
74
+ nest('added').section do
75
+ set_header '* Added:'
76
+ puts dirs_added if options[:brief]
74
77
  end
78
+ end
79
+ # Add information about permissions, shebangs etc.
80
+ report = check_added_files(param, vers, index, added, report, options[:brief])
75
81
 
76
- report[param][vers]['changed'].set_header '* Changed:'
77
- report = check_same_files(param, vers, index, same, report)
82
+ report[param][vers]['changed'].set_header '* Changed:'
83
+ report = check_same_files(param, vers, index, same, report, options[:brief])
84
+ same_files = report[param][vers]['changed'].messages.empty?
85
+ all_same = false unless same_files
86
+
87
+ if previous == current && same_files && !all_same
88
+ report[param][vers] << "#{Rainbow(packages[index-1].spec.version).blue}->" + \
89
+ "#{Rainbow(packages[index].spec.version).blue}: No change"
78
90
  end
91
+
79
92
  end
80
93
 
81
94
  if all_same && options[:log_all]
@@ -93,27 +106,62 @@ class Gem::Comparator
93
106
  ##
94
107
  # Access @unpacked_gem_dirs hash that stores
95
108
  # paths to the unpacked gem dirs
96
- #
109
+ #
97
110
  # Keys of the hash are gem's versions
98
111
 
99
112
  def unpacked_gem_dirs
100
113
  @unpacked_gem_dirs ||= {}
101
114
  end
102
115
 
103
- def check_same_files(param, vers, index, files, report)
116
+ ##
117
+ # This returns [deleted, added] directories between
118
+ # +previous+ and +current+ file lists
119
+ #
120
+ # For top level (.) it compares files themselves
121
+
122
+ def dir_changed(previous, current)
123
+ prev_dirs = DirUtils.dirs_of_files(previous)
124
+ curr_dirs = DirUtils.dirs_of_files(current)
125
+ deleted = DirUtils.remove_subdirs(prev_dirs - curr_dirs)
126
+ added = DirUtils.remove_subdirs(curr_dirs - prev_dirs)
127
+ [deleted, added]
128
+ end
129
+
130
+ def check_added_files(param, vers, index, files, report, brief_mode)
131
+ files.each do |file|
132
+ added_file = File.join(unpacked_gem_dirs[@packages[index].spec.version], file)
133
+
134
+ #line_changes = lines_changed(prev_file, curr_file)
135
+
136
+ changes = Monitor.new_file_permissions(added_file),
137
+ Monitor.new_file_executability(added_file),
138
+ Monitor.new_file_shebang(added_file)
139
+
140
+ if(!changes.join.empty? || !brief_mode)
141
+ report[param][vers]['added'] << "#{file}"
142
+ end
143
+
144
+ changes.each do |change|
145
+ report[param][vers]['added'] << change unless change.empty?
146
+ end
147
+ end
148
+ report
149
+ end
150
+
151
+ def check_same_files(param, vers, index, files, report, brief_mode)
104
152
  files.each do |file|
105
153
  prev_file = File.join(unpacked_gem_dirs[@packages[index-1].spec.version], file)
106
154
  curr_file = File.join(unpacked_gem_dirs[@packages[index].spec.version], file)
107
155
 
108
156
  next unless check_files([prev_file, curr_file])
109
157
 
110
- line_changes = lines_changed(prev_file, curr_file)
158
+ line_changes = Monitor.lines_changed(prev_file, curr_file)
111
159
 
112
- changes = permission_changed(prev_file, curr_file),
113
- executables_changed(prev_file, curr_file),
114
- shebangs_changed(prev_file, curr_file)
160
+ changes = Monitor.files_permissions_changes(prev_file, curr_file),
161
+ Monitor.files_executability_changes(prev_file, curr_file),
162
+ Monitor.files_shebang_changes(prev_file, curr_file)
115
163
 
116
- unless (line_changes.empty? && changes.join.empty?)
164
+ if(!changes.join.empty? || (!brief_mode && !line_changes.empty?))
117
165
  report[param][vers]['changed'] << \
118
166
  "#{file} #{line_changes}"
119
167
  end
@@ -139,124 +187,5 @@ class Gem::Comparator
139
187
  true
140
188
  end
141
189
 
142
-
143
- ##
144
- # Return how many lines differ between +prev_file+
145
- # and +curr_file+ in format +ADDED/-DELETED
146
-
147
- def lines_changed(prev_file, curr_file)
148
- line = compact_files_diff(prev_file, curr_file)
149
- return '' if line.empty?
150
- "#{Rainbow(line.count('+')).green}/#{Rainbow(line.count('-')).red}"
151
- end
152
-
153
- ##
154
- # Return +value+ in the given +spec+
155
-
156
- def value_from_spec(param, spec)
157
- if spec.respond_to? :"#{param}"
158
- spec.send(:"#{param}")
159
- else
160
- warn "#{spec.full_name} does not respond to " +
161
- "#{param}, skipping check"
162
- nil
163
- end
164
- end
165
-
166
- ##
167
- # Return changes between files:
168
- # + for line added
169
- # - for line deleted
170
-
171
- def compact_files_diff(prev_file, curr_file)
172
- changes = ''
173
- Diffy::Diff.new(
174
- prev_file, curr_file, :source => 'files', :context => 0
175
- ).each do |line|
176
- case line
177
- when /^\+/ then changes << Rainbow('+').green
178
- when /^-/ then changes << Rainbow('-').red
179
- end
180
- end
181
- changes
182
- end
183
-
184
- ##
185
- # Get file's permission
186
-
187
- def file_permissions(file)
188
- sprintf("%o", File.stat(file).mode)
189
- end
190
-
191
- ##
192
- # Find and return permission changes between files
193
-
194
- def permission_changed(prev_file, curr_file)
195
- prev_permissions = file_permissions(prev_file)
196
- curr_permissions = file_permissions(curr_file)
197
-
198
- if prev_permissions != curr_permissions
199
- " (!) New permissions: " +
200
- "#{prev_permissions} -> #{curr_permissions}"
201
- else
202
- ''
203
- end
204
- end
205
-
206
- ##
207
- # Find if the file is now/or was executable
208
-
209
- def executables_changed(prev_file, curr_file)
210
- prev_executable = File.stat(prev_file).executable?
211
- curr_executable = File.stat(curr_file).executable?
212
-
213
- if !prev_executable && curr_executable
214
- " (!) File is now executable!"
215
- elsif prev_executable && !curr_executable
216
- " (!) File is no longer executable!"
217
- else
218
- ''
219
- end
220
- end
221
-
222
- ##
223
- # Return the first line of the +file+
224
-
225
- def first_line(file)
226
- begin
227
- File.open(file) { |f| f.readline }.gsub(/(.*)\n/, '\1')
228
- rescue
229
- info "#{file} is binary, skipping shebang check"
230
- ''
231
- end
232
- end
233
-
234
- ##
235
- # Find if the shabang of the file has been changed
236
-
237
- def shebangs_changed(prev_file, curr_file)
238
- first_lines = {}
239
-
240
- [prev_file, curr_file].each do |file|
241
- first_lines[file] = first_line(file)
242
- end
243
-
244
- return '' if first_lines[prev_file] == first_lines[curr_file]
245
-
246
- prev_has_shebang = (first_lines[prev_file] =~ SHEBANG_REGEX)
247
- curr_has_shebang = (first_lines[curr_file] =~ SHEBANG_REGEX)
248
-
249
- if prev_has_shebang && !curr_has_shebang
250
- " (!) Shebang probably lost: #{first_lines[prev_file]}"
251
- elsif !prev_has_shebang && curr_has_shebang
252
- " (!) Shebang probably added: #{first_lines[curr_file]}"
253
- elsif prev_has_shebang && curr_has_shebang
254
- " (!) Shebang probably changed: " +
255
- "#{first_lines[prev_file]} -> #{first_lines[curr_file]}"
256
- else
257
- ''
258
- end
259
- end
260
-
261
190
  end
262
191
  end
@@ -11,10 +11,11 @@ class Gem::Comparator
11
11
  # To compare Gemfiles it needs to extract
12
12
  # gem packages to +options[:output]+
13
13
 
14
- class GemfileComparator
15
- include Gem::Comparator::Base
14
+ class GemfileComparator < Gem::Comparator::Base
16
15
 
17
- COMPARES = :packages
16
+ def initialize
17
+ expect(:packages)
18
+ end
18
19
 
19
20
  ##
20
21
  # Compare Gemfiles using gem's +packages+
@@ -39,29 +40,21 @@ class Gem::Comparator
39
40
  report['gemfiles'][vers].set_header "#{Rainbow(packages[index-1].spec.version).blue}->" +
40
41
  "#{Rainbow(packages[index].spec.version).blue}:"
41
42
 
42
- if File.exist?(prev_gemfile) && File.exist?(curr_gemfile)
43
- added, deleted, updated = compare_gemfiles(prev_gemfile, curr_gemfile)
43
+ added, deleted, updated = compare_gemfiles(prev_gemfile, curr_gemfile)
44
44
 
45
- report['gemfiles'][vers]['added'].section do
46
- set_header '* Added:'
47
- puts added.map { |x| "#{x.name} #{x.requirements_list}" } unless added.empty?
48
- end
49
- report['gemfiles'][vers]['deleted'].section do
50
- set_header '* Deleted'
51
- puts deleted.map { |x| "#{x.name} #{x.requirements_list}" } unless deleted.empty?
52
- end
53
- report['gemfiles'][vers]['updated'].section do
54
- set_header '* Updated'
55
- puts updated unless updated.empty?
56
- end
57
- all_same = false if !added.empty? || !deleted.empty?
58
- elsif File.exist?(prev_gemfile)
59
- report['gemfiles'][vers] << "Gemfile removed"
60
- all_same = false
61
- elsif File.exist?(curr_gemfile)
62
- report['gemfiles'][vers] << "Gemfile added"
63
- all_same = false
45
+ report['gemfiles'][vers]['added'].section do
46
+ set_header '* Added:'
47
+ puts added.map { |x| "#{x.name} #{x.requirements_list}" } unless added.empty?
64
48
  end
49
+ report['gemfiles'][vers]['deleted'].section do
50
+ set_header '* Deleted'
51
+ puts deleted.map { |x| "#{x.name} #{x.requirements_list}" } unless deleted.empty?
52
+ end
53
+ report['gemfiles'][vers]['updated'].section do
54
+ set_header '* Updated'
55
+ puts updated unless updated.empty?
56
+ end
57
+ all_same = false if !added.empty? || !deleted.empty?
65
58
  end
66
59
  if all_same && options[:log_all]
67
60
  report['gemfiles'].set_header "#{same} Gemfiles:"
@@ -81,17 +74,17 @@ class Gem::Comparator
81
74
  end
82
75
 
83
76
  private
84
-
77
+
85
78
  ##
86
79
  # Access @unpacked_gem_dirs hash that stores
87
80
  # paths to the unpacked gem dirs
88
- #
81
+ #
89
82
  # Keys of the hash are gem's versions
90
83
 
91
84
  def unpacked_gem_dirs
92
85
  @unpacked_gem_dirs ||= {}
93
86
  end
94
-
87
+
95
88
  ##
96
89
  # Compare two Gemfiles for dependencies
97
90
  #
@@ -105,14 +98,18 @@ class Gem::Comparator
105
98
 
106
99
  split_dependencies(added, deleted)
107
100
  end
108
-
101
+
109
102
  ##
110
103
  # Get the Gemfile dependencies from +gemfile+
111
104
 
112
105
  def gemfile_deps(gemfile)
113
- parse_gemfile(gemfile).dependencies
106
+ if File.exist?(gemfile)
107
+ parse_gemfile(gemfile).dependencies
108
+ else
109
+ []
110
+ end
114
111
  end
115
-
112
+
116
113
  ##
117
114
  # Parse +gemfile+ using Gemnasium::Parser
118
115
  #
@@ -121,7 +118,7 @@ class Gem::Comparator
121
118
  def parse_gemfile(gemfile)
122
119
  Gemnasium::Parser.gemfile File.open(gemfile).read
123
120
  end
124
-
121
+
125
122
  ##
126
123
  # Find updated dependencies between +added+ and
127
124
  # +deleted+ deps and move them out to +updated+.
@@ -0,0 +1,106 @@
1
+ require 'diffy'
2
+ require 'rubygems/comparator/base'
3
+ require 'rubygems/comparator/dir_utils'
4
+
5
+ class Gem::Comparator
6
+ module Monitor
7
+
8
+ def self.lines_changed(prev_file, curr_file)
9
+ line = compact_files_diff(prev_file, curr_file)
10
+ return '' if line.empty?
11
+ plus = "+#{line.count('+')}"
12
+ minus = "-#{line.count('-')}"
13
+ "#{Rainbow(plus).green}/#{Rainbow(minus).red}"
14
+ end
15
+
16
+ def self.compact_files_diff(prev_file, curr_file)
17
+ changes = ''
18
+ Diffy::Diff.new(
19
+ prev_file, curr_file, :source => 'files', :context => 0
20
+ ).each do |line|
21
+ case line
22
+ when /^\+/ then changes << Rainbow('+').green
23
+ when /^-/ then changes << Rainbow('-').red
24
+ end
25
+ end
26
+ changes
27
+ end
28
+
29
+ def self.files_permissions_changes(prev_file, curr_file)
30
+ prev_permissions = DirUtils.file_permissions(prev_file)
31
+ curr_permissions = DirUtils.file_permissions(curr_file)
32
+
33
+ if prev_permissions != curr_permissions
34
+ " (!) New permissions: " +
35
+ "#{prev_permissions} -> #{curr_permissions}"
36
+ else
37
+ ''
38
+ end
39
+ end
40
+
41
+ def self.new_file_permissions(file)
42
+ file_permissions = DirUtils.file_permissions(file)
43
+
44
+ if file_permissions != '100644'
45
+ unless (DirUtils.gem_bin_file?(file) && file_permissions == '100755')
46
+ " (!) Unexpected permissions: #{file_permissions}"
47
+ end
48
+ else
49
+ ''
50
+ end
51
+ end
52
+
53
+ def self.files_executability_changes(prev_file, curr_file)
54
+ prev_executable = File.stat(prev_file).executable?
55
+ curr_executable = File.stat(curr_file).executable?
56
+
57
+ if !prev_executable && curr_executable
58
+ " (!) File is now executable!"
59
+ elsif prev_executable && !curr_executable
60
+ " (!) File is no longer executable!"
61
+ else
62
+ ''
63
+ end
64
+ end
65
+
66
+ def self.new_file_executability(file)
67
+ file_executable = File.stat(file).executable?
68
+
69
+ if file_executable && !DirUtils.gem_bin_file?(file)
70
+ " (!) File is executable"
71
+ elsif !file_executable && DirUtils.gem_bin_file?(file)
72
+ " (!) File is not executable"
73
+ else
74
+ ''
75
+ end
76
+ end
77
+
78
+ def self.files_shebang_changes(prev_file, curr_file)
79
+ return '' if DirUtils.files_same_first_line?(prev_file, curr_file)
80
+
81
+ prev_has_shebang = DirUtils.file_has_shebang? prev_file
82
+ curr_has_shebang = DirUtils.file_has_shebang? curr_file
83
+
84
+ if prev_has_shebang && !curr_has_shebang
85
+ " (!) Shebang probably lost: #{DirUtils.file_first_line(prev_file)}"
86
+ elsif !prev_has_shebang && curr_has_shebang
87
+ " (!) Shebang probably added: #{DirUtils.file_first_line(curr_file)}"
88
+ elsif prev_has_shebang && curr_has_shebang
89
+ " (!) Shebang probably changed: " +
90
+ "#{first_lines[prev_file]} -> #{DirUtils.file_first_line(curr_file)}"
91
+ else
92
+ ''
93
+ end
94
+ end
95
+
96
+ def self.new_file_shebang(file)
97
+ file_has_shebang = DirUtils.file_has_shebang? file
98
+
99
+ if file_has_shebang
100
+ " (!) Shebang found: #{DirUtils.file_first_line(file)}"
101
+ else
102
+ ''
103
+ end
104
+ end
105
+ end
106
+ end