Skip to content

shell: accept seq --separator=X and --terminator=X - #39265

Open
robobun wants to merge 1 commit into
mainfrom
farm/5f500829/shell-seq-long-option-equals
Open

shell: accept seq --separator=X and --terminator=X#39265
robobun wants to merge 1 commit into
mainfrom
farm/5f500829/shell-seq-long-option-equals

Conversation

@robobun

@robobun robobun commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • In the Bun shell, seq --separator=, 1 3 and seq --terminator=X 1 3 exit 1 with seq: invalid argument. The other spellings of the same options (--separator , , -s ,, -s,, --terminator X) work.
  • Cause: the option loop in src/runtime/shell/builtin/seq.rs only recognizes the long names by exact comparison (arg == b"--separator", arg == b"--terminator"). A --separator=, argument matches no arm, the loop breaks, and the argument is then parsed as the first numeric operand, which fails.
  • This never worked (the Zig implementation had the same loop), so it is not a regression and there is no issue number.

Fix

  • The arm that takes a value attached to the option (-s,) now also strips a --separator= prefix, and the -t, arm a --terminator= prefix. Everything after the first = is the value, so --separator= sets an empty separator and --separator=a=b sets a=b.
  • Why this is right: --name=value and --name value are the two spellings getopt_long accepts for a long option with a required argument, and bun's seq already accepts the second one for these options; .claude/docs/landing-prs.md (API design) asks for every accepted form of an option to be handled identically, naming exactly this pair. GNU seq --separator=, 1 3 accepts the = spelling too (checked locally; GNU seq has no --terminator, that one is BSD's, so the -t change only follows the -s one). The = is required: --separator, is still rejected, as getopt_long would reject it.
  • The arg.len() > 2 guard the old arm had was already implied by the exact -s arm right above it, which still consumes a bare -s (and still reports the missing argument); strip_prefix(b"-s") therefore only ever sees a non-empty remainder, so -sX behaves as before.
  • The value is a sub-slice of the same argv entry, exactly like the existing -s, form, so the RawSlice borrow documented on the separator/terminator fields is unchanged.
  • Verified by the new cases in test/js/bun/shell/commands/seq.test.ts: --separator=,, --separator= (empty), --separator=a=b, --terminator=,, --terminator=, both options together, and --separator, / --terminator, staying rejected. Six of the eight fail on bun 1.4.0 (the two rejection pins pass before and after, they pin the shape of the fix); all 40 tests in the file pass with the fix.
  • test/js/bun/shell/pipeline_stack.test.ts, exec.test.ts (seq --help still reports an invalid argument) and shell-seq-condexpr.test.ts also pass with the fix.

Background


no test proof · iteration 0 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/js/bun/shell/commands/seq.test.ts

The seq builtin only matched the long options spelled as a separate
argument (--separator X), so the --separator=X spelling fell through to
operand parsing and failed with "seq: invalid argument". Take the value
after the = in the same arm that handles the attached -sX / -tX forms.
@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 4 minutes

Limit details: You’ve used all 5 included reviews currently available under your plan.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c6cee758-b2ff-4b1b-b5c2-746d895d384e

📥 Commits

Reviewing files that changed from the base of the PR and between a42889a and 95b7dc3.

📒 Files selected for processing (2)
  • src/runtime/shell/builtin/seq.rs
  • test/js/bun/shell/commands/seq.test.ts

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 6:44 PM PT - Aug 15th, 2026

@robobun, your commit 95b7dc3 is building: #98970

@robobun

robobun commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

Reproduced on bun 1.4.0 (Linux): seq --separator=, 1 3 and seq --terminator=X 1 3 exit 1 with seq: invalid argument, while --separator ,, -s, and --terminator X work. The new cases in test/js/bun/shell/commands/seq.test.ts fail the same way on 1.4.0 and pass with this branch.

Fix is in #39265 (this PR): the attached-value arms in src/runtime/shell/builtin/seq.rs also strip --separator= / --terminator=.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — small, mechanical option-parsing extension with thorough tests.

What was reviewed:

  • Traced the new strip_prefix(b"-s").or_else(...) arms: bare -s/--separator are still consumed by the exact-match arm above, so dropping the len > 2 guard is safe; --separator, (no =) still falls through to the numeric parser and is rejected.
  • Checked that bytes remains a sub-slice of the same argv entry, so the RawSlice borrow invariant documented on the struct fields is unchanged.
  • Verified the new tests cover the happy path, empty value (--separator=), = inside the value, both options together, and pin the rejection of --separator,/--terminator,.
Extended reasoning...

Overview

This PR teaches the Bun shell seq builtin to accept --separator=X and --terminator=X (the --name=value spelling of a getopt_long option with a required argument), which previously fell through the option loop and failed with "invalid argument". The change touches two arms of the flag-parsing loop in src/runtime/shell/builtin/seq.rs (rewriting starts_with(b"-s") && len > 2 as strip_prefix(b"-s").or_else(|| strip_prefix(b"--separator=")), and the same for -t) and adds eight test cases to test/js/bun/shell/commands/seq.test.ts.

Security risks

None. This is option parsing for a shell builtin that emits a numeric sequence to stdout. The value is stored as a borrowed slice of argv (same as the existing -sX path) and only ever copied into the output buffer via extend_from_slice. No paths, no syscalls, no allocation sizing derived from the value.

Level of scrutiny

Low. The diff is ~10 lines of Rust that mechanically extends an existing pattern. I verified the one non-obvious point the PR description calls out: dropping the explicit len > 2 guard is safe because the exact-match arm arg == b"-s" || arg == b"--separator" immediately above still catches the bare forms, so strip_prefix(b"-s") never sees an empty remainder. --separator= (empty value) intentionally reaches the new arm and sets an empty separator, which is tested. --separator, does not match either prefix (second byte is -, not s; and no = at position 11), so it still falls through to parse_num! and fails — also pinned by a test.

Other factors

  • Tests are added alongside the existing -s/--separator cases in the correct file, assert exact stdout/stderr/exit, and cover the variant matrix (both options, empty value, = in value, combined use, negative pins).
  • The RawSlice stored in separator/terminator is a sub-slice of the same argv arena string as before, so the lifetime invariant documented on the struct is preserved.
  • No prior human or bot reviews with outstanding feedback; the bug-hunting system found nothing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant