Skip to content

Keep sbt launcher options out of the joined command string - #235

Merged
eed3si9n merged 1 commit into
sbt:mainfrom
nafg:launcher-options-outside-command-string
Oct 20, 2026
Merged

eed3si9n merged 1 commit into
sbt:mainfrom
nafg:launcher-options-outside-command-string

Conversation

@nafg

@nafg nafg commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Since #227 (released in 0.31.0), an sbt step's preamble and commands are rendered as one quoted, ;-separated command string. That broke steps that pass sbt launcher options. For example

WorkflowStep.Sbt(List("-Dslick.testkit-config=test-dbs/testkit-github.conf", "test"))

used to render as

sbt '++ ${{ matrix.scala }}' -Dslick.testkit-config=test-dbs/testkit-github.conf test

and now renders as

sbt '++ ${{ matrix.scala }}; -Dslick.testkit-config=test-dbs/testkit-github.conf; test'

which sbt rejects with Not a valid command: -. (Seen in nafg/slick-migration-api#521.)

Fix

Arguments starting with - (-D…, -J…, -v) are options for the sbt launcher, never commands, so they are now rendered as separate arguments ahead of the command string:

sbt -Dslick.testkit-config=test-dbs/testkit-github.conf '++ ${{ matrix.scala }}; test'

Options containing a space are single-quoted, as all arguments with spaces were before #227. Steps without launcher options render exactly as they do now.

Verification

Ran both forms by hand against a build with a task that prints a system property:

sbt -Dprobe.x=hello '++ 2.13.18; probe' sbt '++ 2.13.18; -Dprobe.x=hello; probe'
sbt 1.13.0 probe.x = Some(hello) Not a valid command: -
sbt 2.0.11 (thin client) probe.x = Some(hello) Not a valid command: -

Under sbt 2 the property reaches the server only when that invocation starts it. If an earlier step left a server running, the client connects to it and the option doesn't reach it. That was equally true before #227, so this PR doesn't change it.

Added two GenerativePluginSpec cases (a plain launcher option, and one containing a space). githubWorkflowCheck, +test (84 examples on each of sbt 1 and sbt 2) and +scripted pass.

🤖 Generated with Claude Code

Since sbt steps are rendered as one quoted, semicolon-separated command
string, an argument meant for the sbt launcher, such as
-Dslick.testkit-config=..., ends up inside it. sbt then tries to run it
as a command and fails with "Not a valid command: -", under both sbt 1
and sbt 2.

Arguments starting with '-' are launcher options, never commands, so
render them as separate arguments ahead of the command string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@eed3si9n eed3si9n left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks!

@eed3si9n
eed3si9n merged commit 48e3712 into sbt:main Oct 20, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants