xbp 10.43.0

XBP is a zero-config build pack that can also interact with proxies, kafka, sockets, synthetic monitors.
Documentation
//! CLI OCI surface (`xbp oci`, `xbp config oci`).
//!
//! Registry math lives in `xbp-oci`; this module wires auth sources and clap.

mod commands;

pub use commands::{run_config_oci, run_oci};
pub use xbp_oci::{
    parse_image_ref, resolve_auth_from_candidates, DefaultOciPromoter, DefaultOciResolver,
    OciAuth, OciAuthRequest, OciClient, OciPromoter, OciRef, OciResolver,
};

use crate::config::SshConfig;
use crate::strategies::XbpConfig;
use std::env;

/// Build ordered (source, username?, token) candidates for a registry.
pub fn auth_candidates_for_registry(
    registry: &str,
    project: &XbpConfig,
) -> Vec<(String, Option<String>, String)> {
    let mut out = Vec::new();

    if let (Ok(user), Ok(token)) = (env::var("XBP_OCI_USERNAME"), env::var("XBP_OCI_TOKEN")) {
        if !token.trim().is_empty() {
            out.push(("env:XBP_OCI_*".into(), Some(user), token));
        }
    }

    let registry_cfg = project
        .oci
        .as_ref()
        .and_then(|o| o.registries.get(registry));
    let username = registry_cfg.and_then(|r| r.username.clone());

    let wants_github = registry.contains("ghcr.io")
        || registry_cfg
            .and_then(|r| r.token_source.as_deref())
            .map(|s| s.eq_ignore_ascii_case("github"))
            .unwrap_or(false);

    if wants_github {
        for key in ["XBP_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"] {
            if let Ok(token) = env::var(key) {
                if !token.trim().is_empty() {
                    out.push((
                        format!("env:{key}"),
                        username
                            .clone()
                            .or_else(|| Some("x-access-token".into())),
                        token,
                    ));
                }
            }
        }
        if let Ok(cfg) = SshConfig::load() {
            if let Some(token) = cfg.github_oauth2_key.filter(|t| !t.trim().is_empty()) {
                out.push((
                    "global:github".into(),
                    username.clone().or_else(|| Some("x-access-token".into())),
                    token,
                ));
            }
            if let Some(oci) = cfg.oci {
                if let Some(cred) = oci.registries.get(registry) {
                    if let Some(token) = cred.token.clone().filter(|t| !t.trim().is_empty()) {
                        out.push((
                            format!("global:oci:{registry}"),
                            cred.username.clone().or(username.clone()),
                            token,
                        ));
                    }
                }
            }
        }
    } else if let Ok(cfg) = SshConfig::load() {
        if let Some(oci) = cfg.oci {
            if let Some(cred) = oci.registries.get(registry) {
                if let Some(token) = cred.token.clone().filter(|t| !t.trim().is_empty()) {
                    out.push((
                        format!("global:oci:{registry}"),
                        cred.username.clone().or(username),
                        token,
                    ));
                }
            }
        }
    }

    out
}