coverband 6.1.6 → 6.1.7

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +4 -4
  3. data/README.md +123 -0
  4. data/agents.md +217 -0
  5. data/bin/coverband-mcp +42 -0
  6. data/changes.md +18 -0
  7. data/coverband/log.272267 +1 -0
  8. data/coverband.gemspec +3 -1
  9. data/lib/coverband/collectors/route_tracker.rb +1 -1
  10. data/lib/coverband/collectors/view_tracker.rb +21 -13
  11. data/lib/coverband/configuration.rb +57 -18
  12. data/lib/coverband/integrations/resque.rb +2 -2
  13. data/lib/coverband/mcp/http_handler.rb +118 -0
  14. data/lib/coverband/mcp/server.rb +116 -0
  15. data/lib/coverband/mcp/tools/get_coverage_summary.rb +41 -0
  16. data/lib/coverband/mcp/tools/get_dead_methods.rb +69 -0
  17. data/lib/coverband/mcp/tools/get_file_coverage.rb +72 -0
  18. data/lib/coverband/mcp/tools/get_route_tracker_data.rb +60 -0
  19. data/lib/coverband/mcp/tools/get_translation_tracker_data.rb +60 -0
  20. data/lib/coverband/mcp/tools/get_uncovered_files.rb +73 -0
  21. data/lib/coverband/mcp/tools/get_view_tracker_data.rb +60 -0
  22. data/lib/coverband/mcp.rb +27 -0
  23. data/lib/coverband/reporters/base.rb +2 -4
  24. data/lib/coverband/reporters/web.rb +17 -14
  25. data/lib/coverband/utils/lines_classifier.rb +1 -1
  26. data/lib/coverband/utils/result.rb +2 -1
  27. data/lib/coverband/utils/source_file.rb +5 -5
  28. data/lib/coverband/utils/tasks.rb +31 -0
  29. data/lib/coverband/version.rb +1 -1
  30. data/lib/coverband.rb +2 -2
  31. data/test/coverband/file_store_integration_test.rb +72 -0
  32. data/test/coverband/file_store_redis_error_test.rb +56 -0
  33. data/test/coverband/github_issue_586_test.rb +46 -0
  34. data/test/coverband/initialization_timing_test.rb +71 -0
  35. data/test/coverband/mcp/http_handler_test.rb +159 -0
  36. data/test/coverband/mcp/security_test.rb +145 -0
  37. data/test/coverband/mcp/server_test.rb +125 -0
  38. data/test/coverband/mcp/tools/get_coverage_summary_test.rb +75 -0
  39. data/test/coverband/mcp/tools/get_dead_methods_test.rb +162 -0
  40. data/test/coverband/mcp/tools/get_file_coverage_test.rb +159 -0
  41. data/test/coverband/mcp/tools/get_route_tracker_data_test.rb +122 -0
  42. data/test/coverband/mcp/tools/get_translation_tracker_data_test.rb +122 -0
  43. data/test/coverband/mcp/tools/get_uncovered_files_test.rb +177 -0
  44. data/test/coverband/mcp/tools/get_view_tracker_data_test.rb +122 -0
  45. data/test/coverband/reporters/web_test.rb +5 -0
  46. data/test/coverband/tracker_initialization_test.rb +75 -0
  47. data/test/coverband/user_environment_simulation_test.rb +75 -0
  48. data/test/coverband/utils/lines_classifier_test.rb +1 -1
  49. data/test/integration/mcp_integration_test.rb +175 -0
  50. data/test/test_helper.rb +4 -5
  51. metadata +65 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 032cf0d003aefa2d222472e12412e352f5059cfffc93645810692e60b3dc6602
4
- data.tar.gz: b094364d6cc2dbfbc2527209f4bf03025c06199443a0b9fa0380851afe7a00ae
3
+ metadata.gz: e3ae4783bfea151c9e3b5d58e397467085206359b6c3446df24ad136fc067ca3
4
+ data.tar.gz: c7491d46394412b813447b80e21ca29e1cfe62060b54a28a14a6145a8ec5eb84
5
5
  SHA512:
6
- metadata.gz: 14ce46ede0f55f2162878529c8226a022518df0a0a22c83ad323d0856fad43b99a9f694b3b629171b86872bfd16626da9772388cae5a665a5e00e1493ef81b8b
7
- data.tar.gz: c394482fdfddfa2d70dab2453517f2c490e66e1b9ef463a5a097413deb41540158a13a0626a2f3c4e62fc263de3052df6d99c7c7765ddb9ae867074b5b943e94
6
+ metadata.gz: 2cbd8715bf8a5ea95e5705038acec36bd8549fb9ca01b99c4266919904122fb9fd81cbc3e1488d6eaa0c0bb955875c429751523843449abc0c5d01107d620085
7
+ data.tar.gz: f4ac2904d9cde83162aa3f1d350aeb25e337f8c86178b79668170ae00ef64df9d564e15cdd94597cb1ea6652daa244b8454cdc8c3401e14b74662527ff7ea34e
@@ -21,7 +21,7 @@ jobs:
21
21
  # removing jruby again to flaky
