gem-compare 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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