Development Setup¶
This guide covers setting up a development environment for contributing to Shed.
Prerequisites¶
- Go 1.24 or later
- Docker (for building the base image and testing)
- Make
- Git
Getting Started¶
Clone the Repository¶
Build¶
Run Tests¶
# Run all tests
make test
# Run tests with coverage
make coverage
# Run tests with race detection
go test -race ./...
Code Quality¶
Project Structure¶
shed/
├── cmd/
│ ├── shed/ # CLI binary
│ │ ├── main.go # Entry point
│ │ ├── client.go # HTTP client for API
│ │ ├── create.go # create command
│ │ ├── console.go # console command
│ │ └── ...
│ └── shed-server/ # Server binary
│ ├── main.go # Entry point
│ ├── serve.go # serve command
│ └── install.go # systemd install
├── internal/
│ ├── api/ # HTTP API handlers
│ ├── config/ # Configuration types
│ ├── docker/ # Docker client wrapper
│ ├── sshd/ # SSH server
│ ├── sshconfig/ # SSH config management
│ ├── provision/ # Provisioning hooks
│ ├── sync/ # File synchronization
│ ├── tunnels/ # SSH tunnel management
│ └── version/ # Version information
├── scripts/
│ └── build-image.sh # Build shed-base image
├── configs/
│ ├── server.example.yaml
│ └── server.dev.yaml
├── docs/
├── Makefile
└── go.mod
Running Locally¶
Single Machine Development¶
Run both CLI and server on the same machine:
# Terminal 1: Start the server
./bin/shed-server serve -c configs/server.dev.yaml
# Terminal 2: Use the CLI
./bin/shed server add localhost
./bin/shed create test-shed
./bin/shed console test-shed
Making Changes¶
Adding a New CLI Command¶
- Create a new file in
cmd/shed/(e.g.,newcmd.go) - Define a
cobra.Commandvariable - Register it in
cmd/shed/main.go - Implement the command logic
// cmd/shed/newcmd.go
package main
import "github.com/spf13/cobra"
var newCmd = &cobra.Command{
Use: "newcmd",
Short: "Description of the new command",
RunE: runNewCmd,
}
func runNewCmd(cmd *cobra.Command, args []string) error {
// Implementation
return nil
}
Adding a New API Endpoint¶
- Add the route in
internal/api/server.go - Add the handler in
internal/api/handlers.go - Add any new types to
internal/config/types.go
Testing¶
Unit Tests¶
Place unit tests alongside the code:
// internal/config/types_test.go
package config
import "testing"
func TestValidateShedName(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
}{
{"valid", "my-shed", false},
{"empty", "", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateShedName(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("ValidateShedName(%q) error = %v", tt.input, err)
}
})
}
}
Integration Tests¶
Use build tags for Docker-dependent tests:
//go:build integration
package docker
func TestCreateShed_Integration(t *testing.T) {
// Test with real Docker
}
Run integration tests:
Continuous Integration¶
GitHub Actions runs on push to main or feature/* branches:
- Test: Runs all unit tests
- Lint: Runs golangci-lint
- Dockerfile Lint: Runs hadolint
Run checks locally before pushing:
Installing Tools¶
# golangci-lint
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.5
# hadolint (for Dockerfile linting)
# macOS: brew install hadolint