Skip to content

Commit

Permalink
feat: don't redirect short tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
leg100 committed Dec 22, 2024
1 parent 953cbae commit 1925a2d
Show file tree
Hide file tree
Showing 12 changed files with 207 additions and 115 deletions.
36 changes: 36 additions & 0 deletions internal/integration/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,24 @@ func TestExplorer_SingleInitUpgrade(t *testing.T) {
})
}

func TestExplorer_SingleFormat(t *testing.T) {
t.Parallel()

tm := setup(t, "./testdata/single_module")

// Expect one module in tree
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "└ 󰠱 a")
})

// Format module
tm.Type("f")
// Expect short message in footer.
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "fmt: finished successfully…(Press 'o' for full output)")
})
}

func TestExplorer_MultipleFormat(t *testing.T) {
t.Parallel()

Expand All @@ -170,6 +188,24 @@ func TestExplorer_MultipleFormat(t *testing.T) {
})
}

func TestExplorer_SingleValidate(t *testing.T) {
t.Parallel()

tm := setupAndInitModule_Explorer(t)

// Expect one module in tree
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "└ 󰠱 a")
})

// Format module
tm.Type("v")
// Expect short message in footer.
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "validate: finished successfully…(Press 'o' for full output)")
})
}

func TestExplorer_MultipleValidate(t *testing.T) {
t.Parallel()

Expand Down
63 changes: 16 additions & 47 deletions internal/integration/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,19 @@ func TestState_SingleTaint_Untaint(t *testing.T) {
// Taint first resource, which should be random_pet.pet[0]
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlT})

// Expect to be taken to task page for taint
// Expect short message in footer.
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "Resource instance random_pet.pet[0] has been marked as tainted.")
})

// Go back to state page
tm.Type("s")

// Expect resource to be marked as tainted
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "random_pet.pet[0] (tainted)")
return strings.Contains(s, "taint: finished successfully…(Press 'o' for full output)") &&
strings.Contains(s, "random_pet.pet[0] (tainted)")
})

// Untaint resource
tm.Type("U")

// Expect to be taken to task page for untaint
// Expect short message in footer.
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "Resource instance random_pet.pet[0] has been successfully untainted.")
return strings.Contains(s, "untaint: finished successfully…(Press 'o' for full output)") &&
strings.Contains(s, "random_pet.pet[0]")
})
}

Expand Down Expand Up @@ -114,11 +108,10 @@ func TestState_Move(t *testing.T) {
tm.Type("giraffe[99]")
tm.Send(tea.KeyMsg{Type: tea.KeyEnter})

// Expect to be taken to task page for move
// Expect short message in footer.
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "state mv 󰠱 modules/a  default") &&
strings.Contains(s, `Move "random_pet.pet[0]" to "random_pet.giraffe[99]"`) &&
strings.Contains(s, `Successfully moved 1 object(s).`)
return strings.Contains(s, "random_pet.giraffe[99]") &&
strings.Contains(s, "state mv: finished successfully…(Press 'o' for full output)")
})
}

Expand All @@ -136,20 +129,11 @@ func TestState_SingleDelete(t *testing.T) {
})
tm.Type("y")

// User is taken to its task page, which should provide the output from the
// command.
// Expect short message in footer.
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "state rm 󰠱 modules/a  default") &&
strings.Contains(s, "Removed random_pet.pet[0]") &&
strings.Contains(s, "Successfully removed 1 resource instance(s).")
})

// Go back to state page
tm.Type("s")

// Expect only 9 resources.
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "1-9 of 9")
return strings.Contains(s, "state rm: finished successfully…(Press 'o' for full output)") &&
// Expect only 9 resources now.
strings.Contains(s, "1-9 of 9")
})
}

Expand All @@ -171,25 +155,10 @@ func TestState_MultipleDelete(t *testing.T) {
// User is taken to its task page, which should provide the output from the
// command.
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "state rm 󰠱 modules/a  default") &&
strings.Contains(s, "Removed random_pet.pet[0]") &&
strings.Contains(s, "Removed random_pet.pet[1]") &&
strings.Contains(s, "Removed random_pet.pet[2]") &&
strings.Contains(s, "Removed random_pet.pet[3]") &&
strings.Contains(s, "Removed random_pet.pet[4]") &&
strings.Contains(s, "Removed random_pet.pet[5]") &&
strings.Contains(s, "Removed random_pet.pet[6]") &&
strings.Contains(s, "Removed random_pet.pet[7]") &&
strings.Contains(s, "Removed random_pet.pet[8]") &&
strings.Contains(s, "Removed random_pet.pet[9]") &&
strings.Contains(s, "Successfully removed 10 resource instance(s).")
return strings.Contains(s, "state rm: finished successfully…(Press 'o' for full output)") &&
// Expect only 0 resources now.
strings.Contains(s, "1-0 of 0")
})

