···50505151## Installation
52525353+### Using Go Install
5454+5355```bash
5456go install github.com/aottr/compose-sync@latest
5557```
56585757-#### Alternatively build from source
5959+### Using Pre-built Binaries
6060+6161+Download the latest release from the [Releases page](https://github.com/aottr/compose-sync/releases).
6262+6363+### Build from Source
586459651. Clone this repository
60662. Build the application:
+89
cmd/compose-sync/main.go
···11+package main
22+33+import (
44+ "flag"
55+ "fmt"
66+ "log"
77+ "os"
88+ "path/filepath"
99+ "slices"
1010+1111+ "github.com/aottr/compose-sync/internal/core"
1212+ "github.com/aottr/compose-sync/internal/stacks"
1313+ "github.com/aottr/compose-sync/internal/version"
1414+)
1515+1616+func main() {
1717+ configPath := flag.String("config", "config.yml", "Path to configuration file")
1818+ showVersion := flag.Bool("version", false, "Show version information")
1919+ flag.Parse()
2020+2121+ if *showVersion {
2222+ fmt.Printf("compose-sync version %s\n", version.Version)
2323+ fmt.Printf("Commit: %s\n", version.Commit)
2424+ fmt.Printf("Build date: %s\n", version.BuildDate)
2525+ os.Exit(0)
2626+ }
2727+2828+ cfg, err := core.LoadConfig(*configPath)
2929+ if err != nil {
3030+ log.Fatalf("Failed to load config: %v", err)
3131+ }
3232+3333+ lockPath := filepath.Join(cfg.RepoPath, ".compose-sync.lock")
3434+ releaseLock, err := core.AcquireLock(lockPath)
3535+ if err != nil {
3636+ log.Fatalf("Failed to acquire lock: %v", err)
3737+ }
3838+ defer func() {
3939+ if err := releaseLock(); err != nil {
4040+ log.Printf("Warning: failed to release lock: %v", err)
4141+ }
4242+ }()
4343+4444+ currentHost, err := core.DetectHost()
4545+ if err != nil {
4646+ log.Fatalf("Failed to detect host: %v", err)
4747+ }
4848+ fmt.Printf("Detected host: %s\n", currentHost)
4949+5050+ fmt.Printf("Pulling git repository (branch: %s)...\n", cfg.Branch)
5151+ changedFiles, err := core.PullAndDetectChanges(cfg.RepoPath, cfg.Branch)
5252+ if err != nil {
5353+ log.Fatalf("Failed to pull or detect changes: %v", err)
5454+ }
5555+5656+ if len(changedFiles) == 0 {
5757+ fmt.Println("No changes detected.")
5858+ return
5959+ }
6060+6161+ changedStacks, err := stacks.DetectChangedStacks(changedFiles)
6262+ if err != nil {
6363+ log.Fatalf("Failed to detect changed stacks: %v", err)
6464+ }
6565+6666+ assignedStacks, err := core.GetAssignedStacks(cfg.RepoPath, currentHost)
6767+ if err != nil {
6868+ log.Fatalf("Failed to get assigned stacks: %v", err)
6969+ }
7070+ fmt.Printf("Stacks assigned to this host: %v\n", assignedStacks)
7171+7272+ fmt.Printf("Changed stacks: %v\n", changedStacks)
7373+7474+ stacksToDeploy := []string{}
7575+ for _, stack := range changedStacks {
7676+ if slices.Contains(assignedStacks, stack) {
7777+ stacksToDeploy = append(stacksToDeploy, stack)
7878+ }
7979+ }
8080+8181+ if len(stacksToDeploy) == 0 {
8282+ fmt.Println("No changed stacks are assigned to this host.")
8383+ return
8484+ }
8585+8686+ fmt.Printf("Stacks to deploy: %v\n", stacksToDeploy)
8787+8888+ stacks.DeployStacks(cfg.RepoPath, stacksToDeploy, cfg.Concurrency)
8989+}