The .github/workflows/publish.yml file is the entire release automation. One tag push publishes 5 Python packages, 6 npm packages, and creates a GitHub Release. This post explains how.
Trigger
on:
push:
tags:
- "v*.*.*"
- "v*.*.*-*"Any tag matching v1.2.3 or v1.2.3-beta.1 triggers the workflow. Pre-release tags get a beta npm tag.
Job chain
The six jobs run in dependency order:
- publish-pypi — builds all Python wheels, uploads via OIDC
- publish-npm-types — publishes
@pyrpc/types - publish-npm-client — needs types, publishes
@pyrpc/client - publish-npm-react — needs client, publishes
@pyrpc/react - publish-npm-adapters — needs react, publishes
@pyrpc/next,vue,svelte - create-release — waits for all publish jobs, creates GitHub Release
PyPI via OIDC
permissions:
id-token: write
contents: write
steps:
- uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/
skip-existing: trueNo API token. The id-token: write permission lets GitHub create an OIDC token that PyPI trusts. skip-existing prevents errors if a version was already published.
npm chained publishes
Each npm job builds its package before publishing. The chain (needs:) ensures:
- Types are published first (other packages depend on them)
- Client is published before React (React imports from client)
- React is published before Next/Vue/Svelte (adapters import from React)
Pre-release handling
VERSION=$(echo "$GITHUB_REF_NAME" | sed 's/^v//')
if [[ "$VERSION" == *"-"* ]]; then
TAG=$(echo "$VERSION" | awk -F'-' '{print $2}' | awk -F'.' '{print $1}')
npm publish --access public --tag "$TAG"
else
npm publish --access public
fiIf the tag is v0.9.0-beta.1, npm publishes with --tag beta. The latest tag stays on the stable release.

pyRPC