// Go back to state page
tm.Send(tea.KeyMsg{Type: tea.KeyEsc})

// TODO: test that there are zero resources in state. There is currently
// scant information to test for.
}

func TestState_TargetedPlan_SingleResource(t *testing.T) {
Expand Down
4 changes: 4 additions & 0 deletions internal/module/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ func (s *Service) Format(moduleID resource.ID) (task.Spec, error) {
Execution: task.Execution{
TerraformCommand: []string{"fmt"},
},
Immediate: true,
Short: true,
}
return spec, nil
}
Expand All @@ -256,6 +258,8 @@ func (s *Service) Validate(moduleID resource.ID) (task.Spec, error) {
Execution: task.Execution{
TerraformCommand: []string{"validate"},
},
Immediate: true,
Short: true,
}
return spec, nil
}
Expand Down
4 changes: 4 additions & 0 deletions internal/state/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func (s *Service) Delete(workspaceID resource.ID, addrs ...ResourceAddress) (tas
AfterExited: func(t *task.Task) {
s.CreateReloadTask(workspaceID)
},
Short: true,
})
}

Expand All @@ -95,6 +96,7 @@ func (s *Service) Taint(workspaceID resource.ID, addr ResourceAddress) (task.Spe
AfterExited: func(t *task.Task) {
s.CreateReloadTask(workspaceID)
},
Short: true,
})
}

Expand All @@ -111,6 +113,7 @@ func (s *Service) Untaint(workspaceID resource.ID, addr ResourceAddress) (task.S
AfterExited: func(t *task.Task) {
s.CreateReloadTask(workspaceID)
},
Short: true,
})
}

Expand All @@ -127,6 +130,7 @@ func (s *Service) Move(workspaceID resource.ID, src, dest ResourceAddress) (task
AfterExited: func(t *task.Task) {
s.CreateReloadTask(workspaceID)
},
Short: true,
})
}

