codemonitor 0.1.0 → 0.3.3

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: ce80c6de6c3579fdb8bc4d52f63905ec45124f6e4a9891ac0ddae5c90608d3f2
4
- data.tar.gz: da5354cb8704715dc4d005b2615f034a85b6c43ff0a16ef3a4ddcf45561f139e
3
+ metadata.gz: b2b693d8b21e5c6041765bf54d568d21b7e8e54abeb1695d7937166a6bf9b2b7
4
+ data.tar.gz: 3af8eb6ab76db485ddc34a793c04793238b0d92c4523440028dd2809d58c1f50
5
5
  SHA512:
6
- metadata.gz: 9b3ceadf4db389641f91d732038e6fc7affbd604815d3b5e50e16743b601a6471c7bab85445f20669c2ecd364e7f33984d931b941130b2b3dd31eaebf8efdfe8
7
- data.tar.gz: e17eb93b4b386a587ac9265c63508942055a692ab2c53cd9865c20f31a1c4750c18e42bbb9ca8de70a387ceb4fcc25f7330f6c7d69695ed8364005c04652071c
6
+ metadata.gz: 2c0a23989bded3388a21ac6c40c87ed4ed6d18a3f62d9ff804a13e595fb2152eb17dd8955224812ebfac2a71d37a569b1cadf1bf9f46195d87f7576351a6b39d
7
+ data.tar.gz: 8ae23e524620fd956835b5bf85699153a579796701c6e605ab7ad1f670f30622b50ee3d1528b2a79f7ef7b3d698e0aae5d8b463a59b96c69b663ba4e77896d22
data/.rubocop.yml CHANGED
@@ -1,3 +1,6 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
1
4
  Lint/RaiseException:
2
5
  Enabled: false
3
6
 
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in rubocop-changes.gemspec
4
6
  gemspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- codemonitor (0.1.0)
4
+ codemonitor (0.3.0)
5
5
  dogapi (~> 1.45)
6
6
 
7
7
  GEM
data/bin/console CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "rubocop/changes"
4
+ require 'bundler/setup'
5
+ require 'rubocop/changes'
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +11,5 @@ require "rubocop/changes"
10
11
  # require "pry"
11
12
  # Pry.start
12
13
 
13
- require "irb"
14
+ require 'irb'
14
15
  IRB.start(__FILE__)
