Skip to content

stability support - #153

Merged
Sebastian Thiel (Byron) merged 7 commits into
mainfrom
stability-support
Nov 1, 2026
Merged

Sebastian Thiel (Byron) merged 7 commits into
mainfrom
stability-support

Conversation

@Byron

@Byron Byron commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Tasks

  • real-world testing - minimal testing seems to work Ok, but the real test will be trying to release anything with the new system.

@Byron
Sebastian Thiel (Byron) force-pushed the stability-support branch 4 times, most recently from 6eb50fa to 6e14a0d Compare October 30, 2026 04:05
<!-- agent -->
A minor release of a stable dependency must not force major versions of its
workspace dependants. Use Cargo's first nonzero version component as the
compatibility boundary, including incompatible `0.0.x` patch releases.

Validation: reproduced the `1.2.3` to `1.3.0` failure before the fix, then
passed `cargo test --all --locked` (57 unit and integration tests).

Assisted-by: GPT 6.1 Sol
Co-authored-by: GPT 6.1 Sol <codex@openai.com>
@Byron
Sebastian Thiel (Byron) force-pushed the stability-support branch 3 times, most recently from ef616a3 to b6077ea Compare November 1, 2026 15:20
@Byron
Sebastian Thiel (Byron) marked this pull request as ready for review November 1, 2026 15:26
@Byron
Sebastian Thiel (Byron) requested a balanced review from Copilot November 1, 2026 15:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical defects can let incompatible APIs pass CI, hide published compatibility breaks, and produce release plans that omit required bumps or downgrade explicitly requested versions.

9 open issues
What changed in this PR

Adds API stability checks to release planning, with a reusable core library and a new cargo api-check command. Checked API boundaries can limit dependency-driven version bumps while preserving separate stable and experimental compatibility rules.

Changes:

  • Adds local API audits, baseline comparisons, and release-plan preflight checks.
  • Integrates compatibility results into version planning, dependency requirements, and stable breaking-change reports.
  • Supports detached-HEAD previews and adds regression tests and documentation.
File Description
tests/​smart_release/​release.rs Tests breaking-change reports and detached-HEAD behavior.
tests/​smart_release/​main.rs Registers Unix release API tests.
tests/​smart_release/​api.rs Tests checker integration and release planning.
tests/​api_check.rs Tests API commands, compatibility, and lockfile preservation.
tests/​api_check_local.rs Tests local checks and baseline selection.
src/​version.rs Adds experimental minor floors and Cargo compatibility rules.
src/​traverse.rs Integrates API checks into dependency bump propagation.
src/​lib.rs Registers the release checker module.
src/​git/​history.rs Collects history from detached HEAD.
src/​context.rs Supports supplied metadata and detached-HEAD history.
src/​command/​release/​mod.rs Runs checks and reports stable breaking releases.
src/​command/​release/​manifest.rs Constrains experimental dependency requirements.
src/​command/​release/​git.rs Handles detached-HEAD push previews.
src/​command/​mod.rs Exposes the API preflight command.
src/​command/​changelog.rs Enables changelog generation from detached HEAD.
src/​command/​api_check.rs Implements release-plan preflight checks.
src/​cli/​options.rs Registers the API-check subcommand.
src/​cli/​main.rs Dispatches API-check requests.
src/​cli/​main-api-check.rs Adds the API-check binary entry point.
src/​cli/​api_check.rs Defines API-check options and exit handling.
src/​api.rs Executes configured checkers and tracks compatibility.
README.md Documents stability policies, commands, and protocols.
Cargo.toml Registers the binary and core workspace dependency.
Cargo.lock Records core dependencies and the index upgrade.
cargo-smart-release-core/​src/​lib.rs Exposes the reusable API-check module.
cargo-smart-release-core/​src/​api_check/​types.rs Defines requests and compatibility reports.
cargo-smart-release-core/​src/​api_check/​mod.rs Implements metadata handling and baseline selection.
cargo-smart-release-core/​src/​api_check/​check.rs Implements rustdoc audits and API comparisons.
cargo-smart-release-core/​README.md Introduces the core library and requirements.
cargo-smart-release-core/​CHANGELOG.md Initializes the core crate changelog.
cargo-smart-release-core/​Cargo.toml Defines the reusable core crate.

Review effort: Balanced

Comment thread src/api.rs Resolved
Comment thread src/command/api_check.rs Resolved
Comment thread src/traverse.rs Resolved
Comment thread src/traverse.rs Resolved
…leases

Review of tests was very quick, and also through the production code.
I can only assume that this is fine or better than it was before, let's see.

<!-- agent -->
Opt-in `package.metadata.smart-release.api-check` commands can establish a
stable API boundary while private plumbing dependencies keep evolving. Pass
candidate workspace versions, the published baseline, and excluded unstable
features over JSON stdin; require an explicit compatible/incompatible result
and abort on tool failures before release mutations.

Recompute the release plan when a boundary is incompatible so downstream
checkers see its actual proposed version. Preserve direct breaking changes,
conservative handling of unchecked crates, and consumers that enable unstable
features through defaults, aliases, or forwarding. Compatible boundaries still
receive dependency updates and patch releases.

Document the checker contract, including the need to audit external type
identity alongside API syntax. No new runtime dependencies are required.

Assisted-by: GPT 6.1 Sol
Co-authored-by: GPT 6.1 Sol <codex@openai.com>
<!-- Byron -->

Rubber-stamp, but tested it locally.

<!-- agent -->

Collect history from detached HEAD so automatic version bumps and changelog
previews use the checked-out commit, including during Tix travel.

Skip the push preview with an explanatory warning when detached. Reject
actual releases requiring a push before changing files or publishing, while
allowing releases that disable pushing or tags.

Add a regression covering a detached commit behind the branch tip, release
and changelog dry runs, mutation-free rejection, and execution without tags.

Validation: all 69 Rust tests passed with all features; the extended detached
HEAD regression, Clippy with warnings denied, and formatting checks passed.
Shell journey tests and live publishing/pushing were not run.
<!-- Byron -->

Rubber stamp, but tested it locally.

<!-- agent -->
Show a separate warning report before dry runs and execution, including version
transitions, publishing status, and the causes of stable breaking releases.
Compare against known published versions to include pre-bumped manifests.

Validated with 70 workspace tests, rustfmt, and Clippy with warnings denied.
<!-- Byron -->

Refactored quite a bit, added licenses, to make the core library a bit more
like a library, and less like something that is cut out from a CLI application.

<!-- agent -->
Gitoxide needs the same API compatibility checks in its local tooling, CI, and
release planning. Put the manifest-driven checker in `cargo-smart-release-core` so
`it check-api` can reuse it without building the release tool or maintaining a
second implementation.

Expose boundary audits, rustdoc dependency audits, and JSON release requests
through `cargo_smart_release_core::api_check`. Keep its dependencies independent of
`gix`, the release planner, and the CLI. Library callers use
`cargo-smart-release-core` directly.

Add `cargo api-check CRATE` to audit supported feature profiles and compare the
working tree against a supported release tag or an explicit revision. Expose
the plumbing through the `release`, `boundary`, and `audit` subcommands.

Use the existing release planner for `--release-plan`, including proposed
dependency identities, repeated compatibility checks, and index-refresh retries.
Report candidate versions and compatibility verdicts without entering the release
mutation phase. Protect existing lockfiles and fail when a baseline or API check
cannot be evaluated.

Fix the Docs CI failure from ambiguous links to `api_check::check` when
`--document-private-items` also exposes the private module of the same name.
Add function parentheses to both link targets so rustdoc resolves them
unambiguously. Validated with RUSTDOCFLAGS="-D warnings" cargo doc
--workspace --all-features --no-deps --document-private-items and cargo fmt
--all -- --check.

Address PR review feedback on API comparison and checker completion.

> Concurrent checks share the same `<target_directory>/<host>/doc/<library>.json`, but Cargo's build lock is released before this copy. Another invocation can delete or overwrite that file between the completed build and the copy, causing a spurious failure or comparison of the wrong feature profile. Use an invocation-specific rustdoc output directory, or hold a separate lock across removal, generation, and copying.

> Appending a removed baseline profile makes the next loop build it against the current manifest. If its feature was also removed, `feature_args` returns an unknown-feature error, turning the recorded incompatibility into exit 2 and aborting release planning. Keep the removal reason, but only build profiles still declared by the current manifest.

> Because `exposed` includes baseline-only dependencies, removing a public function and its now-unused dependency returns a tool error (exit 2) rather than a completed incompatibility (exit 10). This aborts release planning instead of allowing a breaking release. Treat a known baseline identity disappearing as an identity change, and reserve the resolution error for dependencies unidentified in both versions.

> Signature equality still depends on generic parameter names: `normalized_type` preserves both parameter declarations and `generic` references. Renaming `T` to `U` in `pub fn identity<T>(value: T) -> T` therefore produces `SignatureChanged` and forces a breaking release, although callers remain source-compatible. Canonicalize bound parameter names and their references before comparing signatures, with a regression case for harmless generic renames.