22
22
  gemfile: [ Gemfile.rails7.0, Gemfile.rails7.1, Gemfile.rails7.2, Gemfile.rails8.0 ]
23
23
  # need to add support for multiple gemfiles
24
- ruby: ["3.1", "3.2", "3.3", "3.4", "ruby-head"]
24
+ ruby: ["3.1", "3.2", "3.3", "3.4", "4.0", "ruby-head"]
25
25
  redis-version: [4, 5, 6, 7]
26
26
  exclude:
27
27
  # Rails 8 requires ruby 3.2+.
@@ -29,8 +29,8 @@ jobs:
29
29
  ruby: '3.1'
30
30
  runs-on: ${{ matrix.os }}-latest
31
31
  steps:
32
- - uses: actions/checkout@v4
33
- - uses: supercharge/redis-github-action@1.8.0
32
+ - uses: actions/checkout@v6
33
+ - uses: supercharge/redis-github-action@v2
34
34
  with:
35
35
  redis-version: ${{ matrix.redis-version }}
36
36
  - uses: ruby/setup-ruby@v1
@@ -42,7 +42,7 @@ jobs:
42
42
  starndardrb:
43
43
  runs-on: ubuntu-latest
44
44
  steps:
45
- - uses: actions/checkout@v4
45
+ - uses: actions/checkout@v6
46
46
  - uses: ruby/setup-ruby@v1
47
47
  with:
48
48
  ruby-version: 3.1
data/README.md CHANGED
@@ -406,6 +406,129 @@ A few folks have asked about what size of Redis is needed to run Coverband. I ha
406
406
 
407
407
  # Newer Features
408
408
 
409
+ ### MCP Server for AI Assistants
410
+
411
+ Coverband includes an optional MCP (Model Context Protocol) server that allows AI assistants like Claude to query your production coverage data directly. This enables AI-powered code analysis, dead code detection, and coverage insights.
412
+
413
+ #### Installation
414
+
415
+ Add the MCP gem to your Gemfile:
416
+
417
+ ```ruby
418
+ gem 'mcp'
419
+ ```
420
+
421
+ #### Available Tools
422
+
423
+ The MCP server provides the following tools:
424
+
425
+ | Tool | Description |
426
+ |------|-------------|
427
+ | `get_coverage_summary` | Get overall coverage statistics |
428
+ | `get_file_coverage` | Get detailed coverage for a specific file |
429
+ | `get_uncovered_files` | List files with no coverage data |
430
+ | `get_dead_methods` | Find methods that are never called |
431
+ | `get_view_tracker_data` | Get view/template usage data |
432
+ | `get_route_tracker_data` | Get route usage statistics |
433
+ | `get_translation_tracker_data` | Get translation key usage data |
434
+
435
+ #### Running the MCP Server
436
+
437
+ **Standalone (stdio transport):**
438
+
439
+ ```bash
440
+ bundle exec coverband-mcp
441
+ ```
442
+
443
+ **With a rake task:**
444
+
445
+ ```bash
446
+ bundle exec rake coverband:mcp
447
+ ```
448
+
449
+ **HTTP mode (for remote access):**
450
+
451
+ ```bash
452
+ COVERBAND_MCP_HTTP=true COVERBAND_MCP_PORT=9023 bundle exec rake coverband:mcp
453
+ ```
454
+
455
+ #### Configuring Claude Desktop
456
+
457
+ Add to your Claude Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
458
+
459
+ **Option 1: Stdio transport (recommended for local development)**
460
+
461
+ ```json
462
+ {
463
+ "mcpServers": {
464
+ "coverband": {
465
+ "command": "bundle",
466
+ "args": ["exec", "coverband-mcp"],
467
+ "cwd": "/path/to/your/rails/app"
468
+ }
469
+ }
470
+ }
471
+ ```
472
+
473
+ **Option 2: HTTP transport (for remote access or shared servers)**
474
+
475
+ First, start the MCP server in HTTP mode:
476
+
477
+ ```bash
478
+ COVERBAND_MCP_HTTP=true bundle exec rake coverband:mcp
479
+ ```
480
+
481
+ Then configure Claude Desktop to connect via `mcp-remote`:
482
+
483
+ ```json
484
+ {
485
+ "mcpServers": {
486
+ "coverband": {
487
+ "command": "npx",
488
+ "args": ["mcp-remote", "http://localhost:9023"]
489
+ }
490
+ }
491
+ }
492
+ ```
493
+
494
+ #### Configuring Claude Code
495
+
496
+ Add a `.mcp.json` file to your project root:
497
+
498
+ **Option 1: Stdio transport (recommended)**
499
+
500
+ ```json
501
+ {
502
+ "mcpServers": {
503
+ "coverband": {
504
+ "command": "bundle",
505
+ "args": ["exec", "coverband-mcp"]
506
+ }
507
+ }
508
+ }
509
+ ```
510
+
511
+ **Option 2: HTTP transport**
512
+
513
+ ```json
514
+ {
515
+ "mcpServers": {
516
+ "coverband": {
517
+ "command": "npx",
518
+ "args": ["mcp-remote", "http://localhost:9023"]
519
+ }
520
+ }
521
+ }
522
+ ```
523
+
524
+ #### Environment Variables
525
+
526
+ | Variable | Description | Default |
527
+ |----------|-------------|---------|
528
+ | `COVERBAND_MCP_HTTP` | Enable HTTP transport instead of stdio | `false` |
529
+ | `COVERBAND_MCP_PORT` | Port for HTTP server | `9023` |
530
+ | `COVERBAND_REDIS_URL` | Redis URL for coverage data | `localhost:6379` |
531
+
409
532
  ### Dead Method Scanning (ruby 2.6+)
