debugbundle 0.1.0 → 0.1.2

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: 57ff4962fe1910ae27c974401d207da7c6a1afc1d22f9a5951b93d4c338f1f65
4
- data.tar.gz: cb7d91a8088df973a7a4b344c79799e348fb0cbccc0544eac5f898c458bb931b
3
+ metadata.gz: cd8a77046a7f20cf6971b6e73ca8e5c7557738706e32e3b30e741f3410f8d00a
4
+ data.tar.gz: 0bd638cf69ff2c6894706b1bc996f801e849b6f5a5c134543a3a18d1c598725f
5
5
  SHA512:
6
- metadata.gz: 1eaccc22806e5bf4844d7a1820c52f67add73f63025f5023f664570ac64b4fb6ba46e62faeb2d6a8380c95bd7e1c7efd469cfbba4bb4de81613a4b4476305e40
7
- data.tar.gz: 55ab0c5a6f38b356ad6a49d580fcf95ac68075f3d9633aabb136c70cd9c8d5f9908a0a38a83eb162cd1387d3ce4ca3edb979bb4b727430306facfda3dd50c145
6
+ metadata.gz: c9431211f33958cf178a8ace0e01b3842a2a2ff1bed8e11129e053ceb6b8685ff17499d03c80bc3cfe3fc7fd6839eced529ab01238760f04a65f4181e6685507
7
+ data.tar.gz: f7a432f0dc42da84a44415c767610473d8f332577eeb658c1417e47e4227e2701f7143e67fb9a8fa40db700dfca9f9ba0f655fe270fbd779de0e499507dc6836
data/Makefile CHANGED
@@ -3,6 +3,7 @@ SHELL := /bin/sh
3
3
  RUBY_IMAGE ?= ruby:3.4.2
4
4
  WORKDIR := /workspace
5
5
  BUNDLE_GEMFILE ?= Gemfile
6
+ VERSION ?= $(shell ruby -e 'require "./lib/debugbundle/version"; print DebugBundle::VERSION')
6
7
 
7
8
  DOCKER_RUN = docker run --rm -t \
8
9
  -v "$(PWD):$(WORKDIR)" \
@@ -12,6 +13,8 @@ DOCKER_RUN = docker run --rm -t \
12
13
  BUNDLE_ENV = BUNDLE_GEMFILE="$(BUNDLE_GEMFILE)"
13
14
 
14
15
  .PHONY: bundle-install test lint build shell compat-rack compat-rails compat-sidekiq compat
16
+ .PHONY: smoke
17
+ .PHONY: smoke-published
15
18
 
16
19
  bundle-install:
17
20
  $(DOCKER_RUN) sh -lc "$(BUNDLE_ENV) bundle config set path vendor/bundle && $(BUNDLE_ENV) bundle install"
@@ -25,6 +28,12 @@ lint:
25
28
  build:
26
29
  $(DOCKER_RUN) sh -lc "$(BUNDLE_ENV) bundle config set path vendor/bundle && $(BUNDLE_ENV) bundle install && gem build debugbundle.gemspec"
27
30
 
31
+ smoke:
32
+ $(DOCKER_RUN) sh -lc "gem build debugbundle.gemspec && ruby smoke/run_app_driven_smoke.rb --source local --version $(VERSION)"
33
+
34
+ smoke-published:
35
+ $(DOCKER_RUN) sh -lc "ruby smoke/run_app_driven_smoke.rb --source published --version $(VERSION)"
36
+
28
37
  compat-rack:
29
38
  docker run --rm -t -v "$(PWD):$(WORKDIR)" -w "$(WORKDIR)" ruby:3.1.6 sh -lc 'SIMPLECOV_MINIMUM_COVERAGE=0 BUNDLE_GEMFILE="gemfiles/rack_2_2.gemfile" bundle config set path vendor/bundle && SIMPLECOV_MINIMUM_COVERAGE=0 BUNDLE_GEMFILE="gemfiles/rack_2_2.gemfile" bundle install && SIMPLECOV_MINIMUM_COVERAGE=0 BUNDLE_GEMFILE="gemfiles/rack_2_2.gemfile" bundle exec rspec spec/rack_integration_spec.rb spec/rack_middleware_spec.rb spec/relay_spec.rb'
30
39
  docker run --rm -t -v "$(PWD):$(WORKDIR)" -w "$(WORKDIR)" ruby:3.4.2 sh -lc 'SIMPLECOV_MINIMUM_COVERAGE=0 BUNDLE_GEMFILE="gemfiles/rack_3.gemfile" bundle config set path vendor/bundle && SIMPLECOV_MINIMUM_COVERAGE=0 BUNDLE_GEMFILE="gemfiles/rack_3.gemfile" bundle install && SIMPLECOV_MINIMUM_COVERAGE=0 BUNDLE_GEMFILE="gemfiles/rack_3.gemfile" bundle exec rspec spec/rack_integration_spec.rb spec/rack_middleware_spec.rb spec/relay_spec.rb'
data/README.md CHANGED
@@ -10,38 +10,33 @@ Use this gem to capture Ruby backend exceptions, request metadata, structured lo
10
10
  gem "debugbundle"
11
11
  ```
12
12
 
13
- ## Quick start
13
+ ## Quick Start
14
14
 
15
15
  ```ruby
16
16
  require "debugbundle"
17
+ require "logger"
17
18
 
18
19
  DebugBundle.init(
19
- project_token: ENV.fetch("DEBUGBUNDLE_TOKEN"),
20
- service: "checkout-api",
21
- environment: ENV.fetch("APP_ENV", "production")
20
+ project_token: ENV.fetch("DEBUGBUNDLE_TOKEN"),
21
+ service: "checkout-api",
22
+ environment: ENV.fetch("APP_ENV", "production")
22
23
  )
23
24
 
24
25
  DebugBundle.capture_logger(Logger.new($stdout))
25
- ```
26
-
27
- `DebugBundle.init(...)` arms best-effort process exception capture automatically through `at_exit` and thread exception hooks.
28
26
 
29
- Capture handled exceptions, messages, requests, and probes explicitly:
30
-
31
- ```ruby
32
27
  begin
33
- perform_checkout
28
+ perform_checkout
34
29
  rescue => error
35
- DebugBundle.capture_exception(error, context: { order_id: 123 })
30
+ DebugBundle.capture_exception(error, context: { order_id: 123 })
36
31
  end
37
32
 
38
- DebugBundle.capture_log("payment retry failed", level: :warning, context: { order_id: 123 })
39
33
  DebugBundle.capture_message("worker started", level: :info)
40
- DebugBundle.probe("checkout.tax", { region: "us-east-1" })
41
34
  DebugBundle.flush
42
35
  ```
43
36
 
44
- ## Framework integrations
37
+ `DebugBundle.init(...)` arms best-effort process exception capture automatically through `at_exit` and thread exception hooks.
38
+
39
+ ## Framework Integrations
45
40
 
46
41
  | Surface | Integration |
47
42
  | --- | --- |
@@ -52,44 +47,20 @@ DebugBundle.flush
52
47
  | Semantic Logger | `DebugBundle.capture_semantic_logger` |
53
48
  | Browser relay | `DebugBundle::Relay::Handler` or `DebugBundle::Rack::RelayMiddleware` |
54
49
 
55
- ## Browser relay
50
+ ## Configuration Reference
56
51
 
57
- Ruby backends can receive browser events at `POST /debugbundle/browser` through the relay handler.
52
+ Configuration sources and precedence:
58
53
 
59
- Rails applications can let the Railtie append that route automatically, or override it with:
54
+ 1. Explicit arguments passed to `DebugBundle.init(...)`, `DebugBundle::Client.new(...)`, or `DebugBundle::Relay::Handler.new(...)`.
55
+ 2. Rails `config.debugbundle.*` values when the Railtie wires the default client or relay route.
56
+ 3. Environment variables only when your application chooses to pass them into those explicit Ruby calls. The gem does not auto-read arbitrary config env vars on its own.
57
+ 4. Remote probe directives and capture policy returned by `GET /v1/sdk/config` after a connected client initializes.
60
58
 
61
- - `config.debugbundle.relay_enabled = false` to disable automatic mounting
62
- - `config.debugbundle.relay_path = "/debugbundle/browser"` to change the mounted path
63
- - `config.debugbundle.relay_allowed_origins = ["https://app.example.com"]` to pin allowed origins
64
- - `config.debugbundle.relay_rate_limit_per_minute = 120` to adjust per-IP relay throttling
65
- - `config.debugbundle.relay_rate_limit_store = Rails.cache` to share relay throttling across processes
66
- - `config.debugbundle.relay_handler = custom_handler` to supply a custom relay handler
67
-
68
- The relay:
69
-
70
- - validates same-origin requests by default
71
- - enforces `Content-Type: application/json`
72
- - caps request bodies at 256 KB
73
- - strips browser-supplied credentials and trust-sensitive fields
74
- - preserves browser correlation fields such as `trace_id`
75
- - supports local-only event writes and connected durable forwarding
76
-
77
- ## Remote probes
78
-
79
- Probe buffers stay local and flush on captured exceptions by default. Paid-tier projects can also activate probes for immediate `probe_event` shipping through the polled remote config.
80
-
81
- For one-off request diagnostics, the Ruby SDK also accepts signed trigger tokens:
82
-
83
- - header: `X-DebugBundle-Probe-Trigger: dbundle_probe_...`
84
- - query parameter: `?_debug_probe=dbundle_probe_...`
85
-
86
- Matching probes still stay in the local ring buffer, but the triggered request also emits standalone `probe_event` records immediately when the token is valid.
87
-
88
- ## Configuration
59
+ Capture-policy fields are server-owned and must not be supplied in local SDK config. The Ruby SDK enforces the server response locally after initialization.
89
60
 
90
61
  | Option | Default | Purpose |
91
62
  | --- | --- | --- |
92
- | `project_token` | required | Write-only DebugBundle project token. |
63
+ | `project_token` | required for connected capture | Write-only DebugBundle project token used by server-side transport. |
93
64
  | `service` | `ruby-service` | Service name used in event envelopes. |
94
65
  | `environment` | `development` | Runtime environment. |
95
66
  | `endpoint` | `https://api.debugbundle.com/v1/events` | Connected ingestion endpoint. |
@@ -110,7 +81,178 @@ Matching probes still stay in the local ring buffer, but the triggered request a
110
81
  | `probe_flush_on_error` | `true` | Attach probe buffers to captured exceptions. |
111
82
  | `probes_poll_interval` | `60` | Remote config poll interval in seconds. |
112
83
 
113
- ## Safety defaults
84
+ ## Install Examples by Mode
85
+
86
+ ### Connected mode
87
+
88
+ ```ruby
89
+ require "debugbundle"
90
+
91
+ DebugBundle.init(
92
+ project_token: ENV.fetch("DEBUGBUNDLE_TOKEN"),
93
+ service: "checkout-api",
94
+ environment: "production",
95
+ endpoint: "https://api.debugbundle.com/v1/events"
96
+ )
97
+ ```
98
+
99
+ ### Local-only mode
100
+
101
+ ```ruby
102
+ require "debugbundle"
103
+
104
+ DebugBundle.init(
105
+ project_token: ENV.fetch("DEBUGBUNDLE_TOKEN"),
106
+ service: "checkout-api",
107
+ environment: "development",
108
+ project_mode: :local_only,
109
+ local_events_dir: ".debugbundle/local/events"
110
+ )
111
+ ```
112
+
113
+ ### Rack middleware
114
+
115
+ ```ruby
116
+ require "debugbundle"
117
+ require "rack"
118
+
119
+ client = DebugBundle.init(
120
+ project_token: ENV.fetch("DEBUGBUNDLE_TOKEN"),
121
+ service: "checkout-api",
122
+ environment: "production"
123
+ )
124
+
125
+ app = Rack::Builder.new do
126
+ use DebugBundle::Rack::Middleware, client: client
127
+
128
+ run lambda { |_env|
129
+ [200, { "Content-Type" => "text/plain" }, ["ok"]]
130
+ }
131
+ end
132
+ ```
133
+
134
+ ### Rails initializer
135
+
136
+ ```ruby
137
+ # config/initializers/debugbundle.rb
138
+ require "debugbundle"
139
+
140
+ DebugBundle.init(
141
+ project_token: ENV.fetch("DEBUGBUNDLE_TOKEN"),
142
+ service: "checkout-api",
143
+ environment: Rails.env
144
+ )
145
+
146
+ Rails.application.configure do
147
+ config.debugbundle.relay_allowed_origins = ["https://app.example.com"]
148
+ end
149
+ ```
150
+
151
+ ### Sidekiq server middleware
152
+
153
+ ```ruby
154
+ require "debugbundle"
155
+
156
+ DebugBundle.init(
157
+ project_token: ENV.fetch("DEBUGBUNDLE_TOKEN"),
158
+ service: "checkout-worker",
159
+ environment: ENV.fetch("APP_ENV", "production")
160
+ )
161
+
162
+ Sidekiq.configure_server do |config|
163
+ config.server_middleware do |chain|
164
+ chain.add(DebugBundle::Sidekiq::ServerMiddleware, client: DebugBundle.client)
165
+ end
166
+ end
167
+ ```
168
+
169
+ ### Logger integration
170
+
171
+ ```ruby
172
+ require "debugbundle"
173
+ require "logger"
174
+
175
+ DebugBundle.init(
176
+ project_token: ENV.fetch("DEBUGBUNDLE_TOKEN"),
177
+ service: "checkout-api"
178
+ )
179
+
180
+ logger = Logger.new($stdout)
181
+ DebugBundle.capture_logger(logger)
182
+ ```
183
+
184
+ ## Runtime and Framework Support
185
+
186
+ | Surface | Minimum compatibility version | Recommended production version | Installed-base compatibility lane | Rolling CI lane | Out of scope |
187
+ | --- | --- | --- | --- | --- | --- |
188
+ | Ruby runtime | 3.1 | current maintained Ruby 3.4.x patch line | 3.1 and 3.2 remain supported for installed-base coverage | 3.1, 3.2, 3.3, 3.4 | 3.0 and older |
189
+ | Rails | 7.0 | latest 7.1 patch line | 7.0 compatibility support | 7.0 and 7.1 | 6.x and older |
190
+ | Rack | 2.2 | latest 3.x patch line | 2.2 compatibility support | 2.2 and 3.x | 2.1 and older |
191
+ | Sidekiq | 7.x | latest 8.x patch line | 7.x compatibility support | 7.x and 8.x | 6.x and older |
192
+
193
+ Post-V1 integrations remain out of scope here: Resque, Delayed Job, GoodJob, Sneakers, Shoryuken, Sinatra, Hanami, and deep ActiveRecord or SQL auto-instrumentation.
194
+
195
+ ## Dependency Alignment
196
+
197
+ `debugbundle` ships as one gem in V1, so there is no multi-package version-alignment surface like an npm family rule or Maven BOM.
198
+
199
+ - Pin one `debugbundle` version across your Ruby web app and worker repos when you want identical SDK behavior everywhere.
200
+ - Keep Rack, Rails, and Sidekiq inside the supported lanes above.
201
+ - Do not mix unsupported framework majors into examples or deployment templates.
202
+
203
+ ## Browser Relay
204
+
205
+ Ruby backends can receive browser events at `POST /debugbundle/browser` through the relay handler.
206
+
207
+ Rails applications can let the Railtie append that route automatically, or override it with:
208
+
209
+ - `config.debugbundle.relay_enabled = false` to disable automatic mounting
210
+ - `config.debugbundle.relay_path = "/debugbundle/browser"` to change the mounted path
211
+ - `config.debugbundle.relay_allowed_origins = ["https://app.example.com"]` to pin allowed origins
212
+ - `config.debugbundle.relay_rate_limit_per_minute = 120` to adjust per-IP rate limiting
213
+ - `config.debugbundle.relay_rate_limit_store = Rails.cache` to share rate limiting across processes
214
+ - `config.debugbundle.relay_handler = custom_handler` to supply a custom relay handler
215
+
216
+ Rack applications can mount the same handler explicitly:
217
+
218
+ ```ruby
219
+ relay_handler = DebugBundle::Relay::Handler.new(
220
+ project_mode: :connected,
221
+ project_token: ENV.fetch("DEBUGBUNDLE_TOKEN"),
222
+ endpoint: "https://api.debugbundle.com/v1/events",
223
+ allowed_origins: ["https://app.example.com"]
224
+ )
225
+
226
+ use DebugBundle::Rack::RelayMiddleware, handler: relay_handler
227
+ ```
228
+
229
+ Relay behavior:
230
+
231
+ - same-origin is the safe default when you do not set explicit allowed origins
232
+ - use explicit allowed origins when the frontend and backend live on different hosts
233
+ - requests must use `Content-Type: application/json`
234
+ - request bodies are capped at 256 KB
235
+ - per-IP rate limiting defaults to 60 requests per minute
236
+ - browser-supplied DebugBundle credentials are stripped before delivery, preserving credential isolation
237
+ - browser `service`, `environment`, and correlation fields are preserved unless you explicitly override them in the relay handler
238
+ - local-only mode writes validated browser events to `.debugbundle/local/events`
239
+ - connected durable mode writes validated browser events to `.debugbundle/local/browser-relay-spool` before forwarding to the configured endpoint
240
+ - set `relay_durable_write: false` when you intentionally want connected forwarding without the local spool write
241
+ - to disable the relay entirely, leave the route unmounted or set `relay_enabled = false`
242
+ - a missing token means connected forwarding cannot succeed; treat that as a server config error and leave the route disabled until the server-side token is fixed
243
+
244
+ ## Remote Probes
245
+
246
+ Probe buffers stay local and flush on captured exceptions by default. Paid-tier projects can also activate probes for immediate `probe_event` shipping through the polled remote config.
247
+
248
+ For one-off request diagnostics, the Ruby SDK also accepts signed trigger tokens:
249
+
250
+ - header: `X-DebugBundle-Probe-Trigger: dbundle_probe_...`
251
+ - query parameter: `?_debug_probe=dbundle_probe_...`
252
+
253
+ Matching probes still stay in the local ring buffer, but the triggered request also emits standalone `probe_event` records immediately when the token is valid.
254
+
255
+ ## Safety Defaults
114
256
 
115
257
  - SDK failures are isolated from host application failures.
116
258
  - Sensitive values are redacted before buffering or transport.
@@ -121,13 +263,69 @@ Matching probes still stay in the local ring buffer, but the triggered request a
121
263
  - `development`, `local`, and `test` environments write local event files by default.
122
264
  - Browser relay requests cannot smuggle server-side credentials.
123
265
 
266
+ ## Service Naming
267
+
268
+ Use distinct service names when one DebugBundle project receives events from multiple deployables:
269
+
270
+ - Browser frontend: `checkout-web`
271
+ - Rails or Rack API: `checkout-api`
272
+ - Sidekiq worker: `checkout-worker`
273
+
274
+ The Ruby relay preserves the browser-provided service name and environment by default. Only override relay-level `service` or `environment` when you intentionally want the backend relay host to rewrite browser event identity.
275
+
276
+ ## Safe Startup and Status
277
+
278
+ The SDK never raises host-facing exceptions because of missing config, bad transport responses, or malformed remote config payloads.
279
+
280
+ - `DebugBundle.init(project_token: "")` leaves capture disabled and `DebugBundle.status` reports `:degraded`
281
+ - `DebugBundle.init(enabled: false)` keeps the SDK as a no-op and `DebugBundle.status` reports `:disconnected`
282
+ - `DebugBundle.last_event_at` stays `nil` until a successful flush completes
283
+ - `DebugBundle.flush` is a no-op when the SDK has no buffered events, no transport, or is still waiting out a retry window
284
+
285
+ Status meanings:
286
+
287
+ - `:healthy` — idle or the last flush succeeded
288
+ - `:degraded` — missing token or a temporary rate-limit window is preventing immediate delivery
289
+ - `:disconnected` — the SDK is disabled or repeated transport failures exhausted the connection path
290
+
291
+ The release rule here is fail-safe, not fail-open: connected mode with a missing token must never pretend capture is healthy.
292
+
293
+ ## First-Event Verification
294
+
295
+ Use an explicit test message or exception during setup:
296
+
297
+ ```ruby
298
+ require "debugbundle"
299
+
300
+ DebugBundle.init(
301
+ project_token: ENV.fetch("DEBUGBUNDLE_TOKEN"),
302
+ service: "debugbundle-first-event",
303
+ environment: "staging"
304
+ )
305
+
306
+ DebugBundle.capture_message("debugbundle first-event verification", level: :error)
307
+ DebugBundle.flush
308
+ puts [DebugBundle.status, DebugBundle.last_event_at].inspect
309
+ ```
310
+
311
+ Then confirm the event through your mock ingestion endpoint, a staging project, or the DebugBundle CLI verification flow.
312
+
313
+ This repository also ships a clean-install app-driven smoke harness that validates the built gem and the published gem path:
314
+
315
+ ```sh
316
+ make smoke
317
+ make smoke-published VERSION=0.1.0
318
+ ```
319
+
320
+ `make smoke` builds the gem, installs it into a fresh RubyGems home, drives a Rack request plus a browser relay batch through the public SDK surface, validates event envelope shape, and confirms the mock ingestion endpoint receives the expected service, environment, SDK metadata, and correlation fields.
321
+
124
322
  ## Examples
125
323
 
126
324
  - Rack app: [examples/rack_app.rb](examples/rack_app.rb)
127
325
  - Rails initializer: [examples/rails_initializer.rb](examples/rails_initializer.rb)
128
326
  - Sidekiq server setup: [examples/sidekiq_initializer.rb](examples/sidekiq_initializer.rb)
129
327
 
130
- ## Local development
328
+ ## Development
131
329
 
132
330
  The project defaults to Docker-backed commands:
133
331
 
@@ -137,9 +335,10 @@ make test
137
335
  make compat
138
336
  make lint
139
337
  make build
338
+ make smoke
140
339
  ```
141
340
 
142
- Compatibility lanes are also exercised in CI for Rails 7.0 and 7.1, Rack 2.2 and 3.x, and Sidekiq 7.x and 8.x.
341
+ CI validates RSpec, RuboCop, compatibility lanes, gem build, and the clean-install smoke harness.
143
342
 
144
343
  ## Release
145
344
 
@@ -147,14 +346,7 @@ The repository ships a GitHub Actions release workflow at `.github/workflows/rel
147
346
 
148
347
  - Push a `v*` tag or run the workflow manually with a `version` input.
149
348
  - Configure the `RUBYGEMS_API_KEY` repository secret before enabling publish.
150
- - The workflow runs lint, tests, gem build, local smoke install, GitHub release creation, RubyGems publish, and a published-gem smoke install.
151
-
152
- ## Supported targets
153
-
154
- - Ruby 3.1+
155
- - Rails 7.0+
156
- - Rack 2.2+
157
- - Sidekiq 7.x+
349
+ - The workflow runs lint, tests, gem build, `make smoke`, RubyGems publish, and `make smoke-published VERSION=<tag>` before creating the GitHub release.
158
350
 
159
351
  ## Documentation
160
352
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DebugBundle
4
- VERSION = '0.1.0'
4
+ VERSION = '0.1.2'
5
5
  end
