use crate::{
actions::{Action, ActionSteps},
error,
user_config::{UserActionStep, UserCommandStep, UserStep},
};
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct CacheOptions {
pub key: String,
pub path: String,
}
#[derive(Debug, Clone)]
pub struct SaveCacheAction {}
#[derive(Debug, Clone)]
pub struct RestoreCacheAction {}
#[derive(Debug, Clone)]
pub struct CacheAction {}
fn parse_cache_options(step: &UserActionStep) -> crate::Result<CacheOptions> {
let with = step
.with
.clone()
.ok_or(error::Error::workflow_config_error(
"No `with` options provided",
))?;
let cache_options: CacheOptions = serde_yaml::from_value(with).map_err(|_| {
error::Error::workflow_config_error(
"`path` and `key` are required in `with` options for `cache` action",
)
})?;
Ok(cache_options)
}
fn create_save_cache_step(
name: impl Into<String>,
step: UserActionStep,
) -> crate::Result<UserStep> {
let CacheOptions { key, path } = parse_cache_options(&step)?;
let run = format!(
"
echo \"Save cache with key: $COODEV_CACHE_DIR/{key} and path: {path}\"
if [ -d {path} ]; then
tar -czf $COODEV_CACHE_DIR/{key}.tar.gz -C {path} .
echo \"Cache saved\"
else
echo \"No cache found\"
fi
"
);
let name = Some(name.into());
let continue_on_error = step.continue_on_error.unwrap_or(true);
let timeout = step.timeout.unwrap_or("10m".to_string());
Ok(UserStep::Command(UserCommandStep {
name,
image: Some(String::from("ubuntu")),
run,
continue_on_error: Some(continue_on_error),
environments: None,
secrets: None,
volumes: None,
security_opts: None,
timeout: Some(timeout),
}))
}
fn create_restore_cache_step(
name: impl Into<String>,
step: UserActionStep,
) -> crate::Result<UserStep> {
let CacheOptions { key, path } = parse_cache_options(&step)?;
let run = format!(
"
echo \"Restore cache with key: $COODEV_CACHE_DIR/{key} and path: {path}\"
if [ -f $COODEV_CACHE_DIR/{key}.tar.gz ]; then
if [ ! -d {path} ]; then
mkdir -p {path}
fi
tar -zxf \"$COODEV_CACHE_DIR/{key}.tar.gz\" -C {path}
echo \"Restore cache succeed\"
else
echo \"No cache found\"
fi
"
);
let name = Some(name.into());
let timeout = step.timeout.unwrap_or("10m".to_string());
Ok(UserStep::Command(UserCommandStep {
name,
image: Some(String::from("ubuntu")),
run,
continue_on_error: None,
environments: None,
secrets: None,
volumes: None,
security_opts: None,
timeout: Some(timeout),
}))
}
impl Action for SaveCacheAction {
fn normalize(&self, step: UserActionStep) -> crate::Result<ActionSteps> {
let name = &step
.name
.clone()
.unwrap_or_else(|| "Save cache".to_string());
Ok(ActionSteps {
pre: None,
run: create_save_cache_step(name, step)?,
post: None,
})
}
}
impl Action for RestoreCacheAction {
fn normalize(&self, step: UserActionStep) -> crate::Result<ActionSteps> {
let name = &step
.name
.clone()
.unwrap_or_else(|| "Restore cache".to_string());
Ok(ActionSteps {
pre: None,
run: create_restore_cache_step(name, step)?,
post: None,
})
}
}
impl Action for CacheAction {
fn normalize(&self, step: UserActionStep) -> crate::Result<ActionSteps> {
let name = &step.name.clone().unwrap_or_else(|| "Cache".to_string());
let post_name = format!("Post {}", name);
Ok(ActionSteps {
pre: None,
run: create_restore_cache_step(name, step.clone())?,
post: Some(create_save_cache_step(post_name, step)?),
})
}
}
impl SaveCacheAction {
pub fn new() -> Self {
Self {}
}
}
impl RestoreCacheAction {
pub fn new() -> Self {
Self {}
}
}
impl CacheAction {
pub fn new() -> Self {
Self {}
}
}
#[cfg(test)]
mod tests {
use crate::utils::test::run_workflow;
#[tokio::test]
#[ignore]
async fn test_cache_absolute_dir() -> anyhow::Result<()> {
let yaml = r#"
jobs:
test-job:
name: Test Job
working-directories:
- /root/test
steps:
- name: Cache
uses: cache
with:
key: some-key-${COODEV_SHA}
path: /root/test
- name: List Files
run: |
echo "List files"
ls /root/test
- name: Create File
run: |
echo "Create file"
echo "hello" > /root/test/hello.txt
"#;
let _res = run_workflow(yaml).await?;
Ok(())
}
#[tokio::test]
#[ignore]
async fn test_cache_relative_dir() -> anyhow::Result<()> {
let yaml = r#"
jobs:
test-job:
name: Test Job
steps:
- name: Cache
uses: cache
with:
key: some-key
path: dir
- name: List Files
run: |
echo "List files"
if [ -d dir ]; then
ls -al --color=always dir
echo "File content $(cat dir/hello.txt)"
else
echo "dir not exists"
fi
- name: Create File
run: |
echo "Create dir and file"
mkdir -p dir
echo "hello" > dir/hello.txt
ls -al --color=always
cat dir/hello.txt
"#;
let _res = run_workflow(yaml).await?;
Ok(())
}
}