flowey_cli/pipeline_resolver/github_yaml/
github_yaml_defs.rsuse serde::Deserialize;
use serde::Serialize;
use serde::Serializer;
use serde::ser::SerializeMap;
use std::collections::BTreeMap;
fn validate_name<S, T>(s: &BTreeMap<String, T>, ser: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Serialize,
{
let mut ser_map = ser.serialize_map(Some(s.len()))?;
for (name, v) in s {
if name.is_empty() {
return Err(serde::ser::Error::custom("name cannot be empty"));
}
if name.chars().next().unwrap().is_ascii_digit() {
return Err(serde::ser::Error::custom("name cannot start with a number"));
}
if name.starts_with("GITHUB_") {
return Err(serde::ser::Error::custom(
"name cannot start with 'GITHUB_'",
));
}
if !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
return Err(serde::ser::Error::custom(
"name must be ascii alphanumeric + '_'",
));
}
ser_map.serialize_entry(name, v)?;
}
ser_map.end()
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct WorkflowCall {
#[serde(skip_serializing_if = "Option::is_none")]
pub inputs: Option<Inputs>,
#[serde(skip_serializing_if = "Option::is_none")]
pub outputs: Option<Outputs>,
#[serde(skip_serializing_if = "Option::is_none")]
pub secrets: Option<Secrets>,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct WorkflowDispatch {
pub inputs: Inputs,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct PrTrigger {
#[serde(skip_serializing_if = "Vec::is_empty")]
pub branches: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub branches_ignore: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub types: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CiTrigger {
#[serde(skip_serializing_if = "Vec::is_empty")]
pub branches: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub branches_ignore: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub tags_ignore: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Cron {
pub cron: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Concurrency {
#[serde(skip_serializing_if = "Option::is_none")]
pub group: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cancel_in_progress: Option<bool>,
}
#[derive(Serialize, Deserialize)]
pub struct Triggers {
#[serde(skip_serializing_if = "Option::is_none")]
pub workflow_call: Option<WorkflowCall>,
pub workflow_dispatch: Option<WorkflowDispatch>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pull_request: Option<PrTrigger>,
#[serde(skip_serializing_if = "Option::is_none")]
pub push: Option<CiTrigger>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub schedule: Vec<Cron>,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum PermissionValue {
Read,
Write,
None,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "kebab-case")]
pub enum Permissions {
Actions,
Attestations,
Checks,
Contents,
Deployments,
Discussions,
IdToken,
Issues,
Packages,
Pages,
PullRequests,
RepositoryProjects,
SecurityEvents,
Statuses,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Pipeline {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub on: Option<Triggers>,
#[serde(skip_serializing_if = "Option::is_none")]
pub concurrency: Option<Concurrency>,
#[serde(skip_serializing_if = "Option::is_none")]
pub inputs: Option<Vec<Input>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub jobs: Option<Jobs>,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Inputs {
#[serde(flatten, serialize_with = "validate_name")]
pub inputs: BTreeMap<String, Input>,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Input {
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<Default>,
pub required: bool,
#[serde(flatten)]
pub ty: InputType,
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum Default {
Boolean(bool),
String(String),
Number(i64),
}
#[derive(Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum InputType {
Boolean,
String,
Number,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Outputs {
#[serde(flatten, serialize_with = "validate_name")]
pub outputs: BTreeMap<String, Output>,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Output {
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub value: String,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING-KEBAB-CASE")]
pub struct Secrets {
#[serde(flatten, serialize_with = "validate_name")]
pub secrets: BTreeMap<String, Secret>,
}
#[derive(Serialize, Deserialize)]
pub struct Secret {
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub required: bool,
}
#[derive(Serialize, Deserialize)]
pub enum RunnerOsLabel {
#[serde(rename = "ubuntu-latest")]
UbuntuLatest,
#[serde(rename = "ubuntu-22.04")]
Ubuntu2204,
#[serde(rename = "ubuntu-20.04")]
Ubuntu2004,
#[serde(rename = "windows-latest")]
WindowsLatest,
#[serde(rename = "windows-2022")]
Windows2022,
#[serde(rename = "windows-2019")]
Windows2019,
#[serde(rename = "macos-latest")]
MacOsLatest,
#[serde(rename = "macos-14")]
MacOs14,
#[serde(rename = "macos-13")]
MacOs13,
#[serde(rename = "macos-12")]
MacOs12,
#[serde(rename = "macos-11")]
MacOs11,
#[serde(untagged)]
Custom(String),
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum Runner {
GhHosted(RunnerOsLabel),
SelfHosted(Vec<String>),
Group {
group: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
labels: Vec<String>,
},
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Job {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub runs_on: Option<Runner>,
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
pub permissions: BTreeMap<Permissions, PermissionValue>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub needs: Vec<String>,
#[serde(rename = "if", skip_serializing_if = "Option::is_none")]
pub r#if: Option<String>,
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
pub env: BTreeMap<String, String>,
pub steps: Vec<serde_yaml::Value>,
}
#[derive(Serialize, Deserialize)]
pub struct Jobs {
#[serde(flatten, serialize_with = "validate_name")]
pub jobs: BTreeMap<String, Job>,
}