diff --git a/internal/updater/compose_executor.go b/internal/updater/compose_executor.go new file mode 100644 index 0000000..c5ed2f1 --- /dev/null +++ b/internal/updater/compose_executor.go @@ -0,0 +1,54 @@ +package updater + +import ( + "context" + "fmt" + "os/exec" + "strings" + + "github.com/shcizo/package-updater/internal/discovery" +) + +// ComposeExecutor invokes `docker compose` as a subprocess. +type ComposeExecutor struct{} + +// NewComposeExecutor returns an executor that shells out to docker compose. +func NewComposeExecutor() *ComposeExecutor { + return &ComposeExecutor{} +} + +// Execute runs `docker compose -f ... -p pull ` +// followed by `... up -d `. Working directory is set to +// job.WorkingDir so any relative paths in the compose file resolve correctly. +func (e *ComposeExecutor) Execute(ctx context.Context, job discovery.Job) error { + if job.Refused { + return fmt.Errorf("refused: %s", job.RefusedReason) + } + if err := e.run(ctx, job, "pull"); err != nil { + return fmt.Errorf("pull: %w", err) + } + if err := e.run(ctx, job, "up", "-d"); err != nil { + return fmt.Errorf("up: %w", err) + } + return nil +} + +func (e *ComposeExecutor) run(ctx context.Context, job discovery.Job, args ...string) error { + cliArgs := []string{"compose"} + for _, f := range job.ConfigFiles { + cliArgs = append(cliArgs, "-f", f) + } + cliArgs = append(cliArgs, "-p", job.Project) + cliArgs = append(cliArgs, args...) + cliArgs = append(cliArgs, job.Service) + + cmd := exec.CommandContext(ctx, "docker", cliArgs...) + cmd.Dir = job.WorkingDir + + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("docker %s: %w (output: %s)", + strings.Join(args, " "), err, strings.TrimSpace(string(out))) + } + return nil +}