From c8bf4096de6b5fdeca04c7c2002330c33a4c95a6 Mon Sep 17 00:00:00 2001
From: Ed Page
Date: Tue, 3 Jun 2025 16:01:51 -0500
Subject: [PATCH] feat(publish): Stabilize multi-package publishing
A user will now be able to use flags like `--workspace` with `cargo
publish`.
`cargo package` will now also work with those flags without having to
pass `--no-verify --exclude-lockfile`.
Many release tools have come out that solve this problem.
They will still need a lot of the logic that went into that for other
parts of the release process.
However, a cargo-native solution allows for:
- Verification during dry-run
- Better strategies for waiting for the publish timeout
`cargo publish` is non-atomic at this time.
If there is a server side error, network error, or rate limit during the publish,
the workspace will be left in a partially published state.
Verification is done before any publishing so that won't affect things.
There are multiple strategies we can employ for improving this over time,
including
- atomic publish
- `--idempotent` (#13397)
- leave this to release tools to manage
This includes support for `--dry-run` verification.
As release tools didn't have a way to do this before,
users may be surprised at how slow this is because a `cargo build` is
done instead of a `cargo check`. This is being tracked in #14941.
This adds to `cargo package` the `--registry` and `--index` flags to
help with resolving dependencies when depending on a package being
packaged at that moment.
These flags are only needed when a `cargo package --workspace` operation
would have failed before due to inability to find a locally created
dependency.
Regarding the publish timeout, `cargo publish --workspace` publishes
packages in batches and we only timeout if nothing in the batch has
finished being published within the timeout, deferring the rest to the
next wait-for-publish. So for example, if you have packages `a`, `b`, `c` then
we'll wait up to 60 seconds and if only `a` and `b` were ready in that time,
we'll then wait another 60 seconds for `c`.
During testing, users ran into issues with `.crate` checksums that we've
not been able to reproduce since:
- https://github.com/rust-lang/cargo/issues/1169#issuecomment-2567995987
- #14396
By stabilizing this, Cargo's behavior becomes dependent on an overlay
registry.
When generating a lockfile or verifying a package, we overlay the
locally generated `.crate` files on top of the registry so the registry
appears as it would and everything works.
If there is a conflict with a version, the local version wins which is
important for the dry-run mode of release tools as they won't have
bumped the version yet.
Our concern for the overlay registry is dependency confusion attacks.
Considering this is not accessible for general user operations, this
should be fine.
Fixes #1169
Fixes #10948
---
src/bin/cargo/commands/package.rs | 20 +-
src/bin/cargo/commands/publish.rs | 21 +-
src/cargo/core/features.rs | 6 +-
src/cargo/ops/cargo_package/mod.rs | 4 +-
src/cargo/ops/registry/publish.rs | 23 +-
src/doc/man/cargo-publish.md | 59 +-
src/doc/man/generated_txt/cargo-publish.txt | 26 +-
src/doc/src/commands/cargo-publish.md | 29 +-
src/etc/man/cargo-publish.1 | 24 +-
tests/testsuite/cargo/z_help/stdout.term.svg | 44 +-
.../cargo_package/help/stdout.term.svg | 4 +-
.../cargo_publish/help/stdout.term.svg | 4 +-
tests/testsuite/package.rs | 586 ++----------------
tests/testsuite/publish.rs | 232 +++----
tests/testsuite/workspaces.rs | 6 +-
15 files changed, 192 insertions(+), 896 deletions(-)
diff --git a/src/bin/cargo/commands/package.rs b/src/bin/cargo/commands/package.rs
index d48f32ca8..ae556eda2 100644
--- a/src/bin/cargo/commands/package.rs
+++ b/src/bin/cargo/commands/package.rs
@@ -7,8 +7,8 @@ use cargo::ops::PackageOpts;
pub fn cli() -> Command {
subcommand("package")
.about("Assemble the local package into a distributable tarball")
- .arg_index("Registry index URL to prepare the package for (unstable)")
- .arg_registry("Registry to prepare the package for (unstable)")
+ .arg_index("Registry index URL to prepare the package for")
+ .arg_registry("Registry to prepare the package for")
.arg(
flag(
"list",
@@ -57,22 +57,6 @@ pub fn cli() -> Command {
}
pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
- if args._value_of("registry").is_some() {
- gctx.cli_unstable().fail_if_stable_opt_custom_z(
- "--registry",
- 13947,
- "package-workspace",
- gctx.cli_unstable().package_workspace,
- )?;
- }
- if args._value_of("index").is_some() {
- gctx.cli_unstable().fail_if_stable_opt_custom_z(
- "--index",
- 13947,
- "package-workspace",
- gctx.cli_unstable().package_workspace,
- )?;
- }
let reg_or_index = args.registry_or_index(gctx)?;
let ws = args.workspace(gctx)?;
if ws.root_maybe().is_embedded() {
diff --git a/src/bin/cargo/commands/publish.rs b/src/bin/cargo/commands/publish.rs
index 42b6377c7..57eff8d03 100644
--- a/src/bin/cargo/commands/publish.rs
+++ b/src/bin/cargo/commands/publish.rs
@@ -20,8 +20,8 @@ pub fn cli() -> Command {
.arg_silent_suggestion()
.arg_package_spec_no_all(
"Package(s) to publish",
- "Publish all packages in the workspace (unstable)",
- "Don't publish specified packages (unstable)",
+ "Publish all packages in the workspace",
+ "Don't publish specified packages",
)
.arg_features()
.arg_parallel()
@@ -45,23 +45,6 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
.into());
}
- let unstable = gctx.cli_unstable();
- let enabled = unstable.package_workspace;
- if args.get_flag("workspace") {
- unstable.fail_if_stable_opt_custom_z("--workspace", 10948, "package-workspace", enabled)?;
- }
- if args._value_of("exclude").is_some() {
- unstable.fail_if_stable_opt_custom_z("--exclude", 10948, "package-workspace", enabled)?;
- }
- if args._values_of("package").len() > 1 {
- unstable.fail_if_stable_opt_custom_z(
- "--package (multiple occurrences)",
- 10948,
- "package-workspace",
- enabled,
- )?;
- }
-
ops::publish(
&ws,
&PublishOpts {
diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs
index 1e9dd73ba..5c13b1a6e 100644
--- a/src/cargo/core/features.rs
+++ b/src/cargo/core/features.rs
@@ -845,7 +845,6 @@ unstable_cli_options!(
next_lockfile_bump: bool,
no_embed_metadata: bool = ("Avoid embedding metadata in library artifacts"),
no_index_update: bool = ("Do not update the registry index even if the cache is outdated"),
- package_workspace: bool = ("Handle intra-workspace dependencies when packaging"),
panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"),
profile_hint_mostly_unused: bool = ("Enable the `hint-mostly-unused` setting in profiles to mark a crate as mostly unused."),
profile_rustflags: bool = ("Enable the `rustflags` option in profiles in .cargo/config.toml file"),
@@ -942,6 +941,9 @@ const STABILIZED_CHECK_CFG: &str =
const STABILIZED_DOCTEST_XCOMPILE: &str = "Doctest cross-compiling is now always enabled.";
+const STABILIZED_PACKAGE_WORKSPACE: &str =
+ "Workspace packaging and publishing (a.k.a. `-Zpackage-workspace`) is now always enabled.";
+
fn deserialize_comma_separated_list<'de, D>(
deserializer: D,
) -> Result
-
Selecting more than one package with this option is unstable and available only
-on the
-nightly channel
-and requires the -Z package-workspace flag to enable.
-See https://github.com/rust-lang/cargo/issues/10948 for more information.
+double quotes around each pattern.
This option is unstable and available only on the
-nightly channel
-and requires the -Z package-workspace flag to enable.
-See https://github.com/rust-lang/cargo/issues/10948 for more information.
+single quotes or double quotes around each pattern.
diff --git a/src/etc/man/cargo-publish.1 b/src/etc/man/cargo-publish.1
index 6f9fd22d4..462c94bb6 100644
--- a/src/etc/man/cargo-publish.1
+++ b/src/etc/man/cargo-publish.1
@@ -100,11 +100,6 @@ The default members of a workspace can be set explicitly with the
virtual workspace will include all workspace members (equivalent to passing
\fB\-\-workspace\fR), and a non\-virtual workspace will include only the root crate itself.
.sp
-Selecting more than one package is unstable and available only on the
-\fInightly channel\fR
-and requires the \fB\-Z package\-workspace\fR flag to enable.
-See for more information.
-.sp
\fB\-p\fR \fIspec\fR\[u2026],
\fB\-\-package\fR \fIspec\fR\[u2026]
.RS 4
@@ -113,22 +108,16 @@ SPEC format. This flag may be specified multiple times and supports common Unix
glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally
expanding glob patterns before Cargo handles them, you must use single quotes or
double quotes around each pattern.
-.sp
-Selecting more than one package with this option is unstable and available only
-on the
-\fInightly channel\fR
-and requires the \fB\-Z package\-workspace\fR flag to enable.
-See for more information.
.RE
.sp
\fB\-\-workspace\fR
.RS 4
Publish all members in the workspace.
+.RE
.sp
-This option is unstable and available only on the
-\fInightly channel\fR
-and requires the \fB\-Z package\-workspace\fR flag to enable.
-See for more information.
+\fB\-\-all\fR
+.RS 4
+Deprecated alias for \fB\-\-workspace\fR\&.
.RE
.sp
\fB\-\-exclude\fR \fISPEC\fR\[u2026]
@@ -138,11 +127,6 @@ Exclude the specified packages. Must be used in conjunction with the
common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell
accidentally expanding glob patterns before Cargo handles them, you must use
single quotes or double quotes around each pattern.
-.sp
-This option is unstable and available only on the
-\fInightly channel\fR
-and requires the \fB\-Z package\-workspace\fR flag to enable.
-See for more information.
.RE
.SS "Compilation Options"
.sp
diff --git a/tests/testsuite/cargo/z_help/stdout.term.svg b/tests/testsuite/cargo/z_help/stdout.term.svg
index b0dc5c541..8f424558d 100644
--- a/tests/testsuite/cargo/z_help/stdout.term.svg
+++ b/tests/testsuite/cargo/z_help/stdout.term.svg
@@ -1,4 +1,4 @@
-