@@ -0,0 +1,137 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://debugbundle.com/schemas/event-envelope-ruby-fixture.json",
4
+ "oneOf": [
5
+ {
6
+ "$ref": "#/$defs/request_event_envelope"
7
+ },
8
+ {
9
+ "$ref": "#/$defs/log_event_envelope"
10
+ },
11
+ {
12
+ "$ref": "#/$defs/frontend_exception_envelope"
13
+ }
14
+ ],
15
+ "$defs": {
16
+ "string_map": {
17
+ "type": "object",
18
+ "additionalProperties": true
19
+ },
20
+ "service": {
21
+ "type": "object",
22
+ "additionalProperties": false,
23
+ "required": ["name", "environment"],
24
+ "properties": {
25
+ "name": { "type": "string", "minLength": 1 },
26
+ "runtime": { "type": ["string", "null"], "minLength": 1 },
27
+ "framework": { "type": ["string", "null"], "minLength": 1 },
28
+ "environment": { "type": "string", "minLength": 1 }
29
+ }
30
+ },
31
+ "correlation": {
32
+ "type": "object",
33
+ "additionalProperties": false,
34
+ "properties": {
35
+ "request_id": { "type": ["string", "null"] },
36
+ "trace_id": { "type": ["string", "null"] },
37
+ "session_id": { "type": ["string", "null"] },
38
+ "user_id_hash": { "type": ["string", "null"] }
39
+ }
40
+ },
41
+ "envelope_base": {
42
+ "type": "object",
43
+ "additionalProperties": false,
44
+ "required": [
45
+ "schema_version",
46
+ "event_id",
47
+ "event_type",
48
+ "sdk_name",
49
+ "sdk_version",
50
+ "service",
51
+ "occurred_at",
52
+ "correlation",
53
+ "payload"
54
+ ],
55
+ "properties": {
56
+ "schema_version": { "type": "string", "minLength": 1 },
57
+ "event_id": { "type": "string", "format": "uuid" },
58
+ "event_type": { "type": "string", "minLength": 1 },
59
+ "project_token": { "type": "string", "minLength": 1 },
60
+ "sdk_name": { "type": "string", "minLength": 1 },
61
+ "sdk_version": { "type": "string", "minLength": 1 },
62
+ "service": { "$ref": "#/$defs/service" },
63
+ "occurred_at": { "type": "string", "format": "date-time" },
64
+ "correlation": { "$ref": "#/$defs/correlation" },
65
+ "payload": {}
66
+ }
67
+ },
68
+ "request_event_envelope": {
69
+ "allOf": [
70
+ { "$ref": "#/$defs/envelope_base" },
71
+ {
72
+ "properties": {
73
+ "event_type": { "const": "request_event" },
74
+ "payload": {
75
+ "type": "object",
76
+ "additionalProperties": false,
77
+ "required": ["method", "path", "query", "headers", "response_status", "duration_ms"],
78
+ "properties": {
79
+ "method": { "type": "string", "minLength": 1 },
80
+ "path": { "type": "string", "minLength": 1 },
81
+ "query": { "$ref": "#/$defs/string_map" },
82
+ "headers": { "$ref": "#/$defs/string_map" },
83
+ "body": {},
84
+ "response_status": { "type": "integer", "minimum": 0 },
85
+ "duration_ms": { "type": "number", "minimum": 0 },
86
+ "route_template": { "type": ["string", "null"] },
87
+ "controller": { "type": ["string", "null"] },
88
+ "action": { "type": ["string", "null"] },
89
+ "response_headers": { "$ref": "#/$defs/string_map" },
90
+ "response_body": {}
91
+ }
92
+ }
93
+ }
94
+ }
95
+ ]
96
+ },
97
+ "log_event_envelope": {
98
+ "allOf": [
99
+ { "$ref": "#/$defs/envelope_base" },
100
+ {
101
+ "properties": {
102
+ "event_type": { "const": "log_event" },
103
+ "payload": {
104
+ "type": "object",
105
+ "additionalProperties": false,
106
+ "required": ["level", "message", "attributes"],
107
+ "properties": {
108
+ "level": { "type": "string", "minLength": 1 },
109
+ "message": { "type": "string", "minLength": 1 },
110
+ "attributes": { "$ref": "#/$defs/string_map" }
111
+ }
112
+ }
113
+ }
114
+ }
115
+ ]
116
+ },
117
+ "frontend_exception_envelope": {
118
+ "allOf": [
119
+ { "$ref": "#/$defs/envelope_base" },
120
+ {
121
+ "properties": {
122
+ "event_type": { "const": "frontend_exception" },
123
+ "sdk_name": { "const": "@debugbundle/sdk-browser" },
124
+ "payload": {
125
+ "type": "object",
126
+ "additionalProperties": true,
127
+ "required": ["message"],
128
+ "properties": {
129
+ "message": { "type": "string", "minLength": 1 }
130
+ }
131
+ }
132
+ }
133
+ }
134
+ ]
135
+ }
136
+ }
137
+ }
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'repository metadata' do
6
+ let(:repo_root) { File.expand_path('..', __dir__) }
7
+
8
+ def read_repository_file(path)
9
+ File.read(File.join(repo_root, path))
10
+ end
11
+
12
+ it 'ships the smoke harness and wires it into workflows' do
13
+ %w[
14
+ Makefile
15
+ smoke/run_app_driven_smoke.rb
16
+ smoke/app_driven_smoke_runner.rb
17
+ .github/workflows/ci.yml
18
+ .github/workflows/release.yml
19
+ ].each do |relative_path|
20
+ expect(File).to exist(File.join(repo_root, relative_path))
21
+ end
22
+
23
+ makefile = read_repository_file('Makefile')
24
+ ci_workflow = read_repository_file('.github/workflows/ci.yml')
25
+ release_workflow = read_repository_file('.github/workflows/release.yml')
26
+
27
+ [
28
+ '.PHONY: smoke',
29
+ '.PHONY: smoke-published',
30
+ 'smoke/run_app_driven_smoke.rb --source local',
31
+ 'smoke/run_app_driven_smoke.rb --source published --version $(VERSION)'
32
+ ].each do |fragment|
33
+ expect(makefile).to include(fragment)
34
+ end
35
+
36
+ expect(ci_workflow).to include('make smoke')
37
+ expect(release_workflow).to include('make smoke')
38
+ expect(release_workflow).to include('make smoke-published VERSION=${RELEASE_VERSION}')
39
+ end
40
+
41
+ it 'documents the Ruby release documentation gates in the README' do
42
+ readme = read_repository_file('README.md')
43
+
44
+ [
45
+ '## Configuration Reference',
46
+ 'Configuration sources and precedence:',
47
+ 'Capture-policy fields are server-owned',
48
+ '## Install Examples by Mode',
49
+ '## Runtime and Framework Support',
50
+ '## Dependency Alignment',
51
+ '## Browser Relay',
52
+ '## Service Naming',
53
+ '## Safe Startup and Status',
54
+ '## First-Event Verification',
55
+ 'make smoke',
56
+ 'same-origin',
57
+ 'allowed origins',
58
+ 'rate limiting',
59
+ 'credential isolation',
60
+ 'missing token'
61
+ ].each do |fragment|
62
+ expect(readme).to include(fragment)
63
+ end
64
+ end
65
+ end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: debugbundle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - DebugBundle
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2026-05-25 00:00:00.000000000 Z
10
+ date: 2026-05-26 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: base64
@@ -71,6 +70,7 @@ files:
71
70
  - spec/client_spec.rb
