maximus 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,2 +1,60 @@
1
- lints: true
2
- statistics: true
1
+ paths:
2
+ home: '/'
3
+
4
+ scsslint: true
5
+
6
+ rubocop: true
7
+
8
+ railsbp: true
9
+
10
+ brakeman: true
11
+
12
+ jshint:
13
+ browser: true
14
+ esnext: true
15
+ globals: {}
16
+ globalstrict: true
17
+ undef: true
18
+ unused: true
19
+ jquery: true
20
+
21
+ phantomas:
22
+ block-domain: 'google-analytics.com'
23
+ skip-modules:
24
+ - 'Caching'
25
+ - 'localStorage'
26
+ - 'Cookies'
27
+ - 'assetsWithQueryString'
28
+ - 'nodesWithInlineCSS'
29
+
30
+ stylestats:
31
+ published: false
32
+ paths: false
33
+ stylesheets: false
34
+ styleElements: true
35
+ size: true
36
+ dataUriSize: true
37
+ ratioOfDataUriSize: true
38
+ gzippedSize: false
39
+ simplicity: true
40
+ rules: true
41
+ selectors: true
42
+ mostIdentifier: true
43
+ mostIdentifierSelector: true
44
+ lowestCohesion: true
45
+ lowestCohesionSelector: true
46
+ totalUniqueFontSizes: true
47
+ uniqueFontSize: true
48
+ totalUniqueColors: true
49
+ uniqueColor: true
50
+ idSelectors: true
51
+ universalSelectors: true
52
+ unqualifiedAttributeSelectors: true
53
+ javascriptSpecificSelectors: "[#\\.]js\\-"
54
+ importantKeywords: true
55
+ floatProperties: true
56
+ mediaQueries: true
57
+ propertiesCount: 10
58
+ requestOptions: {}
59
+
60
+ wraith: true
@@ -1,3 +1,5 @@
1
+ /*global phantom */
2
+
1
3
  var system = require('system');
2
4
  var page = require('webpage').create();
3
5
  var fs = require('fs');
@@ -25,7 +27,7 @@ page.settings.userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleW
25
27
  // page.customHeaders = {
26
28
 
27
29
  // 'X-Candy-OVERRIDE': 'https://api.live.bbc.co.uk/'
28
-
30
+
29
31
  // };
30
32
 
31
33
  // If you want to set a cookie, just add your details below in the following way.
@@ -1,3 +1,5 @@
1
+ /*global phantom */
2
+
1
3
  var system = require('system');
2
4
  var page = require('webpage').create();
3
5
  var fs = require('fs');
@@ -25,7 +27,7 @@ page.settings.userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleW
25
27
  // page.customHeaders = {
26
28
 
27
29
  // 'X-Candy-OVERRIDE': 'https://api.live.bbc.co.uk/'
28
-
30
+
29
31
  // };
30
32
 
31
33
  // If you want to set a cookie, just add your details below in the following way.
@@ -14,18 +14,29 @@ module Maximus
14
14
  # @option opts [String] :commit accepts sha, "working", "last", or "master".
15
15
  def initialize(opts = {})
16
16
  opts[:config] ||= Maximus::Config.new({ commit: opts[:commit] })
17
- @config ||= opts[:config]
18
- @settings ||= @config.settings
19
- @psuedo_commit = (!@settings[:commit].blank? && (@settings[:commit] == 'working' || @settings[:commit] == 'last' || @settings[:commit] == 'master') )
20
- @g = Git.open(@settings[:root_dir], :log => @settings[:git_log])
17
+ @config = opts[:config]
18
+
19
+ @settings = @config.settings
20
+ @psuedo_commit = ( !@settings[:commit].blank? && %w(working last master).include?(@settings[:commit]) )
21
+
22
+ @g = Git.open(@settings[:root_dir])
21
23
  end
22
24
 
23
25
  # 30,000 foot view of a commit
24
- # @param commit_sha [String] the sha of the commit
26
+ # @param commit_sha [String] (head_sha) the sha of the commit
25
27
  # @return [Hash] commit data
26
- def commit_export(commit_sha = sha)
27
- ce_commit = vccommit(commit_sha)
28
- ce_diff = diff(ce_commit, @g.object('HEAD^'))
28
+ def commit_export(commit_sha = head_sha)
29
+ commit_sha = commit_sha.to_s
30
+
31
+ ce_commit = @g.gcommit(commit_sha)
32
+
33
+ if first_commit == commit_sha
34
+ ce_diff = diff_initial(first_commit)
35
+ else
36
+ last_commit = @g.gcommit(previous_commit(commit_sha))
37
+ ce_diff = diff(last_commit, ce_commit)
38
+ end
39
+
29
40
  {
30
41
  commit_sha: commit_sha,
31
42
  branch: branch,
@@ -52,58 +63,23 @@ module Maximus
52
63
  # }
53
64
  # }
54
65
  # }
55
- #
66
+ # @param sha1 [String]
67
+ # @param sha2 [String]
56
68
  # @return [Hash] diff_return files changed grouped by file extension and line number
57
- def compare(sha1 = master_commit.sha, sha2 = sha)
69
+ def compare(sha1 = master_commit.sha, sha2 = head_sha)
58
70
  diff_return = {}
59
71
 
60
- if @settings[:commit]
61
- sha1 = case @settings[:commit]
62
- when 'master' then master_commit.sha
63
- when 'last' then @g.object('HEAD^').sha
64
- when 'working' then 'working'
65
- else @settings[:commit]
66
- end
67
- end
68
-
69
- # If working directory, just have a single item array.
70
- # The space here is important because git-lines checks for a second arg,
71
- # and if one is present, it runs git diff without a commit
72
- # or a comparison to a commit.
73
- git_diff = @psuedo_commit ? ["git #{sha1}"] : `git rev-list #{sha1}..#{sha2} --no-merges`.split("\n")
74
-
75
- # Include the first sha because rev-list is doing a traversal
76
- # So sha1 is never included
77
- git_diff << sha1 unless @psuedo_commit
78
-
72
+ sha1 = set_psuedo_commit if @settings[:commit]
79
73
  # Reverse so that we go in chronological order
80
- git_diff.reverse.each do |git_sha|
81
- new_lines = lines_added(git_sha)
74
+ git_spread = commit_range(sha1, sha2).reverse
82
75
 
83
- # Grab all files in that commit and group them by extension
84
- # If working copy, just give the diff names of the files changed
85
- files = @psuedo_commit ? `git diff --name-only` : `git show --pretty="format:" --name-only #{git_sha}`
86
- files = files.split("\n").group_by { |f| f.split('.').pop }
76
+ git_spread.each do |git_sha|
87
77
 
88
- # Don't worry about files that we don't have a lint or a statistic for
89
- flat_associations = associations.clone.flatten(2)
90
- files.delete_if { |k,v| !flat_associations.include?(k) || k.nil? }
78
+ # Grab all files in that commit and group them by extension
79
+ # If working copy, just give the diff names of the files changed
80
+ files = @psuedo_commit ? `git -C #{@settings[:root_dir]} diff --name-only` : `git -C #{@settings[:root_dir]} show --pretty="format:" --name-only #{git_sha}`
91
81
 
92
- associations.each do |ext, related|
93
- files[ext] ||= []
94
- related.each do |child|
95
- unless files[child].blank?
96
- files[child].each do |c|
97
- # hack to ignore deleted files
98
- files[child] = new_lines[c].blank? ? [] : [ filename: "#{@settings[:root_dir]}/#{c}", changes: new_lines[c] ]
99
- end
100
- files[ext].concat(files[child])
101
- files.delete(child)
102
- end
103
- end
104
- end
105
- files.delete_if { |k,v| v.blank? }
106
- diff_return[git_sha.to_sym] = files
82
+ diff_return[git_sha.to_s] = match_associations(git_sha, files)
107
83
  end
108
84
  diff_return
109
85
  end
@@ -124,37 +100,48 @@ module Maximus
124
100
  # 'sha'...
125
101
  # }
126
102
  #
103
+ # @see compare
104
+ # @param lint_by_path [Boolean] only lint by files in git commit and
105
+ # not the commit as a whole
106
+ # @param git_shas [Hash] (#compare) a hash of gitcommit shas
107
+ # and relevant file types in the commit
108
+ # @param nuclear [Boolean] do everything regardless of what's in the commit
127
109
  # @return [Hash] data all data grouped by task
128
- def lints_and_stats(lint_by_path = false, git_shas = compare)
110
+ def lints_and_stats(lint_by_path = false, git_shas = compare, nuclear = false)
129
111
  return false if git_shas.blank?
130
112
  base_branch = branch
131
113
  git_output = {}
132
114
  git_shas.each do |sha, exts|
133
- # @todo better way to silence git, in case there's a real error?
134
- quietly { `git checkout #{sha} -b maximus_#{sha}` } unless @psuedo_commit
135
- puts sha.to_s.color(:blue) if @config.is_dev?
136
- git_output[sha.to_sym] = {
137
- lints: {},
138
- statistics: {}
139
- }
140
- lints = git_output[sha.to_sym][:lints]
141
- statistics = git_output[sha.to_sym][:statistics]
142
- lint_opts = {}
115
+ create_branch(sha) unless @psuedo_commit
116
+ sha = sha.to_s
117
+ puts sha.color(:blue)
118
+ git_output[sha] = {lints: {}, statistics: {}}
119
+ lints = git_output[sha][:lints]
120
+ statistics = git_output[sha][:statistics]
143
121
 
144
122
  # This is where everything goes down
145
123
  exts.each do |ext, files|
124
+
146
125
  # For relevant_lines data
147
126
  lint_opts = {
148
127
  git_files: files,
149
- config: @config
128
+ config: @config,
129
+ file_paths: (lint_file_paths(files, ext) if lint_by_path)
150
130
  }
151
- lint_opts[:file_paths] = lint_file_paths(files, ext) if lint_by_path
152
- case ext
153
- when :scss
154
- lints[:scsslint] = Maximus::Scsslint.new(lint_opts).result
155
131
 
156
- # Do not run statistics if called from command line
157
- if lint_opts[:commit].blank?
132
+ if nuclear
133
+ lints[:scsslint] = Maximus::Scsslint.new(lint_opts).result
134
+ lints[:jshint] = Maximus::Jshint.new(lint_opts).result
135
+ lints[:rubocop] = Maximus::Rubocop.new(lint_opts).result
136
+ lints[:railsbp] = Maximus::Railsbp.new(lint_opts).result
137
+ lints[:brakeman] = Maximus::Brakeman.new(lint_opts).result
138
+ statistics[:stylestat] = Maximus::Stylestats.new({config: @config}).result
139
+ statistics[:phantomas] = Maximus::Phantomas.new({config: @config}).result
140
+ statistics[:wraith] = Maximus::Wraith.new({config: @config}).result
141
+ else
142
+ case ext
143
+ when :scss
144
+ lints[:scsslint] = Maximus::Scsslint.new(lint_opts).result
158
145
 
159
146
  # @todo stylestat is singular here because model name in Rails is singular.
160
147
  # But adding a .classify when it's converted to a model chops off the end s on 'phantomas',
@@ -164,27 +151,105 @@ module Maximus
164
151
  # @todo double pipe here is best way to say, if it's already run, don't run again, right?
165
152
  statistics[:phantomas] ||= Maximus::Phantomas.new({config: @config}).result
166
153
  statistics[:wraith] ||= Maximus::Wraith.new({config: @config}).result
167
- end
168
- when :js
169
- lints[:jshint] = Maximus::Jshint.new(lint_opts).result
170
-
171
- # Do not run statistics if called from command line
172
- if lint_opts[:commit].blank?
154
+ when :js
155
+ lints[:jshint] = Maximus::Jshint.new(lint_opts).result
173
156
 
174
157
  statistics[:phantomas] ||= Maximus::Phantomas.new({config: @config}).result
175
158
 
176
159
  # @todo double pipe here is best way to say, if it's already run, don't run again, right?
177
160
  statistics[:wraith] ||= Maximus::Wraith.new({config: @config}).result
178
- end
179
- when :ruby
180
- lints[:rubocop] = Maximus::Rubocop.new(lint_opts).result
181
- lints[:railsbp] ||= Maximus::Railsbp.new(lint_opts).result
182
- lints[:brakeman] = Maximus::Brakeman.new(lint_opts).result
183
- when :rails
184
- lints[:railsbp] ||= Maximus::Railsbp.new(lint_opts).result
161
+ when :ruby
162
+ lints[:rubocop] = Maximus::Rubocop.new(lint_opts).result
163
+ lints[:railsbp] ||= Maximus::Railsbp.new(lint_opts).result
164
+ lints[:brakeman] = Maximus::Brakeman.new(lint_opts).result
165
+ when :rails
166
+ lints[:railsbp] ||= Maximus::Railsbp.new(lint_opts).result
167
+ end
185
168
  end
186
169
  end
187
- # @todo better way to silence git, in case there's a real error?
170
+ destroy_branch(base_branch, sha) unless @psuedo_commit
171
+ end
172
+ git_output
173
+ end
174
+
175
+ # Find first commit
176
+ # @since 0.1.5
177
+ # @return [String]
178
+ def first_commit
179
+ `git -C #{@settings[:root_dir]} rev-list --max-parents=0 HEAD`.strip!
180
+ end
181
+
182
+ # Get commit before current
183
+ # @since 0.1.5
184
+ # @param current_commit [String] (head_sha) commit to start at
185
+ # @param previous_by [Integer] (1) commit n commits ago
186
+ # @return [String]
187
+ def previous_commit(current_commit = head_sha, previous_by = 1)
188
+ `git -C #{@settings[:root_dir]} rev-list --max-count=#{previous_by + 1} #{current_commit} --reverse | head -n1`.strip!
189
+ end
190
+
191
+ # Define associations to linters based on file extension
192
+ # @return [Hash] linters and extension arrays
193
+ def associations
194
+ {
195
+ scss: ['scss', 'sass'],
196
+ js: ['js'],
197
+ ruby: ['rb', 'Gemfile', 'lock', 'yml', 'Rakefile', 'ru', 'rdoc', 'rake', 'Capfile'],
198
+ rails: ['slim', 'haml', 'jbuilder', 'erb']
199
+ }
200
+ end
201
+
202
+
203
+ protected
204
+
205
+ # Retrieve shas of all commits to be evaluated
206
+ # @since 0.1.5
207
+ #
208
+ # If working directory, just have a single item array.
209
+ # The space here is important because git-lines checks for a second arg,
210
+ # and if one is present, it runs git diff without a commit
211
+ # or a comparison to a commit.
212
+ #
213
+ # Include the first sha because rev-list is doing a traversal
214
+ # So sha1 is never included
215
+ #
216
+ # @param sha1 [String]
217
+ # @param sha2 [String]
218
+ # @return [Array] shas
219
+ def commit_range(sha1, sha2)
220
+ git_spread = @psuedo_commit ? "git #{sha1}" : `git -C #{@settings[:root_dir]} rev-list #{sha1}..#{sha2} --no-merges`
221
+ git_spread = git_spread.nil? ? [] : git_spread.split("\n")
222
+
223
+ git_spread << sha1 unless @psuedo_commit
224
+ git_spread
225
+ end
226
+
227
+ # Get sha if words passed for :commit config option
228
+ # @since 0.1.5
229
+ # @return [String] commit sha
230
+ def set_psuedo_commit
231
+ case @settings[:commit]
232
+ when 'master' then master_commit.sha
233
+ when 'last' then previous_commit(head_sha)
234
+ when 'working' then 'working'
235
+ else @settings[:commit]
236
+ end
237
+ end
238
+
239
+ # Create branch to run report on
240
+ # @todo better way to silence git, in case there's a real error?
241
+ # @since 0.1.5
242
+ # @param sha [String]
243
+ def create_branch(sha)
244
+ quietly { `git -C #{@settings[:root_dir]} checkout #{sha} -b maximus_#{sha}` }
245
+ end
246
+
247
+ # Destroy created branch
248
+ # @todo better way to silence git, in case there's a real error?
249
+ # @since 0.1.5
250
+ # @param base_branch [String] branch we started on
251
+ # @param sha [String] used to check against created branch name
252
+ def destroy_branch(base_branch, sha)
188
253
  quietly {
189
254
  if base_branch == "maximus_#{sha}"
190
255
  @g.branch('master').checkout
@@ -192,16 +257,10 @@ module Maximus
192
257
  @g.branch(base_branch).checkout
193
258
  end
194
259
  @g.branch("maximus_#{sha}").delete
195
- } unless @psuedo_commit
260
+ }
196
261
  end
