From edc31249a3fb320cef531c2203eaa8c307e12830 Mon Sep 17 00:00:00 2001 From: spacebear Date: Tue, 25 Aug 2026 17:56:53 -0400 Subject: [PATCH 1/4] Allow build metadata in release tag verification Release tags for the language bindings uniformly carry the full {version}+payjoin-{version} string, where the build metadata names the wrapped payjoin core release. Registries that cannot represent SemVer build metadata (npm, PyPI, NuGet package identity) publish the bare version, so when the packed version carries no metadata, compare the tag with its metadata stripped. A packed version that does carry metadata, like a Dart package, must still match the tag exactly. --- .github/actions/verify-tag-version/action.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/actions/verify-tag-version/action.yml b/.github/actions/verify-tag-version/action.yml index a21e155f0..437de3c5a 100644 --- a/.github/actions/verify-tag-version/action.yml +++ b/.github/actions/verify-tag-version/action.yml @@ -1,7 +1,10 @@ name: Verify tag version description: > - Check that the pushed tag is the expected prefix followed by exactly the - version about to be published, refusing to publish on any mismatch. + Check that the pushed tag is the expected prefix followed by the version + about to be published, refusing to publish on any mismatch. Tags carry + the full `{version}+payjoin-{version}` string; when the packed version + has no build metadata (npm, PyPI, and NuGet artifacts cannot carry it), + the tag's metadata is stripped before comparing. inputs: tag-prefix: description: Expected tag prefix, e.g. payjoin-csharp- @@ -30,8 +33,16 @@ runs: exit 1 fi tag_version="${TAG#"$PREFIX"}" - if [[ $tag_version != "$VERSION" ]]; then - echo "::error::tag $TAG implies version $tag_version but the packed version is $VERSION; refusing to publish" + compare_version="$tag_version" + # A packed version without build metadata comes from a registry + # that cannot represent it, so the tag is compared with its + # metadata stripped; a packed version that carries metadata must + # match the tag exactly. + if [[ $VERSION != *+* ]]; then + compare_version="${tag_version%%+*}" + fi + if [[ $compare_version != "$VERSION" ]]; then + echo "::error::tag $TAG implies version $compare_version but the packed version is $VERSION; refusing to publish" exit 1 fi echo "version=$tag_version" >>"$GITHUB_OUTPUT" From 60da5fb73eb324db00354ba9a67c3b63d244b5a5 Mon Sep 17 00:00:00 2001 From: spacebear Date: Fri, 21 Aug 2026 15:51:12 -0400 Subject: [PATCH 2/4] Prepare python payjoin 0.2.0 for PyPI Give the package its own semantic version instead of deriving it from the payjoin-ffi crate, following the per-language versioning convention: each package keeps independent semver with the wrapped payjoin core version as build metadata where the registry supports it. PyPI rejects PEP 440 local version labels (the + part), so the package publishes the bare version and records that 0.2.0 wraps payjoin-1.0.0, the first stable payjoin release, in the changelog. 0.2.0 succeeds 0.1.0.dev0, the only version previously published to PyPI, and is a minor bump because the API is not backwards compatible with it. The version now lives in pyproject.toml; setup.py no longer reads Cargo.toml, which drops the toml build and dev dependencies. --- payjoin-ffi/python/CHANGELOG.md | 10 +++++++++ payjoin-ffi/python/RELEASING.md | 34 +++++++++++++++++++++---------- payjoin-ffi/python/pyproject.toml | 6 +++--- payjoin-ffi/python/setup.py | 8 -------- payjoin-ffi/python/uv.lock | 16 ++------------- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/payjoin-ffi/python/CHANGELOG.md b/payjoin-ffi/python/CHANGELOG.md index fcbb96ee9..a78b0d5c8 100644 --- a/payjoin-ffi/python/CHANGELOG.md +++ b/payjoin-ffi/python/CHANGELOG.md @@ -1,3 +1,13 @@ +## [0.2.0] + +- Bindings for payjoin-1.0.0, the first stable payjoin release +- The package now carries its own version, independent of the + payjoin-ffi crate version: 0.2.0 succeeds 0.1.0.dev0, the only version + previously published to PyPI. Earlier entries in this changelog used + the crate version +- See the [payjoin-ffi changelog](../CHANGELOG.md) for the shared API + changes since 0.20.0 + ## [0.20.0] #### APIs added diff --git a/payjoin-ffi/python/RELEASING.md b/payjoin-ffi/python/RELEASING.md index 839261268..6c56ca1da 100644 --- a/payjoin-ffi/python/RELEASING.md +++ b/payjoin-ffi/python/RELEASING.md @@ -6,12 +6,21 @@ Maintainer documentation for publishing the `payjoin` package to ## Versioning -- The package version is the `payjoin-ffi` crate version: `setup.py` reads - it from `payjoin-ffi/Cargo.toml` at build time, so a release always - requires the crate version to be correct first. -- There is no separate Python version to maintain: the publish job derives - the version from the built wheels and refuses to publish if it does not - match the pushed tag. +- The package version is set in `pyproject.toml` (`project.version`). +- It is the package's own semantic version, independent of the + `payjoin-ffi` crate version. The language bindings follow a + `{version}+payjoin-{version}` convention where the build metadata + names the wrapped payjoin core release, but PyPI rejects PEP 440 + local version labels (the `+` part), so the package publishes the + bare version. The release tag carries the full version, and the + wrapped payjoin core version is also recorded in + [`CHANGELOG.md`](CHANGELOG.md). +- A release that only changes the wrapped payjoin core version still + needs at least a patch bump: PyPI sees only the bare version and + rejects re-uploading one that already exists. +- `pyproject.toml` is the only place the version is maintained: the + publish job derives the version from the built wheels and refuses to + publish if it does not match the pushed tag. ## Producing the wheels @@ -35,13 +44,16 @@ CPython satisfying `requires-python` can install them. 1. Confirm every `Build and Test Python` job is green on the release commit in `master`, including the per-platform smoke tests. 2. Tag that commit `payjoin-python-`, where `` is the - `payjoin-ffi` crate version exactly. The tag must be annotated and - signed by a maintainer key in `contrib/release/keys/`, and the tagged - commit must be on `master`; `verify-tag` refuses to publish otherwise. + `pyproject.toml` version plus `+payjoin-` build metadata + naming the wrapped payjoin core release; the publish job strips the + metadata before comparing the tag against the built wheels. The tag + must be annotated and signed by a maintainer key in + `contrib/release/keys/`, and the tagged commit must be on `master`; + `verify-tag` refuses to publish otherwise. ```shell - git tag -s payjoin-python-0.24.0 -m payjoin-python-0.24.0 - git push upstream payjoin-python-0.24.0 + git tag -s payjoin-python-0.2.0+payjoin-1.0.0 -m payjoin-python-0.2.0+payjoin-1.0.0 + git push upstream payjoin-python-0.2.0+payjoin-1.0.0 ``` The tag reruns the full build/wheel/smoke graph at the tagged commit, diff --git a/payjoin-ffi/python/pyproject.toml b/payjoin-ffi/python/pyproject.toml index 8ac9a8b63..aad065392 100644 --- a/payjoin-ffi/python/pyproject.toml +++ b/payjoin-ffi/python/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=83", "wheel", "toml"] +requires = ["setuptools>=83", "wheel"] build-backend = "setuptools.build_meta" [project] @@ -8,7 +8,7 @@ description = "The Python language bindings for the Payjoin Dev Kit" readme = "README.md" requires-python = ">=3.10" license = "MIT" -dynamic = ["version"] +version = "0.2.0" dependencies = ["httpx>=0.28.1,<1.0"] [tool.setuptools] @@ -20,4 +20,4 @@ include-package-data = true pythonpath = ["."] [dependency-groups] -dev = ["toml==0.10.2", "yapf==0.43.0"] +dev = ["yapf==0.43.0"] diff --git a/payjoin-ffi/python/setup.py b/payjoin-ffi/python/setup.py index 3e33e278d..04fe89d78 100644 --- a/payjoin-ffi/python/setup.py +++ b/payjoin-ffi/python/setup.py @@ -1,13 +1,6 @@ #!/usr/bin/env python -import os from setuptools import setup -import toml - -# Read version from Cargo.toml -cargo_toml_path = os.path.join(os.path.dirname(__file__), "..", "Cargo.toml") -cargo_toml = toml.load(cargo_toml_path) -version = cargo_toml["package"]["version"] LONG_DESCRIPTION = """# payjoin This repository creates libraries for various programming languages, all using the Rust-based [Payjoin](https://github.com/payjoin/rust-payjoin) @@ -35,7 +28,6 @@ zip_safe=False, packages=["payjoin"], package_dir={"payjoin": "./src/payjoin"}, - version=version, license="MIT or Apache 2.0", has_ext_modules=lambda: True, ) diff --git a/payjoin-ffi/python/uv.lock b/payjoin-ffi/python/uv.lock index 591efc5b4..b43b15e09 100644 --- a/payjoin-ffi/python/uv.lock +++ b/payjoin-ffi/python/uv.lock @@ -82,6 +82,7 @@ wheels = [ [[package]] name = "payjoin" +version = "0.2.0" source = { editable = "." } dependencies = [ { name = "httpx" }, @@ -89,7 +90,6 @@ dependencies = [ [package.dev-dependencies] dev = [ - { name = "toml" }, { name = "yapf" }, ] @@ -97,10 +97,7 @@ dev = [ requires-dist = [{ name = "httpx", specifier = ">=0.28.1,<1.0" }] [package.metadata.requires-dev] -dev = [ - { name = "toml", specifier = "==0.10.2" }, - { name = "yapf", specifier = "==0.43.0" }, -] +dev = [{ name = "yapf", specifier = "==0.43.0" }] [[package]] name = "platformdirs" @@ -120,15 +117,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] -[[package]] -name = "toml" -version = "0.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, -] - [[package]] name = "tomli" version = "2.2.1" From 0576e4932a3da2660f30dc510658a31133fe7d94 Mon Sep 17 00:00:00 2001 From: spacebear Date: Fri, 21 Aug 2026 15:51:45 -0400 Subject: [PATCH 3/4] Prepare javascript payjoin 0.2.0 for npm Bump the package from 0.1.1, the last version published to npm, to 0.2.0 for the release wrapping payjoin-1.0.0. The API has changed incompatibly since 0.1.1 (session persister rework, typestate and error overhauls, web environment support), so this is a 0.x minor bump. The language bindings follow a {version}+payjoin-{version} convention where the build metadata names the wrapped payjoin core release, but npm strips build metadata from published versions, so package.json carries the bare version and records the full 0.2.0+payjoin-1.0.0 in a releaseTag field for traceability. --- payjoin-ffi/javascript/RELEASING.md | 23 +++++++++++++++++------ payjoin-ffi/javascript/package-lock.json | 4 ++-- payjoin-ffi/javascript/package.json | 3 ++- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/payjoin-ffi/javascript/RELEASING.md b/payjoin-ffi/javascript/RELEASING.md index b0c114b42..84cb87491 100644 --- a/payjoin-ffi/javascript/RELEASING.md +++ b/payjoin-ffi/javascript/RELEASING.md @@ -8,7 +8,16 @@ lives in [`README.md`](README.md). - The package version is set in `package.json`. - It is the package's own semantic version, independent of the - `payjoin-ffi` crate version while the JavaScript API stabilizes. + `payjoin-ffi` crate version. The language bindings follow a + `{version}+payjoin-{version}` convention where the build metadata + names the wrapped payjoin core release, but npm strips build metadata + from published versions, so `package.json` carries the bare version + and records the full one in a `releaseTag` field for traceability. + The release tag carries the full version too. Keep `releaseTag` in + sync when bumping the version. +- A release that only changes the wrapped payjoin core version still + needs at least a patch bump: npm sees only the bare version and + rejects republishing one that already exists. - `package.json` is the only place the version is maintained: the publish job derives the version from the packed tarball and refuses to publish if it does not match the pushed tag. @@ -24,13 +33,15 @@ TypeScript), which is platform-independent. 1. Confirm every `Build and Test JavaScript` job is green on the release commit in `master`. 2. Tag that commit `payjoin-javascript-`, where `` is the - `package.json` version exactly. The tag must be annotated and signed by - a maintainer key in `contrib/release/keys/`, and the tagged commit must - be on `master`; `verify-tag` refuses to publish otherwise. + `package.json` `releaseTag` value exactly; the publish job strips the + build metadata before comparing the tag against the packed tarball. + The tag must be annotated and signed by a maintainer key in + `contrib/release/keys/`, and the tagged commit must be on `master`; + `verify-tag` refuses to publish otherwise. ```shell - git tag -s payjoin-javascript-0.1.1 -m payjoin-javascript-0.1.1 - git push upstream payjoin-javascript-0.1.1 + git tag -s payjoin-javascript-0.2.0+payjoin-1.0.0 -m payjoin-javascript-0.2.0+payjoin-1.0.0 + git push upstream payjoin-javascript-0.2.0+payjoin-1.0.0 ``` The tag reruns the full build/pack/smoke graph at the tagged commit, diff --git a/payjoin-ffi/javascript/package-lock.json b/payjoin-ffi/javascript/package-lock.json index 59d64f613..7733b7940 100644 --- a/payjoin-ffi/javascript/package-lock.json +++ b/payjoin-ffi/javascript/package-lock.json @@ -1,12 +1,12 @@ { "name": "payjoin", - "version": "0.1.1", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "payjoin", - "version": "0.1.1", + "version": "0.2.0", "dependencies": { "@ubjs/core": "^0.31.0-3", "uniffi-bindgen-react-native": "github:spacebear21/uniffi-bindgen-react-native#d10f04d83299" diff --git a/payjoin-ffi/javascript/package.json b/payjoin-ffi/javascript/package.json index 0805956d1..d59a973d4 100644 --- a/payjoin-ffi/javascript/package.json +++ b/payjoin-ffi/javascript/package.json @@ -1,6 +1,7 @@ { "name": "payjoin", - "version": "0.1.1", + "version": "0.2.0", + "releaseTag": "0.2.0+payjoin-1.0.0", "description": "JavaScript/WASM bindings for rust-payjoin (EXPERIMENTAL)", "scripts": { "ubrn:nodejs": "ubrn build web --config ubrn.nodejs.config.yaml --and-generate", From 830845e47f980e9d65da5ba9465ffcc9bdc70e1b Mon Sep 17 00:00:00 2001 From: spacebear Date: Fri, 21 Aug 2026 15:54:25 -0400 Subject: [PATCH 4/4] Prepare C# Payjoin 0.1.0+payjoin-1.0.0 for NuGet Give the package its own semantic version instead of tracking the payjoin-ffi crate version, following the per-language convention where SemVer build metadata names the wrapped payjoin core release: 0.1.0+payjoin-1.0.0 packages payjoin 1.0.0, the first stable payjoin release. Drop the -preview.N suffix since 0.x already conveys a pre-stable API. Resetting below the published 0.24.0-preview.1 avoids continuing a number that named the previously wrapped payjoin core version, which would read as confusing under the new scheme. The old versions must be unlisted on nuget.org so floating installs resolve the new line. NuGet keeps build metadata in the nuspec but strips it from the .nupkg filename and ignores it for version comparison, so the release tag payjoin-csharp-0.1.0+payjoin-1.0.0 is compared against the packed artifact with the metadata stripped. --- payjoin-ffi/csharp/Payjoin.csproj | 2 +- payjoin-ffi/csharp/README.md | 4 ++-- payjoin-ffi/csharp/RELEASING.md | 39 ++++++++++++++++++++++--------- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/payjoin-ffi/csharp/Payjoin.csproj b/payjoin-ffi/csharp/Payjoin.csproj index 5dd9357ad..5257adb08 100644 --- a/payjoin-ffi/csharp/Payjoin.csproj +++ b/payjoin-ffi/csharp/Payjoin.csproj @@ -7,7 +7,7 @@ true true Payjoin - 0.24.0-preview.1 + 0.1.0+payjoin-1.0.0 Payjoin Payjoin Dev Kit Contributors C# bindings for payjoin-ffi, generated from rust-payjoin via UniFFI. diff --git a/payjoin-ffi/csharp/README.md b/payjoin-ffi/csharp/README.md index fcc807eae..b6dce951a 100644 --- a/payjoin-ffi/csharp/README.md +++ b/payjoin-ffi/csharp/README.md @@ -36,9 +36,9 @@ A sender session starts from a BIP 21 URI scanned from the receiver (`Payjoin.Ur Sessions persist each step to an event log through a persister you implement over your own storage; replaying the log with `PayjoinMethods.ReplayReceiverEventLog` recovers the current state after a crash or restart. Every `Save` has a `SaveAsync` counterpart, with async persister interfaces for database-backed storage. -## Preview status +## Stability -The package is in preview while the C# API stabilizes alongside the Rust core's 1.0 release candidates. Expect breaking changes between previews; the package version tracks the underlying `payjoin-ffi` crate. +The package is pre-1.0 while the C# API stabilizes; expect breaking changes between 0.x releases. The version's build metadata names the wrapped payjoin core release, so `0.1.0+payjoin-1.0.0` packages payjoin 1.0.0, the first stable payjoin release. ## Documentation and help diff --git a/payjoin-ffi/csharp/RELEASING.md b/payjoin-ffi/csharp/RELEASING.md index 44a478e1f..a5995ccaa 100644 --- a/payjoin-ffi/csharp/RELEASING.md +++ b/payjoin-ffi/csharp/RELEASING.md @@ -7,12 +7,23 @@ package readme. ## Versioning - The package version is set in `Payjoin.csproj` (``). -- It tracks the `payjoin-ffi` crate version from `payjoin-ffi/Cargo.toml`, - with a `-preview.N` suffix while the C# API stabilizes. For example, - `0.24.0-preview.1` packages `payjoin-ffi 0.24.0`. Pre-release suffixes - follow [SemVer], per NuGet's [package versioning] guidance. -- Bump only the `-preview.N` suffix for packaging-only fixes. Bump - `MAJOR.MINOR.PATCH` together with a `payjoin-ffi` version bump. +- It is the package's own semantic version, independent of the + `payjoin-ffi` crate version. The language bindings follow a + `{version}+payjoin-{version}` convention: the [SemVer] build metadata + names the wrapped payjoin core release, so `0.1.0+payjoin-1.0.0` + packages payjoin 1.0.0. NuGet accepts build metadata per its + [package versioning] guidance but ignores it for version comparison + and strips it from the `.nupkg` filename, so the package identity is + the bare version. +- Bump `MAJOR.MINOR.PATCH` for C# API changes, and update the build + metadata whenever the wrapped payjoin core version changes. +- A release that only changes the wrapped payjoin core version still + needs at least a patch bump: the package identity ignores the build + metadata, and nuget.org rejects republishing an identity that + already exists. +- Versions up to `0.24.0-preview.1` tracked the `payjoin-ffi` crate + version instead. They predate this scheme and are unlisted on + nuget.org so they do not resolve as the latest version. - `Payjoin.csproj` is the only place the version is maintained: the CI smoke test derives the version from the packed artifact. @@ -57,8 +68,9 @@ Review before every publish to nuget.org. Grounded in the NuGet - [ ] Native assets are release-profile builds without `_test-utils` (the pack step's validation target enforces both; confirm it ran in CI). - [ ] The package is under nuget.org's 250 MB size limit. -- [ ] Package version in `Payjoin.csproj` matches `payjoin-ffi`'s crate - version plus the intended pre-release suffix. +- [ ] Package version in `Payjoin.csproj` carries the intended C# version + and its `+payjoin-{version}` build metadata matches the wrapped + payjoin core release. ### Metadata and trust @@ -105,11 +117,16 @@ is ever stored. The workflow is 1. Work through the release readiness checklist above on the release commit in `master`; confirm every `Build and Test CSharp` job is green. 2. Tag that commit `payjoin-csharp-`, where `` is the - `Payjoin.csproj` `` exactly, and push the tag: + `Payjoin.csproj` `` exactly, build metadata included. NuGet + strips the metadata from the `Payjoin..nupkg` filename, so + the publish job strips it from the tag as well before comparing. The + tag must be annotated and signed by a maintainer key in + `contrib/release/keys/`, and the tagged commit must be on `master`; + `verify-tag` refuses to publish otherwise. ```shell - git tag payjoin-csharp-0.24.0-preview.1 - git push upstream payjoin-csharp-0.24.0-preview.1 + git tag -s payjoin-csharp-0.1.0+payjoin-1.0.0 -m payjoin-csharp-0.1.0+payjoin-1.0.0 + git push upstream payjoin-csharp-0.1.0+payjoin-1.0.0 ``` The tag reruns the full build/pack/smoke graph at the tagged commit, then