Implement support for inline commands definition for projects

I think this will help the task definitions that don't line up to a development
project
This commit is contained in:
R Tyler Croy 2023-03-12 19:48:54 -07:00
parent f8976aab10
commit 0d00fb4cfd
4 changed files with 87 additions and 45 deletions

View File

@ -3,9 +3,8 @@ projects:
'inline-config': 'inline-config':
description: | description: |
An inline configured project An inline configured project
filename: 'ci.synchronik.yml' inline:
scm: needs:
github: - git
owner: 'rtyler' commands:
repo: 'synchronik' - 'whoami'
ref: 'main'

View File

@ -10,7 +10,7 @@ use crate::AppState;
/* /*
* Representation of the Synchronik YAML format * Representation of the Synchronik YAML format
*/ */
#[derive(Clone, Debug, Deserialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Yml { pub struct Yml {
pub needs: Vec<String>, pub needs: Vec<String>,
pub commands: Vec<String>, pub commands: Vec<String>,
@ -19,6 +19,11 @@ pub struct Yml {
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum Scm { pub enum Scm {
/*
* The Nonexistent Scm is used for stubbing out the Scm properties when
* inlining configuration
*/
Nonexistent,
GitHub { GitHub {
owner: String, owner: String,
repo: String, repo: String,
@ -31,11 +36,22 @@ pub enum Scm {
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub struct Project { pub struct Project {
description: String, description: String,
pub filename: String, /*
#[serde(with = "serde_yaml::with::singleton_map")] * Used for optionally defining an inline Yml configuration
*/
inline: Option<Yml>,
pub filename: Option<String>,
#[serde(default = "default_scm", with = "serde_yaml::with::singleton_map")]
pub scm: Scm, pub scm: Scm,
} }
/*
* Simple default scm for use when nothing has been otherwise defined
*/
fn default_scm() -> Scm {
Scm::Nonexistent
}
/* /*
* Internal representation of an Agent that has been "loaded" by the server * Internal representation of an Agent that has been "loaded" by the server
* *
@ -86,7 +102,7 @@ pub struct AgentConfig {
pub url: Url, pub url: Url,
} }
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ServerConfig { pub struct ServerConfig {
pub agents: HashMap<String, AgentConfig>, pub agents: HashMap<String, AgentConfig>,
pub projects: HashMap<String, Project>, pub projects: HashMap<String, Project>,
@ -157,21 +173,6 @@ impl ServerConfig {
} }
} }
/*
* Default trait implementation for ServerConfig, will result in an empty set of agents and
* projects
*
* Not really useful for anything other than tests
*/
impl Default for ServerConfig {
fn default() -> Self {
Self {
agents: HashMap::default(),
projects: HashMap::default(),
}
}
}
/* /*
* Merge two Valus from <https://stackoverflow.com/a/67743348> * Merge two Valus from <https://stackoverflow.com/a/67743348>
*/ */
@ -252,4 +253,58 @@ mod tests {
} }
} }
} }
#[test]
fn parse_config_with_scm() {
let conf = r#"
---
agents:
'Local':
url: 'http://localhost:9000'
projects:
'synchronik':
description: |
Self-hosted project
filename: 'ci.synchronik.yml'
scm:
github:
owner: 'rtyler'
repo: 'synchronik'
ref: 'main'
"#;
let value: ServerConfig = serde_yaml::from_str(&conf).expect("Failed to parse");
assert_eq!(value.agents.len(), 1);
}
#[test]
fn parse_config_inline() {
let conf = r#"
---
agents:
'Local':
url: 'http://localhost:9000'
projects:
'synchronik':
description: |
Self-hosted project
inline:
needs:
- git
commands:
- 'whoami'
"#;
let value: ServerConfig = serde_yaml::from_str(&conf).expect("Failed to parse");
assert_eq!(value.agents.len(), 1);
assert_eq!(value.projects.len(), 1);
let project = value.projects.get("synchronik").unwrap();
match &project.inline {
Some(yml) => {
assert!(yml.commands.contains(&"whoami".to_string()));
}
None => {
assert!(false);
}
}
}
} }

View File

@ -2,7 +2,7 @@ use sqlx::SqlitePool;
use crate::models::*; use crate::models::*;
#[derive(Clone, Debug)] #[derive(Clone, Debug, Default)]
pub struct Run { pub struct Run {
run: RunRow, run: RunRow,
project: Project, project: Project,
@ -91,17 +91,6 @@ impl Run {
} }
} }
impl Default for Run {
fn default() -> Self {
Self {
run: RunRow::default(),
project: Project::default(),
scm_info: ScmInfo::default(),
definition: RunDefinition::default(),
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -74,21 +74,20 @@ pub mod api {
if let Some(project) = state.config.projects.get(&name) { if let Some(project) = state.config.projects.get(&name) {
match &project.scm { match &project.scm {
Scm::Nonexistent => {}
Scm::GitHub { Scm::GitHub {
owner, owner,
repo, repo,
scm_ref, scm_ref,
} => { } => {
debug!( let filename = match &project.filename {
"Fetching the file {} from {}/{}", None => "synchronik.yml".to_string(),
&project.filename, owner, repo Some(filename) => filename.to_string(),
); };
debug!("Fetching the file {} from {}/{}", filename, owner, repo);
let res = octocrab::instance() let res = octocrab::instance()
.repos(owner, repo) .repos(owner, repo)
.raw_file( .raw_file(octocrab::params::repos::Commitish(scm_ref.into()), filename)
octocrab::params::repos::Commitish(scm_ref.into()),
&project.filename,
)
.await?; .await?;
let config_file: Yml = serde_yaml::from_str(&res.text().await?)?; let config_file: Yml = serde_yaml::from_str(&res.text().await?)?;
debug!("text: {:?}", config_file); debug!("text: {:?}", config_file);