🐹 CI/CD Integration with TuskLang and Go

Go Documentation

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 main

import ( "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 main

import ( "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 main

import ( "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 main

import ( "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 main

import ( "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 changes

TuskLang 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 compliance

With 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.