Repository navigation
fix(builtins): read shuf's input range under either generated value type - #2770
Merged
Merged
Conversation
The weekly Coreutils Args Drift run has been red since upstream dropped `.value_parser(parse_range)` from `shuf`'s `input-range` arg and moved range parsing into its own `uumain`. The regenerated surface therefore stores a plain `String` where the pin stores a `RangeInclusive<u64>`. `shuf.rs` named only the pre-parsed type, so `get_one::<RangeInclusive<u64>>` mismatched on the regenerated surface. bashkit catches builtin panics, so the only symptom was `shuf -i 1-5` printing nothing: 3960/3989 spec cases passed and `shuf_input_range` failed. Run 39753862402 died there, at *Run cat/tac spec tests* — after regeneration but before the patch upload, so no drift patch and no PR, which is why the drift has stayed invisible. This is the documented failure mode from the `od` case (#2751/#2754): the drift PR enforces a generated-only diff and cannot repair its own consumers, so the consumer fix has to land first and tolerate both surfaces. The new variant is that the two types are not the `String`/`OsString` pair `helpers::string_value` already covers — upstream added and then dropped a value parser yielding a non-string type, moving the arg between "clap pre-parsed it" and "clap handed back raw text". `read_input_range` tries `try_get_one::<RangeInclusive<u64>>` first and falls back to `helpers::string_value` plus a local `parse_input_range`; an `Err` from the typed read means the other surface, not a bad value. The typed read is tried first so the string fallback never trips the `debug_assert` guarding a third, unhandled value type. Verified against both surfaces. Regenerating `shuf_args.rs` from upstream HEAD (eb7d426) shows the dropped value parser is the only semantic change in shuf's whole surface. On that regenerated surface the full spec suite, the shuf unit tests and the shuf resource tests pass; reverting to the pinned surface, all three pass again. Before the fix, the same regenerated surface failed `shuf_input_range` with empty stdout. Behaviour note, recorded in the knowledge: a malformed `-i` exits 2 with clap's rendering under the pinned surface, because clap rejects the value before shuf sees it. On the regenerated surface `parse_input_range` reports it instead and exits 1 with GNU's `shuf: invalid input range: '<range>'`. The pin bump moves this toward GNU, not away; no spec case pins the old shape.
The TM-INF-022 static scan rejects `{raw:?}` anywhere under
`src/builtins/`, test asserts included unless marked `// debug-ok`.
Here Debug only added the surrounding quotes, so the message spells
them itself instead of claiming an exemption.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bashkit | 590d35a | Commit Preview URL Branch Preview URL |
Oct 22 2026, 09:49 AM |
chaliy
added a commit
that referenced
this pull request
Oct 22, 2026
… modules (#2771) Regenerated by `bashkit-coreutils-port` against uutils/coreutils@afe06319a (was d2d96946b). Utilities: cat ls mktemp od readlink realpath shuf stat tac tee truncate. Vendored modules: format. First drift PR the workflow has opened since July: the weekly run had been dying before the patch upload, so the drift never surfaced. #2770 fixed the `shuf` consumer that was breaking it, and run 39762089314 then went green end to end, including the Doppler PAT path from #2760 that had never actually executed. Review of the diff beyond the revision headers: - `shuf_args.rs` drops `parse_range` and `.value_parser(parse_range)`, so `input-range` now stores a plain `String`. This is the change that broke the drift run; `shuf::read_input_range` reads both shapes as of #2770, and the spec suite, shuf unit tests and shuf resource tests were verified against this exact regenerated surface before that landed. One behaviour change rides along, as recorded in the knowledge: a malformed `-i` now exits 1 with GNU's `shuf: invalid input range: '<range>'` rather than exiting 2 with clap's rendering, because the rejection moves from clap's value parser into bashkit. No spec case pinned the old shape. - `num_parser.rs` gains `SUBNANOSECOND_BINARY_EXPONENT` and rejects hexadecimal durations below ~2^-30 seconds. This guard is reachable only under `ParseTarget::Duration`, which nothing in bashkit constructs -- the sole entry point is `parse`, and its in-crate callers pass `Integral` or `Decimal`. Inert for us; vendored verbatim to keep the module aligned to one rev. - Every other file changes only its `Source: uutils/coreutils@<rev>` header. All 50 checks green.
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.
What changed
shufnow reads its-i LO-HIargument without naming the value type upstream chose for it.read_input_rangetries the pre-parsedRangeInclusive<u64>first and falls back to the raw string plus a localparse_input_range; anErrfrom the typed read means the other surface, not a bad value.This unblocks the weekly Coreutils Args Drift run, which is the last red workflow on
main.Why
Upstream dropped
.value_parser(parse_range)fromshuf'sinput-rangearg and moved range parsing into its ownuumain. The regenerated surface therefore stores a plainStringwhere the pin stores aRangeInclusive<u64>.shuf.rsnamed only the pre-parsed type, soget_one::<RangeInclusive<u64>>mismatched after regeneration. bashkit catches builtin panics, so the only symptom wasshuf -i 1-5printing nothing — 3960/3989 spec cases passed andshuf_input_rangefailed. Run 39753862402 died there, at Run cat/tac spec tests: after regeneration but before the patch upload, so no drift patch and no PR. That is why the drift has stayed invisible rather than surfacing as a reviewable diff.This is the documented failure mode from the
odcase (#2751 / #2754): the drift PR enforces a generated-only diff and cannot repair its own consumers, so the consumer fix lands first and tolerates both surfaces. The new variant is that the two types are not theString/OsStringpairhelpers::string_valuealready covers — upstream added and then dropped a value parser yielding a non-string type, moving the arg between "clap pre-parsed it" and "clap handed back raw text".knowledge/runtimes/coreutils-args-port.mdrecords that generalization.Before / After
Regenerating
shuf_args.rsfrom upstream HEAD (eb7d426) shows the dropped value parser is the only semantic change in shuf's whole surface — every other arg (head-countu64,outputpath_buf,file-or-argsos_string, …) is byte-identical:Before — regenerated surface, CI run 39753862402:
After — same regenerated surface, locally:
Reverting to the pinned surface, all of the above pass again — the point of the change is that both hold at once.
Behaviour note. A malformed
-iexits 2 with clap's rendering under the pinned surface, because clap rejects the value beforeshufsees it. On the regenerated surfaceparse_input_rangereports it instead and exits 1 with GNU'sshuf: invalid input range: '<range>'. The pin bump therefore moves this diagnostic toward GNU, not away from it; no spec case pins the old shape.Risk
debug_assertthat fires when an arg is present but readable as neitherStringnorOsString. Reading the typed value first keeps that guard pointed at genuinely unhandled types instead of tripping on the pinnedRangeInclusive.absent_input_range_reads_none_under_both_value_typescovers the no--icase on both shapes.parse_input_rangeduplicates upstream's body-code parser, which is not vendored. It can drift independently; the unit tests pin GNU's semantics, including theLO == HI + 1empty-range spelling.Checklist