197
- git_output
198
- end
199
-
200
-
201
- protected
202
262
 
203
263
  # Get list of file paths
204
- #
205
264
  # @param files [Hash] hash of files denoted by key 'filename'
206
265
  # @param ext [String] file extension - different extensions are joined different ways
207
266
  # @return [String] file paths delimited by comma or space
@@ -214,19 +273,17 @@ module Maximus
214
273
  # Determine which lines were added (where and how many) in a commit
215
274
  #
216
275
  # @example output from method
217
- # {
218
- # 'filename': [
276
+ # { 'filename': [
219
277
  # '0..10',
220
278
  # '11..14'
221
- # ]
222
- # }
279
+ # ] }
223
280
  #
224
281
  # @param git_sha [String] sha of the commit
225
282
  # @return [Hash] ranges by lines added in a commit by file name
226
283
  def lines_added(git_sha)
227
284
  new_lines = {}
228
- lines_added = `#{File.join(File.dirname(__FILE__), 'reporter/git-lines.sh')} #{git_sha}`.split("\n")
229
- lines_added.each do |filename|
285
+ git_lines = `#{File.join(File.dirname(__FILE__), 'reporter/git-lines.sh')} #{@settings[:root_dir]} #{git_sha}`.split("\n")
286
+ git_lines.each do |filename|
230
287
  fsplit = filename.split(':')
231
288
  # if file isn't already part of the array
232
289
  new_lines[fsplit[0]] ||= []
@@ -240,7 +297,7 @@ module Maximus
240
297
 
241
298
  # Get last commit on current branch
242
299
  # @return [String] sha
243
- def sha
300
+ def head_sha
244
301
  @g.object('HEAD').sha
245
302
  end
246
303
 
@@ -256,42 +313,90 @@ module Maximus
256
313
  @g.branches[:master].gcommit
257
314
  end
258
315
 
259
- # Store last commit as Ruby Git::Object
260
- # @param commit_sha [String]
261
- # @return [Git::Object]
262
- def vccommit(commit_sha = sha)
263
- @g.gcommit(commit_sha)
264
- end
265
-
266
316
  # Get general stats of commit on HEAD versus last commit on master branch
267
317
  # @modified 0.1.4
268
- # @param new_commit [Git::Object]
269
318
  # @param old_commit [Git::Object]
319
+ # @param new_commit [Git::Object]
270
320
  # @return [Git::Diff] hash of abbreviated, useful stats with added lines
271
- def diff(new_commit = vccommit, old_commit = master_commit)
272
- stats = @g.diff(new_commit, old_commit).stats
273
- return if lines.blank? || diff.blank?
321
+ def diff(old_commit, new_commit)
322
+ stats = @g.diff(old_commit, new_commit).stats
323
+ lines = lines_added(new_commit.sha)
324
+ return if !lines.is_a?(Hash) || stats.blank?
274
325
  lines.each do |filename, filelines|
