Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use RATTLER_AUTH_FILE and use proper reqwest clients in search #994

Merged
merged 9 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
31 changes: 31 additions & 0 deletions docs/advanced/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,34 @@ On Linux, one can use `GNOME Keyring` (or just Keyring) to access credentials th

If you run on a server with none of the aforementioned keychains available, then pixi falls back to store the credentials in an _insecure_ JSON file.
This JSON file is located at `~/.rattler/credentials.json` and contains the credentials.

## Override the authentication storage

You can use the `RATTLER_AUTH_FILE` environment variable to override the default location of the credentials file. When this environment variable is set, it provides the only source of authentication data that is used by pixi.

E.g.

```bash
export RATTLER_AUTH_FILE=$HOME/credentials.json
```

The JSON should follow the following format:

```json
{
"*.prefix.dev": {
"BearerToken": "your_token"
},
"otherhost.com": {
"BasicHttp": {
"username": "your_username",
"password": "your_password"
}
},
"conda.anaconda.org": {
"CondaToken": "your_token"
}
}
```

Note: if you use a wildcard in the host, any subdomain will match (e.g. `*.prefix.dev` also matches `repo.prefix.dev`).
14 changes: 8 additions & 6 deletions src/cli/global/common.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
use std::path::PathBuf;
use std::sync::Arc;

use indexmap::IndexMap;
use miette::IntoDiagnostic;
use rattler_conda_types::{
Channel, ChannelConfig, MatchSpec, PackageName, Platform, PrefixRecord, RepoDataRecord,
};
use rattler_networking::AuthenticationMiddleware;
use rattler_repodata_gateway::sparse::SparseRepoData;
use rattler_solve::{resolvo, SolverImpl, SolverTask};
use reqwest_middleware::ClientWithMiddleware;

use crate::{config::home_path, prefix::Prefix, repodata};
use crate::{
config::{home_path, Config},
prefix::Prefix,
repodata,
utils::reqwest::build_reqwest_clients,
};

