From 9aa59353edaed414fbdea6b7e46f57360217e2e9 Mon Sep 17 00:00:00 2001 From: Adrian Gierakowski Date: Thu, 5 Oct 2023 22:36:56 +0100 Subject: [PATCH 1/6] implement process entrypoint and run cmd ### process entrypoint `entrypoint` is an optional array of strings which can be set on the process config to override default shell (and shellArg) used to run the `command` When `entrypoint` is set and is non-empty, `command` can also be omitted. This is useful if one want's to: 1. use a different shell per process 2. run any other executable directly, without a shell wrapper ### `run` cmd Equivalent of [docker-compose run](https://docs.docker.com/engine/reference/commandline/compose_run/) which can be used to run a chosen process in the foreground (with std(in|out|err) attached), while it's dependencies are ran in the background (no logs printed to stdio). Deps can be disabled via --no-deps flag. Additional arguments can be passed to the process after `--` separator, for example: ```sh process-compose run main -- arg1 arg2 ``` The separator is necessary in order to distinguish process-compose flags from flags meant to be passed to the process. While the process is running, the logs of the deps can be inspected using the usual command: ``` process-compose process logs dep ``` The process being ran will have `availability.exit_on_end` set to `true` and manually setting it to `false` will have no effect. --- src/app/commander_interface.go | 1 + src/app/process.go | 51 +++++++++++++++++++++++--------- src/app/project_runner.go | 32 ++++++++++++++++++-- src/app/system_test.go | 10 +++---- src/cmd/project_runner.go | 4 +-- src/cmd/root.go | 2 +- src/cmd/run.go | 54 ++++++++++++++++++++++++++++++++++ src/cmd/up.go | 2 +- src/command/command.go | 5 ++-- src/command/command_wrapper.go | 7 +++++ src/tui/proc-info-form.go | 6 ++-- src/types/process.go | 5 +++- src/types/validators.go | 37 +++++++++++++++++++++++ 13 files changed, 186 insertions(+), 30 deletions(-) create mode 100644 src/cmd/run.go diff --git a/src/app/commander_interface.go b/src/app/commander_interface.go index c6cfb33..41251f2 100644 --- a/src/app/commander_interface.go +++ b/src/app/commander_interface.go @@ -15,4 +15,5 @@ type Commander interface { StderrPipe() (io.ReadCloser, error) Stop(int, bool) error SetCmdArgs() + AttachIo() } diff --git a/src/app/process.go b/src/app/process.go index 167c0e9..b4b99ec 100644 --- a/src/app/process.go +++ b/src/app/process.go @@ -53,6 +53,9 @@ type Process struct { liveProber *health.Prober readyProber *health.Prober shellConfig command.ShellConfig + printLogs bool + isMain bool + extraArgs []string } func NewProcess( @@ -62,6 +65,9 @@ func NewProcess( processState *types.ProcessState, procLog *pclog.ProcessLogBuffer, shellConfig command.ShellConfig, + printLogs bool, + isMain bool, + extraArgs []string, ) *Process { colNumeric := rand.Intn(int(color.FgHiWhite)-int(color.FgHiBlack)) + int(color.FgHiBlack) @@ -78,6 +84,9 @@ func NewProcess( shellConfig: shellConfig, procStateChan: make(chan string, 1), procReadyChan: make(chan string, 1), + printLogs: printLogs, + isMain: isMain, + extraArgs: extraArgs, } proc.procReadyCtx, proc.readyCancelFn = context.WithCancel(context.Background()) @@ -92,7 +101,7 @@ func (p *Process) run() int { } if err := p.validateProcess(); err != nil { - log.Error().Err(err).Msgf("Failed to run command %s for process %s", p.getCommand(), p.getName()) + log.Error().Err(err).Msgf(`Failed to run command ["%v"] for process %s`, strings.Join(p.getCommand(), `" "`), p.getName()) p.onProcessEnd(types.ProcessStateError) return 1 } @@ -101,7 +110,7 @@ func (p *Process) run() int { for { err := p.setStateAndRun(p.getStartingStateName(), p.getProcessStarter()) if err != nil { - log.Error().Err(err).Msgf("Failed to run command %s for process %s", p.getCommand(), p.getName()) + log.Error().Err(err).Msgf(`Failed to run command ["%v"] for process %s`, strings.Join(p.getCommand(), `" "`), p.getName()) p.logBuffer.Write(err.Error()) p.onProcessEnd(types.ProcessStateError) return 1 @@ -151,16 +160,23 @@ func (p *Process) run() int { func (p *Process) getProcessStarter() func() error { return func() error { - p.command = command.BuildCommandShellArg(p.shellConfig, p.getCommand()) + p.command = command.BuildCommand( + p.procConf.Executable, + append(p.procConf.Args, p.extraArgs...), + ) p.command.SetEnv(p.getProcessEnvironment()) p.command.SetDir(p.procConf.WorkingDir) - p.command.SetCmdArgs() - stdout, _ := p.command.StdoutPipe() - stderr, _ := p.command.StderrPipe() - go p.handleOutput(stdout, p.handleInfo) - go p.handleOutput(stderr, p.handleError) - //stdin, _ := p.command.StdinPipe() - //go p.handleInput(stdin) + + if p.isMain { + p.command.AttachIo() + } else { + p.command.SetCmdArgs() + stdout, _ := p.command.StdoutPipe() + stderr, _ := p.command.StderrPipe() + go p.handleOutput(stdout, p.handleInfo) + go p.handleOutput(stderr, p.handleError) + } + return p.command.Start() } } @@ -346,8 +362,11 @@ func (p *Process) getNameWithSmartReplica() string { return p.procConf.Name } -func (p *Process) getCommand() string { - return p.procConf.Command +func (p *Process) getCommand() []string { + return append( + []string{(*p.procConf).Executable}, + append(p.procConf.Args, p.extraArgs...)..., + ) } func (p *Process) updateProcState() { @@ -401,13 +420,17 @@ func (p *Process) handleOutput(pipe io.ReadCloser, handler func(message string)) func (p *Process) handleInfo(message string) { p.logger.Info(message, p.getName(), p.procConf.ReplicaNum) - fmt.Printf("[%s\t] %s\n", p.procColor(p.getName()), message) + if p.printLogs { + fmt.Printf("[%s\t] %s\n", p.procColor(p.getName()), message) + } p.logBuffer.Write(message) } func (p *Process) handleError(message string) { p.logger.Error(message, p.getName(), p.procConf.ReplicaNum) - fmt.Printf("[%s\t] %s\n", p.procColor(p.getName()), p.redColor(message)) + if p.printLogs { + fmt.Printf("[%s\t] %s\n", p.procColor(p.getName()), p.redColor(message)) + } p.logBuffer.Write(message) } diff --git a/src/app/project_runner.go b/src/app/project_runner.go index 84afda4..a45589f 100644 --- a/src/app/project_runner.go +++ b/src/app/project_runner.go @@ -25,6 +25,8 @@ type ProjectRunner struct { logger pclog.PcLogger waitGroup sync.WaitGroup exitCode int + mainProcess string + mainProcessArgs []string } func (p *ProjectRunner) GetLexicographicProcessNames() ([]string, error) { @@ -83,7 +85,25 @@ func (p *ProjectRunner) runProcess(config *types.ProcessConfig) { procLog = pclog.NewLogBuffer(0) } procState, _ := p.GetProcessState(config.ReplicaName) - process := NewProcess(p.project.Environment, procLogger, config, procState, procLog, *p.project.ShellConfig) + isMain := config.Name == p.mainProcess + hasMain := p.mainProcess != "" + printLogs := !hasMain + extraArgs := []string{} + if isMain { + extraArgs = p.mainProcessArgs + config.RestartPolicy.ExitOnEnd = true + } + process := NewProcess( + p.project.Environment, + procLogger, + config, + procState, + procLog, + *p.project.ShellConfig, + printLogs, + isMain, + extraArgs, + ) p.addRunningProcess(process) p.waitGroup.Add(1) go func() { @@ -561,10 +581,18 @@ func (p *ProjectRunner) GetProject() *types.Project { return p.project } -func NewProjectRunner(project *types.Project, processesToRun []string, noDeps bool) (*ProjectRunner, error) { +func NewProjectRunner( + project *types.Project, + processesToRun []string, + noDeps bool, + mainProcess string, + mainProcessArgs []string, +) (*ProjectRunner, error) { runner := &ProjectRunner{ project: project, + mainProcess: mainProcess, + mainProcessArgs: mainProcessArgs, } var err error diff --git a/src/app/system_test.go b/src/app/system_test.go index a5682e0..4496f8d 100644 --- a/src/app/system_test.go +++ b/src/app/system_test.go @@ -28,7 +28,7 @@ func TestSystem_TestFixtures(t *testing.T) { t.Errorf(err.Error()) return } - runner, err := NewProjectRunner(project, []string{}, false) + runner, err := NewProjectRunner(project, []string{}, false, "", []string{}) if err != nil { t.Errorf(err.Error()) return @@ -48,7 +48,7 @@ func TestSystem_TestComposeWithLog(t *testing.T) { t.Errorf(err.Error()) return } - runner, err := NewProjectRunner(project, []string{}, false) + runner, err := NewProjectRunner(project, []string{}, false, "", []string{}) if err != nil { t.Errorf(err.Error()) return @@ -81,7 +81,7 @@ func TestSystem_TestComposeChain(t *testing.T) { t.Errorf(err.Error()) return } - runner, err := NewProjectRunner(project, []string{}, false) + runner, err := NewProjectRunner(project, []string{}, false, "", []string{}) if err != nil { t.Errorf(err.Error()) return @@ -117,7 +117,7 @@ func TestSystem_TestComposeChainExit(t *testing.T) { t.Errorf(err.Error()) return } - runner, err := NewProjectRunner(project, []string{}, false) + runner, err := NewProjectRunner(project, []string{}, false, "", []string{}) if err != nil { t.Errorf(err.Error()) return @@ -162,7 +162,7 @@ func TestSystem_TestComposeScale(t *testing.T) { t.Errorf(err.Error()) return } - runner, err := NewProjectRunner(project, []string{}, false) + runner, err := NewProjectRunner(project, []string{}, false, "", []string{}) if err != nil { t.Errorf(err.Error()) return diff --git a/src/cmd/project_runner.go b/src/cmd/project_runner.go index d3cfad2..5c53745 100644 --- a/src/cmd/project_runner.go +++ b/src/cmd/project_runner.go @@ -13,7 +13,7 @@ import ( "time" ) -func getProjectRunner(process []string, noDeps bool) *app.ProjectRunner { +func getProjectRunner(process []string, noDeps bool, mainProcess string, mainProcessArgs []string) *app.ProjectRunner { if *pcFlags.HideDisabled { opts.AddAdmitter(&admitter.DisabledProcAdmitter{}) } @@ -23,7 +23,7 @@ func getProjectRunner(process []string, noDeps bool) *app.ProjectRunner { log.Fatal().Msg(err.Error()) } - runner, err := app.NewProjectRunner(project, process, noDeps) + runner, err := app.NewProjectRunner(project, process, noDeps, mainProcess, mainProcessArgs) if err != nil { fmt.Println(err) log.Fatal().Msg(err.Error()) diff --git a/src/cmd/root.go b/src/cmd/root.go index a54e3b8..2e284c7 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -34,7 +34,7 @@ func run(cmd *cobra.Command, args []string) error { defer func() { _ = logFile.Close() }() - runner := getProjectRunner([]string{}, false) + runner := getProjectRunner([]string{}, false, "", []string{}) api.StartHttpServer(!*pcFlags.Headless, *pcFlags.PortNum, runner) runProject(runner) return nil diff --git a/src/cmd/run.go b/src/cmd/run.go new file mode 100644 index 0000000..816b8c5 --- /dev/null +++ b/src/cmd/run.go @@ -0,0 +1,54 @@ +package cmd + +import ( + "fmt" + "os" + "github.com/f1bonacc1/process-compose/src/api" + "github.com/spf13/cobra" +) + +// runCmd represents the up command +var runCmd = &cobra.Command{ + Use: "run PROCESS [flags] -- [process_args]", + Short: "Run PROCESS in the foreground, and it's dependencies in the background", + Long: `Run selected process with std(in|out|err) attached, while other processes run in the background. +Command line arguments, provided after --, are passed to the PROCESS.`, + Args: cobra.MinimumNArgs(1), + // Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + *pcFlags.Headless = false + + processName := args[0] + + if len(args) > 1 { + argsLenAtDash := cmd.ArgsLenAtDash() + if argsLenAtDash != 1 { + message := "Extra positional arguments provided! To pass args to PROCESS, separate them from process-compose arguments with: --" + fmt.Println(message) + os.Exit(1) + } + args = args[argsLenAtDash:] + } else { + // Clease args as they will contain the processName + args = []string{} + } + + runner := getProjectRunner( + []string{processName}, + *pcFlags.NoDependencies, + processName, + args, + ) + + api.StartHttpServer(false, *pcFlags.PortNum, runner) + runProject(runner) + }, +} + +func init() { + rootCmd.AddCommand(runCmd) + + runCmd.Flags().BoolVarP(pcFlags.NoDependencies, "no-deps", "", *pcFlags.NoDependencies, "don't start dependent processes") + runCmd.Flags().AddFlag(rootCmd.Flags().Lookup("config")) + +} diff --git a/src/cmd/up.go b/src/cmd/up.go index 9e51859..bc85947 100644 --- a/src/cmd/up.go +++ b/src/cmd/up.go @@ -14,7 +14,7 @@ var upCmd = &cobra.Command{ If one or more process names are passed as arguments, will start them and their dependencies only`, Run: func(cmd *cobra.Command, args []string) { - runner := getProjectRunner(args, *pcFlags.NoDependencies) + runner := getProjectRunner(args, *pcFlags.NoDependencies, "", []string{}) api.StartHttpServer(!*pcFlags.Headless, *pcFlags.PortNum, runner) runProject(runner) }, diff --git a/src/command/command.go b/src/command/command.go index 3798202..4355fa8 100644 --- a/src/command/command.go +++ b/src/command/command.go @@ -8,9 +8,10 @@ import ( "runtime" ) -func BuildCommandShellArg(shell ShellConfig, cmd string) *CmdWrapper { + +func BuildCommand(cmd string, args []string) *CmdWrapper { return &CmdWrapper{ - cmd: exec.Command(shell.ShellCommand, shell.ShellArgument, cmd), + cmd: exec.Command(cmd, args...), } //return NewMockCommand() } diff --git a/src/command/command_wrapper.go b/src/command/command_wrapper.go index 9b4ce5a..04f4e13 100644 --- a/src/command/command_wrapper.go +++ b/src/command/command_wrapper.go @@ -2,6 +2,7 @@ package command import ( "io" + "os" "os/exec" ) @@ -37,6 +38,12 @@ func (c *CmdWrapper) StderrPipe() (io.ReadCloser, error) { return c.cmd.StderrPipe() } +func (c *CmdWrapper) AttachIo() () { + c.cmd.Stdin = os.Stdin + c.cmd.Stdout = os.Stdout + c.cmd.Stderr = os.Stderr +} + func (c *CmdWrapper) SetEnv(env []string) { c.cmd.Env = env } diff --git a/src/tui/proc-info-form.go b/src/tui/proc-info-form.go index cbc379e..a04e94a 100644 --- a/src/tui/proc-info-form.go +++ b/src/tui/proc-info-form.go @@ -19,10 +19,12 @@ func (pv *pcView) createProcInfoForm(info *types.ProcessConfig, ports *types.Pro f.SetFieldTextColor(tcell.ColorBlack) f.SetButtonsAlign(tview.AlignCenter) f.SetTitle("Process " + info.Name + " Info") - addStringIfNotEmpty("Command:", info.Command, f) + addStringIfNotEmpty("Entrypoint:", strings.Join(*info.Entrypoint, " "), f) + if info.Command != nil { + addStringIfNotEmpty("Command:", *info.Command, f) + } addStringIfNotEmpty("Working Directory:", info.WorkingDir, f) addStringIfNotEmpty("Log Location:", info.LogLocation, f) - f.AddInputField("Replica:", fmt.Sprintf("%d/%d", info.ReplicaNum+1, info.Replicas), 0, nil, nil) addDropDownIfNotEmpty("Environment:", info.Environment, f) addCSVIfNotEmpty("Depends On:", mapKeysToSlice(info.DependsOn), f) if ports != nil { diff --git a/src/types/process.go b/src/types/process.go index 55fbb3c..f641c96 100644 --- a/src/types/process.go +++ b/src/types/process.go @@ -15,7 +15,8 @@ type ProcessConfig struct { Name string Disabled bool `yaml:"disabled,omitempty"` IsDaemon bool `yaml:"is_daemon,omitempty"` - Command string `yaml:"command"` + Command *string `yaml:"command,omitempty"` + Entrypoint *[]string `yaml:"entrypoint,omitempty"` LogLocation string `yaml:"log_location,omitempty"` LoggerConfig *LoggerConfig `yaml:"log_configuration,omitempty"` Environment Environment `yaml:"environment,omitempty"` @@ -31,6 +32,8 @@ type ProcessConfig struct { Extensions map[string]interface{} `yaml:",inline"` ReplicaNum int ReplicaName string + Executable string + Args []string } func (p *ProcessConfig) GetDependencies() []string { diff --git a/src/types/validators.go b/src/types/validators.go index 8454d3a..f555417 100644 --- a/src/types/validators.go +++ b/src/types/validators.go @@ -19,6 +19,7 @@ func (p *Project) ValidateAfterMerge() error { p.assignDefaultProcessValues() p.cloneReplicas() p.copyWorkingDirToProbes() + p.validateProcessCommand() return p.validateNoCircularDependencies() } @@ -74,6 +75,42 @@ func (p *Project) assignDefaultProcessValues() { } } +func (p *Project) validateProcessCommand() { + for name, proc := range p.Processes { + if proc.Entrypoint == nil { + if proc.Command == nil { + message := fmt.Sprintf("Error: Neither command nor entrypoint is set for process: %s", name) + fmt.Println(message) + log.Fatal().Msg(message) + } + proc.Entrypoint = &[]string{ + p.ShellConfig.ShellCommand, + p.ShellConfig.ShellArgument, + } + } else { + if len(*proc.Entrypoint) == 0 && (proc.Command == nil || *proc.Command == "") { + message := fmt.Sprintf("If entrypoint is empty, command needs to be non-empty (procces: %s)", name) + fmt.Println(message) + log.Fatal().Msg(message) + } + } + + if len(*proc.Entrypoint) == 0 { + proc.Executable = *proc.Command + proc.Args = []string{} + } else { + entrypoint := *proc.Entrypoint + proc.Executable = entrypoint[0] + proc.Args = entrypoint[1:] + if proc.Command != nil { + proc.Args = append(proc.Args, *proc.Command) + } + } + + p.Processes[name] = proc + } +} + func (p *Project) copyWorkingDirToProbes() { for name, proc := range p.Processes { if proc.LivenessProbe != nil && From 282f74297a7191a6bf24e0c680f48a172512b505 Mon Sep 17 00:00:00 2001 From: Adrian Gierakowski Date: Tue, 17 Oct 2023 21:11:54 +0100 Subject: [PATCH 2/6] add flake check --- flake.nix | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index 5d17b81..f226077 100644 --- a/flake.nix +++ b/flake.nix @@ -14,14 +14,19 @@ commit = self.shortRev or "dirty"; }; in - (flake-utils.lib.eachDefaultSystem (system: - { - packages.process-compose = mkPackage nixpkgs.legacyPackages.${system}; + (flake-utils.lib.eachDefaultSystem (system: let + pkgs = nixpkgs.legacyPackages.${system}; + in { + packages.process-compose = mkPackage pkgs; defaultPackage = self.packages."${system}".process-compose; apps.process-compose = flake-utils.lib.mkApp { drv = self.packages."${system}".process-compose; }; apps.default = self.apps."${system}".process-compose; + checks.default = self.packages."${system}".process-compose.overrideAttrs (prev: { + doCheck = true; + nativeBuildInputs = prev.nativeBuildInputs ++ (with pkgs; [python3]); + }); }) ) // { overlays.default = final: prev: { From b99cd40adbde9ff32c6aebae0f357671fd091ce8 Mon Sep 17 00:00:00 2001 From: Adrian Gierakowski Date: Tue, 17 Oct 2023 21:25:20 +0100 Subject: [PATCH 3/6] fix merger_test.go after turning Command into pointer --- src/loader/merger_test.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/loader/merger_test.go b/src/loader/merger_test.go index 60df0ff..bfee07d 100644 --- a/src/loader/merger_test.go +++ b/src/loader/merger_test.go @@ -8,11 +8,13 @@ import ( ) func getBaseProcess() *types.ProcessConfig { + command := "command" + return &types.ProcessConfig{ Name: "proc1", Disabled: false, IsDaemon: false, - Command: "command", + Command: &command, LogLocation: "", Environment: types.Environment{ "k1=v1", @@ -57,11 +59,13 @@ func getBaseProcess() *types.ProcessConfig { } func getOverrideProcess() *types.ProcessConfig { + command := "override command" + return &types.ProcessConfig{ Name: "proc1", Disabled: false, IsDaemon: false, - Command: "override command", + Command: &command, LogLocation: "", Environment: types.Environment{ "k0=v0", @@ -120,11 +124,13 @@ func getOverrideProcess() *types.ProcessConfig { } func getMergedProcess() *types.ProcessConfig { + command := "override command" + return &types.ProcessConfig{ Name: "proc1", Disabled: false, IsDaemon: false, - Command: "override command", + Command: &command, LogLocation: "", Environment: types.Environment{ "k0=v0", From b2b59c23137659009b4645f4ef91054897d69282 Mon Sep 17 00:00:00 2001 From: Adrian Gierakowski Date: Sat, 11 Nov 2023 22:54:10 +0000 Subject: [PATCH 4/6] fix typo: it's => its --- src/cmd/run.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmd/run.go b/src/cmd/run.go index 816b8c5..15dfdc4 100644 --- a/src/cmd/run.go +++ b/src/cmd/run.go @@ -10,7 +10,7 @@ import ( // runCmd represents the up command var runCmd = &cobra.Command{ Use: "run PROCESS [flags] -- [process_args]", - Short: "Run PROCESS in the foreground, and it's dependencies in the background", + Short: "Run PROCESS in the foreground, and its dependencies in the background", Long: `Run selected process with std(in|out|err) attached, while other processes run in the background. Command line arguments, provided after --, are passed to the PROCESS.`, Args: cobra.MinimumNArgs(1), From 33eef85466d4f588b948b594d4f22e0a25891c8b Mon Sep 17 00:00:00 2001 From: Adrian Gierakowski Date: Sat, 11 Nov 2023 23:47:26 +0000 Subject: [PATCH 5/6] use either Command (if not empty), or Entrypoint, not both --- src/loader/merger_test.go | 12 +++--------- src/tui/proc-info-form.go | 6 ++---- src/types/process.go | 4 ++-- src/types/validators.go | 38 ++++++++++++++++---------------------- 4 files changed, 23 insertions(+), 37 deletions(-) diff --git a/src/loader/merger_test.go b/src/loader/merger_test.go index bfee07d..60df0ff 100644 --- a/src/loader/merger_test.go +++ b/src/loader/merger_test.go @@ -8,13 +8,11 @@ import ( ) func getBaseProcess() *types.ProcessConfig { - command := "command" - return &types.ProcessConfig{ Name: "proc1", Disabled: false, IsDaemon: false, - Command: &command, + Command: "command", LogLocation: "", Environment: types.Environment{ "k1=v1", @@ -59,13 +57,11 @@ func getBaseProcess() *types.ProcessConfig { } func getOverrideProcess() *types.ProcessConfig { - command := "override command" - return &types.ProcessConfig{ Name: "proc1", Disabled: false, IsDaemon: false, - Command: &command, + Command: "override command", LogLocation: "", Environment: types.Environment{ "k0=v0", @@ -124,13 +120,11 @@ func getOverrideProcess() *types.ProcessConfig { } func getMergedProcess() *types.ProcessConfig { - command := "override command" - return &types.ProcessConfig{ Name: "proc1", Disabled: false, IsDaemon: false, - Command: &command, + Command: "override command", LogLocation: "", Environment: types.Environment{ "k0=v0", diff --git a/src/tui/proc-info-form.go b/src/tui/proc-info-form.go index a04e94a..868ee51 100644 --- a/src/tui/proc-info-form.go +++ b/src/tui/proc-info-form.go @@ -19,10 +19,8 @@ func (pv *pcView) createProcInfoForm(info *types.ProcessConfig, ports *types.Pro f.SetFieldTextColor(tcell.ColorBlack) f.SetButtonsAlign(tview.AlignCenter) f.SetTitle("Process " + info.Name + " Info") - addStringIfNotEmpty("Entrypoint:", strings.Join(*info.Entrypoint, " "), f) - if info.Command != nil { - addStringIfNotEmpty("Command:", *info.Command, f) - } + addStringIfNotEmpty("Entrypoint:", strings.Join(info.Entrypoint, " "), f) + addStringIfNotEmpty("Command:", info.Command, f) addStringIfNotEmpty("Working Directory:", info.WorkingDir, f) addStringIfNotEmpty("Log Location:", info.LogLocation, f) addDropDownIfNotEmpty("Environment:", info.Environment, f) diff --git a/src/types/process.go b/src/types/process.go index f641c96..fb0073c 100644 --- a/src/types/process.go +++ b/src/types/process.go @@ -15,8 +15,8 @@ type ProcessConfig struct { Name string Disabled bool `yaml:"disabled,omitempty"` IsDaemon bool `yaml:"is_daemon,omitempty"` - Command *string `yaml:"command,omitempty"` - Entrypoint *[]string `yaml:"entrypoint,omitempty"` + Command string `yaml:"command"` + Entrypoint []string `yaml:"entrypoint"` LogLocation string `yaml:"log_location,omitempty"` LoggerConfig *LoggerConfig `yaml:"log_configuration,omitempty"` Environment Environment `yaml:"environment,omitempty"` diff --git a/src/types/validators.go b/src/types/validators.go index f555417..bbc9333 100644 --- a/src/types/validators.go +++ b/src/types/validators.go @@ -77,34 +77,28 @@ func (p *Project) assignDefaultProcessValues() { func (p *Project) validateProcessCommand() { for name, proc := range p.Processes { - if proc.Entrypoint == nil { - if proc.Command == nil { - message := fmt.Sprintf("Error: Neither command nor entrypoint is set for process: %s", name) + command := proc.Command + entrypoint := proc.Entrypoint + + if command != "" { + if len(entrypoint) > 0 { + message := fmt.Sprintf("Both command and entrypoint are set! Using command and ignoring entrypoint (procces: %s)", name) fmt.Println(message) - log.Fatal().Msg(message) + log.Error().Msg(message) } - proc.Entrypoint = &[]string{ - p.ShellConfig.ShellCommand, + + proc.Executable = p.ShellConfig.ShellCommand; + proc.Args = []string{ p.ShellConfig.ShellArgument, + command, } - } else { - if len(*proc.Entrypoint) == 0 && (proc.Command == nil || *proc.Command == "") { - message := fmt.Sprintf("If entrypoint is empty, command needs to be non-empty (procces: %s)", name) - fmt.Println(message) - log.Fatal().Msg(message) - } - } - - if len(*proc.Entrypoint) == 0 { - proc.Executable = *proc.Command - proc.Args = []string{} - } else { - entrypoint := *proc.Entrypoint + } else if len(entrypoint) > 0 { proc.Executable = entrypoint[0] proc.Args = entrypoint[1:] - if proc.Command != nil { - proc.Args = append(proc.Args, *proc.Command) - } + } else { + message := fmt.Sprintf("Either command or entrypoint need to be non-empty (procces: %s)", name) + fmt.Println(message) + log.Fatal().Msg(message) } p.Processes[name] = proc From d787b8be2e7edccfe9b9e56bab700fc194a6f57f Mon Sep 17 00:00:00 2001 From: Adrian Gierakowski Date: Sat, 11 Nov 2023 23:53:41 +0000 Subject: [PATCH 6/6] don't fail if both Command and Entrypoint are empty this would be a breaking change as empty command was previously allowed also: log warning about both Command and Entrypoint being set to stderr to not interfere with downstream consumers of stdout --- src/types/validators.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/types/validators.go b/src/types/validators.go index bbc9333..dcbb21c 100644 --- a/src/types/validators.go +++ b/src/types/validators.go @@ -5,6 +5,7 @@ import ( "github.com/f1bonacc1/process-compose/src/command" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "os" "strings" ) @@ -80,10 +81,10 @@ func (p *Project) validateProcessCommand() { command := proc.Command entrypoint := proc.Entrypoint - if command != "" { + if command != "" || len(entrypoint) == 0 { if len(entrypoint) > 0 { message := fmt.Sprintf("Both command and entrypoint are set! Using command and ignoring entrypoint (procces: %s)", name) - fmt.Println(message) + fmt.Fprintln(os.Stderr, "process-compose:", message) log.Error().Msg(message) } @@ -92,13 +93,9 @@ func (p *Project) validateProcessCommand() { p.ShellConfig.ShellArgument, command, } - } else if len(entrypoint) > 0 { + } else { proc.Executable = entrypoint[0] proc.Args = entrypoint[1:] - } else { - message := fmt.Sprintf("Either command or entrypoint need to be non-empty (procces: %s)", name) - fmt.Println(message) - log.Fatal().Msg(message) } p.Processes[name] = proc