Skip to content

Releasing to PyPI

Samudra is published to PyPI as a single pure-Python wheel by the Release workflow. It authenticates with OIDC trusted publishing, so no API token is stored anywhere.

Installing the package

Samudra itself is pure Python, so one universal wheel serves every platform. PyTorch provides the optimized attention kernels used on GPUs.

# CPU (default)
uv add samudra
pip install samudra

# GPU — adds torchvision; PyTorch SDPA supplies optimized attention
uv add "samudra[cuda]"
pip install "samudra[cuda]"

# Latest nightly dev build
uv add samudra --prerelease=allow
pip install --pre samudra

The cuda extra does not compile attention extensions. PyTorch's SDPA dispatcher selects an available optimized CUDA kernel at runtime, with no additional FlashAttention package or CUDA build toolchain required by Samudra.

Installing exposes a samudra console command that mirrors the module entry points, so you don't need a checkout to run a task:

# Bundled preset by name (the example configs ship in the wheel):
samudra train samudra_om4/train.yaml --experiment.data_root $DATA_PATH
# Or a path to your own YAML:
samudra eval  path/to/eval.yaml  --ckpt_path path/to/checkpoint
samudra viz   path/to/viz.yaml

The example presets under src/samudra/configs/ are packaged with the wheel, so a CONFIG argument may be either a bundled preset name (e.g. samudra_om4/train.yaml) or a filesystem path — a real path always wins, and a bare name that isn't found on disk falls back to the bundled preset. Test-fixture configs under tests/configs/ are dev-only and excluded from the wheel.

How versions are cut

The version is owned by setuptools-scm: there is no version = "..." field to maintain — a git tag is the version. [tool.setuptools_scm] in pyproject.toml configures it, and samudra.__version__ is available at runtime. The release paths differ only in what version reaches the build:

Trigger Mode Version Published?
Push a v* tag stable the tag, e.g. v1.0.01.0.0 (setuptools-scm) ✅ PyPI
Weekly schedule (Mon 06:00 UTC) nightly <next-patch>.dev<YYYYMMDDhhmm> ✅ PyPI
workflow_dispatchnightly/stable as chosen as above ✅ PyPI
workflow_dispatchsmoke smoke <next-patch>+smoke.<sha> ❌ build-only
Pull request touching the script/workflow smoke ❌ build-only
Local editable install (uv sync) <next-patch>.dev<N> from git n/a

The scheduled dev release is weekly rather than daily — Samudra doesn't turn over enough in a day to warrant one, and the timestamp still makes each build unique. The smoke mode is build-only: it names what the build does (a no-publish check), not how it's triggered — every mode, smoke included, can be started by hand from Actions → Release → Run workflow.

On a tagged commit setuptools-scm derives the version straight from the tag. For the two synthetic modes, scripts/package.py computes the version and hands it to setuptools-scm via SETUPTOOLS_SCM_PRETEND_VERSION_FOR_SAMUDRA — it never edits a tracked file. A nightly's base is one patch above the most recent v* tag (or fallback_version before the first tag), and its UTC timestamp keeps every nightly unique and PEP 440-ordered above the last release, so --pre resolves them.

Every publish (stable and nightly) waits on the CPU test suite: the release workflow calls test.yml as a required test job, so a red suite blocks the upload. A tag push doesn't otherwise run the tests, which is why the release workflow invokes them itself.

Cutting a stable release

# Just tag and push — no version bump anywhere:
git tag -a v0.0.1 -m "Initial Release"
git push origin v0.0.1

The tag push runs resolve → test → build → publish, uploading samudra 0.0.1 to PyPI. To dry-run first, use Actions → Release → Run workflow → mode: smoke; that builds and runs twine check without publishing.