bronto-sdk 0.2.1__tar.gz
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.
- bronto_sdk-0.2.1/.gitignore +69 -0
- bronto_sdk-0.2.1/.python-version +1 -0
- bronto_sdk-0.2.1/CHANGELOG.md +140 -0
- bronto_sdk-0.2.1/CODE_OF_CONDUCT.md +10 -0
- bronto_sdk-0.2.1/CONTRIBUTING.md +205 -0
- bronto_sdk-0.2.1/LICENSE +21 -0
- bronto_sdk-0.2.1/PKG-INFO +382 -0
- bronto_sdk-0.2.1/README.md +343 -0
- bronto_sdk-0.2.1/SECURITY.md +111 -0
- bronto_sdk-0.2.1/VERSION +1 -0
- bronto_sdk-0.2.1/api/README.md +53 -0
- bronto_sdk-0.2.1/api/openapi.yaml +17180 -0
- bronto_sdk-0.2.1/api/upstream.sha256 +1 -0
- bronto_sdk-0.2.1/api/vendored.sha256 +1 -0
- bronto_sdk-0.2.1/examples/README.md +20 -0
- bronto_sdk-0.2.1/examples/async_search.py +56 -0
- bronto_sdk-0.2.1/examples/get_monitor.py +32 -0
- bronto_sdk-0.2.1/examples/mcp_endpoint.py +33 -0
- bronto_sdk-0.2.1/examples/search_logs.py +43 -0
- bronto_sdk-0.2.1/justfile +103 -0
- bronto_sdk-0.2.1/pyproject.toml +177 -0
- bronto_sdk-0.2.1/scripts/dist-check.sh +102 -0
- bronto_sdk-0.2.1/scripts/release-notes.sh +42 -0
- bronto_sdk-0.2.1/scripts/ruleset-drift.sh +91 -0
- bronto_sdk-0.2.1/scripts/spec-verify.sh +75 -0
- bronto_sdk-0.2.1/scripts/version-check.sh +93 -0
- bronto_sdk-0.2.1/src/bronto_sdk/__init__.py +57 -0
- bronto_sdk-0.2.1/src/bronto_sdk/_async_client.py +245 -0
- bronto_sdk-0.2.1/src/bronto_sdk/_auth.py +40 -0
- bronto_sdk-0.2.1/src/bronto_sdk/_client.py +234 -0
- bronto_sdk-0.2.1/src/bronto_sdk/_client_config.py +170 -0
- bronto_sdk-0.2.1/src/bronto_sdk/_errors.py +335 -0
- bronto_sdk-0.2.1/src/bronto_sdk/_regions.py +73 -0
- bronto_sdk-0.2.1/src/bronto_sdk/_request_options.py +216 -0
- bronto_sdk-0.2.1/src/bronto_sdk/_transport.py +110 -0
- bronto_sdk-0.2.1/src/bronto_sdk/_version.py +8 -0
- bronto_sdk-0.2.1/src/bronto_sdk/domain/__init__.py +39 -0
- bronto_sdk-0.2.1/src/bronto_sdk/domain/datasets.py +26 -0
- bronto_sdk-0.2.1/src/bronto_sdk/domain/query.py +104 -0
- bronto_sdk-0.2.1/src/bronto_sdk/domain/time_range.py +310 -0
- bronto_sdk-0.2.1/src/bronto_sdk/mcp/__init__.py +77 -0
- bronto_sdk-0.2.1/src/bronto_sdk/models/__init__.py +71 -0
- bronto_sdk-0.2.1/src/bronto_sdk/models/_common.py +49 -0
- bronto_sdk-0.2.1/src/bronto_sdk/models/_dataset.py +50 -0
- bronto_sdk-0.2.1/src/bronto_sdk/models/_error.py +22 -0
- bronto_sdk-0.2.1/src/bronto_sdk/models/_metric.py +21 -0
- bronto_sdk-0.2.1/src/bronto_sdk/models/_monitor.py +258 -0
- bronto_sdk-0.2.1/src/bronto_sdk/models/_search.py +344 -0
- bronto_sdk-0.2.1/src/bronto_sdk/py.typed +0 -0
- bronto_sdk-0.2.1/src/bronto_sdk/resources/__init__.py +28 -0
- bronto_sdk-0.2.1/src/bronto_sdk/resources/_datasets.py +133 -0
- bronto_sdk-0.2.1/src/bronto_sdk/resources/_metrics.py +99 -0
- bronto_sdk-0.2.1/src/bronto_sdk/resources/_monitors.py +240 -0
- bronto_sdk-0.2.1/src/bronto_sdk/resources/_search.py +107 -0
- bronto_sdk-0.2.1/tests/__init__.py +1 -0
- bronto_sdk-0.2.1/tests/conftest.py +153 -0
- bronto_sdk-0.2.1/tests/domain/__init__.py +1 -0
- bronto_sdk-0.2.1/tests/domain/test_datasets.py +46 -0
- bronto_sdk-0.2.1/tests/domain/test_query.py +90 -0
- bronto_sdk-0.2.1/tests/domain/test_time_range.py +231 -0
- bronto_sdk-0.2.1/tests/mcp/__init__.py +1 -0
- bronto_sdk-0.2.1/tests/mcp/test_mcp_registry.py +78 -0
- bronto_sdk-0.2.1/tests/unit/__init__.py +1 -0
- bronto_sdk-0.2.1/tests/unit/models/__init__.py +1 -0
- bronto_sdk-0.2.1/tests/unit/models/test_big_integers.py +117 -0
- bronto_sdk-0.2.1/tests/unit/models/test_dataset.py +53 -0
- bronto_sdk-0.2.1/tests/unit/models/test_error.py +84 -0
- bronto_sdk-0.2.1/tests/unit/models/test_exports.py +52 -0
- bronto_sdk-0.2.1/tests/unit/models/test_monitor.py +163 -0
- bronto_sdk-0.2.1/tests/unit/models/test_search_request.py +131 -0
- bronto_sdk-0.2.1/tests/unit/models/test_search_response.py +200 -0
- bronto_sdk-0.2.1/tests/unit/resources/__init__.py +1 -0
- bronto_sdk-0.2.1/tests/unit/resources/test_datasets.py +86 -0
- bronto_sdk-0.2.1/tests/unit/resources/test_exports.py +60 -0
- bronto_sdk-0.2.1/tests/unit/resources/test_metrics.py +90 -0
- bronto_sdk-0.2.1/tests/unit/resources/test_monitors.py +205 -0
- bronto_sdk-0.2.1/tests/unit/resources/test_search.py +121 -0
- bronto_sdk-0.2.1/tests/unit/test_async_client.py +103 -0
- bronto_sdk-0.2.1/tests/unit/test_auth.py +16 -0
- bronto_sdk-0.2.1/tests/unit/test_client.py +114 -0
- bronto_sdk-0.2.1/tests/unit/test_client_parity.py +175 -0
- bronto_sdk-0.2.1/tests/unit/test_errors.py +174 -0
- bronto_sdk-0.2.1/tests/unit/test_exports.py +28 -0
- bronto_sdk-0.2.1/tests/unit/test_regions.py +75 -0
- bronto_sdk-0.2.1/tests/unit/test_request_options.py +129 -0
- bronto_sdk-0.2.1/tests/unit/test_required_checks.py +257 -0
- bronto_sdk-0.2.1/tests/unit/test_transport.py +110 -0
- bronto_sdk-0.2.1/tests/unit/test_version.py +91 -0
- bronto_sdk-0.2.1/uv.lock +2355 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
parts/
|
|
18
|
+
sdist/
|
|
19
|
+
var/
|
|
20
|
+
wheels/
|
|
21
|
+
share/python-wheels/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
.installed.cfg
|
|
24
|
+
*.egg
|
|
25
|
+
MANIFEST
|
|
26
|
+
|
|
27
|
+
# Unit test / coverage reports
|
|
28
|
+
htmlcov/
|
|
29
|
+
.tox/
|
|
30
|
+
.nox/
|
|
31
|
+
.coverage
|
|
32
|
+
.coverage.*
|
|
33
|
+
coverage.xml
|
|
34
|
+
*.cover
|
|
35
|
+
.cache
|
|
36
|
+
.hypothesis/
|
|
37
|
+
.pytest_cache/
|
|
38
|
+
|
|
39
|
+
# Linters / type checkers
|
|
40
|
+
.ruff_cache/
|
|
41
|
+
.mypy_cache/
|
|
42
|
+
|
|
43
|
+
# pyenv / uv — .python-version IS committed here (see the negation below) so that
|
|
44
|
+
# every checkout and CI run develops on the same interpreter.
|
|
45
|
+
.python-version
|
|
46
|
+
!/.python-version
|
|
47
|
+
|
|
48
|
+
# Environments
|
|
49
|
+
.env
|
|
50
|
+
.venv
|
|
51
|
+
env/
|
|
52
|
+
venv/
|
|
53
|
+
ENV/
|
|
54
|
+
|
|
55
|
+
# Visual Studio Code — settings and extension recommendations are checked in
|
|
56
|
+
# so every contributor and CI see the same diagnostics; everything else is local.
|
|
57
|
+
.vscode/*
|
|
58
|
+
!.vscode/settings.json
|
|
59
|
+
!.vscode/extensions.json
|
|
60
|
+
.idea/
|
|
61
|
+
|
|
62
|
+
# Git
|
|
63
|
+
*.orig
|
|
64
|
+
|
|
65
|
+
# Internal planning notes. The full v0.1 plan names private sibling projects and
|
|
66
|
+
# internal infrastructure; it lives outside this repository. Ignored here so a
|
|
67
|
+
# stray copy can never be committed, or swept into an sdist by a build.
|
|
68
|
+
/high-level-overview-plan.md
|
|
69
|
+
/*-plan.md
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
Breaking changes are listed first with a ⚠️ prefix.
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
## [0.2.1] - 2026-11-11
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Published on [PyPI](https://pypi.org/project/bronto-sdk/)** — install with
|
|
16
|
+
`pip install bronto-sdk` (or `bronto-sdk[mcp]`) instead of a GitHub Release
|
|
17
|
+
URL or a `git+https` requirement. Uploads use Trusted Publishing from the
|
|
18
|
+
release workflow (no API token exists) and carry PEP 740 provenance
|
|
19
|
+
attestations; PyPI receives exactly the files that are Sigstore-signed and
|
|
20
|
+
attached to the GitHub Release, which continues as before.
|
|
21
|
+
[SECURITY.md](https://github.com/bronto-community/bronto-sdk-python/blob/main/SECURITY.md#verifying-a-release)
|
|
22
|
+
covers verifying both.
|
|
23
|
+
|
|
24
|
+
### Security
|
|
25
|
+
|
|
26
|
+
- Refreshed `uv.lock` to PyJWT 2.15.1 and urllib3 2.8.0. Both reach the lock
|
|
27
|
+
only through the `mcp`/`langchain` extras and dev tooling; the SDK never
|
|
28
|
+
imports them and its published dependency ranges are unchanged, so installed
|
|
29
|
+
users resolve their own versions and are not affected.
|
|
30
|
+
- Refreshed `uv.lock` to certifi 2026.10.23, which drops the emSign root CAs
|
|
31
|
+
whose intermediate keys were compromised (GHSA-6q48-6c7w-9gw2). certifi
|
|
32
|
+
reaches the lock only transitively through `httpx`; the SDK's published
|
|
33
|
+
dependency ranges are unchanged, so installed users resolve their own
|
|
34
|
+
certifi and should upgrade it in their own environments.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- Refreshed `uv.lock` to pydantic 2.14.1, langchain-core 1.7.3, and newer dev
|
|
39
|
+
tooling (ruff 0.17.3, pytest 9.2.0, pytest-mock 3.17.0, pytest-asyncio
|
|
40
|
+
1.4.1.post1), and bumped the pinned CI actions. Published dependency ranges
|
|
41
|
+
are unchanged.
|
|
42
|
+
|
|
43
|
+
## [0.2.0] - 2026-09-29
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- **`client.metrics.retrieve_definition(metric_id)`** — `GET
|
|
48
|
+
/metrics/definitions/{id}`, returning a `MetricDefinition`.
|
|
49
|
+
|
|
50
|
+
## [0.1.2] - 2026-09-18
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- **Signed release artifacts.** The wheel and sdist are now signed with
|
|
55
|
+
[Sigstore](https://www.sigstore.dev/) during the release workflow, and a
|
|
56
|
+
`.sigstore.json` bundle is attached to the release beside each one. Signing is
|
|
57
|
+
keyless: the signature is made with a short-lived identity minted for that one
|
|
58
|
+
workflow run, so there is no signing key to hold and the repository still
|
|
59
|
+
stores no secrets. The signature attests that an artifact came from this
|
|
60
|
+
repository's release workflow running on the tag it claims.
|
|
61
|
+
|
|
62
|
+
Verifying needs only pip — `pip install sigstore`, then
|
|
63
|
+
`python -m sigstore verify identity`. [SECURITY.md](SECURITY.md) has the full
|
|
64
|
+
command. The release workflow verifies its own signatures before publishing,
|
|
65
|
+
so an artifact whose signature does not check never reaches a release.
|
|
66
|
+
|
|
67
|
+
### Note
|
|
68
|
+
|
|
69
|
+
- `v0.1.1` was tagged but never released: its release workflow failed while
|
|
70
|
+
signing, before anything was published. The tag is left in place because
|
|
71
|
+
release tags are immutable here. There is no v0.1.1 release, and nothing was
|
|
72
|
+
ever published under it.
|
|
73
|
+
|
|
74
|
+
## [0.1.0] - 2026-09-17
|
|
75
|
+
|
|
76
|
+
First release. Everything below is new.
|
|
77
|
+
|
|
78
|
+
### Added
|
|
79
|
+
|
|
80
|
+
- **Clients.** `BrontoClient` (sync) and `AsyncBrontoClient` (async) over
|
|
81
|
+
`httpx`, with identical method names, context-manager support, `close()` /
|
|
82
|
+
`aclose()`, and `from_env()` reading `BRONTO_API_KEY` plus `BRONTO_REGION` or
|
|
83
|
+
`BRONTO_BASE_URL`. A caller-supplied `http_client` is never closed by the SDK.
|
|
84
|
+
The async client adds a `max_concurrency` semaphore (default 20) bounding
|
|
85
|
+
in-flight requests. `client.get()` / `client.post()` remain public escape
|
|
86
|
+
hatches so an uncovered endpoint never blocks a consumer on an SDK release.
|
|
87
|
+
- **Two-mode credentials.** A client is either *client-bound* (built with
|
|
88
|
+
`api_key` or `bearer_token`) or *per-request* (built with neither, holding no
|
|
89
|
+
credential). The modes never mix and there is no fallback between them, which
|
|
90
|
+
makes a long-lived multi-tenant client safe by construction. Bearer wins over
|
|
91
|
+
API key within one origin; an empty per-request credential fails closed.
|
|
92
|
+
- **Error hierarchy.** `BrontoError`, `BrontoConfigError`,
|
|
93
|
+
`BrontoConnectionError` and `BrontoAPIError`, with status-mapped subclasses
|
|
94
|
+
for 400, 401, 403, 404, 429 (carrying `retry_after`) and 5xx. Errors expose
|
|
95
|
+
`status_code`, `correlation_id`, `details`, `error_type`, `body`, `headers`
|
|
96
|
+
and a `retryable` flag; `str()` always surfaces the status and correlation id.
|
|
97
|
+
No credential reaches an error, a `repr()` or a log line — headers are
|
|
98
|
+
redacted by key name and the stored body is bounded.
|
|
99
|
+
- **Regional registry.** `validate_region`, `rest_base_url`, `mcp_url` and
|
|
100
|
+
`ingest_base_url`. Region is a validated slug rather than a closed enum, so a
|
|
101
|
+
new region works without an SDK release while a value that would smuggle a
|
|
102
|
+
host off `bronto.io` is rejected. `KNOWN_REGIONS` is a hint for error
|
|
103
|
+
messages, never an allow-list. Redirects are not followed, deliberately.
|
|
104
|
+
- **Per-request options.** `RequestOptions` for `timeout`, `headers`, a
|
|
105
|
+
per-request credential, and an `idempotent` flag reserved for v0.2 retries.
|
|
106
|
+
- **`bronto_sdk.models`:** Pydantic v2 models for the covered REST subset —
|
|
107
|
+
`SearchRequest`/`SearchResponse`, `Monitor`/`MonitorsResponse`,
|
|
108
|
+
`MonitorEventsResponse`, `Dataset`/`DatasetsResponse` and `ErrorResponse`.
|
|
109
|
+
Response models accept unknown fields so a new server-side field is additive,
|
|
110
|
+
and no sequence number or epoch-millisecond field is typed `float`.
|
|
111
|
+
- **`bronto_sdk.resources`:** the typed operation surface, wired onto both
|
|
112
|
+
clients as `client.search`, `client.monitors` and `client.datasets` —
|
|
113
|
+
`search.run()`, `monitors.list()`, `monitors.retrieve()`, `monitors.events()`
|
|
114
|
+
and `datasets.list()`, each returning a parsed model. Path parameters are
|
|
115
|
+
URL-quoted.
|
|
116
|
+
- **`bronto_sdk.domain`:** pure Bronto helpers with no I/O. `iso_to_ms`,
|
|
117
|
+
`ms_to_iso`, `parse_time_range` and `parse_window` resolve natural-language
|
|
118
|
+
windows the way the query engine does, with no `dateparser` dependency;
|
|
119
|
+
`quote_value`, `quote_attribute` and `wildcard_pattern` escape values for a
|
|
120
|
+
where clause — **opt-in, never automatic**; `project_dataset` reads both the
|
|
121
|
+
REST and MCP dataset shapes.
|
|
122
|
+
- **`bronto_sdk.mcp`:** the MCP endpoint registry — `url()` (by region, or by
|
|
123
|
+
`base_url` for a staging deployment the region slug cannot name) and
|
|
124
|
+
`auth_headers()`. String and dict helpers only: no `mcp` or `langchain`
|
|
125
|
+
import, and no session opener until v0.2.
|
|
126
|
+
- Documentation: a full README with quickstarts, the credential model, the
|
|
127
|
+
error hierarchy, the domain helpers and a migration table from an ad-hoc
|
|
128
|
+
client, plus four runnable scripts in `examples/`.
|
|
129
|
+
|
|
130
|
+
### Not in this release
|
|
131
|
+
|
|
132
|
+
Automatic retries and backoff, pagination iterators, an MCP session opener,
|
|
133
|
+
LangChain tool decorators, OpenTelemetry hooks, an ingestion client (URL helper
|
|
134
|
+
only), and REST coverage beyond the five operations above.
|
|
135
|
+
|
|
136
|
+
[Unreleased]: https://github.com/bronto-community/bronto-sdk-python/compare/v0.2.1...HEAD
|
|
137
|
+
[0.2.1]: https://github.com/bronto-community/bronto-sdk-python/compare/v0.2.0...v0.2.1
|
|
138
|
+
[0.2.0]: https://github.com/bronto-community/bronto-sdk-python/compare/v0.1.2...v0.2.0
|
|
139
|
+
[0.1.2]: https://github.com/bronto-community/bronto-sdk-python/compare/v0.1.0...v0.1.2
|
|
140
|
+
[0.1.0]: https://github.com/bronto-community/bronto-sdk-python/releases/tag/v0.1.0
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
This project follows the [Contributor Covenant v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
|
|
4
|
+
|
|
5
|
+
In short: be respectful, be constructive, assume good faith. Harassment or
|
|
6
|
+
discriminatory behavior is not tolerated.
|
|
7
|
+
|
|
8
|
+
Report violations privately to the maintainers via
|
|
9
|
+
[GitHub's report-abuse flow](https://github.com/contact/report-abuse) or by
|
|
10
|
+
opening a confidential security advisory on this repository.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Contributing to bronto-sdk
|
|
2
|
+
|
|
3
|
+
We welcome bug reports, feature requests, and pull requests. For anything that
|
|
4
|
+
changes the public API or introduces substantial code, please open (or find) an
|
|
5
|
+
issue first so we can agree on an approach before you write the code. Typo and
|
|
6
|
+
documentation fixes do not need an issue.
|
|
7
|
+
|
|
8
|
+
## 5-minute path
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
git clone https://github.com/bronto-community/bronto-sdk-python
|
|
12
|
+
cd bronto-sdk-python
|
|
13
|
+
uv sync # create .venv and install the project + dev dependencies
|
|
14
|
+
just prepare # format, lint, typecheck, spec digest gate, tests
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
No network access or Bronto account is needed to run the test suite. All HTTP is
|
|
18
|
+
mocked with `httpx.MockTransport`, so the fast suite never touches the network.
|
|
19
|
+
|
|
20
|
+
## Development environment
|
|
21
|
+
|
|
22
|
+
- **[`uv`](https://docs.astral.sh/uv/)** manages the virtual environment and
|
|
23
|
+
dependencies. `uv sync` creates `.venv` and installs everything; commit
|
|
24
|
+
`uv.lock` whenever dependencies change so CI stays reproducible.
|
|
25
|
+
- **[`just`](https://just.systems/)** runs every project command through
|
|
26
|
+
`uv run`. `just --list` shows the recipes; the important ones are below.
|
|
27
|
+
- Minimum supported Python is **3.11**. Local development happens on the newest
|
|
28
|
+
interpreter, but `pyright` and `ruff` are pinned to the 3.11 floor so
|
|
29
|
+
3.12+-only syntax cannot slip past and break the oldest CI leg.
|
|
30
|
+
|
|
31
|
+
| Task | Command |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| Everything CI runs | `just prepare` |
|
|
34
|
+
| Format | `just format` / `just format-check` |
|
|
35
|
+
| Lint | `just lint` |
|
|
36
|
+
| Lint the shell gates in `scripts/` | `just lint-shell` |
|
|
37
|
+
| Typecheck (pyright strict on `src/bronto_sdk`) | `just typecheck` |
|
|
38
|
+
| Tests | `just test` / `just test-one <name>` |
|
|
39
|
+
| Build sdist + wheel | `just build` |
|
|
40
|
+
| Verify the vendored spec digest | `just check-spec` |
|
|
41
|
+
| Verify the three version files agree | `just check-version` |
|
|
42
|
+
| Verify the built artifacts | `just check-dist` |
|
|
43
|
+
| Verify the live ruleset matches the recorded checks | `just check-ruleset` |
|
|
44
|
+
| Preview a release body | `just release-notes X.Y.Z` |
|
|
45
|
+
|
|
46
|
+
## Tests
|
|
47
|
+
|
|
48
|
+
- Prefer test-driven development for new behavior.
|
|
49
|
+
- Use `pytest`, `pytest-mock`, and `pytest-asyncio` (`asyncio_mode = "auto"`).
|
|
50
|
+
- Mock all HTTP with `httpx.MockTransport`; the fast suite must not hit the
|
|
51
|
+
network.
|
|
52
|
+
- Sync and async clients expose identical method names — cover new client
|
|
53
|
+
behavior with the shared sync/async parity fixtures so both stay in step.
|
|
54
|
+
- Coverage target is 90%+, realistic given the small surface.
|
|
55
|
+
|
|
56
|
+
## The vendored OpenAPI spec
|
|
57
|
+
|
|
58
|
+
`api/openapi.yaml` is a conformance reference, never a codegen source. It is
|
|
59
|
+
digest-gated: any edit must ship with a regenerated `api/vendored.sha256` in the
|
|
60
|
+
same commit. See `api/README.md` for the full re-vendoring checklist. `just
|
|
61
|
+
check-spec` (part of `just prepare` and CI) fails on an unrecorded edit.
|
|
62
|
+
|
|
63
|
+
## Changelog
|
|
64
|
+
|
|
65
|
+
User-facing changes must add an entry under `## [Unreleased]` in
|
|
66
|
+
`CHANGELOG.md` (Keep a Changelog format). List breaking changes first with a
|
|
67
|
+
`⚠️` prefix. The pull request template has a `## Changelog` section for the same
|
|
68
|
+
purpose.
|
|
69
|
+
|
|
70
|
+
## Versioning
|
|
71
|
+
|
|
72
|
+
Semver from day one. `v0.x` means the public API may still shift; breaking
|
|
73
|
+
changes are called out with `⚠️` in the changelog. Bump the version with
|
|
74
|
+
`just update-version X.Y.Z`, which writes `VERSION`, `src/bronto_sdk/_version.py`
|
|
75
|
+
and `pyproject.toml` in one step.
|
|
76
|
+
|
|
77
|
+
## Releasing (maintainers)
|
|
78
|
+
|
|
79
|
+
A `vX.Y.Z` tag push runs `release.yml`, which builds the sdist and wheel,
|
|
80
|
+
attaches both to a **GitHub Release**, then publishes the very same files to
|
|
81
|
+
**[PyPI](https://pypi.org/project/bronto-sdk/)**. PyPI is what consumers
|
|
82
|
+
install from (`pip install bronto-sdk`).
|
|
83
|
+
|
|
84
|
+
PyPI uploads use [Trusted Publishing](https://docs.pypi.org/trusted-publishers/):
|
|
85
|
+
the `publish` job exchanges a short-lived GitHub OIDC token for an upload
|
|
86
|
+
credential, so there is no PyPI API token to store, rotate or leak. Do not add
|
|
87
|
+
one, and do not upload by hand with `twine` — a hand upload has no attestation
|
|
88
|
+
and is indistinguishable from a compromised one.
|
|
89
|
+
|
|
90
|
+
To cut a release:
|
|
91
|
+
|
|
92
|
+
1. Move the `## [Unreleased]` entries under a new `## [X.Y.Z] - YYYY-MM-DD`
|
|
93
|
+
heading in `CHANGELOG.md`, and add the link reference at the bottom. The
|
|
94
|
+
release body is extracted from this section, so a missing or empty one fails
|
|
95
|
+
the release — preview it with `just release-notes X.Y.Z`.
|
|
96
|
+
2. `just update-version X.Y.Z`. It verifies its own work; a version it cannot
|
|
97
|
+
write to all three files is now an error rather than a silent no-op.
|
|
98
|
+
3. `just prepare`, then commit.
|
|
99
|
+
4. Tag `vX.Y.Z` and push the tag.
|
|
100
|
+
5. **Approve the deployment.** The `release` job waits on the protected
|
|
101
|
+
`release` environment; approve it from the run's page in the Actions tab.
|
|
102
|
+
Either maintainer on the reviewer list can approve, including the person who
|
|
103
|
+
pushed the tag — the gate is there to make a release a deliberate, recorded
|
|
104
|
+
act, not to force a second pair of hands. The `publish` job runs on its own
|
|
105
|
+
once the GitHub Release exists; it needs no second approval.
|
|
106
|
+
6. Check [pypi.org/project/bronto-sdk](https://pypi.org/project/bronto-sdk/)
|
|
107
|
+
shows the version, with "Provenance" on each file.
|
|
108
|
+
|
|
109
|
+
If `publish` fails after the GitHub Release succeeded (PyPI outage,
|
|
110
|
+
misconfigured publisher), fix the cause and use **Re-run failed jobs** on the
|
|
111
|
+
same run. It re-uploads the artifacts the release job already signed; nothing
|
|
112
|
+
is rebuilt and no version is spent.
|
|
113
|
+
|
|
114
|
+
**A PyPI version is as permanent as its tag.** PyPI never accepts the same file
|
|
115
|
+
name twice, even after the release is deleted. A bad release is yanked on PyPI
|
|
116
|
+
(which hides it from new resolutions without breaking existing pins) and
|
|
117
|
+
superseded by the next patch version — never deleted and re-uploaded.
|
|
118
|
+
|
|
119
|
+
Signing is automatic and needs nothing from you: the workflow signs both
|
|
120
|
+
artifacts with Sigstore using its own short-lived OIDC identity, verifies the
|
|
121
|
+
signatures before publishing, and attaches a `.sigstore.json` bundle to each.
|
|
122
|
+
[SECURITY.md](SECURITY.md) documents how a consumer checks them.
|
|
123
|
+
|
|
124
|
+
The workflow gates the release on the tag matching the version files, then on
|
|
125
|
+
the same lint/test/build checks a pull request runs — literally the same file,
|
|
126
|
+
`.github/workflows/_checks.yml`, so a release cannot be verified on less than a
|
|
127
|
+
pull request is.
|
|
128
|
+
|
|
129
|
+
**Rehearse with `workflow_dispatch`, not with a tag.** Running `release.yml`
|
|
130
|
+
from `main` exercises the version gate, the full test matrix and the build; the
|
|
131
|
+
release and publish jobs are skipped because there is no tag.
|
|
132
|
+
|
|
133
|
+
### One-time PyPI setup
|
|
134
|
+
|
|
135
|
+
Already done for this repository; recorded so it can be checked or rebuilt.
|
|
136
|
+
Trusted Publishing only works when both sides name each other exactly:
|
|
137
|
+
|
|
138
|
+
- **PyPI**: the `bronto-sdk` project (owned by the Bronto PyPI organization) has
|
|
139
|
+
a GitHub trusted publisher with owner `bronto-community`, repository
|
|
140
|
+
`bronto-sdk-python`, workflow `release.yml`, environment `pypi`. Before the
|
|
141
|
+
first upload this is a *pending* publisher, added from the organization's
|
|
142
|
+
Publishing page so the project is created under the organization.
|
|
143
|
+
- **GitHub**: a `pypi` environment (Settings → Environments) whose deployment
|
|
144
|
+
policy allows `v*` tags only, and no reviewers — approving `release` is the
|
|
145
|
+
gate. Restricting it to tags means a workflow run from a branch cannot obtain
|
|
146
|
+
a token PyPI will accept, even if the workflow file is edited on that branch.
|
|
147
|
+
|
|
148
|
+
Renaming the workflow file or the environment breaks publishing until the PyPI
|
|
149
|
+
publisher is updated to match.
|
|
150
|
+
|
|
151
|
+
Do not push a throwaway `vX.Y.Zrc1` tag to rehearse. A repository ruleset makes
|
|
152
|
+
every `v*` tag immutable — it cannot be moved or deleted once pushed, by anyone,
|
|
153
|
+
including admins. That is deliberate: a tag a consumer has pinned must never
|
|
154
|
+
change underneath them. The practical consequence is that any `v*` tag you push
|
|
155
|
+
is permanent, so push one only when you mean it. A genuine pre-release is fine
|
|
156
|
+
(a version with a letter in it, like `v1.2.0rc1`, is published as a GitHub
|
|
157
|
+
pre-release and stays out of "latest"); a disposable one is not, because it
|
|
158
|
+
cannot be disposed of.
|
|
159
|
+
|
|
160
|
+
## What the repository enforces
|
|
161
|
+
|
|
162
|
+
Settings are kept in step with the `bronto-cli` sibling so that the two repos
|
|
163
|
+
are governed alike:
|
|
164
|
+
|
|
165
|
+
- `main` takes changes only through a pull request, with linear history, all
|
|
166
|
+
review threads resolved, and the eight `Checks / …` jobs green. Force-pushes
|
|
167
|
+
and deletion are blocked, with no bypass for anyone.
|
|
168
|
+
- `v*` tags cannot be moved or deleted (above).
|
|
169
|
+
- Actions must be pinned to a commit SHA — GitHub rejects a tag reference
|
|
170
|
+
outright, which is the same rule `zizmor` enforces in CI.
|
|
171
|
+
- Security vulnerabilities are reported privately; see [SECURITY.md](SECURITY.md).
|
|
172
|
+
|
|
173
|
+
The names of those eight required checks are mirrored in
|
|
174
|
+
`.github/required-status-checks.txt`. Two gates keep the mirror honest:
|
|
175
|
+
`tests/unit/test_required_checks.py` fails when the list stops matching the
|
|
176
|
+
names `ci.yml` and `_checks.yml` generate, and `just check-ruleset` (run weekly
|
|
177
|
+
by `ruleset-drift.yml`) fails when it stops matching the live ruleset.
|
|
178
|
+
|
|
179
|
+
### Renaming a required check
|
|
180
|
+
|
|
181
|
+
Read this before touching a job name or the test matrix. **A pull request that
|
|
182
|
+
renames or removes a required check wedges itself.** The old context stops
|
|
183
|
+
reporting on that very pull request, GitHub shows it as pending rather than
|
|
184
|
+
failed — a check that never reports is never late — and with no bypass actors
|
|
185
|
+
nobody can merge it, including admins.
|
|
186
|
+
|
|
187
|
+
Adding the new name to the ruleset first does not help: a required context that
|
|
188
|
+
no job has produced yet blocks every open pull request immediately. The only
|
|
189
|
+
order that never wedges anything is to **contract first**:
|
|
190
|
+
|
|
191
|
+
1. Remove the outgoing context from the `protect-main` ruleset
|
|
192
|
+
(Settings → Rules, or `gh api`).
|
|
193
|
+
2. Merge the pull request that renames the job or changes the matrix. It carries
|
|
194
|
+
the updated `.github/required-status-checks.txt`, which is what the offline
|
|
195
|
+
gate checks.
|
|
196
|
+
3. Add the new context to the ruleset.
|
|
197
|
+
|
|
198
|
+
Between steps 1 and 3 that one check is not enforced, which is the cost of never
|
|
199
|
+
being locked out. Dropping a Python version renames six contexts at once, so
|
|
200
|
+
expect to repeat step 1 for each.
|
|
201
|
+
|
|
202
|
+
## Code of Conduct
|
|
203
|
+
|
|
204
|
+
This project follows the [Contributor Covenant](CODE_OF_CONDUCT.md). By
|
|
205
|
+
participating you agree to abide by it.
|
bronto_sdk-0.2.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bronto
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|