mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-23 03:26:36 +03:00
Refactor pr templates to use front end persisted store
This commit is contained in:
parent
e7e8ffdec2
commit
f2878d8915
@ -1,24 +0,0 @@
|
||||
import { invoke } from './ipc';
|
||||
|
||||
export type ForgeType = 'github' | 'gitlab' | 'bitbucket' | 'azure';
|
||||
|
||||
export class ForgeService {
|
||||
constructor(private projectId: string) {}
|
||||
|
||||
async getAvailableReviewTemplates(): Promise<string[]> {
|
||||
const templates = await invoke<string[]>('get_available_review_templates', {
|
||||
projectId: this.projectId
|
||||
});
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
||||
async getReviewTemplateContent(templatePath: string): Promise<string> {
|
||||
const fileContents: string = await invoke('get_review_template_contents', {
|
||||
relativePath: templatePath,
|
||||
projectId: this.projectId
|
||||
});
|
||||
|
||||
return fileContents;
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ import { persisted } from '@gitbutler/shared/persisted';
|
||||
import { open } from '@tauri-apps/plugin-dialog';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
import { derived, get, writable, type Readable } from 'svelte/store';
|
||||
import type { ForgeType } from './forge';
|
||||
import type { HttpClient } from '@gitbutler/shared/httpClient';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
@ -16,8 +15,6 @@ export type LocalKey = {
|
||||
|
||||
export type Key = Exclude<KeyType, 'local'> | LocalKey;
|
||||
|
||||
export type HostType = { type: ForgeType };
|
||||
|
||||
export class Project {
|
||||
id!: string;
|
||||
title!: string;
|
||||
@ -30,11 +27,6 @@ export class Project {
|
||||
use_diff_context: boolean | undefined;
|
||||
snapshot_lines_threshold!: number | undefined;
|
||||
use_experimental_locking!: boolean;
|
||||
git_host!: {
|
||||
hostType: HostType | undefined;
|
||||
reviewTemplatePath: string | undefined;
|
||||
};
|
||||
|
||||
// Produced just for the frontend to determine if the project is open in any window.
|
||||
is_open!: boolean;
|
||||
|
||||
@ -220,14 +212,6 @@ export class ProjectsService {
|
||||
async getCloudProject(repositoryId: string): Promise<CloudProject> {
|
||||
return await this.httpClient.get(`projects/${repositoryId}.json`);
|
||||
}
|
||||
|
||||
async setForgeType(project: Project, type: ForgeType) {
|
||||
if (project.git_host.hostType?.type === type) return;
|
||||
const hostType: HostType = { type };
|
||||
const forge = { hostType };
|
||||
await invoke('update_project_git_host', { projectId: project.id, forge });
|
||||
this.reload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
20
apps/desktop/src/lib/backend/templateService.ts
Normal file
20
apps/desktop/src/lib/backend/templateService.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { invoke } from './ipc';
|
||||
|
||||
export class TemplateService {
|
||||
constructor(private projectId: string) {}
|
||||
|
||||
async getAvailable(forgeName: string): Promise<string[]> {
|
||||
return await invoke<string[]>('get_available_review_templates', {
|
||||
projectId: this.projectId,
|
||||
forge: { name: forgeName }
|
||||
});
|
||||
}
|
||||
|
||||
async getContent(forgeName: string, templatePath: string): Promise<string> {
|
||||
return await invoke('get_review_template_contents', {
|
||||
relativePath: templatePath,
|
||||
projectId: this.projectId,
|
||||
forge: { name: forgeName }
|
||||
});
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
import { AzureBranch } from './azureBranch';
|
||||
import type { ForgeType } from '$lib/backend/forge';
|
||||
import type { RepoInfo } from '$lib/url/gitUrl';
|
||||
import type { Forge } from '../interface/forge';
|
||||
import type { Forge, ForgeName } from '../interface/forge';
|
||||
import type { ForgeArguments } from '../interface/types';
|
||||
|
||||
export const AZURE_DOMAIN = 'dev.azure.com';
|
||||
@ -13,7 +12,7 @@ export const AZURE_DOMAIN = 'dev.azure.com';
|
||||
* https://github.com/gitbutlerapp/gitbutler/issues/2651
|
||||
*/
|
||||
export class AzureDevOps implements Forge {
|
||||
readonly type: ForgeType = 'azure';
|
||||
readonly name: ForgeName = 'azure';
|
||||
private baseUrl: string;
|
||||
private repo: RepoInfo;
|
||||
private baseBranch: string;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { BitBucketBranch } from './bitbucketBranch';
|
||||
import type { ForgeType } from '$lib/backend/forge';
|
||||
import type { RepoInfo } from '$lib/url/gitUrl';
|
||||
import type { Forge } from '../interface/forge';
|
||||
import type { Forge, ForgeName } from '../interface/forge';
|
||||
import type { DetailedPullRequest, ForgeArguments } from '../interface/types';
|
||||
|
||||
export type PrAction = 'creating_pr';
|
||||
@ -17,7 +16,7 @@ export const BITBUCKET_DOMAIN = 'bitbucket.org';
|
||||
* https://github.com/gitbutlerapp/gitbutler/issues/3252
|
||||
*/
|
||||
export class BitBucket implements Forge {
|
||||
readonly type: ForgeType = 'bitbucket';
|
||||
readonly name: ForgeName = 'bitbucket';
|
||||
private baseUrl: string;
|
||||
private repo: RepoInfo;
|
||||
private baseBranch: string;
|
||||
|
@ -4,16 +4,15 @@ import { GitHubListingService } from './githubListingService';
|
||||
import { GitHubPrService } from './githubPrService';
|
||||
import { GitHubIssueService } from '$lib/forge/github/issueService';
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import type { ForgeType } from '$lib/backend/forge';
|
||||
import type { ProjectMetrics } from '$lib/metrics/projectMetrics';
|
||||
import type { RepoInfo } from '$lib/url/gitUrl';
|
||||
import type { Forge } from '../interface/forge';
|
||||
import type { Forge, ForgeName } from '../interface/forge';
|
||||
import type { ForgeArguments } from '../interface/types';
|
||||
|
||||
export const GITHUB_DOMAIN = 'github.com';
|
||||
|
||||
export class GitHub implements Forge {
|
||||
readonly type: ForgeType = 'github';
|
||||
readonly name: ForgeName = 'github';
|
||||
private baseUrl: string;
|
||||
private repo: RepoInfo;
|
||||
private baseBranch: string;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { GitLabBranch } from './gitlabBranch';
|
||||
import type { ForgeType } from '$lib/backend/forge';
|
||||
import type { RepoInfo } from '$lib/url/gitUrl';
|
||||
import type { Forge } from '../interface/forge';
|
||||
import type { Forge, ForgeName } from '../interface/forge';
|
||||
import type { DetailedPullRequest, ForgeArguments } from '../interface/types';
|
||||
|
||||
export type PrAction = 'creating_pr';
|
||||
@ -18,7 +17,7 @@ export const GITLAB_SUB_DOMAIN = 'gitlab'; // For self hosted instance of Gitlab
|
||||
* https://github.com/gitbutlerapp/gitbutler/issues/2511
|
||||
*/
|
||||
export class GitLab implements Forge {
|
||||
readonly type: ForgeType = 'gitlab';
|
||||
readonly name: ForgeName = 'gitlab';
|
||||
private baseUrl: string;
|
||||
private repo: RepoInfo;
|
||||
private baseBranch: string;
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { buildContextStore } from '@gitbutler/shared/context';
|
||||
import type { ForgeType } from '$lib/backend/forge';
|
||||
import type { ForgeIssueService } from '$lib/forge/interface/forgeIssueService';
|
||||
import type { ForgeBranch } from './forgeBranch';
|
||||
import type { ForgeChecksMonitor } from './forgeChecksMonitor';
|
||||
import type { ForgeListingService } from './forgeListingService';
|
||||
import type { ForgePrService } from './forgePrService';
|
||||
|
||||
export type ForgeName = 'github' | 'gitlab' | 'bitbucket' | 'azure';
|
||||
|
||||
export interface Forge {
|
||||
readonly type: ForgeType;
|
||||
readonly name: ForgeName;
|
||||
// Lists PRs for the repo.
|
||||
listService(): ForgeListingService | undefined;
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
import { getPreferredPRAction, PRAction } from './pr';
|
||||
import { AIService } from '$lib/ai/service';
|
||||
import { Project } from '$lib/backend/projects';
|
||||
import { TemplateService } from '$lib/backend/templateService';
|
||||
import { BaseBranch } from '$lib/baseBranch/baseBranch';
|
||||
import Markdown from '$lib/components/Markdown.svelte';
|
||||
import ContextMenuItem from '$lib/components/contextmenu/ContextMenuItem.svelte';
|
||||
@ -32,6 +33,7 @@
|
||||
import { BranchController } from '$lib/vbranches/branchController';
|
||||
import { PatchSeries, VirtualBranch } from '$lib/vbranches/types';
|
||||
import { getContext, getContextStore } from '@gitbutler/shared/context';
|
||||
import { persisted } from '@gitbutler/shared/persisted';
|
||||
import Button from '@gitbutler/ui/Button.svelte';
|
||||
import Modal from '@gitbutler/ui/Modal.svelte';
|
||||
import Textarea from '@gitbutler/ui/Textarea.svelte';
|
||||
@ -71,6 +73,7 @@
|
||||
const aiService = getContext(AIService);
|
||||
const aiGenEnabled = projectAiGenEnabled(project.id);
|
||||
const forge = getForge();
|
||||
const templateService = getContext(TemplateService);
|
||||
const preferredPRAction = getPreferredPRAction();
|
||||
|
||||
const branch = $derived($branchStore);
|
||||
@ -99,10 +102,16 @@
|
||||
let showAiBox = $state<boolean>(false);
|
||||
let pushBeforeCreate = $state(false);
|
||||
|
||||
// Displays template select component when true.
|
||||
let useTemplate = persisted(false, `use-template-${project.id}`);
|
||||
// Available pull request templates.
|
||||
let templates = $state<string[]>([]);
|
||||
|
||||
async function handleToggleUseTemplate() {
|
||||
if (!templateSelector) return;
|
||||
const displaying = templateSelector.imports.showing;
|
||||
await templateSelector.setUsePullRequestTemplate(!displaying);
|
||||
useTemplate.set(!$useTemplate);
|
||||
if (!$useTemplate) {
|
||||
pullRequestTemplateBody = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const canUseAI = $derived.by(() => {
|
||||
@ -145,6 +154,11 @@
|
||||
aiService.validateConfiguration().then((valid) => {
|
||||
aiConfigurationValid = valid;
|
||||
});
|
||||
if ($forge) {
|
||||
templateService.getAvailable($forge.name).then((availableTemplates) => {
|
||||
templates = availableTemplates;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -363,9 +377,9 @@
|
||||
<ToggleButton
|
||||
icon="doc"
|
||||
label="Use PR template"
|
||||
checked={!!templateSelector?.imports.showing}
|
||||
checked={$useTemplate}
|
||||
onclick={handleToggleUseTemplate}
|
||||
disabled={!templateSelector?.imports.hasTemplates}
|
||||
disabled={templates.length === 0}
|
||||
/>
|
||||
<ToggleButton
|
||||
icon="ai-small"
|
||||
@ -380,7 +394,13 @@
|
||||
</div>
|
||||
|
||||
<!-- PR TEMPLATE SELECT -->
|
||||
<PrTemplateSection bind:this={templateSelector} bind:pullRequestTemplateBody />
|
||||
{#if $useTemplate}
|
||||
<PrTemplateSection
|
||||
bind:this={templateSelector}
|
||||
onselected={(body) => (pullRequestTemplateBody = body)}
|
||||
{templates}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- DESCRIPTION FIELD -->
|
||||
<div class="pr-description-field text-input">
|
||||
|
@ -1,106 +1,75 @@
|
||||
<script lang="ts">
|
||||
import { ForgeService } from '$lib/backend/forge';
|
||||
import { ProjectService, ProjectsService } from '$lib/backend/projects';
|
||||
import { Project } from '$lib/backend/projects';
|
||||
import { TemplateService } from '$lib/backend/templateService';
|
||||
import { getForge } from '$lib/forge/interface/forge';
|
||||
import Select from '$lib/select/Select.svelte';
|
||||
import SelectItem from '$lib/select/SelectItem.svelte';
|
||||
import { getContext } from '@gitbutler/shared/context';
|
||||
import { persisted } from '@gitbutler/shared/persisted';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
pullRequestTemplateBody: string | undefined;
|
||||
templates: string[];
|
||||
onselected: (body: string) => void;
|
||||
}
|
||||
|
||||
let { pullRequestTemplateBody = $bindable() }: Props = $props();
|
||||
let { templates, onselected }: Props = $props();
|
||||
|
||||
const projectsService = getContext(ProjectsService);
|
||||
const projectService = getContext(ProjectService);
|
||||
const forgeService = getContext(ForgeService);
|
||||
const forge = getForge();
|
||||
// TODO: Rename or refactor this service.
|
||||
const templateService = getContext(TemplateService);
|
||||
const project = getContext(Project);
|
||||
|
||||
let allAvailableTemplates = $state<{ label: string; value: string }[]>([]);
|
||||
// The last template that was used. It is used as default if it is in the
|
||||
// list of available commits.
|
||||
const lastTemplate = persisted<string | undefined>(undefined, `last-template-${project.id}`);
|
||||
|
||||
const projectStore = projectService.project;
|
||||
const project = $derived($projectStore);
|
||||
const reviewTemplatePath = $derived(project?.git_host.reviewTemplatePath);
|
||||
const show = $derived(!!reviewTemplatePath);
|
||||
async function setTemplate(path: string) {
|
||||
if ($forge) {
|
||||
lastTemplate.set(path);
|
||||
loadAndEmit(path);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch PR template content
|
||||
$effect(() => {
|
||||
if (!project) return;
|
||||
if (reviewTemplatePath) {
|
||||
forgeService.getReviewTemplateContent(reviewTemplatePath).then((template) => {
|
||||
pullRequestTemplateBody = template;
|
||||
});
|
||||
async function loadAndEmit(path: string) {
|
||||
if (path && $forge) {
|
||||
const template = await templateService.getContent($forge.name, path);
|
||||
if (template) {
|
||||
onselected(template);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if ($lastTemplate && templates.includes($lastTemplate)) {
|
||||
loadAndEmit($lastTemplate);
|
||||
} else if (templates.length === 1) {
|
||||
const path = templates.at(0);
|
||||
if (path) {
|
||||
loadAndEmit(path);
|
||||
lastTemplate.set(path);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Fetch available PR templates
|
||||
$effect(() => {
|
||||
if (!project) return;
|
||||
forgeService.getAvailableReviewTemplates().then((availableTemplates) => {
|
||||
if (availableTemplates) {
|
||||
allAvailableTemplates = availableTemplates.map((availableTemplate) => {
|
||||
return {
|
||||
label: availableTemplate,
|
||||
value: availableTemplate
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
async function setPullRequestTemplatePath(value: string) {
|
||||
if (!project) return;
|
||||
project.git_host.reviewTemplatePath = value;
|
||||
await projectsService.updateProject(project);
|
||||
}
|
||||
|
||||
export async function setUsePullRequestTemplate(value: boolean) {
|
||||
if (!project) return;
|
||||
|
||||
setTemplate: {
|
||||
if (!value) {
|
||||
project.git_host.reviewTemplatePath = undefined;
|
||||
pullRequestTemplateBody = undefined;
|
||||
break setTemplate;
|
||||
}
|
||||
|
||||
if (allAvailableTemplates[0]) {
|
||||
project.git_host.reviewTemplatePath = allAvailableTemplates[0].value;
|
||||
break setTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
await projectsService.updateProject(project);
|
||||
}
|
||||
|
||||
export const imports = {
|
||||
get showing() {
|
||||
return show;
|
||||
},
|
||||
get hasTemplates() {
|
||||
return allAvailableTemplates.length > 0;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if show}
|
||||
<div class="pr-template__wrap">
|
||||
<Select
|
||||
value={reviewTemplatePath}
|
||||
options={allAvailableTemplates.map(({ label, value }) => ({ label, value }))}
|
||||
placeholder="No PR templates found ¯\_(ツ)_/¯"
|
||||
flex="1"
|
||||
searchable
|
||||
disabled={allAvailableTemplates.length <= 1}
|
||||
onselect={setPullRequestTemplatePath}
|
||||
>
|
||||
{#snippet itemSnippet({ item, highlighted })}
|
||||
<SelectItem selected={item.value === reviewTemplatePath} {highlighted}>
|
||||
{item.label}
|
||||
</SelectItem>
|
||||
{/snippet}
|
||||
</Select>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="pr-template__wrap">
|
||||
<Select
|
||||
value={$lastTemplate}
|
||||
options={templates.map((value) => ({ label: value, value }))}
|
||||
placeholder={templates.length > 0 ? 'Choose template' : 'No PR templates found ¯_(ツ)_/¯'}
|
||||
flex="1"
|
||||
searchable
|
||||
disabled={templates.length === 0}
|
||||
onselect={setTemplate}
|
||||
>
|
||||
{#snippet itemSnippet({ item, highlighted })}
|
||||
<SelectItem selected={item.value === $lastTemplate} {highlighted}>
|
||||
{item.label}
|
||||
</SelectItem>
|
||||
{/snippet}
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.pr-template__wrap {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { ForgeService } from '$lib/backend/forge';
|
||||
import { Project, ProjectService } from '$lib/backend/projects';
|
||||
import { TemplateService } from '$lib/backend/templateService';
|
||||
import FileMenuAction from '$lib/barmenuActions/FileMenuAction.svelte';
|
||||
import ProjectSettingsMenuAction from '$lib/barmenuActions/ProjectSettingsMenuAction.svelte';
|
||||
import { BaseBranch, NoDefaultTarget } from '$lib/baseBranch/baseBranch';
|
||||
@ -68,7 +68,7 @@
|
||||
setContext(BranchController, data.branchController);
|
||||
setContext(BaseBranchService, data.baseBranchService);
|
||||
setContext(CommitService, data.commitService);
|
||||
setContext(ForgeService, data.forgeService);
|
||||
setContext(TemplateService, data.templateService);
|
||||
setContext(BaseBranch, baseBranch);
|
||||
setContext(Project, project);
|
||||
setContext(BranchDragActionsFactory, data.branchDragActionsFactory);
|
||||
@ -140,11 +140,7 @@
|
||||
repoInfo && baseBranchName
|
||||
? forgeFactory.build(repoInfo, baseBranchName, forkInfo)
|
||||
: undefined;
|
||||
|
||||
const ghListService = forge?.listService();
|
||||
|
||||
if (forge) projectsService.setForgeType(project, forge.type);
|
||||
|
||||
listServiceStore.set(ghListService);
|
||||
forgeStore.set(forge);
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ForgeService } from '$lib/backend/forge';
|
||||
import { getUserErrorCode, invoke } from '$lib/backend/ipc';
|
||||
import { ProjectService, type Project } from '$lib/backend/projects';
|
||||
import { TemplateService } from '$lib/backend/templateService';
|
||||
import { BaseBranchService } from '$lib/baseBranch/baseBranchService';
|
||||
import { CloudBranchCreationService } from '$lib/branch/cloudBranchCreationService';
|
||||
import { BranchListingService } from '$lib/branches/branchListing';
|
||||
@ -59,7 +59,7 @@ export const load: LayoutLoad = async ({ params, parent }) => {
|
||||
const historyService = new HistoryService(projectId);
|
||||
const baseBranchService = new BaseBranchService(projectId);
|
||||
const commitService = new CommitService(projectId);
|
||||
const forgeService = new ForgeService(projectId);
|
||||
const templateService = new TemplateService(projectId);
|
||||
|
||||
const branchListingService = new BranchListingService(projectId);
|
||||
const remoteBranchService = new RemoteBranchService(
|
||||
@ -107,7 +107,7 @@ export const load: LayoutLoad = async ({ params, parent }) => {
|
||||
authService,
|
||||
baseBranchService,
|
||||
commitService,
|
||||
forgeService,
|
||||
templateService,
|
||||
branchController,
|
||||
historyService,
|
||||
projectId,
|
||||
|
@ -1,9 +1,9 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||
#[serde(tag = "type", rename_all = "lowercase")]
|
||||
#[serde(tag = "name", rename_all = "lowercase")]
|
||||
/// Supported git forge types
|
||||
pub enum ForgeType {
|
||||
pub enum ForgeName {
|
||||
GitHub,
|
||||
GitLab,
|
||||
Bitbucket,
|
||||
|
@ -2,17 +2,19 @@ use std::path::{self, Path};
|
||||
|
||||
use gitbutler_fs::list_files;
|
||||
|
||||
use crate::forge::ForgeType;
|
||||
use crate::forge::ForgeName;
|
||||
|
||||
/// Get a list of available review template paths for a project
|
||||
///
|
||||
/// The paths are relative to the root path
|
||||
pub fn available_review_templates(root_path: &path::Path, forge_type: &ForgeType) -> Vec<String> {
|
||||
pub fn available_review_templates(root_path: &path::Path, forge_name: &ForgeName) -> Vec<String> {
|
||||
dbg!(&forge_name);
|
||||
dbg!(&root_path);
|
||||
let ReviewTemplateFunctions {
|
||||
is_review_template,
|
||||
get_root,
|
||||
..
|
||||
} = get_review_template_functions(forge_type);
|
||||
} = get_review_template_functions(forge_name);
|
||||
|
||||
let forge_root_path = get_root(root_path);
|
||||
let forge_root_path = forge_root_path.as_path();
|
||||
@ -46,24 +48,24 @@ pub struct ReviewTemplateFunctions {
|
||||
pub is_valid_review_template_path: fn(&path::Path, &path::Path) -> bool,
|
||||
}
|
||||
|
||||
pub fn get_review_template_functions(forge_type: &ForgeType) -> ReviewTemplateFunctions {
|
||||
match forge_type {
|
||||
ForgeType::GitHub => ReviewTemplateFunctions {
|
||||
pub fn get_review_template_functions(forge_name: &ForgeName) -> ReviewTemplateFunctions {
|
||||
match forge_name {
|
||||
ForgeName::GitHub => ReviewTemplateFunctions {
|
||||
is_review_template: is_review_template_github,
|
||||
get_root: get_github_directory_path,
|
||||
is_valid_review_template_path: is_valid_review_template_path_github,
|
||||
},
|
||||
ForgeType::GitLab => ReviewTemplateFunctions {
|
||||
ForgeName::GitLab => ReviewTemplateFunctions {
|
||||
is_review_template: is_review_template_gitlab,
|
||||
get_root: get_gitlab_directory_path,
|
||||
is_valid_review_template_path: is_valid_review_template_path_gitlab,
|
||||
},
|
||||
ForgeType::Bitbucket => ReviewTemplateFunctions {
|
||||
ForgeName::Bitbucket => ReviewTemplateFunctions {
|
||||
is_review_template: is_review_template_bitbucket,
|
||||
get_root: get_bitbucket_directory_path,
|
||||
is_valid_review_template_path: is_valid_review_template_path_bitbucket,
|
||||
},
|
||||
ForgeType::Azure => ReviewTemplateFunctions {
|
||||
ForgeName::Azure => ReviewTemplateFunctions {
|
||||
is_review_template: is_review_template_azure,
|
||||
get_root: get_azure_directory_path,
|
||||
is_valid_review_template_path: is_valid_review_template_path_azure,
|
||||
|
@ -5,9 +5,7 @@ mod project;
|
||||
mod storage;
|
||||
|
||||
pub use controller::Controller;
|
||||
pub use project::{
|
||||
ApiProject, AuthKey, CodePushState, FetchResult, ForgeSettings, Project, ProjectId,
|
||||
};
|
||||
pub use project::{ApiProject, AuthKey, CodePushState, FetchResult, Project, ProjectId};
|
||||
pub use storage::UpdateRequest;
|
||||
|
||||
/// A utility to be used from applications to optimize `git2` configuration.
|
||||
|
@ -1,9 +1,8 @@
|
||||
use std::{
|
||||
path::{self, Path, PathBuf},
|
||||
path::{self, PathBuf},
|
||||
time,
|
||||
};
|
||||
|
||||
use gitbutler_forge::{forge::ForgeType, review::available_review_templates};
|
||||
use gitbutler_id::id::Id;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -95,8 +94,6 @@ pub struct Project {
|
||||
pub omit_certificate_check: Option<bool>,
|
||||
// The number of changed lines that will trigger a snapshot
|
||||
pub snapshot_lines_threshold: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub git_host: ForgeSettings,
|
||||
// Experimental flag for new hunk dependency algorithm
|
||||
#[serde(default = "default_true")]
|
||||
pub use_experimental_locking: bool,
|
||||
@ -107,27 +104,6 @@ fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ForgeSettings {
|
||||
#[serde(default)]
|
||||
pub host_type: Option<ForgeType>,
|
||||
#[serde(default)]
|
||||
pub review_template_path: Option<String>,
|
||||
}
|
||||
|
||||
impl ForgeSettings {
|
||||
pub fn init(&mut self, project_path: &Path) {
|
||||
if let Some(forge_type) = &self.host_type {
|
||||
if self.review_template_path.is_none() {
|
||||
self.review_template_path = available_review_templates(project_path, forge_type)
|
||||
.first()
|
||||
.cloned();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Project {
|
||||
/// Determines if the project Operations log will be synched with the GitButHub
|
||||
pub fn oplog_sync_enabled(&self) -> bool {
|
||||
|
@ -3,7 +3,7 @@ use std::path::PathBuf;
|
||||
use anyhow::{Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ApiProject, AuthKey, CodePushState, FetchResult, ForgeSettings, Project, ProjectId};
|
||||
use crate::{ApiProject, AuthKey, CodePushState, FetchResult, Project, ProjectId};
|
||||
|
||||
const PROJECTS_FILE: &str = "projects.json";
|
||||
|
||||
@ -27,7 +27,6 @@ pub struct UpdateRequest {
|
||||
pub omit_certificate_check: Option<bool>,
|
||||
pub use_diff_context: Option<bool>,
|
||||
pub snapshot_lines_threshold: Option<usize>,
|
||||
pub git_host: Option<ForgeSettings>,
|
||||
pub use_experimental_locking: Option<bool>,
|
||||
}
|
||||
|
||||
@ -125,10 +124,6 @@ impl Storage {
|
||||
project.snapshot_lines_threshold = Some(snapshot_lines_threshold);
|
||||
}
|
||||
|
||||
if let Some(git_host) = &update_request.git_host {
|
||||
project.git_host = git_host.clone();
|
||||
}
|
||||
|
||||
if let Some(use_experimental_locking) = &update_request.use_experimental_locking {
|
||||
project.use_experimental_locking = *use_experimental_locking;
|
||||
}
|
||||
|
@ -2,8 +2,11 @@ pub mod commands {
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Context;
|
||||
use gitbutler_forge::review::{
|
||||
available_review_templates, get_review_template_functions, ReviewTemplateFunctions,
|
||||
use gitbutler_forge::{
|
||||
forge::ForgeName,
|
||||
review::{
|
||||
available_review_templates, get_review_template_functions, ReviewTemplateFunctions,
|
||||
},
|
||||
};
|
||||
use gitbutler_project::{Controller, ProjectId};
|
||||
use gitbutler_repo::RepoCommands;
|
||||
@ -17,15 +20,10 @@ pub mod commands {
|
||||
pub fn get_available_review_templates(
|
||||
projects: State<'_, Controller>,
|
||||
project_id: ProjectId,
|
||||
forge: ForgeName,
|
||||
) -> Result<Vec<String>, Error> {
|
||||
let project = projects.get_validated(project_id)?;
|
||||
let root_path = &project.path;
|
||||
let forge_type = project.git_host.host_type;
|
||||
|
||||
let review_templates = forge_type
|
||||
.map(|forge_type| available_review_templates(root_path, &forge_type))
|
||||
.unwrap_or_default();
|
||||
Ok(review_templates)
|
||||
Ok(available_review_templates(&project.path, &forge))
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
@ -34,26 +32,24 @@ pub mod commands {
|
||||
projects: State<'_, Controller>,
|
||||
project_id: ProjectId,
|
||||
relative_path: &Path,
|
||||
) -> Result<String, Error> {
|
||||
forge: ForgeName,
|
||||
) -> anyhow::Result<String, Error> {
|
||||
let project = projects.get_validated(project_id)?;
|
||||
let forge_type = project
|
||||
.git_host
|
||||
.host_type
|
||||
.clone()
|
||||
.context("Project does not have a forge type")?;
|
||||
|
||||
let ReviewTemplateFunctions {
|
||||
is_valid_review_template_path,
|
||||
..
|
||||
} = get_review_template_functions(&forge_type);
|
||||
} = get_review_template_functions(&forge);
|
||||
|
||||
if !is_valid_review_template_path(relative_path, &project.path) {
|
||||
return Err(anyhow::anyhow!("Invalid review template path").into());
|
||||
return Err(anyhow::format_err!(
|
||||
"Invalid review template path: {:?}",
|
||||
Path::join(&project.path, relative_path)
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let file_info = project.read_file_from_workspace(None, relative_path)?;
|
||||
|
||||
Ok(file_info
|
||||
Ok(project
|
||||
.read_file_from_workspace(None, relative_path)?
|
||||
.content
|
||||
.context("PR template was not valid UTF-8")?)
|
||||
}
|
||||
|
@ -151,7 +151,6 @@ fn main() {
|
||||
projects::commands::list_projects,
|
||||
projects::commands::set_project_active,
|
||||
projects::commands::open_project_in_window,
|
||||
projects::commands::update_project_git_host,
|
||||
repo::commands::git_get_local_config,
|
||||
repo::commands::git_set_local_config,
|
||||
repo::commands::check_signing_settings,
|
||||
|
@ -4,9 +4,7 @@ pub mod commands {
|
||||
use std::path;
|
||||
|
||||
use anyhow::Context;
|
||||
use gitbutler_project::{
|
||||
self as projects, Controller, ForgeSettings, ProjectId, UpdateRequest,
|
||||
};
|
||||
use gitbutler_project::{self as projects, Controller, ProjectId};
|
||||
use tauri::{State, Window};
|
||||
use tracing::instrument;
|
||||
|
||||
@ -21,26 +19,6 @@ pub mod commands {
|
||||
Ok(projects.update(&project)?)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[instrument(skip(projects), err(Debug))]
|
||||
pub fn update_project_git_host(
|
||||
projects: State<'_, Controller>,
|
||||
project_id: ProjectId,
|
||||
git_host: ForgeSettings,
|
||||
) -> Result<projects::Project, Error> {
|
||||
let project = projects.get_validated(project_id)?;
|
||||
let root_path = &project.path;
|
||||
let mut git_host = git_host.clone();
|
||||
git_host.init(root_path);
|
||||
|
||||
let request = UpdateRequest {
|
||||
id: project_id,
|
||||
git_host: Some(git_host),
|
||||
..Default::default()
|
||||
};
|
||||
Ok(projects.update(&request)?)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[instrument(skip(projects), err(Debug))]
|
||||
pub fn add_project(
|
||||
|
Loading…
Reference in New Issue
Block a user