/// Global binaries directory, default to `$HOME/.pixi/bin`
pub struct BinDir(pub PathBuf);
Expand Down Expand Up @@ -173,13 +176,12 @@ pub fn load_package_records(
/// The network client and the fetched sparse repodata
pub(super) async fn get_client_and_sparse_repodata(
channels: impl IntoIterator<Item = &'_ Channel>,
config: &Config,
) -> miette::Result<(
ClientWithMiddleware,
IndexMap<(Channel, Platform), SparseRepoData>,
)> {
let authenticated_client = reqwest_middleware::ClientBuilder::new(reqwest::Client::new())
.with_arc(Arc::new(AuthenticationMiddleware::default()))
.build();
let authenticated_client = build_reqwest_clients(Some(config)).1;
let platform_sparse_repodata =
repodata::fetch_sparse_repodata(channels, [Platform::current()], &authenticated_client)
.await?;
Expand Down
3 changes: 2 additions & 1 deletion src/cli/global/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ pub async fn execute(args: Args) -> miette::Result<()> {
.into_diagnostic()?;

// Fetch sparse repodata
let (authenticated_client, sparse_repodata) = get_client_and_sparse_repodata(&channels).await?;
let (authenticated_client, sparse_repodata) =
get_client_and_sparse_repodata(&channels, &config).await?;

// Install the package(s)
let mut executables = vec![];
Expand Down
3 changes: 2 additions & 1 deletion src/cli/global/upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ pub async fn execute(args: Args) -> miette::Result<()> {
channels = channels.into_iter().unique().collect::<Vec<_>>();

// Fetch sparse repodata
let (authenticated_client, sparse_repodata) = get_client_and_sparse_repodata(&channels).await?;
let (authenticated_client, sparse_repodata) =
get_client_and_sparse_repodata(&channels, &config).await?;

let records = load_package_records(package_matchspec, &sparse_repodata)?;
let package_record = records
Expand Down
3 changes: 2 additions & 1 deletion src/cli/global/upgrade_all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ pub async fn execute(args: Args) -> miette::Result<()> {
channels = channels.into_iter().unique().collect::<Vec<_>>();

// Fetch sparse repodata
let (authenticated_client, sparse_repodata) = get_client_and_sparse_repodata(&channels).await?;
let (authenticated_client, sparse_repodata) =
get_client_and_sparse_repodata(&channels, &config).await?;

let mut upgraded = false;
for package_name in packages.iter() {
Expand Down
16 changes: 9 additions & 7 deletions src/cli/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ use indexmap::IndexMap;
use itertools::Itertools;
use miette::IntoDiagnostic;
use rattler_conda_types::{Channel, PackageName, Platform, RepoDataRecord};
use rattler_networking::AuthenticationMiddleware;
use rattler_repodata_gateway::sparse::SparseRepoData;
use regex::Regex;

use strsim::jaro;
use tokio::task::spawn_blocking;

use crate::config::Config;
use crate::utils::reqwest::build_reqwest_clients;
use crate::{progress::await_in_progress, repodata::fetch_sparse_repodata, Project};

/// Search a package, output will list the latest version of package
Expand Down Expand Up @@ -137,12 +137,14 @@ pub async fn execute(args: Args) -> miette::Result<()> {

let package_name_filter = args.package;

let authenticated_client = reqwest_middleware::ClientBuilder::new(reqwest::Client::new())
.with_arc(Arc::new(AuthenticationMiddleware::default()))
.build();
let repo_data = Arc::new(
fetch_sparse_repodata(channels.iter(), [args.platform], &authenticated_client).await?,
);
let client = if let Some(project) = project.as_ref() {
project.authenticated_client().clone()
} else {
build_reqwest_clients(None).1
};

let repo_data =
Arc::new(fetch_sparse_repodata(channels.iter(), [args.platform], &client).await?);

// When package name filter contains * (wildcard), it will search and display a list of packages matching this filter
if package_name_filter.contains('*') {
Expand Down
24 changes: 21 additions & 3 deletions src/utils/reqwest.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::{sync::Arc, time::Duration};
use std::{path::PathBuf, sync::Arc, time::Duration};

use rattler_networking::{retry_policies::ExponentialBackoff, AuthenticationMiddleware};
use rattler_networking::{
authentication_storage, retry_policies::ExponentialBackoff, AuthenticationMiddleware,
AuthenticationStorage,
};
use reqwest::Client;
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};

Expand All @@ -12,6 +15,21 @@ pub fn default_retry_policy() -> ExponentialBackoff {
ExponentialBackoff::builder().build_with_max_retries(3)
}

fn auth_middleware() -> AuthenticationMiddleware {
if let Ok(auth_file) = std::env::var("RATTLER_AUTH_FILE") {
tracing::info!("Loading authentication from file: {:?}", auth_file);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this file location also be part of the global config?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it so! :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you document it aswell


let mut store = AuthenticationStorage::new();
store.add_backend(Arc::from(
authentication_storage::backends::file::FileStorage::new(PathBuf::from(&auth_file)),
));
ruben-arts marked this conversation as resolved.
Show resolved Hide resolved

return AuthenticationMiddleware::new(store);
}

AuthenticationMiddleware::default()
}

pub(crate) fn build_reqwest_clients(config: Option<&Config>) -> (Client, ClientWithMiddleware) {
static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);

Expand All @@ -36,7 +54,7 @@ pub(crate) fn build_reqwest_clients(config: Option<&Config>) -> (Client, ClientW
.expect("failed to create reqwest Client");

let authenticated_client = ClientBuilder::new(client.clone())
.with_arc(Arc::new(AuthenticationMiddleware::default()))
.with_arc(Arc::new(auth_middleware()))
.build();

(client, authenticated_client)
Expand Down
Loading