Merge pull request #3725 from gitbutlerapp/global-askpass

make askpass broker a global
This commit is contained in:
Josh Junon 2024-05-07 16:25:59 +02:00 committed by GitHub
commit 190a75eb5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 64 additions and 74 deletions

View File

@ -5,6 +5,31 @@ use tokio::sync::{oneshot, Mutex};
use crate::{id::Id, virtual_branches::BranchId};
static mut GLOBAL_ASKPASS_BROKER: Option<AskpassBroker> = None;
/// Initialize the global askpass broker.
///
/// # Safety
/// This function **must** be called **at least once**, from only one thread at a time,
/// before any other function from this module is called. **Calls to [`get`] before [`init`] will panic.**
///
/// This function is **NOT** thread safe.
pub unsafe fn init(submit_prompt: impl Fn(PromptEvent<Context>) + Send + Sync + 'static) {
GLOBAL_ASKPASS_BROKER.replace(AskpassBroker::init(submit_prompt));
}
/// Get the global askpass broker.
///
/// # Panics
/// Will panic if [`init`] was not called before this function.
pub fn get_broker() -> &'static AskpassBroker {
unsafe {
GLOBAL_ASKPASS_BROKER
.as_ref()
.expect("askpass broker not initialized")
}
}
pub struct AskpassRequest {
sender: oneshot::Sender<Option<String>>,
}

View File

