Skip to content

Commit

Permalink
feat: don't auto-deselect and add select-range
Browse files Browse the repository at this point in the history
  • Loading branch information
leg100 committed May 3, 2024
1 parent fbed323 commit cde81d2
Show file tree
Hide file tree
Showing 18 changed files with 143 additions and 89 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,35 @@ A TUI application for terraform power users.

Invoke `init`, `validate`, and `fmt` across multiple modules.

![Modules demo](https://vhs.charm.sh/vhs-6Kaob034NVqUJ13CtRBB8X.gif)
![Modules demo](https://vhs.charm.sh/vhs-2yfWXEBhVZAP3CSXEPh8kz.gif)

### Workspaces

Pug supports workspaces. Invoke plan and apply on workspaces. Change the current workspace for a module.

![Workspaces demo](https://vhs.charm.sh/vhs-4dXFepkN3hgcsI1qbEdR7c.gif)
![Workspaces demo](https://vhs.charm.sh/vhs-67mSdPM47zcoEow3ZhCAMc.gif)

### Runs

Create multiple plans and apply them in parallel.

![Runs demo](https://vhs.charm.sh/vhs-6SajZfwq5PYgNEGwIeHu1l.gif)
![Runs demo](https://vhs.charm.sh/vhs-3egf56MXxRyu8C86whpFkB.gif)

View the output of plans and applies.

![Run demo](https://vhs.charm.sh/vhs-3sy6gywlgb7z8eX5ZU2srn.gif)
![Run demo](https://vhs.charm.sh/vhs-5t3KGPCCvMyNjQ5tF05kal.gif)

### State management

Manage state resources. Taint, untaint and delete multiple resources. Select resources for targeted plans.

![State demo](https://vhs.charm.sh/vhs-7zxWs2FStJq900PHWOcZkm.gif)
![State demo](https://vhs.charm.sh/vhs-37tJwravpWzihmJpPDkIm9.gif)

### Tasks

All invocations of terraform are represented as a task.

![Tasks demo](https://vhs.charm.sh/vhs-5221jZ5nkLSaVO3E95QpNV.gif)
![Tasks demo](https://vhs.charm.sh/vhs-4qwOEoZ436PgcNwrPdpoiM.gif)

## Install instructions

Expand Down
12 changes: 9 additions & 3 deletions demos/modules/modules.tape
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,17 @@ Show

Sleep 1s

Ctrl+a Sleep 0.5s Type "i" Sleep 2s
# init all modules
Ctrl+a Sleep 0.5s Type "i" Ctrl+a Sleep 2s
# init first module, again, so that we are taken to its task page
Type "i" Sleep 2s
Type "M"
Ctrl+a Sleep 0.5s Type "f" Sleep 2s
Ctrl+a Sleep 0.5s Type "v" Sleep 2s
# select all
Ctrl+a
# format all
Sleep 0.5s Type "f" Sleep 2s
# validate all
Sleep 0.5s Type "v" Sleep 2s
Type "T" Sleep 2s Enter Sleep 2s

Ctrl+C Sleep 0.5s Type "y"
Expand Down
Binary file modified demos/runs/applied_runs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions demos/runs/runs.tape
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ Type "demos/runs/reset.sh" Enter
Type `TF_CLI_CONFIG_FILE=$PWD/mirror/mirror.tfrc go run main.go -w demos/runs/configs` Enter
Sleep 1s
# run terraform init to initialize modules but don't show viewer this
Ctrl+a Type "i" Sleep 3s
Ctrl+a Type "i" Ctrl+a Sleep 3s
Show

Sleep 2s

# select all modules and create plan for each
Ctrl+a Sleep 0.5s Type "p" Sleep 4s

# select all modules and create an apply for each, and enter 'y' to confirm
Ctrl+a Sleep 0.5s Type "a" Sleep 1s
# create an apply for all, and enter 'y' to confirm
Sleep 0.5s Type "a" Sleep 1s
Screenshot demos/runs/apply_awaiting_confirmation.png
Type "y" Sleep 2s

Expand Down
21 changes: 13 additions & 8 deletions demos/state/state.tape
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,28 @@ Show

Sleep 1s Enter Sleep 0.5s Tab Sleep 0.5s Tab Sleep 1s

# select first three resources
Type "s" Sleep 0.5s
Down Type "s" Sleep 0.5s
Down Type "s" Sleep 0.5s

# taint selected resources
Type "t" Sleep 2s

Type "s" Sleep 0.5s
Up Type "s" Sleep 0.5s
Up Type "s" Sleep 0.5s
# untaint selected resources
Type "u" Sleep 2s

Type "s" Sleep 0.5s
Down Type "s" Sleep 0.5s
# delete selected resources
Type "d" Sleep 1s Type "y" Sleep 2s

Type "s" Sleep 0.5s
Down Type "s" Sleep 0.5s
Down Type "s" Sleep 0.5s
# select three more resources
Type "s"
Down
Type "s"
Down
Type "s"

# create plan targeting selected resources
Type "p" Sleep 3s

Ctrl+C Sleep 0.5s Type "y"
Expand Down
6 changes: 3 additions & 3 deletions demos/tasks/tasks.tape
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ Type "T" Sleep 0.5s
Show

# wait for apply to complete (it runs for at least 3 seconds) and for state to be reloaded
Sleep 5s
Sleep 4s
# show task page for latest task, which should be the reloading of state, and scroll up
Enter Sleep 1s PageUp Sleep 0.5s PageUp 2 Sleep 0.5s PageUp Sleep 0.5s PageUp 2 Sleep 1s
Enter Sleep 1s PageUp Sleep 0.5s PageUp 3 Sleep 0.5s PageUp Sleep 0.5s PageUp 2 Sleep 1s
# go back and show task page for penultimate task, which should be the apply
Type "T" Sleep 0.5s Down Sleep 0.5s Enter Sleep 1s
# scroll up apply output
Enter Sleep 1s PageUp Sleep 1s
Enter Sleep 1s PageUp Sleep 0.5s PageUp Sleep 0.5s PageUp Sleep 1s
Type "T" Sleep 1s

Ctrl+C Sleep 0.5s Type "y"
Expand Down
10 changes: 6 additions & 4 deletions demos/workspaces/workspaces.tape
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@ Type "demos/workspaces/reset.sh" Enter
Type `TF_CLI_CONFIG_FILE=$PWD/mirror/mirror.tfrc go run main.go -w demos/workspaces/configs` Enter
Sleep 1s
# run terraform init to initialize modules but don't show viewer this
Ctrl+a Type "i" Sleep 3s
Ctrl+a Type "i" Ctrl+a Sleep 3s
Show

Sleep 1s

# switch to global workspace listing
Type "W" Sleep 3s
# select all
Ctrl+a
# trigger a plan on all workspaces
Ctrl+a Sleep 0.5s Type "p" Sleep 2s
Sleep 0.5s Type "p" Sleep 2s
# trigger an an apply on each workspace, and enter 'y' to confirm
Ctrl+a Sleep 0.5s Type "a" Sleep 1s
Type "y" Sleep 4s
Sleep 0.5s Type "a" Sleep 1s
Type "y" Ctrl+a Sleep 4s
# switch current workspace on module a from default to dev
Down Sleep 1s Type "C" Sleep 2s
# switch to global module listing, showing the current workspace has changed for module a
Expand Down
37 changes: 13 additions & 24 deletions internal/app/module_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,19 @@ func TestModuleList(t *testing.T) {
return strings.Contains(s, "completed init tasks: (3 successful; 0 errored; 0 canceled; 0 uncreated)")
})

// Select all modules and format
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlA})
// Format all modules
tm.Type("f")
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "completed format tasks: (3 successful; 0 errored; 0 canceled; 0 uncreated)")
})

// Select all modules and validate
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlA})
// Validate all modules
tm.Type("v")
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "completed validate tasks: (3 successful; 0 errored; 0 canceled; 0 uncreated)")
})

// Select all modules and plan
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlA})
// Create plan on the current workspace of each module.
tm.Type("p")
// Expect all 3 modules to be in planned state
waitFor(t, tm, func(s string) bool {
Expand All @@ -56,8 +53,7 @@ func TestModuleList(t *testing.T) {
matchPattern(t, `modules/c.*default.*planned`, s)
})

// Select all modules and apply
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlA})
// Apply current run of each module.
tm.Type("a")

