Skip to content

Commit

Permalink
feat: ability to specify hook shell
Browse files Browse the repository at this point in the history
  • Loading branch information
zaucy committed Oct 5, 2023
1 parent 17f98dc commit f20b3cc
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 12 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ tera = "1.18.1"
globset = "0.4.8"
log = "0.4.16"
stderrlog = "0.5.1"
shlex = "1.1.0"

[dev-dependencies]
assert_cmd = "1.0.3"
Expand Down
4 changes: 2 additions & 2 deletions cog.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ post_bump_hooks = [
# instead of the main ones when running `cog bump --hook-profile example --auto`
[bump_profiles.example]
pre_bump_hooks = [
"echo 'bumping from {{latest}} to {{version}} using a named profile'"
"sh -c \"echo 'bumping from {{latest}} to {{version}} using a named profile'\""
]
post_bump_hooks = [
"echo 'we are done'"
"sh -c \"echo 'we are done'\""
]

# Shareable git-hooks, can be installed using `cog install-hook --all`
Expand Down
3 changes: 2 additions & 1 deletion src/command/bump/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,8 @@ impl CocoGitto {
};
info!("[{command}]");
let package_path = options.package.map(|p| p.path.as_path());
hook.run(package_path).context(hook.to_string())?;
hook.run(settings.get_shell(), package_path)
.context(hook.to_string())?;
println!();
}

Expand Down
2 changes: 1 addition & 1 deletion src/git/hook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ mod tests {

use crate::CocoGitto;

use crate::settings::{GitHook, GitHookType, Settings};
use crate::settings::{GitHook, GitHookType, HookShell, Settings};
use anyhow::Result;
use cmd_lib::run_cmd;
use sealed_test::prelude::*;
Expand Down
69 changes: 61 additions & 8 deletions src/hook/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ mod parser;

use std::collections::{HashMap, VecDeque};
use std::ops::Range;
use std::process::Command;
use std::process::{Command, Output};
use std::str::FromStr;
use std::{fmt, path};

use crate::Tag;
use parser::Token;

use crate::settings::{BumpProfile, HookType};
use crate::settings::{BumpProfile, HookShell, HookType};
use anyhow::{anyhow, ensure, Result};

pub trait Hooks {
fn get_shell(&self) -> Option<&HookShell>;
fn bump_profiles(&self) -> &HashMap<String, BumpProfile>;
fn pre_bump_hooks(&self) -> &Vec<String>;
fn post_bump_hooks(&self) -> &Vec<String>;
Expand Down Expand Up @@ -170,15 +171,39 @@ impl Hook {
Ok(())
}

pub fn run(&self, package_path: Option<&path::Path>) -> Result<()> {
let mut cmd = Command::new("sh");
let cmd = cmd.arg("-c").arg(&self.0);
pub fn run(
&self,
shell: Option<&HookShell>,
package_path: Option<&path::Path>,
) -> Result<Output> {
let executable: String;
let args: Vec<String>;
if let Some(shell) = &shell {
executable = shell.executable.clone();
let mut shell_args = shell.args.clone();
shell_args.push(self.0.clone());
args = shell_args;
} else {
let parsed_args = shlex::split(&self.0).unwrap();
executable = which::which(parsed_args.first().unwrap())
.unwrap()
.to_string_lossy()
.to_string();
args = parsed_args.into_iter().skip(1).collect();
};

let mut cmd = Command::new(&executable);
cmd.args(&args);

if let Some(current_dir) = package_path {
cmd.current_dir(current_dir);
}
let status = cmd.status()?;

let output = cmd.output()?;
let status = output.status;

ensure!(status.success(), "hook failed with status {}", status);
Ok(())
Ok(output)
}
}

Expand Down Expand Up @@ -489,7 +514,7 @@ mod test {
hook.insert_versions(None, Some(&HookVersion::new(Tag::from_str("1.0.0", None)?)))
.unwrap();

let outcome = hook.run(None);
let outcome = hook.run(None, None);

assert_that!(outcome).is_ok();

Expand Down Expand Up @@ -542,4 +567,32 @@ mod test {
.is_equal_to(r#"echo "cog, version: 1.1.0, tag: cog-v1.1.0, current: 1.0.0, current_tag: cog-v1.0.0""#);
Ok(())
}

#[sealed_test]
fn shell_environment_variables() -> Result<()> {
std::env::set_var("TEST_ENV_VAR", "example");
let hook: Hook;
let shell: crate::settings::HookShell;

if cfg!(windows) {
hook = Hook::from_str("echo TEST_ENV_VAR=%TEST_ENV_VAR%")?;
shell = crate::settings::HookShell {
executable: "cmd.exe".to_string(),
args: vec!["/C".to_string()],
};
} else {
hook = Hook::from_str("echo TEST_ENV_VAR=$TEST_ENV_VAR")?;
shell = crate::settings::HookShell {
executable: "sh".to_string(),
args: vec!["-c".to_string()],
};
}

let hook_output = hook.run(Some(&shell), None)?;

let hook_stdout = String::from_utf8(hook_output.stdout)?;
assert_that!(hook_stdout.trim()).is_equal_to("TEST_ENV_VAR=example");

Ok(())
}
}
23 changes: 23 additions & 0 deletions src/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub struct Settings {
pub tag_prefix: Option<String>,
pub skip_ci: Option<String>,
pub skip_untracked: bool,
pub shell: Option<HookShell>,
pub pre_bump_hooks: Vec<String>,
pub post_bump_hooks: Vec<String>,
pub pre_package_bump_hooks: Vec<String>,
Expand All @@ -62,6 +63,7 @@ impl Default for Settings {
tag_prefix: None,
skip_ci: None,
skip_untracked: false,
shell: None,
pre_bump_hooks: vec![],
post_bump_hooks: vec![],
pre_package_bump_hooks: vec![],
Expand Down Expand Up @@ -259,6 +261,15 @@ pub fn changelog_path() -> &'static PathBuf {
&SETTINGS.changelog.path
}

#[derive(Debug, Deserialize, Serialize, Default, Eq, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct HookShell {
#[serde(default)]
pub executable: String,
#[serde(default)]
pub args: Vec<String>,
}

#[derive(Debug, Deserialize, Serialize, Default, Eq, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct BumpProfile {
Expand Down Expand Up @@ -378,6 +389,14 @@ impl Settings {
}

impl Hooks for Settings {
fn get_shell(&self) -> Option<&HookShell> {
if let Some(shell) = &self.shell {
Some(&shell)
} else {
None
}
}

fn bump_profiles(&self) -> &HashMap<String, BumpProfile> {
&self.bump_profiles
}
Expand All @@ -392,6 +411,10 @@ impl Hooks for Settings {
}

impl Hooks for MonoRepoPackage {
fn get_shell(&self) -> Option<&HookShell> {
None
}

fn bump_profiles(&self) -> &HashMap<String, BumpProfile> {
&self.bump_profiles
}
Expand Down
1 change: 1 addition & 0 deletions tests/cog_tests/verify.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fs;
#[cfg(target_family = "unix")]
use std::os::unix::fs::PermissionsExt;
use std::process::Command;

Expand Down

0 comments on commit f20b3cc

Please sign in to comment.