Skip to content

Unify bindings release - #1952

Merged
spacebear21 merged 3 commits into
payjoin:masterfrom
spacebear21:unify-bindings-release
Oct 14, 2026
Merged

spacebear21 merged 3 commits into
payjoin:masterfrom
spacebear21:unify-bindings-release

Conversation

@spacebear21

@spacebear21 spacebear21 commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Release the Python, JavaScript, C#, and Dart bindings together from one signed
payjoin-ffi-<version>+payjoin-<core> tag, with one approval and one GitHub
release, instead of four tags, four workflows, and four approvals.

Why

Each binding carried its own version sequence and its own tag-triggered publish
path. Shipping a core bump meant tagging four times, approving four times, and
nothing checked that the four packages wrapped the same payjoin-ffi commit.

What changes

Three commits, each passing CI on its own:

  1. Pin Dart packages to their release commit. prepare-publish.sh now stages
    the pub.dev package in a separate directory with the native crate pinned to
    the generating checkout, excluding the development Cargo overlay and
    lockfile. The staged package is what gets dry-run checked and published, so
    the rev in native/Cargo.toml no longer has to be edited by hand.
  2. Align binding versions at 0.25.0. All four packages move to the
    payjoin-ffi version. 0.25.0 is above every existing sequence (Python and
    JavaScript 0.3.0, C# 0.2.0, Dart 0.3.0), so no registry sees a version go
    backwards. No API changes. payjoin-ffi/CHANGELOG.md becomes the canonical
    changelog; the language changelogs keep a short entry linking to it.
  3. Release every language binding from one FFI tag. New
    bindings-release.yml verifies the signed tag, checks every manifest carries
    the tagged version (contrib/release/bindings-version.sh, also run by
    tag.sh), runs the existing language workflows through workflow_call, then
    publishes each registry from its own job. The four publish jobs wait on every
    build so they reach the release environment together and one approval
    covers all of them. A failed registry is retried with "Re-run failed jobs"
    against the same tested artifacts. One GitHub release holds every package and
    a single SHA256SUMS, with the FFI changelog section as its body.

The per-language publish jobs, tag triggers, release-version.sh readers, the
release-assets.yml and verify-tag-hygiene.yml wrappers, and the
verify-tag-version action are removed. Net diff deletes more than it adds.
a single SHA256SUMS, with the FFI changelog section as its body.

The per-language publish jobs, tag triggers, release-version.sh readers, the
release-assets.yml and verify-tag-hygiene.yml wrappers, and the
verify-tag-version action are removed. Net diff deletes more than it adds.

Design notes

  • Publish jobs live in the triggered workflow rather than the language
    workflows because PyPI's trusted publisher rejects reusable workflows.
  • Tagging the bindings now runs the same lockfile and changelog invariants as
    the crates, and both tag.sh and the CI verify job refuse to release bindings
    whose wrapped payjoin is not yet on crates.io, since Dart consumers build
    against it.
  • Pre-releases are -rc.N only. Python spells them rcN; npm publishes them to
    the next dist-tag.
  • The payjoin-ffi crate is not published to crates.io; the tag publishes only
    the language packages.

Before the first tag

Registry configuration moves with the workflow and needs a one-time update
(documented in payjoin-ffi/RELEASING.md): PyPI, npm, and NuGet trusted
publishers to bindings-release.yml with environment release; pub.dev tag
pattern to payjoin-ffi-{{version}}; and payjoin-ffi-* allowed in the tag
rules.

Testing

shellcheck, shfmt, actionlint, prettier, and codespell pass on each commit. The
version checker, invariants checker, changelog extractor, and tag.sh dispatch
were exercised locally against the tree. The workflow itself can only be proven
by the first tag push; the single-approval behaviour relies on GitHub approving
per environment per run, and is worth watching on that first release.

This description and the branch were written with AI assistance (Claude Code)
and reviewed by me.

Reviewer: @benalleng or @chavic

Pull Request Checklist

Please confirm the following before requesting review:

@coveralls

coveralls commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 38438516368

Coverage remained the same at 87.494%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 17848
Covered Lines: 15616
Line Coverage: 87.49%
Coverage Strength: 319.55 hits per line

💛 - Coveralls

@spacebear21
spacebear21 force-pushed the unify-bindings-release branch from a63b6dd to ad9fb34 Compare October 8, 2026 16:37
The published native wrapper could use a different Rust revision from
its generated bindings. Stage a package pinned to the generating
checkout without development Cargo overlays or local lockfiles.
The Python, JavaScript, C#, and Dart packages each carried their own
version sequence, so releasing all bindings took four tags and four
approvals. Move every package to the payjoin-ffi version so one tag
can name them all. 0.25.0 is above every existing sequence, so no
registry sees a version go backwards.

Keep the resolved dependency graphs unchanged. Let the Dart publish dry
run proceed over pub's reminder that versions were skipped, since the
jump is deliberate.
Each binding had its own tag, workflow, approval, and GitHub release,
so shipping all four meant four signed tags and four approvals, and
nothing checked that they wrapped the same payjoin-ffi commit.

Push one signed payjoin-ffi-<version>+payjoin-<core> tag instead. A
new workflow verifies the tag and that every manifest carries the
tagged version, runs each language's existing build and smoke checks
through workflow_call, then publishes each registry from its own job.
The four publish jobs wait on every build so they reach the release
environment together and one approval covers them; a registry failure
is retried with "Re-run failed jobs" against the same tested
artifacts, which is why there is no separate recovery path. One
GitHub release carries every package and a single SHA256SUMS, with the
canonical payjoin-ffi changelog section as its body.

Keep the publish jobs in the triggered workflow rather than the
language workflows because PyPI's trusted publisher rejects reusable
workflows. Replace the per-language release-version readers with
bindings-version.sh, which tag.sh and CI both run, and teach the
invariants and changelog helpers the bracketed headings the binding
changelogs use so payjoin-ffi passes the same checks as the crates.
tag.sh also refuses to tag bindings whose wrapped core is not on
crates.io, since Dart consumers build against it.
@spacebear21
spacebear21 force-pushed the unify-bindings-release branch from ad9fb34 to 00d9588 Compare October 13, 2026 17:25
@spacebear21
spacebear21 marked this pull request as ready for review October 13, 2026 17:33
@spacebear21 spacebear21 linked an issue Oct 13, 2026 that may be closed by this pull request
3 of 5 tasks

@benalleng benalleng left a comment

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.

uTACK 00d9588

Nice that we can just use a single payjoin-ffi-<version>+payjoin-<core> tag to release them all at once.

Read through the scripts and actions to the best of my ability but I really think we need to YOLO a release at some point to actually test.

@benalleng

Copy link
Copy Markdown
Collaborator

Should we try to get the registry configuration locked down before merge?


publish-javascript:
name: Publish to npmjs.com
needs: [verify-tag, python, javascript, csharp, dart, kotlin]

@benalleng benalleng Oct 14, 2026 •

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.

I think this is a duplicate wait for verify-tag I don't know if this actually does anything to make it different from the other workflows though since they block on verify-tag above

@spacebear21
spacebear21 merged commit 6a6a28d into payjoin:master Oct 14, 2026
47 checks passed
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.

Automate Package Publishing for All Release Artifacts

3 participants