From d5bb49b0ba51d04f786eb15bbe9217a75acfedc2 Mon Sep 17 00:00:00 2001 From: wagoodman Date: Sun, 18 Feb 2018 10:42:03 -0500 Subject: [PATCH] single line output --- Makefile | 2 +- config.go | 17 +++++++++++ example/13-single-line.yml | 61 ++++++++++++++++++++++++++++++++++++++ task.go | 55 ++++++++++++++++++++++++++++++++-- 4 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 example/13-single-line.yml diff --git a/Makefile b/Makefile index 5025461..fe890ce 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ $(TARGETS): run: go run main.go task.go config.go screen.go download.go log.go \ - run example/06-share-variables.yml + run example/13-single-line.yml examples: clean build ./dist/bashful run example/00-demo.yml diff --git a/config.go b/config.go index 737da0d..a4c7644 100644 --- a/config.go +++ b/config.go @@ -112,6 +112,9 @@ type OptionsConfig struct { // StopOnFailure indicates to halt further program execution if a task command has a non-zero return code StopOnFailure bool `yaml:"stop-on-failure"` + // SingleLineDisplay indicates to show all bashful output in a single line (instead of a line per task + a summary line) + SingleLineDisplay bool `yaml:"single-line"` + // UpdateInterval is the time in seconds that the screen should be refreshed (only if EventDriven=false) UpdateInterval float64 `yaml:"update-interval"` } @@ -119,6 +122,7 @@ type OptionsConfig struct { // NewOptionsConfig creates a new OptionsConfig populated with sane default values func NewOptionsConfig() (obj OptionsConfig) { obj.BulletChar = "•" + obj.CollapseOnCompletion = false obj.ColorError = 160 obj.ColorPending = 22 obj.ColorRunning = 22 @@ -136,6 +140,7 @@ func NewOptionsConfig() (obj OptionsConfig) { obj.ShowTaskEta = false obj.ShowTaskOutput = true obj.StopOnFailure = true + obj.SingleLineDisplay = false obj.UpdateInterval = -1 return obj } @@ -151,6 +156,11 @@ func (options *OptionsConfig) UnmarshalYAML(unmarshal func(interface{}) error) e *options = OptionsConfig(defaultValues) + if options.SingleLineDisplay { + options.ShowSummaryFooter = false + options.CollapseOnCompletion = false + } + // the global options must be available when parsing the task yaml (does order matter?) config.Options = *options return nil @@ -203,6 +213,7 @@ func NewTaskConfig() (obj TaskConfig) { obj.ShowTaskOutput = config.Options.ShowTaskOutput obj.EventDriven = config.Options.EventDriven obj.CollapseOnCompletion = config.Options.CollapseOnCompletion + return obj } @@ -216,6 +227,12 @@ func (taskConfig *TaskConfig) UnmarshalYAML(unmarshal func(interface{}) error) e } *taskConfig = TaskConfig(defaultValues) + + if config.Options.SingleLineDisplay { + taskConfig.ShowTaskOutput = false + taskConfig.CollapseOnCompletion = false + } + return nil } diff --git a/example/13-single-line.yml b/example/13-single-line.yml new file mode 100644 index 0000000..1176224 --- /dev/null +++ b/example/13-single-line.yml @@ -0,0 +1,61 @@ +config: + + # Show only one line of output to the screen (instead of a line per task + a summary line) + single-line: true + log-path: build.log + +x-reference-data: + all-apps: &app-names + - some-lib-4 + - utilities-lib + - important-lib + - some-app1 + - some-app3 + - some-awesome-app-5 + - watcher-app + - yup-another-app-7 + +tasks: + + - name: Cloning Repos + collapse-on-completion: true + parallel-tasks: + - name: "Cloning " + cmd: example/scripts/random-worker.sh 1 % + ignore-failure: true + for-each: *app-names + + - name: Compiling app (with trace) + cmd: example/scripts/random-error.sh 4 + + - name: Installing dependencies + parallel-tasks: + - name: Installing Oracle client + cmd: example/scripts/random-worker.sh 3 + - name: Installing Google chrome + cmd: example/scripts/random-worker.sh 4 + - name: Installing MD helper + cmd: example/scripts/random-worker.sh 5 + - name: Installing Bridgy + cmd: example/scripts/random-worker.sh 6 + + - name: Building Images + cmd: example/scripts/random-worker.sh 3 + + - name: Gathering Secrets + cmd: example/scripts/random-worker.sh 3 + + - name: Building and Migrating + parallel-tasks: + - name: Building app1 + cmd: example/scripts/random-worker.sh 5 + - name: Building some-app3 + cmd: example/scripts/random-worker.sh 5 + - name: Building some-lib-4 + cmd: example/scripts/random-worker.sh 6 + - name: Building some-awesome-app-5 + cmd: example/scripts/random-worker.sh 7 + - name: Building watcher-app + cmd: example/scripts/random-worker.sh 5 + - name: Building public-6 + cmd: example/scripts/random-worker.sh 5 \ No newline at end of file diff --git a/task.go b/task.go index 9323d21..76ce3f8 100644 --- a/task.go +++ b/task.go @@ -359,7 +359,54 @@ func (task *Task) String(terminalWidth int) string { // display prints the current task string status to the screen func (task *Task) display() { terminalWidth, _ := terminal.Width() - newScreen().Display(task.String(int(terminalWidth)), task.Display.Index) + theScreen := newScreen() + if config.Options.SingleLineDisplay { + + var durString, etaString, stepString, errorString string + displayString := "" + + effectiveWidth := int(terminalWidth) + + fillColor := color.ColorCode(strconv.Itoa(config.Options.ColorSuccess) + "+i") + emptyColor := color.ColorCode(strconv.Itoa(config.Options.ColorSuccess)) + if TaskStats.totalFailedTasks > 0 { + fillColor = color.ColorCode(strconv.Itoa(config.Options.ColorError) + "+i") + emptyColor = color.ColorCode(strconv.Itoa(config.Options.ColorError)) + } + + numFill := int(effectiveWidth) * TaskStats.completedTasks / TaskStats.totalTasks + + if config.Options.ShowSummaryTimes { + duration := time.Since(startTime) + durString = fmt.Sprintf(" Runtime[%s]", showDuration(duration)) + + totalEta := time.Duration(config.totalEtaSeconds) * time.Second + remainingEta := time.Duration(totalEta.Seconds()-duration.Seconds()) * time.Second + etaString = fmt.Sprintf(" ETA[%s]", showDuration(remainingEta)) + } + + if TaskStats.completedTasks == TaskStats.totalTasks { + etaString = "" + } + + if config.Options.ShowSummarySteps { + stepString = fmt.Sprintf(" Tasks[%d/%d]", TaskStats.completedTasks, TaskStats.totalTasks) + } + + if config.Options.ShowSummaryErrors { + errorString = fmt.Sprintf(" Errors[%d]", TaskStats.totalFailedTasks) + } + + valueStr := stepString + errorString + durString + etaString + + displayString = fmt.Sprintf("%[1]*s", -effectiveWidth, fmt.Sprintf("%[1]*s", (effectiveWidth+len(valueStr))/2, valueStr)) + displayString = fillColor + displayString[:numFill] + color.Reset + emptyColor + displayString[numFill:] + color.Reset + + theScreen.Display(displayString, 0) + } else { + theScreen.Display(task.String(int(terminalWidth)), task.Display.Index) + } + } // EstimateRuntime returns the ETA in seconds until command completion @@ -722,12 +769,14 @@ func (task *Task) Run(environment map[string]string) { var message bytes.Buffer - task.Pave() + if !config.Options.SingleLineDisplay { + task.Pave() + } task.StartAvailableTasks(environment) task.listenAndDisplay(environment) scr := newScreen() - hasHeader := len(task.Children) > 0 + hasHeader := len(task.Children) > 0 && !config.Options.SingleLineDisplay collapseSection := task.Config.CollapseOnCompletion && hasHeader && len(task.failedTasks) == 0 // complete the proc group status