Skip to content

OXY-162: New Oxygen command-execution API (CommandService / Command2) - #304

Open
Kalin-Rudnicki wants to merge 7 commits into
mainfrom
OXY-162
Open

OXY-162: New Oxygen command-execution API (CommandService / Command2)#304
Kalin-Rudnicki wants to merge 7 commits into
mainfrom
OXY-162

Conversation

@Kalin-Rudnicki

Copy link
Copy Markdown
Owner

Derives from the current/feature/command-improvements brainstorm branch (a chain of
"WIP : savepoint" commits) and brings that exploratory work to a compiling, tested state.

Jira: OXY-162

What the new command API is

A "v2" API in oxygen-zio for running external OS processes from ZIO, intended to eventually
replace the legacy oxygen.zio.system.Command (which shells out via scala.sys.process):

  • CommandService — pluggable execution SPI (executeSync, executeSyncStreamErr,
    executeCode + derived helpers), held in a FiberRef with a platform-specific default.
  • Command2 — immutable fluent builder (command / args / cwd / env / sudo) building a
    BuiltCommand; execute surface for sync capture, streamed stderr, exit-code, and typed
    decode-to-A (String / PlainText schema / JSON), plus legacy-compatible execute* shims.
  • JavaCommandService — JVM/Native impl over java.lang.ProcessBuilder: scoped process
    lifecycle, concurrent stdout/stderr draining (avoids pipe-buffer deadlock), stdin sources.
  • UnimplementedCommandService — Scala.js default (typed Unimplemented failure).
  • stdio ADTs (CommandInputSource / CommandOutputSource / ShowCommand) and a CommandError ADT.
  • Supporting: StringDecoder.DecodingFailure and json.JsonError now extend oxygen Error.

What this PR did to finish it

  • Fixed a blocking bug: CommandInputSource.Empty mapped to Redirect.DISCARD (a WRITE-only
    redirect), so the process failed to start for every no-stdin execution — i.e. the API failed on
    first use. Now uses PIPE + closes the child's stdin (EOF).
  • Restored JS/Native compilation: the shared JavaCommandService used ZSink.fromOutputStream
    (JVM-only) and ProcessBuilder (absent on JS). Rewrote the stream-stdin path to a portable
    blocking chunk-write (Native compiles it), and excluded the file from the JS source set.
  • Resolved WIP loose ends: corrected BuiltCommand shell-escaping to the POSIX '\'' idiom
    (display/logging only — execution never goes through a shell), documented the Command2 vs legacy
    Command coexistence, cleaned up FIX-PRE-MERGE notes.
  • Added tests: Command2Spec (13 tests) — builder, escaping, and live JVM execution.

Verification

  • oxygen-zio compiles on JVM / JS / Native (JS and Native were broken before).
  • sbt utJVM/test743 passed, 0 failed (new Command2Spec + legacy CommandSpec + all
    core/schema/json/zio specs; confirms the Error-trait migration caused no regressions).
  • sbt oxygen-executableJVM/Test/compile succeeds (downstream consumer of the changed core/json APIs).
  • sbt fmt applied.

Honest mergeability assessment

Reviewable and non-broken, but an incremental landing of an in-progress API (~6.5/10). The JVM
happy paths are solid, test-covered, and green, and the previously API-breaking Empty-stdin bug is
fixed. Caveats: Command2 is still parallel to the legacy Command (no call-site migration yet);
the JS backend is intentionally unimplemented; Native compiles but is not exercised by any
test; and several stdio/decode branches (Stream/File/Log, streamed-stderr, JSON/PlainText decode)
are only lightly or not covered. Safe to merge behind the existing unused Command2 name; not a
finished, fully-migrated replacement for Command.

🤖 Generated with Claude Code

https://claude.ai/code/session_011YxWKdsz97QT9BD7AdpSq6

Kalin-Rudnicki and others added 7 commits August 1, 2026 05:27
…le state

Brings the exploratory command-execution API from the `current/feature/command-improvements`
brainstorm branch to a compiling, tested state.

- Fix a blocking bug: `CommandInputSource.Empty` mapped to `Redirect.DISCARD` (a WRITE-only
  redirect), which made the process fail to start for every no-stdin execution. Use `PIPE` and
  close the child's stdin immediately so it observes EOF.
- Make oxygen-zio compile on JS and Native again: the shared `JavaCommandService` used the
  JVM-only `ZSink.fromOutputStream` and `java.lang.ProcessBuilder`. Rewrote the stream-stdin
  path to a portable blocking chunk-write (compiles on Native), and excluded the file from the
  JS source set (JS keeps `UnimplementedCommandService`).
- Resolve WIP loose ends: correct `BuiltCommand` shell-escaping to the POSIX `'\''` idiom
  (display-only), document `Command2` vs legacy `Command` coexistence, tidy comments.
- Add `Command2Spec` covering builder, escaping, and live JVM execution.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YxWKdsz97QT9BD7AdpSq6
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.

1 participant