> Adding a shorter public re-export such as `pub use LongName as S` replaces the stored name even though `LongName` remains available. Signature comparison then uses `S` and incorrectly reports the original export or functions returning it as removed or changed, rejecting an additive release. Track export aliases separately from type identity rather than selecting a new shortest name.

Keep a process lock over shared rustdoc generation and copying; retain removed-profile and removed-dependency incompatibility reports; compare generic bindings and public aliases without treating harmless renames or additions as API breaks.

Validation: cargo test -p cargo-smart-release-core -- --include-ignored (22 tests and documentation test); cargo test --test api_check -- --include-ignored (12 tests); core Clippy with warnings denied; formatter and diff checks. Feature-profile and identity regressions failed before their fixes. Five concurrent shared-target checker pairs returned the expected 0/10 statuses, and a real public dependency removal completed with exit 10.

Assisted-by: GPT 6.1 Sol
Co-authored-by: GPT 6.1 Sol <codex@openai.com>
…eleases

This rule is needed to give users of experimental APIs the chance to receive
patch updates without possibly introducing breakage.
With the rule, minor versions are used for features *and* breaking changes
of the `unstable` gated APIs, so explicitly upgrading their `~X.Y.Z` dependencies
could break, but doesn't have to.
Most code was rubber-stamped, but looked at the `core` crate a bit more carefully.

<!-- agent -->
Consumers of experimental APIs should be able to accept patch updates without
unexpected breakage. As `gix` approaches 1.0, exact pins would otherwise prevent
them from receiving compatible fixes, while broad dependency requirements could
silently introduce incompatible plumbing updates.

Check stable and experimental APIs separately during release planning. Stable
incompatibility retains the existing breaking-release requirement; experimental
incompatibility requires at least a minor release. Compatible full APIs still
permit patch releases, and explicit breaking release intent remains intact.

Preserve `~` dependency requirements and constrain experimental opt-ins,
including aliases, feature forwarding, and workspace inheritance, to a minor
series. Retain matching exact pins and handle unchanged dependencies too.

BREAKING CHANGE: checker requests gain `experimental` and
`experimental_breaking` flags. Update configured checkers together with the
planner; older built-in checkers reject the new requests before mutations.
The corresponding public request structs in `cargo-smart-release-core` also
gain fields.

Validation: workspace tests, rustdoc/semver fixtures, doc tests, strict Clippy,
formatting, and whitespace checks passed. The existing local HTTP-listener test
was excluded after the sandbox prevented it from binding a socket.

Assisted-by: GPT 6.1 Sol
Co-authored-by: GPT 6.1 Sol <codex@openai.com>
Validated that it works by hand.

<!-- agent -->
Older published crates can fail to build rustdoc when required Cargo features
were not forwarded by downstream consumers. A release fixing that problem
needs a way to proceed without pretending the API comparison succeeded.

Add --no-api-check to cargo smart-release for a single invocation. All
configured checkers still run, and completed compatibility and incompatibility
verdicts remain effective. Only comparisons that cannot complete fall back;
metadata errors remain fatal.

Track incomplete stable and experimental comparisons separately. A failed
stable comparison loses its API boundary and uses conservative version and
dependency rules. An incomplete experimental comparison requires at least a
minor release, retaining any completed stable verdict. Skip incomplete
comparisons for the remainder of that planning pass while continuing to recheck
completed comparisons when candidate versions change. Subsequent invocations
attempt all checks again. Preserve the override if the crates index is refreshed.

Execution errors identify the package and baseline and suggest the override
when the upcoming release fixes the old build problem. Explain that manual
compatibility review is required and advise inspecting the dry-run plan. Update
CLI help and README to describe the conditional fallback.

Validation: all 44 smart_release integration tests, strict workspace/all-feature/
all-target Clippy, formatting, and whitespace checks passed. Regression coverage
includes completed compatible and incompatible checkers alongside failures,
independent stable and experimental outcomes, rechecking completed comparisons
after candidate changes, retrying on the next invocation, and metadata validation.

Assisted-by: GPT 6.1 Sol
Co-authored-by: GPT 6.1 Sol <codex@openai.com>
@Byron
Sebastian Thiel (Byron) merged commit c9e89cb into main Nov 1, 2026
16 checks passed
@Byron
Sebastian Thiel (Byron) deleted the stability-support branch November 1, 2026 16:44
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.

3 participants