275
326
  stats[:files][filename][:lines_added] = filelines if stats[:files].has_key?(filename)
276
327
  end
277
328
  stats
278
329
  end
279
330
 
331
+ # Get diff stats on just the initial commit
332
+ # Ruby-git doesn't support this well
333
+ # @see diff
334
+ # @since 0.1.5
335
+ # @param commit_sha [String]
336
+ # @return [Hash] stat data similar to Ruby-git's Diff.stats return
337
+ def diff_initial(commit_sha)
338
+ # Start after the commit information
339
+ data = `git -C #{@settings[:root_dir]} log --numstat --oneline #{commit_sha}`.split("\n")[1..-1]
340
+ value = {
341
+ total: {
342
+ insertions: 0,
343
+ deletions: 0,
344
+ lines: 0,
345
+ files: data.length
346
+ },
347
+ files: {}
348
+ }
349
+ data.each do |d|
350
+ item = d.split("\t")
351
+ insertions = item[0].to_i
352
+ value[:total][:insertions] += insertions
353
+ value[:total][:lines] += insertions
354
+ value[:files][item[2]] = {
355
+ insertions: insertions,
356
+ deletions: 0,
357
+ lines_added: ["0..#{item[0]}"]
358
+ }
359
+ end
360
+ value
361
+ end
362
+
280
363
  # Get remote URL
281
364
  # @return [String, nil] nil returns if remotes is blank
282
365
  def remote
283
366
  @g.remotes.first.url unless @g.remotes.blank?
284
367
  end
285
368
 
286
- # Define associations to linters based on file extension
287
- # @return [Hash] linters and extension arrays
288
- def associations
289
- {
290
- scss: ['scss', 'sass'],
291
- js: ['js'],
292
- ruby: ['rb', 'Gemfile', 'lock', 'yml', 'Rakefile', 'ru', 'rdoc'],
293
- rails: ['slim', 'haml']
294
- }
369
+ # Associate files by extension and match their changes
370
+ # @since 0.1.5
371
+ # @param git_sha [String]
372
+ # @param files [String] list of files from git return
373
+ # @return [Hash] files with matched extensions and changes
374
+ def match_associations(git_sha, files)
375
+ new_lines = lines_added(git_sha)
376
+
377
+ # File.extname is not used here in case dotfiles are encountered
378
+ files = files.split("\n").group_by { |f| f.split('.').pop }
379
+
380
+ # Don't worry about files that we don't have a lint or a statistic for
381
+ flat_associations = associations.clone.flatten(2)
382
+ files.delete_if { |k,v| !flat_associations.include?(k) || k.nil? }
383
+
384
+ associations.each do |ext, related|
385
+ files[ext] ||= []
386
+ related.each do |child|
387
+ unless files[child].blank?
388
+ files[child].each do |c|
389
+ # hack to ignore deleted files
390
+ files[child] = new_lines[c].blank? ? [] : [ filename: "#{@settings[:root_dir]}/#{c}", changes: new_lines[c] ]
391
+ end
392
+ files[ext].concat(files[child])
393
+ files.delete(child)
394
+ end
395
+ end
396
+ end
397
+
398
+ files.delete_if { |k,v| v.blank? }
399
+ files
295
400
  end
296
401
 
297
402
  end