Pull-based GitOps-style Docker Compose deployer: polls a (private) Git repo, detects changed stacks and reconciles only the affected
at main 96 lines 2.5 kB view raw
1package core 2 3import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "strings" 8) 9 10func PullAndDetectChanges(repoPath, branch string) ([]string, error) { 11 if err := ensureGitRepository(repoPath); err != nil { 12 return nil, err 13 } 14 15 prevHead, err := getGitHead(repoPath) 16 if err != nil { 17 return nil, fmt.Errorf("failed to get previous HEAD: %w", err) 18 } 19 20 if err := gitFetchPull(repoPath, branch); err != nil { 21 return nil, fmt.Errorf("failed to pull: %w", err) 22 } 23 24 newHead, err := getGitHead(repoPath) 25 if err != nil { 26 return nil, fmt.Errorf("failed to get new HEAD: %w", err) 27 } 28 if prevHead == newHead { 29 return []string{}, nil 30 } 31 32 output, err := gitCommand(repoPath, "diff", "--name-only", prevHead, newHead) 33 if err != nil { 34 return nil, fmt.Errorf("failed to get changed files: %w", err) 35 } 36 changedFiles := strings.Split(strings.TrimSpace(string(output)), "\n") 37 return changedFiles, nil 38} 39 40/** 41 * Git helper functions 42 */ 43 44func gitCommand(repoPath string, args ...string) ([]byte, error) { 45 cmd := exec.Command("git", args...) 46 cmd.Dir = repoPath 47 return cmd.Output() 48} 49 50func detectCurrentBranch(repoPath string) string { 51 output, err := gitCommand(repoPath, "rev-parse", "--abbrev-ref", "HEAD") 52 if err != nil { 53 return "" 54 } 55 return strings.TrimSpace(string(output)) 56} 57 58func ensureGitRepository(repoPath string) error { 59 if _, err := os.Stat(repoPath); os.IsNotExist(err) { 60 return fmt.Errorf("repository path does not exist: %s", repoPath) 61 } 62 63 if _, err := os.Stat(fmt.Sprintf("%s/.git", repoPath)); os.IsNotExist(err) { 64 return fmt.Errorf("path is not a git repository: %s", repoPath) 65 } 66 return nil 67} 68 69func gitFetchPull(repoPath, branch string) error { 70 if _, err := gitCommand(repoPath, "fetch", "origin", branch); err != nil { 71 return fmt.Errorf("git fetch failed: %w", err) 72 } 73 74 currentBranch, _ := gitCommand(repoPath, "rev-parse", "--abbrev-ref", "HEAD") 75 if strings.TrimSpace(string(currentBranch)) != branch { 76 if _, err := gitCommand(repoPath, "checkout", branch); err != nil { 77 return fmt.Errorf("git checkout failed: %w", err) 78 } 79 } 80 81 cmd := exec.Command("git", "pull", "origin", branch) 82 cmd.Dir = repoPath 83 output, err := cmd.CombinedOutput() 84 if err != nil { 85 return fmt.Errorf("git pull failed: %s, %w", string(output), err) 86 } 87 return nil 88} 89 90func getGitHead(repoPath string) (string, error) { 91 output, err := gitCommand(repoPath, "rev-parse", "HEAD") 92 if err != nil { 93 return "", fmt.Errorf("failed to get HEAD: %w", err) 94 } 95 return strings.TrimSpace(string(output)), nil 96}