feat(publish): workspace publish skips publish=false

This changes how cargo-publish works with the unstable
`-Zpackage-workspace` feature.

Before this, when publishing the entire workspace,
like `cargo publish --workspace`,
if there is a package with `package.pulibsh=false,
it'll fail the entire publish process.

After this, when `--workspace` is passed,
or when publishing the virtual workspace,
the intent is more like “publish all publishable in this workspace”,
so skip `publish=false` packages and proceed to publish others.

The new overall behavior looks like this:

- `cargo publish` (inside a `package.publish = false` package): error
- `cargo publish -p publishable -p unpublishable`: error
- `cargo publish --workspace`: skips `package.publish = false

See https://github.com/rust-lang/cargo/issues/15006#issuecomment-2847660911
This commit is contained in:
Weihang Lo 2025-05-13 22:34:48 -04:00
parent 6f8136488e
commit 4f7494c4ec
No known key found for this signature in database
GPG Key ID: D7DBF189825E82E7
2 changed files with 72 additions and 19 deletions

View File

@ -96,7 +96,17 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
let (unpublishable, pkgs): (Vec<_>, Vec<_>) = pkgs
.into_iter()
.partition(|(pkg, _)| pkg.publish() == &Some(vec![]));
if !unpublishable.is_empty() {
// If `--workspace` is passed,
// the intent is more like "publish all publisable packages in this workspace",
// so skip `publish=false` packages.
let allow_unpublishable = multi_package_mode
&& match &opts.to_publish {
Packages::Default => ws.is_virtual(),
Packages::All(_) => true,
Packages::OptOut(_) => true,
Packages::Packages(_) => false,
};
if !unpublishable.is_empty() && !allow_unpublishable {
bail!(
"{} cannot be published.\n\
`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.",

View File

@ -4037,10 +4037,18 @@ fn one_unpublishable_package() {
p.cargo("publish -Zpackage-workspace")
.masquerade_as_nightly_cargo(&["package-workspace"])
.replace_crates_io(registry.index_url())
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] `main` cannot be published.
`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
[UPDATING] crates.io index
[PACKAGING] dep v0.1.0 ([ROOT]/foo/dep)
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
[VERIFYING] dep v0.1.0 ([ROOT]/foo/dep)
[COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0)
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
[UPLOADING] dep v0.1.0 ([ROOT]/foo/dep)
[UPLOADED] dep v0.1.0 to registry `crates-io`
[NOTE] waiting for `dep v0.1.0` to be available at registry `crates-io`.
You may press ctrl-c to skip waiting; the crate should be available shortly.
[PUBLISHED] dep v0.1.0 at registry `crates-io`
"#]])
.run();
@ -4109,10 +4117,18 @@ fn virtual_ws_with_multiple_unpublishable_package() {
p.cargo("publish -Zpackage-workspace")
.masquerade_as_nightly_cargo(&["package-workspace"])
.replace_crates_io(registry.index_url())
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] `dep`, `main` cannot be published.
`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
[UPDATING] crates.io index
[PACKAGING] publishable v0.1.0 ([ROOT]/foo/publishable)
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
[VERIFYING] publishable v0.1.0 ([ROOT]/foo/publishable)
[COMPILING] publishable v0.1.0 ([ROOT]/foo/target/package/publishable-0.1.0)
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
[UPLOADING] publishable v0.1.0 ([ROOT]/foo/publishable)
[UPLOADED] publishable v0.1.0 to registry `crates-io`
[NOTE] waiting for `publishable v0.1.0` to be available at registry `crates-io`.
You may press ctrl-c to skip waiting; the crate should be available shortly.
[PUBLISHED] publishable v0.1.0 at registry `crates-io`
"#]])
.run();
@ -4173,10 +4189,18 @@ fn workspace_flag_with_unpublishable_packages() {
p.cargo("publish --workspace -Zpackage-workspace")
.masquerade_as_nightly_cargo(&["package-workspace"])
.replace_crates_io(registry.index_url())
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] `non-publishable`, `cwd` cannot be published.
`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
[UPDATING] crates.io index
[PACKAGING] publishable v0.0.0 ([ROOT]/foo/publishable)
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
[VERIFYING] publishable v0.0.0 ([ROOT]/foo/publishable)
[COMPILING] publishable v0.0.0 ([ROOT]/foo/target/package/publishable-0.0.0)
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
[UPLOADING] publishable v0.0.0 ([ROOT]/foo/publishable)
[UPLOADED] publishable v0.0.0 to registry `crates-io`
[NOTE] waiting for `publishable v0.0.0` to be available at registry `crates-io`.
You may press ctrl-c to skip waiting; the crate should be available shortly.
[PUBLISHED] publishable v0.0.0 at registry `crates-io`
"#]])
.run();
@ -4233,8 +4257,15 @@ fn unpublishable_package_as_versioned_dev_dep() {
.replace_crates_io(registry.index_url())
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] `dep` cannot be published.
`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
[UPDATING] crates.io index
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
[UPDATING] crates.io index
[ERROR] failed to prepare local package for uploading
Caused by:
no matching package named `dep` found
location searched: crates.io index
required by package `main v0.0.1 ([ROOT]/foo/main)`
"#]])
.run();
@ -4244,8 +4275,15 @@ fn unpublishable_package_as_versioned_dev_dep() {
.replace_crates_io(registry.index_url())
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] `dep` cannot be published.
`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
[UPDATING] crates.io index
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
[UPDATING] crates.io index
[ERROR] failed to prepare local package for uploading
Caused by:
no matching package named `dep` found
location searched: crates.io index
required by package `main v0.0.1 ([ROOT]/foo/main)`
"#]])
.run();
@ -4255,8 +4293,15 @@ fn unpublishable_package_as_versioned_dev_dep() {
.replace_crates_io(registry.index_url())
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] `dep` cannot be published.
`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
[UPDATING] crates.io index
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
[UPDATING] crates.io index
[ERROR] failed to prepare local package for uploading
Caused by:
no matching package named `dep` found
location searched: crates.io index
required by package `main v0.0.1 ([ROOT]/foo/main)`
"#]])
.run();
@ -4307,10 +4352,8 @@ fn all_unpublishable_packages() {
p.cargo("publish --workspace -Zpackage-workspace")
.masquerade_as_nightly_cargo(&["package-workspace"])
.replace_crates_io(registry.index_url())
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] `non-publishable1`, `non-publishable2` cannot be published.
`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
[UPDATING] crates.io index
"#]])
.run();