Pull-based GitOps-style Docker Compose deployer: polls a (private) Git repo, detects changed stacks and reconciles only the affected
at main 55 lines 1.3 kB view raw
1package stacks 2 3import ( 4 "fmt" 5 "log" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "sync" 10) 11 12func DeployStacks(repoPath string, stacks []string, concurrency int) { 13 semaphore := make(chan struct{}, concurrency) 14 var wg sync.WaitGroup 15 16 for _, stack := range stacks { 17 wg.Add(1) 18 go func(stack string) { 19 defer wg.Done() 20 21 semaphore <- struct{}{} 22 defer func() { <-semaphore }() 23 24 composePath := filepath.Join(repoPath, "stacks", stack, "compose.yml") 25 if _, err := os.Stat(composePath); os.IsNotExist(err) { 26 composePath = filepath.Join(repoPath, "stacks", stack, "compose.yaml") 27 } 28 29 fmt.Printf("Deploying stack: %s\n", stack) 30 if err := deployStack(composePath); err != nil { 31 log.Printf("Failed to deploy stack %s: %v", stack, err) 32 return 33 } 34 fmt.Printf("Successfully deployed stack: %s\n", stack) 35 }(stack) 36 } 37 wg.Wait() 38} 39 40func deployStack(composePath string) error { 41 if _, err := os.Stat(composePath); os.IsNotExist(err) { 42 return fmt.Errorf("compose file does not exist: %s", composePath) 43 } 44 45 composeDir := filepath.Dir(composePath) 46 47 cmd := exec.Command("docker", "compose", "-f", composePath, "up", "-d") 48 cmd.Dir = composeDir 49 output, err := cmd.CombinedOutput() 50 if err != nil { 51 return fmt.Errorf("docker compose up failed: %s, %w", string(output), err) 52 } 53 54 return nil 55}