Allow providing the uv auth login password or token via stdin (#15642)

This commit is contained in:
Zanie Blue 2025-09-02 16:59:58 -05:00 committed by GitHub
parent 63b93a1db0
commit f9e98d1fb6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 105 additions and 3 deletions

View file

@ -5567,12 +5567,16 @@ pub struct AuthLoginArgs {
pub username: Option<String>, pub username: Option<String>,
/// The password to use for the service. /// The password to use for the service.
///
/// Use `-` to read the password from stdin.
#[arg(long, conflicts_with = "token")] #[arg(long, conflicts_with = "token")]
pub password: Option<String>, pub password: Option<String>,
/// The token to use for the service. /// The token to use for the service.
/// ///
/// The username will be set to `__token__`. /// The username will be set to `__token__`.
///
/// Use `-` to read the token from stdin.
#[arg(long, short, conflicts_with = "username", conflicts_with = "password")] #[arg(long, short, conflicts_with = "username", conflicts_with = "password")]
pub token: Option<String>, pub token: Option<String>,

View file

@ -87,6 +87,11 @@ pub(crate) async fn login(
(None, Some(_), Some(_)) => { (None, Some(_), Some(_)) => {
bail!("Cannot include a password in the URL when using `--token`") bail!("Cannot include a password in the URL when using `--token`")
} }
(None, None, Some(value)) | (Some(value), None, None) if value == "-" => {
let mut input = String::new();
std::io::stdin().read_line(&mut input)?;
input.trim().to_string()
}
(Some(cli), None, None) => cli, (Some(cli), None, None) => cli,
(None, Some(url), None) => url.to_string(), (None, Some(url), None) => url.to_string(),
(None, None, Some(token)) => token, (None, None, Some(token)) => token,

View file

@ -1,7 +1,5 @@
#[cfg(feature = "native-auth")]
use anyhow::Result; use anyhow::Result;
use assert_cmd::assert::OutputAssertExt; use assert_cmd::assert::OutputAssertExt;
#[cfg(feature = "native-auth")]
use assert_fs::{fixture::PathChild, prelude::FileWriteStr}; use assert_fs::{fixture::PathChild, prelude::FileWriteStr};
#[cfg(feature = "native-auth")] #[cfg(feature = "native-auth")]
use uv_static::EnvVars; use uv_static::EnvVars;
@ -778,6 +776,88 @@ fn login_text_store() {
); );
} }
#[test]
#[allow(clippy::disallowed_types)]
fn login_password_stdin() -> Result<()> {
let context = TestContext::new_with_versions(&[]);
// Create a temporary file with the password
let password_file = context.temp_dir.child("password.txt");
password_file.write_str("secret-password")?;
// Login with password from stdin
uv_snapshot!(context.auth_login()
.arg("https://example.com/simple")
.arg("--username")
.arg("testuser")
.arg("--password")
.arg("-")
.stdin(std::fs::File::open(password_file)?), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Stored credentials for testuser@https://example.com/
"
);
// Verify the credentials work by retrieving the token
uv_snapshot!(context.auth_token()
.arg("https://example.com/simple")
.arg("--username")
.arg("testuser"), @r"
success: true
exit_code: 0
----- stdout -----
secret-password
----- stderr -----
"
);
Ok(())
}
#[test]
#[allow(clippy::disallowed_types)]
fn login_token_stdin() -> Result<()> {
let context = TestContext::new_with_versions(&[]);
// Create a temporary file with the token
let token_file = context.temp_dir.child("token.txt");
token_file.write_str("secret-token")?;
// Login with token from stdin
uv_snapshot!(context.auth_login()
.arg("https://example.com/simple")
.arg("--token")
.arg("-")
.stdin(std::fs::File::open(token_file)?), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Stored credentials for https://example.com/
"
);
// Verify the credentials work by retrieving the token
uv_snapshot!(context.auth_token()
.arg("https://example.com/simple"), @r"
success: true
exit_code: 0
----- stdout -----
secret-token
----- stderr -----
"
);
Ok(())
}
#[test] #[test]
fn token_text_store() { fn token_text_store() {
let context = TestContext::new_with_versions(&[]); let context = TestContext::new_with_versions(&[]);

View file

@ -15,6 +15,17 @@ This will prompt for the credentials.
The credentials can also be provided using the `--username` and `--password` options, or the The credentials can also be provided using the `--username` and `--password` options, or the
`--token` option for services which use a `__token__` or arbitrary username. `--token` option for services which use a `__token__` or arbitrary username.
!!! note
We recommend providing the secret via stdin. Use `-` to indicate the value should be read from
stdin, e.g., for `--password`:
```console
$ echo 'my-password' | uv auth login example.com --password -
```
The same pattern can be used with `--token`.
Once credentials are added, uv will use them for packaging operations that require fetching content Once credentials are added, uv will use them for packaging operations that require fetching content
from the given service. At this time, only HTTPS Basic authentication is supported. The credentials from the given service. At this time, only HTTPS Basic authentication is supported. The credentials
will not yet be used for Git requests. will not yet be used for Git requests.

View file

@ -110,7 +110,8 @@ uv auth login [OPTIONS] <SERVICE>
<p>May also be set with the <code>UV_NO_PROGRESS</code> environment variable.</p></dd><dt id="uv-auth-login--no-python-downloads"><a href="#uv-auth-login--no-python-downloads"><code>--no-python-downloads</code></a></dt><dd><p>Disable automatic downloads of Python.</p> <p>May also be set with the <code>UV_NO_PROGRESS</code> environment variable.</p></dd><dt id="uv-auth-login--no-python-downloads"><a href="#uv-auth-login--no-python-downloads"><code>--no-python-downloads</code></a></dt><dd><p>Disable automatic downloads of Python.</p>
</dd><dt id="uv-auth-login--offline"><a href="#uv-auth-login--offline"><code>--offline</code></a></dt><dd><p>Disable network access.</p> </dd><dt id="uv-auth-login--offline"><a href="#uv-auth-login--offline"><code>--offline</code></a></dt><dd><p>Disable network access.</p>
<p>When disabled, uv will only use locally cached data and locally available files.</p> <p>When disabled, uv will only use locally cached data and locally available files.</p>
<p>May also be set with the <code>UV_OFFLINE</code> environment variable.</p></dd><dt id="uv-auth-login--password"><a href="#uv-auth-login--password"><code>--password</code></a> <i>password</i></dt><dd><p>The password to use for the service</p> <p>May also be set with the <code>UV_OFFLINE</code> environment variable.</p></dd><dt id="uv-auth-login--password"><a href="#uv-auth-login--password"><code>--password</code></a> <i>password</i></dt><dd><p>The password to use for the service.</p>
<p>Use <code>-</code> to read the password from stdin.</p>
</dd><dt id="uv-auth-login--project"><a href="#uv-auth-login--project"><code>--project</code></a> <i>project</i></dt><dd><p>Run the command within the given project directory.</p> </dd><dt id="uv-auth-login--project"><a href="#uv-auth-login--project"><code>--project</code></a> <i>project</i></dt><dd><p>Run the command within the given project directory.</p>
<p>All <code>pyproject.toml</code>, <code>uv.toml</code>, and <code>.python-version</code> files will be discovered by walking up the directory tree from the project root, as will the project's virtual environment (<code>.venv</code>).</p> <p>All <code>pyproject.toml</code>, <code>uv.toml</code>, and <code>.python-version</code> files will be discovered by walking up the directory tree from the project root, as will the project's virtual environment (<code>.venv</code>).</p>
<p>Other command-line arguments (such as relative paths) will be resolved relative to the current working directory.</p> <p>Other command-line arguments (such as relative paths) will be resolved relative to the current working directory.</p>
@ -120,6 +121,7 @@ uv auth login [OPTIONS] <SERVICE>
<p>Repeating this option, e.g., <code>-qq</code>, will enable a silent mode in which uv will write no output to stdout.</p> <p>Repeating this option, e.g., <code>-qq</code>, will enable a silent mode in which uv will write no output to stdout.</p>
</dd><dt id="uv-auth-login--token"><a href="#uv-auth-login--token"><code>--token</code></a>, <code>-t</code> <i>token</i></dt><dd><p>The token to use for the service.</p> </dd><dt id="uv-auth-login--token"><a href="#uv-auth-login--token"><code>--token</code></a>, <code>-t</code> <i>token</i></dt><dd><p>The token to use for the service.</p>
<p>The username will be set to <code>__token__</code>.</p> <p>The username will be set to <code>__token__</code>.</p>
<p>Use <code>-</code> to read the token from stdin.</p>
</dd><dt id="uv-auth-login--username"><a href="#uv-auth-login--username"><code>--username</code></a>, <code>-u</code> <i>username</i></dt><dd><p>The username to use for the service</p> </dd><dt id="uv-auth-login--username"><a href="#uv-auth-login--username"><code>--username</code></a>, <code>-u</code> <i>username</i></dt><dd><p>The username to use for the service</p>
</dd><dt id="uv-auth-login--verbose"><a href="#uv-auth-login--verbose"><code>--verbose</code></a>, <code>-v</code></dt><dd><p>Use verbose output.</p> </dd><dt id="uv-auth-login--verbose"><a href="#uv-auth-login--verbose"><code>--verbose</code></a>, <code>-v</code></dt><dd><p>Use verbose output.</p>
<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (<a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives">https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives</a>)</p> <p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (<a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives">https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives</a>)</p>