diff --git a/src/bin/cargo/commands/publish.rs b/src/bin/cargo/commands/publish.rs index 572312eefae..eb4d7f8ebea 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::io::IsTerminal; use cargo::ops::{self, PublishOpts}; +use cargo_credential::Secret; pub fn cli() -> Command { subcommand("publish") @@ -8,7 +10,11 @@ pub fn cli() -> Command { .arg_dry_run("Perform all checks without uploading") .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( + opt("token", "Token to use when uploading") + .value_name("TOKEN") + .hide(true), + ) .arg(flag( "no-verify", "Don't verify the contents by building them", @@ -45,13 +51,29 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { .into()); } + let token: Option> = args.get_one::("token").cloned().map(Secret::from); + if token.is_some() { + let _ = gctx + .shell() + .warn("`cargo publish --token ` is deprecated in favor of reading `` from stdin"); + } + + let token = token.or_else(|| { + let mut token_from_stdin = None; + if !std::io::stdin().is_terminal() { + let token = cargo_credential::read_line().unwrap_or_default(); + if !token.is_empty() { + token_from_stdin = Some(token); + } + } + token_from_stdin.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/cargo/ops/registry/mod.rs b/src/cargo/ops/registry/mod.rs index 30e6ded68db..d21431abc06 100644 --- a/src/cargo/ops/registry/mod.rs +++ b/src/cargo/ops/registry/mod.rs @@ -130,7 +130,7 @@ fn registry<'gctx>( ) -> CargoResult<(Registry, RegistrySource<'gctx>)> { let is_index = reg_or_index.map(|v| v.is_index()).unwrap_or_default(); if is_index && token_required.is_some() && token_from_cmdline.is_none() { - bail!("command-line argument --index requires --token to be specified"); + bail!("command-line argument --index requires token to be provided via stdin"); } if let Some(token) = token_from_cmdline { auth::cache_token_from_commandline(gctx, &source_ids.original, token); diff --git a/src/doc/man/cargo-publish.md b/src/doc/man/cargo-publish.md index 88ff22f3847..270ac56d17f 100644 --- a/src/doc/man/cargo-publish.md +++ b/src/doc/man/cargo-publish.md @@ -28,8 +28,8 @@ following steps: and may timeout. In that case, you will need to check for completion manually. This timeout does not affect the upload. -This command requires you to be authenticated with either the `--token` option -or using {{man "cargo-login" 1}}. +This command requires you to be authenticated using +{{man "cargo-login" 1}} or by writing TOKEN value into stdin. See [the reference](../reference/publishing.html) for more details about packaging and publishing. @@ -44,8 +44,6 @@ packaging and publishing. Perform all checks without uploading. {{/option}} -{{> options-token }} - {{#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..911cdcef20d 100644 --- a/src/doc/man/generated_txt/cargo-publish.txt +++ b/src/doc/man/generated_txt/cargo-publish.txt @@ -25,8 +25,8 @@ DESCRIPTION and may timeout. In that case, you will need to check for completion 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). + This command requires you to be authenticated using cargo-login(1) or by + writing TOKEN value into stdin. See the reference for more @@ -37,18 +37,6 @@ OPTIONS --dry-run Perform all checks without uploading. - --token token - API token to use when authenticating. This overrides the token - stored in the credentials file (which is created by cargo-login(1)). - - Cargo config - environment variables can be used to override the tokens stored in - the credentials file. The token for crates.io may be specified with - the CARGO_REGISTRY_TOKEN environment variable. Tokens for other - registries may be specified with environment variables of the form - CARGO_REGISTRIES_NAME_TOKEN where NAME is the name of the registry - in all capital letters. - --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..9f03f1e407b 100644 --- a/src/doc/src/commands/cargo-publish.md +++ b/src/doc/src/commands/cargo-publish.md @@ -24,8 +24,8 @@ following steps: and may timeout. In that case, you will need to check for completion 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)](cargo-login.html). +This command requires you to be authenticated using +[cargo-login(1)](cargo-login.html) or by writing TOKEN value into stdin. See [the reference](../reference/publishing.html) for more details about packaging and publishing. @@ -40,17 +40,6 @@ packaging and publishing.
Perform all checks without uploading.
-
--token token
-
API token to use when authenticating. This overrides the token stored in -the credentials file (which is created by cargo-login(1)).

-

Cargo config environment variables can be -used to override the tokens stored in the credentials file. The token for -crates.io may be specified with the CARGO_REGISTRY_TOKEN environment -variable. Tokens for other registries may be specified with environment -variables of the form CARGO_REGISTRIES_NAME_TOKEN where NAME is the name -of the registry in all capital letters.

- -
--no-verify
Don’t verify the contents by building them.
diff --git a/src/doc/src/reference/config.md b/src/doc/src/reference/config.md index b90aeb92e74..9d3ef49192c 100644 --- a/src/doc/src/reference/config.md +++ b/src/doc/src/reference/config.md @@ -1089,7 +1089,7 @@ Specifies the authentication token for the given registry. This value should only appear in the [credentials](#credentials) file. This is used for registry commands like [`cargo publish`] that require authentication. -Can be overridden with the `--token` command-line option. +Can be overridden with the token value provided to stdin. #### `registries..credential-provider` * Type: string or array of path and arguments @@ -1165,7 +1165,7 @@ Specifies the authentication token for [crates.io]. This value should only appear in the [credentials](#credentials) file. This is used for registry commands like [`cargo publish`] that require authentication. -Can be overridden with the `--token` command-line option. +Can be overridden with the token value provided to stdin. #### `registry.global-credential-providers` * Type: array diff --git a/src/etc/man/cargo-publish.1 b/src/etc/man/cargo-publish.1 index 6d9d3a4a8d1..e20f3b20633 100644 --- a/src/etc/man/cargo-publish.1 +++ b/src/etc/man/cargo-publish.1 @@ -37,8 +37,8 @@ and may timeout. In that case, you will need to check for completion manually. This timeout does not affect the upload. .RE .sp -This command requires you to be authenticated with either the \fB\-\-token\fR option -or using \fBcargo\-login\fR(1). +This command requires you to be authenticated using +\fBcargo\-login\fR(1) or by writing TOKEN value into stdin. .sp See \fIthe reference\fR for more details about packaging and publishing. @@ -50,19 +50,6 @@ packaging and publishing. Perform all checks without uploading. .RE .sp -\fB\-\-token\fR \fItoken\fR -.RS 4 -API token to use when authenticating. This overrides the token stored in -the credentials file (which is created by \fBcargo\-login\fR(1)). -.sp -\fICargo config\fR environment variables can be -used to override the tokens stored in the credentials file. The token for -crates.io may be specified with the \fBCARGO_REGISTRY_TOKEN\fR environment -variable. Tokens for other registries may be specified with environment -variables of the form \fBCARGO_REGISTRIES_NAME_TOKEN\fR where \fBNAME\fR is the name -of the registry in all capital letters. -.RE -.sp \fB\-\-no\-verify\fR .RS 4 Don\[cq]t verify the contents by building them. diff --git a/tests/testsuite/alt_registry.rs b/tests/testsuite/alt_registry.rs index ab0ded2443a..501bf2fc180 100644 --- a/tests/testsuite/alt_registry.rs +++ b/tests/testsuite/alt_registry.rs @@ -300,11 +300,10 @@ Caused by: p.cargo("publish") .replace_crates_io(crates_io.index_url()) - .arg("--token") - .arg(crates_io.token()) .arg("--index") .arg(crates_io.index_url().as_str()) .with_status(101) + .with_stdin(crates_io.token()) .with_stderr_data(str![[r#" [UPDATING] crates.io index [ERROR] failed to verify manifest at `[ROOT]/foo/Cargo.toml` diff --git a/tests/testsuite/cargo_publish/help/stdout.term.svg b/tests/testsuite/cargo_publish/help/stdout.term.svg index a455d6cc347..c138e26c13a 100644 --- a/tests/testsuite/cargo_publish/help/stdout.term.svg +++ b/tests/testsuite/cargo_publish/help/stdout.term.svg @@ -36,77 +36,75 @@ --registry <REGISTRY> Registry to upload the package to - --token <TOKEN> Token to use when uploading + --no-verify Don't verify the contents by building them - --no-verify Don't verify the contents by building them + --allow-dirty Allow dirty working directories to be packaged - --allow-dirty Allow dirty working directories to be packaged + -v, --verbose... Use verbose output (-vv very verbose/build.rs output) - -v, --verbose... Use verbose output (-vv very verbose/build.rs output) + -q, --quiet Do not print cargo log messages - -q, --quiet Do not print cargo log messages + --color <WHEN> Coloring [possible values: auto, always, never] - --color <WHEN> Coloring [possible values: auto, always, never] + --config <KEY=VALUE|PATH> Override a configuration value - --config <KEY=VALUE|PATH> Override a configuration value + -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for - -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for + details - details + -h, --help Print help - -h, --help Print help + - + Package Selection: - Package Selection: + -p, --package [<SPEC>] Package(s) to publish - -p, --package [<SPEC>] Package(s) to publish + --workspace Publish all packages in the workspace - --workspace Publish all packages in the workspace + --exclude <SPEC> Don't publish specified packages - --exclude <SPEC> Don't publish specified packages + - + Feature Selection: - Feature Selection: + -F, --features <FEATURES> Space or comma separated list of features to activate - -F, --features <FEATURES> Space or comma separated list of features to activate + --all-features Activate all available features - --all-features Activate all available features + --no-default-features Do not activate the `default` feature - --no-default-features Do not activate the `default` feature + - + Compilation Options: - Compilation Options: + -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. - -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs. + --keep-going Do not abort the build as soon as there is an error - --keep-going Do not abort the build as soon as there is an error + --target [<TRIPLE>] Build for the target triple - --target [<TRIPLE>] Build for the target triple + --target-dir <DIRECTORY> Directory for all generated artifacts - --target-dir <DIRECTORY> Directory for all generated artifacts + - + Manifest Options: - Manifest Options: + --manifest-path <PATH> Path to Cargo.toml - --manifest-path <PATH> Path to Cargo.toml + --lockfile-path <PATH> Path to Cargo.lock (unstable) - --lockfile-path <PATH> Path to Cargo.lock (unstable) + --locked Assert that `Cargo.lock` will remain unchanged - --locked Assert that `Cargo.lock` will remain unchanged + --offline Run without accessing the network - --offline Run without accessing the network + --frozen Equivalent to specifying both --locked and --offline - --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..6eec87a25f2 100644 --- a/tests/testsuite/publish.rs +++ b/tests/testsuite/publish.rs @@ -180,11 +180,11 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for .run(); } -// Check that the `token` key works at the root instead of under a -// `[registry]` table. +/// Check that the `token` key works at the root instead of under a +/// `[registry]` table. #[cargo_test] fn simple_publish_with_http() { - let _reg = registry::RegistryBuilder::new() + let _reg = RegistryBuilder::new() .http_api() .token(registry::Token::Plaintext("sekrit".to_string())) .build(); @@ -207,6 +207,50 @@ fn simple_publish_with_http() { p.cargo("publish --no-verify --token sekrit --registry dummy-registry") .with_stderr_data(str![[r#" +[WARNING] `cargo publish --token ` is deprecated in favor of reading `` from stdin +[UPDATING] `dummy-registry` index +[WARNING] manifest has no 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 `dummy-registry` +[NOTE] waiting for foo v0.0.1 to be available at registry `dummy-registry` +[HELP] you may press ctrl-c to skip waiting; the crate should be available shortly +[PUBLISHED] foo v0.0.1 at registry `dummy-registry` + +"#]]) + .run(); +} + +/// Check that the `token` read from stdin works at the root instead of under a +/// `[registry]` table. +#[cargo_test] +fn simple_publish_with_http_2() { + let _reg = RegistryBuilder::new() + .http_api() + .token(registry::Token::Plaintext("sekrit".to_string())) + .build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2015" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --no-verify --registry dummy-registry") + .with_stdin("sekrit") + .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. @@ -327,7 +371,7 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for #[cargo_test] fn simple_with_index() { // `publish` generally requires a remote registry - let registry = registry::RegistryBuilder::new().http_api().build(); + let registry = RegistryBuilder::new().http_api().build(); let p = project() .file( @@ -346,10 +390,9 @@ fn simple_with_index() { .build(); p.cargo("publish --no-verify") - .arg("--token") - .arg(registry.token()) .arg("--index") .arg(registry.index_url().as_str()) + .with_stdin(registry.token()) .with_stderr_data(str![[r#" [UPDATING] `[ROOT]/registry` index [WARNING] manifest has no documentation, homepage or repository. @@ -1017,7 +1060,7 @@ fn publish_failed_with_index_and_only_allowed_registry() { .arg(registry.index_url().as_str()) .with_status(101) .with_stderr_data(str![[r#" -[ERROR] command-line argument --index requires --token to be specified +[ERROR] command-line argument --index requires token to be provided via stdin "#]]) .run(); @@ -2185,12 +2228,43 @@ fn index_requires_token() { .arg(registry.index_url().as_str()) .with_status(101) .with_stderr_data(str![[r#" -[ERROR] command-line argument --index requires --token to be specified +[ERROR] command-line argument --index requires token to be provided via stdin "#]]) .run(); } +#[cargo_test] +fn index_requires_token_provided_via_stdin() { + // Use local registry for faster test times since no publish will occur + let registry = registry::init(); + + let credentials = paths::home().join(".cargo/credentials.toml"); + fs::remove_file(&credentials).unwrap(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2015" + authors = [] + license = "MIT" + description = "foo" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify --index") + .arg(registry.index_url().as_str()) + .with_stdin("TOKEN") + .with_status(0) + .run(); +} + // publish with source replacement without --registry #[cargo_test] fn cratesio_source_replacement() { @@ -3619,6 +3693,47 @@ Caused by: .run(); } +#[cargo_test] +fn invalid_token_via_stdin() { + // Checks publish behavior with an invalid token. + let registry = RegistryBuilder::new().http_api().http_index().build(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2015" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("publish --no-verify") + .replace_crates_io(registry.index_url()) + .with_stdin("\x16") + .with_stderr_data(str![[r#" +[UPDATING] crates.io index +[PACKAGING] foo v0.0.1 ([ROOT]/foo) +[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[UPLOADING] foo v0.0.1 ([ROOT]/foo) +[ERROR] failed to publish foo v0.0.1 to registry at http://127.0.0.1:[..]/ + +Caused by: + token contains invalid characters. + Only printable ISO-8859-1 characters are allowed as it is sent in a HTTPS header. + +"#]]) + .with_status(101) + .run(); +} + #[cargo_test] fn versionless_package() { // Use local registry for faster test times since no publish will occur