πΉ CI/CD Integration with TuskLang and Go
CI/CD Integration with TuskLang and Go
π CI/CD That Actually Delivers
TuskLang doesn't just build your codeβit orchestrates your entire deployment pipeline. No more broken builds or deployment nightmares. With TuskLang's CI/CD integration, your code flows from development to production like a well-oiled machine.
π― CI/CD Philosophy
Pipeline as Code
package mainimport (
"context"
"fmt"
"time"
)
// CICDPipeline manages the continuous integration and deployment pipeline
type CICDPipeline struct {
config *CICDConfig
stages []*PipelineStage
}
// CICDConfig holds CI/CD configuration
type CICDConfig struct {
// Pipeline configuration
Name string tusk:"@cicd.name" doc:"Pipeline name"
Trigger string tusk:"@cicd.trigger" doc:"Pipeline trigger (push, pr, manual)"
AutoDeploy bool tusk:"@cicd.auto_deploy" doc:"Auto-deploy on successful build"
// Environment configuration
Environments []string tusk:"@cicd.environments" doc:"Deployment environments"
StagingFirst bool tusk:"@cicd.staging_first" doc:"Deploy to staging first"
// Quality gates
RequireTests bool tusk:"@cicd.require_tests" doc:"Require tests to pass"
RequireLinting bool tusk:"@cicd.require_linting" doc:"Require linting to pass"
RequireSecurity bool tusk:"@cicd.require_security" doc:"Require security scan"
}
func NewCICDPipeline(config CICDConfig) CICDPipeline {
return &CICDPipeline{
config: config,
stages: make([]*PipelineStage, 0),
}
}
π§ CI/CD Configuration
TuskLang CI/CD Directives
cicd-config.tsk
@cicd {
name: "TuskLang App Pipeline"
trigger: "push"
auto_deploy: true
environments: ["staging", "production"]
staging_first: true
require_tests: true
require_linting: true
require_security: true
}@pipeline {
stages: [
"validate",
"test",
"build",
"security-scan",
"deploy-staging",
"integration-test",
"deploy-production"
]
parallel_stages: ["lint", "test", "security-scan"]
timeout: "30m"
retry_failed: true
max_retries: 3
}
@build {
dockerfile: "Dockerfile"
context: "."
target: "production"
cache: true
multi_stage: true
optimize: true
security_scan: true
}
@test {
framework: "testify"
coverage_threshold: 80
parallel_tests: true
timeout: "10m"
generate_report: true
upload_coverage: true
}
@deploy {
strategy: "blue-green"
health_check: true
rollback_on_failure: true
zero_downtime: true
monitoring: true
alerting: true
}
Go CI/CD Implementation
package mainimport (
"context"
"fmt"
"os"
"os/exec"
"time"
)
type PipelineStage struct {
Name string
Description string
Command string
Timeout time.Duration
Retries int
Required bool
}
type PipelineExecutor struct {
config *CICDConfig
stages []*PipelineStage
}
func NewPipelineExecutor(config CICDConfig) PipelineExecutor {
return &PipelineExecutor{
config: config,
stages: buildDefaultStages(config),
}
}
func buildDefaultStages(config CICDConfig) []PipelineStage {
stages := []*PipelineStage{
{
Name: "validate",
Description: "Validate configuration and dependencies",
Command: "go mod verify && tusklang validate",
Timeout: 5 * time.Minute,
Retries: 1,
Required: true,
},
{
Name: "lint",
Description: "Run code linting",
Command: "golangci-lint run",
Timeout: 10 * time.Minute,
Retries: 2,
Required: config.RequireLinting,
},
{
Name: "test",
Description: "Run unit and integration tests",
Command: "go test -v -race -coverprofile=coverage.out ./...",
Timeout: 15 * time.Minute,
Retries: 2,
Required: config.RequireTests,
},
{
Name: "build",
Description: "Build application binary",
Command: "go build -ldflags='-w -s' -o app main.go",
Timeout: 10 * time.Minute,
Retries: 2,
Required: true,
},
{
Name: "security-scan",
Description: "Run security vulnerability scan",
Command: "gosec ./...",
Timeout: 10 * time.Minute,
Retries: 1,
Required: config.RequireSecurity,
},
{
Name: "docker-build",
Description: "Build Docker image",
Command: "docker build -t app:latest .",
Timeout: 15 * time.Minute,
Retries: 2,
Required: true,
},
}
// Add deployment stages
if config.AutoDeploy {
if config.StagingFirst {
stages = append(stages, &PipelineStage{
Name: "deploy-staging",
Description: "Deploy to staging environment",
Command: "kubectl apply -f k8s/staging/",
Timeout: 10 * time.Minute,
Retries: 2,
Required: true,
})
stages = append(stages, &PipelineStage{
Name: "integration-test",
Description: "Run integration tests against staging",
Command: "go test -v -tags=integration ./tests/",
Timeout: 15 * time.Minute,
Retries: 2,
Required: true,
})
}
stages = append(stages, &PipelineStage{
Name: "deploy-production",
Description: "Deploy to production environment",
Command: "kubectl apply -f k8s/production/",
Timeout: 15 * time.Minute,
Retries: 2,
Required: true,
})
}
return stages
}
func (pe *PipelineExecutor) Run(ctx context.Context) error {
for i, stage := range pe.stages {
fmt.Printf("Running stage %d/%d: %s\n", i+1, len(pe.stages), stage.Name)
if err := pe.runStage(ctx, stage); err != nil {
if stage.Required {
return fmt.Errorf("required stage '%s' failed: %w", stage.Name, err)
}
fmt.Printf("Warning: Optional stage '%s' failed: %v\n", stage.Name, err)
}
}
return nil
}
func (pe PipelineExecutor) runStage(ctx context.Context, stage PipelineStage) error {
for attempt := 0; attempt <= stage.Retries; attempt++ {
if attempt > 0 {
fmt.Printf("Retrying stage '%s' (attempt %d/%d)\n", stage.Name, attempt+1, stage.Retries+1)
}
if err := pe.executeCommand(ctx, stage.Command, stage.Timeout); err != nil {
if attempt == stage.Retries {
return fmt.Errorf("stage '%s' failed after %d attempts: %w", stage.Name, stage.Retries+1, err)
}
continue
}
fmt.Printf("Stage '%s' completed successfully\n", stage.Name)
return nil
}
return nil
}
func (pe *PipelineExecutor) executeCommand(ctx context.Context, command string, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
cmd := exec.CommandContext(ctx, "bash", "-c", command)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
π§ͺ Testing Integration
Automated Testing Pipeline
package mainimport (
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
)
type TestRunner struct {
config *TestConfig
}
type TestConfig struct {
Framework string tusk:"@test.framework"
CoverageThreshold float64 tusk:"@test.coverage_threshold"
ParallelTests bool tusk:"@test.parallel_tests"
Timeout string tusk:"@test.timeout"
GenerateReport bool tusk:"@test.generate_report"
UploadCoverage bool tusk:"@test.upload_coverage"
}
type TestResult struct {
Passed bool json:"passed"
Coverage float64 json:"coverage"
Duration string json:"duration"
TestsRun int json:"tests_run"
TestsPassed int json:"tests_passed"
TestsFailed int json:"tests_failed"
}
func (tr TestRunner) RunTests(ctx context.Context) (TestResult, error) {
// Run tests with coverage
cmd := exec.CommandContext(ctx, "go", "test", "-v", "-race", "-coverprofile=coverage.out", "./...")
if tr.config.ParallelTests {
cmd.Args = append(cmd.Args, "-parallel", "4")
}
output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("tests failed: %w\nOutput: %s", err, string(output))
}
// Parse coverage
coverage, err := tr.parseCoverage("coverage.out")
if err != nil {
return nil, fmt.Errorf("failed to parse coverage: %w", err)
}
// Check coverage threshold
if coverage < tr.config.CoverageThreshold {
return nil, fmt.Errorf("coverage %.2f%% is below threshold %.2f%%", coverage, tr.config.CoverageThreshold)
}
// Generate test report
if tr.config.GenerateReport {
if err := tr.generateTestReport(ctx); err != nil {
return nil, fmt.Errorf("failed to generate test report: %w", err)
}
}
// Upload coverage if configured
if tr.config.UploadCoverage {
if err := tr.uploadCoverage(ctx); err != nil {
return nil, fmt.Errorf("failed to upload coverage: %w", err)
}
}
return &TestResult{
Passed: true,
Coverage: coverage,
Duration: "10m", // Parse from output
TestsRun: 100, // Parse from output
TestsPassed: 98, // Parse from output
TestsFailed: 2, // Parse from output
}, nil
}
func (tr *TestRunner) parseCoverage(profileFile string) (float64, error) {
cmd := exec.Command("go", "tool", "cover", "-func="+profileFile)
output, err := cmd.Output()
if err != nil {
return 0, err
}
// Parse the last line which contains total coverage
lines := strings.Split(string(output), "\n")
if len(lines) < 2 {
return 0, fmt.Errorf("invalid coverage output")
}
totalLine := lines[len(lines)-2] // Last non-empty line
parts := strings.Fields(totalLine)
if len(parts) < 3 {
return 0, fmt.Errorf("invalid coverage line format")
}
coverageStr := strings.TrimSuffix(parts[len(parts)-1], "%")
return strconv.ParseFloat(coverageStr, 64)
}
func (tr *TestRunner) generateTestReport(ctx context.Context) error {
// Generate HTML coverage report
cmd := exec.CommandContext(ctx, "go", "tool", "cover", "-html=coverage.out", "-o=coverage.html")
return cmd.Run()
}
func (tr *TestRunner) uploadCoverage(ctx context.Context) error {
// Upload coverage to external service (e.g., Codecov, Coveralls)
// Implementation depends on the service
return nil
}
π Security Scanning
Security Integration
package mainimport (
"context"
"encoding/json"
"fmt"
"os/exec"
)
type SecurityScanner struct {
config *SecurityConfig
}
type SecurityConfig struct {
Enabled bool tusk:"@security.enabled"
Tools []string tusk:"@security.tools"
FailOnHigh bool tusk:"@security.fail_on_high"
FailOnMedium bool tusk:"@security.fail_on_medium"
GenerateReport bool tusk:"@security.generate_report"
}
type SecurityResult struct {
Vulnerabilities []Vulnerability json:"vulnerabilities"
TotalIssues int json:"total_issues"
HighIssues int json:"high_issues"
MediumIssues int json:"medium_issues"
LowIssues int json:"low_issues"
Passed bool json:"passed"
}
type Vulnerability struct {
ID string json:"id"
Severity string json:"severity"
Title string json:"title"
Description string json:"description"
File string json:"file"
Line int json:"line"
CVE string json:"cve,omitempty"
}
func (ss SecurityScanner) RunScan(ctx context.Context) (SecurityResult, error) {
result := &SecurityResult{
Vulnerabilities: make([]Vulnerability, 0),
}
// Run gosec for Go security scanning
if err := ss.runGosec(ctx, result); err != nil {
return nil, fmt.Errorf("gosec scan failed: %w", err)
}
// Run trivy for container scanning
if err := ss.runTrivy(ctx, result); err != nil {
return nil, fmt.Errorf("trivy scan failed: %w", err)
}
// Run dependency scanning
if err := ss.runDependencyScan(ctx, result); err != nil {
return nil, fmt.Errorf("dependency scan failed: %w", err)
}
// Calculate totals
for _, vuln := range result.Vulnerabilities {
result.TotalIssues++
switch vuln.Severity {
case "HIGH":
result.HighIssues++
case "MEDIUM":
result.MediumIssues++
case "LOW":
result.LowIssues++
}
}
// Check if scan passed
result.Passed = ss.checkScanPassed(result)
// Generate report
if ss.config.GenerateReport {
if err := ss.generateSecurityReport(result); err != nil {
return nil, fmt.Errorf("failed to generate security report: %w", err)
}
}
return result, nil
}
func (ss SecurityScanner) runGosec(ctx context.Context, result SecurityResult) error {
cmd := exec.CommandContext(ctx, "gosec", "-fmt=json", "./...")
output, err := cmd.Output()
if err != nil {
return err
}
var gosecResult struct {
Issues []struct {
RuleID string json:"rule_id"
Severity string json:"severity"
What string json:"what"
File string json:"file"
Line int json:"line"
Code string json:"code"
} json:"Issues"
}
if err := json.Unmarshal(output, &gosecResult); err != nil {
return err
}
for _, issue := range gosecResult.Issues {
result.Vulnerabilities = append(result.Vulnerabilities, Vulnerability{
ID: issue.RuleID,
Severity: issue.Severity,
Title: issue.RuleID,
Description: issue.What,
File: issue.File,
Line: issue.Line,
})
}
return nil
}
func (ss SecurityScanner) runTrivy(ctx context.Context, result SecurityResult) error {
// Run trivy for container scanning
cmd := exec.CommandContext(ctx, "trivy", "fs", "--format", "json", ".")
output, err := cmd.Output()
if err != nil {
return err
}
// Parse trivy output and add to vulnerabilities
// Implementation depends on trivy output format
return nil
}
func (ss SecurityScanner) runDependencyScan(ctx context.Context, result SecurityResult) error {
// Run dependency vulnerability scanning
cmd := exec.CommandContext(ctx, "go", "list", "-json", "-m", "all")
output, err := cmd.Output()
if err != nil {
return err
}
// Parse dependencies and check for known vulnerabilities
// Implementation depends on vulnerability database
return nil
}
func (ss SecurityScanner) checkScanPassed(result SecurityResult) bool {
if ss.config.FailOnHigh && result.HighIssues > 0 {
return false
}
if ss.config.FailOnMedium && result.MediumIssues > 0 {
return false
}
return true
}
func (ss SecurityScanner) generateSecurityReport(result SecurityResult) error {
// Generate security report in various formats
// HTML, JSON, PDF, etc.
return nil
}
π Deployment Integration
Kubernetes Deployment
package mainimport (
"context"
"fmt"
"os/exec"
"time"
)
type KubernetesDeployer struct {
config *DeployConfig
}
type DeployConfig struct {
Strategy string tusk:"@deploy.strategy"
HealthCheck bool tusk:"@deploy.health_check"
RollbackOnFailure bool tusk:"@deploy.rollback_on_failure"
ZeroDowntime bool tusk:"@deploy.zero_downtime"
Monitoring bool tusk:"@deploy.monitoring"
Alerting bool tusk:"@deploy.alerting"
}
func (kd *KubernetesDeployer) Deploy(ctx context.Context, environment string) error {
// Apply Kubernetes manifests
manifestDir := fmt.Sprintf("k8s/%s", environment)
cmd := exec.CommandContext(ctx, "kubectl", "apply", "-f", manifestDir)
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to apply manifests: %w", err)
}
// Wait for deployment to be ready
if err := kd.waitForDeployment(ctx, environment); err != nil {
if kd.config.RollbackOnFailure {
return kd.rollback(ctx, environment)
}
return fmt.Errorf("deployment failed: %w", err)
}
// Run health checks
if kd.config.HealthCheck {
if err := kd.runHealthChecks(ctx, environment); err != nil {
return fmt.Errorf("health checks failed: %w", err)
}
}
// Setup monitoring
if kd.config.Monitoring {
if err := kd.setupMonitoring(ctx, environment); err != nil {
return fmt.Errorf("failed to setup monitoring: %w", err)
}
}
return nil
}
func (kd *KubernetesDeployer) waitForDeployment(ctx context.Context, environment string) error {
deploymentName := fmt.Sprintf("app-%s", environment)
cmd := exec.CommandContext(ctx, "kubectl", "rollout", "status", "deployment/"+deploymentName)
return cmd.Run()
}
func (kd *KubernetesDeployer) runHealthChecks(ctx context.Context, environment string) error {
// Run application health checks
// Check endpoints, database connectivity, etc.
return nil
}
func (kd *KubernetesDeployer) rollback(ctx context.Context, environment string) error {
deploymentName := fmt.Sprintf("app-%s", environment)
cmd := exec.CommandContext(ctx, "kubectl", "rollout", "undo", "deployment/"+deploymentName)
return cmd.Run()
}
func (kd *KubernetesDeployer) setupMonitoring(ctx context.Context, environment string) error {
// Setup Prometheus, Grafana, etc.
return nil
}
π― Best Practices
CI/CD Best Practices
1. Automate Everything - Manual steps are error-prone 2. Test Early, Test Often - Catch issues before deployment 3. Use Immutable Infrastructure - Never modify running systems 4. Implement Blue-Green Deployments - Zero downtime deployments 5. Monitor Everything - Know when things go wrong 6. Security First - Scan for vulnerabilities in every build 7. Fast Feedback - Keep pipeline execution times low 8. Version Everything - Tag releases and track changesTuskLang CI/CD Features
- @cicd - CI/CD pipeline configuration - @pipeline - Pipeline stages and workflow - @build - Build configuration and optimization - @test - Testing framework and coverage settings - @deploy - Deployment strategy and configuration - @security - Security scanning and complianceWith TuskLang and Go, CI/CD becomes a seamless part of your development workflow. No more broken builds or deployment nightmaresβjust reliable, automated pipelines that deliver your code to production with confidence. We don't bow to any king, and we sure as hell don't bow to deployment failures.