cargoPublish

cargoPublish :: AttrSet → Effect

Deploys package to crates.io, the Rust community’s crate repository.

Example:

effects.cargoPublish {
  secretName = "cargo-api-token";
  src = ./.;
}
nix

Example secret:

  "cargo-api-token": {
    "kind": "Secret",
    "data": {
      "token": "..."
    },
    "condition": {
      "and": [
        { "isOwner": "my-github-org" },
        { "isRepo": "my-site-repo" }
      ]
    }
  }
json

Parameters

secretName

The secret that will be looked up in secrets.json.

The data field must contain a "token" field, with a string value that is a Cargo API token. To make one, navigate to API tokens and use New Token.

extraPublishArgs

Extra arguments to pass to the cargo publish invocation.

src

Example: src = ./.;

Path to the source code of the package to publish.

src is unpacked by the Nixpkgs unpackPhase.

manifestPath

String containing the path to a Cargo.toml, default: Cargo.toml. Relative paths are relative to the unpacked src.

A store path subpath could be passed instead, bypassing unpackPhase.

registryURL

Optional registry to publish to. Defaults to cargo’s default behavior, which is to publish to package.publish or crates.io.

If you use an alternate registry or private registry, you are recommended to store this information in Cargo.toml.

Note that this must be the backing git repository URL, not a web or API URL.

dryRun

Boolean, default false. When true, runs cargo publish --dry-run which packages and verifies the crate(s) without uploading.

When dryRun is true, the secretName is not required.

assertVersions

Verify package versions before publishing. This can be:

  • An empty attribute set {} (default): no version checking

  • A string like "1.0.0": check that all packages in the workspace have this version

  • An attribute set mapping package names to versions: { my-crate = "1.0.0"; }

Example for workspace with synchronized versions:

effects.cargoPublish {
  src = ./.;
  secretName = "cargo-api-token";
  assertVersions = "1.0.0";  # All packages must be version 1.0.0
}
nix

Workflow

Initial Setup

  1. Enable the module in your flake.nix with hercules-ci.cargo-publish.enable = true

  2. Set the version in Cargo.toml to a version that doesn’t exist on crates.io yet (e.g., 0.1.0)

  3. On each push, Hercules CI runs cargo publish --dry-run to verify the package is publishable

Releasing a Version

  1. Review the version — the version in Cargo.toml is probably a patch bump from the last release. Consider whether breaking changes or new features warrant a minor or major bump instead.

  2. Push a tag matching the version in Cargo.toml:

    git tag 0.1.0
    git push origin 0.1.0
    shell
  3. Hercules CI detects the tag and runs cargo publish

  4. Bump the version in Cargo.toml to the next version (e.g., 0.1.1) to allow dry-run checks to verify subsequent commits

With assertVersions = true (module) or assertVersions = "0.1.0" (function), the effect verifies that all workspace package versions match the tag name before publishing.

Rate Limits

When publishing a workspace with many crates for the first time, you may hit crates.io rate limits. The registry limits new crate registrations more strictly than updates to existing crates. At the time of writing, crates.io allows a burst of about 5 new packages, then requires a wait of approximately 10 minutes before publishing more.

If you encounter rate limit errors during an initial release:

  1. Wait for the rate limit to reset (approximately 10 minutes)

  2. Use extraPublishArgs to publish remaining crates individually:

    effects.cargoPublish {
      src = ./.;
      secretName = "cargo-api-token";
      extraPublishArgs = [ "--package" "my-unpublished-crate" ];
    }
    nix
  3. Run locally with hci effect run to iterate through remaining crates:

    hci effect run onPush.default.effects.cargo-publish --pretend-ref refs/tags/0.1.0
    shell

After the initial release, subsequent version updates are less likely to hit rate limits.