use std::sync::{LazyLock, Mutex};
use bon::Builder;
use clap::Args;
use docker_credential::DockerCredential;
use log::trace;
use crate::{
constants::{
BB_PASSWORD, BB_REGISTRY, BB_USERNAME, CI_REGISTRY, CI_REGISTRY_PASSWORD, CI_REGISTRY_USER,
GITHUB_ACTIONS, GITHUB_ACTOR, GITHUB_TOKEN,
},
get_env_var, string,
};
static INIT: LazyLock<Mutex<bool>> = LazyLock::new(|| Mutex::new(false));
static INIT_CREDS: Mutex<CredentialsArgs> = Mutex::new(CredentialsArgs {
username: None,
password: None,
registry: None,
});
static ENV_CREDENTIALS: LazyLock<Option<Credentials>> = LazyLock::new(|| {
let (username, password, registry) = {
INIT_CREDS.lock().map_or((None, None, None), |mut creds| {
(
creds.username.take(),
creds.password.take(),
creds.registry.take(),
)
})
};
let registry = match (
registry,
get_env_var(CI_REGISTRY).ok(),
get_env_var(GITHUB_ACTIONS).ok(),
) {
(Some(registry), _, _) | (_, Some(registry), _) if !registry.is_empty() => registry,
(_, _, Some(_)) => string!("ghcr.io"),
_ => return None,
};
trace!("Registry: {registry:?}");
let (username, password) = match (
(username, password),
docker_credential::get_credential(®istry).ok(),
docker_credential::get_podman_credential(®istry).ok(),
(
get_env_var(CI_REGISTRY_USER).ok(),
get_env_var(CI_REGISTRY_PASSWORD).ok(),
),
(
get_env_var(GITHUB_ACTOR).ok(),
get_env_var(GITHUB_TOKEN).ok(),
),
) {
((Some(username), Some(password)), _, _, _, _)
| (_, Some(DockerCredential::UsernamePassword(username, password)), _, _, _)
| (_, _, Some(DockerCredential::UsernamePassword(username, password)), _, _)
| (_, _, _, (Some(username), Some(password)), _)
| (_, _, _, _, (Some(username), Some(password)))
if !username.is_empty() && !password.is_empty() =>
{
(username, password)
}
_ => return None,
};
trace!("Username: {username}");
Some(
Credentials::builder()
.registry(registry)
.username(username)
.password(password)
.build(),
)
});
#[derive(Debug, Default, Clone, Builder)]
pub struct Credentials {
pub registry: String,
pub username: String,
pub password: String,
}
impl Credentials {
pub fn init(args: CredentialsArgs) {
trace!("Credentials::init()");
let mut initialized = INIT.lock().expect("Must lock INIT");
if !*initialized {
let mut creds_lock = INIT_CREDS.lock().expect("Must lock USER_CREDS");
*creds_lock = args;
drop(creds_lock);
let _ = ENV_CREDENTIALS.as_ref();
*initialized = true;
}
}
pub fn get() -> Option<&'static Self> {
trace!("credentials::get()");
ENV_CREDENTIALS.as_ref()
}
}
#[derive(Debug, Default, Clone, Builder, Args)]
#[builder(on(String, into))]
pub struct CredentialsArgs {
#[arg(long, env = BB_REGISTRY)]
pub registry: Option<String>,
#[arg(short = 'U', long, env = BB_USERNAME, hide_env_values = true)]
pub username: Option<String>,
#[arg(short = 'P', long, env = BB_PASSWORD, hide_env_values = true)]
pub password: Option<String>,
}