Guide for contributing to Proxsave, including development setup, coding guidelines, and useful commands.
- Overview
- Development Setup
- Project Structure
- Building & Running
- Testing
- Dependency Management
- Code Guidelines
- Contributing
- Useful Commands
- Related Documentation
Proxsave is built with modern Go practices, emphasizing:
- Performance: Compiled binary, concurrent operations
- Reliability: Comprehensive error handling, safe defaults
- Maintainability: Clean architecture, modular design
- Testability: Unit tests, integration tests, mocking
Technology stack:
- Language: Go 1.25+
- Dependencies: See
go.modfor complete list - Build system: Makefile + Go modules
- Compression: xz, zstd, gzip, bzip2, lz4
- Encryption: AGE (age-encryption.org)
- Cloud storage: rclone integration
# Go 1.25 or later
go version
# Build tools
make --version
# Optional: rclone for cloud storage development
rclone version
# Optional: age for encryption development
age --version# Clone from GitHub
git clone https://github.com/tis24dev/proxsave.git
cd proxsave
# Install dependencies
go mod tidy
# Build
make build
# Run tests
go test ./...Recommended IDE setup:
- VSCode with Go extension
- GoLand by JetBrains
- Vim/Neovim with vim-go
VSCode settings (.vscode/settings.json):
{
"go.useLanguageServer": true,
"go.lintTool": "golangci-lint",
"go.lintOnSave": "workspace",
"go.formatTool": "goimports",
"editor.formatOnSave": true,
"go.testFlags": ["-v"],
"go.coverOnSave": true
}proxsave/
├── cmd/
│ └── proxsave/ # Main entry point
├── internal/ # Private application code
│ ├── backup/ # Archiving, manifests, checksums
│ ├── checks/ # Dependency/system checks
│ ├── cli/ # CLI argument parsing
│ ├── config/ # Configuration management + templates
│ ├── environment/ # Environment detection
│ ├── identity/ # Identity helpers
│ ├── logging/ # Logging
│ ├── metrics/ # Prometheus metrics export
│ ├── notify/ # Notification channels (Telegram/Email/Gotify/Webhook)
│ ├── orchestrator/ # Backup/restore workflows
│ ├── pbs/ # PBS helpers
│ ├── security/ # Security checks, permissions
│ ├── storage/ # Storage backends (local/secondary/cloud)
│ ├── tui/ # TUI wizards
│ ├── types/ # Shared types
│ └── version/ # Version info
├── pkg/ # Shared helper packages for Proxsave (not an implicit stable external API)
├── build/ # Build artifacts (binary output)
├── configs/ # Configuration files
├── docs/ # Documentation
├── go.mod # Go module definition
├── go.sum # Dependency checksums
├── Makefile # Build automation
└── README.md # Main documentation
| Module | Purpose | Files |
|---|---|---|
| orchestrator | Core backup/restore orchestration and capability-based restore decisions | internal/orchestrator/*.go |
| config | Configuration management | internal/config/config.go |
| storage | Local/secondary/cloud storage | internal/storage/*.go |
| backup | Collector recipes/bricks, archiving, manifest/checksum helpers | internal/backup/*.go |
| notify | Notification channels | internal/notify/*.go |
| security | Security checks, permissions | internal/security/*.go |
The backup collector is no longer organized around large branch-specific wrappers. It is built from explicit recipes and fine-grained bricks:
newPVERecipe()newPBSRecipe()newDualRecipe()newSystemRecipe()
Important invariants:
dualis a real type, not an aliasdualcomposes PVE + PBS bricks in a single runsystem/commonruns only oncestorage_stackbelongs tocommon/system, not PBS
For the authoritative architecture description, see Collector Architecture.
# Standard development build
make build
# Output: build/proxsave# Optimized build (stripped symbols, smaller binary)
go build -ldflags="-s -w" -o build/proxsave ./cmd/proxsave
# With version info
VERSION=$(git describe --tags --always)
go build -ldflags="-s -w -X main.version=${VERSION}" -o build/proxsave ./cmd/proxsave# Run directly with go run
make run
# Or manually
go run ./cmd/proxsave# Remove build directory
make clean
# Full clean (including dependencies cache)
go clean -cache -modcache
make build# Linux AMD64 (the only published target)
GOOS=linux GOARCH=amd64 go build -o build/proxsave-linux-amd64 ./cmd/proxsave# All tests
go test ./...
# With coverage
go test -cover ./...
# With coverage report
make test-coverage
# Output: coverage.html (open in browser)# Specific package
go test ./internal/config
# Specific test function
go test ./internal/config -run TestLoadConfig
# Verbose output
go test -v ./...# Generate coverage profile
go test -coverprofile=coverage.out ./...
# View coverage in terminal
go tool cover -func=coverage.out
# Generate HTML report
go tool cover -html=coverage.out -o coverage.html# Run benchmarks
go test -bench=. ./...
# Benchmark with memory stats
go test -bench=. -benchmem ./...
# Benchmark specific function
go test -bench=BenchmarkCompression ./internal/compression# Add new dependency
go get github.com/spf13/cobra@latest
# Add specific version
go get github.com/spf13/cobra@v1.8.0
# Tidy up (remove unused, add missing)
go mod tidy# Update all dependencies
go get -u ./...
# Update specific dependency
go get -u github.com/spf13/cobra
# Tidy after updates
go mod tidy# List all dependencies
go list -m all
# List direct dependencies only
go list -m -f '{{if not .Indirect}}{{.}}{{end}}' all
# Check for available updates
go list -u -m all# Create vendor directory (optional)
go mod vendor
# Build with vendor
go build -mod=vendor -o build/proxsave ./cmd/proxsave- Follow Effective Go
- Use
gofmtfor formatting (automatic withgo fmt) - Run
golangci-lintbefore committing - Write godoc comments for exported functions
- Handle errors explicitly (no silent failures)
- Use
context.Contextfor cancellation
Naming conventions:
// Exported (public)
func BackupOrchestrator() {}
type Config struct {}
const MaxRetries = 3
// Unexported (private)
func backupHelper() {}
type internalState struct {}
const defaultTimeout = 30Error handling:
// Good: Explicit error handling
result, err := doSomething()
if err != nil {
return fmt.Errorf("failed to do something: %w", err)
}
// Bad: Ignoring errors
result, _ := doSomething()Context usage:
// Good: Pass context
func ProcessBackup(ctx context.Context, cfg *Config) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
// Process backup
}
}Test file naming: *_test.go
Test function naming: TestFunctionName
Table-driven tests:
func TestCompressionAlgorithms(t *testing.T) {
tests := []struct {
name string
algorithm string
level int
wantErr bool
}{
{"XZ Level 6", "xz", 6, false},
{"Zstd Level 3", "zstd", 3, false},
{"Invalid Algorithm", "invalid", 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test logic
})
}
}Godoc comments:
// BackupOrchestrator coordinates the entire backup process.
// It handles collection, compression, encryption, and storage distribution.
//
// Parameters:
// - ctx: Context for cancellation
// - cfg: Configuration settings
//
// Returns:
// - error: nil on success, error details on failure
func BackupOrchestrator(ctx context.Context, cfg *Config) error {
// Implementation
}Add tests for new features:
- Unit tests for all new functions
- Integration tests for workflows
- Benchmark tests for performance-critical code
Update documentation for changes:
- Update relevant
docs/*.mdfiles - Update README.md if user-facing changes
- Add examples for new features
We welcome contributions! Here's how you can help:
- 🐛 Report bugs: Open an issue with detailed reproduction steps
- 💡 Suggest features: Share your ideas for improvements
- 📖 Improve documentation: Fix typos, add examples, clarify instructions
- 💻 Submit code: Fork, create a branch, and submit a pull request
- ⭐ Star the repo: Show your support!
1. Fork and clone:
# Fork on GitHub, then:
git clone https://github.com/YOUR_USERNAME/proxsave.git
cd proxsave
git remote add upstream https://github.com/tis24dev/proxsave.git2. Create feature branch:
git checkout -b feature/your-feature-name3. Make changes:
# Write code
# Add tests
# Update documentation
# Format code
go fmt ./...
# Run tests
go test ./...
# Run linter (if installed)
golangci-lint run4. Commit changes:
# Atomic commits with clear messages
git add .
git commit -m "Add: feature description
Detailed explanation of changes:
- Added X functionality
- Updated Y module
- Fixed Z issue"Commit message format:
- Add: New feature or functionality
- Update: Improvement to existing feature
- Fix: Bug fix
- Refactor: Code refactoring without functional changes
- Docs: Documentation changes
- Test: Test additions or updates
5. Push and create PR:
git push origin feature/your-feature-name
# Create pull request on GitHub6. Code review:
- Address review comments
- Push updates to the same branch
- PR automatically updates
PR description should include:
- What problem does it solve?
- How does it solve it?
- Any breaking changes?
- Testing performed
Example PR description:
## Description
Adds support for GFS retention policies with configurable tiers.
## Problem
Users need long-term retention policies that comply with Grandfather-Father-Son backup strategies.
## Solution
- Implemented GFS retention logic in `internal/retention/gfs.go`
- Added configuration variables: RETENTION_DAILY, RETENTION_WEEKLY, RETENTION_MONTHLY, RETENTION_YEARLY
- Updated orchestrator to call GFS retention when RETENTION_POLICY=gfs
## Breaking Changes
None. Existing simple retention remains default.
## Testing
- Unit tests for GFS logic
- Integration test with 365 daily backups
- Manual testing with real PVE installation# Development build
make build
# Optimized build
go build -ldflags="-s -w" -o build/proxsave ./cmd/proxsave
# Run without building
make run
# Clean build artifacts
make clean# All tests
go test ./...
# With coverage
go test -cover ./...
make test-coverage
# Specific package
go test ./internal/config
# Verbose
go test -v ./...
# Benchmarks
go test -bench=. ./...
# Lint (requires golangci-lint)
golangci-lint run# Add dependency
go get github.com/spf13/cobra@latest
# Update all dependencies
go get -u ./...
# Tidy up
go mod tidy
# List dependencies
go list -m all
# Vendor dependencies
go mod vendor# Run with delve debugger
dlv debug ./cmd/proxsave
# Run with race detector
go run -race ./cmd/proxsave
# Build with debug symbols
go build -gcflags="all=-N -l" -o build/proxsave-debug ./cmd/proxsave# List remotes
rclone listremotes
# Show remote config
rclone config show gdrive
# List files (long format)
rclone lsl gdrive:pbs-backups/
# List files (short format)
rclone lsf gdrive:pbs-backups/
# Check quota
rclone about gdrive:
# Copy local → remote
rclone copy /local/file.txt gdrive:pbs-backups/
# Copy remote → local
rclone copy gdrive:pbs-backups/file.txt /local/
# Sync (WARNING: deletes non-matching files)
rclone sync /local/dir/ gdrive:pbs-backups/
# Create directory
rclone mkdir gdrive:pbs-backups/subdir
# Delete file
rclone deletefile gdrive:pbs-backups/file.txt
# Delete directory (recursive)
rclone purge gdrive:pbs-backups/old/
# Verify integrity
rclone check /local/dir/ gdrive:pbs-backups/ --checksum- Docs Index - Documentation hub for the
docs/tree - Configuration Guide - All configuration variables
- CLI Reference - Command-line flags
- Collector Architecture - Collector recipes, bricks, and
dual - Restore Technical - Restore internals and compatibility flow
- Migration Guide - Config (backup.env) migration to Go (
--env-migration) - Troubleshooting - Common issues
- Effective Go - Go best practices
- Go Modules - Dependency management
- rclone Documentation - Cloud storage integration
- AGE Specification - Encryption format
Use this checklist when contributing:
Before Coding:
□ Fork and clone repository
□ Create feature branch
□ Review related issues/PRs
□ Plan implementation approach
During Development:
□ Follow Go best practices
□ Write clear, self-documenting code
□ Add godoc comments for exports
□ Handle errors explicitly
□ Use context for cancellation
Testing:
□ Write unit tests for new functions
□ Add integration tests for workflows
□ Run all tests (go test ./...)
□ Check coverage (make test-coverage)
□ Run linter (golangci-lint run)
Documentation:
□ Update relevant docs/*.md files
□ Add usage examples
□ Update docs/README.md and architecture docs if navigation changes
□ Write clear commit messages
Before Submitting PR:
□ Rebase on latest main
□ Run full test suite
□ Verify no breaking changes
□ Check documentation is updated
□ Write clear PR description
After PR Submission:
□ Address review comments
□ Push updates to same branch
□ Thank reviewers
□ Celebrate when merged! 🎉
For reviewers:
- ✅ Verify tests pass
- ✅ Check code follows Go best practices
- ✅ Ensure documentation is updated
- ✅ Look for potential security issues
- ✅ Verify error handling is robust
- ✅ Check for race conditions (use
-race) - ✅ Ensure commit messages are clear
For contributors:
- ⏰ Be patient - reviews take time
- 📝 Respond to all comments
- 🙏 Thank reviewers for their time
- 🔄 Push updates to same branch (no force push after review starts)
- ✅ Mark resolved comments
This project is licensed under the MIT License - see the LICENSE file for details.
- GitHub Issues: https://github.com/tis24dev/proxsave/issues
- Pull Requests: https://github.com/tis24dev/proxsave/pulls
- Discussions: Use GitHub Discussions for questions and ideas
For security vulnerabilities: Please email privately instead of opening a public issue.
Thank you for contributing to Proxsave! 🎉