Development Guide
Complete guide for developing, contributing, and extending Servin Container Runtime, including our enterprise-grade CI/CD pipeline and installer system.
Development Environment
Prerequisites
Set up development environment:
# Required software
- Go 1.21+ (latest stable recommended)
- Git 2.30+
- Make 4.0+
- Docker/Podman (for testing)
- Protocol Buffers compiler (protoc)
- gRPC tools
# Installer development tools
- NSIS 3.0+ (Windows installer development)
- AppImageKit (Linux AppImage creation)
- pkgbuild (macOS package creation)
- QEMU 6.0+ (VM dependencies testing)
# Development tools
- golangci-lint (code linting)
- goreleaser (release automation)
- testify (testing framework)
- mockgen (mock generation)
- protoc-gen-go (protobuf generation)
Environment Setup
# Install Go (Linux/macOS)
curl -L https://go.dev/dl/go1.21.5.linux-amd64.tar.gz | sudo tar -xzC /usr/local
export PATH=$PATH:/usr/local/go/bin
# Install development tools
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/golang/mock/mockgen@latest
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# Clone repository
git clone https://github.com/immyemperor/servin.git
cd servin
# Build cross-platform installer packages
./build-packages.sh --help
# Install dependencies
go mod download
# Build from source
make build
# Run tests
make test
# Install locally
make install
Project Structure
Source Code Organization
servin/
βββ cmd/ # Command line applications
β βββ servind/ # Daemon main
β βββ servin/ # CLI client
β βββ servin-tui/ # Terminal UI
β βββ servin-gui/ # Desktop GUI
βββ pkg/ # Public libraries
β βββ api/ # API definitions
β βββ client/ # Client libraries
β βββ container/ # Container management
β βββ image/ # Image management
β βββ network/ # Network management
β βββ storage/ # Storage drivers
β βββ runtime/ # OCI runtime integration
β βββ util/ # Utility functions
βββ internal/ # Private libraries
β βββ daemon/ # Daemon implementation
β βββ server/ # API server
β βββ cri/ # CRI implementation
β βββ config/ # Configuration
βββ api/ # API specifications
β βββ proto/ # Protocol Buffers
β βββ openapi/ # OpenAPI specifications
βββ docs/ # Documentation
βββ test/ # Test files
β βββ unit/ # Unit tests
β βββ integration/ # Integration tests
β βββ e2e/ # End-to-end tests
βββ scripts/ # Build and utility scripts
βββ deployments/ # Deployment configurations
βββ examples/ # Example configurations
βββ Makefile # Build automation
βββ go.mod # Go module
βββ go.sum # Go dependencies
βββ Dockerfile # Container build
βββ README.md # Project documentation
π CI/CD Pipeline & Installer System
Enterprise-Grade Build Pipeline
Servin includes a comprehensive GitHub Actions CI/CD pipeline that produces enterprise-quality installer packages with embedded VM dependencies.
Build Matrix
strategy:
matrix:
include:
- platform: windows
os: windows-latest
installer: NSIS (.exe)
vm_provider: QEMU/Hyper-V
- platform: linux
os: ubuntu-latest
installer: AppImage
vm_provider: QEMU/KVM
- platform: mac
os: macos-latest
installer: PKG
vm_provider: QEMU/HVF
3-Tier Verification System
# 1. Package Validation
β Platform-specific detection (NSIS/AppImage/PKG)
β Size validation (50MB+ Windows, 30MB+ Linux, 20MB+ macOS)
β File structure verification (PE32/ELF/PKG metadata)
# 2. Integrity Testing
β Binary header validation (PE/ELF magic bytes)
β Cryptographic checksums (SHA256)
β Content validation (component strings)
# 3. VM Dependencies Verification
β Embedded component detection (QEMU, VM images)
β Platform virtualization support verification
β Payload inspection and validation
Cross-Platform Package Building
# Build complete installer packages
./build-packages.sh --windows # Windows NSIS installer
./build-packages.sh --linux # Linux AppImage
./build-packages.sh --macos # macOS PKG installer
./build-packages.sh --all # All platforms
# Validate installer packages
./validate-github-actions.sh # Comprehensive validation
Release Automation
- β Automated Builds: Triggered on tags and commits
- β Quality Gates: 15+ verification points per platform
- β Security Validation: Cryptographic verification and binary integrity
- β Professional Distribution: Organized releases with complete installers
Installer Package Development
Windows NSIS Installer
# installers/windows/servin-installer.nsi
!include "MUI2.nsh"
!include "WinVer.nsh"
Name "Servin Container Runtime"
OutFile "Servin-Installer-${VERSION}.exe"
InstallDir "$PROGRAMFILES64\Servin"
# VM Dependencies Section
Section "VM Dependencies" SecVM
# Install QEMU and VM components
SetOutPath "$INSTDIR\vm"
File /r "vm\*.*"
# Configure Hyper-V if available
Call ConfigureHyperV
SectionEnd
Linux AppImage Creation
# installers/linux/build-appimage.sh
#!/bin/bash
# Build AppImage with embedded QEMU
linuxdeploy \
--appdir AppDir \
--executable servin \
--desktop-file servin.desktop \
--icon-file servin.png \
--plugin qt \
--output appimage
# Embed VM dependencies
mkdir -p AppDir/usr/lib/qemu
cp -r /usr/lib/qemu/* AppDir/usr/lib/qemu/
macOS PKG Creation
# installers/macos/build-package.sh
#!/bin/bash
# Create package with embedded QEMU
pkgbuild \
--root "./package-root" \
--identifier "com.servin.runtime" \
--version "${VERSION}" \
--install-location "/usr/local" \
"Servin-${VERSION}.pkg"
# Include VM dependencies
cp -r /opt/homebrew/bin/qemu* package-root/usr/local/bin/
Core Components
Daemon Architecture
// internal/daemon/daemon.go
package daemon
import (
"context"
"github.com/servin-dev/servin/pkg/container"
"github.com/servin-dev/servin/pkg/image"
"github.com/servin-dev/servin/pkg/network"
"github.com/servin-dev/servin/pkg/storage"
)
type Daemon struct {
containerManager *container.Manager
imageManager *image.Manager
networkManager *network.Manager
storageManager *storage.Manager
config *Config
}
func New(cfg *Config) (*Daemon, error) {
d := &Daemon{
config: cfg,
}
// Initialize managers
d.containerManager = container.NewManager(cfg.ContainerConfig)
d.imageManager = image.NewManager(cfg.ImageConfig)
d.networkManager = network.NewManager(cfg.NetworkConfig)
d.storageManager = storage.NewManager(cfg.StorageConfig)
return d, nil
}
func (d *Daemon) Start(ctx context.Context) error {
// Start all managers
if err := d.storageManager.Start(ctx); err != nil {
return err
}
if err := d.networkManager.Start(ctx); err != nil {
return err
}
if err := d.imageManager.Start(ctx); err != nil {
return err
}
if err := d.containerManager.Start(ctx); err != nil {
return err
}
return nil
}
Container Manager
// pkg/container/manager.go
package container
import (
"context"
"github.com/opencontainers/runtime-spec/specs-go"
)
type Manager struct {
runtime Runtime
store Store
config *Config
}
type Container struct {
ID string
Name string
Image string
Config *specs.Spec
State State
Metadata map[string]string
}
type State int
const (
StateCreated State = iota
StateRunning
StatePaused
StateStopped
StateUnknown
)
func (m *Manager) Create(ctx context.Context, req *CreateRequest) (*Container, error) {
// Validate request
if err := req.Validate(); err != nil {
return nil, err
}
// Generate container ID
id := generateID()
// Create OCI spec
spec, err := m.buildSpec(req)
if err != nil {
return nil, err
}
// Create container
container := &Container{
ID: id,
Name: req.Name,
Image: req.Image,
Config: spec,
State: StateCreated,
}
// Store container
if err := m.store.Save(container); err != nil {
return nil, err
}
return container, nil
}
func (m *Manager) Start(ctx context.Context, id string) error {
container, err := m.store.Get(id)
if err != nil {
return err
}
if container.State != StateCreated {
return ErrInvalidState
}
// Start container via runtime
if err := m.runtime.Start(ctx, container); err != nil {
return err
}
container.State = StateRunning
return m.store.Save(container)
}
Building from Source
Build System
Makefile targets for development:
# Makefile
.PHONY: build test clean install
# Go parameters
GOCMD=go
GOBUILD=$(GOCMD) build
GOCLEAN=$(GOCMD) clean
GOTEST=$(GOCMD) test
GOGET=$(GOCMD) get
GOMOD=$(GOCMD) mod
# Build info
VERSION := $(shell git describe --tags --always --dirty)
COMMIT := $(shell git rev-parse HEAD)
BUILD_TIME := $(shell date -u '+%Y-%m-%d_%H:%M:%S')
# Build flags
LDFLAGS := -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.buildTime=$(BUILD_TIME)"
# Targets
all: build
build: build-daemon build-client build-tui build-gui
build-daemon:
$(GOBUILD) $(LDFLAGS) -o bin/servind cmd/servind/main.go
build-client:
$(GOBUILD) $(LDFLAGS) -o bin/servin cmd/servin/main.go
build-tui:
$(GOBUILD) $(LDFLAGS) -o bin/servin-tui cmd/servin-tui/main.go
build-gui:
$(GOBUILD) $(LDFLAGS) -o bin/servin-gui cmd/servin-gui/main.go
test:
$(GOTEST) -v -race -cover ./...
test-integration:
$(GOTEST) -v -tags=integration ./test/integration/...
test-e2e:
$(GOTEST) -v -tags=e2e ./test/e2e/...
lint:
golangci-lint run
format:
gofmt -s -w .
goimports -w .
clean:
$(GOCLEAN)
rm -rf bin/
install: build
sudo cp bin/servind /usr/local/bin/
sudo cp bin/servin /usr/local/bin/
sudo cp bin/servin-tui /usr/local/bin/
sudo cp bin/servin-gui /usr/local/bin/
docker-build:
docker build -t servin:dev .
proto:
protoc --go_out=. --go-grpc_out=. api/proto/*.proto
deps:
$(GOMOD) download
$(GOMOD) verify
update-deps:
$(GOMOD) get -u ./...
$(GOMOD) tidy
generate:
$(GOCMD) generate ./...
release:
goreleaser release --clean
snapshot:
goreleaser release --snapshot --clean
Cross-platform Build
Build for multiple platforms:
# Build for all platforms
make cross-compile
# Or manually
GOOS=linux GOARCH=amd64 go build -o bin/servin-linux-amd64 cmd/servin/main.go
GOOS=windows GOARCH=amd64 go build -o bin/servin-windows-amd64.exe cmd/servin/main.go
GOOS=darwin GOARCH=amd64 go build -o bin/servin-darwin-amd64 cmd/servin/main.go
GOOS=darwin GOARCH=arm64 go build -o bin/servin-darwin-arm64 cmd/servin/main.go
Container Build
Build Servin in containers:
# Build stage
FROM golang:1.21-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o servind cmd/servind/main.go
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o servin cmd/servin/main.go
# Runtime stage
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /src/servind .
COPY --from=builder /src/servin .
EXPOSE 2375 2376
CMD ["./servind"]
VM Engine Development
Universal Development Provider
Servin includes a Universal Development Provider for cross-platform VM development and testing:
// pkg/vm/universal_development_provider.go
type UniversalDevelopmentVMProvider struct {
config *VMConfig
vmPath string
sshPort int
running bool
containers map[string]*ContainerInfo
}
// State persistence for development workflows
func (p *UniversalDevelopmentVMProvider) saveRunningState() error {
stateFile := filepath.Join(p.vmPath, "vm-running")
if p.running {
return os.WriteFile(stateFile, []byte("running"), 0644)
} else {
os.Remove(stateFile)
return nil
}
}
Development Mode Features
When using --dev flag, Servin provides enhanced development capabilities:
# Enable development mode with VM simulation
servin --dev vm start # Uses universal development provider
servin --dev vm status # Shows simulated VM state
servin --dev vm stop # Graceful shutdown with state persistence
# Development mode automatically:
# - Skips root privilege checks on macOS
# - Uses universal provider across all platforms
# - Persists VM state between command invocations
# - Provides consistent behavior for testing
Cross-Platform Provider System
The VM system supports multiple providers with automatic platform detection:
// Development mode provider selection
func GetVMProvider(config *VMConfig) (VMProvider, error) {
if isDevelopmentMode() {
// Use universal development provider for all platforms
return NewDevelopmentVMProvider(config)
}
// Production providers (platform-specific)
switch runtime.GOOS {
case "darwin":
return NewVirtualizationFrameworkProvider(config)
case "windows":
return NewHyperVProvider(config)
case "linux":
return NewKVMProvider(config)
}
}
VM State Management
The development provider includes file-based state persistence:
# VM state is stored in:
~/.servin/dev-vm/servin-vm/vm-running
# State includes:
# - Running status (file presence indicates running)
# - Container information (JSON metadata)
# - Configuration settings (VM config)
# - Platform information (runtime.GOOS)
GUI Integration Testing
Test VM functionality with the web GUI:
# Start development server with VM support
cd webview_gui
python app.py
# Test VM operations:
# 1. Open http://127.0.0.1:5555
# 2. Navigate to VM Engine section
# 3. Test start/stop operations
# 4. Verify status indicators update correctly
# 5. Check button state management
VM Development Workflow
Recommended development workflow for VM features:
# 1. Clean development environment
rm -rf ~/.servin/dev-vm
# 2. Start with fresh VM state
servin --dev vm status # Should show stopped
# 3. Test VM operations
servin --dev vm start # Should start and persist state
servin --dev vm status # Should show running
servin --dev vm stop # Should stop and clear state
# 4. Verify persistence
servin --dev vm start # Start again
# In another terminal:
servin --dev vm status # Should show running (persisted)
# 5. Test GUI integration
cd webview_gui && python app.py
# Verify web interface reflects correct VM state
Testing
Unit Tests
Write comprehensive unit tests:
// pkg/container/manager_test.go
package container_test
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/servin-dev/servin/pkg/container"
)
func TestManager_Create(t *testing.T) {
tests := []struct {
name string
request *container.CreateRequest
want *container.Container
wantErr bool
}{
{
name: "valid container creation",
request: &container.CreateRequest{
Name: "test-container",
Image: "nginx:latest",
},
want: &container.Container{
Name: "test-container",
Image: "nginx:latest",
State: container.StateCreated,
},
wantErr: false,
},
{
name: "invalid container name",
request: &container.CreateRequest{
Name: "",
Image: "nginx:latest",
},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
manager := setupTestManager(t)
got, err := manager.Create(context.Background(), tt.request)
if tt.wantErr {
assert.Error(t, err)
assert.Nil(t, got)
} else {
assert.NoError(t, err)
assert.NotNil(t, got)
assert.Equal(t, tt.want.Name, got.Name)
assert.Equal(t, tt.want.Image, got.Image)
assert.Equal(t, tt.want.State, got.State)
}
})
}
}
func setupTestManager(t *testing.T) *container.Manager {
mockRuntime := &MockRuntime{}
mockStore := &MockStore{}
config := &container.Config{
Runtime: "test",
}
return container.NewManager(mockRuntime, mockStore, config)
}
Integration Tests
Test component integration:
// test/integration/container_test.go
// +build integration
package integration
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/servin-dev/servin/pkg/container"
)
type ContainerIntegrationSuite struct {
suite.Suite
manager *container.Manager
ctx context.Context
}
func (s *ContainerIntegrationSuite) SetupSuite() {
s.ctx = context.Background()
// Setup real container manager with test config
config := &container.Config{
Runtime: "runc",
StateDir: "/tmp/servin-test",
ImageDir: "/tmp/servin-images",
}
manager, err := container.NewManager(config)
s.Require().NoError(err)
s.manager = manager
}
func (s *ContainerIntegrationSuite) TestContainerLifecycle() {
// Create container
req := &container.CreateRequest{
Name: "test-integration",
Image: "alpine:latest",
Cmd: []string{"sleep", "30"},
}
container, err := s.manager.Create(s.ctx, req)
s.Require().NoError(err)
s.Assert().Equal("test-integration", container.Name)
// Start container
err = s.manager.Start(s.ctx, container.ID)
s.Require().NoError(err)
// Wait for container to be running
time.Sleep(1 * time.Second)
// Check status
status, err := s.manager.Status(s.ctx, container.ID)
s.Require().NoError(err)
s.Assert().Equal(container.StateRunning, status.State)
// Stop container
err = s.manager.Stop(s.ctx, container.ID, 10)
s.Require().NoError(err)
// Remove container
err = s.manager.Remove(s.ctx, container.ID)
s.Require().NoError(err)
}
func TestContainerIntegration(t *testing.T) {
suite.Run(t, new(ContainerIntegrationSuite))
}
End-to-End Tests
Test complete workflows:
// test/e2e/cli_test.go
// +build e2e
package e2e
import (
"os/exec"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCLIWorkflow(t *testing.T) {
// Start daemon
daemon := exec.Command("servind", "--config", "test-config.json")
err := daemon.Start()
assert.NoError(t, err)
defer daemon.Process.Kill()
// Wait for daemon to start
time.Sleep(3 * time.Second)
// Test container creation
cmd := exec.Command("servin", "run", "-d", "--name", "test-e2e", "alpine:latest", "sleep", "30")
output, err := cmd.CombinedOutput()
assert.NoError(t, err)
containerID := strings.TrimSpace(string(output))
assert.NotEmpty(t, containerID)
// Test container listing
cmd = exec.Command("servin", "ps")
output, err = cmd.CombinedOutput()
assert.NoError(t, err)
assert.Contains(t, string(output), "test-e2e")
// Test container stop
cmd = exec.Command("servin", "stop", "test-e2e")
_, err = cmd.CombinedOutput()
assert.NoError(t, err)
// Test container removal
cmd = exec.Command("servin", "rm", "test-e2e")
_, err = cmd.CombinedOutput()
assert.NoError(t, err)
}
Contributing Guidelines
Code Style
Follow Go best practices:
// Good: Clear function names and documentation
// CreateContainer creates a new container with the specified configuration.
// It validates the input parameters and returns an error if validation fails.
func (m *Manager) CreateContainer(ctx context.Context, req *CreateContainerRequest) (*Container, error) {
if err := req.Validate(); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
// Implementation...
}
// Good: Use meaningful variable names
containerManager := container.NewManager(config)
networkInterface := "eth0"
maxRetries := 3
// Bad: Unclear names
mgr := container.NewManager(config)
iface := "eth0"
max := 3
// Good: Handle errors appropriately
result, err := operation()
if err != nil {
return fmt.Errorf("operation failed: %w", err)
}
// Good: Use context for cancellation
func (s *Server) handleRequest(ctx context.Context, req *Request) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
// Handle request
}
}
Commit Messages
Use conventional commit format:
# Format: type(scope): description
feat(container): add support for health checks
fix(api): resolve memory leak in container stats
docs(readme): update installation instructions
test(integration): add network isolation tests
refactor(storage): simplify driver interface
perf(runtime): optimize container startup time
# Breaking changes
feat(api)!: change container create endpoint response format
BREAKING CHANGE: Container create endpoint now returns detailed container info instead of just ID
Pull Request Process
- Fork and Branch
git fork https://github.com/servin-dev/servin git checkout -b feature/my-feature - Make Changes
# Write code # Add tests # Update documentation - Test Changes
make test make test-integration make lint - Commit and Push
git add . git commit -m "feat(container): add health check support" git push origin feature/my-feature - Create Pull Request
- Use descriptive title
- Include detailed description
- Reference related issues
- Add screenshots for UI changes
Code Review Checklist
- Code follows style guidelines
- Tests are included and passing
- Documentation is updated
- No breaking changes (or properly documented)
- Performance impact considered
- Security implications reviewed
- Error handling is appropriate
- Logging is adequate
GUI Development
Architecture Overview
The Servin Desktop GUI is built with a modern web stack:
Backend: Flask REST API server providing container management endpoints Frontend: Modular JavaScript components with responsive CSS design Integration: pywebview for native desktop application experience Distribution: PyInstaller for cross-platform binary compilation
Component Structure
The GUI consists of 7 main JavaScript components:
// Core application controller
static/js/core/ServinGUI.js // Main app initialization and coordination
// Feature components
static/js/components/ContainerDetails.js // Container inspection and management
static/js/components/Logs.js // Real-time log streaming
static/js/components/Terminal.js // Interactive container terminal
static/js/components/FileExplorer.js // Container filesystem browser
// Infrastructure components
static/js/components/APIClient.js // HTTP API communication
static/js/components/SocketManager.js // WebSocket connection management
CSS Framework
Organized styling with 8 CSS modules:
static/css/core/base.css // Base styles and CSS custom properties
static/css/core/layout.css // Main layout and responsive grid
static/css/components/container.css // Container list and cards
static/css/components/container-details.css // Detailed container view
static/css/components/terminal.css // Terminal styling
static/css/components/logs.css // Log display styling
static/css/components/forms.css // Form controls and inputs
static/css/components/buttons.css // Button styles and states
Development Workflow
Set up GUI development environment:
# Navigate to GUI directory
cd cmd/servin-gui
# Install Python dependencies
python -m venv venv
source venv/bin/activate # Linux/macOS
pip install -r requirements.txt
# Run in development mode
python main.py --dev
# Test with browser fallback
python demo.py
Adding New Features
1. Backend API Endpoint
Add new Flask routes in main.py:
@app.route('/api/containers/<container_id>/custom', methods=['GET'])
def get_container_custom(container_id):
try:
# Call servin binary for data
result = subprocess.run([
servin_binary, 'inspect', container_id, '--format', 'json'
], capture_output=True, text=True, check=True)
data = json.loads(result.stdout)
return jsonify(data)
except subprocess.CalledProcessError as e:
return jsonify({'error': str(e)}), 500
2. Frontend Component
Create new component file static/js/components/NewFeature.js:
class NewFeature {
constructor(apiClient) {
this.apiClient = apiClient;
this.initialize();
}
initialize() {
this.setupEventListeners();
this.loadData();
}
async loadData() {
try {
const response = await this.apiClient.request('/api/containers/data');
this.renderData(response);
} catch (error) {
console.error('Failed to load data:', error);
}
}
renderData(data) {
// Update DOM with data
}
setupEventListeners() {
// Add event handlers
}
}
3. CSS Styling
Add component styles in static/css/components/new-feature.css:
.new-feature {
display: flex;
flex-direction: column;
gap: var(--spacing-md);
padding: var(--spacing-lg);
}
.new-feature__header {
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
color: var(--color-text-primary);
}
.new-feature__content {
background: var(--color-surface-secondary);
border-radius: var(--border-radius-md);
padding: var(--spacing-md);
}
Testing GUI Components
Test individual components:
# Run Flask server only (backend testing)
python main.py --no-gui
# Test with different browsers
BROWSER=firefox python demo.py
BROWSER=chrome python demo.py
# Test binary distribution
./build-gui.sh
./bin/servin-gui
Building and Distribution
Create distributable binaries:
# Build for current platform
./build-gui.sh
# Build for all platforms (requires setup)
./build-all.sh
# Manual PyInstaller build
pyinstaller --onefile --windowed \
--add-data "static:static" \
--add-data "templates:templates" \
main.py
Debugging Tips
Component Issues:
- Use browser developer tools for JavaScript debugging
- Check console for API errors and component initialization
- Verify CSS with inspector tools
Backend Issues:
- Enable Flask debug mode with
--devflag - Check terminal output for Python errors
- Verify servin binary path and permissions
WebSocket Issues:
- Monitor network tab for WebSocket connections
- Check SocketManager connection state
- Verify terminal and log streaming functionality
API Development
Adding New Endpoints
Add new API endpoints:
// internal/server/routes.go
func (s *Server) setupRoutes() {
// Container routes
s.router.Handle("/containers/json", s.handleContainerList).Methods("GET")
s.router.Handle("/containers/create", s.handleContainerCreate).Methods("POST")
s.router.Handle("/containers/{id}/start", s.handleContainerStart).Methods("POST")
// New endpoint
s.router.Handle("/containers/{id}/health", s.handleContainerHealth).Methods("GET")
}
func (s *Server) handleContainerHealth(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
health, err := s.daemon.GetContainerHealth(r.Context(), id)
if err != nil {
s.writeError(w, err)
return
}
s.writeJSON(w, health)
}
Protocol Buffers
Define gRPC services:
// api/proto/container.proto
syntax = "proto3";
package servin.container.v1;
option go_package = "github.com/servin-dev/servin/api/container/v1";
service ContainerService {
rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse);
rpc StartContainer(StartContainerRequest) returns (StartContainerResponse);
rpc StopContainer(StopContainerRequest) returns (StopContainerResponse);
rpc ListContainers(ListContainersRequest) returns (ListContainersResponse);
rpc GetContainer(GetContainerRequest) returns (GetContainerResponse);
rpc DeleteContainer(DeleteContainerRequest) returns (DeleteContainerResponse);
}
message Container {
string id = 1;
string name = 2;
string image = 3;
ContainerState state = 4;
map<string, string> labels = 5;
int64 created_at = 6;
int64 started_at = 7;
int64 finished_at = 8;
}
enum ContainerState {
CONTAINER_STATE_UNSPECIFIED = 0;
CONTAINER_STATE_CREATED = 1;
CONTAINER_STATE_RUNNING = 2;
CONTAINER_STATE_PAUSED = 3;
CONTAINER_STATE_STOPPED = 4;
CONTAINER_STATE_REMOVING = 5;
}
message CreateContainerRequest {
string name = 1;
string image = 2;
repeated string command = 3;
repeated string args = 4;
map<string, string> env = 5;
map<string, string> labels = 6;
ContainerConfig config = 7;
}
message CreateContainerResponse {
Container container = 1;
}
Performance Optimization
Profiling
Profile application performance:
// Enable profiling
import _ "net/http/pprof"
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Main application
}
// Profile CPU usage
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
// Profile memory usage
go tool pprof http://localhost:6060/debug/pprof/heap
// Profile goroutines
go tool pprof http://localhost:6060/debug/pprof/goroutine
Benchmarking
Write performance benchmarks:
// pkg/container/manager_bench_test.go
func BenchmarkContainerCreate(b *testing.B) {
manager := setupBenchmarkManager(b)
b.ResetTimer()
for i := 0; i < b.N; i++ {
req := &container.CreateRequest{
Name: fmt.Sprintf("bench-container-%d", i),
Image: "alpine:latest",
}
_, err := manager.Create(context.Background(), req)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkContainerStart(b *testing.B) {
manager := setupBenchmarkManager(b)
containers := make([]*container.Container, b.N)
// Setup containers
for i := 0; i < b.N; i++ {
req := &container.CreateRequest{
Name: fmt.Sprintf("bench-container-%d", i),
Image: "alpine:latest",
}
c, err := manager.Create(context.Background(), req)
if err != nil {
b.Fatal(err)
}
containers[i] = c
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
err := manager.Start(context.Background(), containers[i].ID)
if err != nil {
b.Fatal(err)
}
}
}
Release Process
Versioning
Follow semantic versioning:
- Major: Breaking changes (v1.0.0 β v2.0.0)
- Minor: New features (v1.0.0 β v1.1.0)
- Patch: Bug fixes (v1.0.0 β v1.0.1)
Release Automation
Use GoReleaser for releases:
# .goreleaser.yml
project_name: servin
before:
hooks:
- go mod tidy
- go generate ./...
builds:
- id: servind
main: ./cmd/servind
binary: servind
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X main.version=
- -X main.commit=
- -X main.date=
- id: servin
main: ./cmd/servin
binary: servin
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
archives:
- id: default
builds:
- servind
- servin
name_template: "servin___"
format_overrides:
- goos: windows
format: zip
checksum:
name_template: 'checksums.txt'
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
dockers:
- image_templates:
- "servin/servin:"
- "servin/servin:latest"
dockerfile: Dockerfile
build_flag_templates:
- "--platform=linux/amd64"
release:
github:
owner: servin-dev
name: servin
draft: false
prerelease: auto
π€ Contributing to Servin
Join Our Developer Community
We welcome contributions from developers of all experience levels! Whether youβre fixing a typo, implementing a new feature, or improving performance, every contribution matters.
π₯ Current Contributors
- Brijesh Kumar - Project Founder & Lead Architect
- Abhishek Kumar - Lead Developer & Feature Implementation
π How to Get Started
- Read our CONTRIBUTORS.md - Comprehensive contributor guide
- Check open issues - Find tasks to work on
- Join discussions - Connect with the community
- Follow this development guide - Set up your environment
π‘ Contribution Areas
- π οΈ Core Runtime: Container lifecycle, VM integration, performance
- π¨ User Interfaces: CLI, TUI, GUI improvements
- π Documentation: Guides, examples, API docs
- π§ͺ Testing: Unit tests, integration tests, platform testing
- π§ DevOps: Build system, CI/CD, releases
π Recognition
We recognize contributors through:
- GitHub contributor graphs and commit history
- Documentation acknowledgments in guides and README
- Release notes highlighting major contributions
- Contributor badges for different contribution types
π Get Help
- GitHub Discussions - Ask questions and get help
- GitHub Issues - Report bugs or request features
- Email - Contact core team for private inquiries
This comprehensive development guide covers all aspects of contributing to and extending Servin Container Runtime, from initial setup to production releases.