Skip to content

Commit

Permalink
Merge pull request #5 from fumeapp/proper-error
Browse files Browse the repository at this point in the history
🥅 we are not properly rendering errors in CI mode
  • Loading branch information
acidjazz committed May 20, 2024
2 parents 76c8c2f + 10604d7 commit 1871e59
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 60 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ jobs:

- name: Run Simple
run: go run examples/simple/main.go
- name: Run Error
run: go run examples/error/main.go
continue-on-error: true
- name: Run Progress
run: go run examples/progress/main.go
- name: Run Multi
Expand Down
45 changes: 45 additions & 0 deletions examples/error/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package main

import (
"fmt"
"github.com/fumeapp/taskin"
"time"
)

func main() {

tasks := taskin.New(taskin.Tasks{
{
Title: "Task 1",
Task: func(t *taskin.Task) error {
for i := 0; i < 3; i++ {
t.Title = fmt.Sprintf("Task 1: [%d/3] seconds have passed", i+1)
time.Sleep(500 * time.Millisecond)
}
return nil
},
},
{
Title: "Task 2: Error",
Task: func(t *taskin.Task) error {
return fmt.Errorf("task 2 failed")
},
},
{
Title: "Task 3",
Task: func(t *taskin.Task) error {
for i := 0; i < 3; i++ {
t.Title = fmt.Sprintf("Task 3: [%d/3] seconds have passed", i+1)
time.Sleep(500 * time.Millisecond)
}
return nil
},
},
}, taskin.Defaults)
// }, taskin.Config{Options: taskin.ConfigOptions{ExitOnFailure: false}})
err := tasks.Run()

if err != nil {
panic(err)
}
}
2 changes: 1 addition & 1 deletion examples/simple/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func main() {
t.Title = fmt.Sprintf("Task 2: [%d/3] seconds have passed", i+1)
time.Sleep(500 * time.Millisecond)
}
return fmt.Errorf("task 2 failed")
return nil
},
},
}, taskin.Defaults)
Expand Down
10 changes: 10 additions & 0 deletions models.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import (
"github.com/charmbracelet/bubbles/spinner"
)

type TerminateWithError struct {
Error error
}

type TaskState int

