mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
* 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>
75 lines
2.8 KiB
TypeScript
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)
|
|
})
|
|
})
|