use chrono::{DateTime, Utc};
use crate::types::RunStatus;
pub fn credential_env_keys<I, K>(env_iter: I) -> Vec<String>
where
I: IntoIterator<Item = (K, K)>,
K: AsRef<str>,
{
const SUFFIXES: &[&str] = &["TOKEN", "KEY", "SECRET", "PASSWORD", "CREDENTIAL"];
const PREFIXES: &[&str] = &[
"HETZNER_",
"CLOUDFLARE_",
"CF_",
"AWS_",
"R2_",
"GITHUB_",
"GH_",
"HUGGINGFACE_",
"HF_",
"ANTHROPIC_",
"OPENAI_",
];
let mut keys: Vec<String> = env_iter
.into_iter()
.map(|(k, _)| k.as_ref().to_string())
.filter(|k| {
let upper = k.to_ascii_uppercase();
if PREFIXES.iter().any(|p| upper.starts_with(p)) {
return true;
}
upper
.rsplit('_')
.next()
.map(|tail| SUFFIXES.contains(&tail))
.unwrap_or(false)
})
.collect();
keys.sort();
keys.dedup();
keys
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn credential_env_keys_filters_by_suffix_and_prefix() {
let env = [
("PATH", "/usr/bin"),
("HOME", "/home/u"),
("KEYBOARD", "us"), ("HETZNER_S3_ACCESS_KEY", "xxx"), ("CF_API_TOKEN", "xxx"), ("MY_API_KEY", "xxx"), ("HF_TOKEN", "xxx"), ("DATABASE_PASSWORD", "xxx"), ("RANDOM_VAR", "xxx"),
];
let keys = credential_env_keys(env.iter().map(|(k, v)| (*k, *v)));
assert_eq!(
keys,
vec![
"CF_API_TOKEN".to_string(),
"DATABASE_PASSWORD".to_string(),
"HETZNER_S3_ACCESS_KEY".to_string(),
"HF_TOKEN".to_string(),
"MY_API_KEY".to_string(),
]
);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputStream {
Stdout,
Stderr,
}
#[derive(Debug, Clone)]
pub enum QedEvent {
RunQueued { key: String, at: DateTime<Utc> },
RunStarted {
total_steps: usize,
at: DateTime<Utc>,
},
StepStarted {
index: usize,
name: String,
argv: Vec<String>,
env_keys: Vec<String>,
expect_slow: bool,
at: DateTime<Utc>,
},
StepRemoteDispatched {
index: usize,
name: String,
forge_id: String,
at: DateTime<Utc>,
},
StepAwaitingHuman {
index: usize,
name: String,
form_id: Option<String>,
advance: Option<String>,
audience: crate::types::ManualAudience,
prompt: String,
terminal: Vec<String>,
at: DateTime<Utc>,
},
StepOutput {
index: usize,
name: String,
stream: OutputStream,
line: String,
},
StepFinished {
index: usize,
name: String,
status: RunStatus,
msg: Option<String>,
at: DateTime<Utc>,
},
RunFinished {
status: RunStatus,
at: DateTime<Utc>,
},
SubPipelineStarted {
index: usize,
name: String,
target: String,
child_run_id: String,
at: DateTime<Utc>,
},
SubPipelineFinished {
index: usize,
name: String,
child_run_id: String,
status: RunStatus,
at: DateTime<Utc>,
},
GhaJobStarted {
index: usize,
name: String,
job_id: String,
matrix_index: Option<usize>,
job_key: String,
total_steps: usize,
at: DateTime<Utc>,
},
GhaStepStarted {
index: usize,
name: String,
job_key: String,
step_index: usize,
step_id: Option<String>,
step_name: Option<String>,
action_kind: String,
at: DateTime<Utc>,
},
GhaStepOutput {
index: usize,
name: String,
job_key: String,
step_index: usize,
stream: OutputStream,
line: String,
},
GhaStepFinished {
index: usize,
name: String,
job_key: String,
step_index: usize,
conclusion: String,
msg: Option<String>,
at: DateTime<Utc>,
},
GhaJobFinished {
index: usize,
name: String,
job_key: String,
result: String,
at: DateTime<Utc>,
},
}