diff --git a/commands/local_server_start.go b/commands/local_server_start.go index 7cfd022d..9fc86d1b 100644 --- a/commands/local_server_start.go +++ b/commands/local_server_start.go @@ -29,6 +29,7 @@ import ( "os/exec" "os/signal" "path/filepath" + "sync" "syscall" "github.com/pkg/errors" @@ -309,6 +310,13 @@ var localServerStartCmd = &console.Command{ if fileConfig != nil { reexec.NotifyForeground("workers") + + _, isDockerComposeWorkerConfigured := fileConfig.Workers[project.DockerComposeWorkerKey] + var dockerWg sync.WaitGroup + if isDockerComposeWorkerConfigured { + dockerWg.Add(1) + } + for name, worker := range fileConfig.Workers { pidFile := pid.New(projectDir, worker.Cmd) if pidFile.IsRunning() { @@ -335,10 +343,43 @@ var localServerStartCmd = &console.Command{ runner.BuildCmdHook = func(cmd *exec.Cmd) error { cmd.Env = append(cmd.Env, envs.AsSlice(env)...) - return nil } + if name == project.DockerComposeWorkerKey { + originalBuildCmdHook := runner.BuildCmdHook + + runner.BuildCmdHook = func(cmd *exec.Cmd) error { + cmd.Args = append(cmd.Args, "--detach") + + return originalBuildCmdHook(cmd) + } + + runner.SuccessHook = func(runner *local.Runner, cmd *exec.Cmd) { + terminal.Eprintln("INFO Docker Compose is now up, switching to non detached mode") + + // set up the worker for an immediate restart so + // that it starts monitoring the containers as soon + // as possible after the initial startup + runner.AlwaysRestartOnExit = true + // but next time this process is successful we don't + // have to do anything specific + runner.SuccessHook = nil + // and we move back AlwaysRestartOnExit to false + + runner.BuildCmdHook = func(cmd *exec.Cmd) error { + runner.AlwaysRestartOnExit = false + + return originalBuildCmdHook(cmd) + } + + dockerWg.Done() + } + } else if isDockerComposeWorkerConfigured { + terminal.Eprintfln("INFO Worker \"%s\" waiting for Docker Compose to be up", name) + dockerWg.Wait() + } + ui.Success(fmt.Sprintf("Started worker \"%s\"", name)) if err := runner.Run(); err != nil { terminal.Eprintfln("WARNING Worker \"%s\" exited with an error: %s", name, err) diff --git a/local/project/config.go b/local/project/config.go index c5d3767b..6a003e56 100644 --- a/local/project/config.go +++ b/local/project/config.go @@ -29,6 +29,8 @@ import ( "gopkg.in/yaml.v2" ) +const DockerComposeWorkerKey = "docker_compose" + // Config is the struct taken by New (should not be used for anything else) type Config struct { HomeDir string @@ -143,6 +145,17 @@ func (c *FileConfig) parseWorkers() error { return nil } + if v, ok := c.Workers[DockerComposeWorkerKey]; ok && v == nil { + c.Workers[DockerComposeWorkerKey] = &Worker{ + Cmd: []string{"docker", "compose", "up"}, + Watch: []string{ + "compose.yaml", "compose.override.yaml", + "compose.yml", "compose.override.yml", + "docker-compose.yml", "docker-compose.override.yml", + "docker-compose.yaml", "docker-compose.override.yaml", + }, + } + } if v, ok := c.Workers["yarn_encore_watch"]; ok && v == nil { c.Workers["yarn_encore_watch"] = &Worker{ Cmd: []string{"yarn", "encore", "dev", "--watch"}, diff --git a/local/runner.go b/local/runner.go index f829ccee..f895f450 100644 --- a/local/runner.go +++ b/local/runner.go @@ -60,6 +60,7 @@ type Runner struct { pidFile *pid.PidFile BuildCmdHook func(*exec.Cmd) error + SuccessHook func(*Runner, *exec.Cmd) AlwaysRestartOnExit bool } @@ -226,6 +227,10 @@ func (r *Runner) Run() error { case err := <-cmdExitChan: err = errors.Wrapf(err, `command "%s" failed`, r.pidFile) + if err == nil && r.SuccessHook != nil { + r.SuccessHook(r, cmd) + } + // Command is NOT set up to loop, stop here and remove the pidFile // if the command is successful if !looping {