feat: use native repository credentials #66

Manually merged
day01 merged 1 commits from feat/0.6-native-repository-token into develop 2026-08-30 16:22:34 +00:00
@@ -1,58 +1,90 @@
use syncode_runner_domain::{
ControlVendor, IdentifierError, JobAssignment, SecretName, SecretValue,
};
use syncode_runner_source::{RepositoryCredential, RepositoryUrl};

#[derive(Clone, Copy)]
enum CredentialSecret {
GiteaToken,
GithubToken,
}

pub fn repository_credential(
assignment: &JobAssignment,
vendor: ControlVendor,
origin: RepositoryUrl,
) -> Result<Option<RepositoryCredential>, IdentifierError> {
for candidate in CredentialSecret::for_vendor(vendor) {
if let Some(token) = secret(assignment, *candidate)? {
return Ok(Some(RepositoryCredential::new(origin, token.clone())));
}
}
Ok(None)
}

pub(crate) fn github_token(
assignment: &JobAssignment,
) -> Result<Option<&SecretValue>, IdentifierError> {
secret(assignment, CredentialSecret::GithubToken)
}

fn secret(
assignment: &JobAssignment,
candidate: CredentialSecret,
) -> Result<Option<&SecretValue>, IdentifierError> {
let name = candidate.as_ref().parse::<SecretName>()?;
Ok(assignment.secrets.get(&name))
}

impl CredentialSecret {
const fn for_vendor(vendor: ControlVendor) -> &'static [Self] {
match vendor {
ControlVendor::SynCode | ControlVendor::Gitea | ControlVendor::Forgejo => {
&[Self::GiteaToken, Self::GithubToken]
}
ControlVendor::GitHub => &[Self::GithubToken],
ControlVendor::GitLab => &[],
}
}
}

impl AsRef<str> for CredentialSecret {
fn as_ref(&self) -> &str {
match self {
Self::GiteaToken => "GITEA_TOKEN",
Self::GithubToken => "GITHUB_TOKEN",
}
}
}
use syncode_runner_domain::{
ControlVendor, IdentifierError, JobAssignment, SecretName, SecretValue,
};
use syncode_runner_source::{RepositoryCredential, RepositoryUrl};

#[derive(Clone, Copy)]
enum CredentialSecret {
SyncodeRepository,
Gitea,
Github,
}

pub fn repository_credential(
assignment: &JobAssignment,
vendor: ControlVendor,
origin: RepositoryUrl,
) -> Result<Option<RepositoryCredential>, IdentifierError> {
for candidate in CredentialSecret::for_vendor(vendor) {
if let Some(token) = secret(assignment, *candidate)? {
return Ok(Some(RepositoryCredential::new(origin, token.clone())));
}
}
Ok(None)
}

pub(crate) fn github_token(
assignment: &JobAssignment,
) -> Result<Option<&SecretValue>, IdentifierError> {
secret(assignment, CredentialSecret::Github)
}

fn secret(
assignment: &JobAssignment,
candidate: CredentialSecret,
) -> Result<Option<&SecretValue>, IdentifierError> {
let name = candidate.as_ref().parse::<SecretName>()?;
Ok(assignment.secrets.get(&name))
}

impl CredentialSecret {
const fn for_vendor(vendor: ControlVendor) -> &'static [Self] {
match vendor {
ControlVendor::SynCode => &[Self::SyncodeRepository, Self::Gitea, Self::Github],
ControlVendor::Gitea | ControlVendor::Forgejo => &[Self::Gitea, Self::Github],
ControlVendor::GitHub => &[Self::Github],
ControlVendor::GitLab => &[],
}
}
}

impl AsRef<str> for CredentialSecret {
fn as_ref(&self) -> &str {
match self {
Self::SyncodeRepository => "SYNCODE_REPOSITORY_TOKEN",
Self::Gitea => "GITEA_TOKEN",
Self::Github => "GITHUB_TOKEN",
}
}
}

#[cfg(test)]
mod tests {
use syncode_runner_domain::ControlVendor;

use super::CredentialSecret;

#[test]
fn syncode_prefers_the_native_repository_token() {
let names = CredentialSecret::for_vendor(ControlVendor::SynCode)
.iter()
.map(AsRef::as_ref)
.collect::<Vec<_>>();

assert_eq!(
names,
vec!["SYNCODE_REPOSITORY_TOKEN", "GITEA_TOKEN", "GITHUB_TOKEN"]
);
}

#[test]
fn forge_controls_do_not_request_syncode_credentials() {
for vendor in [ControlVendor::Gitea, ControlVendor::Forgejo] {
assert!(
CredentialSecret::for_vendor(vendor)
.iter()
.all(|candidate| candidate.as_ref() != "SYNCODE_REPOSITORY_TOKEN")
);
}
}
}