coodev-runner 0.1.42

A simple runner for coodev
Documentation
mod cache;
mod git;

use crate::user_config::{UserActionStep, UserStep};
use cache::{CacheAction, RestoreCacheAction, SaveCacheAction};
use git::GitCheckoutAction;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt::Debug, sync::Arc};

#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct ActionSteps {
  pub pre: Option<UserStep>,
  pub run: UserStep,
  pub post: Option<UserStep>,
}

pub trait Action
where
  Self: std::fmt::Debug + std::marker::Sync + std::marker::Send + Debug,
{
  fn normalize(&self, step: UserActionStep) -> crate::Result<ActionSteps>;
}

#[derive(Debug, Clone)]
pub struct Actions {
  actions: HashMap<String, Arc<Box<dyn Action>>>,
}

impl Actions {
  pub fn new() -> Self {
    let mut actions: HashMap<String, Arc<Box<dyn Action>>> = HashMap::new();

    actions.insert(
      "cache/save".to_string(),
      Arc::new(Box::new(SaveCacheAction::new())),
    );

    actions.insert(
      "cache/restore".to_string(),
      Arc::new(Box::new(RestoreCacheAction::new())),
    );

    actions.insert("cache".to_string(), Arc::new(Box::new(CacheAction::new())));

    actions.insert(
      "git/checkout".to_string(),
      Arc::new(Box::new(GitCheckoutAction::new())),
    );

    Self { actions }
  }

  pub fn get(&self, name: &str) -> Option<Arc<Box<dyn Action>>> {
    self.actions.get(name).cloned()
  }

  pub fn register(&mut self, name: String, action: Box<dyn Action>) {
    self.actions.insert(name, Arc::new(action));
  }
}

#[cfg(test)]
mod tests {
  use crate::{
    actions::{Action, ActionSteps},
    user_config::{UserActionStep, UserCommandStep, UserStep},
    utils::test::{create_runner, create_workflow_options, create_workflow_runner},
  };

  #[test]
  fn test_normalize_step_actions() -> anyhow::Result<()> {
    let yaml = r#"
jobs:
  test-job:
    name: Test Job
    steps:
      - name: Cache
        uses: cache
        with:
          key: some-key-${COODEV_SHA}
          path: .
    "#;

    let workflow_runner = create_workflow_runner(yaml)?;
    let workflow = workflow_runner.get_workflow();

    let steps = workflow.jobs.get("test-job").unwrap().steps.clone();
    assert_eq!(steps.len(), 2);
    assert_eq!(steps[0].name, Some("Cache".to_string()));
    assert_eq!(steps[1].name, Some("Post Cache".to_string()));

    Ok(())
  }

  #[test]
  fn test_not_found_action() -> anyhow::Result<()> {
    let yaml = r#"
jobs:
  test-job:
    name: Test Job
    steps:
      - name: Not Found
        uses: not-found-action
    "#;

    let res = create_workflow_runner(yaml);

    if let Err(err) = res {
      assert_eq!(
        err.to_string(),
        "Failed to parse user config: Action `not-found-action` is not found"
      );
    } else {
      panic!("Should be error");
    }

    Ok(())
  }

  #[test]
  fn test_custom_action() -> anyhow::Result<()> {
    let yaml = r#"
  jobs:
    job:
      steps:
        - name: Custom Action
          uses: custom-action
      "#;

    let runner = create_runner()?;

    #[derive(Debug, Clone)]
    struct CustomAction;

    impl Action for CustomAction {
      fn normalize(&self, _step: UserActionStep) -> crate::Result<ActionSteps> {
        Ok(ActionSteps {
          pre: None,
          run: UserStep::Command(UserCommandStep {
            name: Some("Custom Action".to_string()),
            run: "echo 'Custom Action'".to_string(),
            continue_on_error: None,
            image: None,
            environments: None,
            secrets: None,
            volumes: None,
            timeout: None,
            security_opts: None,
          }),
          post: None,
        })
      }
    }

    runner.register_action("custom-action", CustomAction);

    let options = create_workflow_options(yaml);
    let workflow_runner = runner.create_workflow_runner(options)?;
    let workflow = workflow_runner.get_workflow();

    let steps = workflow.jobs.get("job").unwrap().steps.clone();

    assert_eq!(steps.len(), 1);
    assert_eq!(steps[0].name, Some("Custom Action".to_string()));
    assert_eq!(steps[0].run, "echo 'Custom Action'");

    Ok(())
  }
}