410
533
 
411
534
  Rake task that outputs dead methods based on current coverage data:
data/agents.md ADDED
@@ -0,0 +1,217 @@
1
+ # AI Agent Guidelines for Coverband Development
2
+
3
+ This document provides guidance for AI coding agents working on the Coverband project. Following these guidelines ensures code quality and consistency.
4
+
5
+ ## Project Overview
6
+
7
+ Coverband is a Ruby gem that provides rack middleware to measure production code usage (LOC runtime usage). The project uses:
8
+ - **Ruby**: >= 3.1
9
+ - **Test Framework**: Minitest (version ~> 5.0)
10
+ - **Code Style**: StandardRB
11
+ - **CI/CD**: GitHub Actions
12
+
13
+ ## Testing Requirements
14
+
15
+ ### Running Tests
16
+
17
+ All code changes MUST pass the test suite before considering work complete. The project has multiple test targets:
18
+
19
+ #### Standard Test Suite
20
+ ```bash
21
+ bundle exec rake test
22
+ ```
23
+ This runs the main integration and unit tests located in:
24
+ - `test/integration/**/*_test.rb`
25
+ - `test/coverband/**/*_test.rb`
26
+
27
+ #### Forked Tests
28
+ ```bash
29
+ bundle exec rake forked_tests
30
+ ```
31
+ Runs tests that require forked processes (Rails integration tests):
32
+ - `test/forked/**/*_test.rb`
33
+
34
+ Note: Forked tests are not supported on JRuby.
35
+
36
+ #### Full Test Suite
37
+ ```bash
38
+ bundle exec rake test:all
39
+ ```
40
+ Runs all tests including benchmarks and memory tests.
41
+
42
+ #### Default Task
43
+ ```bash
44
+ bundle exec rake
45
+ ```
46
+ Equivalent to `bundle exec rake test`
47
+
48
+ ### Test Framework Details
49
+
50
+ - Uses **Minitest** with **Mocha** for mocking
51
+ - Test files follow the pattern `*_test.rb`
52
+ - Rails integration tests use **Capybara** for browser testing
53
+ - Forked tests use `minitest-fork_executor` for parallel execution
54
+
55
+ ### Test Configuration
56
+
57
+ Key test dependencies:
58
+ - `minitest ~> 5.0` (pinned for compatibility with minitest-fork_executor)
59
+ - `mocha` for mocking/stubbing
60
+ - `minitest-stub-const` for constant stubbing
61
+ - `capybara` for integration testing
62
+ - `rack-test` for Rack testing
63
+
64
+ ## Code Style Requirements
65
+
66
+ ### StandardRB
67
+
68
+ All code MUST pass StandardRB linting before considering work complete.
69
+
70
+ ```bash
71
+ bundle exec standardrb --format github
72
+ ```
73
+
74
+ #### Auto-fix Style Issues
75
+ ```bash
76
+ bundle exec standardrb --fix
77
+ ```
78
+
79
+ ### Configuration
80
+
81
+ StandardRB configuration is in `.standard.yml`:
82
+ - Ruby version: 3.1
83
+ - Parallel execution enabled
84
+ - Some rules are disabled for compatibility with older Ruby versions
85
+ - Test files have relaxed rules (see `.standard.yml` for specifics)
86
+
87
+ ### Common Style Requirements
88
+
89
+ 1. **Module Inclusions**: Add an empty line after `extend` or `include` statements
90
+ 2. **Frozen String Literals**: All Ruby files should start with `# frozen_string_literal: true`
91
+ 3. **Line Length**: Follow StandardRB defaults (no manual line length configuration needed)
92
+
93
+ ## AI Agent Workflow
94
+
95
+ When making code changes, follow this workflow:
96
+
97
+ ### 1. Make Code Changes
98
+ Implement the requested feature or bug fix.
99
+
100
+ ### 2. Run Tests
101
+ ```bash
102
+ bundle exec rake test
103
+ ```
104
+
105
+ If working on Rails integration or forked features:
106
+ ```bash
107
+ bundle exec rake test:all
108
+ ```
109
+
110
+ ### 3. Fix Any Test Failures
111
+ - Read test output carefully
112
+ - Fix issues in the code
113
+ - Re-run tests until all pass
114
+
115
+ ### 4. Check Code Style
116
+ ```bash
117
+ bundle exec standardrb --format github
118
+ ```
119
+
120
+ ### 5. Auto-fix Style Issues (if any)
121
+ ```bash
122
+ bundle exec standardrb --fix
123
+ ```
124
+
125
+ ### 6. Verify Everything Passes
126
+ ```bash
127
+ bundle exec rake test
128
+ bundle exec standardrb --format github
129
+ ```
130
+
131
+ ### 7. Only Then Consider Work Complete
132
+ Do NOT mark work as complete or hand back to the user until:
133
+ - ✅ All tests pass
134
+ - ✅ StandardRB reports no violations
135
+
136
+ ## Common Issues and Solutions
137
+
138
+ ### Mocha Configuration
139
+ - Use only Mocha 3.x compatible configuration options
140
+ - Valid options: `stubbing_method_unnecessarily`, `stubbing_non_public_method`
141
+ - Invalid options (removed): `stubbing_method_on_nil`, `stubbing_method_on_non_mock_object`, `stubbing_non_existent_method`
142
+
143
+ ### Rails Constant Checks
144
+ When checking if Rails is defined:
145
+ ```ruby
146
+ # ✅ Correct
147
+ if defined?(Rails) && Rails.respond_to?(:version)
148
+ # ...
149
+ end
150
+
151
+ # ❌ Wrong (doesn't protect against undefined constant)
152
+ if Rails&.respond_to?(:version)
153
+ # ...
154
+ end
155
+ ```
156
+
157
+ ### Minitest Version
158
+ - Must use Minitest ~> 5.0 for compatibility with `minitest-fork_executor`
159
+ - Minitest 6.0+ is not compatible with the fork executor
160
+
161
+ ## CI/CD
162
+
163
+ The project uses GitHub Actions for CI. On every push/PR:
164
+ 1. Tests run against multiple Ruby versions (3.1, 3.2, 3.3, 3.4, ruby-head)
165
+ 2. Tests run against multiple Rails versions (7.0, 7.1, 7.2, 8.0)
166
+ 3. Tests run against multiple Redis versions (4, 5, 6, 7)
167
+ 4. StandardRB runs separately to check code style
168
+
169
+ Your local testing should match CI requirements:
170
+ - All tests must pass
171
+ - StandardRB must report no violations
172
+
173
+ ## Additional Notes
174
+
175
+ - The project uses Redis as the default storage backend
176
+ - Rails 8.0 requires Ruby 3.2+
177
+ - Test coverage data is stored in `/tmp` during tests
178
+ - Use `test_helper.rb` for common test setup
179
+ - Use `rails_test_helper.rb` for Rails-specific test setup
180
+
181
+ ## Example Complete Workflow
182
+
183
+ ```bash
184
+ # 1. Make changes to code
185
+ vim lib/coverband/some_file.rb
186
+
187
+ # 2. Run tests
188
+ bundle exec rake test
189
+
190
+ # 3. Fix any failures, re-run until passing
191
+ bundle exec rake test
192
+
193
+ # 4. Check style
194
+ bundle exec standardrb --format github
195
+
196
+ # 5. Auto-fix any style issues
197
+ bundle exec standardrb --fix
198
+
199
+ # 6. Final verification
200
+ bundle exec rake test
201
+ bundle exec standardrb --format github
202
+
203
+ # 7. All green? Work is complete! ✅
204
+ ```
205
+
206
+ ## Quick Reference
207
+
208
+ | Command | Purpose |
209
+ |---------|---------|
210
+ | `bundle exec rake` | Run main test suite (default) |
211
+ | `bundle exec rake test` | Run main test suite |
212
+ | `bundle exec rake forked_tests` | Run forked/Rails integration tests |
213
+ | `bundle exec rake test:all` | Run all tests including benchmarks |
214
+ | `bundle exec standardrb --format github` | Check code style |
215
+ | `bundle exec standardrb --fix` | Auto-fix style issues |
216
+
217
+ Remember: **Tests and style checks must pass before work is considered complete!**
data/bin/coverband-mcp ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "coverband/mcp"
6
+
7
+ # Ensure Coverband is configured
8
+ Coverband.configure unless Coverband.configured?
9
+
10
+ # Security check and warning
11
+ unless Coverband.configuration.mcp_enabled?
12
+ puts "❌ ERROR: MCP is not enabled for security reasons."
13
+ puts "To enable MCP access, configure Coverband with:"
14
+ puts
15
+ puts "Coverband.configure do |config|"
16
+ puts " config.mcp_enabled = true"
17
+ puts " config.mcp_password = 'your-secure-password' # Recommended"
18
+ puts " config.mcp_allowed_environments = %w[development staging production] # As needed"
19
+ puts "end"
20
+ puts
21
+ puts "Or set environment variables:"
22
+ puts " COVERBAND_MCP_PASSWORD=your-secure-password"
23
+ puts
24
+ puts "Current environment: #{(defined?(Rails) && Rails.respond_to?(:env) && Rails.env) || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"}"
25
+ exit 1
26
+ end
27
+
28
+ # Display security status
29
+ puts "🔐 SECURITY STATUS:"
30
+ if Coverband.configuration.mcp_password
31
+ puts " ✅ Authentication: Enabled (password protected)"
32
+ else
33
+ puts " ⚠️ Authentication: DISABLED - Consider setting mcp_password for production use"
34
+ end
35
+ env = (defined?(Rails) && Rails.respond_to?(:env) && Rails.env) || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
36
+ puts " 📍 Environment: #{env}"
37
+ puts " 🌍 Allowed environments: #{Coverband.configuration.mcp_allowed_environments.join(", ")}"
38
+ puts
39
+
40
+ # Start the MCP server with stdio transport
41
+ server = Coverband::MCP::Server.new
42
+ server.run_stdio
data/changes.md CHANGED
@@ -1,3 +1,21 @@
1
+ ### Coverband 6.1.7
2
+
3
+ * add back a running demo site
4
+ * source: https://github.com/danmayer/coverband_rails_example
5
+ * demo site: https://coverband-rails-example.onrender.com/demo
6
+ * Feature: Added MCP (Model Context Protocol) server support for AI assistants
7
+ * Allows AI agents (like Claude) to securely query coverage data, find dead code, and analyze usage
8
+ * Includes HTTP and Stdio transports
9
+ * See `docs/mcp_security.md` and README for setup instructions
10
+ * Feature: Added `Coverband::MCP::HttpHandler` for mounting MCP server in Rack apps
11
+ * Improvement: Added `routes_tracker` alias for `route_tracker` in configuration
12
+ * Improvement: Better error handling when Redis is not available (defaults to NullStore with a warning)
13
+ * Improvement: Updates to `lib/coverband/utils/lines_classifier.rb` to better handle `:nocov:` markers
14
+ * Docs: Added `agents.md` for AI contributor guidelines
15
+ * Docs: Added `docs/mcp_security.md`
16
+ * Fix: Logging to `$stdout` instead of `STDOUT` to avoid warnings
17
+ * Fix: Added GitHub Issue templates and Dependabot config
18
+
1
19
  ### Coverband 6.1.6