@ -9,9 +9,7 @@ use anyhow::{Context, Result};
use super::conflicts;
use crate::error::{AnyhowContextExt, Code, ErrorWithContext};
use crate::{
askpass,
askpass::AskpassBroker,
error,
askpass, error,
git::{self, credentials::HelpError, Url},
keys,
projects::{self, AuthKey},
@ -171,7 +169,7 @@ impl Repository {
credentials: &git::credentials::Helper,
remote_name: &str,
branch_name: &str,
askpass: Option<(AskpassBroker, Option<BranchId>)>,
askpass: Option<Option<BranchId>>,
) -> Result<()> {
let target_branch_refname =
git::Refname::from_str(&format!("refs/remotes/{}/{}", remote_name, branch_name))?;
@ -184,14 +182,7 @@ impl Repository {
let refname =
git::RemoteRefname::from_str(&format!("refs/remotes/{remote_name}/{branch_name}",))?;
match self.push(
&commit_id,
&refname,
false,
credentials,
None,
askpass.clone(),
) {
match self.push(&commit_id, &refname, false, credentials, None, askpass) {
Ok(()) => Ok(()),
Err(e) => Err(anyhow::anyhow!(e.to_string())),
}?;
@ -446,7 +437,7 @@ impl Repository {
with_force: bool,
credentials: &git::credentials::Helper,
refspec: Option<String>,
askpass_broker: Option<(AskpassBroker, Option<BranchId>)>,
askpass_broker: Option<Option<BranchId>>,
) -> Result<(), RemoteError> {
let refspec = refspec.unwrap_or_else(|| {
if with_force {
@ -545,7 +536,7 @@ impl Repository {
&self,
remote_name: &str,
credentials: &git::credentials::Helper,
askpass: Option<(AskpassBroker, String)>,
askpass: Option<String>,
) -> Result<(), RemoteError> {
let refspec = format!("+refs/heads/*:refs/remotes/{}/*", remote_name);
@ -658,11 +649,11 @@ pub enum LogUntil {
async fn handle_git_prompt_push(
prompt: String,
askpass: Option<(AskpassBroker, Option<BranchId>)>,
askpass: Option<Option<BranchId>>,
) -> Option<String> {
if let Some((askpass_broker, branch_id)) = askpass {
if let Some(branch_id) = askpass {
tracing::info!("received prompt for branch push {branch_id:?}: {prompt:?}");
askpass_broker
askpass::get_broker()
.submit_prompt(prompt, askpass::Context::Push { branch_id })
.await
} else {
@ -671,13 +662,10 @@ async fn handle_git_prompt_push(
}
}
async fn handle_git_prompt_fetch(
prompt: String,
askpass: Option<(AskpassBroker, String)>,
) -> Option<String> {
if let Some((askpass_broker, action)) = askpass {
async fn handle_git_prompt_fetch(prompt: String, askpass: Option<String>) -> Option<String> {
if let Some(action) = askpass {
tracing::info!("received prompt for fetch with action {action:?}: {prompt:?}");
askpass_broker
askpass::get_broker()
.submit_prompt(prompt, askpass::Context::Fetch { action })
.await
} else {

View File

@ -16,7 +16,6 @@ use super::{
target, target_to_base_branch, BaseBranch, RemoteBranchFile, VirtualBranchesHandle,
};
use crate::{
askpass::AskpassBroker,
git, keys, project_repository,
projects::{self, ProjectId},
users,
@ -336,7 +335,7 @@ impl Controller {
project_id: &ProjectId,
branch_id: &BranchId,
with_force: bool,
askpass: Option<(AskpassBroker, Option<BranchId>)>,
askpass: Option<Option<BranchId>>,
) -> Result<(), Error> {
self.inner(project_id)
.await
@ -403,7 +402,7 @@ impl Controller {
pub async fn fetch_from_target(
&self,
project_id: &ProjectId,
askpass: Option<(AskpassBroker, String)>,
askpass: Option<String>,
) -> Result<BaseBranch, Error> {
self.inner(project_id)
.await
@ -923,7 +922,7 @@ impl ControllerInner {
project_id: &ProjectId,
branch_id: &BranchId,
with_force: bool,
askpass: Option<(AskpassBroker, Option<BranchId>)>,
askpass: Option<Option<BranchId>>,
) -> Result<(), Error> {
let _permit = self.semaphore.acquire().await;
let helper = self.helper.clone();
@ -1019,7 +1018,7 @@ impl ControllerInner {
pub async fn fetch_from_target(
&self,
project_id: &ProjectId,
askpass: Option<(AskpassBroker, String)>,
askpass: Option<String>,
) -> Result<BaseBranch, Error> {
let project = self.projects.get(project_id)?;
let mut project_repository = project_repository::Repository::open(&project)?;

View File

@ -27,7 +27,6 @@ use super::{
use crate::git::diff::{diff_files_into_hunks, trees, FileDiff};
use crate::virtual_branches::branch::HunkHash;
use crate::{
askpass::AskpassBroker,
dedup::{dedup, dedup_fmt},
git::{
self,
@ -2441,7 +2440,7 @@ pub fn push(
branch_id: &BranchId,
with_force: bool,
credentials: &git::credentials::Helper,
askpass: Option<(AskpassBroker, Option<BranchId>)>,
askpass: Option<Option<BranchId>>,
) -> Result<(), errors::PushError> {
let vb_state = project_repository.project().virtual_branches();
@ -2505,7 +2504,7 @@ pub fn push(
with_force,
credentials,
None,
askpass.clone(),
askpass,
)?;
vbranch.upstream = Some(remote_branch.clone());
@ -2516,7 +2515,7 @@ pub fn push(
project_repository.fetch(
remote_branch.remote(),
credentials,
askpass.map(|(broker, _)| (broker, "modal".to_string())),
askpass.map(|_| "modal".to_string()),
)?;
Ok(())

View File

@ -3,7 +3,6 @@ use std::{collections::HashMap, path};
use anyhow::{Context, Result};
use gitbutler_core::error::Error as CoreError;
use gitbutler_core::{
askpass::AskpassBroker,
gb_repository, git,
project_repository::{self, conflicts},
projects::{self, ProjectId},
@ -92,7 +91,7 @@ impl App {
remote_name: &str,
branch_name: &str,
credentials: &git::credentials::Helper,
askpass: Option<(AskpassBroker, Option<BranchId>)>,
askpass: Option<Option<BranchId>>,
) -> Result<(), CoreError> {
let project = self.projects.get(project_id)?;
let project_repository = project_repository::Repository::open(&project)?;
@ -104,7 +103,7 @@ impl App {
project_id: &ProjectId,
remote_name: &str,
credentials: &git::credentials::Helper,
askpass: Option<(AskpassBroker, String)>,
askpass: Option<String>,
) -> Result<(), CoreError> {
let project = self.projects.get(project_id)?;
let project_repository = project_repository::Repository::open(&project)?;

View File

@ -1,21 +1,16 @@
pub mod commands {
use gitbutler_core::{
askpass::{AskpassBroker, AskpassRequest},
askpass::{self, AskpassRequest},
id::Id,
};
use tauri::{AppHandle, Manager};
#[tauri::command(async)]
#[tracing::instrument(skip(handle, response))]
#[tracing::instrument(skip(response))]
pub async fn submit_prompt_response(
handle: AppHandle,
id: Id<AskpassRequest>,
response: Option<String>,
) -> Result<(), ()> {
handle
.state::<AskpassBroker>()
.handle_response(id, response)
.await;
askpass::get_broker().handle_response(id, response).await;
Ok(())
}
}

View File

@ -48,16 +48,13 @@ pub async fn git_test_push(
) -> Result<(), Error> {
let app = handle.state::<app::App>();
let helper = handle.state::<gitbutler_core::git::credentials::Helper>();
let askpass_broker = handle
.state::<gitbutler_core::askpass::AskpassBroker>()
.inner()
.clone();
Ok(app.git_test_push(
&project_id,
remote_name,
branch_name,
&helper,
Some((askpass_broker, None)),
// Run askpass, but don't pass any action
Some(None),
)?)
}
@ -71,15 +68,11 @@ pub async fn git_test_fetch(
) -> Result<(), Error> {
let app = handle.state::<app::App>();
let helper = handle.state::<gitbutler_core::git::credentials::Helper>();
let askpass_broker = handle
.state::<gitbutler_core::askpass::AskpassBroker>()
.inner()
.clone();
Ok(app.git_test_fetch(
&project_id,
remote_name,
&helper,
Some((askpass_broker, action.unwrap_or_else(|| "test".to_string()))),
Some(action.unwrap_or_else(|| "test".to_string())),
)?)
}

View File

@ -68,6 +68,17 @@ fn main() {
logs::init(&app_handle);
// SAFETY(qix-): This is safe because we're initializing the askpass broker here,
// SAFETY(qix-): before any other threads would ever access it.
unsafe {
gitbutler_core::askpass::init({
let handle = app_handle.clone();
move |event| {
handle.emit_all("git_prompt", event).expect("tauri event emission doesn't fail in practice")
}
});
}
let app_data_dir = app_handle.path_resolver().app_data_dir().expect("missing app data dir");
let app_cache_dir = app_handle.path_resolver().app_cache_dir().expect("missing app cache dir");
let app_log_dir = app_handle.path_resolver().app_log_dir().expect("missing app log dir");
@ -77,14 +88,6 @@ fn main() {
tracing::info!(version = %app_handle.package_info().version, name = %app_handle.package_info().name, "starting app");
let askpass_broker = gitbutler_core::askpass::AskpassBroker::init({
let handle = app_handle.clone();
move |event| {
handle.emit_all("git_prompt", event).expect("tauri event emission doesn't fail in practice")
}
});
app_handle.manage(askpass_broker);
let storage_controller = storage::Storage::new(&app_data_dir);
app_handle.manage(storage_controller.clone());

View File

@ -2,7 +2,6 @@ pub mod commands {
use crate::error::Error;
use anyhow::Context;
use gitbutler_core::{
askpass::AskpassBroker,
assets,
error::Code,
git, projects,
@ -266,15 +265,9 @@ pub mod commands {
branch_id: BranchId,
with_force: bool,
) -> Result<(), Error> {
let askpass_broker = handle.state::<AskpassBroker>();
handle
.state::<Controller>()
.push_virtual_branch(
&project_id,
&branch_id,
with_force,
Some((askpass_broker.inner().clone(), Some(branch_id))),
)
.push_virtual_branch(&project_id, &branch_id, with_force, Some(Some(branch_id)))
.await
.map_err(|err| err.context(Code::Unknown))?;
emit_vbranches(&handle, &project_id).await;
@ -499,15 +492,11 @@ pub mod commands {
project_id: ProjectId,
action: Option<String>,
) -> Result<BaseBranch, Error> {
let askpass_broker = handle.state::<AskpassBroker>().inner().clone();
let base_branch = handle
.state::<Controller>()
.fetch_from_target(
&project_id,
Some((
askpass_broker,
action.unwrap_or_else(|| "unknown".to_string()),
)),
Some(action.unwrap_or_else(|| "unknown".to_string())),
)
.await?;
emit_vbranches(&handle, &project_id).await;