This tutorial assumes a Rust workspace with a root Cargo.toml and one or more
member crates.
1. Create `lading.toml`
Create a minimal configuration file at the workspace root:
[bump.documentation]
globs = ["README.md", "docs/**/*.md"]
[publish]
strip_patches = "per-crate"
lading.toml can be omitted entirely. When absent, lading uses the defaults
documented in the configuration reference below.
2. Bump versions
To update the workspace and member crate manifests to 1.2.3:
lading bump 1.2.3
To preview changes without writing any files:
lading bump 1.2.3 --dry-run
After updating manifest versions, lading bump automatically refreshes any
git-tracked Cargo.lock files (excluding those under target/) that have an
adjacent Cargo.toml. Refreshed lockfiles are listed in the command output
with a (lockfile) suffix. In dry-run mode the lockfiles are listed but not
modified.
If bump.documentation.globs is configured, lading also searches those
Markdown files for TOML code fences and updates version values that refer to
workspace crates.
3. Publish in dry-run mode
By default, publish runs cargo publish --dry-run so the full pipeline can
be validated without uploading crates.
lading publish
Before running cargo check and cargo test, lading publish validates that
all git-tracked Cargo.lock files are fresh under --locked mode. If any
lockfile is stale — for example after a lading bump that regenerated a nested
workspace lockfile — the command exits with code 1 and prints a repair command:
Tracked Cargo.lock files are stale after manifest version changes.
Run the following to repair:
cargo generate-lockfile --manifest-path <path>/Cargo.toml
Run the repair command, commit the updated lockfile, then re-run
lading publish.
To require a clean working tree before running the pre-flight checks, pass
--forbid-dirty:
lading publish --forbid-dirty
To perform a real publish (no --dry-run), pass --live:
lading publish --live
Dry-run publishing packages every publishable crate first, then runs
cargo publish --dry-run for every crate. Live publishing follows
publish.order crate by crate: cargo package, then cargo publish, then the
next crate. This lets a later crate depend on a newly published earlier crate
in the same --live run. Live publishing is not transactional; if a later
crate fails, crates already uploaded to crates.io are not rolled back. Reruns
skip versions that are already present on crates.io and continue with the
remaining crates.
bump adopts the workspace README.md for any member crate that sets
readme.workspace = true. The adopted README is written into the crate
directory and relative Markdown links are rewritten so they still resolve from
that directory. publish then stages the already-prepared workspace into a
temporary directory before packaging.
Dry-run limitations with unpublished workspace dependencies
cargo package validates dependency versions against the live crates.io index,
even in dry-run mode. When two or more workspace crates are released together
for the first time and one depends on another at a version that is not yet on
crates.io, cargo package will fail with an error similar to:
error: failed to prepare local package for uploading
Caused by:
failed to select a version for the requirement `inner_crate = "^0.8.0"`
candidate versions found which didn't match: 0.7.0, 0.6.0, ...
location searched: crates.io index
required by package `outer_crate v0.8.0`
This affects dry-run release trains that introduce a new shared version across
multiple workspace crates. lading publish --live avoids the limitation by
publishing each crate immediately after it is packaged, so a later crate can
resolve a dependency that an earlier crate in publish.order just uploaded.
Plain dry-runs still use Cargo's live index. By default, lading downgrades
these index-lookup failures to warnings when the missing dependency is also in
the publish plan and appears earlier in publish order.
Manual staged publishing
When a release must be split manually, run lading publish --live for the
foundational crate first, then run lading publish (dry-run) or
lading publish --live for the remaining workspace once the new version is
indexed:
# 1. Publish the foundational crate live so crates.io has the new version.
lading publish --live --workspace-root path/to/workspace
# 2. Once the new version is indexed, publish (or dry-run) dependent crates.
lading publish --workspace-root path/to/workspace
lading skips crates whose versions are already on crates.io, so the second
invocation only acts on the remaining crates.
--allow-unpublished-workspace-deps (dry-run only)
For CI gating where a real publish is not desirable, dry-run mode defaults to
the same behaviour as passing --allow-unpublished-workspace-deps: it
downgrades the index-lookup failure to a warning when the missing dependency is
itself part of the planned publish set and appears earlier in publish order:
lading publish --allow-unpublished-workspace-deps
The override applies to both the cargo package step and the subsequent
cargo publish --dry-run step (which packages internally and hits the same
crates.io index lookup), so the dry run completes end-to-end.
Use --no-allow-unpublished-workspace-deps to opt out during dry-runs and keep
Cargo's index lookup strict:
lading publish --no-allow-unpublished-workspace-deps
--allow-unpublished-workspace-deps is rejected when combined with --live
because the failure cannot be bypassed during a real publish.
--no-allow-unpublished-workspace-deps remains valid with --live; it
preserves the strict behaviour that live publishes already use. When the
missing dependency is not in the publish plan, or when it appears after
the current crate in publish.order, the failure is still treated as an error.
Fix the explicit publish.order so foundational crates come before dependants,
or remove publish.order and rely on dependency-derived topological sorting.
Each such downgrade is counted and surfaced in the metrics summary emitted at
exit; see Observability.