2
20
 
3
21
  * Check for Rails::Engine - Stephen Wetzel
@@ -0,0 +1 @@
1
+ {"./lib/coverband/integrations/resque.rb":{"first_updated_at":1768697779,"last_updated_at":1768697779,"file_hash":"977366ad12b0f837e4cc3daf56f0ca80","data":[null,null,1,0,0,null,null,1,0,0,0,0,null,null,1,1,1,0,null,0,null,null,null,null,1,0,0,0,null,0,null,0,0,null,null,1,null]},"./lib/coverband/reporters/web.rb":{"first_updated_at":1768697779,"last_updated_at":1768697779,"file_hash":"2787678b2bc7efcac5241a468cb46153","data":[null,null,1,1,null,null,1,null,0,null,null,1,1,1,1,null,1,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1,0,0,null,null,null,null,1,0,null,null,0,0,null,0,null,null,1,0,0,null,null,1,0,null,0,null,0,0,0,0,0,0,0,0,0,null,0,null,null,null,null,0,0,0,null,0,null,0,null,0,null,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,null,null,null,null,1,0,0,0,0,null,null,null,null,null,0,0,null,null,null,1,0,null,null,null,null,null,1,0,null,null,null,0,0,null,null,null,null,null,1,0,0,null,null,1,0,0,null,0,null,null,null,0,null,null,1,0,null,null,1,0,null,null,1,0,null,null,null,null,null,null,1,0,0,null,null,null,null,null,1,0,0,0,null,0,null,0,null,null,1,0,0,0,0,null,0,null,0,null,null,1,0,0,0,null,0,null,0,null,null,1,0,0,0,0,null,0,null,0,null,null,1,null,1,null,0,null,0,0,null,null,null,null,null,null,null,null,null,null,1,0,null,null,null,null]},"./lib/coverband/utils/html_formatter.rb":{"first_updated_at":1768697779,"last_updated_at":1768697779,"file_hash":"8617750f352ca404c391010aabf5eb02","data":[null,null,1,1,1,1,1,null,null,null,null,null,1,1,1,1,null,1,0,0,0,0,0,null,null,1,0,null,null,1,0,null,null,1,0,null,null,1,0,null,null,1,0,null,0,0,null,0,null,null,null,1,null,1,0,null,null,1,0,0,null,null,0,0,null,null,null,1,0,null,null,1,0,null,null,null,1,0,null,null,1,0,null,null,1,0,null,0,0,0,null,null,1,0,null,null,1,0,null,null,1,0,0,0,0,0,null,null,1,0,null,null,null,1,0,null,0,null,null,null,1,0,null,null,null,0,null,0,null,null,1,0,0,0,0,0,0,null,0,null,null,null,1,0,0,0,0,null,0,null,null,null,1,0,0,null,0,null,null,null,null,1,0,null,null,1,0,0,null,0,null,null,null,1,0,null,null,1,0,0,null,null,1,0,0,null,0,0,null,null,null,null,null]},"./lib/coverband/utils/result.rb":{"first_updated_at":1768697779,"last_updated_at":1768697779,"file_hash":"ff599c81f41c81e32631b763a200528c","data":[null,null,1,1,null,null,null,null,null,null,null,null,1,1,1,1,null,null,1,null,null,1,1,null,1,null,1,1,null,null,null,1,0,null,0,0,null,null,null,null,1,0,null,null,null,1,0,null,null,null,null,1,0,null,0,0,0,0,null,null,null,null,null,null,0,null,null,null,null]},"./lib/coverband/utils/file_list.rb":{"first_updated_at":1768697779,"last_updated_at":1768697779,"file_hash":"fd392626f49763a4a541c7925509ef49","data":[null,null,null,null,null,null,null,null,null,1,1,1,null,1,0,null,0,null,null,null,1,0,null,0,null,null,null,1,0,null,0,null,null,null,1,0,null,0,null,null,null,null,1,0,null,null,null,1,0,null,null,null,null,1,0,null,0,null,null,null,null,1,0,null,null,null,null,1,0,null,0,null,null,1,0,null,null,null,null]},"./lib/coverband/utils/source_file.rb":{"first_updated_at":1768697779,"last_updated_at":1768697779,"file_hash":"fce22b672513bddebaf353ff73650db6","data":[null,null,null,null,null,null,null,null,null,1,1,1,null,null,null,null,null,null,1,null,1,null,1,null,1,null,1,null,1,null,null,1,1,1,null,1,0,0,0,null,0,0,0,0,0,null,null,null,1,0,null,null,null,1,0,null,null,null,1,0,null,null,null,1,0,null,null,null,null,1,0,null,null,null,null,1,0,0,0,0,null,null,null,null,1,null,1,null,1,null,null,1,null,1,null,1,1,null,1,0,0,0,0,0,0,0,0,0,null,null,0,0,0,null,null,null,1,0,0,null,0,null,null,null,1,0,null,null,null,1,null,null,0,null,1,null,null,null,1,0,null,1,null,1,0,null,0,0,null,null,0,0,null,null,null,0,null,null,null,1,0,null,null,null,1,0,null,null,null,1,0,null,0,null,null,0,null,null,1,0,null,null,1,0,null,0,null,null,1,0,null,null,1,0,0,null,null,null,1,0,null,null,null,1,0,null,null,1,0,null,null,1,0,null,null,1,0,null,null,null,null,1,0,null,null,null,null,1,0,null,null,null,1,0,null,null,null,1,0,null,null,null,null,1,0,null,0,0,0,0,0,0,null,null,null,null,null,null,null,1,0,null,null,1,0,null,null,1,null,null,null,1,0,0,null,null,null,null]},"./lib/coverband/utils/lines_classifier.rb":{"first_updated_at":1768697779,"last_updated_at":1768697779,"file_hash":"0edac53af03ce933e33845345200e4db","data":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,1,1,1,1,1,null,1,1,1,null,1,0,null,null,1,0,null,null,0,null,null,1,0,null,null,0,null,null,1,0,null,0,0,0,0,0,0,null,0,null,null,null,null,null,null]},"./lib/coverband/utils/results.rb":{"first_updated_at":1768697779,"last_updated_at":1768697779,"file_hash":"081416e6d4a6a505e75ad15e6d574a07","data":[null,null,null,null,null,1,1,1,1,null,1,0,0,0,null,null,1,0,null,0,0,0,null,0,null,null,1,0,null,0,0,null,0,null,0,null,0,0,null,null,1,0,null,0,0,null,0,null,0,null,0,null,null,1,0,null,0,0,0,null,0,null,null,1,0,0,null,0,null,null,null,1,0,null,null,null,1,0,null,null,1,0,null,null,1,0,null,null,1,null,1,0,null,null,1,0,null,null,1,0,null,null,1,0,null,null,null,null,null,null,null,1,0,null,0,null,null,null,null]},"./lib/coverband/reporters/html_report.rb":{"first_updated_at":1768697779,"last_updated_at":1768697779,"file_hash":"1eb64bf06752d4a576109af8bcec6c69","data":[null,null,1,1,1,1,null,null,1,0,0,null,0,0,0,null,0,null,0,0,null,0,null,null,null,1,0,null,null,null,null,1,0,null,null,1,0,null,null,1,null,1,0,null,null,null,null,null,1,0,null,null,null,null,null,null,null]},"./lib/coverband/reporters/json_report.rb":{"first_updated_at":1768697779,"last_updated_at":1768697779,"file_hash":"4c3d2aa6266411fd5aa23a2e87f4c8cf","data":[null,null,null,null,1,1,1,1,null,null,1,0,0,0,0,0,0,0,0,null,0,null,0,0,null,0,null,null,null,1,0,null,null,1,0,0,null,null,null,null,null,0,0,null,null,null,null,null,null,0,0,null,null,null,null,null,null,0,null,null,1,null,1,0,0,0,0,0,0,null,0,null,null,null,1,0,null,0,0,null,0,null,null,null,null,0,0,0,0,0,0,null,0,0,0,null,null,null,null,0,null,null,null,null,null,null,0,0,0,0,0,0,null,0,null,null,1,null,0,null,null,null,null,null,null,null,null,null,1,0,0,null,0,null,null,null,null,null,null,null,null,null,null,0,0,null,null,null,null,null]},"./test/unique_files.rb":{"first_updated_at":1768697779,"last_updated_at":1768697779,"file_hash":"80447d67747af66d3792f3f9ec0be166","data":[null,null,1,1,1,null,1,null,1,0,0,0,0,0,0,0,null,0,0,0,null,0,0,0,null,null,1,0,0,0,null,null,1,0,null,null,1,1,0,null,null]}}
data/coverband.gemspec CHANGED
@@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
31
31
  }
32
32
 
33
33
  spec.add_development_dependency "benchmark-ips"
34
+ spec.add_development_dependency "benchmark" # Ruby 4.0+ requires explicit dependency
34
35
  spec.add_development_dependency "capybara"
35
36
  spec.add_development_dependency "m"
36
37
  spec.add_development_dependency "memory_profiler"
@@ -38,7 +39,7 @@ Gem::Specification.new do |spec|
38
39
  # note: we are also adding 'spy' as mocha doesn't want us to spy on redis calls...
39
40
  spec.add_development_dependency "spy"
40
41
  # ^^^ probably need a large test cleanup refactor
41
- spec.add_development_dependency "minitest"
42
+ spec.add_development_dependency "minitest", "~> 5.0"
42
43
  spec.add_development_dependency "minitest-fork_executor"
43
44
  spec.add_development_dependency "minitest-stub-const"
44
45
  spec.add_development_dependency "mocha"
@@ -55,6 +56,7 @@ Gem::Specification.new do |spec|
55
56
  # spec.add_development_dependency "minitest-profile"
56
57
  spec.add_development_dependency "webmock"
57
58
  spec.add_development_dependency "dalli" # Default memcached adapter
59
+ spec.add_development_dependency "mcp" # For MCP server support testing
58
60
 
