mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-19 15:41:31 +03:00
Refactor credential check
- show tests while running (with indicator) - incremental progress update
This commit is contained in:
parent
653c831471
commit
6137d6b63a
@ -2,40 +2,33 @@ import { invoke } from './ipc';
|
||||
|
||||
export type GitCredentialCheck = {
|
||||
error?: string;
|
||||
name?: string;
|
||||
ok: boolean;
|
||||
};
|
||||
|
||||
export type CredentialCheckError = {
|
||||
check: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export class AuthService {
|
||||
async checkGitFetch(projectId: string, remoteName: string | null | undefined) {
|
||||
if (!remoteName) return { ok: false, error: 'No remote specified' };
|
||||
try {
|
||||
const resp = await invoke<string>('git_test_fetch', {
|
||||
projectId: projectId,
|
||||
remoteName
|
||||
});
|
||||
return { ok: !resp };
|
||||
} catch (err: any) {
|
||||
return { ok: false, error: err.message };
|
||||
}
|
||||
async checkGitFetch(projectId: string, remoteName: string) {
|
||||
const resp = await invoke<string>('git_test_fetch', {
|
||||
projectId: projectId,
|
||||
remoteName
|
||||
});
|
||||
if (resp) throw new Error(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
async checkGitPush(
|
||||
projectId: string,
|
||||
remoteName: string | null | undefined,
|
||||
branchName: string | null | undefined
|
||||
) {
|
||||
if (!remoteName) return { ok: false, error: 'No remote specified' };
|
||||
if (!branchName) return { ok: false, error: 'No branchspecified' };
|
||||
try {
|
||||
const resp = await invoke<string>('git_test_push', {
|
||||
projectId: projectId,
|
||||
remoteName,
|
||||
branchName
|
||||
});
|
||||
return { ok: !resp };
|
||||
} catch (err: any) {
|
||||
return { ok: false, error: err.message };
|
||||
}
|
||||
async checkGitPush(projectId: string, remoteName: string, branchName: string) {
|
||||
const resp = await invoke<string>('git_test_push', {
|
||||
projectId: projectId,
|
||||
remoteName,
|
||||
branchName
|
||||
});
|
||||
if (resp) throw new Error(resp);
|
||||
return { name: 'push', ok: true };
|
||||
}
|
||||
|
||||
async getPublicKey() {
|
||||
|
@ -1,91 +1,122 @@
|
||||
<script lang="ts">
|
||||
import Button from './Button.svelte';
|
||||
import Icon from './Icon.svelte';
|
||||
import InfoMessage, { type MessageStyle } from './InfoMessage.svelte';
|
||||
import Link from './Link.svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
import type { AuthService, GitCredentialCheck } from '$lib/backend/auth';
|
||||
import type { AuthService } from '$lib/backend/auth';
|
||||
|
||||
export let authService: AuthService;
|
||||
export let projectId: string;
|
||||
export let remoteName: string | null | undefined;
|
||||
export let branchName: string | null | undefined;
|
||||
|
||||
let credentialsCheck: any | undefined;
|
||||
let loading = false;
|
||||
$: success = true;
|
||||
$: loading = false;
|
||||
$: errors = 0;
|
||||
$: passes = 0;
|
||||
$: style = checkToStyle(loading, success, errors);
|
||||
|
||||
let fetchError: string | undefined;
|
||||
let pushError: string | undefined;
|
||||
let success = false;
|
||||
type Check = { name: string; promise: Promise<any> };
|
||||
$: checks = [] as Check[];
|
||||
|
||||
$: style = checkToStyle(credentialsCheck);
|
||||
|
||||
function checkToStyle(check: GitCredentialCheck | undefined): MessageStyle {
|
||||
if (check?.ok) return 'success';
|
||||
if (check?.error) return 'warn';
|
||||
function checkToStyle(loading: boolean, success: boolean, errors: number): MessageStyle {
|
||||
if (success) return 'success';
|
||||
if (errors > 0) return 'warn';
|
||||
if (loading) return 'neutral';
|
||||
return 'neutral';
|
||||
}
|
||||
|
||||
const isRejected = (input: PromiseSettledResult<unknown>): input is PromiseRejectedResult =>
|
||||
input.status === 'rejected';
|
||||
|
||||
const isFulfilled = <T,>(
|
||||
input: PromiseSettledResult<unknown>
|
||||
): input is PromiseFulfilledResult<T> => input.status === 'fulfilled';
|
||||
|
||||
async function checkCredentials() {
|
||||
loading = true;
|
||||
success = false;
|
||||
await checkPush();
|
||||
await checkFetch();
|
||||
loading = false;
|
||||
success = true;
|
||||
}
|
||||
passes = 0;
|
||||
errors = 0;
|
||||
|
||||
async function checkFetch() {
|
||||
credentialsCheck = undefined;
|
||||
loading = true;
|
||||
credentialsCheck = await authService.checkGitFetch(projectId, remoteName);
|
||||
if (credentialsCheck.error) {
|
||||
fetchError = credentialsCheck.error;
|
||||
if (!remoteName || !branchName) {
|
||||
return;
|
||||
}
|
||||
loading = false;
|
||||
}
|
||||
|
||||
async function checkPush() {
|
||||
credentialsCheck = undefined;
|
||||
loading = true;
|
||||
credentialsCheck = await authService.checkGitPush(projectId, remoteName, branchName);
|
||||
if (credentialsCheck.error) {
|
||||
pushError = credentialsCheck.error;
|
||||
try {
|
||||
checks = [
|
||||
{
|
||||
name: 'Fetch',
|
||||
promise: authService.checkGitFetch(projectId, remoteName).catch((reason) => {
|
||||
++errors;
|
||||
throw reason;
|
||||
})
|
||||
},
|
||||
{ name: 'Push', promise: authService.checkGitPush(projectId, remoteName, branchName) }
|
||||
];
|
||||
const results = await Promise.allSettled(checks.map((c) => c.promise));
|
||||
errors = results.filter(isRejected).map((r) => `${r.reason}`).length;
|
||||
passes = results.filter(isFulfilled).map((r) => `${r.value}`).length;
|
||||
setTimeout(() => (success = errors == 0), 1250);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
loading = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="credential-check">
|
||||
{#if success || fetchError || pushError}
|
||||
<div transition:slide>
|
||||
{#if checks.length > 0}
|
||||
<div transition:slide={{ duration: 250 }}>
|
||||
<InfoMessage {style} filled outlined={false}>
|
||||
<svelte:fragment slot="title">
|
||||
{#if loading}
|
||||
Checking git credentials …
|
||||
{:else if fetchError}
|
||||
Unable to fetch
|
||||
{:else if pushError}
|
||||
Unable to push
|
||||
{:else if errors > 0}
|
||||
There was a problem with your credentials
|
||||
{:else}
|
||||
All checks passed successfully
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment>
|
||||
{#if fetchError}
|
||||
We were unable to fetch from the remote, please check your authentication settings.
|
||||
<Link href="https://docs.gitbutler.com/troubleshooting/fetch-push">Learn more</Link>.
|
||||
{fetchError}
|
||||
{:else if pushError}
|
||||
We were unable to push to the remote, please check your authentication settings.
|
||||
<Link href="https://docs.gitbutler.com/troubleshooting/fetch-push">Learn more</Link>.
|
||||
{pushError}
|
||||
<svelte:fragment slot="content">
|
||||
{#if loading || (checks.length > 0 && (errors > 0 || (errors == 0 && passes == 0)))}
|
||||
<div class="checks-list" transition:slide={{ duration: 250, delay: 1000 }}>
|
||||
{#each checks as check}
|
||||
<div class="check-result">
|
||||
{#await check.promise}
|
||||
<Icon name="spinner" spinnerRadius={3.5} />
|
||||
{:then}
|
||||
<Icon name="success-small" color="success" />
|
||||
{:catch}
|
||||
<Icon name="error-small" color="error" />
|
||||
{/await}
|
||||
{check.name}
|
||||
{#await check.promise catch err}
|
||||
- {err}
|
||||
{/await}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if errors > 0}
|
||||
<div class="help-text" transition:slide>
|
||||
Try another setting and test again? You can also refer to our
|
||||
<Link href="https://docs.gitbutler.com/troubleshooting/fetch-push">
|
||||
fetch & pull documentation
|
||||
</Link>
|
||||
for help fixing this problem.
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</InfoMessage>
|
||||
</div>
|
||||
{/if}
|
||||
<Button wide icon="test" {loading} disabled={loading} on:click={checkCredentials}>
|
||||
Test credentials
|
||||
<Button wide icon="test" disabled={loading} on:click={checkCredentials}>
|
||||
{#if loading || checks.length == 0}
|
||||
Test credentials
|
||||
{:else}
|
||||
Re-test credentials
|
||||
{/if}
|
||||
</Button>
|
||||
<div class="disclaimer">
|
||||
To test the push command, we create an empty branch and promptly remove it after the check. <Link
|
||||
@ -101,6 +132,21 @@
|
||||
gap: var(--space-16);
|
||||
}
|
||||
|
||||
.checks-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-6);
|
||||
padding-left: var(--space-4);
|
||||
margin-top: var(--space-12);
|
||||
}
|
||||
.check-result {
|
||||
display: flex;
|
||||
gap: var(--space-6);
|
||||
}
|
||||
.help-text {
|
||||
margin-top: var(--space-6);
|
||||
}
|
||||
|
||||
.disclaimer {
|
||||
color: var(--clr-theme-scale-ntrl-50);
|
||||
background: var(--clr-theme-container-pale);
|
||||
|
@ -67,7 +67,11 @@
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="info-message__text text-base-body-12"><slot /></div>
|
||||
{#if SLOTS.content}
|
||||
<slot name="content" />
|
||||
{:else}
|
||||
<div class="info-message__text text-base-body-12"><slot /></div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if primary || secondary}
|
||||
<div class="info-message__actions">
|
||||
|
Loading…
Reference in New Issue
Block a user