codemonitor 0.7.1 → 0.7.5

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: 3827599eb7fa2efbf7519ac50cab319cbb558ccd8c8919bbbc655699c12c10e8
4
- data.tar.gz: 574ea905f2ab2643a83e807a9109a2d3761e2b89e6d657aad9e9c7c4d0cca4be
3
+ metadata.gz: 62ece0078a173b6863e1cfe9e08ca70150d270abe25ec08aba06601f15ea2516
4
+ data.tar.gz: 40323f63b3fc1982bae1bb56c1f80ea16c4e11736d4d1280cfe4afc58c343fe4
5
5
  SHA512:
6
- metadata.gz: 1dbcd9264909f8a556b97752c1c600bca72a072799855cd897485d1acbde01ece7277a17662db12056d7ff37230c100d47a4dcdfb99efe1e24de35de885cd7c2
7
- data.tar.gz: 63bd28af1b58d502e4a1f767050b3062d37994e5bdf90a4b0592162286ea9eebd7b843018f9d4978910551f829cffd70e07038a3d04d65e562fdef4874e2ea66
6
+ metadata.gz: 1445c31e4ddfd0492a4bf1ebb8b939702c9c9bafbcadddc209a33ab90185b8593ee41d21904465329de751e926cb9f6888123dde679f56888cdf8c6c6fb3c73b
7
+ data.tar.gz: e37bc0f7965f010d686137975889e1d0d7292f2237ccd4c1c01f44ff16ee37789e0574e71d93fa5b69cab95636124ce5e6c4a09616ddaed64febb89863fddcd6
data/Gemfile.lock CHANGED
@@ -1,32 +1,32 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- codemonitor (0.7.0)
4
+ codemonitor (0.7.5)
5
5
  dogapi (~> 1.45)
