Contributing Guidelines
Welcome
Thank you for your interest in contributing to cpm! This document provides guidelines and best practices for contributing to the project. We welcome contributions of all types: code, documentation, bug reports, feature requests, and more.
Table of Contents
- Code of Conduct
- Getting Started
- Development Setup
- Contribution Workflow
- Coding Standards
- Testing Guidelines
- Documentation
- Pull Request Process
- Community
Code of Conduct
Our Standards
- Be respectful and inclusive
- Welcome newcomers and beginners
- Focus on constructive feedback
- Assume good intentions
- Respect differing viewpoints
Unacceptable Behavior
- Harassment or discrimination
- Trolling or insulting comments
- Personal or political attacks
- Publishing others' private information
- Other unprofessional conduct
Getting Started
Types of Contributions
We welcome:
- Bug fixes: Fix issues in existing code
- Features: Implement new functionality
- Documentation: Improve or add documentation
- Tests: Add or improve test coverage
- Performance: Optimize existing code
- Refactoring: Improve code quality
- Examples: Add usage examples
Before You Start
- Check existing issues and pull requests
- For large changes, open an issue to discuss
- Ensure your idea aligns with project goals
- Ask questions if anything is unclear
Development Setup
Prerequisites
# Required
- Go 1.24.0 or later
- Git 2.x or later
- SQLite3 development libraries
- SSH client
- rsync
# Optional
- make (for Makefile commands)
- Docker (for testing)
Setup Steps
-
Fork the repository:
# Click "Fork" on GitHub git clone https://github.com/YOUR_USERNAME/cpm.git cd cpm -
Add upstream remote:
git remote add upstream https://github.com/ORIGINAL_OWNER/cpm.git git fetch upstream -
Install dependencies:
go mod download -
Build the project:
go build -o cpm -
Run tests:
go test ./... -
Initialize cpm:
./cpm config init
Development Environment
# Set up development config
export GITM_CONFIG=~/.cpm/dev-config.yaml
export GITM_DATA_DIR=~/cpm-dev/data
# Use development database
export GITM_DATABASE=~/cpm-dev/test.db
# Run in development mode
./cpm --verbose <command>
Contribution Workflow
1. Create a Branch
# Update main branch
git checkout main
git pull upstream main
# Create feature branch
git checkout -b feature/your-feature-name
# Or for bug fixes
git checkout -b fix/bug-description
Branch Naming Convention
feature/- New featuresfix/- Bug fixesdocs/- Documentation changesrefactor/- Code refactoringtest/- Test additions/changesperf/- Performance improvements
2. Make Changes
# Make your changes
vim internal/repo/repo.go
# Test your changes
go test ./internal/repo/
# Build and test manually
go build
./cpm <test-command>
3. Commit Changes
# Stage changes
git add <files>
# Commit with descriptive message
git commit -m "feat: add repository tagging support"
Commit Message Format
Use conventional commits format:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting)refactor: Code refactoringtest: Adding or updating testsperf: Performance improvementschore: Maintenance tasks
Examples:
feat(ssh): add ed25519 key generation support
Implements ed25519 key pair generation with proper
permissions (0600 for private, 0644 for public).
Closes #123
fix(sync): resolve rsync timeout on large repos
Increase default timeout from 60s to 300s for large
repository transfers. Add configurable timeout option.
Fixes #456
4. Push Changes
# Push to your fork
git push origin feature/your-feature-name
5. Create Pull Request
- Go to GitHub repository
- Click "New Pull Request"
- Select your fork and branch
- Fill out PR template
- Submit for review
Coding Standards
Go Style Guide
Follow standard Go conventions:
// Package documentation
package repo
import (
"fmt"
"os"
"cpm/internal/db"
)
// Function documentation
// InitRepo initializes a new bare git repository at the specified path.
// It creates the directory structure and runs git init --bare.
//
// Parameters:
// name: Repository name (without .git extension)
// path: Directory path to create repository in
//
// Returns:
// error: Initialization error if any
func InitRepo(name, path string) error {
// Validate inputs
if name == "" {
return fmt.Errorf("repository name cannot be empty")
}
// Implementation
repoPath := filepath.Join(path, name+".git")
// Create directory
if err := os.MkdirAll(repoPath, 0755); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
// Initialize git repository
cmd := exec.Command("git", "init", "--bare", repoPath)
if err := cmd.Run(); err != nil {
return fmt.Errorf("git init failed: %w", err)
}
return nil
}
Code Organization
internal/
├── config/ # Configuration management
│ ├── config.go # Core config functions
│ └── config_test.go
├── db/ # Database operations
│ ├── db.go # Connection management
│ ├── schema.go # Table schemas
│ ├── models.go # Data models
│ ├── crud.go # CRUD operations
│ └── db_test.go
└── repo/ # Repository operations
├── repo.go # Core repository functions
├── transfer.go # Push/pull operations
└── repo_test.go
Best Practices
-
Error Handling: Always wrap errors with context
if err != nil { return fmt.Errorf("operation failed: %w", err) } -
Variable Names: Use descriptive names
// Good repositoryPath := "/path/to/repo" // Avoid p := "/path/to/repo" -
Function Length: Keep functions focused (< 50 lines)
-
Comments: Document exported functions and complex logic
-
Constants: Use constants for magic numbers
const ( DefaultSSHPort = 22 DefaultTimeout = 300 * time.Second )
Testing Guidelines
Unit Tests
func TestInitRepo(t *testing.T) {
// Setup
tmpDir := t.TempDir()
repoName := "test-repo"
// Execute
err := InitRepo(repoName, tmpDir)
// Assert
if err != nil {
t.Fatalf("InitRepo failed: %v", err)
}
// Verify repository exists
repoPath := filepath.Join(tmpDir, repoName+".git")
if _, err := os.Stat(repoPath); os.IsNotExist(err) {
t.Errorf("Repository not created at %s", repoPath)
}
}
Table-Driven Tests
func TestValidateUsername(t *testing.T) {
tests := []struct {
name string
username string
wantErr bool
}{
{"valid", "alice", false},
{"valid-hyphen", "alice-smith", false},
{"empty", "", true},
{"invalid-chars", "alice@example", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateUsername(tt.username)
if (err != nil) != tt.wantErr {
t.Errorf("ValidateUsername() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
Test Coverage
Aim for 80%+ test coverage:
# Run tests with coverage
go test -cover ./...
# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
Integration Tests
// +build integration
func TestEndToEnd(t *testing.T) {
// Setup test environment
// Run full workflow
// Verify end-to-end functionality
}
Run integration tests:
go test -tags=integration ./...
Documentation
Code Documentation
Document all exported functions:
// CreateUser creates a new user in the database with the provided details.
// The username must be unique. If a public key is provided, it will be
// validated before storage.
//
// Parameters:
// username: Unique username (alphanumeric, hyphens, underscores)
// email: Valid email address
// publicKey: SSH public key in authorized_keys format (optional)
//
// Returns:
// int64: New user ID
// error: Creation error (e.g., duplicate username, invalid email)
//
// Example:
// userID, err := db.CreateUser("alice", "alice@example.com", "ssh-ed25519 AAAA...")
// if err != nil {
// log.Fatal(err)
// }
func (db *DB) CreateUser(username, email, publicKey string) (int64, error) {
// Implementation
}
User Documentation
Update relevant documentation files:
- Command reference for new commands
- Architecture docs for system changes
- Troubleshooting for known issues
- Examples for new features
Changelog
Add entries to CHANGELOG.md:
## [Unreleased]
### Added
- Repository tagging support for categorization
### Fixed
- Sync timeout on repositories larger than 1GB
### Changed
- Default SSH key algorithm to ed25519
Pull Request Process
PR Checklist
Before submitting:
- Code follows project style guidelines
- Tests added for new functionality
- All tests pass locally
- Documentation updated
- Commit messages follow convention
- Branch is up to date with main
- Self-review completed
PR Template
Fill out the PR template:
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
Describe testing performed
## Screenshots (if applicable)
Add screenshots for UI changes
## Checklist
- [ ] Tests pass
- [ ] Documentation updated
- [ ] Changelog updated
Review Process
- Automated Checks: CI/CD runs tests
- Code Review: Maintainers review code
- Revisions: Address feedback
- Approval: At least one maintainer approval
- Merge: Maintainer merges PR
Addressing Feedback
# Make requested changes
git add <files>
git commit -m "address review feedback"
# Update PR
git push origin feature/your-feature-name
Release Process
Versioning
We use Semantic Versioning (SemVer):
- MAJOR: Breaking changes
- MINOR: New features (backward compatible)
- PATCH: Bug fixes
Release Workflow
- Update CHANGELOG.md
- Update version in code
- Create git tag
- Build binaries
- Create GitHub release
- Announce release
Community
Communication Channels
- GitHub Issues: Bug reports, feature requests
- GitHub Discussions: Questions, ideas
- Pull Requests: Code contributions
Getting Help
- Check documentation
- Search existing issues
- Ask in GitHub Discussions
- Tag maintainers if urgent
Recognition
Contributors are recognized in:
- CONTRIBUTORS.md file
- Release notes
- Project documentation
Questions?
If you have questions about contributing:
- Check this guide
- Review existing issues/PRs
- Ask in GitHub Discussions
- Contact maintainers
Thank You!
Your contributions make cpm better for everyone. We appreciate your time and effort!
Happy Contributing!