Skip to main content

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

Setup

1. Create crates.io Account

  1. Visit https://crates.io/
  2. Sign in with GitHub account
  3. Verify email address

2. Generate API Token

  1. Go to https://crates.io/settings/tokens
  2. Click "New Token"
  3. Name: noetl-publish
  4. Permissions: publish-update
  5. 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 description
  • license - License identifier (e.g., "MIT")
  • repository - GitHub repository URL
  • keywords - Up to 5 keywords for discoverability
  • categories - Crates.io categories
  • readme - 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:

  1. Verify API token is configured
  2. Check required metadata fields
  3. Build the package
  4. Run dry-run validation
  5. Prompt for confirmation
  6. 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:

  1. Publish order matters: No dependencies on other workspace members
  2. Version sync: Use version.workspace = true
  3. 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:

  1. Go to repository Settings > Secrets and variables > Actions
  2. New repository secret
  3. Name: CARGO_REGISTRY_TOKEN
  4. 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"