Skip to content

Commit

Permalink
feat(commit): add gitsign support
Browse files Browse the repository at this point in the history
Due to gitsign relying on oauth 2 browser redirection
this feature has no automated test

Closes cocogitto#226
  • Loading branch information
oknozor committed Mar 8, 2024
1 parent 093306a commit 56a8f32
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 3 deletions.
36 changes: 33 additions & 3 deletions src/git/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ impl Repository {
let signature = if self.ssh_sign() {
let program = self.ssh_program();
ssh_sign_string(program, key, &commit_as_str)?
} else if self.x509_sign() {
let program = self.gpg_x509_program();
let user = sig.email().ok_or(Git2Error::MissingEmailInSignature)?;
x509_gitsign(program, user, &commit_as_str)?
} else {
let program = self.gpg_program();
gpg_sign_string(program, key, &commit_as_str)?
Expand All @@ -86,6 +90,32 @@ impl Repository {
}
}

fn x509_gitsign(program: String, user: &str, content: &str) -> Result<String, Git2Error> {
let mut child = Command::new(program);
child.args(["--armor", "-b", "-s", "-u", user]);

let mut child = child
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("error calling gpg command, is gpg installed ?");

{
let stdin = child.stdin.as_mut().unwrap();
stdin.write_all(content.as_bytes())?;
}

child.wait_with_output().map(|output| {
if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).to_string())
} else {
Err(Git2Error::GpgError(
String::from_utf8_lossy(&output.stderr).to_string(),
))
}
})?
}

fn gpg_sign_string(
program: String,
key: Option<String>,
Expand Down Expand Up @@ -140,9 +170,9 @@ fn ssh_sign_string(
let buffer_ref = buffer.into_temp_path();
child.args([
"-f",
signing_key_ref.to_str().unwrap(),
buffer_ref.to_str().unwrap(),
]); // TODO: Is there a way to avoid unwrap here ?
signing_key_ref.to_string_lossy().as_ref(),
buffer_ref.to_string_lossy().as_ref(),
]);

child
.stdin(Stdio::null())
Expand Down
4 changes: 4 additions & 0 deletions src/git/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub enum Git2Error {
InvalidCommitRangePattern(String),
SshProgramError(String),
SshError(String),
MissingEmailInSignature,
UnknownRevision(String),
}

Expand Down Expand Up @@ -129,6 +130,9 @@ impl Display for Git2Error {
Git2Error::InvalidCommitRangePattern(pattern) => {
writeln!(f, "invalid commit range pattern: `{pattern}`")
}
Git2Error::MissingEmailInSignature => {
writeln!(f, "`user.email` is required to sign commit with gitsign")
}
Git2Error::SshProgramError(_) => {
writeln!(f, "there was a problem while executing the ssh program")
}
Expand Down
18 changes: 18 additions & 0 deletions src/git/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ impl Repository {
config.get_bool("commit.gpgSign").unwrap_or(false)
}

pub(crate) fn gpg_x509_program(&self) -> String {
let config = self.0.config().expect("failed to retrieve gitconfig");
config
.get_string("gpg.x509.program")
.unwrap_or("gpg".to_string())
}

pub(crate) fn gpg_program(&self) -> String {
let config = self.0.config().expect("failed to retrieve gitconfig");
config
Expand All @@ -44,6 +51,17 @@ impl Repository {
.unwrap_or("ssh-keygen".to_string())
}

pub(crate) fn x509_sign(&self) -> bool {
let config = self.0.config().expect("failed to retrieve gitconfig");
if config.get_bool("commit.gpgSign").is_err() {
return false;
}

config
.get_string("gpg.format")
.is_ok_and(|s| s.to_lowercase() == "x509")
}

pub(crate) fn init<S: AsRef<Path> + ?Sized>(path: &S) -> Result<Repository, Git2Error> {
let repository =
Git2Repository::init(path).map_err(Git2Error::FailedToInitializeRepository)?;
Expand Down

0 comments on commit 56a8f32

Please sign in to comment.