Expand Down
6 changes: 6 additions & 0 deletions internal/task/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ type Spec struct {
// WorkspaceID is the ID of the workspace the task belongs to. If nil, the
// task does not belong to a workspace.
WorkspaceID *resource.ID
// TaskGroupID specifies the ID of the task group this task is to belong to.
// Nil means the task does not belong to a group.
TaskGroupID *resource.ID
// Execution specifies the execution of a program.
Execution Execution
// AdditionalExecution specifies the execution of another program. The
Expand All @@ -30,6 +33,9 @@ type Spec struct {
JSON bool
// Skip queue and immediately start task
Immediate bool
// Short if true indicates that the task runtime is short and the output is
// minimal.
Short bool
// Wait blocks until the task has finished
Wait bool
// Description assigns an optional description to the task to display to the
Expand Down
25 changes: 25 additions & 0 deletions internal/task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strings"
"sync"
"time"
"unicode"

"github.com/leg100/pug/internal"
"github.com/leg100/pug/internal/resource"
Expand All @@ -26,6 +27,7 @@ type Task struct {

ModuleID *resource.ID
WorkspaceID *resource.ID
TaskGroupID *resource.ID
Identifier Identifier
Program string
Args []string
Expand All @@ -35,6 +37,7 @@ type Task struct {
State Status
JSON bool
Immediate bool
Short bool
AdditionalEnv []string
DependsOn []resource.ID
// Summary summarises the outcome of a task to the end-user.
Expand Down Expand Up @@ -109,10 +112,14 @@ func (f *factory) newTask(spec Spec) (*Task, error) {
if spec.WorkspaceID != nil && spec.ModuleID == nil {
return nil, errors.New("workspace ID cannot be provided without module ID")
}
if spec.Blocking && spec.Immediate {
return nil, errors.New("a task cannot both be blocking and immediately")
}
task := &Task{
ID: resource.NewID(resource.Task),
ModuleID: spec.ModuleID,
WorkspaceID: spec.WorkspaceID,
TaskGroupID: spec.TaskGroupID,
Identifier: spec.Identifier,
State: Pending,
Created: time.Now(),
Expand All @@ -128,6 +135,7 @@ func (f *factory) newTask(spec Spec) (*Task, error) {
Blocking: spec.Blocking,
DependsOn: spec.dependsOn,
Immediate: spec.Immediate,
Short: spec.Short,
exclusive: spec.Exclusive,
Description: spec.Description,
Spec: spec,
Expand Down Expand Up @@ -414,3 +422,20 @@ func (t *Task) updateState(state Status) {
}
}
}

func StripError(s string) string {
// Strip ANSI escape codes
s = internal.StripAnsi(s)
// Strip non-ascii chars
s = strings.Map(func(r rune) rune {
if r > unicode.MaxASCII {
return -1
}
return r
}, s)
// Strip leading and trailing whitespace
s = strings.TrimSpace(s)
// Compact whitespace
s = strings.Join(strings.Fields(s), " ")
return s
}
42 changes: 9 additions & 33 deletions internal/task/task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package task
import (
"context"
"io"
"os"
"strings"
"testing"

"github.com/leg100/pug/internal"
Expand Down Expand Up @@ -73,36 +75,10 @@ func TestTask_cancel(t *testing.T) {
assert.Equal(t, Exited, task.State)
}

// func TestTask_WaitFor_immediateExit(t *testing.T) {
// f := factory{program: "../testdata/task"}
// task, err := f.newTask(".")
// require.NoError(t, err)
// task.run()()
//
// require.True(t, task.WaitFor(Exited))
// }
//
// func TestTask_WaitFor(t *testing.T) {
// f := factory{program: "../testdata/killme"}
// task, err := f.newTask(".")
// require.NoError(t, err)
//
// // wait for task to exit in background
// got := make(chan bool)
// go func() {
// got <- task.WaitFor(Exited)
// }()
//
// // start task in background
// go func() {
// task.run()()
// }()
//
// // wait for task to start
// assert.Equal(t, "ok, you can kill me now\n", <-iochan.DelimReader(task.NewReader(), '\n'))
// // then cancel
// task.cancel()
//
// // verify task exits
// require.True(t, <-got)
// }
func TestStripError(t *testing.T) {
b, err := os.ReadFile("./testdata/validate.out")
require.NoError(t, err)
got := StripError(string(b))
want := "Error: Could not load plugin Plugin reinitialization required. Please run \"terraform init\"."
assert.True(t, strings.HasPrefix(got, want), got)
}
6 changes: 4 additions & 2 deletions internal/tui/color.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ const (
BurntOrange = lipgloss.Color("214")
Yellow = lipgloss.Color("#DBBD70")
Green = lipgloss.Color("34")
LightGreen = lipgloss.Color("86")
Turquoise = lipgloss.Color("86")
DarkGreen = lipgloss.Color("#325451")
LightGreen = lipgloss.Color("47")
GreenBlue = lipgloss.Color("#00A095")
DeepBlue = lipgloss.Color("39")
LightBlue = lipgloss.Color("81")
Expand All @@ -26,11 +27,12 @@ const (
White = lipgloss.Color("#ffffff")
OffWhite = lipgloss.Color("#a8a7a5")
Pink = lipgloss.Color("30")
HotPink = lipgloss.Color("200")
)

var (
DebugLogLevel = Blue
InfoLogLevel = lipgloss.AdaptiveColor{Dark: string(LightGreen), Light: string(Green)}
InfoLogLevel = lipgloss.AdaptiveColor{Dark: string(Turquoise), Light: string(Green)}
ErrorLogLevel = Red
WarnLogLevel = Yellow

Expand Down
4 changes: 4 additions & 0 deletions internal/tui/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,10 @@ func (h *Helpers) CreateTasks(fn task.SpecFunc, ids ...resource.ID) tea.Cmd {
if err != nil {
return ErrorMsg(fmt.Errorf("creating task: %w", err))
}
if task.Short {
// Don't navigate the user to the task page for short tasks.
return nil
}
return NewNavigationMsg(TaskKind, WithParent(task.ID))
default:
specs := make([]task.Spec, 0, len(ids))
Expand Down
5 changes: 5 additions & 0 deletions internal/tui/keys/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type common struct {
Validate key.Binding
Format key.Binding
Cost key.Binding
LastTask key.Binding
}

// Keys shared by several models.
Expand Down Expand Up @@ -87,4 +88,8 @@ var Common = common{
key.WithKeys("$"),
key.WithHelp("$", "cost"),
),
LastTask: key.NewBinding(
key.WithKeys("o"),
key.WithHelp("o", "last task output"),
),
}
Loading

0 comments on commit 1925a2d

Please sign in to comment.