59
61
  # TODO: Remove when other production adapters exist
60
62
  # because the default configuration of redis store, we really do require
@@ -13,7 +13,7 @@ module Coverband
13
13
  TITLE = "Routes"
14
14
 
15
15
  def initialize(options = {})
16
- if Rails&.respond_to?(:version) && Gem::Version.new(Rails.version) < Gem::Version.new("7.1.0")
16
+ if defined?(Rails) && Rails.respond_to?(:version) && Gem::Version.new(Rails.version) < Gem::Version.new("7.1.0")
17
17
  require_relative "../utils/rails6_ext"
18
18
  end
19
19
 
@@ -64,23 +64,14 @@ module Coverband
64
64
  views = redis_store.hgetall(tracker_key)
65
65
  normalized_views = {}
66
66
  views.each_pair do |view, time|
67
- roots.each do |root|
68
- view = view.gsub(root, "")
69
- end
70
- normalized_views[view] = time
67
+ normalized_view = normalize_path(view)
68
+ normalized_views[normalized_view] = time
71
69
  end
72
70
  normalized_views
73
71
  end
74
72
 
75
73
  def all_keys
76
- all_views = []
77
- target.each do |view|
78
- roots.each do |root|
79
- view = view.gsub(root, "")
80
- end
81
- all_views << view
82
- end
83
- all_views.uniq
74
+ target.map { |view| normalize_path(view) }.uniq
84
75
  end
85
76
 
86
77
  def unused_keys(used_views = nil)
@@ -106,9 +97,26 @@ module Coverband
106
97
  @ignore_patterns.none? { |pattern| file.match?(pattern) }
107
98
  end
108
99
 
100
+ def normalize_path(view)
101
+ normalized = view.dup
102
+ original_length = normalized.length
103
+
104
+ roots.each do |root|
105
+ normalized = normalized.gsub(root, "")
106
+ end
107
+
108
+ # Only remove leading slash if we actually modified the path by removing a root
109
+ # This ensures consistent relative paths after root removal
110
+ if normalized.length < original_length
111
+ normalized.sub(/^\//, "")
112
+ else
113
+ normalized
114
+ end
115
+ end
116
+
109
117
  def concrete_target
110
118
  if defined?(Rails.application)
111
- Dir.glob("#{@project_directory}/**/app/{views,components}/**/*.html.{erb,haml,slim}")
119
+ Dir.glob("#{@project_directory}/{,packs,engines/*}/app/{views,components}/**/*.html.{erb,haml,slim}")
112
120
  else
113
121
  []
114
122
  end
@@ -14,13 +14,14 @@ module Coverband
14
14
  :view_tracker, :defer_eager_loading_data,
15
15
  :track_routes, :track_redirect_routes, :route_tracker,
16
16
  :track_translations, :translations_tracker,
17
- :trackers, :csp_policy, :hide_settings
17
+ :trackers, :csp_policy, :hide_settings,
18
+ :mcp_enabled
18
19
 
19
20
  attr_writer :logger, :s3_region, :s3_bucket, :s3_access_key_id,
20
21
  :s3_secret_access_key, :password, :api_key, :service_url, :coverband_timeout, :service_dev_mode,
21
22
  :service_test_mode, :process_type, :track_views, :redis_url,
22
23
  :background_reporting_sleep_seconds, :reporting_wiggle,
23
- :send_deferred_eager_loading_data, :paged_reporting
24
+ :send_deferred_eager_loading_data, :paged_reporting, :mcp_allowed_environments, :mcp_password
24
25
 
25
26
  attr_reader :track_gems, :ignore
26
27
 
@@ -31,19 +32,19 @@ module Coverband
31
32
  # * Perhaps detect heroku deployment ENV var opposed to tasks?
32
33
  #####
33
34
  IGNORE_TASKS = ["coverband:clear",
34
- "coverband:coverage",
35
- "coverband:coverage_server",
36
- "assets:precompile",
37
- "webpacker:compile",
38
- "db:version",
39
- "db:create",
40
- "db:drop",
41
- "db:seed",
42
- "db:setup",
43
- "db:test:prepare",
44
- "db:structure:dump",
45
- "db:structure:load",
46
- "db:version"]
35
+ "coverband:coverage",
36
+ "coverband:coverage_server",
37
+ "assets:precompile",
38
+ "webpacker:compile",
39
+ "db:version",
40
+ "db:create",
41
+ "db:drop",
42
+ "db:seed",
43
+ "db:setup",
44
+ "db:test:prepare",
45
+ "db:structure:dump",
46
+ "db:structure:load",
47
+ "db:version"]
47
48
 
