Merge pull request #3978 from gitbutlerapp/add-git-config-module

support setting signCommits git config
This commit is contained in:
Kiril Videlov 2024-06-03 21:15:14 +02:00 committed by GitHub
commit 0b57a44d63
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 119 additions and 1 deletions

View File

@ -29,6 +29,12 @@ const defaultGitConfig = Object.freeze({
class DummyGitConfigService implements GitConfigService { class DummyGitConfigService implements GitConfigService {
constructor(private config: { [index: string]: string | undefined }) {} constructor(private config: { [index: string]: string | undefined }) {}
async getSignCommitsConfig(_projectId: string): Promise<boolean | undefined> {
throw new Error('Method not implemented.');
}
async setSignCommitsConfig(_projectId: string, _value: boolean): Promise<unknown> {
throw new Error('Method not implemented.');
}
async get<T extends string>(key: string): Promise<T | undefined> { async get<T extends string>(key: string): Promise<T | undefined> {
return (this.config[key] || undefined) as T | undefined; return (this.config[key] || undefined) as T | undefined;

View File

@ -13,4 +13,15 @@ export class GitConfigService {
async set<T extends string>(key: string, value: T) { async set<T extends string>(key: string, value: T) {
return await invoke<T | undefined>('git_set_global_config', { key, value }); return await invoke<T | undefined>('git_set_global_config', { key, value });
} }
// Gets the value of `gitbutler.signCommits`
// Determines if the app should attempt to sign commits using per the git configuration.
async getSignCommitsConfig(projectId: string): Promise<boolean | undefined> {
return await invoke<boolean | undefined>('get_sign_commits_config', { projectId });
}
// Sets the value of `gitbutler.signCommits`
async setSignCommitsConfig(projectId: string, value: boolean) {
return await invoke('set_sign_commits_config', { projectId, value });
}
} }

View File

@ -2,10 +2,12 @@
import SectionCard from './SectionCard.svelte'; import SectionCard from './SectionCard.svelte';
import Spacer from './Spacer.svelte'; import Spacer from './Spacer.svelte';
import TextBox from './TextBox.svelte'; import TextBox from './TextBox.svelte';
import { GitConfigService } from '$lib/backend/gitConfigService';
import { Project, ProjectService } from '$lib/backend/projects'; import { Project, ProjectService } from '$lib/backend/projects';
import Toggle from '$lib/components/Toggle.svelte'; import Toggle from '$lib/components/Toggle.svelte';
import { projectRunCommitHooks } from '$lib/config/config'; import { projectRunCommitHooks } from '$lib/config/config';
import { getContext } from '$lib/utils/context'; import { getContext } from '$lib/utils/context';
import { onMount } from 'svelte';
const projectService = getContext(ProjectService); const projectService = getContext(ProjectService);
const project = getContext(Project); const project = getContext(Project);
@ -14,6 +16,7 @@
let allowForcePushing = project?.ok_with_force_push; let allowForcePushing = project?.ok_with_force_push;
let omitCertificateCheck = project?.omit_certificate_check; let omitCertificateCheck = project?.omit_certificate_check;
const gitConfig = getContext(GitConfigService);
const runCommitHooks = projectRunCommitHooks(project.id); const runCommitHooks = projectRunCommitHooks(project.id);
async function setWithForcePush(value: boolean) { async function setWithForcePush(value: boolean) {
@ -30,6 +33,16 @@
project.snapshot_lines_threshold = value; project.snapshot_lines_threshold = value;
await projectService.updateProject(project); await projectService.updateProject(project);
} }
let signCommits = false;
async function setSignCommits(value: boolean) {
signCommits = value;
await gitConfig.setSignCommitsConfig(project.id, value);
}
onMount(async () => {
signCommits = (await gitConfig.getSignCommitsConfig(project.id)) || false;
});
</script> </script>
<section class="wrapper"> <section class="wrapper">
@ -48,6 +61,20 @@
</svelte:fragment> </svelte:fragment>
</SectionCard> </SectionCard>
<SectionCard orientation="row" labelFor="allowForcePush">
<svelte:fragment slot="title">Sign commits</svelte:fragment>
<svelte:fragment slot="caption">
GitButler will sign commits as per your git configuration.
</svelte:fragment>
<svelte:fragment slot="actions">
<Toggle
id="signCommits"
bind:checked={signCommits}
on:change={async () => await setSignCommits(signCommits)}
/>
</svelte:fragment>
</SectionCard>
<SectionCard orientation="row" labelFor="omitCertificateCheck"> <SectionCard orientation="row" labelFor="omitCertificateCheck">
<svelte:fragment slot="title">Ignore host certificate checks</svelte:fragment> <svelte:fragment slot="title">Ignore host certificate checks</svelte:fragment>
<svelte:fragment slot="caption"> <svelte:fragment slot="caption">

View File

@ -0,0 +1,35 @@
use crate::projects::Project;
use anyhow::Result;
use git2::ConfigLevel;
const CFG_SIGN_COMMITS: &str = "gitbutler.signCommits";
impl Project {
pub fn set_sign_commits(&self, val: bool) -> Result<()> {
self.set_local_bool(CFG_SIGN_COMMITS, val)
}
pub fn sign_commits(&self) -> Result<Option<bool>> {
self.get_bool(CFG_SIGN_COMMITS)
}
fn set_local_bool(&self, key: &str, val: bool) -> Result<()> {
let repo = git2::Repository::open(&self.path)?;
let config = repo.config()?;
match config.open_level(ConfigLevel::Local) {
Ok(mut local) => local.set_bool(key, val).map_err(Into::into),
Err(err) => Err(err.into()),
}
}
fn get_bool(&self, key: &str) -> Result<Option<bool>> {
let repo = git2::Repository::open(&self.path)?;
let config = repo.config()?;
match config.get_bool(key) {
Ok(value) => Ok(Some(value)),
Err(err) => match err.code() {
git2::ErrorCode::NotFound => Ok(None),
_ => Err(err.into()),
},
}
}
}

View File

@ -0,0 +1 @@
pub mod git;

View File

@ -15,6 +15,7 @@
pub mod askpass; pub mod askpass;
pub mod assets; pub mod assets;
pub mod config;
pub mod dedup; pub mod dedup;
pub mod error; pub mod error;
pub mod fs; pub mod fs;

View File

@ -0,0 +1,34 @@
use crate::error::Error;
use anyhow::Context;
use gitbutler_core::projects::{self, ProjectId};
use tauri::Manager;
use tracing::instrument;
#[tauri::command(async)]
#[instrument(skip(handle), err(Debug))]
pub async fn get_sign_commits_config(
handle: tauri::AppHandle,
project_id: ProjectId,
) -> Result<Option<bool>, Error> {
handle
.state::<projects::Controller>()
.get(project_id)
.context("failed to get project")?
.sign_commits()
.map_err(Into::into)
}
#[tauri::command(async)]
#[instrument(skip(handle), err(Debug))]
pub async fn set_sign_commits_config(
handle: tauri::AppHandle,
project_id: ProjectId,
value: bool,
) -> Result<(), Error> {
handle
.state::<projects::Controller>()
.get(project_id)
.context("failed to get project")?
.set_sign_commits(value)
.map_err(Into::into)
}

View File

@ -21,6 +21,7 @@ pub mod menu;
pub mod watcher; pub mod watcher;
pub mod askpass; pub mod askpass;
pub mod config;
pub mod error; pub mod error;
pub mod github; pub mod github;
pub mod keys; pub mod keys;

View File

@ -15,7 +15,7 @@
use gitbutler_core::{assets, git, storage}; use gitbutler_core::{assets, git, storage};
use gitbutler_tauri::{ use gitbutler_tauri::{
app, askpass, commands, github, keys, logs, menu, projects, remotes, undo, users, app, askpass, commands, config, github, keys, logs, menu, projects, remotes, undo, users,
virtual_branches, watcher, zip, virtual_branches, watcher, zip,
}; };
use tauri::{generate_context, Manager}; use tauri::{generate_context, Manager};
@ -209,6 +209,8 @@ fn main() {
undo::list_snapshots, undo::list_snapshots,
undo::restore_snapshot, undo::restore_snapshot,
undo::snapshot_diff, undo::snapshot_diff,
config::get_sign_commits_config,
config::set_sign_commits_config,
menu::menu_item_set_enabled, menu::menu_item_set_enabled,
keys::commands::get_public_key, keys::commands::get_public_key,
github::commands::init_device_oauth, github::commands::init_device_oauth,