Skip to content

Commit ee1bcd1

Browse files
authored
add e2e test covering gpg (#2986)
1 parent 7a397b0 commit ee1bcd1

1 file changed

Lines changed: 121 additions & 0 deletions

File tree

asyncgit/src/sync/sign.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,4 +743,125 @@ mod tests {
743743
std::env::remove_var("GNUPGHOME");
744744
Ok(())
745745
}
746+
747+
/// e2e openpgp signing: generate a throwaway `gpg` key, sign a real
748+
/// commit and verify it. Serial + unix-only: uses a process-wide `GNUPGHOME`.
749+
#[cfg(unix)]
750+
#[test]
751+
#[serial]
752+
fn test_openpgp_sign_and_verify_e2e() -> Result<()> {
753+
use std::os::unix::fs::PermissionsExt;
754+
use std::process::Command;
755+
756+
fn tool_available(bin: &str) -> bool {
757+
Command::new(bin)
758+
.arg("--version")
759+
.stdout(std::process::Stdio::null())
760+
.stderr(std::process::Stdio::null())
761+
.status()
762+
.map(|s| s.success())
763+
.unwrap_or(false)
764+
}
765+
766+
assert!(
767+
tool_available("gpg"),
768+
"gpg is required for the openpgp e2e test"
769+
);
770+
771+
let email = "gitui-openpgp-test@example.com";
772+
let gnupg = tempfile::tempdir()?;
773+
let home = gnupg.path();
774+
std::fs::set_permissions(
775+
home,
776+
std::fs::Permissions::from_mode(0o700),
777+
)?;
778+
779+
// GPGSign inherits env, so point the child gpg at our keyring.
780+
std::env::set_var("GNUPGHOME", home);
781+
782+
let run = |program: &str, args: &[&str]| {
783+
let out = Command::new(program)
784+
.args(args)
785+
.env("GNUPGHOME", home)
786+
.output()
787+
.unwrap_or_else(|e| {
788+
panic!("failed to run {program}: {e}")
789+
});
790+
assert!(
791+
out.status.success(),
792+
"{program} {args:?} failed: {}",
793+
String::from_utf8_lossy(&out.stderr)
794+
);
795+
out
796+
};
797+
798+
// unattended keygen: %no-protection => no passphrase, so no pinentry
799+
// and no agent trust dance are needed (unlike the x509/gpgsm path).
800+
let params = home.join("keyparams");
801+
std::fs::write(
802+
&params,
803+
format!(
804+
"%no-protection\nKey-Type: RSA\nKey-Length: 2048\nSubkey-Type: RSA\nSubkey-Length: 2048\nName-Real: gitui test\nName-Email: {email}\nExpire-Date: 0\n%commit\n"
805+
),
806+
)?;
807+
run(
808+
"gpg",
809+
&["--batch", "--gen-key", params.to_str().unwrap()],
810+
);
811+
812+
let (_tmp_dir, repo) = repo_init_empty()?;
813+
{
814+
let mut config = repo.config()?;
815+
config.set_str("gpg.format", "openpgp")?;
816+
config.set_str("user.signingKey", email)?;
817+
}
818+
let signer =
819+
SignBuilder::from_gitconfig(&repo, &repo.config()?)?;
820+
assert_eq!("gpg", signer.program());
821+
822+
let sig = git2::Signature::now("gitui test", email)?;
823+
let tree = {
824+
let mut index = repo.index()?;
825+
let tree_id = index.write_tree()?;
826+
repo.find_tree(tree_id)?
827+
};
828+
let commit_id = create_signed_commit(
829+
&repo,
830+
&*signer,
831+
&sig,
832+
&sig,
833+
"openpgp signed commit",
834+
&tree,
835+
&[],
836+
)?;
837+
838+
let (signature, signed_data) =
839+
repo.extract_signature(&commit_id, None)?;
840+
let signature = std::str::from_utf8(&signature).unwrap();
841+
assert!(
842+
signature.contains("BEGIN PGP SIGNATURE"),
843+
"expected an armored OpenPGP signature, got: {signature}"
844+
);
845+
846+
let sig_file = home.join("commit.sig");
847+
let data_file = home.join("commit.data");
848+
std::fs::write(&sig_file, signature)?;
849+
std::fs::write(&data_file, &*signed_data)?;
850+
let verify = run(
851+
"gpg",
852+
&[
853+
"--verify",
854+
sig_file.to_str().unwrap(),
855+
data_file.to_str().unwrap(),
856+
],
857+
);
858+
let verify_err = String::from_utf8_lossy(&verify.stderr);
859+
assert!(
860+
verify_err.contains("Good signature"),
861+
"gpg did not accept the signature: {verify_err}"
862+
);
863+
864+
std::env::remove_var("GNUPGHOME");
865+
Ok(())
866+
}
746867
}

0 commit comments

Comments
 (0)