diff --git a/src/bin/cargo/commands/publish.rs b/src/bin/cargo/commands/publish.rs index 572312eefae..c7747ae71ee 100644 --- a/src/bin/cargo/commands/publish.rs +++ b/src/bin/cargo/commands/publish.rs @@ -1,6 +1,8 @@ use crate::command_prelude::*; +use std::ops::Not; use cargo::ops::{self, PublishOpts}; +use cargo_credential::Secret; pub fn cli() -> Command { subcommand("publish") @@ -9,6 +11,7 @@ pub fn cli() -> Command { .arg_index("Registry index URL to upload the package to") .arg_registry("Registry to upload the package to") .arg(opt("token", "Token to use when uploading").value_name("TOKEN")) + .arg(flag("token-stdin", "Read token from stdin (unstable)").conflicts_with("token")) .arg(flag( "no-verify", "Don't verify the contents by building them", @@ -45,13 +48,30 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { .into()); } + let token_from_cmd = args.get_one::("token"); + let should_read_token_stdin = args.flag("token-stdin"); + if should_read_token_stdin { + gctx.cli_unstable().fail_if_stable_opt("--token-stdin", 0)?; + } + let token = token_from_cmd + .cloned() + .or_else(|| { + if should_read_token_stdin + && let token_from_stdin = cargo_credential::read_line().unwrap_or_default() + && token_from_stdin.is_empty().not() + { + Some(token_from_stdin) + } else { + None + } + }) + .map(Secret::from); + ops::publish( &ws, &PublishOpts { gctx, - token: args - .get_one::("token") - .map(|s| s.to_string().into()), + token, reg_or_index, verify: !args.flag("no-verify"), allow_dirty: args.flag("allow-dirty"), diff --git a/src/doc/man/cargo-publish.md b/src/doc/man/cargo-publish.md index 88ff22f3847..8e0b744a102 100644 --- a/src/doc/man/cargo-publish.md +++ b/src/doc/man/cargo-publish.md @@ -30,6 +30,7 @@ following steps: This command requires you to be authenticated with either the `--token` option or using {{man "cargo-login" 1}}. +Alternatively, you can set `--token-stdin` flag and pipe the _token_ value to stdin. See [the reference](../reference/publishing.html) for more details about packaging and publishing. @@ -46,6 +47,10 @@ Perform all checks without uploading. {{> options-token }} +{{#option "`--token-stdin`" }} +Read token from stdin (unstable). +{{/option}} + {{#option "`--no-verify`" }} Don't verify the contents by building them. {{/option}} diff --git a/src/doc/man/generated_txt/cargo-publish.txt b/src/doc/man/generated_txt/cargo-publish.txt index c303754b2eb..0858af3c445 100644 --- a/src/doc/man/generated_txt/cargo-publish.txt +++ b/src/doc/man/generated_txt/cargo-publish.txt @@ -26,7 +26,8 @@ DESCRIPTION manually. This timeout does not affect the upload. This command requires you to be authenticated with either the --token - option or using cargo-login(1). + option or using cargo-login(1). Alternatively, you can set --token-stdin + flag and pipe the token value to stdin. See the reference for more @@ -49,6 +50,9 @@ OPTIONS CARGO_REGISTRIES_NAME_TOKEN where NAME is the name of the registry in all capital letters. + --token-stdin + Read token from stdin (unstable). + --no-verify Don’t verify the contents by building them. diff --git a/src/doc/src/commands/cargo-publish.md b/src/doc/src/commands/cargo-publish.md index 6b6081d8276..16b72f07800 100644 --- a/src/doc/src/commands/cargo-publish.md +++ b/src/doc/src/commands/cargo-publish.md @@ -26,6 +26,7 @@ following steps: This command requires you to be authenticated with either the `--token` option or using [cargo-login(1)](cargo-login.html). +Alternatively, you can set `--token-stdin` flag and pipe the _token_ value to stdin. See [the reference](../reference/publishing.html) for more details about packaging and publishing. @@ -51,6 +52,10 @@ variables of the form CARGO_REGISTRIES_NAME_TOKEN where NAME< of the registry in all capital letters. +
--token-stdin
+
Read token from stdin (unstable).
+ +
--no-verify
Don’t verify the contents by building them.
diff --git a/src/etc/man/cargo-publish.1 b/src/etc/man/cargo-publish.1 index 6d9d3a4a8d1..43a1c6344ce 100644 --- a/src/etc/man/cargo-publish.1 +++ b/src/etc/man/cargo-publish.1 @@ -39,6 +39,7 @@ manually. This timeout does not affect the upload. .sp This command requires you to be authenticated with either the \fB\-\-token\fR option or using \fBcargo\-login\fR(1). +Alternatively, you can set \fB\-\-token\-stdin\fR flag and pipe the \fItoken\fR value to stdin. .sp See \fIthe reference\fR for more details about packaging and publishing. @@ -63,6 +64,11 @@ variables of the form \fBCARGO_REGISTRIES_NAME_TOKEN\fR where \fBNAME\fR is the of the registry in all capital letters. .RE .sp +\fB\-\-token\-stdin\fR +.RS 4 +Read token from stdin (unstable). +.RE +.sp \fB\-\-no\-verify\fR .RS 4 Don\[cq]t verify the contents by building them. diff --git a/tests/testsuite/cargo_publish/help/stdout.term.svg b/tests/testsuite/cargo_publish/help/stdout.term.svg index a455d6cc347..d81e448df16 100644 --- a/tests/testsuite/cargo_publish/help/stdout.term.svg +++ b/tests/testsuite/cargo_publish/help/stdout.term.svg @@ -38,75 +38,77 @@ --token <TOKEN> Token to use when uploading - --no-verify Don't verify the contents by building them + --token-stdin Read token from stdin (unstable) - --allow-dirty Allow dirty working directories to be packaged + --no-verify Don't verify the contents by building them - -v, --verbose... Use verbose output (-vv very verbose/build.rs output) + --allow-dirty Allow dirty working directories to be packaged - -q, --quiet Do not print cargo log messages + -v, --verbose... Use verbose output (-vv very verbose/build.rs output) - --color <WHEN> Coloring [possible values: auto, always, never] + -q, --quiet Do not print cargo log messages - --config <KEY=VALUE|PATH> Override a configuration value + --color <WHEN> Coloring [possible values: auto, always, never] - -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for + --config <KEY=VALUE|PATH> Override a configuration value - details + -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for - -h, --help Print help + details - + -h, --help Print help - Package Selection: + - -p, --package [<SPEC>] Package(s) to publish + Package Selection: - --workspace Publish all packages in the workspace + -p, --package [<SPEC>] Package(s) to publish - --exclude <SPEC> Don't publish specified packages + --workspace Publish all packages in the workspace - + --exclude <SPEC> Don't publish specified packages - Feature Selection: + - -F, --features <FEATURES> Space or comma separated list of features to activate + Feature Selection: - --all-features Activate all available features + -F, --features <FEATURES> Space or comma separated list of features to activate - --no-default-features Do not activate the `default` feature + --all-features Activate all available features - + --no-default-features Do not activate the `default` feature - Compilation Options: + - -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. + Compilation Options: - --keep-going Do not abort the build as soon as there is an error + -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. - --target [<TRIPLE>] Build for the target triple + --keep-going Do not abort the build as soon as there is an error - --target-dir <DIRECTORY> Directory for all generated artifacts + --target [<TRIPLE>] Build for the target triple - + --target-dir <DIRECTORY> Directory for all generated artifacts - Manifest Options: + - --manifest-path <PATH> Path to Cargo.toml + Manifest Options: - --lockfile-path <PATH> Path to Cargo.lock (unstable) + --manifest-path <PATH> Path to Cargo.toml - --locked Assert that `Cargo.lock` will remain unchanged + --lockfile-path <PATH> Path to Cargo.lock (unstable) - --offline Run without accessing the network + --locked Assert that `Cargo.lock` will remain unchanged - --frozen Equivalent to specifying both --locked and --offline + --offline Run without accessing the network - + --frozen Equivalent to specifying both --locked and --offline - Run `cargo help publish` for more detailed information. + - + Run `cargo help publish` for more detailed information. + + diff --git a/tests/testsuite/publish.rs b/tests/testsuite/publish.rs index 846b0689328..4870942bc36 100644 --- a/tests/testsuite/publish.rs +++ b/tests/testsuite/publish.rs @@ -4509,3 +4509,177 @@ Caused by: "#]]) .run(); } + +#[cargo_test] +fn publish_reads_token_from_stdin_with_flag() { + let registry = RegistryBuilder::new() + .alternative_named("i-need-token") + .http_api() + .token(registry::Token::Plaintext("TOKEN".to_string())) + .auth_required() + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2024" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify -Z unstable-options --token-stdin") + .masquerade_as_nightly_cargo(&["token-stdin"]) + .with_stdin("TOKEN") + .replace_crates_io(registry.index_url()) + .with_status(0) + .with_stderr_data(str![[r##" +[UPDATING] crates.io index +[WARNING] manifest has no description, license, license-file, documentation, homepage or repository. +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[PACKAGING] foo v0.0.1 ([ROOT]/foo) +[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[UPLOADING] foo v0.0.1 ([ROOT]/foo) +[UPLOADED] foo v0.0.1 to registry `crates-io` +[NOTE] waiting for foo v0.0.1 to be available at registry `crates-io` +[HELP] you may press ctrl-c to skip waiting; the crate should be available shortly +[PUBLISHED] foo v0.0.1 at registry `crates-io` + +"##]]) + .run(); +} + +#[cargo_test] +fn publish_does_not_read_token_from_stdin_without_flag() { + let registry = RegistryBuilder::new() + .alternative_named("i-need-token") + .http_api() + .token(registry::Token::Plaintext("TOKEN".to_string())) + .auth_required() + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2024" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify") + .masquerade_as_nightly_cargo(&["token-stdin"]) + .with_stdin("TOKEN") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr_data(str![[r##" +[UPDATING] crates.io index +[ERROR] no token found, please run `cargo login` +or use environment variable CARGO_REGISTRY_TOKEN + +"##]]) + .run(); +} + +#[cargo_test] +fn publish_works_without_token_specified_when_login_is_made_before() { + let registry = RegistryBuilder::new() + .alternative_named("i-need-token") + .http_api() + .token(registry::Token::Plaintext("TOKEN".to_string())) + .auth_required() + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2024" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("login -v") + .replace_crates_io(registry.index_url()) + .with_status(0) + .with_stdin("TOKEN") + .run(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(0) + .with_stderr_data(str![[r##" +[UPDATING] crates.io index +[WARNING] manifest has no description, license, license-file, documentation, homepage or repository. +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[PACKAGING] foo v0.0.1 ([ROOT]/foo) +[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[UPLOADING] foo v0.0.1 ([ROOT]/foo) +[UPLOADED] foo v0.0.1 to registry `crates-io` +[NOTE] waiting for foo v0.0.1 to be available at registry `crates-io` +[HELP] you may press ctrl-c to skip waiting; the crate should be available shortly +[PUBLISHED] foo v0.0.1 at registry `crates-io` + +"##]]) + .run(); +} + +#[cargo_test] +fn publish_fails_without_token_specified_when_login_failed_before() { + let registry = RegistryBuilder::new() + .alternative_named("i-need-token") + .http_api() + .token(registry::Token::Plaintext("TOKEN".to_string())) + .auth_required() + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2024" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("login -v") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr_data(str![[r##" +[UPDATING] crates.io index +[CREDENTIAL] cargo:token get crates-io +[CREDENTIAL] cargo:token login crates-io +please paste the token for crates-io below +[ERROR] credential provider `cargo:token` failed action `login` + +Caused by: + please provide a non-empty token + +"##]]) + .run(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_status(101) + .with_stderr_data(str![[r##" +[UPDATING] crates.io index +[ERROR] no token found, please run `cargo login` +or use environment variable CARGO_REGISTRY_TOKEN + +"##]]) + .run(); +}