data/codemonitor.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  lib = File.expand_path('lib', __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
  require 'codemonitor/version'
@@ -26,7 +28,7 @@ Gem::Specification.new do |spec|
26
28
  # Specify which files should be added to the gem when it is released.
27
29
  # The `git ls-files -z` loads the files in the RubyGem that have been added
28
30
  # into git.
29
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
31
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
30
32
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
31
33
  end
32
34
  spec.bindir = 'exe'
@@ -37,8 +39,8 @@ Gem::Specification.new do |spec|
37
39
  spec.add_runtime_dependency 'dogapi', '~> 1.45'
38
40
 
39
41
  spec.add_development_dependency 'bundler', '~> 2.0'
42
+ spec.add_development_dependency 'pry', '~> 0.13.1'
40
43
  spec.add_development_dependency 'rake', '~> 13.0'
41
44
  spec.add_development_dependency 'rspec', '~> 3.9'
42
- spec.add_development_dependency 'pry', '~> 0.13.1'
43
45
  spec.add_development_dependency 'rubocop', '~> 0.80'
44
46
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Engines
4
+ module Debug
5
+ class Extractor
6
+ METRICS = %i[
7
+ debug_random
8
+ ].freeze
9
+
10
+ def call(provider)
11
+ return unless requirements?
12
+
13
+ metrics = METRICS.map do |metric|
14
+ [metric, send(metric) || 0]
15
+ end.to_h
16
+
17
+ provider.emit(metrics)
18
+ end
19
+
20
+ private
21
+
22
+ def requirements?
23
+ true
24
+ end
25
+
26
+ def debug_random
27
+ rand(0..100)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Engines
6
+ module Eslint
7
+ class Extractor
8
+ METRICS = %i[
9
+ eslint_number_of_offended_files
10
+ eslint_number_of_offenses
11
+ eslint_number_of_correctable
12
+ ].freeze
13
+
14
+ def initialize
15
+ @threshold = ENV.fetch('CODEMONITOR_ESLINT_THRESHOLD', '10').to_i
16
+ end
17
+
18
+ def call(provider)
19
+ return unless requirements?
20
+
21
+ metrics = METRICS.map do |metric|
22
+ [metric, send(metric)]
23
+ end.to_h
24
+
25
+ metrics
26
+ .merge!(eslint_by_severity)
27
+ .merge!(eslint_by_rule)
28
+
29
+ provider.emit(metrics)
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :threshold
35
+
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
+ def eslint
43
+ @eslint ||= JSON.parse(File.read('eslint.output.json'))
44
+ end
45
+
46
+ def eslint_files
47
+ eslint
48
+ end
49
+
50
+ def eslint_number_of_offended_files
51
+ eslint_files.length
52
+ end
53
+
54
+ def eslint_offenses
55
+ eslint_files
56
+ .map { |offense| offense['messages'] }
57
+ .flatten
58
+ end
59
+
60
+ def eslint_number_of_offenses
61
+ eslint_offenses.length
62
+ end
63
+
64
+ def eslint_number_of_correctable
65
+ eslint_offenses
66
+ .filter { |offense| offense['correctable'] }
67
+ .length
68
+ end
69
+
70
+ def eslint_by_severity
71
+ eslint_offenses
72
+ .each_with_object(Hash.new(0)) do |offense, total|
73
+ total[severity(offense['severity'])] += 1
74
+ end.map do |key, value|
75
+ ["eslint_severity_#{key}", value]
76
+ end.to_h
77
+ end
78
+
79
+ def eslint_by_rule
80
+ eslint_offenses
81
+ .each_with_object(Hash.new(0)) do |offense, total|
82
+ total[offense['ruleId']] += 1
83
+ end.map do |key, value|
84
+ ["eslint_rule_#{clean(key)}", value] if value >= threshold
85
+ end.compact.to_h
86
+ end
87
+
88
+ def clean(key)
89
+ key.gsub(%r{[-,./ ]}, '_').downcase
90
+ end
91
+
92
+ def severity(value)
93
+ return 'warning' if value == 1
94
+ return 'error' if value == 2
95
+
96
+ 'unknown'
97
+ end
98
+ end
99
+ end
100
+ end
@@ -15,6 +15,10 @@ module Engines
15
15
  git_number_of_lines
16
16
  ].freeze
17
17
 
18
+ def initialize
19
+ @threshold = ENV.fetch('CODEMONITOR_GIT_FILES_THRESHOLD', '0').to_i
20
+ end
21
+
18
22
  def call(provider)
19
23
  return unless requirements?
20
24
 
@@ -22,11 +26,16 @@ module Engines
22
26
  [metric, send(metric)]
23
27
  end.to_h
24
28
 
29
+ metrics
30
+ .merge!(git_file_extensions)
31
+
25
32
  provider.emit(metrics)
26
33
  end
27
34
 
28
35
  private
29
36
 
37
+ attr_reader :threshold
38
+
30
39
  def requirements?
31
40
  File.exist?('.git')
32
41
  end
@@ -58,7 +67,7 @@ module Engines
58
67
  end
59
68
 
60
69
  def git_number_of_files
61
- Shell.run('git ls-tree -r master --name-only')
70
+ Shell.run('git ls-tree -r HEAD --name-only')
62
71
  .lines
63
72
  .count
64
73
  end
@@ -74,6 +83,18 @@ module Engines
74
83
  File.read(File.expand_path(file.strip, Dir.pwd)).lines.count
75
84
  end.sum
76
85
  end
86
+
87
+ def git_file_extensions
88
+ Shell.run('git ls-files')
89
+ .lines
90
+ .map do |file|
91
+ File.extname(file.strip)
92
+ end.each_with_object(Hash.new(0)) do |type, total|
93
+ total[type.gsub('.', '')] += 1
94
+ end.map do |key, value|
95
+ ["git_file_extensions_#{key}", value] if value >= threshold
96
+ end.compact.to_h
97
+ end
77
98
  end
78
99
  end
