Contributing to VEM
Thank you for your interest in contributing to VEM! This guide will help you get started with contributing to the project.
Table of Contents
- Getting Started
- Development Environment
- Code Style and Guidelines
- Testing
- Submitting Changes
- Release Process
- Community Guidelines
Getting Started
Prerequisites
Ensure you have the required development tools:
# Rust toolchain (1.70+)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup update stable
# Development dependencies
sudo apt-get install universal-ctags vim neovim git
# Additional tools
cargo install cargo-tarpaulin # Coverage
cargo install cargo-audit # Security auditing
cargo install cargo-clippy # Linting
Fork and Clone
-
Fork the repository on GitHub
-
Clone your fork:
git clone https://github.com/YOUR-USERNAME/vem.git cd vem -
Add upstream remote:
git remote add upstream https://github.com/ryo-arima/vem.git -
Verify setup:
git remote -v # origin https://github.com/YOUR-USERNAME/vem.git (fetch) # origin https://github.com/YOUR-USERNAME/vem.git (push) # upstream https://github.com/ryo-arima/vem.git (fetch) # upstream https://github.com/ryo-arima/vem.git (push)
Development Environment
Building VEM
# Debug build (development)
cargo build
# Release build (production)
cargo build --release
# Run from source
cargo run -- --help
# Install locally for testing
cargo install --path .
Setting Up Test Environment
# Create test workspace
mkdir -p ~/vem-dev-test
cd ~/vem-dev-test
# Set VEM_HOME for testing
export VEM_HOME="$PWD/.vem"
# Initialize test environment
vem init
# Create sample environments for testing
cp -r ~/vem/etc/.vem/envs/* ~/.vem/environments/
Development Workflow
-
Create feature branch:
git checkout -b feature/your-feature-name -
Make changes following code guidelines
-
Test changes:
# Run tests make test # Run linting make lint # Test manually cargo run -- create test-env -
Commit changes:
git add . git commit -m "feat: add your feature description"
Code Style and Guidelines
Rust Style
VEM follows Rust standard conventions:
# Format code
cargo fmt
# Check formatting
cargo fmt -- --check
# Lint code
cargo clippy -- -D warnings
# Check security
cargo audit
Code Organization
src/
├── main.rs # Entry point
├── base.rs # Core application structure
├── mod.rs # Module declarations
├── cnf/ # Configuration management
├── ctl/ # Controllers (command handlers)
├── ent/ # Entities (data structures)
├── rep/ # Repositories (data access)
├── usc/ # Use cases (business logic)
└── util/ # Utilities and helpers
Naming Conventions
- Files: Snake case (
environment_manager.rs) - Functions: Snake case (
create_environment) - Structs: Pascal case (
EnvironmentConfig) - Constants: Screaming snake case (
DEFAULT_CONFIG_PATH) - Modules: Snake case (
ctags_manager)
Error Handling
Use VEM’s custom error types:
#![allow(unused)] fn main() { use crate::util::error::{VemError, Result}; fn example_function() -> Result<String> { let config = load_config() .map_err(|e| VemError::ConfigError(format!("Failed to load: {}", e)))?; Ok(config.name.clone()) } }
Documentation
- Public functions: Must have doc comments
- Complex logic: Inline comments explaining why, not what
- Examples: Include usage examples for public APIs
#![allow(unused)] fn main() { /// Creates a new environment with the specified configuration. /// /// # Arguments /// /// * `name` - The name of the environment to create /// * `config` - Environment configuration /// /// # Examples /// /// ``` /// use vem::environment::create_environment; /// /// let config = EnvironmentConfig::default(); /// create_environment("my-env", config)?; /// ``` /// /// # Errors /// /// Returns `VemError::EnvironmentExists` if environment already exists. pub fn create_environment(name: &str, config: EnvironmentConfig) -> Result<()> { // Implementation } }
Testing
Running Tests
# All tests
cargo test
# Unit tests only
cargo test --lib
# Integration tests only
cargo test --test '*'
# Specific test
cargo test test_create_environment
# With coverage
cargo tarpaulin --out html
Writing Tests
Unit Tests
#![allow(unused)] fn main() { #[cfg(test)] mod tests { use super::*; use tempfile::TempDir; #[test] fn test_parse_environment_config() { let toml_content = r#" name = "test-env" description = "Test environment" editor = "vim" "#; let config = parse_config(toml_content).unwrap(); assert_eq!(config.name, "test-env"); assert_eq!(config.editor, "vim"); } #[test] fn test_create_environment_with_temp_dir() { let temp_dir = TempDir::new().unwrap(); let vem_home = temp_dir.path().join(".vem"); let result = create_environment_at_path(&vem_home, "test", &Config::default()); assert!(result.is_ok()); assert!(vem_home.join("environments").join("test").exists()); } } }
Integration Tests
#![allow(unused)] fn main() { // tests/integration/environment_management.rs use std::process::Command; use tempfile::TempDir; #[test] fn test_full_environment_workflow() { let temp_dir = TempDir::new().unwrap(); // Create environment let output = Command::new("cargo") .args(&["run", "--", "create", "test-env"]) .env("VEM_HOME", temp_dir.path()) .output() .unwrap(); assert!(output.status.success()); // Switch environment let output = Command::new("cargo") .args(&["run", "--", "switch", "test-env"]) .env("VEM_HOME", temp_dir.path()) .output() .unwrap(); assert!(output.status.success()); // Verify current environment let output = Command::new("cargo") .args(&["run", "--", "current"]) .env("VEM_HOME", temp_dir.path()) .output() .unwrap(); let current = String::from_utf8(output.stdout).unwrap(); assert!(current.contains("test-env")); } }
Test Data Management
Create reusable test fixtures:
#![allow(unused)] fn main() { // tests/common/fixtures.rs use tempfile::TempDir; use std::path::PathBuf; pub struct TestEnvironment { pub temp_dir: TempDir, pub vem_home: PathBuf, } impl TestEnvironment { pub fn new() -> Self { let temp_dir = TempDir::new().unwrap(); let vem_home = temp_dir.path().join(".vem"); std::fs::create_dir_all(&vem_home).unwrap(); Self { temp_dir, vem_home } } pub fn create_sample_config(&self, name: &str) -> PathBuf { let config_content = format!(r#" name = "{}" description = "Test environment" editor = "vim" [plugin_managers] vim-plug = true "#, name); let config_path = self.vem_home.join("environments").join(name).join("vem.toml"); std::fs::create_dir_all(config_path.parent().unwrap()).unwrap(); std::fs::write(&config_path, config_content).unwrap(); config_path } } }
Submitting Changes
Pull Request Process
-
Ensure tests pass:
make test make lint -
Update documentation if needed
-
Create descriptive commits:
# Good commit messages git commit -m "feat: add multi-repository ctags support" git commit -m "fix: handle empty configuration files gracefully" git commit -m "docs: update installation guide for macOS" -
Push to your fork:
git push origin feature/your-feature-name -
Create pull request on GitHub:
- Use descriptive title
- Include detailed description
- Reference related issues
- Add screenshots for UI changes
Commit Message Format
Use Conventional Commits:
type(scope): description
[optional body]
[optional footer]
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasks
Examples:
feat(ctags): add multi-repository support
fix(config): handle malformed TOML gracefully
docs(api): update environment management examples
refactor(cli): extract command parsing logic
test(integration): add environment switching tests
chore(deps): update dependencies to latest versions
PR Review Process
- Automated checks: CI must pass
- Code review: At least one maintainer approval required
- Manual testing: Reviewers may test changes locally
- Documentation: Ensure docs are updated for user-facing changes
Review Guidelines
For contributors:
- Respond promptly to review feedback
- Keep PRs focused and atomic
- Include tests for new functionality
- Update documentation for API changes
For reviewers:
- Be constructive and specific
- Test changes manually when appropriate
- Check for security implications
- Verify backwards compatibility
Release Process
Version Numbering
VEM follows Semantic Versioning:
- MAJOR: Breaking changes
- MINOR: New features (backwards compatible)
- PATCH: Bug fixes (backwards compatible)
Release Steps
- Update version in
Cargo.toml - Update CHANGELOG.md with release notes
- Create release tag:
git tag -a v1.2.0 -m "Release v1.2.0" git push upstream v1.2.0 - GitHub Actions automatically builds and publishes releases
Release Notes Format
## [1.2.0] - 2024-01-15
### Added
- Multi-repository ctags support
- AI-enhanced context for development tools
- New sample environments (ai-development, modern-nvim)
### Changed
- Improved configuration validation
- Enhanced error messages
### Fixed
- Environment switching on Windows
- TOML parsing edge cases
### Security
- Updated dependencies with security patches
Community Guidelines
Code of Conduct
VEM follows the Rust Code of Conduct. Key points:
- Be respectful and inclusive
- Be constructive in discussions and reviews
- Focus on technical merit of contributions
- Help newcomers learn and contribute
Communication Channels
- GitHub Issues: Bug reports and feature requests
- GitHub Discussions: Questions and general discussion
- Pull Requests: Code contributions and technical discussions
Getting Help
For new contributors:
- Look for issues labeled
good first issue - Ask questions in GitHub Discussions
- Review existing code and documentation
- Start with small improvements
For development questions:
- Check existing documentation
- Search GitHub Issues and Discussions
- Create a discussion post with specific questions
Recognition
Contributors are recognized through:
- CONTRIBUTORS.md: Listed alphabetically
- Release notes: Feature contributions acknowledged
- GitHub: Contributor statistics and graphs
Development Best Practices
Performance Considerations
- Profile before optimizing: Use
cargo benchfor benchmarking - Memory efficiency: Avoid unnecessary allocations
- I/O operations: Use async where appropriate
- Large datasets: Consider streaming for file operations
Security Guidelines
- Input validation: Sanitize all external inputs
- File permissions: Use appropriate file modes
- Path traversal: Validate paths to prevent directory escape
- Dependencies: Regularly audit with
cargo audit
Debugging
# Debug builds include more information
cargo build
# Enable debug logging
RUST_LOG=debug cargo run -- command
# Use debugger
rust-gdb target/debug/vem
# Memory debugging
valgrind --tool=memcheck target/debug/vem command
IDE Setup
VS Code:
- Install
rust-analyzerextension - Configure
settings.json:{ "rust-analyzer.check.command": "clippy", "rust-analyzer.cargo.features": "all" }
Vim/Neovim:
- Use
rust.vimplugin - Configure with Language Server Protocol (LSP)
Documentation
- Code documentation: Use rustdoc comments (
///) - User documentation: Update
docs/src/for user-facing changes - API documentation: Generate with
cargo doc --open - Examples: Include usage examples in documentation
Thank You
Your contributions help make VEM better for everyone. Whether you’re fixing bugs, adding features, improving documentation, or helping other users, every contribution is valued and appreciated!
For questions about contributing, please open a GitHub Discussion or contact the maintainers.