// Confirm apply
Expand Down Expand Up @@ -108,16 +104,7 @@ func TestModuleList_ReloadWorkspaces(t *testing.T) {
return strings.Count(s, "default") == 3
})

// Reload workspaces for current module
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlW})

// Expect message to inform user that reload has finished.
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "completed reload-workspace task successfully")
})

// Reload workspaces for each and every module
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlA})
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlW})

// Expect message to inform user that all three reloads have completed
Expand Down Expand Up @@ -147,9 +134,7 @@ func TestModuleList_CreateRun(t *testing.T) {
})

// Select all modules and init
tm.Send(tea.KeyMsg{
Type: tea.KeyCtrlA,
})
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlA})
tm.Type("i")

// Wait for each module to be initialized, and to have its current workspace
Expand All @@ -158,7 +143,10 @@ func TestModuleList_CreateRun(t *testing.T) {
return strings.Count(s, "default") == 3
})

// Create a run on two modules, but not on the last one ("c")
// Clear selection
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlBackslash})

// Create a run on two modules, but not on the last one ("/modules/c")
tm.Type("s")
tm.Send(tea.KeyMsg{Type: tea.KeyDown})
tm.Type("s")
Expand Down Expand Up @@ -190,9 +178,7 @@ func TestModuleList_ApplyCurrentRun(t *testing.T) {
})

// Select all modules and init
tm.Send(tea.KeyMsg{
Type: tea.KeyCtrlA,
})
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlA})
tm.Type("i")

