Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2fed864
Enhance trusted publishing support for GitLab CI
harsh-ps-2003 Aug 29, 2025
de660d5
test nit
harsh-ps-2003 Aug 29, 2025
7fc20b8
Refactor GitLab trusted publishing tests and update OIDC token handling
harsh-ps-2003 Aug 29, 2025
71c6e3e
Add audience endpoint mocks for PyPI and TestPyPI in GitLab trusted p…
harsh-ps-2003 Aug 29, 2025
50864a3
fmt nit
harsh-ps-2003 Aug 29, 2025
2e990d5
- Updated the `get_token` function to use a normalized environment ke…
harsh-ps-2003 Aug 29, 2025
612bed1
error nit
harsh-ps-2003 Aug 29, 2025
ad50d9e
Prefer HTTPS for OIDC minting; allow HTTP only in test builds
harsh-ps-2003 Aug 30, 2025
9ff70c1
test refactor
harsh-ps-2003 Aug 30, 2025
527108d
nit
harsh-ps-2003 Aug 30, 2025
655c7b7
switch to ambient_id for OIDC token discovery
woodruffw Sep 9, 2025
699b54d
Merge branch 'main' into gitlab
woodruffw Sep 9, 2025
cc12bcf
fix clippy findings
woodruffw Sep 9, 2025
be876cd
fix some tests
woodruffw Sep 9, 2025
170fe8e
fix remaining tests
woodruffw Sep 9, 2025
167062d
bump docs, run full clippy
woodruffw Sep 9, 2025
1c95e95
bump ambient_id
woodruffw Sep 9, 2025
4ee48ae
specialize GitHub permissions error case rendering
woodruffw Sep 10, 2025
ea4d25d
push IdToken further into params
woodruffw Sep 10, 2025
2a33439
improve suggestion on NoToken
woodruffw Sep 10, 2025
d272484
bump snapshot
woodruffw Sep 10, 2025
c800f63
Merge remote-tracking branch 'origin/main' into gitlab
woodruffw Sep 10, 2025
f1dc54d
docs: update trusted publishing docs to include GitLab CI/CD
woodruffw Sep 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ uv-virtualenv = { path = "crates/uv-virtualenv" }
uv-warnings = { path = "crates/uv-warnings" }
uv-workspace = { path = "crates/uv-workspace" }

ambient-id = { version = "0.0.4"}
anstream = { version = "0.6.15" }
anyhow = { version = "1.0.89" }
arcstr = { version = "1.2.0" }
Expand Down
8 changes: 7 additions & 1 deletion crates/uv-publish/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ uv-redacted = { workspace = true }
uv-static = { workspace = true }
uv-warnings = { workspace = true }

ambient-id = { workspace = true }
astral-tokio-tar = { workspace = true }
async-compression = { workspace = true }
base64 = { workspace = true }
Expand All @@ -42,12 +43,17 @@ serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
tokio-util = { workspace = true , features = ["io"] }
tokio-util = { workspace = true, features = ["io"] }
tracing = { workspace = true }
url = { workspace = true }

[dev-dependencies]
insta = { workspace = true }

[features]
# Test only feature to enable non-HTTPS URL handling
# in unit tests.
test = []

[lints]
workspace = true
52 changes: 22 additions & 30 deletions crates/uv-publish/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod trusted_publishing;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::{Duration, SystemTime};
use std::{env, fmt, io};
use std::{fmt, io};

use fs_err::tokio::File;
use futures::TryStreamExt;
Expand All @@ -21,7 +21,6 @@ use tokio::io::{AsyncReadExt, BufReader};
use tokio::sync::Semaphore;
use tokio_util::io::ReaderStream;
use tracing::{Level, debug, enabled, trace, warn};
use trusted_publishing::TrustedPublishingToken;
use url::Url;

use uv_auth::{Credentials, PyxTokenStore};
Expand All @@ -38,10 +37,9 @@ use uv_fs::{ProgressReader, Simplified};
use uv_metadata::read_metadata_async_seek;
use uv_pypi_types::{HashAlgorithm, HashDigest, Metadata23, MetadataError};
use uv_redacted::DisplaySafeUrl;
use uv_static::EnvVars;
use uv_warnings::{warn_user, warn_user_once};
use uv_warnings::warn_user;

use crate::trusted_publishing::TrustedPublishingError;
use crate::trusted_publishing::{TrustedPublishingError, TrustedPublishingToken};

#[derive(Error, Debug)]
pub enum PublishError {
Expand Down Expand Up @@ -324,26 +322,20 @@ pub async fn check_trusted_publishing(
{
return Ok(TrustedPublishResult::Skipped);
}
// If we aren't in GitHub Actions, we can't use trusted publishing.
if env::var(EnvVars::GITHUB_ACTIONS) != Ok("true".to_string()) {
return Ok(TrustedPublishResult::Skipped);
}
// We could check for credentials from the keyring or netrc the auth middleware first, but
// given that we are in GitHub Actions we check for trusted publishing first.
debug!(
"Running on GitHub Actions without explicit credentials, checking for trusted publishing"
);

debug!("Attempting to get a token for trusted publishing");
// Attempt to get a token for trusted publishing.
match trusted_publishing::get_token(registry, client.for_host(registry).raw_client())
.await
{
Ok(token) => Ok(TrustedPublishResult::Configured(token)),
Err(err) => {
// TODO(konsti): It would be useful if we could differentiate between actual errors
// such as connection errors and warn for them while ignoring errors from trusted
// publishing not being configured.
debug!("Could not obtain trusted publishing credentials, skipping: {err}");
Ok(TrustedPublishResult::Ignored(err))
}
// Success: we have a token for trusted publishing.
Ok(Some(token)) => Ok(TrustedPublishResult::Configured(token)),
// Failed to discover an ambient OIDC token.
Ok(None) => Ok(TrustedPublishResult::Ignored(
TrustedPublishingError::NoToken,
)),
// Hard failure during OIDC discovery or token exchange.
Err(err) => Ok(TrustedPublishResult::Ignored(err)),
}
}
TrustedPublishing::Always => {
Expand All @@ -363,15 +355,15 @@ pub async fn check_trusted_publishing(
return Err(PublishError::MixedCredentials(conflicts.join(" and ")));
}

if env::var(EnvVars::GITHUB_ACTIONS) != Ok("true".to_string()) {
warn_user_once!(
"Trusted publishing was requested, but you're not in GitHub Actions."
);
}

let token =
let Some(token) =
trusted_publishing::get_token(registry, client.for_host(registry).raw_client())
.await?;
.await?
else {
return Err(PublishError::TrustedPublishing(
TrustedPublishingError::NoToken,
));
};

Ok(TrustedPublishResult::Configured(token))
}
TrustedPublishing::Never => Ok(TrustedPublishResult::Skipped),
Expand Down
Loading
Loading