You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
RFC: Complete Bash-to-Rust Migration with Eval Protocol
Summary
This RFC proposes a three-phase plan to migrate all remaining Bash commands in sdkman-cli to Rust native binaries. The key enabler is an eval protocol that allows Rust binaries to emit shell environment mutations (PATH, JAVA_HOME, etc.) that the Bash wrapper evaluates on their behalf.
Background
SDKMAN's CLI is currently split between:
sdkman-cli (Bash): The sdk() shell function and ~23 Bash scripts
The migration has been stalled since 2024 because the remaining commands (use, install, list, update, upgrade, env) need to modify the calling shell's environment variables. A subprocess cannot directly modify its parent's environment, which is the fundamental blocker.
As discussed in #189, the solution is to have Rust binaries output eval-able shell code that the wrapper script executes.
Proposal
Eval Protocol Design
Rust binaries emit two types of output on stdout:
Display lines (no prefix) — human-readable output, printed to terminal
Eval lines (prefixed with SDKMAN_EVAL:) — shell commands to be eval'd by the wrapper
Example for sdk use java 17.0.0-tem:
Using java version 17.0.0-tem in this shell.
SDKMAN_EVAL:export JAVA_HOME=/home/user/.sdkman/candidates/java/17.0.0-tem
SDKMAN_EVAL:export PATH=/home/user/.sdkman/candidates/java/17.0.0-tem/bin:/usr/bin:...
The Bash wrapper (sdkman-main.sh) would be updated to:
Each native binary gets a --supports-eval-protocol flag so the shell wrapper can detect support. This ensures backward compatibility with older binaries.
Three Phases
Phase 1: Pure Filesystem Commands (no protocol needed)
Command
Complexity
Operations
flush
Low
Delete tmp/metadata/version dirs, report freed space
config
Very Low
Open $SDKMAN_DIR/etc/config in $EDITOR
cache
Very Low
Validate candidates cache file
These follow the exact same pattern as existing commands — no architectural changes required.
Phase 2: Eval Protocol + use Command
This is the critical phase that proves the protocol works:
Add --supports-eval-protocol flag to all binaries
Implement use command with eval protocol output
Update sdkman-main.sh routing to parse eval lines
Cross-shell testing (bash, zsh)
Phase 3: Network + Install Commands
With the protocol proven, migrate the remaining commands:
Command
Key Dependencies
Challenge Level
list
reqwest (HTTP)
Medium — remote API + local scan
update
reqwest (HTTP)
Medium — fetch + diff cache
install
reqwest, zip/tar, sha2
High — download, verify, extract, prompt
upgrade
(delegates to install)
Medium — iterate + batch
env
(delegates to use/install)
High — .sdkmanrc parsing, 4 subcommands
New Rust Dependencies
Crate
Purpose
Phase
reqwest + rustls-tls
HTTP client (no OpenSSL)
Phase 3
zip, tar, flate2
Archive extraction
Phase 3
sha2, md-5
Checksum verification
Phase 3
dialoguer
Interactive prompts
Phase 3
Coordinated Release
The eval protocol requires a coordinated release of both sdkman-cli (updated shell wrapper) and sdkman-cli-native (new binaries). The existing sdkman_native_enable config flag provides a safe rollback mechanism.
What Remains as Bash
These components cannot be migrated to Rust and will permanently remain as shell scripts:
sdkman-init.sh — must be sourced by .bashrc/.zshrc to bootstrap the shell environment
sdkman-main.sh — the sdk() function dispatcher (will become thinner over time)
Alternatives Considered
fd 3 (separate file descriptor): Cleaner separation but fragile to set up across bash/zsh/platforms.
JSON output: Requires jq dependency or slow pure-Bash JSON parsing.
Shim/proxy binary approach (like aqua): Would require a complete architectural redesign.
RFC: Complete Bash-to-Rust Migration with Eval Protocol
Summary
This RFC proposes a three-phase plan to migrate all remaining Bash commands in sdkman-cli to Rust native binaries. The key enabler is an eval protocol that allows Rust binaries to emit shell environment mutations (PATH, JAVA_HOME, etc.) that the Bash wrapper evaluates on their behalf.
Background
SDKMAN's CLI is currently split between:
sdk()shell function and ~23 Bash scriptsThe migration has been stalled since 2024 because the remaining commands (use, install, list, update, upgrade, env) need to modify the calling shell's environment variables. A subprocess cannot directly modify its parent's environment, which is the fundamental blocker.
As discussed in #189, the solution is to have Rust binaries output eval-able shell code that the wrapper script executes.
Proposal
Eval Protocol Design
Rust binaries emit two types of output on stdout:
SDKMAN_EVAL:) — shell commands to be eval'd by the wrapperExample for
sdk use java 17.0.0-tem:The Bash wrapper (
sdkman-main.sh) would be updated to:Feature Detection
Each native binary gets a
--supports-eval-protocolflag so the shell wrapper can detect support. This ensures backward compatibility with older binaries.Three Phases
Phase 1: Pure Filesystem Commands (no protocol needed)
flushconfig$SDKMAN_DIR/etc/configin$EDITORcacheThese follow the exact same pattern as existing commands — no architectural changes required.
Phase 2: Eval Protocol +
useCommandThis is the critical phase that proves the protocol works:
--supports-eval-protocolflag to all binariesusecommand with eval protocol outputsdkman-main.shrouting to parse eval linesPhase 3: Network + Install Commands
With the protocol proven, migrate the remaining commands:
listupdateinstallupgradeenvNew Rust Dependencies
reqwest+rustls-tlszip,tar,flate2sha2,md-5dialoguerCoordinated Release
The eval protocol requires a coordinated release of both sdkman-cli (updated shell wrapper) and sdkman-cli-native (new binaries). The existing
sdkman_native_enableconfig flag provides a safe rollback mechanism.What Remains as Bash
These components cannot be migrated to Rust and will permanently remain as shell scripts:
sdkman-init.sh— must be sourced by.bashrc/.zshrcto bootstrap the shell environmentsdkman-main.sh— thesdk()function dispatcher (will become thinner over time)Alternatives Considered
jqdependency or slow pure-Bash JSON parsing.sdk? #189): Would add JVM build complexity. Rust is already established in this project.Open Questions
SDKMAN_EVAL:or something shorter likeEVAL:?installcommand handle post-install hooks (download and source via eval, or interpret in Rust)?Related
sdk? #189 — Original discussion about fully native SDKMAN