72
71
  - spec/debugbundle_spec.rb
73
72
  - spec/file_transport_spec.rb
73
+ - spec/fixtures/event-envelope.schema.json
74
74
  - spec/logger_integration_spec.rb
75
75
  - spec/rack_integration_spec.rb
76
76
  - spec/rack_middleware_spec.rb
@@ -79,6 +79,7 @@ files:
79
79
  - spec/redaction_spec.rb
80
80
  - spec/relay_spec.rb
81
81
  - spec/remote_config_spec.rb
82
+ - spec/repository_metadata_spec.rb
82
83
  - spec/sidekiq_integration_spec.rb
83
84
  - spec/sidekiq_middleware_spec.rb
84
85
  - spec/spec_helper.rb
@@ -91,7 +92,6 @@ metadata:
91
92
  source_code_uri: https://github.com/debugbundle/debugbundle-ruby
92
93
  changelog_uri: https://github.com/debugbundle/debugbundle-ruby/blob/main/CHANGELOG.md
93
94
  rubygems_mfa_required: 'true'
94
- post_install_message:
95
95
  rdoc_options: []
96
96
  require_paths:
97
97
  - lib
@@ -106,8 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
106
  - !ruby/object:Gem::Version
107
107
  version: '0'
108
108
  requirements: []
109
- rubygems_version: 3.4.19
110
- signing_key:
109
+ rubygems_version: 3.6.2
111
110
  specification_version: 4
112
111
  summary: DebugBundle SDK for Ruby
113
112
  test_files: []