codemonitor 0.3.5 → 0.5.0

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 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