Files
orca/src/shared/git-clone-failure-message.test.ts
T
b7e84aea3c Fix automatic branch rename for non-English git locales (#8012)
* Fix automatic branch rename for non-English git locales

A gettext-enabled git (Homebrew git, most Linux distro gits) under a
non-English locale translates every diagnostic, including the `fatal:`
prefix, so Orca's stderr phrase parsers stop matching. The first-message
branch auto-rename was the headline casualty: isNoUpstreamError missed
the translated no-upstream error, branchHasUpstream failed closed to
"has upstream", and the rename settled silently and permanently.

- Force LC_ALL=C on all Orca-spawned machine-parsed git: the local
  prompt-guard env chokepoint, the three relay git spawn sites, and both
  local clone spawns (progress + failure-message parsing). User
  terminals are untouched.
- Replace the boolean upstream check with a tri-state probe: rename
  proceeds only on a proven missing upstream; an unreadable probe now
  raises the rename-failed badge and retries instead of settling.

Fixes #7808

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>

* Consolidate untranslated-git-locale into runner and relay primitives

Replace the five per-site LC_ALL=C patches with one shared
UNTRANSLATED_GIT_OUTPUT_ENV (LANGUAGE=en LC_ALL=en_US.UTF-8
LANG=en_US.UTF-8) injected inside the git runner primitives
(promptGuardGitEnv, gitSpawn, gitExecFileSync, gitExecFileAsyncBuffer)
and a relay buildRelayGitEnv() helper, so every current and future
machine-parsed git spawn is covered by construction — including the
fs-handler-git-fallback sites the per-site approach missed. The UTF-8
English locale keeps a UTF-8 LC_CTYPE for hooks git spawns; LANGUAGE is
pinned because gettext consults it before LC_ALL.

WSL-routed git gets the same values as a shell assignment prefix built
in resolveCommand, since spawn env cannot cross the wsl.exe boundary —
closing the WSL gap the first pass accepted.

Also scrub credential-bearing remote URLs from the probe-failed message
surfaced on the worktree card.

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>

* Scrub credential-bearing URLs from clone failure messages

---------

Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-10 01:03:16 -07:00

75 lines
2.8 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from 'vitest'
import { getGitCloneFailureMessage } from './git-clone-failure-message'
afterEach(() => {
vi.restoreAllMocks()
})
describe('getGitCloneFailureMessage', () => {
it('turns an existing destination into an actionable message after progress output', () => {
expect(
getGitCloneFailureMessage(
[
'Cloning into \u001b[32morca\u001b[0m...\r',
"fatal: destination path 'orca' already exists and is not an empty directory.\n"
].join(''),
{ clonePath: '/work/orca' }
)
).toBe(
'Destination already exists and is not empty: /work/orca. Choose a different parent folder, delete the existing folder, or add the existing repository instead.'
)
})
it('prefers the last fatal line over a trailing fragment', () => {
expect(
getGitCloneFailureMessage(
"fatal: destination path 'orca' already exists and is not an empty directory.\r\nand the repository exists.\n"
)
).toBe(
'Destination already exists and is not empty: orca. Choose a different parent folder, delete the existing folder, or add the existing repository instead.'
)
})
it('uses the known clone path for relay destination fragments', () => {
expect(
getGitCloneFailureMessage('Clone failed: and the repository exists.', {
clonePath: '/srv/orca'
})
).toBe(
'Destination already exists and is not empty: /srv/orca. Choose a different parent folder, delete the existing folder, or add the existing repository instead.'
)
})
it('falls back to the last non-empty line', () => {
expect(getGitCloneFailureMessage('warning: retrying\nnetwork vanished\n')).toBe(
'network vanished'
)
})
it('scrubs credential-bearing clone URLs before surfacing the fatal line', () => {
// Clone errors echo the URL the user typed — the most likely git error to
// embed a live token — and the message reaches dialogs and bug reports.
const stderr =
'Cloning into repo...\n' +
"fatal: repository 'https://user:ghp_secret123@github.com/org/repo.git/' not found\n"
expect(getGitCloneFailureMessage(stderr)).toBe(
"fatal: repository 'https://github.com/org/repo.git/' not found"
)
})
it('summarizes CRLF-heavy stderr without line-array splitting', () => {
const splitSpy = vi.spyOn(String.prototype, 'split')
const stderr = `${'remote: counting objects\r\n'.repeat(10_000)}fatal: repository not found\r\n`
expect(getGitCloneFailureMessage(stderr)).toBe('fatal: repository not found')
const usedLineSplit = splitSpy.mock.calls.some(
([separator]) =>
(typeof separator === 'string' && separator === '\n') ||
(separator instanceof RegExp && separator.source === '\\r?\\n')
)
expect(usedLineSplit).toBe(false)
})
})