mirror of
https://github.com/stablyai/orca.git
synced 2026-10-17 00:03:08 +00:00
Suppress Git Credential Manager OAuth popup on git clone (fixes #7652)
Orca's git runner disables the interactive credential prompt on every git call that goes through gitExecFileAsync/gitStreamStdout, but the two raw 'git clone' spawns (desktop repos:clone and the runtime clone path) passed no env, so they inherited process.env with no guard. On Windows a clone that needs GitHub auth then makes Git Credential Manager pop its 'Connect to GitHub' OAuth window, and in a network-restricted intranet the browser/device flow never completes while git's credential retry re-pops it. Apply nonInteractiveGitEnv() to both clone spawns so the prompt is suppressed (GCM_INTERACTIVE=never, credential.interactive=false, GIT_TERMINAL_PROMPT=0). The credential *helper* is kept, so cached-token clones for private repos still work; only the interactive fallback popup is disabled and the clone fails fast with a clear error instead.
This commit is contained in:
@@ -103,7 +103,14 @@ vi.mock('../git/repo', async () => {
|
||||
|
||||
vi.mock('../git/runner', () => ({
|
||||
gitExecFileAsync: vi.fn(),
|
||||
gitSpawn: gitSpawnMock
|
||||
gitSpawn: gitSpawnMock,
|
||||
// Mirrors the real guard's credential-prompt markers so the clone regression
|
||||
// test (#7652) can assert Git Credential Manager's OAuth popup is disabled.
|
||||
nonInteractiveGitEnv: (env: NodeJS.ProcessEnv = process.env) => ({
|
||||
...env,
|
||||
GIT_TERMINAL_PROMPT: '0',
|
||||
GCM_INTERACTIVE: 'never'
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('../git/worktree', () => ({
|
||||
@@ -2067,6 +2074,28 @@ describe('repos:add + repos:clone', () => {
|
||||
expect(result).toHaveProperty('path', join(destination, 'orca'))
|
||||
})
|
||||
|
||||
it('clones with the non-interactive credential guard so Git Credential Manager cannot pop its OAuth window (#7652)', async () => {
|
||||
const destination = await createTempRoot()
|
||||
|
||||
await handlers.get('repos:clone')!(null, {
|
||||
url: 'https://example.com/orca.git',
|
||||
destination
|
||||
})
|
||||
|
||||
// Without this env, a clone that needs GitHub auth makes Git Credential
|
||||
// Manager pop its "Connect to GitHub" OAuth window on Windows and loop it
|
||||
// when the network cannot complete the flow.
|
||||
expect(gitSpawnMock).toHaveBeenCalledWith(
|
||||
['clone', '--progress', '--', 'https://example.com/orca.git', join(destination, 'orca')],
|
||||
expect.objectContaining({
|
||||
env: expect.objectContaining({
|
||||
GIT_TERMINAL_PROMPT: '0',
|
||||
GCM_INTERACTIVE: 'never'
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('treats cloneAbort with no active clone as a no-op', async () => {
|
||||
await expect(handlers.get('repos:cloneAbort')!(null, undefined)).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
@@ -42,7 +42,7 @@ import { isTuiAgent } from '../../shared/tui-agent-config'
|
||||
import { invalidateAuthorizedRootsCache } from './filesystem-auth'
|
||||
import type { ChildProcess } from 'node:child_process'
|
||||
import { access, mkdir, readdir, rm } from 'node:fs/promises'
|
||||
import { gitExecFileAsync, gitSpawn } from '../git/runner'
|
||||
import { gitExecFileAsync, gitSpawn, nonInteractiveGitEnv } from '../git/runner'
|
||||
import { isAbsolute, join, posix } from 'node:path'
|
||||
import {
|
||||
cleanupClaimedCloneTarget,
|
||||
@@ -2253,6 +2253,13 @@ export function registerRepoHandlers(mainWindow: BrowserWindow, store: Store): v
|
||||
try {
|
||||
proc = gitSpawn(['clone', '--progress', '--', args.url, clonePath], {
|
||||
cwd: args.destination,
|
||||
// Why: without the non-interactive guard, a clone that needs
|
||||
// GitHub auth makes Git Credential Manager pop its "Connect to
|
||||
// GitHub" OAuth window on Windows; in a network-restricted env the
|
||||
// browser/device flow can never complete and git's credential
|
||||
// retry re-pops it (issue #7652). Fail fast with a clear error and
|
||||
// let Orca's non-intrusive GitHub state stand instead.
|
||||
env: nonInteractiveGitEnv(),
|
||||
stdio: ['ignore', 'ignore', 'pipe']
|
||||
})
|
||||
} catch (err) {
|
||||
|
||||
@@ -41,7 +41,7 @@ import {
|
||||
AGENT_PROMPT_SUBMIT_DELAY_MS,
|
||||
buildAgentPromptPasteBytes
|
||||
} from '../../shared/agent-prompt-injection'
|
||||
import { gitExecFileAsync, wslAwareSpawn } from '../git/runner'
|
||||
import { gitExecFileAsync, wslAwareSpawn, nonInteractiveGitEnv } from '../git/runner'
|
||||
import {
|
||||
cleanupClaimedCloneTarget,
|
||||
claimCloneTarget,
|
||||
@@ -10666,6 +10666,12 @@ export class OrcaRuntimeService {
|
||||
try {
|
||||
proc = wslAwareSpawn('git', ['clone', '--progress', '--', trimmedUrl, clonePath], {
|
||||
cwd: trimmedDestination,
|
||||
// Why: without the non-interactive guard, a clone that needs GitHub
|
||||
// auth makes Git Credential Manager pop its "Connect to GitHub" OAuth
|
||||
// window on Windows; in a network-restricted env the browser/device
|
||||
// flow can never complete and git's credential retry re-pops it
|
||||
// (issue #7652). Fail fast with a clear error instead.
|
||||
env: nonInteractiveGitEnv(),
|
||||
stdio: ['ignore', 'ignore', 'pipe']
|
||||
})
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user