const (
Expand Down Expand Up @@ -39,3 +43,9 @@ type Runner struct {
}

type Runners []Runner

type Model struct {
Runners Runners
Shutdown bool
ShutdownError error
}
91 changes: 55 additions & 36 deletions mvc.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,70 +6,84 @@ import (
"github.com/charmbracelet/lipgloss"
)

func (r *Runners) Init() tea.Cmd {
func (m *Model) Init() tea.Cmd {

var cmds []tea.Cmd
for i := range *r {
if (*r)[i].Spinner != nil {
cmds = append(cmds, (*r)[i].Spinner.Tick)
for i := range m.Runners {
if (m.Runners)[i].Spinner != nil {
cmds = append(cmds, (m.Runners)[i].Spinner.Tick)
}
for j := range (*r)[i].Children {
if (*r)[i].Children[j].Spinner != nil {
cmds = append(cmds, (*r)[i].Children[j].Spinner.Tick)
for j := range (m.Runners)[i].Children {
if (m.Runners)[i].Children[j].Spinner != nil {
cmds = append(cmds, (m.Runners)[i].Children[j].Spinner.Tick)
}
}
}
return tea.Batch(cmds...)
}

func (r *Runners) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd

if m.Shutdown && m.ShutdownError != nil {
return m, tea.Quit
}

switch msg := msg.(type) {

case TerminateWithError:
m.SetShutdown(msg.Error)
return m, tea.Quit

case spinner.TickMsg:
allDone := true
for i := range *r {
for i := range m.Runners {

if (*r)[i].State == Running || (*r)[i].State == NotStarted {
if (m.Runners)[i].State == Running || (m.Runners)[i].State == NotStarted {
if !IsCI() {
newSpinner, cmd := (*r)[i].Spinner.Update(msg)
(*r)[i].Spinner = &newSpinner
newSpinner, cmd := (m.Runners)[i].Spinner.Update(msg)
(m.Runners)[i].Spinner = &newSpinner
cmds = append(cmds, cmd)
}
}

for j := range (*r)[i].Children {
if (*r)[i].Children[j].State == Running || (*r)[i].Children[j].State == NotStarted {
for j := range (m.Runners)[i].Children {
if (m.Runners)[i].Children[j].State == Running || (m.Runners)[i].Children[j].State == NotStarted {
if !IsCI() {
newSpinner, cmd := (*r)[i].Children[j].Spinner.Update(msg)
(*r)[i].Children[j].Spinner = &newSpinner
newSpinner, cmd := (m.Runners)[i].Children[j].Spinner.Update(msg)
(m.Runners)[i].Children[j].Spinner = &newSpinner
cmds = append(cmds, cmd)
}
}
}

if (*r)[i].State == Failed {
return r, tea.Quit
if (m.Runners)[i].State == Failed {
return m, tea.Quit
}

if (*r)[i].State != Completed && (*r)[i].State != Failed {
if (m.Runners)[i].State != Completed && (m.Runners)[i].State != Failed {
allDone = false
}
}

if allDone {
return r, tea.Quit
return m, tea.Quit
}

return r, tea.Batch(cmds...)
return m, tea.Batch(cmds...)
}

return r, nil
return m, nil
}

func (r *Runners) checkTasksState() (allDone, anyFailed bool) {
func (m *Model) SetShutdown(err error) {
m.Shutdown = true
m.ShutdownError = err
}

func (m *Model) checkTasksState() (allDone, anyFailed bool) {
allDone = true
for _, runner := range *r {
for _, runner := range m.Runners {
if runner.State != Completed && runner.State != Failed {
allDone = false
}
Expand All @@ -80,24 +94,25 @@ func (r *Runners) checkTasksState() (allDone, anyFailed bool) {
return
}

func (r *Runners) View() string {
func (m *Model) View() string {
var view string

// check if CI is set, if it is then don't return the view until all tasks are completed or one has failed
if IsCI() {
allDone, _ := r.checkTasksState()
if !allDone {
allDone, anyFailed := m.checkTasksState()
if !allDone && !anyFailed {
return ""
}
}
for _, runner := range *r {

for _, runner := range m.Runners {
status := ""
switch runner.State {
case NotStarted:
status = lipgloss.NewStyle().Foreground(runner.Config.Colors.Pending).Render(runner.Config.Chars.NotStarted) + " " + runner.Task.Title // Gray bullet
status = Color(runner.Config.Colors.Pending, runner.Config.Chars.NotStarted) + " " + runner.Task.Title // Gray bullet
case Running:
if len(runner.Children) > 0 {
status = lipgloss.NewStyle().Foreground(runner.Config.Colors.ParentStarted).Render(runner.Config.Chars.ParentStarted) + " " + runner.Task.Title
status = Color(runner.Config.Colors.ParentStarted, runner.Config.Chars.ParentStarted) + " " + runner.Task.Title
} else {
if runner.Task.ShowProgress.Total != 0 {
percent := float64(runner.Task.ShowProgress.Current) / float64(runner.Task.ShowProgress.Total)
Expand All @@ -115,17 +130,17 @@ func (r *Runners) View() string {
}
}
case Completed:
status = lipgloss.NewStyle().Foreground(runner.Config.Colors.Success).Render(runner.Config.Chars.Success) + " " + runner.Task.Title // Green checkmark
status = Color(runner.Config.Colors.Success, runner.Config.Chars.Success) + " " + runner.Task.Title // Green checkmark
case Failed:
status = lipgloss.NewStyle().Foreground(runner.Config.Colors.Failure).Render(runner.Config.Chars.Failure) + " " + runner.Task.Title // Red 'x'
status = Color(runner.Config.Colors.Failure, runner.Config.Chars.Failure) + " " + runner.Task.Title // Red 'x'
}
view += lipgloss.NewStyle().Render(status) + "\n"

for _, child := range runner.Children {
status = ""
switch child.State {
case NotStarted:
status = lipgloss.NewStyle().Foreground(child.Config.Colors.Pending).Render(runner.Config.Chars.NotStarted) + " " + child.Task.Title // Gray bullet
status = Color(child.Config.Colors.Pending, runner.Config.Chars.NotStarted) + " " + child.Task.Title // Gray bullet
case Running:
if child.Task.ShowProgress.Total != 0 {
percent := float64(child.Task.ShowProgress.Current) / float64(child.Task.ShowProgress.Total)
Expand All @@ -142,11 +157,15 @@ func (r *Runners) View() string {
}
}
case Completed:
status = lipgloss.NewStyle().Foreground(child.Config.Colors.Success).Render("✔") + " " + child.Task.Title // Green checkmark
status = Color(child.Config.Colors.Success, runner.Config.Chars.Success) + " " + child.Task.Title // Green checkmark
case Failed:
status = lipgloss.NewStyle().Foreground(child.Config.Colors.Failure).Render("✘") + " " + child.Task.Title // Red 'x'
status = Color(child.Config.Colors.Failure, runner.Config.Chars.Failure) + " " + child.Task.Title // Red 'x'
}
if IsCI() {
view += " " + status + "\n"
} else {
view += " " + lipgloss.NewStyle().Render(status) + "\n"
}
view += " " + lipgloss.NewStyle().Render(status) + "\n"
}
}
return view
Expand Down
39 changes: 21 additions & 18 deletions mvc_test.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
package taskin

import (
"os"
"testing"
)

func TestRunners_Init(t *testing.T) {
r := &Runners{
// Initialize your Runners struct here
}
func TestModelInit(t *testing.T) {
// Initialize a new Model
m := &Model{}

cmd := r.Init()
// Call the Init method
cmd := m.Init()

// If Init is not implemented, it should return nil
// Check if the returned command is not nil
if cmd != nil {
t.Errorf("Expected Init to return nil")
t.Errorf("Expected command to be not nil, got not nil")
}
}

func TestRunners_View(t *testing.T) {
r := &Runners{
// Initialize your Runners struct here
}
func TestModelUpdate(t *testing.T) {
// Initialize a new Model
m := &Model{}

// Set the "CI" environment variable
os.Setenv("CI", "true")
// Call the Update method with a dummy message
newModel, cmd := m.Update("dummy message")

view := r.View()
// Check if the returned model is not nil
if newModel == nil {
t.Errorf("Expected model to be not nil, got nil")
}

// If "CI" is set and not all tasks are completed, View should return an empty string
if view != "" {
t.Errorf("Expected View to return an empty string")
// Check if the returned command is nil
if cmd != nil {
t.Errorf("Expected command to be nil, got non-nil")
}
}

// Add more tests for other methods in the Model struct
Loading

0 comments on commit 1871e59

Please sign in to comment.