fix: bash 3.2 fractional read timeouts + CI portability lint#14
Merged
Conversation
User hit `read: 0.01: invalid timeout specification` after pressing
Enter on an empty command-mode prompt. Root cause: macOS default
/bin/bash is 3.2 and only accepts integer `read -t` timeouts; bash 4+
accepts decimals.
Two fixes:
1. Drop the 0.01s read in the dashboard loop entirely — when the
refresh interval has already elapsed, `_dashboard_refresh_cache`
immediately and continue, no read at all. Net effect: no drift
either, and works on every bash.
2. Extract the Esc-vs-arrow-key disambiguation read (the OTHER
fractional-timeout site I'd missed) into a single helper:
`_read_short_peek`. Helper checks BASH_HAS_FRACTIONAL_TIMEOUT
(set once at script load) and uses 50ms on bash 4+ or a
non-blocking peek (`-t 0`) on bash 3.2. Arrow-key follow-up
bytes are typically already buffered when we reach the peek,
so non-blocking still works in practice.
CI: new "Bash 3.2 portability lint" job greps the script for
patterns that break on bash 3.2:
- `read -t <fractional>`
- mapfile / readarray
- ${var^^} / ${var,,} case conversions
- declare -n / local -n namerefs
- read -i (initial buffer)
Lines with `# bash4-ok` annotation are skipped (the one helper that
intentionally branches by version uses this). Pure-comment lines are
also skipped so prose mentioning the patterns doesn't trip the lint.
This catches the class of issue at PR time instead of at runtime
on a user's mac mini.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes interactive dashboard input handling to be compatible with macOS’s default Bash 3.2 (which rejects fractional read -t timeouts) and adds CI enforcement to prevent future Bash-4+ portability regressions.
Changes:
- Introduce a Bash-version capability flag and
_read_short_peekhelper to safely disambiguate loneEscvs escape sequences across Bash 3.2 vs 4+. - Remove the fractional
read -t 0.01refresh behavior by refreshing immediately when the interval elapses (no read needed). - Add a new GitHub Actions portability-lint job that flags common Bash-4+ constructs (fractional
read -t,mapfile,${var^^}, namerefs,read -i) unless annotated with# bash4-ok.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
gh-runner-status |
Adds Bash version capability detection + _read_short_peek, and removes fractional read timeout usage in the dashboard loop. |
.github/workflows/ci.yml |
Adds a new bash32-portability CI job to detect Bash-3.2-incompatible patterns early. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
You hit
read: 0.01: invalid timeout specificationwhen pressing Enter on an empty command-mode prompt.Root cause: macOS default
/bin/bashis 3.2 (Apple froze it pre-GPLv3). Bash 3.2 only accepts integerread -ttimeouts; the dashboard had aread -t 0.01for "refresh-now" semantics that worked on bash 4+ in development but errored on the actual installed bash.There was also a second fractional
read -t 0.05for distinguishing lone Esc from arrow-key escape sequences — the same bug, I missed it.Two fixes
_dashboard_refresh_cacheandcontinue— no read needed. Eliminates drift too._read_short_peekhelper for the Esc disambiguation. Branches on a singleBASH_HAS_FRACTIONAL_TIMEOUTflag set at script load. 50ms on bash 4+, non-blocking-t 0on bash 3.2 (arrow-key follow-up bytes are already buffered when we reach the peek, so it works either way).CI: new portability lint
To catch this class at PR time, new
bash32-portabilityjob greps for patterns that break on bash 3.2:read -t <fractional>mapfile/readarray${var^^}/${var,,}case conversionsdeclare -n/local -nnamerefsread -i(initial buffer)Lines tagged
# bash4-ok(gated uses) and pure comment lines are skipped. The helper that intentionally usesread -t 0.05carries thebash4-okannotation.This is the second time I've shipped a bash-3.2 issue (first was
read -e -iin PR #6). The CI lint catches both retroactively and prevents future ones.Test plan
shellcheckcleanread -t 0.01path is gone🤖 Generated with Claude Code