git: Intercept signing prompt from GPG when committing (#34096)

Closes #30111 

- [x] basic implementation
- [x] implementation for remote projects
- [x] surface error output from GPG if signing fails
- [ ] ~~Windows~~

Release Notes:

- git: Passphrase prompts from GPG to unlock commit signing keys are now
shown in Zed.
This commit is contained in:
Cole Miller 2025-07-10 20:38:51 -04:00 committed by GitHub
parent 87362c602f
commit 842ac984d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 193 additions and 51 deletions

View file

@ -1726,6 +1726,18 @@ impl GitStore {
let repository_id = RepositoryId::from_proto(envelope.payload.repository_id);
let repository_handle = Self::repository_for_request(&this, repository_id, &mut cx)?;
let askpass = if let Some(askpass_id) = envelope.payload.askpass_id {
make_remote_delegate(
this,
envelope.payload.project_id,
repository_id,
askpass_id,
&mut cx,
)
} else {
AskPassDelegate::new_always_failing()
};
let message = SharedString::from(envelope.payload.message);
let name = envelope.payload.name.map(SharedString::from);
let email = envelope.payload.email.map(SharedString::from);
@ -1739,6 +1751,7 @@ impl GitStore {
CommitOptions {
amend: options.amend,
},
askpass,
cx,
)
})?
@ -3462,11 +3475,14 @@ impl Repository {
message: SharedString,
name_and_email: Option<(SharedString, SharedString)>,
options: CommitOptions,
askpass: AskPassDelegate,
_cx: &mut App,
) -> oneshot::Receiver<Result<()>> {
let id = self.id;
let askpass_delegates = self.askpass_delegates.clone();
let askpass_id = util::post_inc(&mut self.latest_askpass_id);
self.send_job(Some("git commit".into()), move |git_repo, _cx| async move {
self.send_job(Some("git commit".into()), move |git_repo, cx| async move {
match git_repo {
RepositoryState::Local {
backend,
@ -3474,10 +3490,16 @@ impl Repository {
..
} => {
backend
.commit(message, name_and_email, options, environment)
.commit(message, name_and_email, options, askpass, environment, cx)
.await
}
RepositoryState::Remote { project_id, client } => {
askpass_delegates.lock().insert(askpass_id, askpass);
let _defer = util::defer(|| {
let askpass_delegate = askpass_delegates.lock().remove(&askpass_id);
debug_assert!(askpass_delegate.is_some());
});
let (name, email) = name_and_email.unzip();
client
.request(proto::Commit {
@ -3489,9 +3511,9 @@ impl Repository {
options: Some(proto::commit::CommitOptions {
amend: options.amend,
}),
askpass_id: Some(askpass_id),
})
.await
.context("sending commit request")?;
.await?;
Ok(())
}