diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 38adda101..a97859a18 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -115,6 +115,18 @@ Each release crate (`payjoin`, `payjoin-cli`, `payjoin-mailroom`) and `payjoin-f If the change has no user-visible effect (refactoring, tests, CI, comments), say so in the pull request description and a maintainer will apply the `changelog: none` label. CI fails a pull request that touches one of those crates' `src/` without either a changelog line or that label. +### Deprecations + +Mark a deprecation with the version placeholder instead of guessing the next release: + +```rust +#[deprecated(since = "0.0.0-unreleased", note = "use `Receiver::classify`")] +``` + +`contrib/release/bump.sh` replaces it with the crate's version in the release PR, and the release checks fail if one is left. rustc warns callers about it like any other deprecation. Don't use `TBD`, which rustc treats as a future deprecation and does not warn about. + +UniFFI does not carry deprecation attributes into the bindings, so `payjoin-ffi` states a deprecation in the doc comment and names the crate whose release deprecates the item, e.g. `Deprecated since payjoin 0.0.0-unreleased`. The payjoin-ffi bump fills in the version that crate is at. + ### Nix Development Shells Where [nix](https://nixos.org/) is available (NixOS or diff --git a/.github/ISSUE_TEMPLATE/minor-release.md b/.github/ISSUE_TEMPLATE/minor-release.md index bc76242b4..78816a1ea 100644 --- a/.github/ISSUE_TEMPLATE/minor-release.md +++ b/.github/ISSUE_TEMPLATE/minor-release.md @@ -24,8 +24,9 @@ landed since the last release, so there is nothing to compile here. - [ ] Branch `bump-CRATE-MAJOR-MINOR+1` from `master`. - [ ] Run `contrib/release/bump.sh CRATE MAJOR.MINOR+1.0`. It sets the version, rewrites every workspace member's requirement on the crate, renames `## Unreleased` to - `## MAJOR.MINOR+1.0` with a fresh empty `## Unreleased` above it, regenerates both - lock files and runs check-invariants. It needs jq, and the lock file step calls + `## MAJOR.MINOR+1.0` with a fresh empty `## Unreleased` above it, fills in the + `0.0.0-unreleased` placeholders in the crate's source, regenerates both lock files + and runs check-invariants. It needs jq, and the lock file step calls `cargo +nightly`, so run it where rustup has a nightly toolchain. `nix develop .#release` has jq but no rustup; there, pass `--no-lock`, then run `contrib/update-lock-files.sh` from a rustup shell and `contrib/release/check-invariants.sh CRATE` again. diff --git a/contrib/release/bump.sh b/contrib/release/bump.sh index 577237b64..09c234870 100755 --- a/contrib/release/bump.sh +++ b/contrib/release/bump.sh @@ -3,8 +3,9 @@ # Prepare a release crate's bump commit. Sets the crate's version, rewrites # every workspace member's version requirement on it, renames the # "## Unreleased" changelog section to the new version and opens a fresh -# empty one above it, regenerates both lock files, then runs -# check-invariants. Leaves the changes uncommitted for review. +# empty one above it, fills in the version placeholders in the crate's +# source, regenerates both lock files, then runs check-invariants. Leaves +# the changes uncommitted for review. # # payjoin-ffi is bumped the same way, except that its changelog entry # opens by naming the wrapped payjoin release, and its version is also @@ -55,6 +56,27 @@ open_section() { # ' "$1" >"$1.tmp" && mv "$1.tmp" "$1" } +# Replace the version placeholder in the crate's source. A bare +# `since = "0.0.0-unreleased"` (a deprecation attribute) takes the new +# version. `since 0.0.0-unreleased` in prose takes that workspace +# crate's version: the bindings' doc comments name the payjoin release, since +# UniFFI does not carry Rust deprecation attributes into the bindings. +fill_unreleased() { # + local placeholder="${UNRELEASED//./\\.}" name version f + while IFS= read -r f; do + sed -i "s/since = \"$placeholder\"/since = \"$1\"/g" "$f" + while IFS= read -r name; do + if [ "$name" = "$crate" ]; then + version="$1" + else + version="$(manifest_version "$name")" + fi + sed -i "s/since $name $placeholder/since $name $version/g" "$f" + done < <(cargo_metadata | jq -r '.packages[].name') + echo " $f: $UNRELEASED filled in" + done < <(git grep -l -F -e "$UNRELEASED" -- "$crate/src") +} + # Set the binding packages to the payjoin-ffi version. Each manifest is # edited in place with sed and read back, as the Cargo.toml edit is, so a # line that no longer looks the way this expects fails instead of being @@ -173,6 +195,8 @@ else echo " $changelog: '## Unreleased' -> '$heading', fresh Unreleased section added" fi +fill_unreleased "$new" + if [ "$lock" -eq 1 ]; then echo "Regenerating lock files (contrib/update-lock-files.sh)" ./contrib/update-lock-files.sh diff --git a/contrib/release/check-invariants.sh b/contrib/release/check-invariants.sh index f9a14498e..82c0605bb 100755 --- a/contrib/release/check-invariants.sh +++ b/contrib/release/check-invariants.sh @@ -2,7 +2,8 @@ # # Check that each release crate is internally consistent: every dependent's # version requirement on it matches its manifest version, both tracked lock -# files record that version, and its CHANGELOG.md has a section for it. +# files record that version, its CHANGELOG.md has a section for it, and its +# source has no version placeholder left for bump.sh to fill in. # Offline, a few seconds. Checks all release crates, or the ones named. set -euo pipefail # shellcheck source=contrib/release/crates.sh @@ -35,13 +36,16 @@ mismatches="$(cargo_metadata | jq -r --argjson t "$(printf '%s' "$crates" | jq - for crate in $crates; do version="$(manifest_version "$crate")" - echo "Checking $crate $version: lock files and CHANGELOG.md" + echo "Checking $crate $version: lock files, CHANGELOG.md and placeholders" for lock in Cargo-minimal.lock Cargo-recent.lock; do recorded="$(lockfile_version "$REPO_ROOT/$lock" "$crate")" [ "$recorded" = "$version" ] || problem "$lock records $crate ${recorded:-}, expected $version" done grep -qxF -e "## $version" -e "## [$version]" "$REPO_ROOT/$crate/CHANGELOG.md" || problem "$crate/CHANGELOG.md has no ## $version section" + if git -C "$REPO_ROOT" grep -n -F -e "$UNRELEASED" -- "$crate/src"; then + problem "$crate/src still has the $UNRELEASED placeholder; bump.sh fills it in" + fi done [ "$status" -eq 0 ] && echo "Release invariants hold for: $crates" diff --git a/contrib/release/crates.sh b/contrib/release/crates.sh index 7c0a8b07d..c84b67356 100644 --- a/contrib/release/crates.sh +++ b/contrib/release/crates.sh @@ -6,6 +6,13 @@ RELEASE_CRATES="payjoin payjoin-cli payjoin-mailroom" +# Stands in for the version a pending change will ship in, as in +# `#[deprecated(since = "0.0.0-unreleased")]`. bump.sh replaces it. It is +# valid semver so clippy's deprecated_semver lint accepts it, unlike "TBD", +# which rustc reads as a future deprecation and does not warn about. +# shellcheck disable=SC2034 # read by the scripts that source this file +UNRELEASED="0.0.0-unreleased" + REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" # `cargo metadata` for the workspace. Command substitutions run in a subshell diff --git a/payjoin-ffi/RELEASING.md b/payjoin-ffi/RELEASING.md index 1dbcbf011..c68e27310 100644 --- a/payjoin-ffi/RELEASING.md +++ b/payjoin-ffi/RELEASING.md @@ -35,8 +35,10 @@ It sets the version in `payjoin-ffi/Cargo.toml`, the four package manifests, `python/uv.lock` and `javascript/package-lock.json`, renames `## Unreleased` in `payjoin-ffi/CHANGELOG.md` to the version with a fresh empty `## Unreleased` above it and a first entry naming the wrapped -`payjoin` release, and opens a section in each language changelog with a -stock entry pointing at it. The lock file step calls +`payjoin` release, opens a section in each language changelog with a +stock entry pointing at it, and fills in the `0.0.0-unreleased` +placeholders in `payjoin-ffi/src`, such as `Deprecated since payjoin +0.0.0-unreleased`, with the version each names. The lock file step calls `cargo +nightly`, which the release shell lacks, so pass `--no-lock` there, then run `contrib/update-lock-files.sh` from a rustup shell and `contrib/release/check-invariants.sh payjoin-ffi` again.