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

Add support for GPG signing commits #219

Closed
wants to merge 4 commits into from
Closed
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
20 changes: 20 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ jobs:
profile: minimal
components: clippy

- name: Install GnuPG on Ubuntu
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get -qq install libgpg-error-dev libgpgme11-dev

- name: Install GnuPG on MacOS
if: matrix.os == 'macos-latest'
run: brew install gpgme

- name: Install GnuPG on Windows
if: matrix.os == 'windows-latest'
run: |
$path = [Environment]::GetEnvironmentVariable("path", "machine")
$newPath = ($path.Split(';') | Where-Object { $_ -eq 'C:\ProgramData\chocolatey\bin' }) -join ';'
[Environment]::SetEnvironmentVariable("path", $newPath, "machine")
choco install gpg4win
refreshenv

- name: Build Debug
run: |
rustc --version
Expand Down Expand Up @@ -58,6 +75,9 @@ jobs:
profile: minimal
target: x86_64-unknown-linux-musl

- name: Install GnuPG
run: sudo apt-get -qq install libgpg-error-dev libgpgme11-dev

- name: Setup MUSL
run: |
sudo apt-get -qq install musl-tools
Expand Down
84 changes: 84 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions asyncgit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ rayon-core = "1.7"
crossbeam-channel = "0.4"
log = "0.4"
thiserror = "1.0"
gpg-error = "0.5.1"
gpgme = "0.9.2"

[dev-dependencies]
tempfile = "3.1"
Expand Down
3 changes: 3 additions & 0 deletions asyncgit/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub enum Error {

#[error("git error:{0}")]
Git(#[from] git2::Error),

#[error("gpg error:#{0}")]
Gpg(#[from] gpg_error::Error),
}

pub type Result<T> = std::result::Result<T, Error>;
Expand Down
70 changes: 67 additions & 3 deletions asyncgit/src/sync/commit.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::{get_head, utils::repo, CommitId};
use crate::error::Result;
use git2::{ErrorCode, ObjectType, Repository, Signature};
use gpgme::{Context, Protocol};
use scopetime::scope_time;

///
Expand Down Expand Up @@ -54,6 +55,7 @@ pub fn commit(repo_path: &str, msg: &str) -> Result<CommitId> {
scope_time!("commit");

let repo = repo(repo_path)?;
let config = repo.config()?;

let signature = signature_allow_undefined_name(&repo)?;
let mut index = repo.index()?;
Expand All @@ -68,16 +70,78 @@ pub fn commit(repo_path: &str, msg: &str) -> Result<CommitId> {

let parents = parents.iter().collect::<Vec<_>>();

Ok(repo
.commit(
let commit_oid = if config
.get_bool("commit.gpgsign")
.unwrap_or(false)
{
// Generate commit content
let commit_bufffer = repo.commit_create_buffer(
&signature,
&signature,
msg,
&tree,
parents.as_slice(),
)?;
let commit_content = commit_bufffer
.as_str()
.expect("Buffer was not valid UTF-8");

// Prepare to sign using the designated key in the user's git config
let mut gpg_ctx = Context::from_protocol(Protocol::OpenPgp)?;
if let Ok(key_id) = config.get_string("user.signingkey") {
let key = gpg_ctx.get_key(key_id)?;
gpg_ctx.add_signer(&key)?;
}
gpg_ctx.set_armor(true);

// Create GPG signature for commit content
let mut signature_buffer = Vec::new();
gpg_ctx
.sign_detached(&*commit_bufffer, &mut signature_buffer)?;
let gpg_signature = std::str::from_utf8(&signature_buffer)
.expect("Buffer was not valid UTF-8");

let commit_oid = repo.commit_signed(
&commit_content,
&gpg_signature,
None,
)?;

match repo.head() {
// If HEAD reference is returned, simply update the target.
Ok(mut head) => {
head.set_target(commit_oid, msg)?;
}
// If there is an error getting HEAD, likely it is a new repo
// and a reference to a default branch needs to be created.
Err(_) => {
// Default branch name behavior as of git 2.28.
let default_branch_name = config
.get_str("init.defaultBranch")
.unwrap_or("master");

repo.reference(
&format!("refs/heads/{}", default_branch_name),
commit_oid,
true,
msg,
)?;
}
}

commit_oid
} else {
repo.commit(
Some("HEAD"),
&signature,
&signature,
msg,
&tree,
parents.as_slice(),
)?
.into())
};

Ok(commit_oid.into())
}

/// Tag a commit.
Expand Down