use serde_yaml::{Mapping, Value};
use super::reader;
pub const UBICLOUD_LABEL: &str = "ubicloud-standard-4";
pub const HOSTED_LABEL: &str = "ubuntu-latest";
const FORK_CONDITION: &str = "github.event.pull_request.head.repo.fork";
#[derive(Clone, Copy, Debug)]
pub enum Origin {
NoPullRequest,
SameRepository,
Fork,
}
fn unquoted(term: &str) -> Option<&str> { term.trim().strip_prefix('\'')?.strip_suffix('\'') }
pub fn selected_runner(runs_on: &str, origin: Origin) -> Option<String> {
let body = runs_on
.trim()
.strip_prefix("${{")?
.strip_suffix("}}")?
.trim();
let (condition, arms) = body.split_once(" && ")?;
let (fork_arm, other_arm) = arms.split_once(" || ")?;
if condition.trim() != FORK_CONDITION {
return None;
}
let chosen = match origin {
Origin::Fork => fork_arm,
Origin::NoPullRequest | Origin::SameRepository => other_arm,
};
unquoted(chosen).map(str::to_owned)
}
pub fn placement_faults(runs_on: &str) -> Vec<String> {
[
(Origin::NoPullRequest, UBICLOUD_LABEL),
(Origin::SameRepository, UBICLOUD_LABEL),
(Origin::Fork, HOSTED_LABEL),
]
.into_iter()
.filter_map(|(origin, wanted)| {
let chosen = selected_runner(runs_on, origin);
(chosen.as_deref() != Some(wanted))
.then(|| format!("{origin:?} selects {chosen:?}, wanted {wanted:?}"))
})
.collect()
}
fn runs_on_text(job: &Mapping) -> Option<String> {
match reader::get(job, "runs-on")? {
Value::String(text) => Some(text.clone()),
other => serde_yaml::to_string(other).ok(),
}
}
fn names_ubicloud(job: &Mapping) -> bool {
runs_on_text(job).is_some_and(|text| text.contains("ubicloud"))
}
#[derive(Debug, PartialEq, Eq)]
pub struct Placed {
pub workflow: String,
pub job: String,
pub ceiling: Option<u64>,
}
pub fn placed_jobs(all: &reader::Workflows) -> Vec<Placed> {
all.iter()
.flat_map(|(workflow, document)| {
reader::jobs(document)
.into_iter()
.filter(|(_, job)| names_ubicloud(job))
.map(move |(job, mapping)| Placed {
workflow: workflow.clone(),
job: job.to_owned(),
ceiling: reader::get(mapping, "timeout-minutes").and_then(Value::as_u64),
})
})
.collect()
}
pub fn placement_expressions(all: &reader::Workflows) -> Vec<(String, String)> {
all.iter()
.flat_map(|(workflow, document)| {
reader::jobs(document)
.into_iter()
.filter(|(_, job)| names_ubicloud(job))
.filter_map(move |(job, mapping)| {
Some((format!("{workflow}: {job}"), runs_on_text(mapping)?))
})
})
.collect()
}