79
100
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Engines
6
+ module Packwerk
7
+ class Extractor
8
+ METRICS = %i[
9
+ packwerk_number_of_dependency_violations
10
+ packwerk_number_of_privacy_violations
11
+ ].freeze
12
+
13
+ def initialize; end
14
+
15
+ def call(provider)
16
+ return unless requirements?
17
+
18
+ metrics = METRICS.map do |metric|
19
+ [metric, send(metric)]
20
+ end.to_h
21
+
22
+ provider.emit(metrics)
23
+ end
24
+
25
+ private
26
+
27
+ def requirements?
28
+ packwerk_files.length.positive?
29
+ end
30
+
31
+ # NOTE: This output file must be created by an external command
32
+ def packwerk_files
33
+ Dir.glob('./**/deprecated_references.yml')
34
+ end
35
+
36
+ def packwerk_number_of_dependency_violations
37
+ packwerk_violations['dependency']
38
+ end
39
+
40
+ def packwerk_number_of_privacy_violations
41
+ packwerk_violations['privacy']
42
+ end
43
+
44
+ def packwerk_items
45
+ @packwerk_items ||= packwerk_files
46
+ .map { |file| YAML.load_file(file) }
47
+ .reduce({}, :merge)
48
+ .map { |_key, values| values }
49
+ .flatten
50
+ .reduce({}, :merge)
51
+ end
52
+
53
+ def packwerk_violations
54
+ packwerk_items
55
+ .values
56
+ .each_with_object(Hash.new(0)) do |offense, total|
57
+ offense['violations'].each do |violation|
58
+ total[violation] += 1
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Engines
6
+ module Sorbet
7
+ class Extractor
8
+ METRICS = %i[
9
+ sorbet_number_of_sig_count
10
+ sorbet_number_of_input_classes_total
11
+ sorbet_number_of_input_sends_total
12
+ sorbet_number_of_input_files
13
+ sorbet_number_of_input_methods_total
14
+ sorbet_number_of_input_modules_total
15
+
16
+ sorbet_number_of_sigil_true
17
+ sorbet_number_of_sigil_false
18
+ sorbet_number_of_sigil_autogenerated
19
+ sorbet_number_of_sigil_strong
20
+ sorbet_number_of_sigil_strict
21
+ sorbet_number_of_sigil_ignore
22
+ ].freeze
23
+
24
+ def initialize; end
25
+
26
+ def call(provider)
27
+ return unless requirements?
28
+
29
+ metrics = METRICS.map do |metric|
30
+ [metric, send(metric)]
31
+ end.to_h
32
+
33
+ provider.emit(metrics)
34
+ end
35
+
36
+ private
37
+
38
+ def requirements?
39
+ File.exist?('sorbet.output.json')
40
+ end
41
+
42
+ # NOTE: This output file must be created by an external command
43
+ def sorbet
44
+ @sorbet ||= JSON.parse(File.read('sorbet.output.json'))
45
+ end
46
+
47
+ def sorbet_metrics
48
+ sorbet['metrics']
49
+ end
50
+
51
+ def find_metric(name, default = 0)
52
+ match = sorbet_metrics.find do |metric|
53
+ metric['name'] == name
54
+ end
55
+
56
+ return default if match.nil?
57
+
58
+ match['value']
59
+ end
60
+
61
+ def sorbet_number_of_sig_count
62
+ find_metric('codemetrics.types.sig.count')
63
+ end
64
+
65
+ def sorbet_number_of_input_classes_total
66
+ find_metric('codemetrics.types.input.classes.total')
67
+ end
68
+
69
+ def sorbet_number_of_input_sends_total
70
+ find_metric('codemetrics.types.input.sends.total')
71
+ end
72
+
73
+ def sorbet_number_of_input_files
74
+ find_metric('codemetrics.types.input.files')
75
+ end
76
+
77
+ def sorbet_number_of_input_methods_total
78
+ find_metric('codemetrics.types.input.methods.total')
79
+ end
80
+
81
+ def sorbet_number_of_input_modules_total
82
+ find_metric('codemetrics.types.input.modules.total')
83
+ end
84
+
85
+ def sorbet_number_of_sigil_true
86
+ find_metric('codemetrics.types.input.files.sigil.true')
87
+ end
88
+
89
+ def sorbet_number_of_sigil_false
90
+ find_metric('codemetrics.types.input.files.sigil.false')
91
+ end
92
+
93
+ def sorbet_number_of_sigil_autogenerated
94
+ find_metric('codemetrics.types.input.files.sigil.autogenerated')
95
+ end
96
+
97
+ def sorbet_number_of_sigil_strong
98
+ find_metric('codemetrics.types.input.files.sigil.strong')
99
+ end
100
+
101
+ def sorbet_number_of_sigil_strict
102
+ find_metric('codemetrics.types.input.files.sigil.strict')
103
+ end
104
+
105
+ def sorbet_number_of_sigil_ignore
106
+ find_metric('codemetrics.types.input.files.sigil.ignore')
107
+ end
108
+ end
109
+ end
110
+ end
data/exe/codemonitor CHANGED
@@ -4,10 +4,14 @@
4
4
  require_relative '../providers/console'
