codemonitor 0.3.5 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54b2ce0b7c2997591225e5e7487433854b3fff767e3272b3d02210de337253cf
4
- data.tar.gz: aa9a60e54480ec4602ec108f45b84323aa04aa46f3df622a4db2a303cb1e6ec3
3
+ metadata.gz: f85a1e1dbcae9d0bd26c461505a12449283dbf2029ee34ba59b36a2277e00321
4
+ data.tar.gz: 2cc548a6646d98cbeab3b72ce2dff06fd0d40175bf599fec2db5e12659aec7c4
5
5
  SHA512:
6
- metadata.gz: 47fb68d254802463f047d96ef409b111ee3f65c6e007a74122bb90151a4e4d457ed2b7b3c4e2bb6da0cc7da45284a933a43167714b39d7d6ebe291f68e8a1822
7
- data.tar.gz: 2a466e51f3cd2e5936600680068af4bdb0ee717e9fcbfcc657394d08640f2b00799e2f82a3f183093bd6e1e8c0ee286c2948b3f5f777b5eb8e2e45fd464d4fcf
6
+ metadata.gz: 7af9d278c2c2fcbba028ecb161cf7ee49b374ef701aee25bbb2ccfc3fe714a2fcf54d05c18a43653dee3e3d7263904668b38fa74569c8168c22a87be4bd6b4e2
7
+ data.tar.gz: f928807e202db2c1b60b3cec7e5df66abd44b04c8c4ddc165a201476c8e04a36ac3f3bd036abc5e3eb54aac66299bc99ac8827aae822d4587d00a1d22a6ff96d
data/Gemfile.lock CHANGED
@@ -1,25 +1,56 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- codemonitor (0.3.0)
4
+ codemonitor (0.5.0)
5
5
  dogapi (~> 1.45)
6
+ octokit (~> 4.0)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
11
+ addressable (2.8.0)
12
+ public_suffix (>= 2.0.2, < 5.0)
10
13
  ast (2.4.2)
11
14
  coderay (1.1.3)
12
15
  diff-lcs (1.3)
13
16
  dogapi (1.45.0)
14
17
  multi_json
18
+ faraday (1.10.0)
19
+ faraday-em_http (~> 1.0)
20
+ faraday-em_synchrony (~> 1.0)
21
+ faraday-excon (~> 1.1)
22
+ faraday-httpclient (~> 1.0)
23
+ faraday-multipart (~> 1.0)
24
+ faraday-net_http (~> 1.0)
25
+ faraday-net_http_persistent (~> 1.0)
26
+ faraday-patron (~> 1.0)
27
+ faraday-rack (~> 1.0)
28
+ faraday-retry (~> 1.0)
29
+ ruby2_keywords (>= 0.0.4)
30
+ faraday-em_http (1.0.0)
31
+ faraday-em_synchrony (1.0.0)
32
+ faraday-excon (1.1.0)
33
+ faraday-httpclient (1.0.1)
34
+ faraday-multipart (1.0.3)
35
+ multipart-post (>= 1.2, < 3)
36
+ faraday-net_http (1.0.1)
37
+ faraday-net_http_persistent (1.2.0)
38
+ faraday-patron (1.0.0)
39
+ faraday-rack (1.0.0)
40
+ faraday-retry (1.0.3)
15
41
  method_source (1.0.0)
16
42
  multi_json (1.15.0)
43
+ multipart-post (2.1.1)
44
+ octokit (4.22.0)
45
+ faraday (>= 0.9)
46
+ sawyer (~> 0.8.0, >= 0.5.3)
17
47
  parallel (1.20.1)
18
48
  parser (3.0.1.1)
19
49
  ast (~> 2.4.1)
20
50
  pry (0.13.1)
21
51
  coderay (~> 1.1)
22
52
  method_source (~> 1.0)
53
+ public_suffix (4.0.7)
23
54
  rainbow (3.0.0)
24
55
  rake (13.0.3)
25
56
  regexp_parser (2.1.1)
@@ -49,6 +80,10 @@ GEM
49
80
  rubocop-ast (1.5.0)
50
81
  parser (>= 3.0.1.1)
51
82
  ruby-progressbar (1.11.0)
83
+ ruby2_keywords (0.0.5)
84
+ sawyer (0.8.2)
85
+ addressable (>= 2.3.5)
86
+ faraday (> 0.8, < 2.0)
52
87
  unicode-display_width (1.7.0)
53
88
 
54
89
  PLATFORMS
@@ -63,4 +98,4 @@ DEPENDENCIES
63
98
  rubocop (~> 0.80)
64
99
 
65
100
  BUNDLED WITH