6
6
  octokit (~> 9.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- addressable (2.8.7)
12
- public_suffix (>= 2.0.2, < 7.0)
11
+ addressable (2.8.8)
12
+ public_suffix (>= 2.0.2, < 8.0)
13
13
  ast (2.4.2)
14
14
  coderay (1.1.3)
15
15
  diff-lcs (1.3)
16
16
  dogapi (1.45.0)
17
17
  multi_json
18
- faraday (2.13.4)
18
+ faraday (2.14.0)
19
19
  faraday-net_http (>= 2.0, < 3.5)
20
20
  json
21
21
  logger
22
- faraday-net_http (3.4.1)
23
- net-http (>= 0.5.0)
24
- json (2.13.2)
22
+ faraday-net_http (3.4.2)
23
+ net-http (~> 0.5)
24
+ json (2.18.1)
25
25
  logger (1.7.0)
26
26
  method_source (1.0.0)
27
- multi_json (1.17.0)
28
- net-http (0.6.0)
29
- uri
27
+ multi_json (1.19.1)
28
+ net-http (0.9.1)
29
+ uri (>= 0.11.1)
30
30
  octokit (9.2.0)
31
31
  faraday (>= 1, < 3)
32
32
  sawyer (~> 0.9)
@@ -36,7 +36,7 @@ GEM
36
36
  pry (0.13.1)
37
37
  coderay (~> 1.1)
38
38
  method_source (~> 1.0)
39
- public_suffix (6.0.2)
39
+ public_suffix (7.0.2)
40
40
  rainbow (3.0.0)
41
41
  rake (13.0.3)
42
42
  regexp_parser (2.1.1)
@@ -66,11 +66,11 @@ GEM
66
66
  rubocop-ast (1.5.0)
67
67
  parser (>= 3.0.1.1)
68
68
  ruby-progressbar (1.11.0)
69
- sawyer (0.9.2)
69
+ sawyer (0.9.3)
70
70
  addressable (>= 2.3.5)
71
71
  faraday (>= 0.17.3, < 3)
72
72
  unicode-display_width (1.7.0)
73
- uri (1.0.3)
73
+ uri (1.1.1)
74
74
 
75
75
  PLATFORMS
76
76
  ruby
@@ -84,4 +84,4 @@ DEPENDENCIES
84
84
  rubocop (~> 0.80)
85
85
 
86
86
  BUNDLED WITH
87
- 2.3.12
87
+ 2.7.2
data/README.md CHANGED
@@ -1,145 +1,452 @@
1
1
  # 🖥️ CodeMonitor
2
2
 
3
- A engine to collect multiple metrics from your repository and push them to a
4
- time series provider.
3
+ **CodeMonitor** is a powerful Ruby gem that collects code quality metrics, dependency information, test coverage, and other important statistics from your repository. It aggregates data from multiple analysis tools (linters, type checkers, test frameworks, etc.) and sends them to time series databases or monitoring providers like Datadog for tracking trends over time.
5
4
 
5
+ ## 📊 What Does It Do?
6
6
 
7
- # Engines
7
+ CodeMonitor acts as a centralized metrics collection hub for your codebase. It:
8
+
9
+ - **Collects metrics** from various development tools and analysis extractors
10
+ - **Aggregates data** from linters (ESLint, Rubocop, Semgrep), type checkers (Sorbet), test coverage tools (Jest, SimpleCov), dependency analyzers (npm, Knip, Packwerk), and more
11
+ - **Normalizes metrics** into a consistent format
12
+ - **Pushes data** to monitoring providers (Datadog, Console output)
13
+ - **Tracks trends** over time to help you monitor code quality, technical debt, and project health
14
+
15
+ ## 🚀 Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'codemonitor'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ ```bash
26
+ bundle install
27
+ ```
28
+
29
+ Or install it yourself as:
30
+
31
+ ```bash
32
+ gem install codemonitor
33
+ ```
34
+
35
+ ## 💻 Usage
36
+
37
+ ### Basic Usage
38
+
39
+ Run CodeMonitor in your project directory:
40
+
41
+ ```bash
42
+ codemonitor
43
+ ```
44
+
45
+ By default, it will:
46
+ 1. Auto-detect which extractors are available (based on configuration files)
47
+ 2. Collect metrics from all detected extractors
48
+ 3. Output results to the console
49
+
50
+ ### Configuration
51
+
52
+ Configure CodeMonitor using environment variables:
53
+
54
+ - **`CODEMONITOR_PROVIDER`**: Choose where to send metrics (`console` or `datadog`). Default: `console`
55
+ - **`CODEMONITOR_EXTRACTORS`**: Comma-separated list of specific extractors to run (e.g., `git,eslint,npm`). If not set, all available extractors run.
56
+
57
+ Example:
58
+
59
+ ```bash
60
+ # Send metrics to Datadog
61
+ CODEMONITOR_PROVIDER=datadog DATADOG_API_KEY=your_key codemonitor
62
+
63
+ # Run only specific extractors
64
+ CODEMONITOR_EXTRACTORS=git,npm,eslint codemonitor
65
+ ```
66
+
67
+ # Extractors
68
+
69
+ Extractors are the data collectors that extract metrics from various tools and sources. Each extractor automatically activates when its requirements are met.
8
70
 
9
71
  ## Git
10
72
 
11
- Collect multiple metrics from the a Git repository.
73
+ Collects repository statistics including commits, branches, tags, contributors, and file metrics.
12
74
 
13
- **Requirements / Setup**:
75
+ **Requirements**:
14
76
 
15
- You need a `.git` folder present in the current folder:
77
+ - A `.git` folder in the current directory
16
78
 
17
79
  **Options**:
18
80
 
19
- `CODEMONITOR_GIT_FILES_THRESHOLD`: Don't emit metrics about number of files, from those that are above of this threshold. (Default: `0`)
81
+ - `CODEMONITOR_GIT_FILES_THRESHOLD`: Filter out file extension metrics below this count (Default: `0`)
20
82
 
21
- ## Eslint
83
+ **Metrics**:
22
84
 
23
- Collect multiple metrics from the a project with [Eslint](https://eslint.org/) configured.
85
+ | Metric | Description |
86
+ |--------|-------------|
87
+ | `git_number_of_commits` | Total number of commits in the repository |
88
+ | `git_number_of_branches` | Total number of branches |
89
+ | `git_number_of_tags` | Total number of tags |
90
+ | `git_number_of_contributors` | Total number of unique contributors |
91
+ | `git_number_of_files` | Total number of tracked files |
92
+ | `git_number_of_ignores_files` | Number of ignored files |
93
+ | `git_number_of_lines` | Total lines of code across all files |
94
+ | `git_file_extensions_*` | File count per extension (e.g., `git_file_extensions_rb`, `git_file_extensions_js`) |
24
95
 
25
- **Requirements / Setup**:
96
+ ## ESLint
26
97
 
27
- You need a `.eslintrc.js` and a `eslint.output.json` file present in the current folder.
98
+ Collects JavaScript/TypeScript linting metrics including violations by severity and rule.
28
99
 
29
- You can generate the `eslint.output.json` file with this example command:
100
+ **Requirements**:
30
101
 
31
- ```
102
+ - `eslint.output.json` file in the current directory
103
+
104
+ Generate the required file:
105
+
106
+ ```bash
32
107
  eslint -f json -o eslint.output.json
33
108
  ```
109
+
34
110
  **Options**:
35
111
 
36
- `CODEMONITOR_ESLINT_THRESHOLD`: Don't emit metrics about eslint rules that are above of this threshold. (Default: `10`)
112
+ - `CODEMONITOR_ESLINT_THRESHOLD`: Only report rules with violations >= this threshold (Default: `10`)
113
+
114
+ **Metrics**:
115
+
116
+ | Metric | Description |
117
+ |--------|-------------|
118
+ | `eslint_number_of_offended_files` | Number of files with ESLint violations |
119
+ | `eslint_number_of_offenses` | Total number of ESLint violations |
120
+ | `eslint_number_of_correctable` | Number of auto-fixable violations |
121
+ | `eslint_severity_warning` | Number of warnings |
122
+ | `eslint_severity_error` | Number of errors |
123
+ | `eslint_rule_*` | Violation count per rule (e.g., `eslint_rule_no_console`) |
124
+
125
+
126
+ ## npm
37
127
 
128
+ Collects Node.js dependency information including counts, computed dependencies, and vulnerability data from npm audit.
38
129
 
39
- ## Npm
130
+ **Requirements**:
40
131
 
41
- Collect multiple metrics from the a NodeJS.
132
+ - `package.json` file in the current directory
133
+ - `package-lock.json` file in the current directory
42
134
 
43
- **Requirements / Setup**:
135
+ **Metrics**:
44
136
 
45
- You need a `package.json` file present in the current folder.
137
+ | Metric | Description |
138
+ |--------|-------------|
139
+ | `npm_number_of_prod_dependencies` | Number of production dependencies |
140
+ | `npm_number_of_dev_dependencies` | Number of development dependencies |
141
+ | `npm_number_of_scripts` | Number of npm scripts defined |
142
+ | `npm_number_of_computed_prod_dependencies` | Total production dependencies (including transitive) |
143
+ | `npm_number_of_computed_dev_dependencies` | Total dev dependencies (including transitive) |
144
+ | `npm_number_of_computed_optional_dependencies` | Total optional dependencies |
145
+ | `npm_number_of_computed_peer_dependencies` | Total peer dependencies |
146
+ | `npm_number_of_computed_peer_optional_dependencies` | Total optional peer dependencies |
147
+ | `npm_number_of_computed_total_dependencies` | Grand total of all dependencies |
148
+ | `npm_number_of_vulnerable_dependencies_info` | Vulnerabilities: info severity |
149
+ | `npm_number_of_vulnerable_dependencies_low` | Vulnerabilities: low severity |
150
+ | `npm_number_of_vulnerable_dependencies_moderate` | Vulnerabilities: moderate severity |
151
+ | `npm_number_of_vulnerable_dependencies_high` | Vulnerabilities: high severity |
152
+ | `npm_number_of_vulnerable_dependencies_critical` | Vulnerabilities: critical severity |
153
+ | `npm_number_of_vulnerable_dependencies_total` | Total number of vulnerabilities |
46
154
 
47
155
  ## Packwerk
48
156
 
49
- Collect multiple metrics from the a Ruby project with [Packwerk](https://github.com/Shopify/packwerk) configured.
157
+ Collects metrics from Shopify's [Packwerk](https://github.com/Shopify/packwerk) package system for Ruby, tracking dependency and privacy violations.
50
158
 
51
- **Requirements / Setup**:
159
+ **Requirements**:
52
160
 
53
- You need a `deprecated_references.yml` files present in the current project.
161
+ - `package_todo.yml` files present in the project (in any subdirectory)
162
+
163
+ **Metrics**:
164
+
165
+ | Metric | Description |
166
+ |--------|-------------|
167
+ | `packwerk_number_of_dependency_violations` | Number of package dependency violations |
168
+ | `packwerk_number_of_privacy_violations` | Number of package privacy violations |
54
169
 
55
170
  ## Rubocop
56
171
 
57
- Collect multiple metrics from the a Ruby project with [Rubocop](https://github.com/rubocop/rubocop) configured.
172
+ Collects Ruby linting metrics from [Rubocop](https://github.com/rubocop/rubocop), including violations by severity and cop.
58
173
 
59
- **Requirements / Setup**:
174
+ **Requirements**:
60
175
 
61
- You need a `.rubocop.yml` and a `rubocop.output.json` file present in the current folder.
176
+ - `rubocop.output.json` file in the current directory
62
177
 
63
- You can generate the `rubocop.output.json` file with this example command:
178
+ Generate the required file:
64
179
 
65
- ```
66
- bundle exec srb tc --metrics-prefix 'codemetrics' --metrics-file sorbet.output.json
180
+ ```bash
181
+ bundle exec rubocop -f json -o rubocop.output.json
67
182
  ```
68
183
 
69
184
  **Options**:
70
185
 
71
- `CODEMONITOR_RUBOCOP_THRESHOLD`: Don't emit metrics about rubocop cops that are above of this threshold. (Default: `50`)
186
+ - `CODEMONITOR_RUBOCOP_THRESHOLD`: Only report cops with violations >= this threshold (Default: `50`)
187
+
188
+ **Metrics**:
189
+
190
+ | Metric | Description |
191
+ |--------|-------------|
192
+ | `rubocop_number_of_offenses` | Total number of Rubocop violations |
193
+ | `rubocop_number_of_correctable` | Number of auto-correctable violations |
194
+ | `rubocop_severity_*` | Violations by severity (e.g., `rubocop_severity_warning`) |
195
+ | `rubocop_cop_*` | Violations per cop (e.g., `rubocop_cop_style_frozen_string_literal_comment`) |
72
196
 
73
197
 
74
198
  ## Semgrep
75
199
 
76
- Collect multiple metrics from the a with [Semgrep](https://semgrep.dev/) configured.
200
+ Collects security and code quality findings from [Semgrep](https://semgrep.dev/).
77
201
 
78
- **Requirements / Setup**:
202
+ **Requirements**:
79
203
 
80
- You need a `.semgrep.yml` and a `semgrep.output.json` file present in the current folder.
204
+ - `semgrep.output.json` file in the current directory
81
205
 
82
- You can generate the `semgrep.output.json` file with this example command:
206
+ Generate the required file:
83
207
 
84
- ```
208
+ ```bash
85
209
  semgrep --json -o semgrep.output.json
86
210
  ```
87
211
 
88
212
  **Options**:
89
213
 
90
- `CODEMONITOR_SEMGREP_THRESHOLD`: Don't emit metrics about rubocop cops that are above of this threshold. (Default: `50`)
214
+ - `CODEMONITOR_SEMGREP_THRESHOLD`: Only report check IDs with findings >= this threshold (Default: `50`)
215
+
216
+ **Metrics**:
217
+
218
+ | Metric | Description |
219
+ |--------|-------------|
220
+ | `semgrep_number_of_offenses` | Total number of Semgrep findings |
221
+ | `semgrep_number_of_errors` | Number of Semgrep errors during analysis |
222
+ | `semgrep_check_*` | Findings per check ID |
91
223
 
92
224
  ## Knip
93
225
 
94
- Collect multiple metrics from [Knip](https://knip.dev/)
226
+ Collects unused code metrics from [Knip](https://knip.dev/), identifying unused dependencies, exports, and types.
95
227
 
96
- **Requirements / Setup**:
228
+ **Requirements**:
97
229
 
98
- You need a `knip.output.json` file present in the current folder.
230
+ - `knip.output.json` file in the current directory
99
231
 
100
- You can generate the `knip.output.json` file with this example command:
232
+ Generate the required file:
101
233
 
102
- ```
234
+ ```bash
103
235
  knip --reporter json > knip.output.json
104
236
  ```
105
237
 
238
+ **Metrics**:
239
+
240
+ | Metric | Description |
241
+ |--------|-------------|
242
+ | `knip_number_of_dependecies` | Unused dependencies |
243
+ | `knip_number_of_devDependencies` | Unused dev dependencies |
244
+ | `knip_number_of_optionalPeerDependencies` | Unused optional peer dependencies |
245
+ | `knip_number_of_unlisted` | Unlisted dependencies |
246
+ | `knip_number_of_binaries` | Unused binaries |
247
+ | `knip_number_of_unresolved` | Unresolved imports |
248
+ | `knip_number_of_exports` | Unused exports |
249
+ | `knip_number_of_types` | Unused types |
250
+ | `knip_number_of_enumMembers` | Unused enum members |
251
+ | `knip_number_of_duplicates` | Duplicate exports |
252
+
106
253
  ## Sorbet
107
254
 
108
- Collect multiple metrics from [Sorbet](https://sorbet.org/) configured.
255
+ Collects type coverage metrics from [Sorbet](https://sorbet.org/), Stripe's type checker for Ruby.
109
256
 
110
- **Requirements / Setup**:
257
+ **Requirements**:
111
258
 
112
- You need a `sorbet.output.json` file present in the current folder.
259
+ - `sorbet.output.json` file in the current directory
113
260
 
114
- You can generate the `sorbet.output.json` file with this example command:
261
+ Generate the required file:
115
262
 
116
- ```
263
+ ```bash
117
264
  bundle exec srb tc --metrics-prefix 'codemetrics' --metrics-file sorbet.output.json
118
265
  ```
119
266
 
120
- ## SCC
267
+ **Metrics**:
268
+
269
+ | Metric | Description |
270
+ |--------|-------------|
271
+ | `sorbet_number_of_sig_count` | Number of type signatures |
272
+ | `sorbet_number_of_input_classes_total` | Total number of classes |
273
+ | `sorbet_number_of_input_sends_total` | Total number of method calls |
274
+ | `sorbet_number_of_input_files` | Number of Ruby files analyzed |
275
+ | `sorbet_number_of_input_methods_total` | Total number of methods |
276
+ | `sorbet_number_of_input_modules_total` | Total number of modules |
277
+ | `sorbet_number_of_sigil_true` | Files with `typed: true` |
278
+ | `sorbet_number_of_sigil_false` | Files with `typed: false` |
279
+ | `sorbet_number_of_sigil_autogenerated` | Files with `typed: autogenerated` |
280
+ | `sorbet_number_of_sigil_strong` | Files with `typed: strong` |
281
+ | `sorbet_number_of_sigil_strict` | Files with `typed: strict` |
282
+ | `sorbet_number_of_sigil_ignore` | Files with `typed: ignore` |
283
+
284
+ ## SCC (Sloc Cloc and Code)
285
+
286
+ Collects code statistics from [SCC](https://github.com/boyter/scc), including lines of code, complexity, and language breakdowns.
287
+
288
+ **Requirements**:
289
+
290
+ - `scc.output.json` file in the current directory
291
+
292
+ Generate the required file:
293
+
294
+ ```bash
295
+ scc -f json > scc.output.json
296
+ ```
297
+
298
+ **Metrics**:
299
+
300
+ | Metric | Description |
301
+ |--------|-------------|
302
+ | `scc_total_bytes` | Total bytes across all files |
303
+ | `scc_total_lines` | Total lines across all files |
304
+ | `scc_total_code` | Total lines of code |
305
+ | `scc_total_comment` | Total comment lines |
306
+ | `scc_total_blank` | Total blank lines |
307
+ | `scc_total_complexity` | Total cyclomatic complexity |
308
+ | `scc_total_count` | Total number of files |
309
+ | `scc_total_weightedcomplexity` | Weighted complexity score |
310
+ | `scc_type_*_*` | Per-language metrics (e.g., `scc_type_ruby_code`, `scc_type_javascript_lines`) |
311
+
312
+ ## Jest
313
+
314
+ Collects JavaScript/TypeScript test coverage metrics from Jest's JSON summary reporter.
315
+
316
+ **Requirements**:
317
+
318
+ - `jest_json_summary.output.json` file in the current directory
319
+
320
+ Configure Jest to generate this file using the `json-summary` reporter in your `jest.config.js`.
321
+
322
+ **Metrics**:
323
+
324
+ | Metric | Description |
325
+ |--------|-------------|
326
+ | `jest_json_summary_lines_total` | Total lines in test coverage |
327
+ | `jest_json_summary_lines_covered` | Covered lines |
328
+ | `jest_json_summary_lines_skipped` | Skipped lines |
329
+ | `jest_json_summary_lines_pct` | Line coverage percentage |
330
+ | `jest_json_summary_statements_*` | Statement coverage metrics |
331
+ | `jest_json_summary_functions_*` | Function coverage metrics |
332
+ | `jest_json_summary_branches_*` | Branch coverage metrics |
333
+
334
+ ## SimpleCov
335
+
336
+ Collects Ruby test coverage metrics from [SimpleCov](https://github.com/simplecov-ruby/simplecov).
337
+
338
+ **Requirements**:
339
+
340
+ - `simplecov_json_coverage.output.json` file in the current directory
341
+
342
+ Configure SimpleCov to use the JSON formatter in your test setup.
121
343
 
122
- Collect multiple metrics from [SCC](https://github.com/boyter/scc) configured.
344
+ **Metrics**:
123
345
 
124
- **Requirements / Setup**:
346
+ | Metric | Description |
347
+ |--------|-------------|
348
+ | `simplecov_json_coverage_metrics_covered_percent` | Code coverage percentage |
349
+ | `simplecov_json_coverage_metrics_covered_strength` | Coverage strength score |
350
+ | `simplecov_json_coverage_metrics_covered_lines` | Number of covered lines |
351
+ | `simplecov_json_coverage_metrics_total_lines` | Total number of lines |
125
352
 
126
- You need a `scc.output.json` file present in the current folder.
353
+ ## TSC (TypeScript Compiler)
127
354
 
128
- You can generate the `scc.output.json` file with this example command:
355
+ Collects TypeScript compiler diagnostics and performance metrics from `tsc --extendedDiagnostics` output.
129
356
 
357
+ **Requirements**:
358
+
359
+ - `tsc.output.txt` file in the current directory
360
+
361
+ Generate the required file:
362
+
363
+ ```bash
364
+ pnpm tsc --extendedDiagnostics --incremental false > tsc.output.txt
130
365
  ```
131
- scc -f json scc.output.json
366
+
367
+ Or using npm:
368
+
369
+ ```bash
370
+ npm run tsc -- --extendedDiagnostics --incremental false > tsc.output.txt
132
371
  ```
133
372
 
373
+ **Metrics**:
374
+
375
+ | Metric | Description |
376
+ |--------|-------------|
377
+ | `tsc_files` | Total number of files analyzed |
378
+ | `tsc_lines_of_library` | Lines of library code |
379
+ | `tsc_lines_of_definitions` | Lines of type definitions |
380
+ | `tsc_lines_of_typescript` | Lines of TypeScript code |
381
+ | `tsc_lines_of_javascript` | Lines of JavaScript code |
382
+ | `tsc_lines_of_json` | Lines of JSON files |
383
+ | `tsc_lines_of_other` | Lines of other file types |
384
+ | `tsc_identifiers` | Total number of identifiers |
385
+ | `tsc_symbols` | Total number of symbols |
386
+ | `tsc_types` | Total number of types |
387
+ | `tsc_instantiations` | Total number of type instantiations |
388
+ | `tsc_memory_used_kb` | Memory used by compiler (in kilobytes) |
389
+ | `tsc_assignability_cache_size` | Size of assignability cache |
390
+ | `tsc_identity_cache_size` | Size of identity cache |
391
+ | `tsc_subtype_cache_size` | Size of subtype cache |
392
+ | `tsc_strict_subtype_cache_size` | Size of strict subtype cache |
393
+ | `tsc_io_read_time_seconds` | Time spent reading files (seconds) |
394
+ | `tsc_parse_time_seconds` | Time spent parsing (seconds) |
395
+ | `tsc_resolve_module_time_seconds` | Time spent resolving modules (seconds) |
396
+ | `tsc_resolve_type_reference_time_seconds` | Time spent resolving type references (seconds) |
397
+ | `tsc_resolve_library_time_seconds` | Time spent resolving libraries (seconds) |
398
+ | `tsc_program_time_seconds` | Time spent on program construction (seconds) |
399
+ | `tsc_bind_time_seconds` | Time spent binding (seconds) |
400
+ | `tsc_check_time_seconds` | Time spent type checking (seconds) |
401
+ | `tsc_print_time_seconds` | Time spent printing (seconds) |
402
+ | `tsc_emit_time_seconds` | Time spent emitting output (seconds) |
403
+ | `tsc_total_time_seconds` | Total compilation time (seconds) |
404
+
405
+ ## GitHub
406
+
407
+ Collects pull request metrics directly from GitHub, including open PRs and lead time.
408
+
409
+ **Requirements**:
410
+
411
+ - `GITHUB_TOKEN` environment variable with a GitHub personal access token
412
+ - `GITHUB_REPOSITORY` environment variable (format: `owner/repo`)
413
+
414
+ **Options**:
415
+
416
+ - `GITHUB_SINCE_DAYS`: Number of days to look back for lead time calculation (Default: `30`)
417
+
418
+ Example:
419
+
420
+ ```bash
421
+ GITHUB_TOKEN=ghp_xxx GITHUB_REPOSITORY=owner/repo codemonitor
422
+ ```
423
+
424
+ **Metrics**:
425
+
426
+ | Metric | Description |
427
+ |--------|-------------|
428
+ | `github_number_of_open_pull_requests` | Number of open pull requests |
429
+ | `github_number_of_lead_time_in_days` | Average lead time for merged PRs (in days) |
430
+
134
431
  # Providers
135
432
 
433
+ Providers determine where the collected metrics are sent. CodeMonitor supports multiple output destinations.
434
+
136
435
  ## Console
137
436
 
138
- Simply outputs all metrics to the console.
437
+ Outputs all metrics to standard output in a simple text format.
139
438
 
140
439
  **Usage**:
141
440
 
142
- The console provider prints metrics in the format `metric_name: value`. If metrics include tags (using the `#` delimiter format), they are printed as is:
441
+ ```bash
442
+ codemonitor # Console is the default provider
443
+ # OR
444
+ CODEMONITOR_PROVIDER=console codemonitor
445
+ ```
446
+
447
+ **Output Format**:
448
+
449
+ The console provider prints metrics in the format `metric_name: value`. If metrics include tags (using the `#` delimiter), they are displayed as-is:
143
450
 
144
451
  ```
145
452
  requests: 100
@@ -150,25 +457,114 @@ latency#backend,region:us-east-1,env:prod: 150
150
457
 
151
458
  ## Datadog
152
459
 
153
- Sends metrics to Datadog using the Datadog API. Supports metric tagging for better organization and filtering.
460
+ Sends metrics to [Datadog](https://www.datadoghq.com/) using the Datadog API for monitoring and visualization over time.
461
+
462
+ **Usage**:
463
+
464
+ ```bash
465
+ CODEMONITOR_PROVIDER=datadog DATADOG_API_KEY=your_api_key codemonitor
466
+ ```
154
467
 
155
468
  **Environment Variables**:
156
469
 
157
- - `DATADOG_API_KEY` (required): your Datadog API key
158
- - `DATADOG_PREFIX` (optional): prefix to add to all metric names (default: `'codemonitor.'`)
470
+ - `DATADOG_API_KEY` (required): Your Datadog API key
471
+ - `DATADOG_PREFIX` (optional): Prefix added to all metric names (default: `'codemonitor.'`)
159
472
 
160
473
  **Metric Tagging**:
161
474
 
162
- The Datadog provider supports adding tags to metrics by including them in the metric name using the `#` delimiter:
475
+ The Datadog provider supports adding tags to metrics for better organization and filtering. Include tags in the metric name using the `#` delimiter:
476
+
477
+ - **Format**: `metric_name#tag1,tag2:value,tag3`
478
+ - **Example**: `requests#frontend,app:webserver` → sends metric `requests` with tags `['frontend', 'app:webserver']`
479
+ - Tags follow Datadog's standard format: simple tags (`tag`) or key-value pairs (`key:value`)
480
+
481
+ More about Datadog tagging: [Datadog Tagging Documentation](https://docs.datadoghq.com/getting_started/tagging/)
482
+
483
+ ## 🔧 Advanced Usage
484
+
485
+ ### Running Specific Extractors
486
+
487
+ To run only specific extractors, use the `CODEMONITOR_EXTRACTORS` environment variable:
488
+
489
+ ```bash
490
+ # Run only Git and ESLint extractors
491
+ CODEMONITOR_EXTRACTORS=git,eslint codemonitor
492
+
493
+ # Run npm and Rubocop with Datadog output
494
+ CODEMONITOR_PROVIDER=datadog DATADOG_API_KEY=xxx CODEMONITOR_EXTRACTORS=npm,rubocop codemonitor
495
+ ```
496
+
497
+ ### Interactive Mode
498
+
499
+ CodeMonitor can accept metrics directly from standard input using the `--interactive` flag. This allows you to send custom metrics without needing to configure extractors:
500
+
501
+ ```bash
502
+ codemonitor --interactive <<EOF
503
+ {
504
+ metric_name: 100,
505
+ another_metric: 200,
506
+ "tagged_metric#tag1,tag2:value": 300
507
+ }
508
+
509
+
510
+ EOF
511
+ ```
512
+
513
+ The interactive mode reads from stdin until it encounters two consecutive empty lines. The input must be a valid Ruby hash. This is useful for:
514
+ - Sending ad-hoc metrics
515
+ - Integrating with scripts that generate metrics dynamically
516
+ - Testing providers without setting up extractors
517
+
518
+ ## Environment Variables
519
+
520
+ - `CODEMONITOR_PROVIDER`: Provider to use (default: `console`, options: `console`, `datadog`)
521
+ - `CODEMONITOR_EXTRACTORS`: Comma-separated list of extractors to run (default: all extractors)
522
+
523
+ For provider-specific configuration, see the [Providers](#providers) section below.
524
+
525
+ ### CI/CD Integration
526
+
527
+ CodeMonitor is designed to run in CI/CD pipelines. Here's an example GitHub Actions workflow:
528
+
529
+ ```yaml
530
+ name: Code Metrics
531
+ on: [push]
532
+
533
+ jobs:
534
+ metrics:
535
+ runs-on: ubuntu-latest
536
+ steps:
537
+ - uses: actions/checkout@v2
538
+
539
+ # Generate output files for various tools
540
+ - name: Run ESLint
541
+ run: eslint -f json -o eslint.output.json || true
542
+
543
+ - name: Run Rubocop
544
+ run: bundle exec rubocop -f json -o rubocop.output.json || true
545
+
546
+ # Collect and send metrics
547
+ - name: Run CodeMonitor
548
+ env:
549
+ CODEMONITOR_PROVIDER: datadog
550
+ DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
551
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
552
+ GITHUB_REPOSITORY: ${{ github.repository }}
553
+ run: codemonitor
554
+ ```
555
+
556
+ ### Best Practices
557
+
558
+ 1. **Generate tool outputs before running CodeMonitor**: Most extractors require JSON output files from their respective tools
559
+ 2. **Use thresholds wisely**: Adjust threshold values to focus on significant metrics and reduce noise
560
+ 3. **Track trends over time**: Send metrics to Datadog or similar to visualize trends and set up alerts
561
+ 4. **Run in CI/CD**: Automate metric collection on every commit or pull request
562
+ 5. **Combine multiple extractors**: Use Git + linters + test coverage for a comprehensive health overview
163
563
 
164
- - Format: `metric_name#tag1,tag2:value,tag3`
165
- - Example: `requests#frontend,app:webserver` → sends metric `requests` with tags `['frontend', 'app:webserver']`
166
- - Tags follow Datadog's standard format where tags can be simple (`tag`) or key-value pairs (`key:value`)
564
+ ## 🤝 Contribute
167
565
 
168
- More about tagging: [https://docs.datadoghq.com/getting_started/tagging/](https://docs.datadoghq.com/getting_started/tagging/)
566
+ This project started as a side project, so there's always room for improvement! If you have ideas for new extractors, better metrics, or code improvements, pull requests are very welcome. 😊
169
567
 
170
- # Contribute
568
+ ## 📝 License
171
569
 
172
- This project started as a side project, so I'm sure that is full
173
- of mistakes and areas to be improve. If you think you can tweak the code to
174
- make it better, I'll really appreciate a pull request. ;)
570
+ This project is available under the MIT License. See the LICENSE file for details.
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Engines
4
+ module Tsc
5
+ class Extractor
6
+ METRICS = %i[
7
+ tsc_files
8
+ tsc_lines_of_library
9
+ tsc_lines_of_definitions
10
+ tsc_lines_of_typescript
11
+ tsc_lines_of_javascript
12
+ tsc_lines_of_json
13
+ tsc_lines_of_other
14
+ tsc_identifiers
15
+ tsc_symbols
16
+ tsc_types
17
+ tsc_instantiations
18
+ tsc_memory_used_kb
19
+ tsc_assignability_cache_size
20
+ tsc_identity_cache_size
21
+ tsc_subtype_cache_size
22
+ tsc_strict_subtype_cache_size
23
+ tsc_io_read_time_seconds
24
+ tsc_parse_time_seconds
25
+ tsc_resolve_module_time_seconds
26
+ tsc_resolve_type_reference_time_seconds
27
+ tsc_resolve_library_time_seconds
28
+ tsc_program_time_seconds
29
+ tsc_bind_time_seconds
30
+ tsc_check_time_seconds
31
+ tsc_print_time_seconds
32
+ tsc_emit_time_seconds
33
+ tsc_total_time_seconds
34
+ ].freeze
35
+
36
+ def call(provider)
37
+ metrics = METRICS.map do |metric|
38
+ [metric, send(metric)]
39
+ end.to_h
40
+
41
+ provider.emit(metrics)
42
+ end
43
+
44
+ def requirements?
45
+ File.exist?('tsc.output.txt')
46
+ end
47
+
48
+ private
49
+
50
+ def tsc_output
51
+ @tsc_output ||= File.read('tsc.output.txt')
52
+ end
53
+
54
+ def extract_integer(pattern)
55
+ match = tsc_output.match(pattern)
56
+ return 0 if match.nil?
57
+
58
+ match[1].gsub(/\s+/, '').to_i
59
+ end
60
+
61
+ def extract_time(pattern)
62
+ match = tsc_output.match(pattern)
63
+ return 0.0 if match.nil?
64
+
65
+ time_str = match[1]
66
+ # Convert time format like "0.63s" to 0.63
67
+ time_str.sub(/s$/, '').to_f
68
+ end
69
+
70
+ def tsc_files
71
+ extract_integer(/^Files:\s+(\d+)/)
72
+ end
73
+
74
+ def tsc_lines_of_library
75
+ extract_integer(/^Lines of Library:\s+(\d+)/)
76
+ end
77
+
78
+ def tsc_lines_of_definitions
79
+ extract_integer(/^Lines of Definitions:\s+(\d+)/)
80
+ end
81
+
82
+ def tsc_lines_of_typescript
83
+ extract_integer(/^Lines of TypeScript:\s+(\d+)/)
84
+ end
85
+
86
+ def tsc_lines_of_javascript
87
+ extract_integer(/^Lines of JavaScript:\s+(\d+)/)
88
+ end
89
+
90
+ def tsc_lines_of_json
91
+ extract_integer(/^Lines of JSON:\s+(\d+)/)
92
+ end
93
+
94
+ def tsc_lines_of_other
95
+ extract_integer(/^Lines of Other:\s+(\d+)/)
96
+ end
97
+
98
+ def tsc_identifiers
99
+ extract_integer(/^Identifiers:\s+(\d+)/)
100
+ end
101
+
102
+ def tsc_symbols
103
+ extract_integer(/^Symbols:\s+(\d+)/)
104
+ end
105
+
106
+ def tsc_types
107
+ extract_integer(/^Types:\s+(\d+)/)
108
+ end
109
+
110
+ def tsc_instantiations
111
+ extract_integer(/^Instantiations:\s+(\d+)/)
112
+ end
113
+
114
+ def tsc_memory_used_kb
115
+ # Extract memory in KB (e.g., "6942037K" -> 6942037)
116
+ match = tsc_output.match(/^Memory used:\s+(\d+)K/)
117
+ return 0 if match.nil?
118
+
119
+ match[1].to_i
120
+ end
121
+
122
+ def tsc_assignability_cache_size
123
+ extract_integer(/^Assignability cache size:\s+(\d+)/)
124
+ end
125
+
126
+ def tsc_identity_cache_size
127
+ extract_integer(/^Identity cache size:\s+(\d+)/)
128
+ end
129
+
130
+ def tsc_subtype_cache_size
131
+ extract_integer(/^Subtype cache size:\s+(\d+)/)
132
+ end
133
+
134
+ def tsc_strict_subtype_cache_size
135
+ extract_integer(/^Strict subtype cache size:\s+(\d+)/)
136
+ end
137
+
138
+ def tsc_io_read_time_seconds
139
+ extract_time(/^I\/O Read time:\s+([\d.]+s)/)
140
+ end
141
+
142
+ def tsc_parse_time_seconds
143
+ extract_time(/^Parse time:\s+([\d.]+s)/)
144
+ end
145
+
146
+ def tsc_resolve_module_time_seconds
147
+ extract_time(/^ResolveModule time:\s+([\d.]+s)/)
148
+ end
149
+
150
+ def tsc_resolve_type_reference_time_seconds
151
+ extract_time(/^ResolveTypeReference time:\s+([\d.]+s)/)
152
+ end
153
+
154
+ def tsc_resolve_library_time_seconds
155
+ extract_time(/^ResolveLibrary time:\s+([\d.]+s)/)
156
+ end
157
+
158
+ def tsc_program_time_seconds
159
+ extract_time(/^Program time:\s+([\d.]+s)/)
160
+ end
161
+
162
+ def tsc_bind_time_seconds
163
+ extract_time(/^Bind time:\s+([\d.]+s)/)
164
+ end
165
+
166
+ def tsc_check_time_seconds
167
+ extract_time(/^Check time:\s+([\d.]+s)/)
168
+ end
169
+
170
+ def tsc_print_time_seconds
171
+ extract_time(/^printTime time:\s+([\d.]+s)/)
172
+ end
173
+
174
+ def tsc_emit_time_seconds
175
+ extract_time(/^Emit time:\s+([\d.]+s)/)
176
+ end
177
+
178
+ def tsc_total_time_seconds
179
+ extract_time(/^Total time:\s+([\d.]+s)/)
180
+ end
181
+ end
182
+ end
183
+ end
data/exe/codemonitor CHANGED
@@ -18,6 +18,7 @@ require_relative '../engines/scc/extractor'
18
18
  require_relative '../engines/semgrep/extractor'
19
19
  require_relative '../engines/simplecov-json-coverage/extractor'
20
20
  require_relative '../engines/sorbet/extractor'
21
+ require_relative '../engines/tsc/extractor'
21
22
 
22
23
  PROVIDERS = {
23
24
  console: Providers::Console,
@@ -38,30 +39,69 @@ EXTRACTORS = {
38
39
  scc: Engines::Scc::Extractor,
39
40
  semgrep: Engines::Semgrep::Extractor,
40
41
  simplecov_json_coverage: Engines::SimpleCovJsonCoverage::Extractor,
41
- sorbet: Engines::Sorbet::Extractor
42
+ sorbet: Engines::Sorbet::Extractor,
43
+ tsc: Engines::Tsc::Extractor
42
44
  }.freeze
43
45
 
46
+ # Parse command-line arguments
47
+ interactive_mode = ARGV.include?('--interactive')
48
+
44
49
  config_provider = ENV['CODEMONITOR_PROVIDER'] || 'console'
45
50
  config_extractors = ENV['CODEMONITOR_EXTRACTORS']
46
51
 
47
52
  provider = PROVIDERS[config_provider.to_sym].new
48
- extractors = if config_extractors
49
- EXTRACTORS.slice(*config_extractors.split(',').map(&:to_sym)).values
50
- else
51
- EXTRACTORS.values
52
- end
53
53
 
54
54
  puts '# process start'
55
55
 
56
- # Run all the extractors and collect the data
57
- extractors
58
- .map(&:new)
59
- .map do |extractor|
60
- raise "Requirements not fullfiled in #{extractor.class.name}" unless extractor.requirements?
56
+ if interactive_mode
57
+ # Read from stdin until two consecutive empty lines
58
+ lines = []
59
+ empty_line_count = 0
60
+
61
+ while empty_line_count < 2
62
+ line = $stdin.gets
63
+ break unless line
64
+
65
+ line = line.chomp
61
66
 
62
- extractor.call(provider)
67
+ if line.empty?
68
+ empty_line_count += 1
69
+ else
70
+ empty_line_count = 0
71
+ lines << line
72
+ end
63
73
  end
64
74
 
75
+ # Join the lines and evaluate as a Ruby hash
76
+ input_str = lines.join("\n")
77
+
78
+ begin
79
+ metrics = eval(input_str)
80
+ raise "Input must be a Hash" unless metrics.is_a?(Hash)
81
+
82
+ provider.emit(metrics)
83
+ rescue SyntaxError, StandardError => e
84
+ puts "Error parsing input: #{e.message}"
85
+ exit 1
86
+ end
87
+ else
88
+ # Original behavior: run extractors
89
+ extractors = if config_extractors
90
+ EXTRACTORS.slice(*config_extractors.split(',').map(&:to_sym)).values
91
+ else
92
+ EXTRACTORS.values
93
+ end
94
+
95
+ # Run all the extractors and collect the data
96
+ extractors
97
+ .map(&:new)
98
+ .map do |extractor|
99
+ raise "Requirements not fullfiled in #{extractor.class.name}" unless extractor.requirements?
100
+
101
+ extractor.call(provider)
102
+ end
103
+ end
104
+
65
105
  # Send all the data to the provider
66
106
  provider.send
67
107
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CodeMonitor
4
- VERSION = "0.7.1"
4
+ VERSION = "0.7.5"
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.7.1
4
+ version: 0.7.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ferran Basora
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-09-04 00:00:00.000000000 Z
11
+ date: 2026-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dogapi
@@ -144,6 +144,7 @@ files:
144
144
  - engines/semgrep/extractor.rb
145
145
  - engines/simplecov-json-coverage/extractor.rb
146
146
  - engines/sorbet/extractor.rb
147
+ - engines/tsc/extractor.rb
147
148
  - exe/codemonitor
148
149
  - lib/codemonitor/version.rb
149
150
  - lib/shell.rb