0.2.0 — HTTP stack on httpware; Python 3.10 dropped
Breaking release. The provider HTTP stack is rebuilt on top of httpware 0.8.2 — a maintained sibling client framework — replacing the in-tree RetryingTransport + HttpClient plumbing that shipped in 0.1.x. Operator-visible behavior changes are listed below. Python 3.10 is no longer supported.
If you pin semvertag in CI and use Python 3.11+ already, you can skip straight to "Behavior changes" — the rest is internal.
What's new
- HTTP stack on httpware.
GitLabProviderholds ahttpware.Clientdirectly.httpware.Retrymiddleware replaces the hand-rolledRetryingTransport.httpware.PydanticDecoderdecodes response bodies viaresponse_model=— no more in-tree validator plumbing. - Smaller surface. Net ~600 LOC of HTTP plumbing deleted across the two migrations.
semvertag/_transport.pyandsemvertag/providers/_http.pyare gone; their tests deleted too. - Uniform error contract.
httpware.ClientErrorcovers transport failures, status errors, AND decode failures uniformly. semvertag's error tree (ConfigError/2,AuthError/3,ProviderAPIError/4) is unchanged at the CLI boundary — exit codes are stable. httpware[pydantic]>=0.8.2is a new transitive dependency. End users who installsemvertagfrom PyPI pick it up automatically.
Breaking changes
Python 3.10 dropped
requires-python = ">=3.11,<4". semvertag 0.1.x supported 3.10; 0.2.0 does not. The bump is forced by httpware's own floor.
Migration: upgrade your CI runners and local development environment to Python 3.11 or newer. If you cannot, stay on semvertag==0.1.1.
Behavior changes from the new retry middleware
The 0.1.x RetryingTransport is gone; httpware.Retry takes over. These are the user-observable differences in CI logs:
| Knob | 0.1.x | 0.2.0 |
|---|---|---|
| Retryable statuses | 408, 429, 500, 502, 503, 504 | same |
| Methods retried | all (transport-level) | idempotent only — POST not retried |
| Backoff base | 1.0 s, full-jitter | 0.1 s, full-jitter |
| Max sleep per attempt | implicit via 30 s wall cap | 5 s |
| Total-attempt wall cap | 30 s per request | none; httpware.RetryBudget (10 deposits + 20% retry ratio) caps storms across requests instead |
| Retry-After combine | max(server_hint, local_backoff) |
server hint honored verbatim, capped at max sleep |
Net effect for a typical CI run: faster transient-failure recovery (backoff starts 10× shorter), create_tag (POST) now fails immediately on a 429 instead of retrying — operator must rerun the job. All other surfaces unchanged.
Error message wording
Provider error messages produced by decoder failures (malformed JSON, schema mismatch) now read "GitLab _ProjectResponse response could not be decoded: <pydantic error>" instead of the 0.1.x "shape invalid: ..." / "malformed JSON in response body" wording. Only matters if you grep stderr for specific substrings. Exception types (ProviderAPIError) and exit codes (4) are unchanged.
Migration
For most users, the migration is one line in CI config:
- python-version: "3.10"
+ python-version: "3.11"If you grep stderr or test output for the old retry/decode error wording, update the strings. If you depend on create_tag POSTs being retried automatically on 429, add an outer retry loop in your CI script (e.g., gh workflow rerun on failure).
The semvertag CLI surface, exit codes, environment variables (SEMVERTAG_*), and the templates/semvertag.yml GitLab CI Catalog descriptor are all unchanged.
See also
Two design+implementation cycles fed this release:
- httpware migration — replace the in-tree HTTP stack with
httpware.Client+httpware.Retry. - Decoder adoption — switch the three GETs to use
response_model=/send_with_response, delete the in-tree validator helpers.