codemonitor 0.3.2 → 0.3.6

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: 2b9e0298cc93e22a2eb9b0de7b44ceafc1a0b34edaaac684d590c01d0b5dfc88
4
- data.tar.gz: b3fbe2ebc9efe2de493f79492f0f26a4c20b21b744cf8cdc71be987d95cf8cce
3
+ metadata.gz: 32f7be0ad2a196ea1dd1e420c56b9a2b1c87c2b7e0381f6e968fd1df706f86c0
4
+ data.tar.gz: 5a88b2dec7531acc15f99085ee4f456ee18a0e535cec5f242feaabc718974c96
5
5
  SHA512:
6
- metadata.gz: 6f86d0f5080a730085b40782c71127400914054430a375c602030970303be00e50b9df4d85da182a5a2c67e6d733866727e37fa59ab9213cceff96196ea07552
7
- data.tar.gz: 4ce76891b6e0ae204fb737b1ccecee4b7c19b6e2f698b0b92853b33bda98978c374c5924290797a3f19bb550c53dd2f5ffc5ddfe741eedd800148aff8c8cfa56
6
+ metadata.gz: d34229aa5e21856c951a9b707f5269cdffbe4c4c1839b741fdddadf672829e3c94a83a08075f3abeff2e414f33eaedbebdbb42a36ab3abfecd5d7165abc54657
7
+ data.tar.gz: d6a0d476275b4738887fa4acf83948fac95700142fb3fbe0092a54dd778ee5da4182d7b8cba4f781678d4ae85d8fe34fa106dbe7c4db598f8b4baf9c3595f102
data/Gemfile CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- # Specify your gem's dependencies in rubocop-changes.gemspec
5
+ # Specify your gem's dependencies in codemonitor.gemspec
6
6
  gemspec
data/README.md CHANGED
@@ -1,9 +1,134 @@
1
- # CodeMonitor
1
+ # 🖥️ CodeMonitor
2
2
 
3
3
  A engine to collect multiple metrics from your repository and push them to a
4
4
  time series provider.
5
5
 
6
6
 
7
- # Inspiration
7
+ # Engines
8
8
 
