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 }