mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-23 20:54:50 +03:00
ForgeType
Set the type of repository forge type (e.g. GitHub, GitLab) once the base branch is determined. Get the available templates with the updated method
This commit is contained in:
parent
4ee01031f6
commit
d41e271e99
15
apps/desktop/src/lib/backend/forge.ts
Normal file
15
apps/desktop/src/lib/backend/forge.ts
Normal file
@ -0,0 +1,15 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import { persisted } from '@gitbutler/shared/persisted';
|
||||
import { open } from '@tauri-apps/api/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';
|
||||
|
||||
@ -15,6 +16,8 @@ export type LocalKey = {
|
||||
|
||||
export type Key = Exclude<KeyType, 'local'> | LocalKey;
|
||||
|
||||
export type HostType = { type: ForgeType };
|
||||
|
||||
export class Project {
|
||||
id!: string;
|
||||
title!: string;
|
||||
@ -28,8 +31,8 @@ export class Project {
|
||||
snapshot_lines_threshold!: number | undefined;
|
||||
use_new_locking!: boolean;
|
||||
git_host!: {
|
||||
hostType: 'github' | 'gitlab' | 'bitbucket' | 'azure';
|
||||
pullRequestTemplatePath: string;
|
||||
hostType: HostType | undefined;
|
||||
reviewTemplatePath: string | undefined;
|
||||
};
|
||||
|
||||
// Produced just for the frontend to determine if the project is open in any window.
|
||||
@ -217,6 +220,14 @@ export class ProjectsService {
|
||||
async getCloudProject(repositoryId: string): Promise<CloudProject> {
|
||||
return await this.httpClient.get(`projects/${repositoryId}.json`);
|
||||
}
|
||||
|
||||
async setGitHostType(project: Project, type: ForgeType) {
|
||||
if (project.git_host.hostType?.type === type) return;
|
||||
const hostType: HostType = { type };
|
||||
const gitHost = { hostType };
|
||||
await invoke('update_project_git_host', { projectId: project.id, gitHost });
|
||||
this.reload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { AzureBranch } from './azureBranch';
|
||||
import type { ForgeType } from '$lib/backend/forge';
|
||||
import type { RepoInfo } from '$lib/url/gitUrl';
|
||||
import type { GitHost } from '../interface/gitHost';
|
||||
import type { GitHostArguments } from '../interface/types';
|
||||
@ -12,6 +13,7 @@ export const AZURE_DOMAIN = 'dev.azure.com';
|
||||
* https://github.com/gitbutlerapp/gitbutler/issues/2651
|
||||
*/
|
||||
export class AzureDevOps implements GitHost {
|
||||
readonly type: ForgeType = 'azure';
|
||||
private baseUrl: string;
|
||||
private repo: RepoInfo;
|
||||
private baseBranch: string;
|
||||
@ -48,11 +50,6 @@ export class AzureDevOps implements GitHost {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async availablePullRequestTemplates(_path?: string) {
|
||||
// See: https://learn.microsoft.com/en-us/azure/devops/repos/git/pull-request-templates?view=azure-devops#default-pull-request-templates
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async pullRequestTemplateContent(_path?: string) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { BitBucketBranch } from './bitbucketBranch';
|
||||
import type { ForgeType } from '$lib/backend/forge';
|
||||
import type { RepoInfo } from '$lib/url/gitUrl';
|
||||
import type { GitHost } from '../interface/gitHost';
|
||||
import type { DetailedPullRequest, GitHostArguments } from '../interface/types';
|
||||
@ -16,6 +17,7 @@ export const BITBUCKET_DOMAIN = 'bitbucket.org';
|
||||
* https://github.com/gitbutlerapp/gitbutler/issues/3252
|
||||
*/
|
||||
export class BitBucket implements GitHost {
|
||||
readonly type: ForgeType = 'bitbucket';
|
||||
private baseUrl: string;
|
||||
private repo: RepoInfo;
|
||||
private baseBranch: string;
|
||||
@ -52,11 +54,6 @@ export class BitBucket implements GitHost {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async availablePullRequestTemplates(_path?: string) {
|
||||
// See: https://confluence.atlassian.com/bitbucketserver/create-a-pull-request-808488431.html#Createapullrequest-templatePullrequestdescriptiontemplates
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async pullRequestTemplateContent(_path?: string) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import { GitHubListingService } from './githubListingService';
|
||||
import { GitHubPrService } from './githubPrService';
|
||||
import { GitHubIssueService } from '$lib/gitHost/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 { GitHost } from '../interface/gitHost';
|
||||
@ -12,6 +13,7 @@ import type { GitHostArguments } from '../interface/types';
|
||||
export const GITHUB_DOMAIN = 'github.com';
|
||||
|
||||
export class GitHub implements GitHost {
|
||||
readonly type: ForgeType = 'github';
|
||||
private baseUrl: string;
|
||||
private repo: RepoInfo;
|
||||
private baseBranch: string;
|
||||
|
@ -107,18 +107,4 @@ export class GitHubPrService implements GitHostPrService {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async availablePullRequestTemplates(path: string): Promise<string[] | undefined> {
|
||||
// TODO: Find a workaround to avoid this dynamic import
|
||||
// https://github.com/sveltejs/kit/issues/905
|
||||
const { join } = await import('@tauri-apps/api/path');
|
||||
const targetPath = await join(path, '.github');
|
||||
|
||||
const availableTemplates: string[] | undefined = await invoke(
|
||||
'available_pull_request_templates',
|
||||
{ rootPath: targetPath }
|
||||
);
|
||||
|
||||
return availableTemplates;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { GitLabBranch } from './gitlabBranch';
|
||||
import type { ForgeType } from '$lib/backend/forge';
|
||||
import type { RepoInfo } from '$lib/url/gitUrl';
|
||||
import type { GitHost } from '../interface/gitHost';
|
||||
import type { DetailedPullRequest, GitHostArguments } from '../interface/types';
|
||||
@ -17,6 +18,7 @@ export const GITLAB_SUB_DOMAIN = 'gitlab'; // For self hosted instance of Gitlab
|
||||
* https://github.com/gitbutlerapp/gitbutler/issues/2511
|
||||
*/
|
||||
export class GitLab implements GitHost {
|
||||
readonly type: ForgeType = 'gitlab';
|
||||
private baseUrl: string;
|
||||
private repo: RepoInfo;
|
||||
private baseBranch: string;
|
||||
@ -53,11 +55,6 @@ export class GitLab implements GitHost {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async availablePullRequestTemplates(_path?: string) {
|
||||
// See: https://docs.gitlab.com/ee/user/project/description_templates.html
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async pullRequestTemplateContent(_path?: string) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { buildContextStore } from '@gitbutler/shared/context';
|
||||
import type { ForgeType } from '$lib/backend/forge';
|
||||
import type { GitHostIssueService } from '$lib/gitHost/interface/gitHostIssueService';
|
||||
import type { GitHostBranch } from './gitHostBranch';
|
||||
import type { GitHostChecksMonitor } from './gitHostChecksMonitor';
|
||||
@ -6,6 +7,7 @@ import type { GitHostListingService } from './gitHostListingService';
|
||||
import type { GitHostPrService } from './gitHostPrService';
|
||||
|
||||
export interface GitHost {
|
||||
readonly type: ForgeType;
|
||||
// Lists PRs for the repo.
|
||||
listService(): GitHostListingService | undefined;
|
||||
|
||||
|
@ -17,7 +17,6 @@ export interface GitHostPrService {
|
||||
baseBranchName,
|
||||
upstreamName
|
||||
}: CreatePullRequestArgs): Promise<PullRequest>;
|
||||
availablePullRequestTemplates(path: string): Promise<string[] | undefined>;
|
||||
pullRequestTemplateContent(path: string, projectId: string): Promise<string | undefined>;
|
||||
merge(method: MergeMethod, prNumber: number): Promise<void>;
|
||||
prMonitor(prNumber: number): GitHostPrMonitor;
|
||||
|
@ -82,7 +82,7 @@
|
||||
props.type === 'preview-series' ? props.upstreamName : branch.upstreamName
|
||||
);
|
||||
const baseBranchName = $derived($baseBranch.shortName);
|
||||
const prTemplatePath = $derived(project.git_host.pullRequestTemplatePath);
|
||||
const prTemplatePath = $derived(project.git_host.reviewTemplatePath);
|
||||
let isDraft = $state<boolean>($preferredPRAction === PRAction.CreateDraft);
|
||||
|
||||
let modal = $state<ReturnType<typeof Modal>>();
|
||||
|
@ -1,9 +1,8 @@
|
||||
<script lang="ts">
|
||||
import notFoundSvg from '$lib/assets/empty-state/not-found.svg?raw';
|
||||
import { Project, ProjectsService } from '$lib/backend/projects';
|
||||
import { ForgeService } from '$lib/backend/forge';
|
||||
import { ProjectService, ProjectsService } from '$lib/backend/projects';
|
||||
import SectionCard from '$lib/components/SectionCard.svelte';
|
||||
import { getGitHost } from '$lib/gitHost/interface/gitHost';
|
||||
import { createGitHostPrServiceStore } from '$lib/gitHost/interface/gitHostPrService';
|
||||
import Select from '$lib/select/Select.svelte';
|
||||
import SelectItem from '$lib/select/SelectItem.svelte';
|
||||
import Section from '$lib/settings/Section.svelte';
|
||||
@ -14,25 +13,27 @@
|
||||
import EmptyStatePlaceholder from '@gitbutler/ui/EmptyStatePlaceholder.svelte';
|
||||
|
||||
const projectsService = getContext(ProjectsService);
|
||||
const project = getContext(Project);
|
||||
const gitHost = getGitHost();
|
||||
const prService = createGitHostPrServiceStore(undefined);
|
||||
$effect(() => prService.set($gitHost?.prService()));
|
||||
const projectService = getContext(ProjectService);
|
||||
const forgeService = getContext(ForgeService);
|
||||
|
||||
let useTemplate = $state(!!project.git_host?.pullRequestTemplatePath);
|
||||
let selectedTemplate = $state(project.git_host?.pullRequestTemplatePath ?? '');
|
||||
const projectStore = projectService.project;
|
||||
const project = $derived($projectStore);
|
||||
const useTemplate = $derived(!!project?.git_host.reviewTemplatePath);
|
||||
const selectedTemplate = $derived(project?.git_host.reviewTemplatePath);
|
||||
let allAvailableTemplates = $state<{ label: string; value: string }[]>([]);
|
||||
let isTemplatesAvailable = $state(false);
|
||||
|
||||
$effect(() => {
|
||||
if (!project.path) return;
|
||||
$prService?.availablePullRequestTemplates(project.path).then((availableTemplates) => {
|
||||
if (!project) {
|
||||
return;
|
||||
}
|
||||
|
||||
forgeService.getAvailableReviewTemplates().then((availableTemplates) => {
|
||||
if (availableTemplates) {
|
||||
allAvailableTemplates = availableTemplates.map((availableTemplate) => {
|
||||
const relativePath = availableTemplate.replace(`${project.path}/`, '');
|
||||
return {
|
||||
label: relativePath,
|
||||
value: relativePath
|
||||
label: availableTemplate,
|
||||
value: availableTemplate
|
||||
};
|
||||
});
|
||||
|
||||
@ -42,15 +43,26 @@
|
||||
});
|
||||
|
||||
async function setUsePullRequestTemplate(value: boolean) {
|
||||
if (!value) {
|
||||
project.git_host.pullRequestTemplatePath = '';
|
||||
if (!project) return;
|
||||
|
||||
setTemplate: {
|
||||
if (!value) {
|
||||
project.git_host.reviewTemplatePath = undefined;
|
||||
break setTemplate;
|
||||
}
|
||||
|
||||
if (allAvailableTemplates[0]) {
|
||||
project.git_host.reviewTemplatePath = allAvailableTemplates[0].value;
|
||||
break setTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
await projectsService.updateProject(project);
|
||||
}
|
||||
|
||||
async function setPullRequestTemplatePath(value: string) {
|
||||
selectedTemplate = value;
|
||||
project.git_host.pullRequestTemplatePath = value;
|
||||
if (!project) return;
|
||||
project.git_host.reviewTemplatePath = value;
|
||||
await projectsService.updateProject(project);
|
||||
}
|
||||
</script>
|
||||
@ -66,7 +78,8 @@
|
||||
<svelte:fragment slot="actions">
|
||||
<Toggle
|
||||
id="use-pull-request-template-boolean"
|
||||
bind:checked={useTemplate}
|
||||
checked={useTemplate}
|
||||
disabled={!isTemplatesAvailable}
|
||||
on:click={(e) => {
|
||||
setUsePullRequestTemplate(
|
||||
(e.target as MouseEvent['target'] & { checked: boolean }).checked
|
||||
|
@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { ForgeService } from '$lib/backend/forge';
|
||||
import { Project, ProjectService } from '$lib/backend/projects';
|
||||
import FileMenuAction from '$lib/barmenuActions/FileMenuAction.svelte';
|
||||
import ProjectSettingsMenuAction from '$lib/barmenuActions/ProjectSettingsMenuAction.svelte';
|
||||
@ -71,6 +72,7 @@
|
||||
setContext(BranchController, data.branchController);
|
||||
setContext(BaseBranchService, data.baseBranchService);
|
||||
setContext(CommitService, data.commitService);
|
||||
setContext(ForgeService, data.forgeService);
|
||||
setContext(BaseBranch, baseBranch);
|
||||
setContext(Project, project);
|
||||
setContext(BranchDragActionsFactory, data.branchDragActionsFactory);
|
||||
@ -146,6 +148,8 @@
|
||||
|
||||
const ghListService = gitHost?.listService();
|
||||
|
||||
if (gitHost) projectsService.setGitHostType(project, gitHost.type);
|
||||
|
||||
listServiceStore.set(ghListService);
|
||||
gitHostStore.set(gitHost);
|
||||
});
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ForgeService } from '$lib/backend/forge';
|
||||
import { getUserErrorCode, invoke } from '$lib/backend/ipc';
|
||||
import { ProjectService, type Project } from '$lib/backend/projects';
|
||||
import { BaseBranchService } from '$lib/baseBranch/baseBranchService';
|
||||
@ -61,6 +62,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 branchListingService = new BranchListingService(projectId);
|
||||
const remoteBranchService = new RemoteBranchService(
|
||||
@ -108,6 +110,7 @@ export const load: LayoutLoad = async ({ params, parent }) => {
|
||||
authService,
|
||||
baseBranchService,
|
||||
commitService,
|
||||
forgeService,
|
||||
branchController,
|
||||
historyService,
|
||||
projectId,
|
||||
|
Loading…
Reference in New Issue
Block a user