9
- https://github.com/scribd/github-action-datadog-reporting
9
+ ## Git
10
+
11
+ Collect multiple metrics from the a Git repository.
12
+
13
+ **Requirements / Setup**:
14
+
15
+ You need a `.git` folder present in the current folder:
16
+
17
+ **Options**:
18
+
19
+ `CODEMONITOR_GIT_FILES_THRESHOLD`: Don't emit metrics about number of files, from those that are above of this threshold. (Default: `0`)
20
+
21
+ ## Eslint
22
+
23
+ Collect multiple metrics from the a project with [Eslint](https://eslint.org/) configured.
24
+
25
+ **Requirements / Setup**:
26
+
27
+ You need a `.eslintrc.js` and a `eslint.output.json` file present in the current folder.
28
+
29
+ You can generate the `eslint.output.json` file with this example command:
30
+
31
+ ```
32
+ eslint -f json -o eslint.output.json
33
+ ```
34
+ **Options**:
35
+
36
+ `CODEMONITOR_ESLINT_THRESHOLD`: Don't emit metrics about eslint rules that are above of this threshold. (Default: `10`)
37
+
38
+
39
+ ## Npm
40
+
41
+ Collect multiple metrics from the a NodeJS.
42
+
43
+ **Requirements / Setup**:
44
+
45
+ You need a `package.json` file present in the current folder.
46
+
47
+ ## Packwerk
48
+
49
+ Collect multiple metrics from the a Ruby project with [Packwerk](https://github.com/Shopify/packwerk) configured.
50
+
51
+ **Requirements / Setup**:
52
+
53
+ You need a `deprecated_references.yml` files present in the current project.
54
+
55
+ ## Rubocop
56
+
57
+ Collect multiple metrics from the a Ruby project with [Rubocop](https://github.com/rubocop/rubocop) configured.
58
+
59
+ **Requirements / Setup**:
60
+
61
+ You need a `.rubocop.yml` and a `rubocop.output.json` file present in the current folder.
62
+
63
+ You can generate the `rubocop.output.json` file with this example command:
64
+
65
+ ```
66
+ bundle exec srb tc --metrics-prefix 'codemetrics' --metrics-file sorbet.output.json
67
+ ```
68
+
69
+ **Options**:
70
+
71
+ `CODEMONITOR_RUBOCOP_THRESHOLD`: Don't emit metrics about rubocop cops that are above of this threshold. (Default: `50`)
72
+
73
+
74
+ ## Semgrep
75
+
76
+ Collect multiple metrics from the a with [Semgrep](https://semgrep.dev/) configured.
77
+
78
+ **Requirements / Setup**:
79
+
80
+ You need a `.semgrep.yml` and a `semgrep.output.json` file present in the current folder.
81
+
82
+ You can generate the `semgrep.output.json` file with this example command:
83
+
84
+ ```
85
+ semgrep --json -o semgrep.output.json
86
+ ```
87
+
88
+ **Options**:
89
+
90
+ `CODEMONITOR_SEMGREP_THRESHOLD`: Don't emit metrics about rubocop cops that are above of this threshold. (Default: `50`)
91
+
92
+ ## Sorbet
93
+
94
+ Collect multiple metrics from the a with [Sorbet](https://sorbet.org/) configured.
95
+
96
+ **Requirements / Setup**:
97
+
98
+ You need a `sorbet.output.json` file present in the current folder.
99
+
100
+ You can generate the `sorbet.output.json` file with this example command:
101
+
102
+ ```
103
+ bundle exec srb tc --metrics-prefix 'codemetrics' --metrics-file sorbet.output.json
104
+ ```
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
+
120
+ # Providers
121
+
122
+ ## Console
123
+
124
+ TODO
125
+
126
+ ## Datadog
127
+
128
+ TODO
129
+
130
+ # Contribute
131
+
132
+ This project started as a side project, so I'm sure that is full
133
+ of mistakes and areas to be improve. If you think you can tweak the code to
134
+ make it better, I'll really appreciate a pull request. ;)
data/codemonitor.gemspec CHANGED
@@ -37,6 +37,7 @@ Gem::Specification.new do |spec|
37
37
  spec.require_paths = ['lib']
38
38
 
39
39
  spec.add_runtime_dependency 'dogapi', '~> 1.45'
40
+ spec.add_runtime_dependency 'octokit', '~> 4.0'
40
41
 
41
42
  spec.add_development_dependency 'bundler', '~> 2.0'
42
43
  spec.add_development_dependency 'pry', '~> 0.13.1'
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Engines
4
+ module Custom
5
+ class Extractor
6
+ def call(provider)
7
+ return unless requirements?
8
+
9
+ provider.emit(metrics)
10
+ end
11
+
12
+ private
13
+
14
+ def requirements?
15
+ custom_files.length.positive?
16
+ end
17
+
18
+ def custom_files
19
+ Dir.glob('./.codemonitor/*.rb')
20
+ end
21
+
22
+ def metrics
23
+ custom_files.map do |file|
24
+ values = begin
25
+ eval File.read(file)
26
+ rescue SyntaxError => e
27
+ raise "Unable to execute the custom codemonitor script `#{file}` file"
28
+ end
29
+
30
+ raise "Malformed return value from `#{file}` file. It must be a hash of metrics" unless values.is_a?(Hash)
31
+
32
+ values
33
+ end.reduce({}, :merge)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'octokit'
4
+ require 'date'
5
+
6
+ Octokit.configure do |c|
7
+ c.auto_paginate = true
8
+ end
9
+
10
+ module Engines
11
+ module Github
12
+ class Extractor
13
+ METRICS = %i[
14
+ github_number_of_open_pull_requests
15
+ github_number_of_lead_time_in_days
16
+ ].freeze
17
+
18
+ def initialize
19
+ @access_token = ENV['GITHUB_TOKEN']
20
+ @repository = ENV['GITHUB_REPOSITORY']
21
+ @since_days = ENV['GITHUB_SINCE_DAYS'] || 30
22
+ end
23
+
24
+ def call(provider)
25
+ return unless requirements?
26
+
27
+ metrics = METRICS.map do |metric|
28
+ [metric, send(metric)]
29
+ end.to_h
30
+
31
+ provider.emit(metrics)
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :access_token, :repository, :since_days
37
+
38
+ def github
39
+ @github ||= Octokit::Client.new(access_token: access_token)
40
+ end
41
+
42
+ def requirements?
43
+ !access_token.nil? && !repository.nil?
44
+ end
45
+
46
+ def since
47
+ (Date.today - since_days).to_time.iso8601
48
+ end
49
+
50
+ def github_number_of_open_pull_requests
51
+ github.issues(repository, state: 'open').length
52
+ end
53
+
54
+ def github_number_of_lead_time_in_days
55
+ diffs = github
56
+ .issues(repository, since: since, state: 'closed')
57
+ .map do |issue|
58
+ next nil if issue[:pull_request][:merged_at].nil? || issue[:created_at].nil?
59
+
60
+ merged_at = Time.at(issue[:pull_request][:merged_at])
61
+ created_at = Time.at(issue[:created_at])
62
+
63
+ merged_at - created_at
64
+ end.reject do |diff|
65
+ diff.nil?
66
+ end
67
+
68
+ value = (diffs.reduce(:+) / diffs.size.to_f / (24 * 60 * 60))
69
+
70
+ value.round(2)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,62 @@
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]
10
+
11
+ def initialize; end
12
+
13
+ def call(provider)
14
+ return unless requirements?
15
+
16
+ metrics = METRICS.map do |metric|
17
+ [metric, send(metric)]
18
+ end.to_h
19
+
20
+ metrics
21
+ .merge!(scc_totals)
22
+ .merge!(scc_by_file_type)
23
+
24
+ provider.emit(metrics)
25
+ end
26
+
27
+ private
28
+
29
+ def requirements?
30
+ File.exist?('scc.output.json')
31
+ end
32
+
33
+ # NOTE: This output file must be created by an external command
34
+ def scc
35
+ @scc ||= JSON.parse(File.read('scc.output.json'))
36
+ end
37
+
38
+ def scc_totals
39
+ scc.each_with_object({}) do |type, totals|
40
+ FIELDS.each do |field|
41
+ key = "scc_total_#{clean(field)}"
42
+ totals[key] = 0 unless totals.key?(key)
43
+
44
+ totals[key] += type[field]
45
+ end
46
+ end
47
+ end
48
+
49
+ def scc_by_file_type
50
+ scc.map do |type|
51
+ FIELDS.map do |field|
52
+ ["scc_type_#{clean(type['Name'])}_#{clean(field)}", type[field]]
53
+ end.to_h
54
+ end.inject(&:merge)
55
+ end
56
+
57
+ def clean(key)
58
+ key.gsub(%r{[-,./ ]}, '_').downcase
59
+ end
60
+ end
61
+ end
62
+ end
data/exe/codemonitor CHANGED
@@ -7,11 +7,14 @@ require_relative '../providers/datadog'
7
7
  require_relative '../engines/eslint/extractor'
8
8
  require_relative '../engines/debug/extractor'
9
9
  require_relative '../engines/git/extractor'
10
+ require_relative '../engines/github/extractor'
10
11
  require_relative '../engines/npm/extractor'
11
12
  require_relative '../engines/packwerk/extractor'
12
13
  require_relative '../engines/rubocop/extractor'
13
14
  require_relative '../engines/semgrep/extractor'
14
15
  require_relative '../engines/sorbet/extractor'
16
+ require_relative '../engines/scc/extractor'
17
+ require_relative '../engines/custom/extractor'
15
18
 
16
19
  PROVIDERS = {
17
20
  console: Providers::Console,
@@ -22,11 +25,14 @@ EXTRACTORS = {
22
25
  eslint: Engines::Eslint::Extractor,
23
26
  debug: Engines::Debug::Extractor,
24
27
  git: Engines::Git::Extractor,
28
+ github: Engines::Github::Extractor,
25
29
  npm: Engines::Npm::Extractor,
26
30
  packwerk: Engines::Packwerk::Extractor,
27
31
  rubocop: Engines::Rubocop::Extractor,
28
32
  semgrep: Engines::Semgrep::Extractor,
29
- sorbet: Engines::Sorbet::Extractor
33
+ sorbet: Engines::Sorbet::Extractor,
34
+ scc: Engines::Scc::Extractor,
35
+ custom: Engines::Custom::Extractor
30
36
  }.freeze
31
37
 
32
38
  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.3.2'
4
+ VERSION = '0.3.6'
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
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.2
4
+ version: 0.3.6
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-07-01 00:00:00.000000000 Z
11
+ date: 2022-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dogapi
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.45'
27
+ - !ruby/object:Gem::Dependency
28
+ name: octokit
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -115,12 +129,15 @@ files:
115
129
  - bin/console
116
130
  - bin/setup
117
131
  - codemonitor.gemspec
132
+ - engines/custom/extractor.rb
118
133
  - engines/debug/extractor.rb
119
134
  - engines/eslint/extractor.rb
120
135
  - engines/git/extractor.rb
136
+ - engines/github/extractor.rb
121
137
  - engines/npm/extractor.rb
122
138
  - engines/packwerk/extractor.rb
123
139
  - engines/rubocop/extractor.rb
140
+ - engines/scc/extractor.rb
124
141
  - engines/semgrep/extractor.rb
125
142
  - engines/sorbet/extractor.rb
126
143
  - exe/codemonitor