Bindings release workflows - #1809
Draft
spacebear21 wants to merge 12 commits into
Draft
Conversation
Six workflows repeat the same two steps to prepare a job for nix develop: the Determinate nix installer and the magic nix cache with its FlakeHub lookup disabled. Upcoming release workflows for the language bindings would copy the pair four more times. Move the pair into .github/actions/setup-nix. The Swatinem Rust build cache stays an explicit separate step in the jobs that compile, since it is unrelated to nix. No job's steps change beyond the substitution.
Every registry publish job must refuse to push an artifact whose version differs from what the pushed tag calls for. The check was written inline in the NuGet publish job; the upcoming npm, pub.dev, and PyPI publish jobs would each duplicate it. Move the prefix-strip-and-compare into a verify-tag-version composite action taking the tag prefix and the actual packed version. The NuGet job keeps its locate step (exactly one .nupkg, version parsed from the filename) and hands the result to the action. Local actions resolve from the workspace, so the job gains a checkout step it previously avoided; its contents: read permission already covers it.
Publishing ends the same way for every binding: download the packed artifact, generate SHA256SUMS, and attach both to the GitHub release for the pushed tag. The NuGet workflow carried this as an inline job; the upcoming npm and PyPI publish paths would each copy it. Move the job to release-assets.yml as a workflow_call and point the C# github-release job at it. The artifact-pattern input downloads and merges multiple artifacts for workflows that build one per platform, and unzip-globs generalizes the C#-specific step that hashes native libraries nested inside the package. Two behavior notes: the job now runs on ubuntu-26.04 like the rest of the repo instead of ubuntu-latest, and pre-release detection derives from a SemVer hyphen in the tag's version (ignoring + build metadata) instead of matching only -preview and -rc, which classifies all existing tags identically.
The two per-target cargo builds run in the background and were reaped with a bare `wait`, which always returns zero no matter how the children exited. Under `set -e` a failed build therefore fell through to lipo, which would reuse a stale library from an earlier build if one was present. Wait on each build's PID so either failure stops the script.
The script hardcoded --profile dev and --features _test-utils, each annotated with a reminder that a real release must not ship the test utils feature. Adopt the PAYJOIN_FFI_FEATURES / PAYJOIN_FFI_PROFILE convention the C# generate_bindings.sh already uses: features default to _test-utils and the profile to dev, so tests behave exactly as before, while a release build sets PAYJOIN_FFI_FEATURES= and PAYJOIN_FFI_PROFILE=release to get an optimized library with no test-only APIs.
contrib/pack.sh builds the package from the maintained recent lockfile and packs the npm tarball into artifacts/, mirroring how contrib/test.sh drives the test build. The tarball ships only dist/ (wasm + compiled TypeScript), which is platform-independent, so a single pack is the entire release build. The test-utils napi addon is a dev-only helper for the integration tests, so generate_bindings.sh gains a PAYJOIN_JS_BUILD_TEST_UTILS toggle (default on, preserving test behavior) that the pack script switches off.
Add the tag-gated publishing path to the JavaScript workflow, mirroring the NuGet flow in csharp.yml: every run packs the tarball and smoke-installs it on Linux and macOS; pushing a payjoin-javascript-<version> tag additionally reruns the graph at the tagged commit, then publishes the smoke-tested tarball to npmjs.com and attaches it to the GitHub release. The publish job verifies the tag matches the packed version, attests build provenance, and authenticates with npm trusted publishing (OIDC), so no long-lived registry token exists anywhere. It runs in the release environment for a required-reviewer gate and also waits for the build-and-test jobs at the tagged commit before anything reaches the registry. RELEASING.md records the release steps and the one-time npmjs.com trusted-publisher and environment setup.
The script hardcoded --features dart,_test-utils --profile dev, so every published release shipped bindings declaring test-only APIs whose symbols are absent from consumer builds, a limitation CONTRIBUTING.md called out. Adopt the PAYJOIN_FFI_FEATURES / PAYJOIN_FFI_PROFILE convention the C# generate_bindings.sh already uses: defaults are unchanged for tests, and a release build sets PAYJOIN_FFI_FEATURES= (empty) to emit bindings with no test-only declarations.
Add the tag-gated publishing path to the Dart workflow, following the NuGet flow in csharp.yml adapted to a source-only registry: pub.dev ships Dart source plus the native/ wrapper crate and consumers compile the Rust through hook/build.dart, so there is no artifact to pack, smoke test, or attach to a GitHub release. Every run regenerates the production bindings and validates the archive with a publish dry run (contrib/prepare-publish.sh). The dry run tolerates only the known analyzer warning that generated bindings unavoidably carry; any other validation finding fails the job, since the real publish runs with --force, which would proceed over a new warning. Pushing a payjoin-dart-<version> tag reruns tests and verification at the tagged commit, then publish-pub checks the tag against pubspec.yaml, regenerates the bindings, and publishes through pub.dev automated publishing (OIDC), so no long-lived credential exists anywhere. The job runs in the release environment for a required-reviewer gate. CONTRIBUTING.md's releasing section now documents the CI flow, the one-time pub.dev automated-publishing setup, and the manual fallback.
contrib/build-wheel.sh builds the production wheel into dist/ from the maintained recent lockfile, mirroring how contrib/test.sh drives the test build: release profile, no _test-utils, then uv build. Two retags follow the build. The bindings load the bundled library through ctypes, so the wheel is retagged py3-none instead of the building interpreter's cpXY tag, letting every CPython covered by requires-python install it. On Linux, auditwheel then verifies the library's external dependencies and applies the manylinux tag the binary satisfies, since PyPI rejects raw linux_x86_64 wheels; on macOS the lipo fat dylib gets the universal2 tag. The wheel CLI and auditwheel come from the python dev shell (the uv2nix venv omits build backends). contrib/test.sh now clears dist/ before building its wheel: the release wheel carries different tags than the test build, so a leftover copy would otherwise sit beside the fresh wheel and break the install step's glob.
Add the tag-gated publishing path to the Python workflow, mirroring the NuGet flow in csharp.yml: every run builds the release wheels (manylinux x86_64 and macOS universal2) and smoke-installs them on Linux, arm64 macOS, and Intel macOS with a CPython other than the one they were built with, proving the py3-none retag. Pushing a payjoin-python-<version> tag reruns the graph at the tagged commit, then publishes the smoke-tested wheels to PyPI and attaches them to the GitHub release. The publish job verifies the tag matches the wheel versions (the version is the payjoin-ffi crate version, read by setup.py), attests build provenance, and uploads through PyPI trusted publishing (OIDC) with PEP 740 attestations, so no long-lived registry token exists anywhere. It runs in the release environment for a required-reviewer gate and also waits for the build-and-test jobs at the tagged commit before anything reaches the registry. RELEASING.md records the release steps and the one-time PyPI trusted-publisher and environment setup.
The wheel advertised build, setuptools, wheel, semantic-version, and typing-extensions as exact-pinned runtime requirements, though the package imports none of them: the generated bindings use only the standard library and http.py needs httpx. Every consumer pulled in build tooling, and the stale exact pins (typing-extensions 4.0.1 is from 2021) conflict with any package wanting a newer release. Keep httpx as the only runtime dependency, loosened to a compatible range for the same reason, and leave the build backend's requirements to build-system.requires, which already lists them. Regenerate uv.lock accordingly.
Collaborator
Coverage Report for CI Build 31719107638Coverage remained the same at 86.718%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Unreviewed slop from Fable 5, sketching out comprehensive release & publishing workflows for all downstream language bindings.
Pull Request Checklist
Please confirm the following before requesting review:
AI
in the body of this PR.