Thank you for your interest in contributing to SDKMAN! Native CLI. We welcome contributions that help migrate SDKMAN commands from Bash to Rust using our incremental strangler pattern approach.
This is not a standard single-binary Rust project. We build multiple independent binaries, each replacing one SDKMAN Bash function. Please read Discussion #18 to understand our architectural approach.
Discuss significant changes first:
- Open a discussion or issue before starting major work
- Propose new command migrations before implementing
- Seek feedback early to ensure alignment with project direction
See the "Local development setup" section in README.md for installation and build instructions.
src/
├── lib.rs # Shared constants and utilities
├── helpers/ # Common helper functions
└── bin/
├── command1/ # Each directory is a separate binary
├── command2/ # that implements one sdk subcommand
└── ...
Each binary in src/bin/*/main.rs is independent and uses shared code from lib.rs and helpers/.
- Fork and clone the repository
- Create a branch for your changes
- Make focused changes - one feature/fix per PR
- Write/update tests in the
tests/directory - Run tests:
cargo test - Format code:
cargo fmt - Check for issues:
cargo clippy - Commit using Conventional Commits format
The project uses both unit tests and integration tests:
- Unit tests: Located within command files (e.g.,
src/bin/help/main.rs) for testing individual functions - Integration tests: Located in
tests/directory for end-to-end command testing - Snapshot tests: Help text is validated using
instasnapshots in the help command
The tests/support/ module provides helper functions to create virtual SDKMAN environments for integration tests.
cargo test # Run all tests
cargo test --test current # Run specific integration test file
cargo insta review # Review snapshot changes (help text)Integration tests are marked #[serial] because they manipulate environment variables.
To migrate a Bash command to a native binary:
- Open a discussion proposing the migration
- Research the existing Bash implementation in sdkman-cli
- Create
src/bin/newcommand/main.rsfollowing existing patterns - Use
clapfor argument parsing, import helpers fromcrate::helpers - Write integration tests in
tests/newcommand.rs - Test manually by running
./install.shto inject the native binaries into your local SDKMAN installation
✅ Do:
- Keep PRs small and focused (one command/fix/feature)
- Write clear commit messages using conventional commit format
- Add tests for new functionality
- Update help text snapshots if needed
- Respond to feedback constructively
❌ Don't:
- Submit large "change the world" PRs without prior discussion
- Mix unrelated changes in a single PR
- Ignore project conventions and patterns
- Skip tests or code formatting
- Ensure all tests pass and code is formatted
- Push to your fork and open a PR against
master - Fill out the PR description explaining what and why
- Wait for maintainer review
- Address feedback and iterate as needed
- Formatting: Run
cargo fmtbefore committing - Linting: Address
cargo clippywarnings - Naming: Follow Rust conventions (
snake_casefor functions,PascalCasefor types) - Error handling: Use appropriate exit codes from the
exitcodecrate - Documentation: Comment non-obvious logic, keep help text clear
- Discord: Join our Discord server - we value discussion here before anything else!
- Discussions: Open a GitHub Discussion for proposals and questions
- Bugs: Report via GitHub Issues
Please read our Code of Conduct. We are committed to providing a welcoming and inclusive environment for all contributors.
By contributing to this project, you agree to the terms of the Developer Certificate of Origin. This certifies that you have the right to submit your contribution under the project's open source license.
By contributing, you agree that your contributions will be licensed under the same license as the project.
Thank you for helping make SDKMAN better! 🙏