Skip to content

Commit

Permalink
WIP: multiple progress bars
Browse files Browse the repository at this point in the history
  • Loading branch information
UncleGedd committed Mar 10, 2024
1 parent 950dddf commit b11a192
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 90 deletions.
6 changes: 3 additions & 3 deletions src/pkg/bundle/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func deployPackages(packages []types.Package, resume bool, b *Bundle, zarfPackag
}

// deploy each package
for _, pkg := range packagesToDeploy {
for i, pkg := range packagesToDeploy {
sha := strings.Split(pkg.Ref, "@sha256:")[1] // using appended SHA from create!
pkgTmp, err := utils.MakeTempDir(config.CommonOptions.TempDirectory)
if err != nil {
Expand Down Expand Up @@ -178,9 +178,9 @@ func deployPackages(packages []types.Package, resume bool, b *Bundle, zarfPackag
return err
}

// bubbletea recommends calling the Program directly; calling model.Update() doesn't work
// https://github.com/charmbracelet/bubbletea/discussions/374
// bubbletea recommends calling the Program directly: https://github.com/charmbracelet/bubbletea/discussions/374
tui.Program.Send(fmt.Sprintf("package:%s", pkg.Name))
tui.Program.Send(fmt.Sprintf("idx:%d", i))
if err := pkgClient.Deploy(); err != nil {
return err
}
Expand Down
185 changes: 98 additions & 87 deletions src/pkg/bundle/tui/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"github.com/defenseunicorns/zarf/src/types"
)

// todo: watch naming collisions, spinner also has a TickMsg
// todo: watch naming collisions, spinners also has a TickMsg
type tickMsg time.Time
type operation string

Expand All @@ -40,45 +40,36 @@ type bndlClientShim interface {
}

type model struct {
bndlClient bndlClientShim
completeChan chan int
fatalChan chan error
progress progress.Model
currentPkg string
totalComponents int
componentChan chan int
spinner spinner.Model
confirmed bool
complete bool
bndlClient bndlClientShim
completeChan chan int
fatalChan chan error
progressBars []progress.Model
pkgNames []string
pkgIdx int
totalComponentsPerPkg []int
spinners []spinner.Model
confirmed bool
complete bool
}

func InitModel(client bndlClientShim) model {

Check warning on line 55 in src/pkg/bundle/tui/tui.go

View workflow job for this annotation

GitHub Actions / validate

exported func InitModel returns unexported type tui.model, which can be annoying to use

Check warning on line 55 in src/pkg/bundle/tui/tui.go

View workflow job for this annotation

GitHub Actions / validate

exported function InitModel should have comment or be unexported
// configure spinner
s := spinner.New()
s.Spinner = spinner.Dot
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))

var confirmed bool
if config.CommonOptions.Confirm {
confirmed = true
}

return model{
bndlClient: client,
completeChan: make(chan int),
fatalChan: make(chan error),
componentChan: make(chan int),
progress: progress.New(progress.WithDefaultGradient()),
currentPkg: "",
spinner: s,
confirmed: confirmed,
bndlClient: client,
completeChan: make(chan int),
fatalChan: make(chan error),
confirmed: confirmed,
}
}

func (m model) Init() tea.Cmd {
return tea.Sequence(func() tea.Msg {
return DeployOp
}, m.spinner.Tick)
})
}

