Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow %x and %? to be used in errorcmd. #135

Merged
merged 1 commit into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions snare.conf.5
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ the repository.
a literal
.Ql % .
.El
.Pp
Note that
.Ql %
may not be followed by any character other than those above.
Expand Down Expand Up @@ -147,10 +148,24 @@ the repository owner.
the repository.
.It Sy %s
the path to the file containing the job's combined stderr / stdout.
.It Sy %x
the exit type:
.Qq status
(i.e. normal exit);
.Qq signal ;
or
.Qq unknown .
.It Sy %?
the exit status / signal number (either an integer or the literal string
.Qq unknown )
that
.Em cmd
failed with.
.It Sy %%
a literal
.Ql % .
.El
.Pp
Note that
.Ql %
may not be followed by any character other than those above.
Expand Down
4 changes: 2 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ impl GitHub {

/// Verify that the `errorcmd` string is valid, returning `Ok())` if so or `Err(String)` if not.
fn verify_errorcmd_str(errorcmd: &str) -> Result<(), String> {
GitHub::verify_str(errorcmd, &['e', 'o', 'r', 'j', 's', '%'])
GitHub::verify_str(errorcmd, &['e', 'o', 'r', 'j', 's', '?', 'x', '%'])
}

fn verify_str(s: &str, modifiers: &[char]) -> Result<(), String> {
Expand Down Expand Up @@ -473,7 +473,7 @@ mod test {
fn test_verify_errorcmd_string() {
assert!(GitHub::verify_errorcmd_str("").is_ok());
assert!(GitHub::verify_errorcmd_str("a").is_ok());
assert!(GitHub::verify_errorcmd_str("%% %e %o %r %j %s %%").is_ok());
assert!(GitHub::verify_errorcmd_str("%% %e %o %r %j %s %x %? %%").is_ok());
assert!(GitHub::verify_errorcmd_str("%%").is_ok());
assert!(GitHub::verify_errorcmd_str("%").is_err());
assert!(GitHub::verify_errorcmd_str("a%").is_err());
Expand Down
50 changes: 43 additions & 7 deletions src/jobrunner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ use std::{
error::Error,
fs::{self, remove_file},
io::{Read, Write},
os::unix::io::{AsRawFd, RawFd},
os::unix::{
io::{AsRawFd, RawFd},
process::ExitStatusExt,
},
path::PathBuf,
process::{self, Child, Command},
sync::Arc,
Expand Down Expand Up @@ -201,14 +204,28 @@ impl JobRunner {
// `Some(_)` and the unwrap thus safe.
let mut exited = false;
let mut exited_success = false;
let mut exit_type = "";
let mut exit_code = String::new();
match self.running[i].as_mut().unwrap().child.try_wait() {
Ok(Some(status)) => {
exited = true;
exited_success = status.success();
if let Some(x) = status.code() {
exit_type = "status";
exit_code = x.to_string();
} else if let Some(x) = status.signal() {
exit_type = "signal";
exit_code = x.to_string();
} else {
exit_type = "unknown";
exit_code = "unknown".to_owned();
}
}
Err(_) => {
exited = true;
exited_success = false;
exit_type = "unknown";
exit_code = "unknown".to_string();
}
Ok(None) => (),
}
Expand All @@ -220,7 +237,9 @@ impl JobRunner {
"errorcmd exited unsuccessfully: {}",
job.rconf.errorcmd.as_ref().unwrap()
));
} else if let Some(errorchild) = self.run_errorcmd(job) {
} else if let Some(errorchild) =
self.run_errorcmd(job, exit_type, &exit_code)
{
let job = &mut self.running[i].as_mut().unwrap();
job.child = errorchild;
job.is_errorcmd = true;
Expand Down Expand Up @@ -489,7 +508,7 @@ impl JobRunner {
}

/// Run the user's errorcmd (if they've specified one).
fn run_errorcmd(&self, job: &Job) -> Option<Child> {
fn run_errorcmd(&self, job: &Job, exit_type: &str, exit_code: &str) -> Option<Child> {
if let Some(raw_errorcmd) = &job.rconf.errorcmd {
let errorcmd = errorcmd_replace(
raw_errorcmd,
Expand All @@ -498,6 +517,8 @@ impl JobRunner {
&job.repo,
job.json_path.as_os_str().to_str().unwrap(),
job.stderrout.path().as_os_str().to_str().unwrap(),
exit_type,
exit_code,
);
match Command::new(&self.shell)
.arg("-c")
Expand Down Expand Up @@ -550,6 +571,8 @@ fn cmd_replace(
/// * `%r` with `repo`
/// * `%j` with `json_path`
/// * `%s` with `stderrout_path`
/// * `%x` with `exit_type` ("status", "signal", or "unknown")
/// * `%?` with `exit_code` (integer or "unknown")
///
/// Note that `raw_cmd` *must* have been validated by config::GitHub::verify_errorcmd_str or
/// undefined behaviour will occur.
Expand All @@ -560,13 +583,17 @@ fn errorcmd_replace(
repo: &str,
json_path: &str,
stderrout_path: &str,
exit_type: &str,
exit_code: &str,
) -> String {
let modifiers = [
('e', event_type),
('o', owner),
('r', repo),
('j', json_path),
('s', stderrout_path),
('x', exit_type),
('?', exit_code),
('%', "%"),
]
.iter()
Expand Down Expand Up @@ -656,11 +683,20 @@ mod test {

#[test]
fn test_errorcmd_replace() {
assert_eq!(errorcmd_replace("", "", "", "", "", ""), "");
assert_eq!(errorcmd_replace("a", "", "", "", "", ""), "a");
assert_eq!(errorcmd_replace("", "", "", "", "", "", "", ""), "");
assert_eq!(errorcmd_replace("a", "", "", "", "", "", "", ""), "a");
assert_eq!(
errorcmd_replace("%% %e %o %r %j %s %%", "ee", "oo", "rr", "jj", "ss"),
"% ee oo rr jj ss %"
errorcmd_replace(
"%% %e %o %r %j %s %x %? %%",
"ee",
"oo",
"rr",
"jj",
"ss",
"ex",
"ec"
),
"% ee oo rr jj ss ex ec %"
);
}
}
14 changes: 9 additions & 5 deletions tests/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,20 @@ fn errorcmd() {
let td = Builder::new()
.tempdir_in(env!("CARGO_TARGET_TMPDIR"))
.unwrap();
let mut tp = td.path().to_owned();
tp.push("t");
let tps = tp.as_path().to_str().unwrap();
let mut tp1 = td.path().to_owned();
tp1.push("t1");
let tp1s = tp1.as_path().to_str().unwrap();
let mut tp2 = td.path().to_owned();
tp2.push("t2");
let tp2s = tp2.as_path().to_str().unwrap();

run_success(
&format!(
r#"listen = "127.0.0.1:0";
github {{
match ".*" {{
cmd = "dd if=/dev/zero bs=1k count=256 status=none && dd if=/dev/zero of=/dev/stderr bs=1k count=256 status=none && exit 1";
errorcmd = "cp %s {tps}";
errorcmd = "echo %x %? > {tp1s} && cp %s {tp2s}";
secret = "secretsecret";
}}
}}"#
Expand Down Expand Up @@ -135,7 +138,8 @@ payload={{
move |response: String| {
if response.starts_with("HTTP/1.1 200 OK") {
sleep(SNARE_PAUSE);
assert_eq!(read_to_string(&tp).unwrap().len(), 2 * 256 * 1024);
assert_eq!(read_to_string(&tp1).unwrap().trim(), "status 1");
assert_eq!(read_to_string(&tp2).unwrap().len(), 2 * 256 * 1024);
Ok(())
} else {
Err(format!("Received HTTP response '{response}'").into())
Expand Down
14 changes: 11 additions & 3 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,17 @@ where
G: Fn(String) -> Result<(), Box<dyn Error>> + RefUnwindSafe + UnwindSafe + 'static,
{
let (mut sn, tp) = snare_command(cfg)?;
match sn.try_wait() {
Ok(None) => (),
_ => todo!(),
for i in 0..5 {
match sn.try_wait() {
Ok(None) => break,
_ => {
if i < 4 {
sleep(Duration::from_secs(1))
} else {
panic!()
}
}
}
}
let tp = Rc::new(tp);

Expand Down