mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-10 09:19:42 +02:00
feat(git-status): configurable max repositories and git timeout; keep unreadable repos listed
Settings (per device): Git status: max repositories (1-50, default 12) and git timeout (5-120 s, default 30, was a fixed 10). Both go to /git-status and /git-diff as maxRepos / timeout query parameters, clamped server-side (an empty value means the default). A repository whose git status fails stays in the list with the reason instead of being dropped silently, shows as '? N' in the indicator, and the truncation line now names the limit and the setting. The discovery cache is keyed by the limit. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
ac94f339ac
commit
3ae22f64a4
@@ -298,3 +298,50 @@ describe('GET /api/sessions/:id/git-diff in a folder of several repositories', (
|
||||
expect(calls.filter(([, verb]) => verb === 'status').map(([cwd]) => cwd)).toEqual([api]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /api/sessions/:id/git-status limits', () => {
|
||||
it('honours maxRepos and timeout (seconds), clamped, and reports the limit it used', async () => {
|
||||
for (const n of ['a', 'b', 'c']) {
|
||||
mkdirSync(join(dir, n));
|
||||
git(join(dir, n), 'init', '-q', '-b', 'main');
|
||||
}
|
||||
const seen: Array<number | undefined> = [];
|
||||
const spy: GitRunner = (cwd, args, opts) => {
|
||||
seen.push(opts?.timeoutMs);
|
||||
return execFileSyncGit(cwd, args);
|
||||
};
|
||||
const { app } = await setup({ git: spy });
|
||||
const res = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/sessions/test-session-1/git-status?maxRepos=2&timeout=7',
|
||||
});
|
||||
const data = res.json().data;
|
||||
expect(data.repos.map((r: { name: string }) => r.name)).toEqual(['a', 'b']);
|
||||
expect(data).toMatchObject({ reposTruncated: true, repoLimit: 2 });
|
||||
expect(new Set(seen)).toEqual(new Set([7000]));
|
||||
|
||||
clearGitStatusCache();
|
||||
const wild = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/sessions/test-session-1/git-status?maxRepos=9999&timeout=1&fresh=1',
|
||||
});
|
||||
expect(wild.json().data.repoLimit).toBe(50);
|
||||
expect(seen.at(-1)).toBe(5000);
|
||||
|
||||
clearGitStatusCache();
|
||||
const junk = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/sessions/test-session-1/git-status?maxRepos=abc&timeout=xyz&fresh=1',
|
||||
});
|
||||
expect(junk.json().data.repoLimit).toBe(12);
|
||||
expect(seen.at(-1)).toBe(30_000);
|
||||
|
||||
clearGitStatusCache();
|
||||
await app.inject({ method: 'GET', url: '/api/sessions/test-session-1/git-status?maxRepos=&timeout=&fresh=1' });
|
||||
expect(seen.at(-1)).toBe(30_000); // empty means "not given", not 0
|
||||
});
|
||||
});
|
||||
|
||||
function execFileSyncGit(cwd: string, args: string[]): Promise<string> {
|
||||
return Promise.resolve(execFileSync('git', ['--no-optional-locks', ...args], { cwd, env: ENV, encoding: 'utf8' }));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user