Skip to content

Release 0.2.0#21

Merged
bradh11 merged 8 commits into
mainfrom
release/0.2.0
Apr 14, 2026
Merged

Release 0.2.0#21
bradh11 merged 8 commits into
mainfrom
release/0.2.0

Conversation

@bradh11
Copy link
Copy Markdown
Owner

@bradh11 bradh11 commented Apr 14, 2026

Release 0.2.0

Minor version bump. This release sweeps every unreleased commit on develop since v0.1.4 onto main and tags 0.2.0.

Why a minor (not patch) bump

This release contains genuine breaking changes under semver, even for a pre-1.0 library:

  • Validator author contract: user args on validate() must now be keyword-only, type-annotated, and have a default value. Enforcement runs in __init_subclass__ at import time. Third-party validators that don't conform will raise TypeError at import.
  • subject_alt_names / sensitive_date signatures: both validators' user arguments became keyword-only. Direct positional callers break.
  • validator_args canonical form is now a nested dict. The pre-0.2.0 bare-list shorthand still works with a DeprecationWarning (migration, not a hard break).
  • Rust toolchain floor moved from 1.86 to 1.88 (transitively via the time 0.3.47 security bump). Affects contributors and source builds only — published wheels are unaffected.

See CHANGELOG.md for the full per-category list and migration notes.

What's in this release

Highlights (full list in the CHANGELOG):

  • Dynamic validator argument dispatch (closes [Feature]: Make validator args dynamic #18): new validators get argument passing for free, no core changes needed.
  • CertMonitor.describe_validators(): introspection helper returning every validator's arg schema.
  • sensitive_date cleanup: accepts bare date, ISO strings, (name, date) tuples, and datetime in addition to SensitiveDate; adds structured sensitive_date_matches field; weekend/leap-day conditions now emit warning strings; errors returned as dicts instead of raise.
  • Security: RUSTSEC-2026-0009 fix (time crate bumped to 0.3.47).
  • CHANGELOG backfill: the missing [0.1.4] section was reconstructed from the published release notes.

Commits included

  • chore: uv.lock format migration (revision 2 -> 3)
  • chore: prepare 0.2.0 release (version bumps + CHANGELOG)
  • fix: sensitive_date validator cleanup and ergonomics (#20)
  • feat: dynamic validator args dispatch (#18) (#19)
  • housekeeping: remove obsolete test scripts for public key verification
  • feat: :sparkles: Add SensitiveDateValidator (#15)
  • Add Contributor Code of Conduct and remove Rust toolchain installation instructions (#17)

Test plan

  • make test green at 0.2.0 (ruff format/lint, cargo fmt/clippy, 360 pytest at 98.73%, mypy, cargo audit, bandit, wheel build)
  • cargo check regenerated Cargo.lock with certinfo v0.2.0
  • Release notes will be auto-extracted by release.yml from the ## [0.2.0] CHANGELOG section on tag push
  • GitHub Actions CI green on this PR
  • Tag v0.2.0 will be pushed manually after this PR merges to main

After merge

Per the existing release workflow:

  1. This PR merges to main (release notes and version bump land there).
  2. You push the v0.2.0 tag on main — that triggers release.yml and creates the GitHub Release from the CHANGELOG section. PyPI publishing is handled by ci.yml.
  3. develop needs a fast-forward (or merge) from main to pick up the version bump + CHANGELOG so the next round of development starts from the right base.

bradh11 and others added 8 commits June 17, 2025 16:05
* feat: ✨ Add SensitiveDateValidator

* README

* Docs, linting, and additional tests

* MODULARIZATION_REPORT

* Typos
* security: bump time crate to 0.3.47 (RUSTSEC-2026-0009)

Updates Cargo.lock to pull in time >=0.3.47, addressing the DoS via
stack exhaustion advisory flagged by cargo audit. The vulnerable
version was pulled in transitively through x509-parser 0.16.0.

Requires rustc >=1.88.0, which matches what the CI rust job already
installs via dtolnay/rust-toolchain@stable.

* feat: dynamic validator args dispatch (#18)

Replaces the hardcoded subject_alt_names special case in core.validate()
with a generic dispatch that discovers each validator's user arguments
from its validate() signature. New validators automatically participate
in argument passing without any core changes.

Contract for validator authors:

  - The first three positional params of validate() are framework-
    supplied (cert/cipher data, host, port). Their names don't matter.
  - Any additional user-configurable arguments must be keyword-only,
    annotated, and have a default value.
  - Enforcement runs in BaseCertValidator/BaseCipherValidator
    __init_subclass__ at import time; malformed signatures raise
    TypeError before the class can be used.

Performance: signature inspection happens once, at class definition,
via __init_subclass__. The per-call dispatch hot path is a frozen-set
difference and a dict unpack — sub-microsecond, zero inspect calls.

User-facing API:

  - Canonical form: validate(validator_args={"name": {"arg": value}})
  - The bare-list shorthand (subject_alt_names=[...]) still works for
    one-arg validators but now emits a DeprecationWarning.
  - New CertMonitor.describe_validators() returns every validator's
    name, doc, and argument schema (annotation, default) for
    introspection — reads the cached _user_params populated at class
    definition time.

Validator migrations (signature changes only, no behavior changes):

  - subject_alt_names: alternate_names is now keyword-only.
  - sensitive_date: *args: SensitiveDate -> *, dates: Optional[List[
    SensitiveDate]] = None. Weekend/leap-day flagging, return shape,
    and the internal isinstance check are unchanged.

Existing tests that asserted positional dispatch were updated to the
kwargs form. New tests cover enforcement (well-formed, missing
annotation, missing default, *args, **kwargs, cipher parity), dispatch
(canonical dict form, deprecation shim with pytest.warns, unknown arg,
invalid arg type, validator raising TypeError), and describe_validators
(shape, per-validator args, plain-class annotation rendering).

Coverage: 98.67% (gate 95%); validators/base.py and all new core
dispatch code are at 100%.
Follow-up to #18 addressing the issues that held this validator back
from the last release. Purely additive changes to public output; core
behavior (weekend / leap-day / user-date matching) is unchanged.

Changes
-------

- Error handling: replaces ``raise TypeError`` for malformed input with
  a structured error dict, matching every other validator's contract.
  The #18 dispatch layer also catches TypeError as a safety net, but
  the check now lives where it belongs — at the top of ``validate()``.

- Input normalization: ``dates`` now accepts any of ``SensitiveDate``,
  ``date``, ``datetime``, an ISO 8601 string, or a ``(name, date)``
  tuple. Bare dates and ISO strings auto-generate names from the ISO
  form, so users reading blackout dates from a YAML file or a simple
  list don't have to import the ``SensitiveDate`` named tuple just to
  use the validator. All forms can be mixed freely in a single call.

- Structured match field: adds ``sensitive_date_matches`` to the return
  dict — a list of ``{"name", "date"}`` entries for every user-supplied
  date that matched. Callers that previously had to regex-parse the
  ``warnings`` list can now read a machine-friendly field. ``warnings``
  is preserved for human-readable summaries.

- Weekend / leap-day warning strings: when ``weekend_expiry`` or
  ``leapday_expiry`` fire, a corresponding human-readable line is now
  appended to ``warnings``. Previously these conditions set booleans but
  produced empty warnings, which was confusing when scanning logs.

- Shared ``parse_not_after`` helper: extracts the ``notAfter`` format
  string into ``certmonitor/validators/_utils.py`` and migrates both
  ``expiration`` and ``sensitive_date`` to use it. Future format changes
  only need to touch one place.

Docs
----

- Adds a sensitive_date example to ``docs/usage/validator_args.md``
  showing all four input forms.
- Adds the previously-missing ``SensitiveDate`` nav entry to
  ``mkdocs.yml`` so the validator's auto-generated reference page is
  reachable.
- Regenerates ``MODULARIZATION_REPORT.md``.

Tests
-----

New coverage: one test per input form (SensitiveDate, date, datetime,
ISO string, ``(name, date)`` tuple, ``(name, datetime)`` tuple, mixed);
weekend warning string content for both Saturday and Sunday; leap-day
warning string; structured ``sensitive_date_matches`` field; structured
error dicts for invalid type, malformed ISO string, and bad tuple shape;
``dates=[]`` and ``dates=None`` behavior.

All 360 tests pass; coverage 98.73% (sensitive_date.py, _utils.py, and
expiration.py all at 100%).

Depends on #18 (branches off feature/dynamic-validator-args).
- Bump certmonitor to 0.2.0 in pyproject.toml
- Bump certinfo (Rust crate) to 0.2.0 in Cargo.toml and regenerate Cargo.lock
- Backfill missing CHANGELOG entry for 0.1.4 from the published release notes
- Add [0.2.0] CHANGELOG section with Breaking changes, Added, Changed,
  Deprecated, and Fixed categories

This is a minor version bump because the release contains genuine breaking
changes: the validator author contract now requires user args to be
keyword-only, annotated, and defaulted (enforced at import time), and the
subject_alt_names and sensitive_date validator signatures changed. The
bare-list validator_args shorthand still works via a deprecation shim.

Also bumps the minimum rustc from 1.86 to 1.88 transitively via the time
0.3.47 upgrade that addresses RUSTSEC-2026-0009. Affects contributors and
source builds only; published wheels are unaffected.
Automated reformat performed by uv during ``make develop`` — bumps the
lockfile schema revision and renames ``upload_time`` to ``upload-time``
across every entry. No package versions change.

Separated from the 0.2.0 release-prep commit so that commit stays
reviewable.
- Fix duplicated sections that accumulated from earlier edits during
  the version bump from 0.1.5 to 0.2.0
- Rewrite the 0.2.0 entry to match the marketing style used for the
  v0.1.4 release notes (header, overview, emoji section markers,
  Python compatibility + license footer, Full Changelog compare link)
- Reframe validator author contract changes as notable Changed items
  rather than breaking changes — no user-facing API breaks, existing
  validator_args call style still works via the deprecation shim
- Correct the overview: the sensitive_date validator itself is what
  has been sitting on develop since #15; input form improvements came
  along with this release
@bradh11 bradh11 closed this Apr 14, 2026
@bradh11 bradh11 reopened this Apr 14, 2026
@bradh11 bradh11 merged commit 4b98d76 into main Apr 14, 2026
15 checks passed
@bradh11 bradh11 deleted the release/0.2.0 branch April 17, 2026 00:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants