coverband 6.2.2 → 7.0.0.rc.1
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 +4 -4
- data/.github/workflows/diagram.yml +2 -2
- data/.github/workflows/main.yml +35 -7
- data/Gemfile +15 -0
- data/Gemfile.rails7.0 +11 -0
- data/Gemfile.rails7.1 +5 -1
- data/Gemfile.rails7.2 +4 -0
- data/Gemfile.rails8.0 +14 -0
- data/README.md +218 -12
- data/Rakefile +16 -5
- data/changes.md +59 -0
- data/coverband.gemspec +17 -12
- data/lib/coverband/adapters/active_support_cache_store.rb +54 -0
- data/lib/coverband/adapters/base.rb +42 -10
- data/lib/coverband/adapters/file_store.rb +2 -2
- data/lib/coverband/adapters/hash_redis_store.rb +22 -3
- data/lib/coverband/adapters/null_store.rb +0 -4
- data/lib/coverband/adapters/redis_cleanup.rb +101 -0
- data/lib/coverband/adapters/redis_store.rb +32 -65
- data/lib/coverband/adapters/session_coverage.rb +172 -0
- data/lib/coverband/adapters/stdout_store.rb +0 -4
- data/lib/coverband/adapters/tracker_storage/base.rb +79 -0
- data/lib/coverband/adapters/tracker_storage/document_repository.rb +49 -0
- data/lib/coverband/adapters/tracker_storage/factory.rb +57 -0
- data/lib/coverband/adapters/tracker_storage/redis_hash_repository.rb +127 -0
- data/lib/coverband/collectors/abstract_tracker.rb +135 -51
- data/lib/coverband/collectors/query_burst_tracker.rb +72 -19
- data/lib/coverband/collectors/route_tracker.rb +6 -0
- data/lib/coverband/collectors/tracker_registry.rb +63 -0
- data/lib/coverband/collectors/translation_tracker.rb +6 -0
- data/lib/coverband/collectors/view_tracker.rb +11 -5
- data/lib/coverband/configuration.rb +95 -131
- data/lib/coverband/mcp/http_handler.rb +1 -1
- data/lib/coverband/mcp/server.rb +4 -1
- data/lib/coverband/mcp/tools/get_storage_health.rb +28 -0
- data/lib/coverband/reporters/base.rb +0 -2
- data/lib/coverband/reporters/html_report.rb +2 -3
- data/lib/coverband/reporters/json_report.rb +2 -2
- data/lib/coverband/reporters/web.rb +72 -15
- data/lib/coverband/storage/document.rb +173 -0
- data/lib/coverband/storage/generation.rb +165 -0
- data/lib/coverband/storage/generation_change.rb +53 -0
- data/lib/coverband/storage/generation_coordinator.rb +145 -0
- data/lib/coverband/storage/io_guard.rb +29 -0
- data/lib/coverband/storage/read_fallback.rb +30 -0
- data/lib/coverband/storage/redis_target.rb +74 -0
- data/lib/coverband/storage/session.rb +527 -0
- data/lib/coverband/storage/target.rb +117 -0
- data/lib/coverband/storage/writer.rb +194 -0
- data/lib/coverband/storage_health.rb +90 -0
- data/lib/coverband/utils/configuration_template.rb +14 -1
- data/lib/coverband/utils/railtie.rb +2 -2
- data/lib/coverband/utils/result.rb +5 -5
- data/lib/coverband/utils/results.rb +32 -16
- data/lib/coverband/utils/source_file.rb +17 -27
- data/lib/coverband/utils/tasks.rb +71 -6
- data/lib/coverband/version.rb +1 -1
- data/lib/coverband.rb +98 -20
- data/public/application.css +540 -146
- data/test/benchmarks/benchmark.rake +13 -0
- data/test/coverband/adapters/active_support_cache_store_test.rb +613 -0
- data/test/coverband/adapters/capabilities_test.rb +80 -0
- data/test/coverband/adapters/redis_store_test.rb +63 -2
- data/test/coverband/adapters/solid_cache_store_test.rb +181 -0
- data/test/coverband/adapters/tracker_storage_test.rb +170 -0
- data/test/coverband/collectors/query_burst_tracker_test.rb +155 -10
- data/test/coverband/collectors/route_tracker_test.rb +2 -13
- data/test/coverband/collectors/tracker_registry_test.rb +218 -0
- data/test/coverband/collectors/translation_tracker_test.rb +76 -16
- data/test/coverband/collectors/view_tracker_test.rb +49 -12
- data/test/coverband/configuration_test.rb +36 -38
- data/test/coverband/mcp/server_test.rb +2 -1
- data/test/coverband/mcp/tools/get_storage_health_test.rb +41 -0
- data/test/coverband/reporters/html_test.rb +26 -0
- data/test/coverband/reporters/json_test.rb +53 -0
- data/test/coverband/reporters/web_test.rb +96 -0
- data/test/coverband/storage/generation_coordinator_test.rb +191 -0
- data/test/coverband/storage/io_guard_test.rb +127 -0
- data/test/coverband/storage/protocol_conformance_test.rb +725 -0
- data/test/coverband/storage_health_test.rb +99 -0
- data/test/coverband/track_key_test.rb +8 -0
- data/test/coverband/utils/cleanup_tasks_test.rb +188 -0
- data/test/coverband/utils/file_list_test.rb +2 -2
- data/test/coverband/utils/result_test.rb +4 -4
- data/test/coverband/utils/results_test.rb +100 -10
- data/test/coverband/utils/source_file_test.rb +40 -6
- data/test/forked/rails_cache_full_stack_test.rb +64 -0
- data/test/forked/rails_full_stack_test.rb +7 -0
- data/test/integration/full_stack_deferred_eager_test.rb +0 -1
- data/test/integration/full_stack_send_deferred_eager_test.rb +0 -1
- data/test/integration/full_stack_test.rb +0 -1
- data/test/integration/mcp_integration_test.rb +4 -2
- data/test/integration/standalone_json_report_test.rb +46 -0
- data/test/integration/without_redis_test.rb +76 -0
- data/test/rails7_dummy/config/application.rb +1 -0
- data/test/rails7_dummy/config/coverband_missing_redis.rb +0 -2
- data/test/rails7_dummy/config/coverband_rails_cache.rb +16 -0
- data/test/rails8_dummy/config/application.rb +1 -0
- data/test/rails8_dummy/config/coverband_rails_cache.rb +3 -0
- data/test/rails_test_helper.rb +2 -1
- data/test/test_helper.rb +5 -17
- data/views/file_list.erb +2 -2
- data/views/source_file.erb +4 -3
- metadata +51 -29
- data/lib/coverband/adapters/memcached_store.rb +0 -85
- data/lib/coverband/adapters/web_service_store.rb +0 -152
- data/lib/coverband/collectors/view_tracker_service.rb +0 -59
- data/test/coverband/adapters/memecached_store_test.rb +0 -26
- data/test/coverband/adapters/web_service_store_test.rb +0 -56
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 122ec261f93431af71ca87c0113241a9197af2daa07c3d32013b98d893cd3a78
|
|
4
|
+
data.tar.gz: 785fa634bdba056657cb2b0e358f387decb4aa020faee0f50580dedd929ddcaa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 990b9e30e696866dd1e68e8c26b394608c3697f8564ea0ff2683592d1b02fabaae5a0dcc0aa4e3c7a12e73cef259aa2f441fd154a910cb7694c0d10acfb9dab7
|
|
7
|
+
data.tar.gz: 43c6e3982076da3771c7a961a9ac7c409c8576731d8e76b4a943a67323ff4e3391f9b96de89ba5050ee1c2868a436b25c9a386265d643bf82b053c6b65953f20
|
|
@@ -6,7 +6,7 @@ on:
|
|
|
6
6
|
- main
|
|
7
7
|
jobs:
|
|
8
8
|
get_data:
|
|
9
|
-
runs-on: ubuntu-
|
|
9
|
+
runs-on: ubuntu-24.04
|
|
10
10
|
steps:
|
|
11
11
|
- name: Checkout code
|
|
12
12
|
uses: actions/checkout@master
|
|
@@ -14,4 +14,4 @@ jobs:
|
|
|
14
14
|
uses: githubocto/repo-visualizer@main
|
|
15
15
|
with:
|
|
16
16
|
excluded_paths: "ignore,.github"
|
|
17
|
-
branch: diagram
|
|
17
|
+
branch: diagram
|
data/.github/workflows/main.yml
CHANGED
|
@@ -13,8 +13,8 @@ jobs:
|
|
|
13
13
|
fail-fast: false
|
|
14
14
|
matrix:
|
|
15
15
|
# need to figure out how to use redis on macos github actions
|
|
16
|
-
# os: [ubuntu, macos]
|
|
17
|
-
os: [ubuntu]
|
|
16
|
+
# os: [ubuntu-24.04, macos-15]
|
|
17
|
+
os: [ubuntu-24.04]
|
|
18
18
|
# remove until I sort out CI issues for truffle
|
|
19
19
|
# truffleruby,
|
|
20
20
|
# truffleruby-head,
|
|
@@ -25,9 +25,11 @@ jobs:
|
|
|
25
25
|
redis-version: [4, 5, 6, 7]
|
|
26
26
|
exclude:
|
|
27
27
|
# Rails 8 requires ruby 3.2+.
|
|
28
|
-
- gemfile: '
|
|
28
|
+
- gemfile: 'Gemfile.rails8.0'
|
|
29
29
|
ruby: '3.1'
|
|
30
|
-
runs-on: ${{ matrix.os }}
|
|
30
|
+
runs-on: ${{ matrix.os }}
|
|
31
|
+
env:
|
|
32
|
+
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
|
31
33
|
services:
|
|
32
34
|
redis:
|
|
33
35
|
image: redis:${{ matrix.redis-version }}
|
|
@@ -38,6 +40,15 @@ jobs:
|
|
|
38
40
|
--health-interval 10s
|
|
39
41
|
--health-timeout 5s
|
|
40
42
|
--health-retries 5
|
|
43
|
+
memcached:
|
|
44
|
+
image: memcached:1.6
|
|
45
|
+
ports:
|
|
46
|
+
- 11211:11211
|
|
47
|
+
options: >-
|
|
48
|
+
--health-cmd "timeout 2 bash -c '</dev/tcp/127.0.0.1/11211'"
|
|
49
|
+
--health-interval 10s
|
|
50
|
+
--health-timeout 5s
|
|
51
|
+
--health-retries 5
|
|
41
52
|
steps:
|
|
42
53
|
- uses: actions/checkout@v7
|
|
43
54
|
- uses: ruby/setup-ruby@v1
|
|
@@ -45,9 +56,26 @@ jobs:
|
|
|
45
56
|
ruby-version: ${{ matrix.ruby }}
|
|
46
57
|
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
|
47
58
|
- run: bundle exec rake test:all
|
|
59
|
+
env:
|
|
60
|
+
# the memcached adapter has an ENV gate so local runs need no service,
|
|
61
|
+
# which also meant CI never exercised it
|
|
62
|
+
COVERBAND_MEMCACHED: "true"
|
|
48
63
|
- run: "RUBYOPT='--enable=frozen-string-literal --debug=frozen-string-literal' bundle exec rake"
|
|
64
|
+
env:
|
|
65
|
+
COVERBAND_MEMCACHED: "true"
|
|
66
|
+
# Solid Cache needs Rails 7.2+, and its engine changes process wide state,
|
|
67
|
+
# so it runs on its own, only where it is available.
|
|
68
|
+
#
|
|
69
|
+
# Its gems sit in an optional bundler group, which bundler-cache does not
|
|
70
|
+
# install, so the group is installed here rather than assumed present.
|
|
71
|
+
- name: Solid Cache adapter tests
|
|
72
|
+
if: matrix.gemfile == 'Gemfile.rails8.0'
|
|
73
|
+
run: |
|
|
74
|
+
bundle config set --local with solid_cache
|
|
75
|
+
bundle install --jobs 4
|
|
76
|
+
bundle exec rake test:solid_cache
|
|
49
77
|
starndardrb:
|
|
50
|
-
runs-on: ubuntu-
|
|
78
|
+
runs-on: ubuntu-24.04
|
|
51
79
|
steps:
|
|
52
80
|
- uses: actions/checkout@v7
|
|
53
81
|
- uses: ruby/setup-ruby@v1
|
|
@@ -56,10 +84,10 @@ jobs:
|
|
|
56
84
|
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
|
57
85
|
- run: bundle exec standardrb --format github
|
|
58
86
|
javascript:
|
|
59
|
-
runs-on: ubuntu-
|
|
87
|
+
runs-on: ubuntu-24.04
|
|
60
88
|
steps:
|
|
61
89
|
- uses: actions/checkout@v7
|
|
62
|
-
- uses: actions/setup-node@
|
|
90
|
+
- uses: actions/setup-node@v7
|
|
63
91
|
with:
|
|
64
92
|
node-version: "20"
|
|
65
93
|
- run: node --test test/javascript/*.test.mjs
|
data/Gemfile
CHANGED
|
@@ -11,3 +11,18 @@ gem "webrick"
|
|
|
11
11
|
|
|
12
12
|
# Required for Ruby 3.4+ (extracted from stdlib)
|
|
13
13
|
gem "cgi"
|
|
14
|
+
|
|
15
|
+
# ActiveSupport 8.1's MemCacheStore passes positional options to ConnectionPool,
|
|
16
|
+
# which connection_pool 3.x turned into keyword arguments. Pinned so the
|
|
17
|
+
# memcached backed cache adapter tests can actually run.
|
|
18
|
+
gem "connection_pool", "~> 2.5"
|
|
19
|
+
|
|
20
|
+
# Solid Cache is a central target for the ActiveSupport::Cache adapter: it is
|
|
21
|
+
# how Coverband reaches Postgres, MySQL, and SQLite. Needs Rails 7.2+.
|
|
22
|
+
#
|
|
23
|
+
# Kept out of the default group on purpose: the dummy Rails apps require every
|
|
24
|
+
# default gem, and Solid Cache's engine expects an ActiveJob they do not load.
|
|
25
|
+
group :solid_cache, optional: true do
|
|
26
|
+
gem "solid_cache"
|
|
27
|
+
gem "sqlite3"
|
|
28
|
+
end
|
data/Gemfile.rails7.0
CHANGED
|
@@ -9,5 +9,16 @@ gem "haml"
|
|
|
9
9
|
gem "slim"
|
|
10
10
|
gem "webrick"
|
|
11
11
|
|
|
12
|
+
# ActiveSupport's MemCacheStore passes positional options to ConnectionPool,
|
|
13
|
+
# which connection_pool 3.x turned into keyword arguments.
|
|
14
|
+
gem "connection_pool", "~> 2.5"
|
|
15
|
+
|
|
16
|
+
# Rack 2 requires ostruct, which Ruby 4 no longer ships as a default gem.
|
|
17
|
+
gem "ostruct"
|
|
18
|
+
|
|
12
19
|
# Required for Ruby 3.4+ (extracted from stdlib)
|
|
13
20
|
gem "cgi"
|
|
21
|
+
|
|
22
|
+
# railties 7.0 requires tsort without declaring it, and Ruby 4.1 no longer
|
|
23
|
+
# ships it as a default gem.
|
|
24
|
+
gem "tsort"
|
data/Gemfile.rails7.1
CHANGED
|
@@ -9,5 +9,9 @@ gem "haml"
|
|
|
9
9
|
gem "slim"
|
|
10
10
|
gem "webrick"
|
|
11
11
|
|
|
12
|
+
# ActiveSupport's MemCacheStore passes positional options to ConnectionPool,
|
|
13
|
+
# which connection_pool 3.x turned into keyword arguments.
|
|
14
|
+
gem "connection_pool", "~> 2.5"
|
|
15
|
+
|
|
12
16
|
# Required for Ruby 3.4+ (extracted from stdlib)
|
|
13
|
-
gem "cgi"
|
|
17
|
+
gem "cgi"
|
data/Gemfile.rails7.2
CHANGED
|
@@ -9,5 +9,9 @@ gem "haml"
|
|
|
9
9
|
gem "slim"
|
|
10
10
|
gem "webrick"
|
|
11
11
|
|
|
12
|
+
# ActiveSupport's MemCacheStore passes positional options to ConnectionPool,
|
|
13
|
+
# which connection_pool 3.x turned into keyword arguments.
|
|
14
|
+
gem "connection_pool", "~> 2.5"
|
|
15
|
+
|
|
12
16
|
# Required for Ruby 3.4+ (extracted from stdlib)
|
|
13
17
|
gem "cgi"
|
data/Gemfile.rails8.0
CHANGED
|
@@ -9,5 +9,19 @@ gem "haml"
|
|
|
9
9
|
gem "slim"
|
|
10
10
|
gem "webrick"
|
|
11
11
|
|
|
12
|
+
# ActiveSupport's MemCacheStore passes positional options to ConnectionPool,
|
|
13
|
+
# which connection_pool 3.x turned into keyword arguments.
|
|
14
|
+
gem "connection_pool", "~> 2.5"
|
|
15
|
+
|
|
12
16
|
# Required for Ruby 3.4+ (extracted from stdlib)
|
|
13
17
|
gem "cgi"
|
|
18
|
+
|
|
19
|
+
# Solid Cache is a central target for the ActiveSupport::Cache adapter: it is
|
|
20
|
+
# how Coverband reaches Postgres, MySQL, and SQLite. Needs Rails 7.2+.
|
|
21
|
+
#
|
|
22
|
+
# Kept out of the default group on purpose: the dummy Rails apps require every
|
|
23
|
+
# default gem, and Solid Cache's engine expects an ActiveJob they do not load.
|
|
24
|
+
group :solid_cache, optional: true do
|
|
25
|
+
gem "solid_cache"
|
|
26
|
+
gem "sqlite3"
|
|
27
|
+
end
|
data/README.md
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
# Coverband
|
|
4
4
|
|
|
5
5
|
[](https://github.com/danmayer/coverband/actions)
|
|
6
|
-
[](https://coveralls.io/github/danmayer/coverband?branch=main)
|
|
7
6
|
[](https://codeclimate.com/github/danmayer/coverband/maintainability)
|
|
8
7
|
[](https://discord.gg/KAH38EV)
|
|
9
8
|
|
|
@@ -69,12 +68,15 @@ The `rediss://` scheme works with:
|
|
|
69
68
|
|
|
70
69
|
## Gem Installation
|
|
71
70
|
|
|
72
|
-
Add
|
|
71
|
+
Add these lines to your application's `Gemfile`, remember to `bundle install` after updating:
|
|
73
72
|
|
|
74
|
-
```
|
|
73
|
+
```ruby
|
|
75
74
|
gem 'coverband'
|
|
75
|
+
gem 'redis' # the default store
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
Redis is the default store and needs no configuration beyond a reachable Redis. Since Coverband 7.0 the `redis` gem is no longer installed for you, so add it yourself as above. If you would rather keep coverage somewhere else (Solid Cache, memcached, files), leave out `redis` and set `config.store`, see [ActiveSupport::Cache Store](#activesupportcache-store). Without the `redis` gem and without a configured store, Coverband logs a warning at boot and records nothing. Upgrading from 6.x? See [Upgrading to 7.0](#upgrading-to-70).
|
|
79
|
+
|
|
78
80
|
### No custom code or middleware required
|
|
79
81
|
|
|
80
82
|
With older versions of Coverband, projects would report to redis using rack or sidekiq middleware. After Coverband 4.0, this should no longer be required and could cause performance issues. Reporting to redis is now automatically done within a background thread with no custom code needed.
|
|
@@ -97,6 +99,46 @@ use Coverband::BackgroundMiddleware
|
|
|
97
99
|
run ActionController::Dispatcher.new
|
|
98
100
|
```
|
|
99
101
|
|
|
102
|
+
## Upgrading to 7.0
|
|
103
|
+
|
|
104
|
+
7.0 is a major release with breaking changes. Most apps only need the first two steps. The full list is in the [changelog](https://github.com/danmayer/coverband/blob/main/changes.md).
|
|
105
|
+
|
|
106
|
+
1. **Add the `redis` gem if you use Redis.** Coverband no longer installs it for you. Redis is still the default store and still needs no other configuration once the gem is in your Gemfile:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
gem 'coverband'
|
|
110
|
+
gem 'redis'
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Without it, and without a configured `config.store`, Coverband logs a warning at boot and records nothing.
|
|
114
|
+
|
|
115
|
+
2. **Coverage starts fresh.** The storage format changed, so data recorded by 6.x is ignored rather than migrated. Once you are happy with the new data, delete the old keys with `rake coverband:clear_legacy` (Redis only).
|
|
116
|
+
|
|
117
|
+
3. **Delete removed settings from `config/coverband.rb`.** These now do nothing. Assigning one logs a warning naming the setting, but the app still boots:
|
|
118
|
+
|
|
119
|
+
| Setting | What to do |
|
|
120
|
+
| --- | --- |
|
|
121
|
+
| `reporter` | Delete it. It has had no effect since Coverband 5.0. |
|
|
122
|
+
| `api_key`, `service_url`, `coverband_timeout`, `service_dev_mode`, `service_test_mode`, `process_type` | Delete them, see the coverband.io step below. |
|
|
123
|
+
| `s3_region`, `s3_bucket`, `s3_access_key_id`, `s3_secret_access_key`, `track_gems`, `gem_details`, `simulate_oneshot_lines_coverage` | Delete them. They were already no-ops. |
|
|
124
|
+
|
|
125
|
+
4. **coverband.io users: configure a store.** coverband.io is no longer maintained, so reporting to it (`WebServiceStore`) has been removed, along with `config/coverband_service.rb` and the `COVERBAND_API_KEY`, `COVERBAND_URL`, `COVERBAND_ENABLE_DEV_MODE`, `COVERBAND_ENABLE_TEST_MODE`, and `PROCESS_TYPE` environment variables. Move any settings from `config/coverband_service.rb` into `config/coverband.rb` and use Redis (step 1) or an [ActiveSupport::Cache store](#activesupportcache-store).
|
|
126
|
+
|
|
127
|
+
5. **`MemcachedStore` users: switch to `ActiveSupportCacheStore`.** `MemcachedStore` has been removed. The replacement takes the same cache and adds tracker support:
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
# before
|
|
131
|
+
config.store = Coverband::Adapters::MemcachedStore.new(cache, memcached_namespace: "myapp")
|
|
132
|
+
# after
|
|
133
|
+
config.store = Coverband::Adapters::ActiveSupportCacheStore.new(cache, cache_namespace: "myapp")
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
6. **Custom code against Coverband internals.** Only relevant if you wrote a tracker or call adapters directly:
|
|
137
|
+
- In a custom tracker, use `storage` instead of `redis_store`.
|
|
138
|
+
- Instead of `store.raw_store`, use `store.cleanup` (`clear_legacy!`, `clear_orphans!`), which returns `nil` for stores that cannot enumerate keys.
|
|
139
|
+
- Replace calls to the misspelled `Coverband::Utils::Results#runtime_relavent_lines` with `#runtime_relevant_lines`; the old spelling was removed in 7.0.
|
|
140
|
+
- `Coverband::Utils::SourceFile` and `Coverband::Utils::Result` take stored report records (`{"data" => [...]}`) rather than bare line arrays.
|
|
141
|
+
|
|
100
142
|
## Coverband Web UI
|
|
101
143
|
|
|
102
144
|

|
|
@@ -129,15 +171,20 @@ run ActionController::Dispatcher.new
|
|
|
129
171
|
|
|
130
172
|
The columns in the web UI are as follows:
|
|
131
173
|
|
|
132
|
-
- **% covered** - Percentage of
|
|
133
|
-
- **% runtime** - Percentage of the runtime lines
|
|
174
|
+
- **% covered** - Percentage of relevant lines covered, during either eager loading or runtime
|
|
175
|
+
- **% runtime** - Percentage of the runtime relevant lines that were hit at runtime. Runtime relevant lines are the relevant lines that were *not* hit while the application eager loaded, so this is the share of the code that only real usage can exercise (method bodies, branches, etc.) which real usage actually exercised
|
|
134
176
|
- **Lines** - Total lines in the file including lines unreachable or uncover-able. An unreachable line would be an empty line with no code, comments, or `end` statements.
|
|
135
177
|
- **Relevant lines** - Lines that are coverable, i.e. not empty
|
|
136
|
-
- **Lines
|
|
178
|
+
- **Lines covered** - Relevant lines hit during either eager loading or runtime
|
|
179
|
+
- **Lines runtime** - Relevant lines hit at least once at runtime
|
|
137
180
|
- **Lines missed** - Relevant lines not covered
|
|
138
181
|
- **Avg hits/line** - Total of coverage to the file divided by relevant lines.
|
|
139
182
|
|
|
140
|
-
|
|
183
|
+
The individual file view also lists the file's **runtime relevant lines**, the denominator of **% runtime**. A file only reports 100% runtime when every one of its runtime relevant lines was hit at runtime. If a file was never eager loaded (the default in development), every relevant line is runtime relevant, so **% runtime** matches **% covered**.
|
|
184
|
+
|
|
185
|
+
**Lines runtime** can include load time lines such as class and method definitions. Those lines are hit at runtime when a process loads the file lazily after Coverband switched to runtime coverage, e.g. a console, runner, or job process that does not eager load. Such lines still count as eager loaded for **% runtime** as long as any process loaded them during eager loading.
|
|
186
|
+
|
|
187
|
+
When viewing an individual file, a line of code such as a class or method definition may appear green because it is eager loaded by the application, but still not be hit at all in runtime by actual users. Each line's badge shows its `load` (eager loading) and `runtime` hit counts separately.
|
|
141
188
|
|
|
142
189
|

|
|
143
190
|
#### Coverage first seen and Last activity recorded
|
|
@@ -233,7 +280,7 @@ Take Coverband for a spin on the live Heroku deployed [Coverband Demo](https://c
|
|
|
233
280
|
If you need to configure Coverband, this can be done by creating a `config/coverband.rb` file relative to your project root.
|
|
234
281
|
|
|
235
282
|
- See [lib/coverband/configuration.rb](https://github.com/danmayer/coverband/blob/main/lib/coverband/configuration.rb) for all options
|
|
236
|
-
- By default Coverband will try to store data to Redis \* Redis endpoint is looked for in this order: `ENV['COVERBAND_REDIS_URL']`, `ENV['REDIS_URL']`, or `localhost`
|
|
283
|
+
- By default Coverband will try to store data to Redis (when the `redis` gem is installed) \* Redis endpoint is looked for in this order: `ENV['COVERBAND_REDIS_URL']`, `ENV['REDIS_URL']`, or `localhost`
|
|
237
284
|
|
|
238
285
|
Below is an example config file for a Rails 5 app:
|
|
239
286
|
|
|
@@ -358,6 +405,61 @@ For each tracked key, the Query Burst Tracker reports:
|
|
|
358
405
|
|
|
359
406
|
Tip: use this tracker in production-like traffic, where query patterns are most realistic.
|
|
360
407
|
|
|
408
|
+
### Writing a Custom Tracker
|
|
409
|
+
|
|
410
|
+
Trackers can be shipped by an application or a separate gem without changing Coverband. A tracker subclasses `Coverband::Collectors::AbstractTracker`, registers itself once when its file is required, and is initialized at Rails boot when its enablement block returns true.
|
|
411
|
+
|
|
412
|
+
```ruby
|
|
413
|
+
# lib/my_app/coverband/feature_flag_tracker.rb
|
|
414
|
+
class FeatureFlagTracker < Coverband::Collectors::AbstractTracker
|
|
415
|
+
# required: the path this tracker reports on inside the Coverband web report,
|
|
416
|
+
# and the label for its tab
|
|
417
|
+
REPORT_ROUTE = "feature_flag_tracker"
|
|
418
|
+
TITLE = "Feature Flags"
|
|
419
|
+
|
|
420
|
+
def railtie!
|
|
421
|
+
tracker = self
|
|
422
|
+
ActiveSupport::Notifications.subscribe("feature_flag.my_app") do |_name, _start, _finish, _id, payload|
|
|
423
|
+
tracker.track_key(payload.fetch(:key))
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
private
|
|
428
|
+
|
|
429
|
+
def concrete_target
|
|
430
|
+
MyApp::FeatureFlags.all.map(&:key)
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
Coverband::Configuration.add_tracker_flag(:track_feature_flags, default: false)
|
|
435
|
+
Coverband::Collectors::TrackerRegistry.register(
|
|
436
|
+
:feature_flag_tracker,
|
|
437
|
+
tracker_class: FeatureFlagTracker,
|
|
438
|
+
enabled: ->(config) { config.track_feature_flags }
|
|
439
|
+
)
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
Require the tracker before enabling it in `config/coverband.rb`:
|
|
443
|
+
|
|
444
|
+
```ruby
|
|
445
|
+
require "my_app/coverband/feature_flag_tracker"
|
|
446
|
+
|
|
447
|
+
Coverband.configure do |config|
|
|
448
|
+
config.track_feature_flags = true
|
|
449
|
+
end
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
Every tracker must declare its own `REPORT_ROUTE`. `AbstractTracker`'s default of `"/"` matches every path in the web report, so a tracker that inherits it would answer all of Coverband's own report routes. Registering such a tracker raises `ArgumentError` rather than letting it take over the UI.
|
|
453
|
+
|
|
454
|
+
`tracker_class` may also be a callable that returns a tracker instance when initialization needs to choose an implementation. Registration and enablement are evaluated at boot, not on every request. Duplicate tracker names raise `ArgumentError` so load-order mistakes fail early. Coverband's Query Burst Tracker is a bundled example of the same registration pattern.
|
|
455
|
+
|
|
456
|
+
Trackers registered this way have no dedicated `Coverband.configuration` accessor, so reach them by their registered name:
|
|
457
|
+
|
|
458
|
+
```ruby
|
|
459
|
+
Coverband.configuration.tracker_for(:feature_flag_tracker)
|
|
460
|
+
Coverband.track_key(:feature_flag_tracker, "flags.new_checkout")
|
|
461
|
+
```
|
|
462
|
+
|
|
361
463
|
### Hiding settings
|
|
362
464
|
|
|
363
465
|
Coverband provides a view of all of its current settings. Sometimes you might want to hide this view,
|
|
@@ -376,6 +478,14 @@ The web report includes a client-side "dead-code session" toolbar to help drive
|
|
|
376
478
|
|
|
377
479
|
Session state lives in your browser's localStorage: "Reset session" clears the hidden set; "Start over" (in the modal) clears the marked list.
|
|
378
480
|
|
|
481
|
+
### Dark mode
|
|
482
|
+
|
|
483
|
+
The web report follows your operating system's appearance setting through the CSS `prefers-color-scheme` media query. There is nothing to configure and no toggle in the UI: if your desktop is in dark mode, the report is dark, and it switches back when your desktop does. Printing always uses the light theme.
|
|
484
|
+
|
|
485
|
+
The coverage row colors (covered / missed / never / skipped) are chosen for contrast on the dark surface rather than darkened mechanically, so the states stay as easy to tell apart as in the light theme.
|
|
486
|
+
|
|
487
|
+
If you restyle the report, every color in `public/application.css` resolves through a custom property defined on `:root`, and the dark theme is a single `@media (prefers-color-scheme: dark)` block that redefines those properties. Overriding a property is enough to change both themes from one place.
|
|
488
|
+
|
|
379
489
|
### Fixing Coverage Only Shows Loading Hits
|
|
380
490
|
|
|
381
491
|
If all your coverage is being counted as loading or eager_loading coverage, and nothing is showing as runtime Coverage the initialization hook failed for some reason. The most likely reason for this issue is manually calling `eager_load!` on some Plugin/Gem. If you or a plugin is altering the Rails initialization process, you can manually flip Coverband to runtime coverage by calling these two lines, in an `after_initialize` block, in `application.rb`.
|
|
@@ -441,6 +551,55 @@ Coverband on very high volume sites with many server processes reporting can hav
|
|
|
441
551
|
|
|
442
552
|
See more discussion [here](https://github.com/danmayer/coverband/issues/384).
|
|
443
553
|
|
|
554
|
+
### ActiveSupport::Cache Store
|
|
555
|
+
|
|
556
|
+
Coverband can store coverage in any `ActiveSupport::Cache::Store`, which covers
|
|
557
|
+
Redis, Memcached, files, and — through [Solid Cache](https://github.com/rails/solid_cache) —
|
|
558
|
+
Postgres, MySQL, and SQLite.
|
|
559
|
+
|
|
560
|
+
```ruby
|
|
561
|
+
# Rails.cache does not exist yet when config/coverband.rb loads, so pass a block
|
|
562
|
+
config.store = Coverband::Adapters::ActiveSupportCacheStore.new { Rails.cache }
|
|
563
|
+
|
|
564
|
+
# or hand it a store directly
|
|
565
|
+
config.store = Coverband::Adapters::ActiveSupportCacheStore.new(
|
|
566
|
+
ActiveSupport::Cache::MemCacheStore.new("localhost:11211")
|
|
567
|
+
)
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
The block form is the one to use with Solid Cache: its models come from a Rails
|
|
571
|
+
engine that has not loaded when `config/coverband.rb` runs, so building a
|
|
572
|
+
`SolidCache::Store` there raises `uninitialized constant
|
|
573
|
+
SolidCache::Store::Entries::Entry`.
|
|
574
|
+
|
|
575
|
+
Coverband starts from `before_configuration`, which is earlier than Rails
|
|
576
|
+
assigns `Rails.cache`, so the first reporting cycle can run before the block can
|
|
577
|
+
resolve. That cycle's coverage is held and written once the cache appears; it is
|
|
578
|
+
not lost, and it does not raise into the booting app.
|
|
579
|
+
|
|
580
|
+
This adapter supports the view, route, translation, and query burst trackers.
|
|
581
|
+
It replaces `MemcachedStore`, which was removed in 7.0 (see
|
|
582
|
+
[Upgrading to 7.0](#upgrading-to-70)).
|
|
583
|
+
|
|
584
|
+
Things to know before pointing it at a cache:
|
|
585
|
+
|
|
586
|
+
- **A cache may evict what Coverband writes.** Use a store that will not expire
|
|
587
|
+
the data: a dedicated Solid Cache database with `max_age: nil` plus a size
|
|
588
|
+
limit, or a Redis instance with no eviction policy. Coverband detects the loss
|
|
589
|
+
and reports it in the web UI when it can, but it cannot prevent it.
|
|
590
|
+
- **Memcached's 1MB value limit applies to every document**, trackers included.
|
|
591
|
+
A large translation or view tracker can exceed it, in which case the write is
|
|
592
|
+
refused and Coverband logs what could not be stored. Raise the slab limit
|
|
593
|
+
(`-I`) or use Redis/Solid Cache for large apps.
|
|
594
|
+
- **`ActiveSupport::Cache::MemoryStore` is per process**, so it is only useful
|
|
595
|
+
for development and tests.
|
|
596
|
+
- **Solid Cache** writes go through ActiveRecord, so size your connection pool
|
|
597
|
+
for one extra connection per process, and prefer a separate cache database so
|
|
598
|
+
Coverband's writes do not evict your application's own cached data.
|
|
599
|
+
- For very large multi-process deployments `Coverband::Adapters::HashRedisStore`
|
|
600
|
+
is still the best option: it avoids write conflicts entirely rather than
|
|
601
|
+
repairing them.
|
|
602
|
+
|
|
444
603
|
### Clear Coverage
|
|
445
604
|
|
|
446
605
|
Now that Coverband uses MD5 hashes there should be no reason to manually clear coverage unless one is testing, changing versions, or possibly debugging Coverband itself.
|
|
@@ -452,6 +611,28 @@ This will clear both the coverage and trackers data. It can also be done through
|
|
|
452
611
|
To clear only coverage data, run `rake coverband:clear_coverage`.
|
|
453
612
|
To clear only trackers data, run `rake coverband:clear_tracker`.
|
|
454
613
|
|
|
614
|
+
Redis-backed stores also support two maintenance tasks:
|
|
615
|
+
|
|
616
|
+
- `rake coverband:clear_legacy` removes Coverband-owned keys from storage
|
|
617
|
+
formats retired by the 7.0 upgrade.
|
|
618
|
+
- `rake coverband:clear_orphans` removes old generation documents that no
|
|
619
|
+
pointer references, after rechecking that each candidate is not live or too
|
|
620
|
+
young to delete safely.
|
|
621
|
+
|
|
622
|
+
In Coverband 7.0, adapters no longer expose their backend through `raw_store`.
|
|
623
|
+
Code that needs these supported maintenance operations can request the narrow
|
|
624
|
+
capability instead:
|
|
625
|
+
|
|
626
|
+
```ruby
|
|
627
|
+
cleanup = Coverband.configuration.store.cleanup
|
|
628
|
+
cleanup&.clear_legacy!
|
|
629
|
+
cleanup&.clear_orphans!
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
`RedisStore` and `HashRedisStore` provide this capability. Adapters that cannot
|
|
633
|
+
enumerate their keys return `nil`; clear the backing cache or rely on its expiry
|
|
634
|
+
policy for those stores.
|
|
635
|
+
|
|
455
636
|
### Adding Rake Tasks outside of Rails
|
|
456
637
|
|
|
457
638
|
Rails apps should automatically include the tasks via the Railtie.
|
|
@@ -550,6 +731,28 @@ The MCP server provides the following tools:
|
|
|
550
731
|
| `get_view_tracker_data` | Get view/template usage data |
|
|
551
732
|
| `get_route_tracker_data` | Get route usage statistics |
|
|
552
733
|
| `get_translation_tracker_data` | Get translation key usage data |
|
|
734
|
+
| `get_storage_health` | Get observed coverage and tracker storage protocol state |
|
|
735
|
+
|
|
736
|
+
#### Storage health
|
|
737
|
+
|
|
738
|
+
`Coverband.storage_health` returns normalized diagnostics for coverage and every initialized
|
|
739
|
+
tracker. The normal JSON report also includes this value as its top-level `storage_health`
|
|
740
|
+
field, and MCP exposes the same representation through `get_storage_health`:
|
|
741
|
+
|
|
742
|
+
```ruby
|
|
743
|
+
Coverband.storage_health
|
|
744
|
+
# => {
|
|
745
|
+
# coverage: {status: "ok", data_loss: nil, unwritten: nil},
|
|
746
|
+
# trackers: {view_tracker: {status: "stalled", data_loss: nil,
|
|
747
|
+
# unwritten: {deltas: 3, since: "2026-08-19T12:01:00Z"}}}
|
|
748
|
+
# }
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
Statuses are `ok`, `stalled`, `data_loss`, or `unsupported`; `data_loss` takes precedence
|
|
752
|
+
when both loss and unwritten work are present. These diagnostics describe state Coverband's
|
|
753
|
+
storage protocol has already observed. They are not an active backend availability or
|
|
754
|
+
connectivity probe. SimpleCov-compatible JSON generated with `for_merged_report` does not
|
|
755
|
+
include this metadata.
|
|
553
756
|
|
|
554
757
|
#### Running the MCP Server
|
|
555
758
|
|
|
@@ -673,7 +876,7 @@ Outputs:
|
|
|
673
876
|
# Prerequisites
|
|
674
877
|
|
|
675
878
|
- Ruby 3.1+
|
|
676
|
-
-
|
|
879
|
+
- A production store: Redis by default (add the `redis` gem), or any `ActiveSupport::Cache` store such as Solid Cache or memcached
|
|
677
880
|
|
|
678
881
|
### Ruby and Rails Version Support
|
|
679
882
|
|
|
@@ -721,13 +924,16 @@ If you submit a change please make sure the tests and benchmarks are passing.
|
|
|
721
924
|
- **total fail** on front end code, for line for line coverage, because of the precompiled template step basically coverage doesn't work well for `erb`, `slim`, and the like.
|
|
722
925
|
- related it will try to report something, but the line numbers reported for `ERB` files are often off and aren't considered useful. I recommend filtering out .erb using the `config.ignore` option. The default configuration excludes these files
|
|
723
926
|
- **NOTE:** We now have file level coverage for view files, but don't support line level detail
|
|
724
|
-
- **
|
|
725
|
-
-
|
|
927
|
+
- **Scout APM Auto Instrumentation** — fixed in Ruby 3.4.10 / 4.0.4, broken on earlier Rubies
|
|
928
|
+
- **Symptom:** in an environment that uses Scout's auto instruments (usually production or staging) Coverband stops reporting coverage. You will see one or two files loaded at the start, and everything else shows up as 0% / never loaded. This is dangerous: live code, particularly your controllers, gets reported as dead code.
|
|
929
|
+
- **Cause:** this is a Ruby bug, not a bug in Coverband or Scout. Scout's auto instrumentation installs a `RubyVM::InstructionSequence.load_iseq` hook and loads your files through `RubyVM::InstructionSequence.compile` / `.compile_file`. Until recently those methods did not register a file with the `Coverage` module the way a normal `require` does, so the file was never measured and never even appeared in `Coverage.result`. Note that this affected *every* file loaded while the hook was installed, not just the controllers Scout rewrites, which is why coverage appears to vanish entirely.
|
|
930
|
+
- **Fix:** upgrade to **Ruby >= 3.4.10 or >= 4.0.4**, which include the fix for [Ruby Bug #22018](https://bugs.ruby-lang.org/issues/22018). Coverband and Scout auto instruments then work together with no configuration changes and no minimum version of either gem. Both `lines` and `oneshot_lines` collection modes are unaffected.
|
|
931
|
+
- **Workaround for Ruby <= 3.3:** the fix was not backported to Ruby 3.1, 3.2, or 3.3, so on those versions you must disable auto instruments to collect coverage — set `auto_instruments: false` in `scout_apm.yml` or `SCOUT_AUTO_INSTRUMENTS=false` in the environment.
|
|
726
932
|
- Bug tracked here: https://github.com/scoutapp/scout_apm_ruby/issues/343
|
|
727
933
|
- **Coverband, [Elastic APM](https://github.com/elastic/apm-agent-ruby) and resque**
|
|
728
934
|
- In an environment that uses the Elastic APM ruby agent, resque jobs will fail with `Transactions may not be nested. Already inside #<ElasticAPM::Transaction>` if the `elastic-apm` gem is loaded _before_ the `coverband` gem
|
|
729
935
|
- Put `coverage` ahead of `elastic-apm` in your Gemfile
|
|
730
|
-
- **Bootsnap**,
|
|
936
|
+
- **Bootsnap**, you can run bootsnap alongside Coverband, but you lose part of the boot time speedup. [Bootsnap detects that coverage is enabled](https://github.com/Shopify/bootsnap/blob/main/lib/bootsnap/compile_cache/iseq.rb) and disables its `ISeq` compile cache, because a cached instruction sequence would not carry the `Coverage` instrumentation. Bootsnap's other caches (load path, YAML, JSON) are unaffected and keep working.
|
|
731
937
|
|
|
732
938
|
### Debugging Redis Store
|
|
733
939
|
|
data/Rakefile
CHANGED
|
@@ -21,6 +21,22 @@ Rake::TestTask.new(:test) do |test|
|
|
|
21
21
|
test.verbose = true
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
###
|
|
25
|
+
# Solid Cache brings a Rails engine with it, which changes how the rest of the
|
|
26
|
+
# suite behaves in the same process. It gets its own task and its own process.
|
|
27
|
+
###
|
|
28
|
+
desc "run the Solid Cache backed adapter tests (SQLite, needs solid_cache)"
|
|
29
|
+
task :"test:solid_cache" do
|
|
30
|
+
# BUNDLE_WITH opts the optional group onto the load path for this run only.
|
|
31
|
+
# It has to already be installed: `bundle install --with solid_cache`.
|
|
32
|
+
sh({"COVERBAND_SOLID_CACHE" => "true", "BUNDLE_WITH" => "solid_cache"},
|
|
33
|
+
"bundle exec ruby -Ilib -Itest test/coverband/adapters/solid_cache_store_test.rb") do |ok, _res|
|
|
34
|
+
unless ok
|
|
35
|
+
abort "solid_cache tests failed. If the gems are missing, run: bundle install --with solid_cache"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
24
40
|
desc "run the web report's javascript unit tests (node --test)"
|
|
25
41
|
task :"test:javascript" do
|
|
26
42
|
# Pass expanded file paths rather than the bare directory: node's
|
|
@@ -51,8 +67,3 @@ desc "start webserver"
|
|
|
51
67
|
task :server do
|
|
52
68
|
exec "rackup -I lib"
|
|
53
69
|
end
|
|
54
|
-
|
|
55
|
-
desc "publish gem with 2 factor auth, reminder how"
|
|
56
|
-
task :publish_gem do
|
|
57
|
-
exec "gem push pkg/coverband-4.2.3.XXX.gem"
|
|
58
|
-
end
|
data/changes.md
CHANGED
|
@@ -1,3 +1,62 @@
|
|
|
1
|
+
### 7.0.0
|
|
2
|
+
|
|
3
|
+
**Breaking changes**
|
|
4
|
+
|
|
5
|
+
* **Upgrading from 6.x? Follow [Upgrading to 7.0](https://github.com/danmayer/coverband#upgrading-to-70) in the README**, which lists what to change for each item below. Removed APIs are removed outright rather than deprecated first.
|
|
6
|
+
* **The `redis` gem is no longer a dependency of Coverband. If you use Redis (the default), add `gem "redis"` to your Gemfile when you upgrade.** Redis is still the default store and still needs no other configuration: whenever the `redis` gem is installed Coverband loads it and stores coverage there exactly as before. Apps that only got `redis` through Coverband will otherwise boot with a warning telling them to add it, and record nothing until they do. Apps that store coverage elsewhere (`ActiveSupportCacheStore` with Solid Cache, memcached, or files) no longer need Redis installed at all
|
|
7
|
+
* **Coverage and tracker history do not survive this upgrade.** Stored documents now carry the metadata the new merge protocol needs, and keys are scoped by a generation token, so the storage format version changed for both `RedisStore` and the cache adapters. Old keys are ignored rather than migrated, and coverage accumulates fresh from the upgrade. Delete the old `coverband_3_2.*` keys once you are happy with the new data.
|
|
8
|
+
* **Reporting to coverband.io (`WebServiceStore`) has been removed, as the hosted service is no longer maintained.** This also removes `ViewTrackerService`, loading `config/coverband_service.rb`, the `api_key`, `service_url`, `coverband_timeout`, `service_dev_mode`, `service_test_mode`, and `process_type` settings, and the `COVERBAND_API_KEY`, `COVERBAND_URL`, `COVERBAND_ENABLE_DEV_MODE`, `COVERBAND_ENABLE_TEST_MODE`, and `PROCESS_TYPE` environment variables. Setting `COVERBAND_API_KEY` no longer switches Coverband into service mode. Move settings into `config/coverband.rb` and configure Redis or an `ActiveSupportCacheStore`
|
|
9
|
+
* Removed configuration settings that no longer had production consumers: `reporter` (no effect since 5.0), `s3_region`, `s3_bucket`, `s3_access_key_id`, `s3_secret_access_key`, `track_gems`, `gem_details`, and `simulate_oneshot_lines_coverage`, plus the coverband.io settings above. Assigning any of them logs a warning naming the setting and how to fix it, and is otherwise ignored, so an old `config/coverband.rb` still boots.
|
|
10
|
+
* `Coverband::Utils::SourceFile` and `Coverband::Utils::Result` now accept only enriched file records from stored Coverband reports. Passing the legacy bare line-coverage Array is no longer supported.
|
|
11
|
+
* **`Coverband::Adapters::MemcachedStore` has been removed.** Use `ActiveSupportCacheStore.new(cache, cache_namespace: "...")` with the same memcached cache; it also supports the trackers. `ActiveSupportCacheStore` no longer accepts `memcached_namespace`
|
|
12
|
+
* **`background_reporting_sleep_seconds` now defaults to 600** for the new cache adapter (`HashRedisStore` still defaults to 300, everything else to 60), because each report rewrites a whole document.
|
|
13
|
+
* `Adapters::Base#size` now returns bytes as an Integer or `nil`; `size_in_mib` owns the "N/A" presentation. A store returning the string `"N/A"` previously reported `0.00` MiB.
|
|
14
|
+
* `Adapters::Base#get_coverage_report` now reports the receiver's coverage instead of routing through `Coverband.configuration.store`, which quietly returned another store's data when the receiver was not the configured one.
|
|
15
|
+
* Removed `AbstractTracker#redis_store`; custom trackers use `#storage`. Trackers no longer reach through the store to raw Redis commands.
|
|
16
|
+
* Removed the misspelled `Coverband::Utils::Results#runtime_relavent_lines`; use `#runtime_relevant_lines` instead.
|
|
17
|
+
* Removed `Adapters::Base#raw_store` and the Redis adapters' raw-client escape hatch. Redis maintenance now uses the narrow `store.cleanup` capability (`clear_legacy!` and `clear_orphans!`); adapters that cannot enumerate keys return `nil`.
|
|
18
|
+
* Work is retained when generation resolution fails, so a backend blip during a reporting cycle no longer discards that cycle's coverage. Previously only deltas that had reached the pending queue were protected, and the generation is resolved before the payload is enqueued. The retention caps apply to that held work too, so a sustained outage cannot grow the queue without limit
|
|
19
|
+
* A cache store that is not resolvable yet (`ActiveSupportCacheStore.new { Rails.cache }` running before Rails assigns `Rails.cache`) is no longer a `NoMethodError` on a nil target. It holds the cycle's work, logs, and resolves on a later cycle. A resolver returning something that is not a cache store now names the problem instead of failing deep in the storage layer
|
|
20
|
+
* Rake tasks that reach the store (`coverband:clear`, `clear_coverage`, `clear_tracker`, `clear_legacy`, `clear_orphans`, `coverage`, `coverage_json`, `coverage_html`) load the Rails environment first. With a lazily configured store they previously failed with an unavailable cache unless `rake environment` was named explicitly
|
|
21
|
+
* Coverage data loss (eviction, dropped pending deltas) is now surfaced on the web report index. It was logged and shown on the tracker tabs, but the coverage index reported partial numbers as though they were complete
|
|
22
|
+
* `Adapters::SessionCoverage#save_report` returns the protocol state from `Session#record` rather than `nil`, so a caller can tell a write that landed from one that was deferred or refused
|
|
23
|
+
* Coverage now absorbs the two-cycle outage the design commits to. The cap bounds the queue, and the cycle that recovers takes a slot in it, so a cap of 2 only ever absorbed one; it is now 3
|
|
24
|
+
* Data loss from dropping a delta that was already written and merely awaiting confirmation is reported as `unconfirmed_dropped` rather than `pending_dropped`, and the web report says the numbers may be undercounted instead of claiming earlier results are unavailable. Only the second kind is lost coverage
|
|
25
|
+
* Trackers keep their own keys whenever a report is not stored (`:failed`, `:unavailable`, or an error), so an unreachable backend is no longer lossier for them than one that raises. `TrackerStorage::Base#retains_pending?` is removed; the decision comes from what `record` returns
|
|
26
|
+
* A write refused or errored *after* the delta was enqueued now reports `:retained` rather than `:failed`, since the work is in the storage queue and a caller holding a second copy would replay it. This is the documented memcached >1MB path, where it double counted `QueryBurstTracker`
|
|
27
|
+
* `QueryBurstTracker` reports on a quiet cycle when storage is holding work it could not write, instead of waiting for the next burst to flush it
|
|
28
|
+
* A document that cannot be written is now reported while it is happening, rather than an hour later. Its retention caps never fire on a quiet tracker -- nothing new arrives for them to drop -- so only the absolute age cap eventually turned a permanently refused write (a tracker document past memcached's 1MB limit, for instance) into a loss event, and until then the tab read "0 used" exactly like an app that used none. The web report now says how many reports could not be stored and since when
|
|
29
|
+
* Presence trackers (views, routes, translations) re-supply their known keys when storage reports work dropped at its retention caps, so a backend outage longer than the queue no longer leaves a used view reading as unused. Their merge is idempotent, so this cannot double count; `QueryBurstTracker` sums, so its loss stays irreducible and reported
|
|
30
|
+
* `save_report` no longer mutates the report it is given. The merge used to sum stored counts back into the caller's line arrays, so a caller that reused its report object re-submitted counts that were already recorded.
|
|
31
|
+
|
|
32
|
+
**Features**
|
|
33
|
+
|
|
34
|
+
* Replaced the generation lifecycle mixin and its implicit instance-variable/hook contract with a state-owning coordinator and typed generation-change events (#656)
|
|
35
|
+
* Added structured storage health through `Coverband.storage_health`, normal JSON reports, and the MCP `get_storage_health` tool, so monitoring can distinguish healthy, stalled, data-loss, and unsupported diagnostic states (#658)
|
|
36
|
+
* Added `Coverband::Adapters::ActiveSupportCacheStore`, which stores coverage in any `ActiveSupport::Cache::Store` — Redis, Memcached, files, or Solid Cache for Postgres/MySQL/SQLite. The cache target can be passed lazily (`ActiveSupportCacheStore.new { Rails.cache }`) so it works from `config/coverband.rb`, where `Rails.cache` does not exist yet (#533)
|
|
37
|
+
* Trackers (views, routes, translations, query bursts) now work on non-Redis stores. They previously reached through the store to raw Redis commands, so file and memcached users got trackers that silently collected nothing and empty web UI tabs
|
|
38
|
+
* Fixed the read-modify-write conflict Coverband has always had on `RedisStore`: concurrent reports could silently drop one process's contribution. Reports are now applied under a per-writer sequence, so a conflicting write is detected and repaired on the next cycle without double counting. `QueryBurstTracker` had the same conflict on its cumulative counters and is fixed the same way
|
|
39
|
+
* Resets are now strong: `clear!` and `reset_recordings` retire a whole generation key, so a straggling write from before the reset cannot undo it
|
|
40
|
+
* Coverband now detects when a backend has dropped its data (cache eviction, `Rails.cache.clear`) and reports it in the web UI rather than silently showing partial numbers
|
|
41
|
+
* Trackers are skipped with an explanatory log line when the configured store cannot support them, instead of collecting nothing
|
|
42
|
+
* Query bursts no longer count Coverband's own storage queries against the surrounding request, which would otherwise inflate query counts on a database backed cache
|
|
43
|
+
* Added `persistent_coverage?`, `supports_trackers?`, and `supports_paged_reports?` capability predicates on adapters, plus `file_count` and `cached_file_count` defaults so non-Redis stores no longer raise from the web UI's datatables path
|
|
44
|
+
* `redis_ttl` applies to stored coverage documents again; generation pointers deliberately never expire, since a pointer that outlived its document would leave live coverage unreachable
|
|
45
|
+
* Storage reads degrade to empty and log when the backend is unavailable (an unreachable cache, or a Solid Cache table that has not been created yet) rather than raising into the request rendering the report; write failures still reach the reporting paths that log them
|
|
46
|
+
* Added `rake test:solid_cache`, an ENV gated SQLite backed Solid Cache suite covering atomic pointer creation, batched pointer reads, ActiveRecord being unavailable, a missing Solid Cache schema, and Coverband's own SQL not being attributed to the request that triggered the report
|
|
47
|
+
* Added `rake coverband:clear_orphans` to reclaim generation keys no longer referenced by a pointer, and scoped `coverband:clear_legacy` to Coverband's own namespaces and tracker names so it cannot delete unrelated application keys
|
|
48
|
+
* CI now runs a memcached service, so the memcached backed adapter is actually exercised
|
|
49
|
+
* Coverage adapters share one implementation of the merge protocol, and the generation lifecycle is shared between the session and the Redis hash repository, rather than each reimplementing the same bookkeeping
|
|
50
|
+
* The web report has a dark theme. It follows the operating system through `prefers-color-scheme`, so there is nothing to configure and no toggle; every color in `public/application.css` now resolves through a custom property on `:root`, which makes the report restylable from one place. Coverage row colors are picked for contrast on the dark surface rather than darkened mechanically, so covered / missed / never / skipped stay as easy to tell apart; thanks @naofumi-fujii (#665, #666, #667)
|
|
51
|
+
* Added `Coverband::Collectors::TrackerRegistry` so applications and gems can register their own trackers, along with `Coverband::Configuration.add_tracker_flag` for their enablement flags and `Coverband.configuration.tracker_for` / `Coverband.track_key` support for trackers without a dedicated accessor; bundled trackers now self-register and are initialized generically at Rails boot (#651)
|
|
52
|
+
|
|
53
|
+
**Bug fixes**
|
|
54
|
+
|
|
55
|
+
* The web report's `/report_json` route and `JSONReport.new(store, as_report: true)` no longer raise `NoMethodError` outside Rails, where ActiveSupport's `as_json` is not loaded; thanks @dajiaohuang (#669)
|
|
56
|
+
* The web report's password check accepts passwords containing `:` (it split the credentials on every colon) and compares in constant time, as does the MCP endpoint's bearer token check. This also drops the runtime dependency on the `base64` gem
|
|
57
|
+
* `Coverband::Utils::Results#respond_to?(name, true)` no longer raises `ArgumentError`
|
|
58
|
+
* The runtime % no longer reads 100% for files with runtime relevant lines that were never hit, and no longer divides by every line of a file that was never eager loaded. It only counts runtime hits on lines that eager loading did not cover, and takes line relevance from real coverage data rather than the never loaded placeholder, which marked blank and comment lines as relevant. Runtime relevant lines on the file page use the same calculation, and the runtime % there is colored by its own value rather than by % covered (#668)
|
|
59
|
+
|
|
1
60
|
### 6.2.2
|
|
2
61
|
|
|
3
62
|
* Feature: Added a dead-code review session to the web report — hide files/folders by hovering any path segment, mark files for deletion with an optional comment, and export marked entries to CSV to drive an out-of-band code-removal process; session state lives in browser localStorage; thanks @obie (#647, #649)
|