diff --git a/src/lib/server/git-error.ts b/src/lib/server/git-error.ts new file mode 100644 index 0000000..4082c6a --- /dev/null +++ b/src/lib/server/git-error.ts @@ -0,0 +1,27 @@ +/** + * Pure git error-message helpers, split out of git.ts so they can be unit-tested + * without importing git.ts's heavy deps (db/docker). No I/O here. + */ + +/** + * The permission-denied message, tailored to the auth in use (#1509). A token/password + * credential paired with an `git@...` SSH URL is the common trap: git uses the SSH + * transport and never applies the token, so it fails with a public-key error - blaming + * "SSH credentials" then is actively misleading. Point at the real cause instead. + */ +export function permissionDeniedMessage( + authType?: string | null, + url?: string | null +): string { + const isSshUrl = !!url && url.startsWith('git@'); + if (authType === 'password') { + if (isSshUrl) { + return 'Permission denied. This credential uses a token, but the repository URL is an SSH URL (git@...). Use an https:// URL for token authentication.'; + } + return 'Permission denied. Check your access token and that it has repository access.'; + } + if (authType === 'ssh') { + return 'Permission denied. Check your SSH key and that it is authorized for this repository.'; + } + return 'Permission denied. Check your credentials.'; +} diff --git a/tests/git-error.test.ts b/tests/git-error.test.ts new file mode 100644 index 0000000..c7a27b0 --- /dev/null +++ b/tests/git-error.test.ts @@ -0,0 +1,35 @@ +import { describe, test, expect } from 'bun:test'; +import { permissionDeniedMessage } from '../src/lib/server/git-error'; + +describe('permissionDeniedMessage (#1509 - auth-aware git error)', () => { + test('token credential on an SSH url points at the URL mismatch, not SSH creds', () => { + const msg = permissionDeniedMessage('password', 'git@github.com:owner/repo.git'); + expect(msg).toContain('token'); + expect(msg).toContain('https://'); + expect(msg).not.toContain('SSH credentials'); + }); + + test('token credential on an https url blames the token, not SSH', () => { + const msg = permissionDeniedMessage('password', 'https://github.com/owner/repo.git'); + expect(msg).toContain('token'); + expect(msg).not.toContain('SSH'); + }); + + test('ssh credential gets an SSH-key-specific message', () => { + const msg = permissionDeniedMessage('ssh', 'git@github.com:owner/repo.git'); + expect(msg).toContain('SSH key'); + }); + + test('unknown/absent auth gets a generic message (no SSH assumption)', () => { + const msg = permissionDeniedMessage(undefined, undefined); + expect(msg).toContain('Permission denied'); + expect(msg).not.toContain('SSH'); + expect(msg).not.toContain('token'); + }); + + test('token with no url still blames the token (not SSH)', () => { + const msg = permissionDeniedMessage('password', null); + expect(msg).toContain('token'); + expect(msg).not.toContain('SSH URL'); + }); +});