Complete Development Guide
Comprehensive guide for developing and contributing to Simple VPS Provisioner.
View Quick Development Overview →
Table of Contents
- Development Environment
- Code Architecture
- Development Workflows
- Testing Guide
- Contributing Process
- Release Process
Development Environment
Prerequisites
Required:
- Go 1.21 or higher
- Git
- Linux/macOS or WSL on Windows
Recommended:
- Debian 12 or Ubuntu 22.04 VM for testing
- VS Code with Go extension
- Docker for testing in clean environments
Initial Setup
# Clone repository
git clone https://github.com/willjackson/simple-vps-provisioner.git
cd simple-vps-provisioner
# Initialize modules
go mod tidy
# Build
go build -o svp
# Run
./svp --version
Code Architecture
Package Organization
pkg/
├── cms/ # CMS installation (Drupal, WordPress)
├── config/ # Configuration files and management
├── database/ # MariaDB setup and operations
├── ssl/ # Let's Encrypt and SSL certificates
├── system/ # System packages, firewall, services
├── updater/ # Self-update functionality
├── utils/ # Shared utilities (logging, exec, checks)
└── web/ # Nginx and PHP-FPM configuration
Design Principles
- Separation of Concerns - Each package handles one domain
- No Circular Dependencies - Clear dependency hierarchy
- Idempotency - Safe to run multiple times
- Verify-Only Pattern - Check before modify
Example Function Pattern
// InstallSomething installs and configures something
func InstallSomething(verifyOnly bool) error {
// Check if already done
if isAlreadyDone() {
utils.Verify("Something already configured")
return nil
}
// If verify-only mode, report failure
if verifyOnly {
utils.Fail("Something not configured")
return fmt.Errorf("something not configured")
}
// Actually do the work
utils.Log("Installing something...")
if err := doWork(); err != nil {
return fmt.Errorf("failed to install: %v", err)
}
utils.Ok("Something installed successfully")
return nil
}
Development Workflows
Adding a New Feature
- Create Feature Branch
git checkout -b feature/my-feature - Identify Package
- System operations →
pkg/system/ - Web server →
pkg/web/ - Database →
pkg/database/ - CMS-specific →
pkg/cms/
- System operations →
- Implement Feature
// pkg/system/myfeature.go package system import "svp/pkg/utils" func ConfigureMyFeature(verifyOnly bool) error { // Implementation } - Add CLI Flag (if needed)
// types/types.go type Config struct { MyFeature bool } // main.go flag.BoolVar(&cfg.MyFeature, "my-feature", false, "Enable my feature") - Update Command Handler
// cmd/setup.go if cfg.MyFeature { if err := system.ConfigureMyFeature(cfg.VerifyOnly); err != nil { return err } } - Test Locally
go build -o svp sudo ./svp setup -my-feature - Document
- Update README.md
- Update docs/
- Update CHANGELOG.md
- Commit and Push
git add . git commit -m "Add my feature" git push origin feature/my-feature
Testing Guide
Manual Testing
- Spin up test VM
# Debian 12 or Ubuntu 22.04 multipass launch --name svp-test 22.04 multipass shell svp-test - Build and copy
go build -o svp multipass transfer svp svp-test:/home/ubuntu/ - Test on VM
multipass shell svp-test # Test without SSL (omit --le-email) sudo ./svp setup --cms drupal --domain test.local - Verify
sudo ./svp verify curl -I http://test.local
Testing Checklist
- Fresh Debian 12 installation
- Fresh Ubuntu 22.04 installation
- Drupal installation
- WordPress installation
- Multi-domain setup
- Git deployment
- Database import
- SSL certificate
- PHP version update
- Verify mode
- Update mode
Contributing Process
1. Fork and Clone
# Fork on GitHub, then:
git clone https://github.com/YOUR_USERNAME/simple-vps-provisioner.git
cd simple-vps-provisioner
# Add upstream
git remote add upstream https://github.com/willjackson/simple-vps-provisioner.git
2. Create Feature Branch
git checkout -b feature/my-feature
3. Make Changes
- Write clear, concise code
- Follow existing patterns
- Add comments for exported functions
- Handle errors properly
4. Test Thoroughly
- Build without errors
- Test on real Debian/Ubuntu
- Check all modes work
- Verify doesn’t break existing functionality
5. Commit
git add .
git commit -m "Add feature: description"
Good commit messages:
- “Add support for PostgreSQL database”
- “Fix SSL certificate renewal issue”
- “Update PHP version detection logic”
Bad commit messages:
- “fix”
- “update”
- “changes”
6. Push and Create PR
git push origin feature/my-feature
Then create Pull Request on GitHub with:
- Clear title
- Description of changes
- Why the change is needed
- How it was tested
7. Address Feedback
# Make changes
git add .
git commit -m "Address review feedback"
git push origin feature/my-feature
Release Process
Semantic Versioning
Format: MAJOR.MINOR.PATCH
- MAJOR: Breaking changes
- MINOR: New features (backward compatible)
- PATCH: Bug fixes
Release Steps
1. Prepare
# Ensure on main branch
git checkout main
git pull origin main
# Update CHANGELOG.md
vim CHANGELOG.md
# Move [Unreleased] items to [1.0.31]
# Commit
git add CHANGELOG.md
git commit -m "Prepare release v1.0.31"
git push origin main
2. Tag
# Create annotated tag
git tag -a v1.0.31 -m "Release v1.0.31"
# Push tag
git push origin v1.0.31
3. Automated Build
GitHub Actions will automatically:
- Build for all platforms
- Generate checksums
- Create GitHub release
- Upload binaries
4. Verify
# Check release page
https://github.com/willjackson/simple-vps-provisioner/releases
# Test installation
curl -fsSL https://raw.githubusercontent.com/willjackson/simple-vps-provisioner/main/install-from-github.sh | sudo bash
# Check version
svp --version
Code Guidelines
File Organization
// Package declaration
package system
// Imports (stdlib first, then external, then local)
import (
"fmt"
"strings"
"svp/pkg/utils"
)
// Constants
const (
DefaultValue = "default"
)
// Type definitions
type Config struct {
Value string
}
// Exported functions (documented)
// PublicFunction does something useful
func PublicFunction() error {
// Implementation
}
// Unexported functions
func privateHelper() error {
// Implementation
}
Error Handling
// Good ✅
if err := DoSomething(); err != nil {
return fmt.Errorf("failed to do something: %v", err)
}
// Bad ❌
err := DoSomething()
if err != nil {
return err // No context
}
// Bad ❌
_ = DoSomething() // Ignored error
Logging
// Use utils logger
utils.Log("Starting operation...")
utils.Verify("Checking status")
utils.Ok("Operation complete")
// Don't print directly
fmt.Println("Message") // ❌
Debugging
Enable Debug Mode
# Environment variable
export DEBUG=1
./svp setup
# Or flag
./svp --debug setup
Common Issues
Build fails:
export GO111MODULE=on
go mod tidy
go build
Import errors:
# Use full import paths
import "svp/pkg/utils" // ✓
import "./pkg/utils" // ✗
Module issues:
go clean -modcache
go mod download
go mod tidy
Resources
Documentation
Go Resources
External Tools
Getting Help
- GitHub Issues: Report bugs
- Discussions: Ask questions
| ← Back to Development Overview | Documentation → |