Radish alpha
r
rad:z3qg5TKmN83afz2fj9z3fQjU8vaYE
Radicle CI adapter for native CI
Radicle
Git
refactor: get rid of function with too many arguments using builder
Lars Wirzenius committed 2 years ago
commit 5266ca1cb9d689e9c936c7a003faae4983883960
parent bc7ffba
2 files changed +138 -49
modified src/bin/radicle-native-ci.rs
@@ -106,9 +106,17 @@ fn fallible_main_inner(
        info!("Request to trigger CI on {}, {}", repo, commit);
        builder.repo(repo);
        builder.commit(commit);
-
        let result = run(
-
            run_id, storage, repo, commit, &src, logfile, &run_log, builder,
-
        );
+
        let mut runner = RunnerBuilder::default()
+
            .run_id(run_id)
+
            .storage(storage)
+
            .repo(repo)
+
            .commit(commit)
+
            .src(&src)
+
            .log(logfile)
+
            .run_log(&run_log)
+
            .builder(builder)
+
            .build()?;
+
        let result = runner.run();
        if let Err(e) = result {
            error!("CI run failed: {}", e);
            logfile.write(format!("CI failed: {:?}\n", e))?;
@@ -141,69 +149,146 @@ fn mkdir_run(config: &Config) -> Result<(Uuid, PathBuf), NativeError> {
    Ok((run_id, run_dir))
}

-
/// Perform the CI run.
-
#[allow(clippy::too_many_arguments)]
-
fn run(
+
#[derive(Debug)]
+
struct Runner<'a> {
    run_id: RunId,
-
    storage: &Path,
+
    storage: PathBuf,
    repo: Id,
    commit: Oid,
-
    src: &Path,
-
    log: &mut LogFile,
-
    run_log: &Path,
-
    builder: &mut RunInfoBuilder,
-
) -> Result<(), NativeError> {
-
    let mut run_log = LogFile::open(run_log)?;
+
    src: PathBuf,
+
    log: &'a mut LogFile,
+
    run_log: PathBuf,
+
    builder: &'a mut RunInfoBuilder,
+
}

-
    log.write(format!("CI run on {}, {}\n", repo, commit))?;
+
impl<'a> Runner<'a> {
+
    /// Perform the CI run.
+
    fn run(&mut self) -> Result<(), NativeError> {
+
        let mut run_log = LogFile::open(&self.run_log)?;

-
    run_log.write_str("# Log from Radicle native CI\n\n")?;
-
    run_log.write(format!("* Repository id: `{}`\n", repo))?;
-
    run_log.write(format!("* Commit: `{}`\n\n", commit))?;
+
        self.log
+
            .write(format!("CI run on {}, {}\n", self.repo, self.commit))?;

-
    write_response(&Response::triggered(RunId::from(
-
        run_id.to_string().as_str(),
-
    )))?;
+
        run_log.write_str("# Log from Radicle native CI\n\n")?;
+
        run_log.write(format!("* Repository id: `{}`\n", self.repo))?;
+
        run_log.write(format!("* Commit: `{}`\n\n", self.commit))?;

-
    let repo_path = storage.join(repo.canonical());
-
    debug!("repo path: {}", repo_path.display());
+
        write_response(&Response::triggered(RunId::from(
+
            self.run_id.to_string().as_str(),
+
        )))?;

-
    debug!("cloning repository to {}", src.display());
-
    log.write_str("clone repository\n")?;
-
    runcmd(
-
        &mut run_log,
-
        &[
-
            "git",
-
            "clone",
-
            repo_path.to_str().unwrap(),
-
            src.to_str().unwrap(),
-
        ],
-
        Path::new("."),
-
    )?;
+
        let repo_path = self.storage.join(self.repo.canonical());
+
        debug!("repo path: {}", repo_path.display());

-
    debug!("checking out commit {}", commit);
-
    log.write_str("check out commit\n")?;
-
    runcmd(&mut run_log, &["git", "checkout", &commit.to_string()], src)?;
+
        debug!("cloning repository to {}", self.src.display());
+
        self.log.write_str("clone repository\n")?;
+
        runcmd(
+
            &mut run_log,
+
            &[
+
                "git",
+
                "clone",
+
                repo_path.to_str().unwrap(),
+
                self.src.to_str().unwrap(),
+
            ],
+
            Path::new("."),
+
        )?;

-
    let runspec = RunSpec::from_file(&src.join(RUNSPEC_PATH))?;
-
    log.write(format!("CI run spec: {:#?}\n", runspec))?;
+
        debug!("checking out commit {}", self.commit);
+
        self.log.write_str("check out commit\n")?;
+
        runcmd(
+
            &mut run_log,
+
            &["git", "checkout", &self.commit.to_string()],
+
            &self.src,
+
        )?;

-
    debug!("running CI in cloned repository");
-
    log.write_str("run shell snippet in repository\n")?;
-
    let snippet = format!("set -xeuo pipefail\n{}", &runspec.shell);
-
    runcmd(&mut run_log, &["bash", "-c", &snippet], src)?;
+
        let runspec = RunSpec::from_file(&self.src.join(RUNSPEC_PATH))?;
+
        self.log.write(format!("CI run spec: {:#?}\n", runspec))?;

-
    let result = RunResult::Success;
+
        debug!("running CI in cloned repository");
+
        self.log.write_str("run shell snippet in repository\n")?;
+
        let snippet = format!("set -xeuo pipefail\n{}", &runspec.shell);
+
        runcmd(&mut run_log, &["bash", "-c", &snippet], &self.src)?;

-
    write_response(&Response::finished(result.clone()))?;
+
        let result = RunResult::Success;

-
    run_log.write_str("CI run finished successfully\n")?;
+
        write_response(&Response::finished(result.clone()))?;

-
    std::fs::remove_dir_all(src).map_err(|e| NativeError::RemoveDir(src.into(), e))?;
+
        run_log.write_str("CI run finished successfully\n")?;

-
    builder.result(result);
+
        std::fs::remove_dir_all(&self.src)
+
            .map_err(|e| NativeError::RemoveDir(self.src.clone(), e))?;

-
    Ok(())
+
        self.builder.result(result);
+

+
        Ok(())
+
    }
+
}
+

+
#[derive(Debug, Default)]
+
struct RunnerBuilder<'a> {
+
    run_id: Option<RunId>,
+
    storage: Option<PathBuf>,
+
    repo: Option<Id>,
+
    commit: Option<Oid>,
+
    src: Option<PathBuf>,
+
    log: Option<&'a mut LogFile>,
+
    run_log: Option<PathBuf>,
+
    builder: Option<&'a mut RunInfoBuilder>,
+
}
+

+
impl<'a> RunnerBuilder<'a> {
+
    fn run_id(mut self, run_id: RunId) -> Self {
+
        self.run_id = Some(run_id);
+
        self
+
    }
+

+
    fn storage(mut self, path: &Path) -> Self {
+
        self.storage = Some(path.into());
+
        self
+
    }
+

+
    fn repo(mut self, id: Id) -> Self {
+
        self.repo = Some(id);
+
        self
+
    }
+

+
    fn commit(mut self, oid: Oid) -> Self {
+
        self.commit = Some(oid);
+
        self
+
    }
+

+
    fn src(mut self, path: &Path) -> Self {
+
        self.src = Some(path.into());
+
        self
+
    }
+

+
    fn log(mut self, log: &'a mut LogFile) -> Self {
+
        self.log = Some(log);
+
        self
+
    }
+

+
    fn run_log(mut self, path: &Path) -> Self {
+
        self.run_log = Some(path.into());
+
        self
+
    }
+

+
    fn builder(mut self, builder: &'a mut RunInfoBuilder) -> Self {
+
        self.builder = Some(builder);
+
        self
+
    }
+

+
    fn build(self) -> Result<Runner<'a>, NativeError> {
+
        Ok(Runner {
+
            run_id: self.run_id.ok_or(NativeError::Unset("run_id"))?,
+
            storage: self.storage.ok_or(NativeError::Unset("storage"))?,
+
            repo: self.repo.ok_or(NativeError::Unset("repo"))?,
+
            commit: self.commit.ok_or(NativeError::Unset("commit"))?,
+
            src: self.src.ok_or(NativeError::Unset("src"))?,
+
            log: self.log.ok_or(NativeError::Unset("log"))?,
+
            run_log: self.run_log.ok_or(NativeError::Unset("run_log"))?,
+
            builder: self.builder.ok_or(NativeError::Unset("builder"))?,
+
        })
+
    }
}

#[derive(Debug, thiserror::Error)]
@@ -220,6 +305,9 @@ enum NativeError {
    #[error("failed to remove {0}")]
    RemoveDir(PathBuf, #[source] std::io::Error),

+
    #[error("programming error: failed to set field {0}")]
+
    Unset(&'static str),
+

    #[error(transparent)]
    Config(#[from] ConfigError),

modified src/logfile.rs
@@ -4,6 +4,7 @@ use std::{
    path::{Path, PathBuf},
};

+
#[derive(Debug)]
pub struct LogFile {
    filename: PathBuf,
    file: File,