Development¶
Guide for contributing to txpark development.
Development Setup¶
Prerequisites¶
- Go 1.22+: Install Go
- kubectl 1.25+: Install kubectl
- Docker (optional): For building container images
- Make: For running build tasks
- Git: For version control
Clone Repository¶
Install Dependencies¶
Build from Source¶
# Build binary
go build -o bin/txpark cmd/txpark/main.go
# Or use make
make build
# Install to $GOPATH/bin
go install cmd/txpark/main.go
Run Locally¶
# Run without installing
go run cmd/txpark/main.go --help
# Example: list testnets
go run cmd/txpark/main.go list
# With debugging
go run cmd/txpark/main.go --debug status my-testnet
Project Structure¶
txpark/
├── cmd/
│ └── txpark/
│ └── main.go # Entry point
├── internal/
│ ├── cmd/ # Command implementations
│ │ ├── root.go # Root command & global flags
│ │ ├── deploy.go # Deploy command
│ │ ├── list.go # List command
│ │ ├── status.go # Status command
│ │ ├── logs.go # Logs command
│ │ ├── shell.go # Shell command
│ │ ├── upgrade.go # Upgrade command
│ │ ├── destroy.go # Destroy command
│ │ └── version.go # Version command
│ ├── k8s/ # Kubernetes client
│ │ └── client.go # K8s operations
│ ├── helm/ # Helm client
│ │ └── helm.go # Helm operations
│ ├── gitlab/ # GitLab client
│ │ └── client.go # Pipeline triggering
│ ├── errors/ # Error handling
│ │ └── errors.go # Custom error types
│ ├── logger/ # Logging
│ │ └── logger.go # Structured logging
│ ├── profiles/ # Resource profiles
│ │ └── profiles.go # Profile definitions
│ └── testnet/ # Testnet models
│ └── info.go # Testnet metadata
├── helm/
│ └── txpark-testnet/ # Helm chart
│ ├── Chart.yaml
│ ├── values.yaml
│ └── templates/
├── scripts/ # Helper scripts
│ ├── setup-sequencer.sh # Generate sequencer config
│ └── ...
├── docs/ # MkDocs documentation
│ ├── index.md
│ ├── getting-started.md
│ └── ...
├── mkdocs.yml # MkDocs configuration
├── go.mod # Go module definition
├── go.sum # Go module checksums
├── Makefile # Build automation
└── README.md # Project overview
Key Directories¶
cmd/: Application entry pointsinternal/: Private application code (not importable by other projects)internal/cmd/: CLI command implementations using Cobrainternal/k8s/: Kubernetes client wrapperinternal/helm/: Helm client wrapperhelm/: Helm charts for testnet deployment
Development Workflow¶
1. Create a Feature Branch¶
2. Make Changes¶
Edit code, add tests, update documentation.
3. Test Locally¶
# Build
make build
# Test manually
./bin/txpark list
./bin/txpark status my-testnet
# Run tests
make test
# Run linters
make lint
4. Commit Changes¶
5. Push and Create MR¶
git push origin feature/my-feature
# Create merge request on GitLab
# https://gitlab.com/tezos-infra/evm/txpark/-/merge_requests/new
Adding a New Command¶
Example: Adding a restart command.
1. Create Command File¶
Create internal/cmd/restart.go:
package cmd
import (
"context"
"fmt"
"github.com/spf13/cobra"
"gitlab.com/tezos-infra/evm/txpark/internal/errors"
"gitlab.com/tezos-infra/evm/txpark/internal/logger"
)
var (
// Restart flags
restartComponent string
)
// restartCmd represents the restart command
var restartCmd = &cobra.Command{
Use: "restart <testnet-name>",
Short: "Restart testnet components",
Long: `Restart one or more testnet components.
Examples:
# Restart EVM sequencer
txpark restart my-testnet --component evm-sequencer
# Restart all components
txpark restart my-testnet --all`,
Args: cobra.ExactArgs(1),
RunE: runRestart,
}
func init() {
rootCmd.AddCommand(restartCmd)
restartCmd.Flags().StringVarP(&restartComponent, "component", "c", "", "component to restart")
}
func runRestart(cmd *cobra.Command, args []string) error {
log := GetLogger()
ctx := context.Background()
testnetName := args[0]
log.Info("Restarting testnet component",
logger.String("testnet", testnetName),
logger.String("component", restartComponent))
// Implementation here...
// 1. Get K8s client
// 2. Find pods by component
// 3. Delete pods (triggers restart)
// 4. Wait for new pods to be ready
return nil
}
2. Update Documentation¶
Add command to docs/commands.md.
3. Test¶
Testing¶
Unit Tests¶
# Run all tests
go test ./...
# Run with coverage
go test -cover ./...
# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
Integration Tests¶
Manual Testing¶
# Build
make build
# Deploy test testnet
./bin/txpark deploy test-dev --lifetime 1h
# Verify
./bin/txpark status test-dev
./bin/txpark logs test-dev evm-sequencer
# Cleanup
./bin/txpark destroy test-dev
Code Style¶
Go Guidelines¶
Follow Effective Go and these project-specific conventions:
Error handling:
// ✅ Use custom error types with suggestions
if err != nil {
return errors.NewNotFoundError("testnet", name).
WithSuggestion("run 'txpark list' to see available testnets")
}
// ❌ Don't use bare errors
if err != nil {
return fmt.Errorf("testnet not found: %w", err)
}
Logging:
// ✅ Use structured logging
log.Info("Deploying testnet",
logger.String("name", testnetName),
logger.Int("lifetime", lifetime))
// ❌ Don't use fmt.Printf for logs
fmt.Printf("Deploying %s with lifetime %d\n", testnetName, lifetime)
Command structure:
// ✅ Keep RunE functions focused
func runDeploy(cmd *cobra.Command, args []string) error {
log := GetLogger()
ctx := context.Background()
// 1. Validate inputs
// 2. Perform operation
// 3. Display results
return nil
}
// ❌ Don't mix validation, execution, and display
Formatting¶
Linting¶
# Install golangci-lint
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# Run linters
golangci-lint run
# Fix auto-fixable issues
golangci-lint run --fix
Documentation¶
Code Documentation¶
Add godoc comments to all exported functions:
// Client provides Kubernetes operations for testnet management.
// It wraps the kubernetes.Clientset with helper methods specific
// to txpark functionality.
type Client struct {
clientset kubernetes.Interface
config *Config
log *logger.Logger
}
// GetTestnetNamespace converts a testnet name to its namespace name.
// It prepends "testnet-" to the provided name following the naming convention.
func (c *Client) GetTestnetNamespace(testnetName string) string {
return fmt.Sprintf("testnet-%s", testnetName)
}
MkDocs Documentation¶
When adding features, update relevant documentation:
- Getting Started: For installation or first-use changes
- Commands: For new commands or flag changes
- Workflows: For new usage patterns
- Best Practices: For recommendations
- Development: For contributor-facing changes
Building Documentation¶
Setup Python Environment¶
It's recommended to use a Python virtual environment to avoid conflicts with system packages:
# Create virtual environment
python -m venv .venv
# Activate virtual environment
source .venv/bin/activate # On Linux/macOS
# or
.venv\Scripts\activate # On Windows
# Install dependencies
pip install mkdocs-material
Build and Preview¶
# Build documentation
make doc
# Serve locally for preview (with live reload)
make doc-serve
# Opens at http://127.0.0.1:8000
# When done, deactivate virtual environment
deactivate
Note: The .venv/ directory is already in .gitignore and won't be committed.
Deployment¶
Infrastructure Setup¶
The documentation is hosted as a pod in the Kubernetes cluster. No additional infrastructure setup is required beyond the cluster itself.
Deploying Documentation¶
Deploy content to the cluster:
# Activate venv if not already
source .venv/bin/activate
# Build, push, and deploy in one command
make doc-deploy
This will: 1. Build the MkDocs site 2. Create a Docker image with nginx, docs, and install script 3. Push to the container registry 4. Deploy to the cluster using Helm
Manual Steps¶
If you prefer to run steps individually:
# Build documentation
make doc
# Build Docker image
make doc-build-image
# Push to registry
make doc-push
# Deploy with Helm
helm upgrade --install txpark-docs ./helm/txpark-docs \
--namespace txpark-system --create-namespace
Accessing Content¶
After deployment, content is accessible via HTTPS:
- Documentation: https://doc.txpark.nomadic-labs.com/
- Install script: https://doc.txpark.nomadic-labs.com/install.sh
Building Releases¶
Version Tagging¶
Build Binaries¶
# Build for all platforms
make build-all
# Build for specific platform
GOOS=linux GOARCH=amd64 go build -o bin/txpark-linux-amd64 cmd/txpark/main.go
GOOS=darwin GOARCH=arm64 go build -o bin/txpark-darwin-arm64 cmd/txpark/main.go
Release Process¶
- Update version in
cmd/txpark/main.go - Update CHANGELOG.md
- Create and push version tag
- GitLab CI builds and uploads binaries
- Create release notes on GitLab
Contributing Guidelines¶
Before Submitting¶
- [ ] Code follows project style guidelines
- [ ] Tests pass:
go test ./... - [ ] Linters pass:
golangci-lint run - [ ] Documentation updated
- [ ] Commit messages are descriptive
- [ ] Branch is up to date with main
Merge Request Template¶
## Description
Brief description of the changes.
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
Describe testing performed:
- [ ] Unit tests added/updated
- [ ] Manual testing completed
- [ ] Integration tests passed
## Checklist
- [ ] Code follows project style
- [ ] Documentation updated
- [ ] Tests added/updated
- [ ] No breaking changes (or documented)
## Related Issues
Closes #123
Code Review Process¶
- Create merge request
- Automated checks run (tests, linters)
- Team reviews code
- Address feedback
- Maintainer approves and merges
Debugging¶
Enable Debug Logging¶
# Run with debug output
go run cmd/txpark/main.go --debug status my-testnet
# Or with built binary
./bin/txpark --debug logs my-testnet evm-sequencer
Using Delve Debugger¶
# Install delve
go install github.com/go-delve/delve/cmd/dlv@latest
# Debug with breakpoints
dlv debug cmd/txpark/main.go -- status my-testnet
# In delve:
# (dlv) break internal/cmd/status.go:42
# (dlv) continue
# (dlv) print testnetName
Common Issues¶
Import cycle:
package gitlab.com/tezos-infra/evm/txpark/internal/cmd
imports gitlab.com/tezos-infra/evm/txpark/internal/k8s
imports gitlab.com/tezos-infra/evm/txpark/internal/cmd: import cycle
Solution: Refactor to break the cycle, usually by moving shared code to a separate package.
Undefined function:
Solution: Check internal/logger/logger.go for available helper functions. Use logger.Any for complex types.
Architecture Decisions¶
Why Cobra?¶
- Industry standard for Go CLIs
- Rich feature set (subcommands, flags, aliases)
- Excellent documentation and community support
- Easy to test and maintain
Why Custom Error Types?¶
// Provides better UX with actionable suggestions
errors.NewNotFoundError("testnet", name).
WithSuggestion("run 'txpark list' to see available testnets")
Instead of generic errors, users get: - Clear error classification - Contextual information - Actionable next steps
Why Helm?¶
- Declarative infrastructure
- Templating and values system
- Rollback capabilities
- Wide adoption in Kubernetes ecosystem
Performance Considerations¶
Efficient Kubernetes Queries¶
// ✅ Use label selectors
pods, err := clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{
LabelSelector: "component=evm-sequencer",
})
// ❌ Don't fetch all and filter in Go
allPods, _ := clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{})
for _, pod := range allPods.Items {
if pod.Labels["component"] == "evm-sequencer" { ... }
}
Parallel Operations¶
// When listing multiple testnets
var wg sync.WaitGroup
results := make(chan *testnet.Info, len(namespaces))
for _, ns := range namespaces {
wg.Add(1)
go func(namespace string) {
defer wg.Done()
info, err := testnet.GetInfo(ctx, client, namespace)
if err == nil {
results <- info
}
}(ns)
}
wg.Wait()
close(results)
Security Best Practices¶
Never Log Secrets¶
// ❌ Don't log sensitive data
log.Info("Creating secret", logger.String("secretKey", secretValue))
// ✅ Log only metadata
log.Info("Creating secret", logger.String("name", secretName))
Validate Input¶
// Always validate user input
func validateTestnetName(name string) error {
pattern := `^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
matched, _ := regexp.MatchString(pattern, name)
if !matched {
return errors.NewValidationError("invalid testnet name")
}
return nil
}
Getting Help¶
- Issues: GitLab Issues
- Merge Requests: GitLab MRs
- Documentation: doc.txpark.nomadic-labs.com
Next Steps¶
- Review Commands to understand existing functionality
- Check Workflows for usage examples
- Read Best Practices for operational guidelines