// allows us to get way more Zarf output
Expand Down Expand Up @@ -128,53 +119,59 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.complete = true
return m, tea.Sequence(tickCmd())
case <-m.fatalChan:
m.spinner.Spinner.Frames = []string{""}
m.spinner.Style = lipgloss.NewStyle().SetString("❌")
s, spinnerCmd := m.spinner.Update(spinner.TickMsg{})
m.spinner = s
m.spinners[m.pkgIdx].Spinner.Frames = []string{""}
m.spinners[m.pkgIdx].Style = lipgloss.NewStyle().SetString("❌")
s, spinnerCmd := m.spinners[m.pkgIdx].Update(spinner.TickMsg{})
m.spinners[m.pkgIdx] = s
return m, tea.Sequence(spinnerCmd, finalPause(), tea.Quit)
default:
switch msg := msg.(type) {
case progress.FrameMsg:
progressModel, cmd := m.progress.Update(msg)
m.progress = progressModel.(progress.Model)
progressModel, cmd := m.progressBars[m.pkgIdx].Update(msg)
m.progressBars[m.pkgIdx] = progressModel.(progress.Model)
return m, cmd
case tickMsg:
var progressCmd tea.Cmd

if m.complete {
progressCmd = m.progress.SetPercent(100)
m.spinner.Spinner.Frames = []string{""}
m.spinner.Style = lipgloss.NewStyle().SetString("✅")
s, spinnerCmd := m.spinner.Update(spinner.TickMsg{})
m.spinner = s
progressCmd = m.progressBars[m.pkgIdx].SetPercent(100)
m.spinners[m.pkgIdx].Spinner.Frames = []string{""}
m.spinners[m.pkgIdx].Style = lipgloss.NewStyle().SetString("✅")
s, spinnerCmd := m.spinners[m.pkgIdx].Update(spinner.TickMsg{})
m.spinners[m.pkgIdx] = s
return m, tea.Sequence(progressCmd, spinnerCmd, finalPause(), tea.Quit)
}

if m.totalComponents > 0 {
deployedPkg := GetDeployedPackage(m.currentPkg)
if len(m.totalComponentsPerPkg) > m.pkgIdx && m.totalComponentsPerPkg[m.pkgIdx] > 0 {
deployedPkg := GetDeployedPackage(m.pkgNames[m.pkgIdx])
if deployedPkg != nil && !resetProgress {
// todo: instead of going off of DeployedComponents, find a way to include deployedPkg.DeployedComponents[0].Status
progressCmd = m.progress.SetPercent(float64(len(deployedPkg.DeployedComponents)) / float64(m.totalComponents))
if m.progress.Percent() == 1 {
progressCmd = m.progressBars[m.pkgIdx].SetPercent(
float64(len(deployedPkg.DeployedComponents)) / float64(m.totalComponentsPerPkg[m.pkgIdx]),
)
if m.progressBars[m.pkgIdx].Percent() == 1 {
// todo: instead of going off percentage, go off successful deployment of the pkg
// stop the spinner and show success
m.spinner.Spinner.Frames = []string{""}
m.spinner.Style = lipgloss.NewStyle().SetString("✅")
// stop the spinners and show success
m.spinners[m.pkgIdx].Spinner.Frames = []string{""}
m.spinners[m.pkgIdx].Style = lipgloss.NewStyle().SetString("✅")
}
} else {
// handle upgrade scenario by resetting the progress bar until DeployedComponents is back to 1 (ie. the first component)
progressCmd = m.progress.SetPercent(0)
progressCmd = m.progressBars[m.pkgIdx].SetPercent(0)
if deployedPkg != nil && len(deployedPkg.DeployedComponents) >= 1 {
resetProgress = false
}
}
}
// must send a spinner.TickMsg to the spinner to keep it spinning
s, spinnerCmd := m.spinner.Update(spinner.TickMsg{})
m.spinner = s
// must send a spinners.TickMsg to the spinners to keep it spinning
if len(m.spinners) > 0 {
s, spinnerCmd := m.spinners[m.pkgIdx].Update(spinner.TickMsg{})
m.spinners[m.pkgIdx] = s

return m, tea.Sequence(progressCmd, spinnerCmd, tickCmd())
}

return m, tea.Sequence(progressCmd, spinnerCmd, tickCmd())
return m, tea.Sequence(progressCmd, tickCmd())

case tea.KeyMsg:
switch msg.String() {
Expand Down Expand Up @@ -209,14 +206,23 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case string:
if strings.Split(msg, ":")[0] == "package" {
pkgName := strings.Split(msg, ":")[1]
m.currentPkg = pkgName
m.pkgNames = append(m.pkgNames, pkgName)
// if pkg is already deployed, set resetProgress to true
if deployedPkg := GetDeployedPackage(pkgName); deployedPkg != nil && len(deployedPkg.DeployedComponents) != 0 {
resetProgress = true
}
} else if strings.Split(msg, ":")[0] == "totalComponents" {
if totalComponents, err := strconv.Atoi(strings.Split(msg, ":")[1]); err == nil {
m.totalComponents = totalComponents // else return err
m.totalComponentsPerPkg = append(m.totalComponentsPerPkg, totalComponents)
}
} else if strings.Split(msg, ":")[0] == "idx" {
if currentPkgIdx, err := strconv.Atoi(strings.Split(msg, ":")[1]); err == nil {
m.pkgIdx = currentPkgIdx
m.progressBars = append(m.progressBars, progress.New(progress.WithDefaultGradient()))
s := spinner.New()
s.Spinner = spinner.Dot
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
m.spinners = append(m.spinners, s)
}
}
}
Expand All @@ -235,48 +241,53 @@ func (m model) View() string {
}

func (m model) deployView() string {
width := 100 // todo: make dynamic?
text := lipgloss.NewStyle().
Width(50).
Align(lipgloss.Left).
Padding(0, 3).
Render(fmt.Sprintf("%s Package %s deploying ...", m.spinner.View(), m.currentPkg))

// render progress bar until deployment is complete
progressBar := lipgloss.NewStyle().
Width(50).
Align(lipgloss.Left).
Padding(0, 3).
MarginTop(1).
Render(m.progress.View())
ui := lipgloss.JoinVertical(lipgloss.Center, text, progressBar)

if m.complete {
text = lipgloss.NewStyle().
view := ""
for i := range m.progressBars {
width := 100 // todo: make dynamic?
text := lipgloss.NewStyle().
Width(50).
Align(lipgloss.Left).
Padding(0, 3).
Render(fmt.Sprintf("%s Package %s deployed", m.spinner.View(), m.currentPkg))
ui = lipgloss.JoinVertical(lipgloss.Center, text)
}
Render(fmt.Sprintf("%s Package %s deploying ...", m.spinners[i].View(), m.pkgNames[i]))

boxStyle := lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("#874BFD")).
Padding(1, 0).
BorderTop(true).
BorderLeft(true).
BorderRight(true).
BorderBottom(true)
subtle := lipgloss.AdaptiveColor{Light: "#D9DCCF", Dark: "#383838"}

box := lipgloss.Place(width, 9,
lipgloss.Left, lipgloss.Top,
boxStyle.Render(ui),
lipgloss.WithWhitespaceForeground(subtle),
)
// render progress bar until deployment is complete
bar := lipgloss.NewStyle().
Width(50).
Align(lipgloss.Left).
Padding(0, 3).
MarginTop(1).
Render(m.progressBars[i].View())

ui := lipgloss.JoinVertical(lipgloss.Center, text, bar)

if m.complete {
text = lipgloss.NewStyle().
Width(50).
Align(lipgloss.Left).
Padding(0, 3).
Render(fmt.Sprintf("%s Package %s deployed", m.spinners[i].View(), m.pkgNames[i]))
ui = lipgloss.JoinVertical(lipgloss.Center, text)
}

boxStyle := lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("#874BFD")).
Padding(1, 0).
BorderTop(true).
BorderLeft(true).
BorderRight(true).
BorderBottom(true)
subtle := lipgloss.AdaptiveColor{Light: "#D9DCCF", Dark: "#383838"}

box := lipgloss.Place(width, 9,
lipgloss.Left, lipgloss.Top,
boxStyle.Render(ui),
lipgloss.WithWhitespaceForeground(subtle),
)
view += fmt.Sprintf("%s\n", box)
}

return box
return view
}

func (m model) preDeployView() string {
Expand Down
1 change: 1 addition & 0 deletions src/pkg/sources/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func (r *RemoteBundle) LoadPackage(dst *layout.PackagePaths, unarchiveAll bool)
}

// record number of components to be deployed for TUI
// todo: won't work for optional components......
tui.Program.Send(fmt.Sprintf("totalComponents:%d", len(pkg.Components)))

dst.SetFromLayers(layers)
Expand Down
1 change: 1 addition & 0 deletions src/pkg/sources/tarball.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func (t *TarballBundle) LoadPackage(dst *layout.PackagePaths, unarchiveAll bool)
dst.SetFromPaths(files)

// record number of components to be deployed for TUI
// todo: won't work for optional components......
tui.Program.Send(fmt.Sprintf("totalComponents:%d", len(pkg.Components)))

if err := sources.ValidatePackageIntegrity(dst, pkg.Metadata.AggregateChecksum, t.isPartial); err != nil {
Expand Down

0 comments on commit b11a192

Please sign in to comment.