Radish alpha
r
rad:z3qg5TKmN83afz2fj9z3fQjU8vaYE
Radicle CI adapter for native CI
Radicle
Git
feat: write a non-run log file
Lars Wirzenius committed 2 years ago
commit 389839552e9e6dc4dda34a145e9bf38512a3825d
parent f3368ab
1 file changed +40 -73
modified src/main.rs
@@ -51,13 +51,13 @@ fn fallible_main() -> Result<(), NativeError> {

    let config = Config::load_via_env()?;
    let mut logfile = config.open_log()?;
-
    logfile.write("radicle-native-ci starts".into())?;
+
    logfile.write("radicle-native-ci starts\n".into())?;

    let (run_id, run_dir) = mkdir_run(&config)?;
-
    logfile.write(format!("created run directory {}", run_dir.display()))?;
+
    logfile.write(format!("run directory {}\n", run_dir.display()))?;

    let src = run_dir.join("src");
-
    let log = run_dir.join("log");
+
    let run_log = run_dir.join("log");

    let profile = Profile::load().map_err(NativeError::LoadProfile)?;
    let storage = profile.storage.path();
@@ -66,11 +66,12 @@ fn fallible_main() -> Result<(), NativeError> {

    if let Request::Trigger { repo, commit } = req {
        info!("Request to trigger CI on {}, {}", repo, commit);
-
        run(run_id, storage, repo, commit, &src, &log)?;
+
        run(run_id, storage, repo, commit, &src, &mut logfile, &run_log)?;
    } else {
-
        write_response(&Response::error("first request was not Trigger"))?;
+
        write_response(&Response::error("first request was not Trigger\n"))?;
    }

+
    logfile.write("radicle-native-ci ends successfully".into())?;
    info!("radicle-native-ci ends");
    Ok(())
}
@@ -111,28 +112,16 @@ fn run(
    repo: Id,
    commit: Oid,
    src: &Path,
-
    log_filename: &Path,
+
    log: &mut LogFile,
+
    run_log: &Path,
) -> Result<(), NativeError> {
-
    let mut log = Rc::new(
-
        std::fs::File::create(log_filename)
-
            .map_err(|e| NativeError::CreateLog(log_filename.into(), e))?,
-
    );
-

-
    logmsg(
-
        log_filename,
-
        &mut log,
-
        "# Log from Radicle native CI\n\n".into(),
-
    )?;
-
    logmsg(
-
        log_filename,
-
        &mut log,
-
        format!("* Repository id: `{}`\n", repo),
-
    )?;
-
    logmsg(
-
        log_filename,
-
        &mut log,
-
        format!("* Commit: `{}`\n\n", commit),
-
    )?;
+
    let mut run_log = LogFile::open(run_log)?;
+

+
    log.write(format!("CI run on {}, {}\n", repo, commit))?;
+

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

    write_response(&Response::triggered(RunId::from(
        run_id.to_string().as_str(),
@@ -143,8 +132,7 @@ fn run(

    debug!("cloning repository to {}", src.display());
    runcmd(
-
        log_filename,
-
        &mut log,
+
        &mut run_log,
        &[
            "git",
            "clone",
@@ -155,58 +143,44 @@ fn run(
    )?;

    let runspec = RunSpec::from_file(&src.join(RUNSPEC_PATH))?;
+
    log.write(format!("CI run spec: {:#?}\n", runspec))?;

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

    write_response(&Response::finished(RunResult::Success))?;

-
    logmsg(
-
        log_filename,
-
        &mut log,
-
        "CI run finished successfully".into(),
-
    )?;
+
    run_log.write("CI run finished successfully\n".into())?;

    Ok(())
}

/// Run a command in a directory.
-
fn runcmd(
-
    log_filename: &Path,
-
    log: &mut Rc<File>,
-
    argv: &[&str],
-
    cwd: &Path,
-
) -> Result<(), NativeError> {
+
fn runcmd(log: &mut LogFile, argv: &[&str], cwd: &Path) -> Result<(), NativeError> {
    let mut p = Popen::create(
        argv,
        PopenConfig {
            cwd: Some(cwd.as_os_str().into()),
-
            stdout: Redirection::RcFile(log.clone()),
+
            stdout: Redirection::RcFile(log.file()?),
            stderr: Redirection::Merge,
            ..Default::default()
        },
    )
    .map_err(|e| NativeError::PopenCreate(format!("{:?}", argv), e))?;
-
    logmsg(
-
        log_filename,
-
        log,
-
        format!(
-
            "# Run command\n\nCommand:\n\n~~~\n{:?}\n~~~\n\nOutput:\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n",
-
            argv
-
        ),
-
    )?;
+

+
    const FENCD_BLOCK: &str = "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";
+
    log.write("# Run command\n\n".into())?;
+
    log.write(format!("~~~\n{:?}\n~~~\n\n", argv))?;
+
    log.write(format!("Output:\n\n{}\n", FENCD_BLOCK))?;
+

    p.communicate(None)
        .map_err(|e| NativeError::ChildComms(format!("{:?}", argv), e))?;
    let exit = p
        .wait()
        .map_err(|e| NativeError::PopenFailed(format!("{:?}", argv), e))?;
    debug!("exit: {:?}", exit);
-
    logmsg(
-
        log_filename,
-
        log,
-
        "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n\n".into(),
-
    )?;
+
    log.write(format!("\n{}\n\n\n", FENCD_BLOCK))?;

    if !exit.success() {
        let error = Response::error(&format!("command failed: {:?}", argv));
@@ -218,14 +192,6 @@ fn runcmd(
    Ok(())
}

-
/// Write a message to the run log.
-
fn logmsg(filename: &Path, log: &mut Rc<File>, msg: String) -> Result<(), NativeError> {
-
    let x = Rc::get_mut(log).ok_or(NativeError::Rc("log file handle"))?;
-
    x.write_all(msg.as_bytes())
-
        .map_err(|e| NativeError::WriteLog(filename.into(), e))?;
-
    Ok(())
-
}
-

/// Configuration file for `radicle-native-ci`.
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
@@ -264,7 +230,7 @@ impl Config {

struct LogFile {
    filename: PathBuf,
-
    file: std::fs::File,
+
    file: Rc<std::fs::File>,
}

impl LogFile {
@@ -276,12 +242,22 @@ impl LogFile {
            .map_err(|e| NativeError::OpenLogFile(filename.into(), e))?;
        Ok(Self {
            filename: filename.into(),
-
            file,
+
            file: Rc::new(file),
        })
    }

+
    fn file(&self) -> Result<Rc<File>, NativeError> {
+
        let file = std::fs::OpenOptions::new()
+
            .append(true)
+
            .create(true)
+
            .open(&self.filename)
+
            .map_err(|e| NativeError::OpenLogFile(self.filename.clone(), e))?;
+
        Ok(Rc::new(file))
+
    }
+

    fn write(&mut self, msg: String) -> Result<(), NativeError> {
        self.file
+
            .as_ref()
            .write_all(msg.as_bytes())
            .map_err(|e| NativeError::WriteLogFile(self.filename.clone(), e))
    }
@@ -312,12 +288,6 @@ impl RunSpec {

#[derive(Debug, thiserror::Error)]
enum NativeError {
-
    #[error("failed to create log file {0}")]
-
    CreateLog(PathBuf, #[source] std::io::Error),
-

-
    #[error("failed to write to log file {0}")]
-
    WriteLog(PathBuf, #[source] std::io::Error),
-

    #[error("failed to read configuration file {0}")]
    ReadConfig(PathBuf, #[source] std::io::Error),

@@ -345,9 +315,6 @@ enum NativeError {
    #[error("failed to write response to stdout: {0:?}")]
    WriteResponse(Response, #[source] MessageError),

-
    #[error("failed to get resource counted value: {0}")]
-
    Rc(&'static str),
-

    #[error("failed to get environment variable {0}")]
    GetEnv(&'static str, #[source] std::env::VarError),