| |
|
| |
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();
|
| |
|
| |
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(())
|
| |
}
|
| |
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(),
|
| |
)?;
|
| |
|
| |
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));
|
| |
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)]
|
| |
.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))
|
| |
}
|
| |
|
| |
#[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),
|
| |
|