cli.go
func (cli *CLI) Run(args []string) int {
	// Parse the flags
	config, paths, once, dry, isVersion, err := cli.ParseFlags(args[1:])
	if err != nil {
		if err == flag.ErrHelp {
			fmt.Fprintf(cli.errStream, usage, version.Name)
			return 0
		}
		fmt.Fprintln(cli.errStream, err.Error())
		return ExitCodeParseFlagsError
	}

	// Save original config (defaults + parsed flags) for handling reloads
	cliConfig := config.Copy()

	// Load configuration paths, with CLI taking precedence
	config, err = loadConfigs(paths, cliConfig)
	if err != nil {
		return logError(err, ExitCodeConfigError)
	}

	config.Finalize()

	// Setup the config and logging
	config, err = cli.setup(config)
	if err != nil {
		return logError(err, ExitCodeConfigError)
	}

	// Print version information for debugging
	log.Printf("[INFO] %s", version.HumanVersion)

	// If the version was requested, return an "error" containing the version
	// information. This might sound weird, but most *nix applications actually
	// print their version on stderr anyway.
	if isVersion {
		log.Printf("[DEBUG] (cli) version flag was given, exiting now")
		fmt.Fprintf(cli.errStream, "%s\n", version.HumanVersion)
		return ExitCodeOK
	}

	// Initial runner
	runner, err := manager.NewRunner(config, dry, once)
	if err != nil {
		return logError(err, ExitCodeRunnerError)
	}
	go runner.Start()

	// Listen for signals
	signal.Notify(cli.signalCh)

	for {
		select {
		case err := <-runner.ErrCh:
			// Check if the runner's error returned a specific exit status, and return
			// that value. If no value was given, return a generic exit status.
			code := ExitCodeRunnerError
			if typed, ok := err.(manager.ErrExitable); ok {
				code = typed.ExitStatus()
			}
			return logError(err, code)
		case <-runner.DoneCh:
			return ExitCodeOK
		case s := <-cli.signalCh:
			log.Printf("[DEBUG] (cli) receiving signal %q", s)

			switch s {
			case *config.ReloadSignal:
				fmt.Fprintf(cli.errStream, "Reloading configuration...\n")
				runner.Stop()

				// Re-parse any configuration files or paths
				config, err = loadConfigs(paths, cliConfig)
				if err != nil {
					return logError(err, ExitCodeConfigError)
				}
				config.Finalize()

				// Load the new configuration from disk
				config, err = cli.setup(config)
				if err != nil {
					return logError(err, ExitCodeConfigError)
				}

				runner, err = manager.NewRunner(config, dry, once)
				if err != nil {
					return logError(err, ExitCodeRunnerError)
				}
				go runner.Start()
			case *config.KillSignal:
				fmt.Fprintf(cli.errStream, "Cleaning up...\n")
				runner.Stop()
				return ExitCodeInterrupt
			case signals.SignalLookup["SIGCHLD"]:
				// The SIGCHLD signal is sent to the parent of a child process when it
				// exits, is interrupted, or resumes after being interrupted. We ignore
				// this signal because the child process is monitored on its own.
				//
				// Also, the reason we do a lookup instead of a direct syscall.SIGCHLD
				// is because that isn't defined on Windows.
			default:
				// Propagate the signal to the child process
				runner.Signal(s)
			}
		case <-cli.stopCh:
			return ExitCodeOK
		}
	}
}
func (cli *CLI) Run(args []string) int {
	// Parse the flags
	config, paths, once, dry, isVersion, err := cli.ParseFlags(args[1:])
	if err != nil {
		if err == flag.ErrHelp {
			fmt.Fprintf(cli.errStream, usage, version.Name)
			return 0
		}
		fmt.Fprintln(cli.errStream, err.Error())
		return ExitCodeParseFlagsError
	}

	// Save original config (defaults + parsed flags) for handling reloads
	cliConfig := config.Copy()

	// Load configuration paths, with CLI taking precedence
	config, err = loadConfigs(paths, cliConfig)
	if err != nil {
		return logError(err, ExitCodeConfigError)
	}

	config.Finalize()

	// Setup the config and logging
	config, err = cli.setup(config)
	if err != nil {
		return logError(err, ExitCodeConfigError)
	}

	// Print version information for debugging
	log.Printf("[INFO] %s", version.HumanVersion)

	// If the version was requested, return an "error" containing the version
	// information. This might sound weird, but most *nix applications actually
	// print their version on stderr anyway.
	if isVersion {
		log.Printf("[DEBUG] (cli) version flag was given, exiting now")
		fmt.Fprintf(cli.errStream, "%s\n", version.HumanVersion)
		return ExitCodeOK
	}

	// Initial runner
	runner, err := manager.NewRunner(config, dry, once)
	if err != nil {
		return logError(err, ExitCodeRunnerError)
	}
	go runner.Start()

	// Listen for signals
	signal.Notify(cli.signalCh)

	for {
		select {
		case err := <-runner.ErrCh:
			// Check if the runner's error returned a specific exit status, and return
			// that value. If no value was given, return a generic exit status.
			code := ExitCodeRunnerError
			if typed, ok := err.(manager.ErrExitable); ok {
				code = typed.ExitStatus()
			}
			return logError(err, code)
		case <-runner.DoneCh:
			return ExitCodeOK
		case s := <-cli.signalCh:
			log.Printf("[DEBUG] (cli) receiving signal %q", s)

			switch s {
			case *config.ReloadSignal:
				fmt.Fprintf(cli.errStream, "Reloading configuration...\n")
				runner.Stop()

				// Re-parse any configuration files or paths
				config, err = loadConfigs(paths, cliConfig)
				if err != nil {
					return logError(err, ExitCodeConfigError)
				}
				config.Finalize()

				// Load the new configuration from disk
				config, err = cli.setup(config)
				if err != nil {
					return logError(err, ExitCodeConfigError)
				}

				runner, err = manager.NewRunner(config, dry, once)
				if err != nil {
					return logError(err, ExitCodeRunnerError)
				}
				go runner.Start()
			case *config.KillSignal:
				fmt.Fprintf(cli.errStream, "Cleaning up...\n")
				runner.Stop()
				return ExitCodeInterrupt
			case signals.SignalLookup["SIGCHLD"]:
				// The SIGCHLD signal is sent to the parent of a child process when it
				// exits, is interrupted, or resumes after being interrupted. We ignore
				// this signal because the child process is monitored on its own.
				//
				// Also, the reason we do a lookup instead of a direct syscall.SIGCHLD
				// is because that isn't defined on Windows.
			default:
				// Propagate the signal to the child process
				runner.Signal(s)
			}
		case <-cli.stopCh:
			return ExitCodeOK
		}
	}
}
func ( cli * CLI ) Run ( args [ ] string ) int { // Parse the flags config , paths , once , dry , isVersion , err := cli . ParseFlags ( args [ 1 : ] ) if err != nil { if err == flag . ErrHelp { fmt . Fprintf ( cli . errStream , usage , version . Name ) return 0 } fmt . Fprintln ( cli . errStream , err . Error ( ) ) return ExitCodeParseFlagsError } // Save original config (defaults + parsed flags) for handling reloads cliConfig := config . Copy ( ) // Load configuration paths, with CLI taking precedence config , err = loadConfigs ( paths , cliConfig ) if err != nil { return logError ( err , ExitCodeConfigError ) } config . Finalize ( ) // Setup the config and logging config , err = cli . setup ( config ) if err != nil { return logError ( err , ExitCodeConfigError ) } // Print version information for debugging log . Printf ( " " , version . HumanVersion ) // If the version was requested, return an "error" containing the version // information. This might sound weird, but most *nix applications actually // print their version on stderr anyway. if isVersion { log . Printf ( " " ) fmt . Fprintf ( cli . errStream , " \n " , version . HumanVersion ) return ExitCodeOK } // Initial runner runner , err := manager . NewRunner ( config , dry , once ) if err != nil { return logError ( err , ExitCodeRunnerError ) } go runner . Start ( ) // Listen for signals signal . Notify ( cli . signalCh ) for { select { case err := <- runner . ErrCh : // Check if the runner's error returned a specific exit status, and return // that value. If no value was given, return a generic exit status. code := ExitCodeRunnerError if typed , ok := err . ( manager . ErrExitable ) ; ok { code = typed . ExitStatus ( ) } return logError ( err , code ) case <- runner . DoneCh : return ExitCodeOK case s := <- cli . signalCh : log . Printf ( " " , s ) switch s { case * config . ReloadSignal : fmt . Fprintf ( cli . errStream , " \n " ) runner . Stop ( ) // Re-parse any configuration files or paths config , err = loadConfigs ( paths , cliConfig ) if err != nil { return logError ( err , ExitCodeConfigError ) } config . Finalize ( ) // Load the new configuration from disk config , err = cli . setup ( config ) if err != nil { return logError ( err , ExitCodeConfigError ) } runner , err = manager . NewRunner ( config , dry , once ) if err != nil { return logError ( err , ExitCodeRunnerError ) } go runner . Start ( ) case * config . KillSignal : fmt . Fprintf ( cli . errStream , " \n " ) runner . Stop ( ) return ExitCodeInterrupt case signals . SignalLookup [ " " ] : // The SIGCHLD signal is sent to the parent of a child process when it // exits, is interrupted, or resumes after being interrupted. We ignore // this signal because the child process is monitored on its own. // // Also, the reason we do a lookup instead of a direct syscall.SIGCHLD // is because that isn't defined on Windows. default : // Propagate the signal to the child process runner . Signal ( s ) } case <- cli . stopCh : return ExitCodeOK } } }
// Run accepts a slice of arguments and returns an int representing the exit
// status from the command.
Run accepts a slice of arguments and returns an int representing the exit status from the command .