idsmith/
├── src/ # Rust core library + CLI
├── tests/ # Integration tests
├── bindings/
│ ├── python/ # PyO3 + maturin → PyPI
│ └── node/ # napi-rs → npm
└── .github/workflows/ # CI per package, with path filters
This is a Cargo workspace. The root crate (idsmith) publishes to crates.io independently. Binding crates (idsmith-python, idsmith-node) are publish = false.
cargo check -p idsmith
cargo test -p idsmith
cargo fmt -p idsmith -- --check
cargo clippy -p idsmith -- -D warnings
cargo run -p idsmith -- iban DE 5cd bindings/python
python -m venv .venv && source .venv/bin/activate
pip install maturin[patchelf] pytest
maturin develop
pytest tests/ -vcd bindings/node
npm install
npm run build
npm test# Check everything compiles (requires no extra toolchains)
cargo check --workspace
# Format all Rust code
cargo fmt --workspace
# Lint all Rust code
cargo clippy --workspace -- -D warningsEach workflow runs only when relevant files change:
| Workflow | Triggers on |
|---|---|
ci.yml |
src/**, tests/**, Cargo.toml |
python-ci.yml |
bindings/python/**, src/**, Cargo.toml |
node-ci.yml |
bindings/node/**, src/**, Cargo.toml |
release.yml |
Cargo.toml (version change detection) |
This means changing only Python bindings won't trigger Rust core CI, and vice versa.
The workspace shares a single Cargo.lock. This is committed to git and ensures reproducible builds. When updating dependencies, the lock file changes will show binding dependencies (pyo3, napi) even if you only touched the core crate — this is expected.
All three packages publish from a single version bump:
- Bump
versionin rootCargo.toml,bindings/python/pyproject.toml, andbindings/node/package.json - Push to
main→release.ymldetects version change, builds binaries, creates GitHub release - The GitHub release triggers all three publish workflows:
publish.yml→ crates.iopython-publish.yml→ PyPI (builds wheels for Linux/macOS/Windows)node-publish.yml→ npm (builds native modules for all platforms)
CARGO_REGISTRY_TOKEN— crates.io API tokenNPM_TOKEN— npm publish token- PyPI uses OIDC trusted publishing (no secret needed, configure in PyPI settings)