Skip to content

Commit a869149

Browse files
feat: support OCLIF_STDIN_TIMEOUT_MS env var for stdin read timeout @W-24151668@
The hard-coded 10ms timeout in readStdin() causes intermittent failures when piping input on loaded systems or CI runners. Allow users to override it via the OCLIF_STDIN_TIMEOUT_MS environment variable, defaulting to the current 10ms when not set.
1 parent c4a1e07 commit a869149

2 files changed

Lines changed: 75 additions & 1 deletion

File tree

‎src/parser/parse.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export const readStdin = async (): Promise<null | string> => {
7373
const lines: string[] = []
7474
const ac = new AbortController()
7575
const {signal} = ac
76-
const timeout = setTimeout(() => ac.abort(), 10)
76+
const timeout = setTimeout(() => ac.abort(), Number(process.env.OCLIF_STDIN_TIMEOUT_MS) || 10)
7777

7878
const rl = createInterface({
7979
input: stdin,
@@ -749,3 +749,4 @@ export class Parser<
749749
return flagTokenMap
750750
}
751751
}
752+
// test

‎test/parser/parse.test.ts‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,6 +2185,79 @@ See more help with --help`)
21852185
})
21862186
})
21872187

2188+
describe('readStdin', () => {
2189+
let originalEnv: string | undefined
2190+
2191+
beforeEach(() => {
2192+
originalEnv = process.env.OCLIF_STDIN_TIMEOUT_MS
2193+
delete globalThis.oclif?.stdinCache
2194+
})
2195+
2196+
afterEach(() => {
2197+
if (originalEnv === undefined) {
2198+
delete process.env.OCLIF_STDIN_TIMEOUT_MS
2199+
} else {
2200+
process.env.OCLIF_STDIN_TIMEOUT_MS = originalEnv
2201+
}
2202+
2203+
sinon.restore()
2204+
})
2205+
2206+
it('should use OCLIF_STDIN_TIMEOUT_MS env var for timeout', async () => {
2207+
process.env.OCLIF_STDIN_TIMEOUT_MS = '5000'
2208+
const spy = sinon.spy(globalThis, 'setTimeout')
2209+
2210+
Object.defineProperty(process.stdin, 'isTTY', {configurable: true, value: undefined})
2211+
try {
2212+
await parser.readStdin()
2213+
} catch {
2214+
// may abort, that's fine
2215+
}
2216+
2217+
const stdinTimeoutCall = spy.getCalls().find((call) => {
2218+
const delay = call.args[1]
2219+
return delay === 5000
2220+
})
2221+
expect(stdinTimeoutCall).to.not.be.undefined
2222+
})
2223+
2224+
it('should default to 10ms when OCLIF_STDIN_TIMEOUT_MS is not set', async () => {
2225+
delete process.env.OCLIF_STDIN_TIMEOUT_MS
2226+
const spy = sinon.spy(globalThis, 'setTimeout')
2227+
2228+
Object.defineProperty(process.stdin, 'isTTY', {configurable: true, value: undefined})
2229+
try {
2230+
await parser.readStdin()
2231+
} catch {
2232+
// may abort, that's fine
2233+
}
2234+
2235+
const stdinTimeoutCall = spy.getCalls().find((call) => {
2236+
const delay = call.args[1]
2237+
return delay === 10
2238+
})
2239+
expect(stdinTimeoutCall).to.not.be.undefined
2240+
})
2241+
2242+
it('should default to 10ms when OCLIF_STDIN_TIMEOUT_MS is invalid', async () => {
2243+
process.env.OCLIF_STDIN_TIMEOUT_MS = 'not-a-number'
2244+
const spy = sinon.spy(globalThis, 'setTimeout')
2245+
2246+
Object.defineProperty(process.stdin, 'isTTY', {configurable: true, value: undefined})
2247+
try {
2248+
await parser.readStdin()
2249+
} catch {
2250+
// may abort, that's fine
2251+
}
2252+
2253+
const stdinTimeoutCall = spy.getCalls().find((call) => {
2254+
const delay = call.args[1]
2255+
return delay === 10
2256+
})
2257+
expect(stdinTimeoutCall).to.not.be.undefined
2258+
})
2259+
})
2260+
21882261
describe('allowStdin', () => {
21892262
const stdinValue = 'x'
21902263
const stdinPromise = new Promise<null | string>((resolve) => {

0 commit comments

Comments
 (0)