Thank you for your interest in vex. We welcome issues and pull requests from the community.
Requirements:
- Rust stable (recommended via rustup)
- macOS (Apple Silicon or Intel)
git clone https://github.com/imnotnoahhh/vex.git
cd vex
cargo buildvex stores all its data under ~/.vex/. If you have a stable vex installed, running your dev build directly would read and write the same directory, potentially corrupting your installed tool versions.
The fix is to override HOME for your dev build, pointing it at a throwaway directory:
# Build the dev binary
cargo build --release
# Set up a convenience alias using an absolute path (works from any directory)
alias vex-dev="HOME=/tmp/vex-dev $(pwd)/target/release/vex"
# Now use vex-dev freely — it reads/writes /tmp/vex-dev/.vex/ only
vex-dev init
vex-dev install python@3.12
vex-dev list python
vex-dev doctor
# Your real ~/.vex/ is completely untouched
vex list python # still shows your stable installHOME=<path> <command> is standard shell syntax that overrides an environment variable for a single command only. /tmp is cleaned up on reboot, so there's no permanent mess.
When you're done testing, clean up manually if needed:
rm -rf /tmp/vex-devBefore opening a PR, make sure all checks pass:
cargo fmt --all --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
bash scripts/check-docs.shCI runs fmt, clippy, test, and audit checks.
- Keep stable, public project docs in the repository root (for example:
README.md,CHANGELOG.md,CONTRIBUTING.md). - Keep maintainer and process docs under
docs/development/. - Put temporary notes or archival docs under
docs/archive/. docs/archive/should generally stay ignored in.gitignoreto avoid committing temporary notes.
src/
├── main.rs # Thin binary entry point
├── app.rs # CLI dispatch
├── cli/ # clap argument definitions
├── commands/ # Command implementations
├── tools/ # Tool adapters plus shared resolution helpers
├── downloader/ # Download transport, retry, and progress plumbing
├── installer/ # Online/offline install orchestration and extraction helpers
├── switcher/ # Symlink updates, rollback, and failure fixtures
├── resolver/ # Version file discovery and parsing
├── templates/ # Built-in project starters, merge planning, rollback-safe writes
├── team_config/ # Safe `--from` sources (`vex-config.toml`, HTTPS, Git)
├── shell/ # Shell detection and generated hooks
├── updater/ # Self-update release selection, extraction, and repair helpers
├── version_files.rs # `.tool-versions` and single-value writers
├── checksum.rs # Shared SHA256 helpers
├── versioning.rs # Shared version normalization helpers
└── error.rs # Unified error types with actionable suggestions
- Add a new module under
src/tools/(for example,python.rs). - Implement the
Tooltrait (name,list_remote,download_url,bin_names,bin_subpath). - Register it in
get_tool()insrc/tools/mod.rs. - Add unit tests and CLI integration tests.
Permanent Branch:
main— production branch, protected, only accepts PRs- Every merge to
mainshould be release-ready - Tagged with version numbers (for example,
v1.5.0) - CI runs on every push
- Every merge to
Temporary Branches:
Feature branches are created from main and deleted after merging. Branch names must match conventional commit types:
feat/xxx— new features (e.g.,feat/add-python-support)fix/xxx— bug fixes (e.g.,fix/node-checksum-validation)docs/xxx— documentation changes (e.g.,docs/update-installation-guide)chore/xxx— maintenance tasks (e.g.,chore/bump-dependencies)ci/xxx— CI/CD changes (e.g.,ci/add-coverage-report)
Workflow:
- Create a branch from
main:git checkout -b feat/your-feature - Make changes and commit with conventional commit messages
- Push and open a PR to
main - After merge, the branch is automatically deleted
All commits must follow Conventional Commits:
<type>: <description>
[optional body]
Types:
feat:— new featurefix:— bug fixdocs:— documentation onlychore:— maintenance (deps, configs, etc.)ci:— CI/CD changesdeps:— dependency updates
Examples:
feat: add Python support
fix: resolve Node.js download checksum mismatch
docs: clarify installation steps in README
chore: bump MSRV to 1.89
- Create a branch from
mainwith the appropriate prefix (feat/,fix/, etc.) - Make your changes and commit with conventional commit messages
- Open a PR to
main - Ensure all CI checks pass
- Wait for review and approval
- Unit tests: Put in each module under
#[cfg(test)] mod tests - CLI integration tests: Live in
tests/cli_test.rs - End-to-end tests: Live in
tests/e2e_test.rs - Benchmarks: Live in
benches/benchmarks.rs
# Run all tests (excluding ignored)
cargo test
# Run all tests including network-dependent ones
cargo test --features network-tests
# Run specific test
cargo test test_name
# Run benchmarks
cargo bench- Mark network-dependent tests with
#[ignore]so CI does not run them by default - Add tests for new features and bug fixes
- Ensure tests are deterministic and don't depend on external state
- Use
tempfilecrate for temporary directories in tests - Test both success and error cases
- If you touch install/switch failure paths, add or update cleanup/rollback coverage
- If you change templates or team config behavior, update both CLI tests and docs in the same PR
When adding security features, ensure comprehensive test coverage:
- Path traversal protection: Test with malicious paths (
../, absolute paths) - Disk space checks: Test with insufficient space scenarios
- HTTP timeouts: Test timeout and retry logic
- Checksum verification: Test with corrupted downloads
- Lock mechanism: Test concurrent installation attempts
See docs/development/testing.md for detailed testing guidelines.