Adds `gh` CLI as a GitHub credential fallback for users who already have `gh auth login` configured locally. OpenChamber-owned OAuth credentials remain the primary source of truth; the `gh` token is only used when no stored OpenChamber GitHub access token exists and the fallback is not disabled. The fallback is implemented as a credential provider only: GitHub features continue to use the existing Octokit/GitHub API paths for issues, pull requests, checks, merges, and related operations. The PR does not replace those endpoints with `gh issue` or `gh pr` CLI commands. Server changes: - Add `gh-cli-credential.js` to read `gh auth token` with a bounded timeout. - Cache the `gh` token lookup for 30 seconds, including negative results, to avoid repeated subprocess spawning on status/polling paths. - Hide the subprocess window on Windows via `windowsHide: true`. - Clear the gh CLI token cache when the fallback setting changes. - Update `getOctokitOrNull()` to prefer stored OpenChamber OAuth tokens and fall back to the `gh` token only when enabled. - Add `ghCliDisabled` persistence in the existing settings file with atomic writes and `0o600` file permissions. - Add `POST /api/github/auth/gh-cli` to enable or disable the fallback. - Extend `/api/github/auth/status` with `ghCli` metadata: availability, disabled state, active state, and active user when applicable. UI/runtime changes: - Extend `GitHubAuthStatus` and `GitHubAPI` with gh CLI fallback metadata and toggle support. - Add web RuntimeAPI support for toggling the gh CLI fallback through `runtimeFetch`, preserving active runtime/remote target behavior. - Add deterministic VS Code unsupported handling for the gh CLI toggle. - Update GitHub Settings to show gh CLI availability and active status. - When gh CLI is the active auth source, show it in the connected account card and offer Disable instead of Disconnect. - Keep Add Account available so users can still connect an OpenChamber OAuth account, which then takes priority over gh CLI. - Add localized gh CLI settings strings across supported settings locales. Fixes addressed during review: - Removed unreachable UI branches in the inactive gh CLI card. - Avoided duplicate and repeated `gh auth token` subprocess calls. - Hardened settings file permissions for the new persisted flag. - Routed the gh CLI toggle through the RuntimeAPI/runtimeFetch path instead of direct browser `fetch`. - Added targeted tests for hidden subprocess options and negative-result cache behavior. - Fixed a VS Code webview Response body typing issue that blocked type-check.
44 lines
1.2 KiB
JavaScript
44 lines
1.2 KiB
JavaScript
import { beforeEach, describe, expect, mock, test } from 'bun:test';
|
|
|
|
const execFileSyncMock = mock(() => '');
|
|
|
|
mock.module('child_process', () => ({
|
|
execFileSync: execFileSyncMock,
|
|
}));
|
|
|
|
const { clearGhCliTokenCache, getGhCliToken } = await import('./gh-cli-credential.js');
|
|
|
|
describe('gh CLI credential lookup', () => {
|
|
beforeEach(() => {
|
|
execFileSyncMock.mockReset();
|
|
clearGhCliTokenCache();
|
|
});
|
|
|
|
test('hides the subprocess window on Windows', () => {
|
|
execFileSyncMock.mockReturnValueOnce('token\n');
|
|
|
|
expect(getGhCliToken()).toBe('token');
|
|
expect(execFileSyncMock).toHaveBeenCalledWith('gh', ['auth', 'token'], {
|
|
encoding: 'utf8',
|
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
timeout: 5000,
|
|
windowsHide: true,
|
|
});
|
|
});
|
|
|
|
test('caches unavailable gh CLI result until cache is cleared', () => {
|
|
execFileSyncMock.mockImplementation(() => {
|
|
throw new Error('gh unavailable');
|
|
});
|
|
|
|
expect(getGhCliToken()).toBeNull();
|
|
expect(getGhCliToken()).toBeNull();
|
|
expect(execFileSyncMock).toHaveBeenCalledTimes(1);
|
|
|
|
clearGhCliTokenCache();
|
|
|
|
expect(getGhCliToken()).toBeNull();
|
|
expect(execFileSyncMock).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|