Skip to content

Commit 558b1d5

Browse files
lesnik512claude
andcommitted
docs: release notes for 1.1.0
Bug-audit-v2 cycle: 26 findings across 4 PRs (#108-#111). Lifecycle hardening, config validation, CI gate, generalized TeardownError aggregation. Two behavior changes called out: FastAPIConfig no longer stomps user app fields; CorsConfig wildcard+credentials now raises. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent be1e498 commit 558b1d5

1 file changed

Lines changed: 79 additions & 0 deletions

File tree

planning/releases/1.1.0.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# lite-bootstrap 1.1.0 — Lifecycle hardening, config validation, CI gate
2+
3+
**1.1.0 is a minor release. No intentional public-API breakage.** The two behavior changes that could affect existing code are fixes to genuine bugs and are called out in *Behavior changes* below.
4+
5+
This release closes a 26-finding bug-audit cycle (audits + retro live under `planning/specs/2026-06-05-bug-audit-v2*.md`). The changes split across four shipped PRs:
6+
7+
- **#108** — Lifecycle & teardown correctness (10 findings)
8+
- **#109** — Config UX & security validation (6 findings)
9+
- **#110** — Hygiene + CI gate (4 findings)
10+
- **#111** — Generalized `TeardownError` aggregation + cascade tests + README lifecycle docs (3 deferred follow-ups)
11+
12+
Test suite grew from 153 → 194 (+27%) at 100% line coverage throughout. `pip-audit` now runs on every PR and weekly via cron; a new `filterwarnings` config catches accidental `InstrumentSkippedWarning` emissions.
13+
14+
## New features
15+
16+
- **Injectable `prometheus_collector_registry` on `FastStreamConfig`.** Pass an existing `prometheus_client.CollectorRegistry` to expose counters registered elsewhere through FastStream's `/metrics` endpoint. Defaults to a fresh per-instance registry — fully backward compatible.
17+
- **`opentelemetry_excluded_urls` field on `FastStreamConfig`.** Was a `getattr` fallback before; now a discoverable, IDE-completable config field matching `FastAPIConfig` and `LitestarConfig`.
18+
- **`SentryInstrument.teardown()`.** Calls `sentry_sdk.flush(timeout=2)` then `sentry_sdk.init()` (no args) to reset the SDK to a no-op state. Previously the SDK stayed globally configured after bootstrapper teardown, leaking state across process-local tests.
19+
- **`FastStreamLoggingInstrument.teardown()`.** Restores `broker.config.logger.params_storage` to its pre-bootstrap value. The bootstrap mutated broker state; teardown didn't reverse it.
20+
21+
## Bug fixes
22+
23+
### Lifecycle & teardown (PR #108)
24+
25+
- **OpenTelemetry teardown now flushes spans and shuts down the tracer provider** (LOG-1, LOG-2). `bootstrap()` stored the `TracerProvider` only as a local; `teardown()` couldn't reach it to call `shutdown()`. Buffered spans in `BatchSpanProcessor` were dropped on graceful shutdown. Teardown also restores the two OTel-namespace stdlib loggers (`opentelemetry.instrumentation.instrumentor`, `opentelemetry.trace`) to their pre-bootstrap `disabled` state.
26+
- **`LoggingInstrument.teardown()` runs all cleanup steps even on partial failure** (LOG-3). A raise from any `handler.close()` previously left remaining handlers attached, skipped the root-level reset, and never called `close_handlers()` on the memory factory. Now wrapped in `try/finally` with per-handler error capture; all collected errors raise together via `TeardownError(errors)`.
27+
- **`LitestarOpenTelemetryInstrumentationMiddleware` cache evicts dead refs** (LOG-6). The old `dict[int, ASGIApp]` keyed by `id()` never evicted, holding wrapper `OpenTelemetryMiddleware` instances alive after Litestar dropped `next_app`. Replaced with `weakref.WeakKeyDictionary`; non-weakrefable apps fall through to the un-cached path.
28+
- **Double-bootstrap on the same application is now detected and warned** (LOG-7, LOG-8). Constructing two `FastMcpBootstrapper`s around the same `FastMCP` (or two `FastAPIBootstrapper`s around the same `FastAPI`) previously stacked teardown hooks. The second construction now emits a `UserWarning`, skips the re-attachment, and tells you the second bootstrapper's `teardown()` won't fire on ASGI shutdown.
29+
- **Generalized `TeardownError` aggregation across all instruments** (PR #111). `OpenTelemetryInstrument`, `FastStreamLoggingInstrument`, and `SentryInstrument` now run their full cleanup sequence even if an early step raises — aggregating errors into a single `TeardownError` or letting `super().teardown()` run via `try/finally`. Previously a misbehaving instrumentor / broker / Sentry flush silently skipped subsequent cleanup.
30+
- **Pyroscope's precondition check survives `python -O`** (LOG-5/SEC-4). Replaced two `assert` statements (in `_narrow_app` and `PyroscopeInstrument.bootstrap`) with explicit `raise TypeError(...)` and `raise RuntimeError(...)`. `python -O` strips asserts; the invariants now hold under all optimization levels. Closes the two `bandit` B101 findings.
31+
32+
### Config & security (PR #109)
33+
34+
- **`FastAPIConfig` no longer stomps user-supplied app's `title`/`debug`/`version`** (UX-1). Previously the three assignments ran unconditionally; a user passing `FastAPI(title="My API", version="3.0.0")` would silently have those clobbered by lite-bootstrap defaults. Now the assignments only run in the `UnsetType` branch (when lite-bootstrap constructed the app). **See Behavior changes below.**
35+
- **`enable_offline_docs` validates `request.scope["root_path"]` against the existing path allowlist** (SEC-1). Invalid root paths (e.g., HTML-injection payloads via a malicious upstream proxy's `X-Forwarded-Prefix`) now fall back to empty and emit a warning instead of being reflected into Swagger/Redoc HTML script tags. Threat model: not an issue in default ASGI deployments; only matters if `ProxyHeadersMiddleware` trusts upstream prefix headers.
36+
- **OpenTelemetry endpoint with `insecure=True` emits a warning for non-local hosts** (SEC-2). New `OpenTelemetryConfig.__post_init__` parses the endpoint (handles both `host:port` and `scheme://host:port` forms, including IPv6 brackets and `unix://`) and warns when traces would ship unencrypted to a non-`localhost`/`127.0.0.1`/`::1`/`unix://` target.
37+
- **`CorsConfig` rejects unsafe wildcard + credentials combos at construction** (SEC-3). `cors_allowed_credentials=True` combined with `cors_allowed_origins=["*"]` (or a permissive regex like `".*"`/`".+"`) is the canonical CORS misconfiguration — browsers reject the response. Now raises `ConfigurationError` immediately instead of silently building a non-functional CORS layer. **See Behavior changes below.**
38+
39+
### Hygiene & process (PR #110)
40+
41+
- **Missing-dependency events now log via stdlib `logging` in addition to `warnings.warn`** (UX-4). Users running under `python -W ignore` or `PYTHONWARNINGS=ignore` previously saw nothing when a configured instrument's optional dep was missing. The new `logger.warning` line on `lite_bootstrap.bootstrappers.base` is unaffected by warning filters.
42+
43+
## Behavior changes
44+
45+
Two changes could affect existing code in ways the previous release wouldn't have. Both fix real bugs; surfaced here so you can audit.
46+
47+
1. **User-supplied `FastAPI` instance retains its own `title`, `debug`, `version`.** If you were relying on lite-bootstrap to overwrite these from `service_name`/`service_debug`/`service_version` after handing it a pre-built `FastAPI()`, you'll now see your original values. Migration: set these on your `FastAPI()` directly, or use `FastAPIConfig(application_kwargs={...})` to have lite-bootstrap construct the app.
48+
49+
2. **`CorsConfig(cors_allowed_origins=["*"], cors_allowed_credentials=True)` now raises `ConfigurationError`.** This combo was never functional — browsers reject responses with `Access-Control-Allow-Credentials: true` and `Access-Control-Allow-Origin: *`. If your code constructed this combo and worked anyway (because the credentials header was silently dropped by FastAPI's CORSMiddleware), construction now fails with a clear message. Migration: enumerate allowed origins explicitly, or set `cors_allowed_credentials=False`.
50+
51+
## New constraints documented
52+
53+
Three lifecycle constraints surfaced by the audit are now documented in `README.md` and `CLAUDE.md`:
54+
55+
- **One bootstrapper per application instance.** Second construction emits a warning and skips re-attachment (see LOG-7/LOG-8 above).
56+
- **One `OpenTelemetryInstrument` per process.** The OTel SDK enforces `set_tracer_provider` as set-once via `_TRACER_PROVIDER_SET_ONCE.do_once(...)` (verified against `opentelemetry/trace/__init__.py:548-556`); `teardown()` cannot reset the global pointer.
57+
- **`__post_init__` cascade invariant.** Every config-class `__post_init__` must call `super().__post_init__()`. `BaseConfig` ships a no-op as the chain terminator. `FastAPIConfig` uses the explicit `super(FastAPIConfig, self).__post_init__()` form because `@dataclass(slots=True)` breaks bare `super()`.
58+
59+
## CI changes
60+
61+
- **`security-audit.yml` workflow added.** `pip-audit` runs on every PR and weekly via cron against the lockfile (`uv export --all-extras --no-hashes`). The default-branch run is purely informational; PRs that introduce CVEs will block until resolved.
62+
- **`InstrumentSkippedWarning` escalated to error in tests.** Any unexpected emission outside a `pytest.warns(...)` block now fails the test (registered via `pytest_configure()` in `tests/conftest.py`; can't live in `pyproject.toml` because that import order breaks pytest-cov tracing).
63+
64+
## Backwards compatibility
65+
66+
Aside from the two *Behavior changes* called out above, every public API behaves identically. New fields default to their old behavior (`prometheus_collector_registry=None` → fresh registry as before; `opentelemetry_excluded_urls=[]` → empty set as before). New warnings/validators trigger only on configurations that were already broken or risky.
67+
68+
The 26-fix list with full file:line references and rationale is in:
69+
70+
- `planning/specs/2026-06-05-bug-audit-v2.md` — the audit
71+
- `planning/specs/2026-06-05-bug-audit-v2-sequencing.md` — the 3-PR breakdown
72+
- `planning/specs/2026-06-05-bug-audit-v2-retro.md` — what the cycle taught us
73+
74+
## References
75+
76+
- Audit: [`planning/specs/2026-06-05-bug-audit-v2.md`](../specs/2026-06-05-bug-audit-v2.md)
77+
- Sequencing: [`planning/specs/2026-06-05-bug-audit-v2-sequencing.md`](../specs/2026-06-05-bug-audit-v2-sequencing.md)
78+
- Retro: [`planning/specs/2026-06-05-bug-audit-v2-retro.md`](../specs/2026-06-05-bug-audit-v2-retro.md)
79+
- PRs: [#108](https://github.com/modern-python/lite-bootstrap/pull/108), [#109](https://github.com/modern-python/lite-bootstrap/pull/109), [#110](https://github.com/modern-python/lite-bootstrap/pull/110), [#111](https://github.com/modern-python/lite-bootstrap/pull/111)

0 commit comments

Comments
 (0)