// Wait for each module to be initialized, and to have its current workspace
Expand All @@ -201,6 +187,9 @@ func TestModuleList_ApplyCurrentRun(t *testing.T) {
return strings.Count(s, "default") == 3
})

// Clear selection
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlBackslash})

// Attempt to apply initialized module but has no plan
tm.Type("a")

Expand Down
9 changes: 9 additions & 0 deletions internal/app/run_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ func TestRunList_Multiple(t *testing.T) {
return strings.Count(s, "default") == 3
})

// Clear selection
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlBackslash})

// Go to global workspaces page
tm.Type("W")

Expand All @@ -103,6 +106,9 @@ func TestRunList_Multiple(t *testing.T) {
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlA})
tm.Type("p")

// Clear selection
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlBackslash})

// Go to global runs page
tm.Type("R")

Expand All @@ -122,6 +128,9 @@ func TestRunList_Multiple(t *testing.T) {
})
tm.Type("y")

// Clear selection
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlBackslash})

// Expect all four runs to enter the applied state.
waitFor(t, tm, func(s string) bool {
return matchPattern(t, `modules/a.*default.*applied`, s) &&
Expand Down
7 changes: 6 additions & 1 deletion internal/app/workspace_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ func TestWorkspaceList_CreateRun(t *testing.T) {
return strings.Count(s, "default") == 3
})

// Clear selection
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlBackslash})

// Go to global workspaces page
tm.Type("W")

Expand Down Expand Up @@ -115,6 +118,9 @@ func TestWorkspaceList_ApplyCurrentRun(t *testing.T) {
return strings.Count(s, "default") == 3
})

// Clear selection
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlBackslash})

// Go to global workspaces page
tm.Type("W")

Expand All @@ -141,7 +147,6 @@ func TestWorkspaceList_ApplyCurrentRun(t *testing.T) {
})

// Apply all four workspaces
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlA})
tm.Type("a")
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "Apply 4 runs (y/N)?")
Expand Down
32 changes: 21 additions & 11 deletions internal/tui/keys/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ import (
)

type global struct {
Modules key.Binding
Workspaces key.Binding
Runs key.Binding
Tasks key.Binding
Logs key.Binding
Escape key.Binding
Enter key.Binding
Select key.Binding
SelectAll key.Binding
Quit key.Binding
Help key.Binding
Modules key.Binding
Workspaces key.Binding
Runs key.Binding
Tasks key.Binding
Logs key.Binding
Escape key.Binding
Enter key.Binding
Select key.Binding
SelectAll key.Binding
SelectClear key.Binding
SelectRange key.Binding
Quit key.Binding
Help key.Binding
}

var Global = global{
Expand Down Expand Up @@ -55,6 +57,14 @@ var Global = global{
key.WithKeys("ctrl+a"),
key.WithHelp("ctrl+a", "select all"),
),
SelectClear: key.NewBinding(
key.WithKeys(`ctrl+\`),
key.WithHelp(`ctrl+\`, "clear selection"),
),
SelectRange: key.NewBinding(
key.WithKeys(`ctrl+@`),
key.WithHelp(`ctrl+<space>`, "select range"),
),
Quit: key.NewBinding(
key.WithKeys("ctrl+c"),
key.WithHelp("^c", "exit"),
Expand Down
8 changes: 1 addition & 7 deletions internal/tui/module/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,23 +160,18 @@ func (m list) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return tui.NewNavigationMsg(tui.TaskKind, tui.WithParent(task.Resource))
}
default:
// create init tasks, deselect whatever was selected, and keep
// user on current page.
// create init tasks, and keep user on current page.
cmd := tui.CreateTasks("init", m.ModuleService.Init, m.table.HighlightedOrSelectedKeys()...)
m.table.DeselectAll()
return m, cmd
}
case key.Matches(msg, keys.Common.Validate):
cmd := tui.CreateTasks("validate", m.ModuleService.Validate, m.table.HighlightedOrSelectedKeys()...)
m.table.DeselectAll()
return m, cmd
case key.Matches(msg, keys.Common.Format):
cmd := tui.CreateTasks("format", m.ModuleService.Format, m.table.HighlightedOrSelectedKeys()...)
m.table.DeselectAll()
return m, cmd
case key.Matches(msg, localKeys.ReloadWorkspaces):
cmd := tui.CreateTasks("reload-workspace", m.WorkspaceService.Reload, m.table.HighlightedOrSelectedKeys()...)
m.table.DeselectAll()
return m, cmd
case key.Matches(msg, keys.Common.Destroy):
createRunOptions.Destroy = true
Expand All @@ -191,7 +186,6 @@ func (m list) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if err != nil {
return m, tui.ReportError(err, "")
}
m.table.DeselectAll()
return m, tuirun.CreateRuns(m.RunService, createRunOptions, workspaceIDs...)
case key.Matches(msg, keys.Common.Apply):
runIDs, err := m.table.Prune(func(mod *module.Module) (resource.ID, error) {
Expand Down
Loading

0 comments on commit cde81d2

Please sign in to comment.