Crates.io Release Process
This guide documents the process for publishing NoETL CLI to crates.io, the official Rust package registry.
Overview
The noetl crate is published to https://crates.io/crates/noetl, allowing users to install via cargo install noetl.
Note: The crate name is noetl (directory: crates/noetlctl, PyPI: noetlctl).
Prerequisites
- crates.io Account: https://crates.io/
- API Token: Generate at https://crates.io/settings/tokens
- Cargo Ownership: Must be added as owner of
noetl-clicrate - Workspace Version: Ensure version is updated in root
Cargo.toml
Setup
1. Create crates.io Account
- Visit https://crates.io/
- Sign in with GitHub account
- Verify email address
2. Generate API Token
- Go to https://crates.io/settings/tokens
- Click "New Token"
- Name:
noetl-publish - Permissions:
publish-update - Copy the generated token
3. Configure Cargo
# Login with token
cargo login <your-token>
# Or set environment variable
export CARGO_REGISTRY_TOKEN=<your-token>
Token is stored in ~/.cargo/credentials.toml.
Package Preparation
1. Update Cargo.toml Metadata
Ensure crates/noetlctl/Cargo.toml has all required fields:
[package]
name = "noetl"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
description = "NoETL workflow automation CLI"
keywords = ["workflow", "automation", "etl", "mlops", "data-pipeline"]
categories = ["command-line-utilities", "development-tools"]
readme = "README.md"
Required fields for crates.io:
description- Short package descriptionlicense- License identifier (e.g., "MIT")repository- GitHub repository URLkeywords- Up to 5 keywords for discoverabilitycategories- Crates.io categoriesreadme- Path to README file
2. Update README
Ensure crates/noetlctl/README.md is user-friendly:
- Installation instructions
- Quick start example
- Feature highlights
- Link to full documentation
3. Update Workspace Version
# Cargo.toml (workspace root)
[workspace.package]
version = "2.5.3"
Important: The crate name is noetl but the directory is crates/noetlctl and PyPI package is noetlctl. This naming scheme allows:
- Clean cargo install:
cargo install noetl - Consistent directory naming:
noetlctl= "noetl control" - Separate PyPI namespace:
pip install noetlctl
Publication Process
Manual Publication
# Navigate to crate directory
cd crates/noetlctl
# Verify package builds
cargo build --release
# Dry run (verify packaging)
cargo publish --dry-run
# Publish to crates.io
cargo publish
Using Publish Script
# From repository root
./scripts/publish_crate.sh 2.5.3
The script will:
- Verify API token is configured
- Check required metadata fields
- Build the package
- Run dry-run validation
- Prompt for confirmation
- Publish to crates.io
Verification
Check Publication Status
# View package on crates.io
open https://crates.io/crates/noetl-cli
# Test installation
cargo install noetl-cli --force
noetl --version
Monitor Downloads
View statistics at:
Versioning
Version Numbers
Follow semantic versioning (SemVer):
- Major (X.0.0): Breaking changes
- Minor (0.X.0): New features, backwards compatible
- Patch (0.0.X): Bug fixes, backwards compatible
Yanking Versions
If a version has critical bugs:
# Yank version (prevents new installs, doesn't break existing)
cargo yank --vers 2.5.2
# Un-yank if mistake
cargo yank --vers 2.5.2 --undo
Workspace Publishing
Since noetl-cli is in a workspace:
- Publish order matters: No dependencies on other workspace members
- Version sync: Use
version.workspace = true - Shared metadata: Use
license.workspace = true, etc.
If publishing multiple crates:
# Publish in dependency order
cargo publish -p noetl-cli
# cargo publish -p gateway # if needed
Automation with GitHub Actions
Create .github/workflows/publish-crate.yml:
name: Publish to crates.io
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Verify version
run: |
TAG_VERSION=${GITHUB_REF#refs/tags/v}
CARGO_VERSION=$(grep '^version' Cargo.toml | head -1 | cut -d'"' -f2)
if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
echo "Version mismatch: tag=$TAG_VERSION, cargo=$CARGO_VERSION"
exit 1
fi
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
cd crates/noetlctl
cargo publish
Add CARGO_REGISTRY_TOKEN to repository secrets:
- Go to repository Settings > Secrets and variables > Actions
- New repository secret
- Name:
CARGO_REGISTRY_TOKEN - Value: Your crates.io API token
Best Practices
Pre-Release Checklist
- Update version in workspace
Cargo.toml - Update CHANGELOG.md
- Test build:
cargo build --release -p noetl-cli - Run tests:
cargo test -p noetl-cli - Update README if needed
- Run
cargo publish --dry-run - Create Git tag:
git tag v2.5.3 - Push tag:
git push origin v2.5.3
Post-Release
- Verify package appears on crates.io
- Test installation:
cargo install noetl-cli --force - Update documentation with new version
- Announce release (GitHub Releases, blog, etc.)
Documentation
Update docs.rs documentation by creating crates/noetlctl/.cargo-ok or using:
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
Troubleshooting
"crate already exists"
Version already published. Increment version number.
"failed to verify permissions"
Not listed as crate owner. Ask existing owner to add you:
cargo owner --add github:username noetl-cli
"license is not specified"
Add to Cargo.toml:
license = "MIT"
# or
license-file = "LICENSE"
"package size exceeds limit"
Crates.io has 10MB limit. Exclude unnecessary files:
# Cargo.toml
[package]
exclude = [
"tests/",
"benches/",
"examples/",
".github/",
"*.log",
]
Build fails on crates.io
Ensure all dependencies are from crates.io (not git):
# Bad: git dependency
dependency = { git = "https://..." }
# Good: crates.io dependency
dependency = "1.0"