authorgravatar for cjimenez@r21digital.comCarlos Jimenez <cjimenez@r21digital.com> 2026-08-15 05:07:38-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-15 12:07:38+00:00
logd715c2e56bb718d2225cc0f07cc65e6c637dc229
tree58de10671fee5ab321e3964426bcb1d39bb2df92
parentad47d2347c6917f7db33e6e3902e1e8e5d5281ec
signature Signed by PGP key B5690EEEBB952194

fix(server): let slow provider CLIs raise their discovery probe budget (#6223)

Co-authored-by: Julius Marminge <jmarminge@gmail.com>

2 files changed, 16 insertions(+), 3 deletions(-)

apps/server/src/sourceControl/AzureDevOpsSourceControlProvider.ts+4
...@@ -45,6 +45,10 @@ export const discovery = {...@@ -45,6 +45,10 @@ export const discovery = {
45 executable: "az",45 executable: "az",
46 versionArgs: ["--version"],46 versionArgs: ["--version"],
47 authArgs: ["account", "show", "--query", "user.name", "-o", "tsv"],47 authArgs: ["account", "show", "--query", "user.name", "-o", "tsv"],
48 // `az` boots a fresh Python interpreter on every invocation, so even `az --version`
49 // takes ~6s on Windows and overruns the default budget, leaving the provider reported
50 // as missing on machines where it is installed. `gh` and `glab` answer in ~0.3s.
51 probeTimeoutMs: 20_000,
48 parseAuth: parseAzureAuth,52 parseAuth: parseAzureAuth,
49 installHint:53 installHint:
50 "Install the Azure command-line tools (`az`), then enable Azure DevOps support with `az extension add --name azure-devops`.",54 "Install the Azure command-line tools (`az`), then enable Azure DevOps support with `az extension add --name azure-devops`.",
apps/server/src/sourceControl/SourceControlProviderDiscovery.ts+12-3
...@@ -33,6 +33,7 @@ export type SourceControlCliDiscoverySpec = SourceControlDiscoverySpecBase & {...@@ -33,6 +33,7 @@ export type SourceControlCliDiscoverySpec = SourceControlDiscoverySpecBase & {
33 readonly executable: string;33 readonly executable: string;
34 readonly versionArgs: ReadonlyArray<string>;34 readonly versionArgs: ReadonlyArray<string>;
35 readonly authArgs: ReadonlyArray<string>;35 readonly authArgs: ReadonlyArray<string>;
36 readonly probeTimeoutMs?: number;
36 readonly parseAuth: (input: SourceControlAuthProbeInput) => SourceControlProviderAuth;37 readonly parseAuth: (input: SourceControlAuthProbeInput) => SourceControlProviderAuth;
37 readonly refineUnknownRemote?: (38 readonly refineUnknownRemote?: (
38 input: SourceControlUnknownRemoteRefinementInput,39 input: SourceControlUnknownRemoteRefinementInput,
...@@ -52,6 +53,14 @@ type SourceControlCliRemoteRefinementSpec = SourceControlCliDiscoverySpec & {...@@ -52,6 +53,14 @@ type SourceControlCliRemoteRefinementSpec = SourceControlCliDiscoverySpec & {
52 readonly refineUnknownRemote: NonNullable<SourceControlCliDiscoverySpec["refineUnknownRemote"]>;53 readonly refineUnknownRemote: NonNullable<SourceControlCliDiscoverySpec["refineUnknownRemote"]>;
53};54};
5455
56// Most provider CLIs answer `--version` in well under a second, so a short budget keeps
57// discovery snappy. Specs whose CLI is known to be slower can raise it via probeTimeoutMs.
58const DEFAULT_PROBE_TIMEOUT_MS = 5_000;
59
60function probeTimeoutMs(spec: SourceControlCliDiscoverySpec): number {
61 return spec.probeTimeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
62}
63
55interface DiscoveryProbeResult {64interface DiscoveryProbeResult {
56 readonly kind: SourceControlProviderKind;65 readonly kind: SourceControlProviderKind;
57 readonly label: string;66 readonly label: string;
...@@ -167,7 +176,7 @@ function probeCli(input: {...@@ -167,7 +176,7 @@ function probeCli(input: {
167 command: input.spec.executable,176 command: input.spec.executable,
168 args: input.spec.versionArgs,177 args: input.spec.versionArgs,
169 cwd: input.cwd,178 cwd: input.cwd,
170 timeoutMs: 5_000,179 timeoutMs: probeTimeoutMs(input.spec),
171 maxOutputBytes: 8_000,180 maxOutputBytes: 8_000,
172 appendTruncationMarker: true,181 appendTruncationMarker: true,
173 })182 })
...@@ -244,7 +253,7 @@ export function probeSourceControlProvider(input: {...@@ -244,7 +253,7 @@ export function probeSourceControlProvider(input: {
244 args: spec.authArgs,253 args: spec.authArgs,
245 cwd: input.cwd,254 cwd: input.cwd,
246 allowNonZeroExit: true,255 allowNonZeroExit: true,
247 timeoutMs: 5_000,256 timeoutMs: probeTimeoutMs(spec),
248 maxOutputBytes: 8_000,257 maxOutputBytes: 8_000,
249 appendTruncationMarker: true,258 appendTruncationMarker: true,
250 })259 })
...@@ -287,7 +296,7 @@ export const refineUnknownRemoteProvider = Effect.fn("refineUnknownRemoteProvide...@@ -287,7 +296,7 @@ export const refineUnknownRemoteProvider = Effect.fn("refineUnknownRemoteProvide
287 args: spec.authArgs,296 args: spec.authArgs,
288 cwd: input.cwd,297 cwd: input.cwd,
289 allowNonZeroExit: true,298 allowNonZeroExit: true,
290 timeoutMs: 5_000,299 timeoutMs: probeTimeoutMs(spec),
291 maxOutputBytes: 8_000,300 maxOutputBytes: 8_000,
292 appendTruncationMarker: true,301 appendTruncationMarker: true,
293 })302 })