48
49
  # Heroku when building assets runs code from a dynamic directory
49
50
  # /tmp was added to avoid coverage from /tmp/build directories during
@@ -91,6 +92,11 @@ module Coverband
91
92
  @csp_policy = false
92
93
  @hide_settings = false
93
94
 
95
+ # MCP (Model Context Protocol) security settings
96
+ @mcp_enabled = false
97
+ @mcp_password = nil
98
+ @mcp_allowed_environments = %w[development test]
99
+
94
100
  # coverband service settings
95
101
  @api_key = nil
96
102
  @service_url = nil
@@ -144,14 +150,40 @@ module Coverband
144
150
  @logger ||= if defined?(Rails.logger) && Rails.logger
145
151
  Rails.logger
146
152
  else
147
- Logger.new(STDOUT)
153
+ Logger.new($stdout)
148
154
  end
149
155
  end
150
156
 
157
+ # Alias for backward compatibility - track_key uses :routes_tracker symbol
158
+ def routes_tracker
159
+ route_tracker
160
+ end
161
+
151
162
  def password
152
163
  @password || ENV["COVERBAND_PASSWORD"]
153
164
  end
154
165
 
166
+ def mcp_enabled?
167
+ # MCP is disabled by default and explicitly controlled
168
+ return false unless @mcp_enabled
169
+
170
+ # Check if current environment is allowed
171
+ current_env = (defined?(Rails) && Rails.respond_to?(:env) && Rails.env) ||
172
+ ENV["RAILS_ENV"] ||
173
+ ENV["RACK_ENV"] ||
174
+ "development"
175
+
176
+ mcp_allowed_environments.include?(current_env.to_s)
177
+ end
178
+
179
+ def mcp_password
180
+ @mcp_password || ENV["COVERBAND_MCP_PASSWORD"]
181
+ end
182
+
183
+ def mcp_allowed_environments
184
+ @mcp_allowed_environments || %w[development test]
185
+ end
186
+
155
187
  # The adjustments here either protect the redis or service from being overloaded
156
188
  # the tradeoff being the delay in when reporting data is available
157
189
  # if running your own redis increasing this number reduces load on the redis CPU
@@ -180,7 +212,14 @@ module Coverband
180
212
  require "coverband/adapters/web_service_store"
181
213
  Coverband::Adapters::WebServiceStore.new(service_url)
182
214
  else
183
- Coverband::Adapters::RedisStore.new(Redis.new(url: redis_url), redis_store_options)
215
+ begin
216
+ Coverband::Adapters::RedisStore.new(Redis.new(url: redis_url), redis_store_options)
217
+ rescue Redis::CannotConnectError => error
218
+ logger.info "Redis is not available (#{error}), defaulting to NullStore"
219
+ logger.info "If this is intended, please explicitly configure your store: config.store = Coverband::Adapters::FileStore.new('log/coverage')"
220
+ require "coverband/adapters/null_store"
221
+ Coverband::Adapters::NullStore.new
222
+ end
184
223
  end
185
224
  end
186
225
 
@@ -242,7 +281,7 @@ module Coverband
242
281
  @all_root_patterns ||= all_root_paths.map { |path| /^#{path}/ }.freeze
243
282
  end
244
283
 
245
- SKIPPED_SETTINGS = %w[@s3_secret_access_key @store @api_key @password]
284
+ SKIPPED_SETTINGS = %w[@s3_secret_access_key @store @api_key @password @mcp_password]
246
285
  def to_h
247
286
  instance_variables
248
287
  .each_with_object({}) do |var, hash|