Files
GitLite/src/lib/credentials.ts
T
Christoph 2bd60f0e5b feat(git): enhance error reporting and auth robustness
The commit enhances Git operation reliability by improving how authentication errors are detected and displayed to the user. It standardizes Git output language across different locales and introduces a mechanism to summarize complex raw Git stderr messages, ensuring users receive clear feedback when cloning or interacting with repositories that fail due to credentials.

- Standardize git command output using LC_ALL=C for consistent English messaging.
- Implement error summarization logic to extract the most relevant message from raw Git stderr.
- Update credential dialogs to display summarized and improved authentication failure details.
2026-07-19 16:41:21 +02:00

72 lines
2.3 KiB
TypeScript

import type { StoredCredential } from "./types";
/**
* Derives a credential key from a remote URL, scoped to host + organisation —
* the same granularity Azure DevOps / GitHub use. Examples:
* https://github.com/owner/repo.git -> github.com/owner
* https://dev.azure.com/org/project/_git/repo -> dev.azure.com/org
* git@github.com:owner/repo.git -> github.com/owner
* ssh://git@host:2222/owner/repo -> host/owner
* Returns null when nothing usable can be parsed.
*/
export function orgKeyFromUrl(raw: string): string | null {
const url = raw.trim();
if (!url) return null;
let host = "";
let path = "";
// scp-like syntax: user@host:owner/repo.git (no scheme, single colon segment)
const scp = url.match(/^[^@/]+@([^:/]+):(.+)$/);
if (scp && !url.includes("://")) {
host = scp[1];
path = scp[2];
} else {
try {
const parsed = new URL(url);
host = parsed.host; // host:port, without userinfo
path = parsed.pathname;
} catch {
return null;
}
}
host = host.toLowerCase();
const org = path.replace(/^\/+/, "").split("/").filter(Boolean)[0] ?? "";
if (!host) return null;
return org ? `${host}/${org}` : host;
}
/** A stored credential is expired only if it carries a past expiry date. */
export function isCredentialExpired(cred: StoredCredential): boolean {
if (!cred.expiresAt) return false;
const time = new Date(cred.expiresAt).getTime();
return !Number.isNaN(time) && time < Date.now();
}
const AUTH_PREFIX = "AUTH_FAILED:";
export function isAuthError(message: string): boolean {
return message.startsWith(AUTH_PREFIX);
}
export function stripAuthPrefix(message: string): string {
return message.startsWith(AUTH_PREFIX)
? message.slice(AUTH_PREFIX.length).trim()
: message;
}
/**
* Condenses raw git stderr to the single most relevant line for the
* credential dialog. Prefers the server's own explanation ("remote: ...")
* over git's generic wrapper ("fatal: Authentication failed for ...").
*/
export function summarizeGitError(message: string): string {
const lines = message
.split("\n")
.map((line) => line.trim())
.filter(Boolean);
const remote = lines.find((line) => line.toLowerCase().startsWith("remote:"));
return remote ?? lines[0] ?? "";
}