66
- 2.2.17
101
+ 2.3.12
data/README.md CHANGED
@@ -12,7 +12,7 @@ Collect multiple metrics from the a Git repository.
12
12
 
13
13
  **Requirements / Setup**:
14
14
 
15
- You need a `.git` folder present in the current folder:
15
+ You need a `.git` folder present in the current folder:
16
16
 
17
17
  **Options**:
18
18
 
@@ -103,6 +103,20 @@ You can generate the `sorbet.output.json` file with this example command:
103
103
  bundle exec srb tc --metrics-prefix 'codemetrics' --metrics-file sorbet.output.json
104
104
  ```
105
105
 
106
+ ## SCC
107
+
108
+ Collect multiple metrics from [SCC](https://github.com/boyter/scc) configured.
109
+
110
+ **Requirements / Setup**:
111
+
112
+ You need a `scc.output.json` file present in the current folder.
113
+
114
+ You can generate the `scc.output.json` file with this example command:
115
+
116
+ ```
117
+ scc -f json scc.output.json
118
+ ```
119
+
106
120
  # Providers
107
121
 
108
122
  ## Console
@@ -4,17 +4,15 @@ module Engines
4
4
  module Custom
5
5
  class Extractor
6
6
  def call(provider)
7
- return unless requirements?
8
-
9
7
  provider.emit(metrics)
10
8
  end
11
9
 
12
- private
13
-
14
10
  def requirements?
15
11
  custom_files.length.positive?
16
12
  end
17
13
 
14
+ private
15
+
18
16
  def custom_files
19
17
  Dir.glob('./.codemonitor/*.rb')
20
18
  end
@@ -8,8 +8,6 @@ module Engines
8
8
  ].freeze
9
9
 
10
10
  def call(provider)
11
- return unless requirements?
12
-
13
11
  metrics = METRICS.map do |metric|
14
12
  [metric, send(metric) || 0]
15
13
  end.to_h
@@ -17,12 +15,12 @@ module Engines
17
15
  provider.emit(metrics)
18
16
  end
19
17
 
20
- private
21
-
22
18
  def requirements?
23
19
  true
24
20
  end
25
21
 
22
+ private
23
+
26
24
  def debug_random
27
25
  rand(0..100)
28
26
  end
@@ -16,8 +16,6 @@ module Engines
16
16
  end
17
17
 
18
18
  def call(provider)
19
- return unless requirements?
20
-
21
19
  metrics = METRICS.map do |metric|
22
20
  [metric, send(metric)]
23
21
  end.to_h
@@ -29,16 +27,14 @@ module Engines
29
27
  provider.emit(metrics)
30
28
  end
31
29
 
30
+ def requirements?
31
+ File.exist?('eslint.output.json')
32
+ end
33
+
32
34
  private
33
35
 
34
36
  attr_reader :threshold
35
37
 
36
- def requirements?
37
- # FIXME: Review if this is the only check we can do or there are more.
38
- File.exist?('.eslintrc.js')
39
- end
40
-
41
- # NOTE: This output file must be created by an external command
42
38
  def eslint
43
39
  @eslint ||= JSON.parse(File.read('eslint.output.json'))
44
40
  end
@@ -20,8 +20,6 @@ module Engines
20
20
  end
21
21
 
22
22
  def call(provider)
23
- return unless requirements?
24
-
25
23
  metrics = METRICS.map do |metric|
26
24
  [metric, send(metric)]
27
25
  end.to_h
@@ -32,14 +30,14 @@ module Engines
32
30
  provider.emit(metrics)
33
31
  end
34
32
 
35
- private
36
-
37
- attr_reader :threshold
38
-
39
33
  def requirements?
40
34
  File.exist?('.git')
41
35
  end
42
36
 
37
+ private
38
+
39
+ attr_reader :threshold
40
+
43
41
  def git_number_of_commits
44
42
  Shell.run("git log --format='%h'").lines.count
45
43
  end
@@ -22,8 +22,6 @@ module Engines
22
22
  end
23
23
 
24
24
  def call(provider)
25
- return unless requirements?
26
-
27
25
  metrics = METRICS.map do |metric|
28
26
  [metric, send(metric)]
29
27
  end.to_h
@@ -31,6 +29,10 @@ module Engines
31
29
  provider.emit(metrics)
32
30
  end
33
31
 
32
+ def requirements?
33
+ !access_token.nil? && !repository.nil?
34
+ end
35
+
34
36
  private
35
37
 
36
38
  attr_reader :access_token, :repository, :since_days
@@ -39,10 +41,6 @@ module Engines
39
41
  @github ||= Octokit::Client.new(access_token: access_token)
40
42
  end
41
43
 
42
- def requirements?
43
- !access_token.nil? && !repository.nil?
44
- end
45
-
46
44
  def since
47
45
  (Date.today - since_days).to_time.iso8601
48
46
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Engines
6
+ module JestJsonSummary
7
+ class Extractor
8
+ METRICS = %i[].freeze
9
+
10
+ def initialize; end
11
+
12
+ def call(provider)
13
+ metrics = METRICS.map do |metric|
14
+ [metric, send(metric)]
15
+ end.to_h
16
+
17
+ metrics
18
+ .merge!(total_lines)
19
+ .merge!(total_statements)
20
+ .merge!(total_functions)
21
+ .merge!(total_branches)
22
+ .merge!(total_branches_true)
23
+
24
+ provider.emit(metrics)
25
+ end
26
+
27
+ def requirements?
28
+ File.exist?('jest_json_summary.output.json')
29
+ end
30
+
31
+ private
32
+
33
+ def json_summary
34
+ @json_summary ||= JSON.parse(File.read('jest_json_summary.output.json'))
35
+ end
36
+
37
+ def total_lines
38
+ flatten('lines')
39
+ end
40
+
41
+ def total_statements
42
+ flatten('statements')
43
+ end
44
+
45
+ def total_functions
46
+ flatten('functions')
47
+ end
48
+
49
+ def total_branches
50
+ flatten('branches')
51
+ end
52
+
53
+ def total_branches_true
54
+ flatten('branchesTrue', 'branches_true')
55
+ end
56
+
57
+ def flatten(member, rename = nil)
58
+ json_summary['total'][member].map do |key, value|
59
+ ["jest_json_summary_#{rename || member}_#{key}", value.to_i]
60
+ end.to_h
61
+ end
62
+ end
63
+ end
64
+ end
@@ -8,18 +8,24 @@ module Engines
8
8
  module Npm
9
9
  class Extractor
10
10
  METRICS = %i[
11
- npm_number_of_dependencies
11
+ npm_number_of_prod_dependencies
12
12
  npm_number_of_dev_dependencies
13
13
  npm_number_of_scripts
14
- npm_number_of_vulnerable_dependencies
14
+ npm_number_of_computed_prod_dependencies
15
+ npm_number_of_computed_dev_dependencies
16
+ npm_number_of_computed_optional_dependencies
17
+ npm_number_of_computed_peer_dependencies
18
+ npm_number_of_computed_peer_optional_dependencies
19
+ npm_number_of_computed_total_dependencies
20
+ npm_number_of_vulnerable_dependencies_info
15
21
  npm_number_of_vulnerable_dependencies_low
16
22
  npm_number_of_vulnerable_dependencies_moderate
17
23
  npm_number_of_vulnerable_dependencies_high
24
+ npm_number_of_vulnerable_dependencies_critical
25
+ npm_number_of_vulnerable_dependencies_total
18
26
  ].freeze
19
27
 
20
28
  def call(provider)
21
- return unless requirements?
22
-
23
29
  metrics = METRICS.map do |metric|
24
30
  [metric, send(metric) || 0]
25
31
  end.to_h
@@ -27,26 +33,26 @@ module Engines
27
33
  provider.emit(metrics)
28
34
  end
29
35
 
30
- private
31
-
32
36
  def requirements?
33
- File.exist?('package.json')
37
+ File.exist?('package.json') && File.exist?('package-lock.json')
34
38
  end
35
39
 
36
- def npm_number_of_dependencies
37
- npm_package['dependencies'].keys.length
40
+ private
41
+
42
+ def npm_number_of_prod_dependencies
43
+ npm_package['dependencies']&.keys&.length
38
44
  end
39
45
 
40
46
  def npm_number_of_dev_dependencies
41
- npm_package['devDependencies'].keys.length
47
+ npm_package['devDependencies']&.keys&.length
42
48
  end
43
49
 
44
50
  def npm_number_of_scripts
45
51
  npm_package['scripts'].keys.length
46
52
  end
47
53
 
48
- def npm_number_of_vulnerable_dependencies
49
- npm_audit['advisories'].length
54
+ def npm_number_of_vulnerable_dependencies_info
55
+ npm_audit_by_severity['info']
50
56
  end
51
57
 
52
58
  def npm_number_of_vulnerable_dependencies_low
@@ -61,6 +67,38 @@ module Engines
61
67
  npm_audit_by_severity['high']
62
68
  end
63
69
 
70
+ def npm_number_of_vulnerable_dependencies_critical
71
+ npm_audit_by_severity['critical']
72
+ end
73
+
74
+ def npm_number_of_vulnerable_dependencies_total
75
+ npm_audit_by_severity['total']
76
+ end
77
+
78
+ def npm_number_of_computed_prod_dependencies
79
+ npm_audit_by_dependencies['prod']
80
+ end
81
+
82
+ def npm_number_of_computed_dev_dependencies
83
+ npm_audit_by_dependencies['dev']
84
+ end
85
+
86
+ def npm_number_of_computed_optional_dependencies
87
+ npm_audit_by_dependencies['optional']
88
+ end
89
+
90
+ def npm_number_of_computed_peer_dependencies
91
+ npm_audit_by_dependencies['peer']
92
+ end
93
+
94
+ def npm_number_of_computed_peer_optional_dependencies
95
+ npm_audit_by_dependencies['peerOptional']
96
+ end
97
+
98
+ def npm_number_of_computed_total_dependencies
99
+ npm_audit_by_dependencies['total']
100
+ end
101
+
64
102
  def npm_package
65
103
  @npm_package ||= JSON.parse(File.read('package.json'))
66
104
  end
@@ -69,10 +107,12 @@ module Engines
69
107
  @npm_audit ||= JSON.parse(Shell.run('npm audit --json'))
70
108
  end
71
109
 
110
+ def npm_audit_by_dependencies
111
+ npm_audit['metadata']['dependencies']
112
+ end
113
+
72
114
  def npm_audit_by_severity
73
- npm_audit['advisories']
74
- .map { |_key, value| value['severity'] }
75
- .each_with_object(Hash.new(0)) { |e, total| total[e] += 1; }
115
+ npm_audit['metadata']['vulnerabilities']
76
116
  end
77
117
  end
78
118
  end
@@ -13,8 +13,6 @@ module Engines
13
13
  def initialize; end
14
14
 
15
15
  def call(provider)
16
- return unless requirements?
17
-
18
16
  metrics = METRICS.map do |metric|
19
17
  [metric, send(metric)]
20
18
  end.to_h
@@ -22,12 +20,12 @@ module Engines
22
20
  provider.emit(metrics)
23
21
  end
24
22
 
25
- private
26
-
27
23
  def requirements?
28
24
  packwerk_files.length.positive?
29
25
  end
30
26
 
27
+ private
28
+
31
29
  # NOTE: This output file must be created by an external command
32
30
  def packwerk_files
33
31
  Dir.glob('./**/deprecated_references.yml')
@@ -15,8 +15,6 @@ module Engines
15
15
  end
16
16
 
17
17
  def call(provider)
18
- return unless requirements?
19
-
20
18
  metrics = METRICS.map do |metric|
21
19
  [metric, send(metric)]
22
20
  end.to_h
@@ -28,15 +26,14 @@ module Engines
28
26
  provider.emit(metrics)
29
27
  end
30
28
 
29
+ def requirements?
30
+ File.exist?('rubocop.output.json')
31
+ end
32
+
31
33
  private
32
34
 
33
35
  attr_reader :threshold
34
36
 
35
- def requirements?
36
- File.exist?('.rubocop.yml')
37
- end
38
-
39
- # NOTE: This output file must be created by an external command
40
37
  def rubocop
41
38
  @rubocop ||= JSON.parse(File.read('rubocop.output.json'))
42
39
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Engines
6
+ module Scc
7
+ class Extractor
8
+ METRICS = %i[].freeze
9
+ FIELDS = %w[Bytes Lines Code Comment Blank Complexity Count WeightedComplexity].freeze
10
+
11
+ def initialize; end
12
+
13
+ def call(provider)
14
+ metrics = METRICS.map do |metric|
15
+ [metric, send(metric)]
16
+ end.to_h
17
+
18
+ metrics
19
+ .merge!(scc_totals)
20
+ .merge!(scc_by_file_type)
21
+
22
+ provider.emit(metrics)
23
+ end
24
+
25
+ def requirements?
26
+ File.exist?('scc.output.json')
27
+ end
28
+
29
+ private
30
+
31
+ def scc
32
+ @scc ||= JSON.parse(File.read('scc.output.json'))
33
+ end
34
+
35
+ def scc_totals
36
+ scc.each_with_object({}) do |type, totals|
37
+ FIELDS.each do |field|
38
+ key = "scc_total_#{clean(field)}"
39
+ totals[key] = 0 unless totals.key?(key)
40
+
41
+ totals[key] += type[field]
42
+ end
43
+ end
44
+ end
45
+
46
+ def scc_by_file_type
47
+ scc.map do |type|
48
+ FIELDS.map do |field|
49
+ ["scc_type_#{clean(type['Name'])}_#{clean(field)}", type[field]]
50
+ end.to_h
51
+ end.inject(&:merge)
52
+ end
53
+
54
+ def clean(key)
55
+ key.gsub(%r{[-,./ ]}, '_').downcase
56
+ end
57
+ end
58
+ end
59
+ end
@@ -15,8 +15,6 @@ module Engines
15
15
  end
16
16
 
17
17
  def call(provider)
18
- return unless requirements?
19
-
20
18
  metrics = METRICS.map do |metric|
21
19
  [metric, send(metric)]
22
20
  end.to_h
@@ -26,15 +24,14 @@ module Engines
26
24
  provider.emit(metrics)
27
25
  end
28
26
 
27
+ def requirements?
28
+ File.exist?('semgrep.output.json')
29
+ end
30
+
29
31
  private
30
32
 
31
33
  attr_reader :threshold
32
34
 
33
- def requirements?
34
- File.exist?('.semgrep.yml')
35
- end
36
-
37
- # NOTE: This output file must be created by an external command
38
35
  def semgrep
39
36
  @semgrep ||= JSON.parse(File.read('semgrep.output.json'))
40
37
  end
@@ -24,8 +24,6 @@ module Engines
24
24
  def initialize; end
25
25
 
26
26
  def call(provider)
27
- return unless requirements?
28
-
29
27
  metrics = METRICS.map do |metric|
30
28
  [metric, send(metric)]
31
29
  end.to_h
@@ -33,13 +31,12 @@ module Engines
33
31
  provider.emit(metrics)
34
32
  end
35
33
 
36
- private
37
-
38
34
  def requirements?
39
35
  File.exist?('sorbet.output.json')
40
36
  end
41
37
 
42
- # NOTE: This output file must be created by an external command
38
+ private
39
+
43
40
  def sorbet
44
41
  @sorbet ||= JSON.parse(File.read('sorbet.output.json'))
45
42
  end
data/exe/codemonitor CHANGED
@@ -13,7 +13,9 @@ require_relative '../engines/packwerk/extractor'
13
13
  require_relative '../engines/rubocop/extractor'
14
14
  require_relative '../engines/semgrep/extractor'
15
15
  require_relative '../engines/sorbet/extractor'
16
+ require_relative '../engines/scc/extractor'
16
17
  require_relative '../engines/custom/extractor'
18
+ require_relative '../engines/jest-json-summary/extractor'
17
19
 
18
20
  PROVIDERS = {
19
21
  console: Providers::Console,
@@ -30,7 +32,9 @@ EXTRACTORS = {
30
32
  rubocop: Engines::Rubocop::Extractor,
31
33
  semgrep: Engines::Semgrep::Extractor,
32
34
  sorbet: Engines::Sorbet::Extractor,
33
- custom: Engines::Custom::Extractor
35
+ scc: Engines::Scc::Extractor,
36
+ custom: Engines::Custom::Extractor,
37
+ jest_json_summary: Engines::JestJsonSummary::Extractor
34
38
  }.freeze
35
39
 
36
40
  config_provider = ENV['CODEMONITOR_PROVIDER'] || 'console'
@@ -49,6 +53,8 @@ puts '# process start'
49
53
  extractors
50
54
  .map(&:new)
51
55
  .map do |extractor|
56
+ raise "Requirements not fullfiled in #{extractor.class.name}" unless extractor.requirements?
57
+
52
58
  extractor.call(provider)
53
59
  end
54
60
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CodeMonitor
4
- VERSION = '0.3.5'
4
+ VERSION = '0.5.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: codemonitor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ferran Basora
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-12 00:00:00.000000000 Z
11
+ date: 2022-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dogapi
@@ -134,9 +134,11 @@ files:
134
134
  - engines/eslint/extractor.rb
135
135
  - engines/git/extractor.rb
136
136
  - engines/github/extractor.rb
137
+ - engines/jest-json-summary/extractor.rb
137
138
  - engines/npm/extractor.rb
138
139
  - engines/packwerk/extractor.rb
139
140
  - engines/rubocop/extractor.rb
141
+ - engines/scc/extractor.rb
140
142
  - engines/semgrep/extractor.rb
141
143
  - engines/sorbet/extractor.rb
142
144
  - exe/codemonitor
@@ -166,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
166
168
  - !ruby/object:Gem::Version
167
169
  version: '0'
168
170
  requirements: []
169
- rubygems_version: 3.0.1
171
+ rubygems_version: 3.1.2
170
172
  signing_key:
171
173
  specification_version: 4
172
174
  summary: Collect many metrics your code is generating