5
5
  require_relative '../providers/datadog'
6
6
 
7
+ require_relative '../engines/eslint/extractor'
8
+ require_relative '../engines/debug/extractor'
7
9
  require_relative '../engines/git/extractor'
8
10
  require_relative '../engines/npm/extractor'
9
- require_relative '../engines/semgrep/extractor'
11
+ require_relative '../engines/packwerk/extractor'
10
12
  require_relative '../engines/rubocop/extractor'
13
+ require_relative '../engines/semgrep/extractor'
14
+ require_relative '../engines/sorbet/extractor'
11
15
 
12
16
  PROVIDERS = {
13
17
  console: Providers::Console,
@@ -15,10 +19,14 @@ PROVIDERS = {
15
19
  }.freeze
16
20
 
17
21
  EXTRACTORS = {
22
+ eslint: Engines::Eslint::Extractor,
23
+ debug: Engines::Debug::Extractor,
18
24
  git: Engines::Git::Extractor,
19
25
  npm: Engines::Npm::Extractor,
26
+ packwerk: Engines::Packwerk::Extractor,
27
+ rubocop: Engines::Rubocop::Extractor,
20
28
  semgrep: Engines::Semgrep::Extractor,
21
- rubocop: Engines::Rubocop::Extractor
29
+ sorbet: Engines::Sorbet::Extractor
22
30
  }.freeze
23
31
 
24
32
  config_provider = ENV['CODEMONITOR_PROVIDER'] || 'console'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CodeMonitor
4
- VERSION = '0.1.0'
4
+ VERSION = '0.3.3'
5
5
  end
data/providers/datadog.rb CHANGED
@@ -17,7 +17,7 @@ module Providers
17
17
  def send
18
18
  datadog_client.batch_metrics do
19
19
  pending.each do |metric, value|
20
- metric = metric_prefix + metric
20
+ metric = "#{metric_prefix}#{metric}"
21
21
  puts "#{metric}: #{value}"
22
22
  datadog_client.emit_point(metric, value, type: 'gauge')
23
23
  end
@@ -26,6 +26,6 @@ module Providers
26
26
 
27
27
  private
28
28
 
29
- attr_reader :pending, :datadog_client
29
+ attr_reader :pending, :metric_prefix, :datadog_client
30
30
  end
31
31
  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.1.0
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ferran Basora
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-11 00:00:00.000000000 Z
11
+ date: 2021-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dogapi
@@ -39,47 +39,47 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '2.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake
42
+ name: pry
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '13.0'
47
+ version: 0.13.1
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '13.0'
54
+ version: 0.13.1
55
55
  - !ruby/object:Gem::Dependency
56
- name: rspec
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.9'
61
+ version: '13.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '3.9'
68
+ version: '13.0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: pry
70
+ name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.13.1
75
+ version: '3.9'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.13.1
82
+ version: '3.9'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rubocop
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -115,10 +115,14 @@ files:
115
115
  - bin/console
116
116
  - bin/setup
117
117
  - codemonitor.gemspec
118
+ - engines/debug/extractor.rb
119
+ - engines/eslint/extractor.rb
118
120
  - engines/git/extractor.rb
119
121
  - engines/npm/extractor.rb
122
+ - engines/packwerk/extractor.rb
120
123
  - engines/rubocop/extractor.rb
121
124
  - engines/semgrep/extractor.rb
125
+ - engines/sorbet/extractor.rb
122
126
  - exe/codemonitor
123
127
  - lib/codemonitor/version.rb
124
128
  - lib/shell.rb