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