rtb-vcs 0.7.0

Rust Tool Base — release-provider abstractions and backends (GitHub, GitLab, Bitbucket, Gitea, Codeberg, Direct).
Documentation
//! Auth glue: resolve a `CredentialRef` for git operations and
//! compose the subprocess env / `-c credential.helper` snippet that
//! ships the resolved token to `git` without exposing it via argv.
//!
//! Per v0.5 scope §3.3 / A2 resolution: no parallel `TokenSource`
//! trait. Auth-requiring `Repo` methods (`clone`, `fetch`, `push`)
//! take a `&CredentialRef` and resolve through this glue.

use rtb_credentials::{CredentialRef, Resolver};
use secrecy::{ExposeSecret, SecretString};

use super::RepoError;

/// Env-var name that the inline `credential.helper` script reads to
/// obtain the password. Stays process-private (not in argv).
pub const TOKEN_ENV: &str = "RTB_VCS_GIT_TOKEN";

/// Inline `credential.helper` config snippet. The script lives
/// entirely in the helper config value (not in argv as separate
/// tokens, not in any tempfile), and reads `RTB_VCS_GIT_TOKEN`
/// from process env to obtain the password.
///
/// Username is hardcoded as `x-access-token` — GitHub's PAT
/// convention, also accepted by GitLab and Gitea for token-based
/// auth. Tools that need different usernames per provider can wrap
/// `Repo::clone` / `fetch` themselves once a concrete consumer
/// surfaces the need.
pub const CREDENTIAL_HELPER: &str =
    "!f() { echo username=x-access-token; echo password=$RTB_VCS_GIT_TOKEN; }; f";

/// Resolve `cref` for git auth, mapping the underlying
/// `rtb_credentials::CredentialError` into [`RepoError::Auth`] so
/// callsites get a single error type.
pub async fn resolve_for_git(
    resolver: &Resolver,
    cref: &CredentialRef,
) -> Result<SecretString, RepoError> {
    resolver.resolve(cref).await.map_err(RepoError::Auth)
}

/// Convenience: construct a default-platform [`Resolver`] and
/// resolve `cref`. Same precedence chain as `rtb-credentials`'s
/// canonical resolver (env → keychain → literal → fallback-env).
///
/// Used by `Repo::clone` / `Repo::fetch` when the caller supplies
/// a `CredentialRef` via `CloneOptions::with_credential` /
/// `FetchOptions::with_credential`.
pub async fn resolve_default(cref: &CredentialRef) -> Result<SecretString, RepoError> {
    let resolver = Resolver::with_platform_default();
    resolve_for_git(&resolver, cref).await
}

/// Exposed-secret helper: returns the resolved token as a String
/// the caller can set as an env var on a subprocess. Caller is
/// responsible for never logging the returned value.
pub fn expose(secret: &SecretString) -> String {
    secret.expose_secret().to_string()
}