Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions .github/actions/verify-tag-version/action.yml
Original file line number Diff line number Diff line change
@@ -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-
Expand Down Expand Up @@ -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
Comment on lines +36 to +43

@spacebear21 spacebear21 Aug 25, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This feels a bit clunky but it seemed like the most straightforward way to enforce uniformity across tags (payjoin-{lang}-{version}+payjoin-{version}) despite some package managers not supporting the + syntax.

Open to suggestions for better ways to structure the release tags. The current approach took inspiration from https://gitlab.com/ark-bitcoin/bark-ffi-bindings/-/blob/master/RELEASE.md?ref_type=heads#versioning-per-language

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"
2 changes: 1 addition & 1 deletion payjoin-ffi/csharp/Payjoin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPackable>true</IsPackable>
<PackageId>Payjoin</PackageId>
<Version>0.24.0-preview.1</Version>
<Version>0.1.0+payjoin-1.0.0</Version>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do you see any confusion with csharp always being a version behind? I know this is not technically the most correct semver but it seems annoying to always have this one minor version behind the others

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The way bark does versioning is: all downstream languages track the ffi version, so they're all in sync.

IMO there are good reasons to keep each language's version independent. There may be releases affecting only one language, like if a particular bindings generator e.g. uniffi-dart comes out with an important fix. That doesn't warrant making releases in all the other languages just to keep the versions in sync. And in the future when we add support for a new language we probably want to start it at 0.1.0 anyway while it's still being experimented with.

<Title>Payjoin</Title>
<Authors>Payjoin Dev Kit Contributors</Authors>
<Description>C# bindings for payjoin-ffi, generated from rust-payjoin via UniFFI.</Description>
Expand Down
4 changes: 2 additions & 2 deletions payjoin-ffi/csharp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
39 changes: 28 additions & 11 deletions payjoin-ffi/csharp/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,23 @@ package readme.
## Versioning

- The package version is set in `Payjoin.csproj` (`<Version>`).
- 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.

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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-<version>`, where `<version>` is the
`Payjoin.csproj` `<Version>` exactly, and push the tag:
`Payjoin.csproj` `<Version>` exactly, build metadata included. NuGet
strips the metadata from the `Payjoin.<version>.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
Expand Down
23 changes: 17 additions & 6 deletions payjoin-ffi/javascript/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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-<version>`, where `<version>` 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,
Expand Down
4 changes: 2 additions & 2 deletions payjoin-ffi/javascript/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion payjoin-ffi/javascript/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
10 changes: 10 additions & 0 deletions payjoin-ffi/python/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
34 changes: 23 additions & 11 deletions payjoin-ffi/python/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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-<version>`, where `<version>` 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-<version>` 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,
Expand Down
6 changes: 3 additions & 3 deletions payjoin-ffi/python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[build-system]
requires = ["setuptools>=83", "wheel", "toml"]
requires = ["setuptools>=83", "wheel"]
build-backend = "setuptools.build_meta"

[project]
Expand All @@ -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]
Expand All @@ -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"]
8 changes: 0 additions & 8 deletions payjoin-ffi/python/setup.py
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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,
)
16 changes: 2 additions & 14 deletions payjoin-ffi/python/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading