-
Notifications
You must be signed in to change notification settings - Fork 381
crate publishing workflow #755
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
96952ee
6affcd3
f717d30
52849b1
4710c0d
556f9af
0759b3c
24b706a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,104 @@ | ||||||||||||||||||||||||||
| # Release Process | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Publishing DiskANN crates to [crates.io](https://crates.io). | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ## Overview | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| All workspace crates are published together with synchronized version numbers using `cargo publish --workspace`, which automatically resolves dependency order and waits for each crate to be indexed before publishing its dependents. The release is triggered by pushing a version tag. The Rust toolchain version is read from [`rust-toolchain.toml`](../../rust-toolchain.toml). | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ## Prerequisites | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| 1. **CRATES_IO_TOKEN Secret**: A crates.io API token configured as a GitHub repository secret named `CRATES_IO_TOKEN` with publish permissions for all DiskANN crates. | ||||||||||||||||||||||||||
| - Create a token: [crates.io/settings/tokens](https://crates.io/settings/tokens) | ||||||||||||||||||||||||||
| - Add the secret: Repository Settings → Secrets and variables → Actions → New repository secret | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| 2. **Maintainer Access**: Write access to the repository and owner/maintainer of all crates on crates.io. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ## Dry-Run Testing | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| **Always test before publishing a real release.** | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Option 1: GitHub Actions (Recommended) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| 1. Navigate to: `https://github.com/microsoft/DiskANN/actions/workflows/publish.yml` | ||||||||||||||||||||||||||
| 2. Click **Run workflow**, select your branch, keep **dry-run = true** | ||||||||||||||||||||||||||
| 3. Watch the workflow — look for successful `cargo publish --workspace --dry-run` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Option 2: Local | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||||||
| cargo publish --locked --workspace --dry-run | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
| 3. Watch the workflow — look for successful `cargo publish --workspace --dry-run` | |
| ### Option 2: Local | |
| ```bash | |
| cargo publish --locked --workspace --dry-run | |
| 3. Watch the workflow — look for successful `cargo publish --locked --dry-run` | |
| ### Option 2: Local | |
| ```bash | |
| cargo publish --locked --dry-run |
Copilot
AI
Feb 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The claim that dry-run tests "Dependency resolution and publish ordering" is misleading. Since workspace-wide publishing isn't supported by cargo publish, the actual implementation will need to determine publish order separately. A dry-run of a single crate publish won't validate that all crates will be published in the correct dependency order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may want to mention what to do if the publish step fails. Basically, we either need to manually fix and publish the remaining crates, or fix the publish issue, bump the version number, and bump again (or if the fix doesn't change the versions that succeeded, then the remaining ones can be updated and pushed potentially in the same release).
If the publishing fails part way through for networking issues, then we'll need to handle the remaining ones manually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This recommends pushing directly to main, which we can't do due to branch protection rules.
Instead, we should use this workflow:
- Checkout main, bump the version and push.
- Make a pull-request out of the version bumped commit.
- Use the dry-run as a pre-merge check.
- Merge the PR and use the github UI to tag the release along with change notes.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,81 @@ | ||||||||||||||||||||||||||||||||||||||||||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||||||||||||||||||||||||||||||||||||||||||
| # Licensed under the MIT license. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| # Publishes all workspace crates to crates.io. | ||||||||||||||||||||||||||||||||||||||||||
| # Triggered by pushing a version tag (v{major}.{minor}.{patch}) or manually via workflow_dispatch. | ||||||||||||||||||||||||||||||||||||||||||
| # Requires CRATES_IO_TOKEN secret. Rust toolchain version is read from rust-toolchain.toml. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| name: Publish to crates.io | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||||||||||||||
| push: | ||||||||||||||||||||||||||||||||||||||||||
| tags: | ||||||||||||||||||||||||||||||||||||||||||
| - 'v[0-9]+.[0-9]+.[0-9]+' | ||||||||||||||||||||||||||||||||||||||||||
| workflow_dispatch: | ||||||||||||||||||||||||||||||||||||||||||
| inputs: | ||||||||||||||||||||||||||||||||||||||||||
| dry_run: | ||||||||||||||||||||||||||||||||||||||||||
| description: 'Run in dry-run mode (test without actually publishing)' | ||||||||||||||||||||||||||||||||||||||||||
| required: false | ||||||||||||||||||||||||||||||||||||||||||
| default: 'true' | ||||||||||||||||||||||||||||||||||||||||||
| type: choice | ||||||||||||||||||||||||||||||||||||||||||
| options: | ||||||||||||||||||||||||||||||||||||||||||
| - 'true' | ||||||||||||||||||||||||||||||||||||||||||
| - 'false' | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||||||||||
| RUST_BACKTRACE: 1 | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| defaults: | ||||||||||||||||||||||||||||||||||||||||||
| run: | ||||||||||||||||||||||||||||||||||||||||||
| shell: bash | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||||||||||||||||||||
| contents: read | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||||||||||||
| publish: | ||||||||||||||||||||||||||||||||||||||||||
| name: ${{ github.event.inputs.dry_run == 'true' && 'Dry-run publish test' || 'Publish crates to crates.io' }} | ||||||||||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||||||||||
| - name: Checkout repository | ||||||||||||||||||||||||||||||||||||||||||
| uses: actions/checkout@v4 | ||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||
| lfs: true | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| - name: Read Rust version from rust-toolchain.toml | ||||||||||||||||||||||||||||||||||||||||||
| id: rust-version | ||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||
| RUST_VERSION=$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml) | ||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reading from |
||||||||||||||||||||||||||||||||||||||||||
| echo "channel=$RUST_VERSION" >> "$GITHUB_OUTPUT" | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| - name: Install Rust ${{ steps.rust-version.outputs.channel }} | ||||||||||||||||||||||||||||||||||||||||||
| uses: dtolnay/rust-toolchain@stable | ||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||
| toolchain: ${{ steps.rust-version.outputs.channel }} | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| - uses: Swatinem/rust-cache@v2 | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| - name: Verify version matches tag | ||||||||||||||||||||||||||||||||||||||||||
| if: github.event_name == 'push' | ||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One thing I noticed while adding another comment is that since manual running takes Copilot suggest putting something like this early in the workflow - name: Prevent publish from non-main branch
if: >-
github.event_name == 'workflow_dispatch'
&& github.event.inputs.dry_run != 'true'
&& github.ref != 'refs/heads/main'
run: |
echo "Live publishing is only allowed from main. Use dry-run for other branches."
exit 1which I think is reasonable. It will fail the triggered manually on a branch that is not |
||||||||||||||||||||||||||||||||||||||||||
| TAG_VERSION="${GITHUB_REF#refs/tags/v}" | ||||||||||||||||||||||||||||||||||||||||||
| CARGO_VERSION=$(grep '^version = ' Cargo.toml | head -n1 | sed 's/.*"\(.*\)".*/\1/') | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| CARGO_VERSION=$(grep '^version = ' Cargo.toml | head -n1 | sed 's/.*"\(.*\)".*/\1/') | |
| CARGO_VERSION=$(grep -A 5 '^\[workspace\.package\]' Cargo.toml | grep 'version = ' | head -n1 | sed 's/.*"\(.*\)".*/\1/') |
Copilot
AI
Feb 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding a validation step to ensure that all workspace crates use version.workspace = true and don't have hardcoded versions. This would catch configuration errors before attempting to publish. A simple check like grep -r '^version = "' */Cargo.toml | grep -v workspace could identify crates with hardcoded versions.
Copilot
AI
Feb 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Running the full test suite on every publish attempt (including dry-run) adds significant time to the workflow. Consider making the test step conditional with if: github.event.inputs.dry_run != 'true' || github.event_name == 'push' to skip tests during dry-run testing, since tests are already validated in PR CI checks before merging. However, keeping tests for actual releases (tag pushes) is valuable as a final safety check.
| - name: Run tests | |
| - name: Run tests | |
| if: github.event.inputs.dry_run != 'true' || github.event_name == 'push' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Vermy minor - this can be slightly safer with
env:
DRY_RUN: ${{ github.event.inputs.dry_run || 'false ' }}And then using DRY_RUN to set the DRY_RUN_FLAG internally rather than expanding the entire github.event directly in the shell script. Evaluating it in env should force it to be a boolean as an extra safety measure.
Copilot
AI
Feb 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dry-run default expression github.event.inputs.dry_run || 'false' will evaluate to 'false' when the workflow is triggered by a tag push (since github.event.inputs.dry_run will be empty/null). This is correct behavior for tag-triggered publishes. However, consider making this more explicit with a comment or using a more readable approach like setting a workflow-level environment variable that checks both the trigger type and input.
| if [ "${{ github.event.inputs.dry_run || 'false' }}" = "true" ]; then | |
| # For tag-push events, github.event.inputs.dry_run is empty, so this evaluates to a real (non-dry-run) publish. | |
| if [ "${{ github.event.inputs.dry_run }}" = "true" ]; then |
Copilot
AI
Feb 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When not in dry-run mode (live publishing), there's no visual indicator in the logs. Consider adding an else clause to echo a message like "📦 LIVE MODE - Publishing to crates.io" to make it clear when actual publishing is happening. This helps prevent accidental publishes and makes logs easier to understand.
| echo "🧪 DRY-RUN MODE" | |
| echo "🧪 DRY-RUN MODE" | |
| else | |
| echo "📦 LIVE MODE - Publishing to crates.io" |
Copilot
AI
Feb 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cargo publish command does not support a --workspace flag. As of Rust 1.92 (and as of January 2025), cargo publish only publishes a single crate at a time. To publish multiple workspace crates, you need to either:
- Use a loop to publish each crate individually in dependency order
- Use a tool like
cargo-workspacesorcargo-release - Manually specify the publish order
The current command will fail with an error. You'll need to implement a solution that publishes crates one at a time, ensuring dependencies are published and indexed before their dependents.
| cargo publish --locked --workspace $DRY_RUN_FLAG | |
| echo "Resolving workspace crates to publish..." | |
| PKGS=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.source == null) | .name') | |
| if [ -z "$PKGS" ]; then | |
| echo "::error::No workspace crates found to publish" | |
| exit 1 | |
| fi | |
| for pkg in $PKGS; do | |
| echo "📦 Publishing crate: $pkg (DRY_RUN_FLAG='$DRY_RUN_FLAG')" | |
| cargo publish --locked -p "$pkg" $DRY_RUN_FLAG | |
| # When actually publishing, wait a bit for crates.io index to update | |
| if [ -z "$DRY_RUN_FLAG" ]; then | |
| echo "Waiting for crates.io index to update before publishing the next crate..." | |
| sleep 30 | |
| fi | |
| done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not accurate. There is a cargo publish --workspace and it does work. It doesn't necessarily handle partial failures gracefully, but hopefully those are relatively rare.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation states that
cargo publish --workspace"automatically resolves dependency order and waits for each crate to be indexed before publishing its dependents," but this is not accurate. Thecargo publishcommand does not have a--workspaceflag and does not automatically handle dependency ordering or index polling. This documentation needs to be updated to reflect the actual implementation approach.