How to publish Spanda Rust, Python, and TypeScript SDKs from this repository using GitHub Actions.
Version policy: SDKs use an independent semver line from workspace and desktop — bump with
python3 scripts/bump_version.py patch --stream sdk only when SDK client APIs change. See
versioning.md.
| SDK | Source path | Registry package | Release tag pattern |
|---|---|---|---|
| Rust | crates/spanda-sdk/ |
spanda-sdk |
crates-sdk-vX.Y.Z |
| Python | sdk/python/ |
spanda-sdk |
sdk-python-vX.Y.Z |
| TypeScript | sdk/typescript/ |
@davalgi-spanda/sdk |
npm-sdk-vX.Y.Z |
Version numbers live in (bump together via --stream sdk):
crates/spanda-sdk/Cargo.toml → versionsdk/python/pyproject.toml → [project].versionsdk/typescript/package.json → "version"Tag suffix must match the SDK version (for example sdk-python-v0.5.1 when SDK version =
"0.5.1").
| App | Source path | Distribution | Release tag pattern |
|---|---|---|---|
| Tauri shell | packages/control-center-desktop/ |
GitHub Release + workflow artifacts | desktop-vX.Y.Z |
Keep these three files on the same semver before tagging:
packages/control-center-desktop/package.json → "version"packages/control-center-desktop/src-tauri/Cargo.toml → versionpackages/control-center-desktop/src-tauri/tauri.conf.json → "version"./scripts/verify_desktop_release_ready.sh
# Or bump first:
python3 scripts/bump_version.py patch --stream desktop
git tag desktop-v0.6.3 && git push origin desktop-v0.6.3
Automatic desktop release: When a merged PR has a release:* label and changes Control Center
paths, Auto release bumps desktop and pushes desktop-v* (same semver component as workspace).
See control-center-versioning.md.
Workflow: desktop-release.yml. Optional secrets:
TAURI_UPDATER_PUBKEY, TAURI_SIGNING_PRIVATE_KEY, APPLE_SIGNING_IDENTITY,
APPLE_NOTARIZE_PROFILE. See desktop-release-runbook.md.
Both publish workflows read repository secrets (not committed files).
| Secret name | Used by | Purpose |
|---|---|---|
CRATES_IO_TOKEN |
publish-sdk-rust.yml | Upload spanda-sdk to crates.io |
PYPI_API_TOKEN |
publish-sdk-python.yml | Upload wheels to PyPI |
NPM_TOKEN |
publish-sdk-typescript.yml | Upload @davalgi-spanda/sdk to npm |
You need admin (or equivalent) access on the repository to create secrets.
Never commit tokens to the repo, paste them in issues/PRs, or store them in .pypirc / .npmrc
under version control.
CRATES_IO_TOKEN)spanda-github-actions) and copy the token.Tokens expire (for example 365 days). Set a calendar reminder to create a new token and update
CRATES_IO_TOKEN in GitHub before expiry.
CRATES_IO_TOKENOn tag push crates-sdk-v*:
cargo test -p spanda-sdk (with and without grpc feature)cargo package -p spanda-sdkcargo publish -p spanda-sdkManual trigger: Actions → Publish Rust SDK → Run workflow (tests/package only; upload on matching tags).
Install after publish:
cargo add spanda-sdk
# optional gRPC client
cargo add spanda-sdk --features grpc
PYPI_API_TOKEN)spanda-sdk after it exists.pypi-AgEI....For PyPI uploads, the username is always __token__; GitHub Actions only needs the token string
as the secret value.
PYPI_API_TOKENpypi-... token (not __token__)On tag push sdk-python-v*:
pytest sdk/python/testssdk/python/distManual trigger: Actions → Publish Python SDK → Run workflow (tests and build always run; upload only on matching tags).
NPM_TOKEN)@davalgi-spanda and not @spandaThe npm organization name spanda is already taken by another account. Scoped packages must
live under an org or user you control. This repo publishes TypeScript as:
@davalgi-spanda/sdk
Create the org at npmjs.com/org/create if needed. Public packages under an org are free — the org itself does not expire.
@davalgi-spanda/* (or the specific package)Copy the token (npm_...).
The organization does not expire — only the access token does. npm caps write-enabled granular tokens at 90 days.
Before expiry:
NPM_TOKEN in GitHub Settings → Secrets and variables → Actions.Set a calendar reminder (~80 days) so CI publish does not fail mid-release.
NPM_TOKENnpm_... tokensdk/typescript/package.json must include:
{
"name": "@davalgi-spanda/sdk",
"publishConfig": { "access": "public" }
}
On tag push npm-sdk-v*:
npm ci, npm test, npm run build under sdk/typescriptnpm publish --access publicManual trigger: Actions → Publish TypeScript SDK → Run workflow.
From repo root:
./scripts/verify_sdk_publish_ready.sh
This runs Python tests, builds a PyPI wheel, runs TypeScript tests/build, and checks related packages — without uploading.
Individual checks:
# Python
pip install -e "sdk/python[dev]"
pytest sdk/python/tests -q
(cd sdk/python && python -m build)
# TypeScript
npm ci --prefix sdk/typescript
npm test --prefix sdk/typescript
npm run build --prefix sdk/typescript
npm pack --prefix sdk/typescript
Use the SDK stream bump helper (keeps Rust, Python, and TypeScript in sync):
python3 scripts/bump_version.py patch --stream sdk
Or edit manually:
crates/spanda-sdk/Cargo.tomlsdk/python/pyproject.tomlsdk/typescript/package.jsonCommit and push to main.
Current release line: 0.5.6 — Recovery Orchestrator client methods (getRecoveryPredictive,
listRecoverableEntities, recommendRecovery; Rust gRPC when grpc feature enabled, proto semver
from GET /v1/version — currently 1.0.14). Prior 0.5.5 added Twin Cloud history,
import-replay, and gRPC twins.
./scripts/verify_sdk_publish_ready.sh
./scripts/publish_sdk_release.sh # uses version from crates/spanda-sdk/Cargo.toml
./scripts/publish_sdk_release.sh --dry-run # preview tags only
Or tag manually:
VERSION=0.5.6
git tag crates-sdk-v${VERSION}
git tag sdk-python-v${VERSION}
git tag npm-sdk-v${VERSION}
git push origin crates-sdk-v${VERSION} sdk-python-v${VERSION} npm-sdk-v${VERSION}
GitHub → Actions — confirm Publish Rust SDK, Publish Python SDK, and Publish TypeScript SDK succeed.
pip install spanda-sdk==0.5.5
python -c "from spanda_sdk import SpandaClient; print(SpandaClient.local())"
cargo add spanda-sdk@0.5.5
npm install @davalgi-spanda/sdk@0.5.5
node -e "import('@davalgi-spanda/sdk').then(m => console.log(m.SpandaClient.local()))"
./scripts/verify_sdk_published.sh 0.5.5
GitHub Actions: Verify published SDKs (sdk-publish-verify.yml) — weekly or manual
workflow_dispatch.
Check registry pages:
Set a calendar reminder before each token expires and update the GitHub secret.
| Registry | Secret | Typical expiry |
|---|---|---|
| crates.io | CRATES_IO_TOKEN |
Up to 365 days (set at token creation) |
| PyPI | PYPI_API_TOKEN |
Per token policy on pypi.org |
| npm | NPM_TOKEN |
Up to 90 days (write granular tokens) |
| Symptom | Likely cause | Fix |
|---|---|---|
| crates.io 400 verified email | Email not verified on crates.io account | crates.io/settings/profile — verify inbox link; re-tag after confirmation |
| crates.io empty token | CRATES_IO_TOKEN missing in GitHub |
Add secret; re-tag crates-sdk-v* |
| PyPI publish 403 / invalid credentials | Wrong or revoked PYPI_API_TOKEN |
Regenerate token; update GitHub secret |
| npm publish 401 / E401 | Expired token or missing Bypass 2FA | New granular token with Bypass 2FA; update NPM_TOKEN |
| npm scope / org error | Package name not under your org | Use @davalgi-spanda/sdk, not @spanda/sdk |
| npm “organization name not available” | Name taken globally | Pick another org (for example @davalgi-spanda) |
| Tag pushed but no upload | Tag pattern mismatch | Use sdk-python-v* or npm-sdk-v* exactly |
| Version already exists on registry | Re-used tag/version | Bump version in manifest; new tag |
| Workflow green but package missing | Publish step skipped | Publish runs only on tag push, not workflow_dispatch alone |
If a token was exposed (chat, log, commit), revoke it immediately on PyPI/npm and create a new one.
| Workflow | Tag | Package |
|---|---|---|
| publish-sdk-rust.yml | crates-sdk-v* |
spanda-sdk |
| publish-sdk-python.yml | sdk-python-v* |
spanda-sdk (PyPI) |
| publish-sdk-typescript.yml | npm-sdk-v* |
@davalgi-spanda/sdk |
| publish-npm-web.yml | npm-web-v* |
@davalgi-spanda/web |
| desktop-release.yml | desktop-v* |
@spanda/control-center-desktop (GitHub Release) |
Legacy Python enterprise client: packages/sdk-python — not published by the canonical SDK
workflow.
@davalgi-spanda/web)Same NPM_TOKEN and org as the TypeScript SDK. Package lives in packages/web/ with exports for
ControlCenterPanel and index.css.
git tag npm-web-v0.4.0
git push origin npm-web-v0.4.0
See packages/web/PUBLISHING.md.