Skip to content

Commit

Permalink
feat: make common actions avail in all panes
Browse files Browse the repository at this point in the history
  • Loading branch information
leg100 committed Dec 21, 2024
1 parent 79a0ec4 commit 953cbae
Show file tree
Hide file tree
Showing 14 changed files with 361 additions and 284 deletions.
15 changes: 13 additions & 2 deletions internal/plan/service.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package plan

import (
"errors"
"fmt"

"github.com/leg100/pug/internal"
Expand Down Expand Up @@ -123,8 +124,8 @@ func (s *Service) ApplyPlan(taskID resource.ID) (task.Spec, error) {
if err != nil {
return task.Spec{}, err
}
if planTask.State != task.Exited {
return task.Spec{}, fmt.Errorf("plan task is not in the exited state: %s", planTask.State)
if err := IsApplyable(planTask); err != nil {
return task.Spec{}, err
}
plan, err := s.getByTaskID(taskID)
if err != nil {
Expand All @@ -133,6 +134,16 @@ func (s *Service) ApplyPlan(taskID resource.ID) (task.Spec, error) {
return plan.applyTaskSpec()
}

func IsApplyable(t *task.Task) error {
if t.Identifier != PlanTask {
return errors.New("task is not a plan")
}
if t.State != task.Exited {
return fmt.Errorf("plan task is in state other than exited: %s", t.State)
}
return nil
}

func (s *Service) Get(runID resource.ID) (*plan, error) {
return s.table.Get(runID)
}
Expand Down
153 changes: 153 additions & 0 deletions internal/tui/actions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package tui

import (
"fmt"
"strings"

"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea"
"github.com/leg100/pug/internal/plan"
"github.com/leg100/pug/internal/resource"
"github.com/leg100/pug/internal/task"
"github.com/leg100/pug/internal/tui/keys"
)

// ActionHandler handles actions common to more than one model.
type ActionHandler struct {
*Helpers
IDRetriever
}

type IDRetriever interface {
GetModuleIDs() ([]resource.ID, error)
GetWorkspaceIDs() ([]resource.ID, error)
}

func (m *ActionHandler) Update(msg tea.Msg) tea.Cmd {
var (
createPlanOptions plan.CreateOptions
// TODO: if only one worskpace is being applied, then customise message
// to mention name of workspace being applied.
applyPrompt = "Auto-apply %d workspaces?"
upgrade bool
)
switch msg := msg.(type) {
case tea.KeyMsg:
switch {
case key.Matches(msg, keys.Common.InitUpgrade):
upgrade = true
fallthrough
case key.Matches(msg, keys.Common.Init):
ids, err := m.GetModuleIDs()
if err != nil {
return ReportError(err)
}
fn := func(moduleID resource.ID) (task.Spec, error) {
return m.Modules.Init(moduleID, upgrade)
}
return m.CreateTasks(fn, ids...)
case key.Matches(msg, keys.Common.Execute):
ids, err := m.GetModuleIDs()
if err != nil {
return ReportError(err)
}
return CmdHandler(PromptMsg{
Prompt: fmt.Sprintf("Execute program in %d module directories: ", len(ids)),
Placeholder: "terraform version",
Action: func(v string) tea.Cmd {
if v == "" {
return nil
}
// split value into program and any args
parts := strings.Split(v, " ")
prog := parts[0]
args := parts[1:]
fn := func(moduleID resource.ID) (task.Spec, error) {
return m.Modules.Execute(moduleID, prog, args...)
}
return m.CreateTasks(fn, ids...)
},
Key: key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "confirm")),
Cancel: key.NewBinding(key.WithKeys("esc"), key.WithHelp("esc", "cancel")),
})
case key.Matches(msg, keys.Common.Validate):
ids, err := m.GetModuleIDs()
if err != nil {
return ReportError(err)
}
cmd := m.CreateTasks(m.Modules.Validate, ids...)
return cmd
case key.Matches(msg, keys.Common.Format):
ids, err := m.GetModuleIDs()
if err != nil {
return ReportError(err)
}
cmd := m.CreateTasks(m.Modules.Format, ids...)
return cmd
case key.Matches(msg, keys.Common.PlanDestroy):
createPlanOptions.Destroy = true
fallthrough
case key.Matches(msg, keys.Common.Plan):
ids, err := m.GetWorkspaceIDs()
if err != nil {
return ReportError(err)
}
fn := func(workspaceID resource.ID) (task.Spec, error) {
return m.Plans.Plan(workspaceID, createPlanOptions)
}
return m.CreateTasks(fn, ids...)
case key.Matches(msg, keys.Common.Destroy):
createPlanOptions.Destroy = true
applyPrompt = "Destroy resources of %d workspaces?"
fallthrough
case key.Matches(msg, keys.Common.AutoApply):
ids, err := m.GetWorkspaceIDs()
if err != nil {
return ReportError(err)
}
fn := func(workspaceID resource.ID) (task.Spec, error) {
return m.Plans.Apply(workspaceID, createPlanOptions)
}
return YesNoPrompt(
fmt.Sprintf(applyPrompt, len(ids)),
m.CreateTasks(fn, ids...),
)
case key.Matches(msg, keys.Common.Cost):
ids, err := m.GetWorkspaceIDs()
if err != nil {
return ReportError(err)
}
spec, err := m.Workspaces.Cost(ids...)
if err != nil {
return ReportError(fmt.Errorf("creating task: %w", err))
}
return m.CreateTasksWithSpecs(spec)
case key.Matches(msg, keys.Common.State):
ids, err := m.GetWorkspaceIDs()
if err != nil {
return ReportError(err)
}
if len(ids) == 0 {
return nil
}
return NavigateTo(ResourceListKind, WithParent(ids[0]))
}
}
return nil
}

func (m *ActionHandler) HelpBindings() []key.Binding {
return []key.Binding{
keys.Common.Init,
keys.Common.InitUpgrade,
keys.Common.Format,
keys.Common.Validate,
keys.Common.Plan,
keys.Common.PlanDestroy,
keys.Common.AutoApply,
keys.Common.Destroy,
keys.Common.Execute,
keys.Common.State,
keys.Common.Cost,
}
}
5 changes: 0 additions & 5 deletions internal/tui/explorer/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import "github.com/charmbracelet/bubbles/key"
type keyMap struct {
Enter key.Binding
SetCurrentWorkspace key.Binding
Execute key.Binding
ReloadModules key.Binding
ReloadWorkspaces key.Binding
}
Expand All @@ -19,10 +18,6 @@ var localKeys = keyMap{
key.WithKeys("C"),
key.WithHelp("C", "set current workspace"),
),
Execute: key.NewBinding(
key.WithKeys("x"),
key.WithHelp("x", "execute program"),
),
ReloadModules: key.NewBinding(
key.WithKeys("ctrl+r"),
key.WithHelp("ctrl+r", "reload modules"),
Expand Down
Loading

0 comments on commit 953cbae

Please sign in to comment.