Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ toolchain go1.22.3

require (
github.com/briandowns/spinner v1.23.1
github.com/charmbracelet/bubbles v0.20.0
github.com/charmbracelet/bubbletea v1.2.4
github.com/google/uuid v1.6.0
github.com/mitchellh/cli v1.1.5
github.com/olekukonko/tablewriter v0.0.5
Expand All @@ -18,13 +20,24 @@ require (
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.1 // indirect
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/charmbracelet/lipgloss v1.0.0 // indirect
github.com/charmbracelet/x/ansi v0.4.5 // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.11 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/posener/complete v1.1.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
Expand All @@ -34,6 +47,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/sync v0.10.0 // indirect
)

require (
Expand Down
29 changes: 29 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,31 @@ github.com/Masterminds/sprig/v3 v3.2.1 h1:n6EPaDyLSvCEa3frruQvAiHuNp2dhBlMSmkEr+
github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/briandowns/spinner v1.23.1 h1:t5fDPmScwUjozhDj4FA46p5acZWIPXYE30qW2Ptu650=
github.com/briandowns/spinner v1.23.1/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM=
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE=
github.com/charmbracelet/bubbletea v1.2.4/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg=
github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo=
github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSexTdRM=
github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
Expand Down Expand Up @@ -52,6 +68,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
Expand All @@ -61,6 +79,8 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
Expand All @@ -72,6 +92,12 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
Expand Down Expand Up @@ -129,8 +155,11 @@ golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
52 changes: 27 additions & 25 deletions internal/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import (
"context"
"errors"
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/dream11/odin/internal/ui"
"io"
"os"

"github.com/briandowns/spinner"
"github.com/dream11/odin/pkg/constant"
Expand All @@ -21,6 +24,15 @@ type Service struct{}

var responseMap = make(map[string]string)

var program = tea.NewProgram(
&ui.ServiceDeployModel{
ServiceView: ui.ServiceView{
Name: "Initiating service deployment",
},
},
tea.WithAltScreen(),
)

// DeployService deploys service
func (e *Service) DeployService(ctx *context.Context, request *serviceProto.DeployServiceRequest) error {
conn, requestCtx, err := grpcClient(ctx)
Expand All @@ -33,35 +45,25 @@ func (e *Service) DeployService(ctx *context.Context, request *serviceProto.Depl
return err
}

log.Info("Deploying Service...")
spinnerInstance := spinner.New(spinner.CharSets[constant.SpinnerType], constant.SpinnerDelay)
err = spinnerInstance.Color(constant.SpinnerColor, constant.SpinnerStyle)
if err != nil {
return err
}

var message string
for {
response, err := stream.Recv()
spinnerInstance.Stop()
if err != nil {
if errors.Is(err, context.Canceled) || err == io.EOF {
break
go func() {
for {
response, err := stream.Recv()
if err != nil {
if errors.Is(err, context.Canceled) || err == io.EOF {
break
}
log.Errorf("TraceID: %s", (*requestCtx).Value(constant.TraceIDKey))
program.Quit()
}
if response != nil {
program.Send(ui.GetServiceDeployModel(response, (*requestCtx).Value(constant.TraceIDKey).(string)))
}
log.Errorf("TraceID: %s", (*requestCtx).Value(constant.TraceIDKey))
return err
}
}()

if response != nil {

message = util.GenerateResponseMessage(response.GetServiceResponse())
logFailedComponentMessagesOnce(response.GetServiceResponse())
spinnerInstance.Prefix = fmt.Sprintf(" %s ", message)
spinnerInstance.Start()
}
if _, err := program.Run(); err != nil {
os.Exit(1)
}

log.Info(message)
return err
}

Expand Down
163 changes: 163 additions & 0 deletions internal/ui/deploy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package ui

import (
"fmt"
"github.com/charmbracelet/bubbles/progress"
"github.com/charmbracelet/bubbles/spinner"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/dream11/odin/pkg/util"
"strings"
)

func (s *ServiceDeployModel) Init() tea.Cmd {
s.ServiceDisplayMeta.Progress = progress.New(progress.WithDefaultScaledGradient())
s.ServiceDisplayMeta.Progress.PercentageStyle = ProgressBarStyle
s.ServiceDisplayMeta.Progress.SetPercent(100)
s.ServiceDisplayMeta.Cursor = 0
return nil
}

func (s *ServiceDeployModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var updateCmds []tea.Cmd
switch msg := msg.(type) {
// Handle updates
case ServiceDeployModel:
s.ServiceDisplayMeta.Ready = true
s.ServiceView = msg.ServiceView
if len(s.ServiceView.ComponentsView) > len(s.ServiceDisplayMeta.ComponentDisplayMeta) {
for i := len(s.ServiceDisplayMeta.ComponentDisplayMeta); i < len(s.ServiceView.ComponentsView); i++ {
s.ServiceDisplayMeta.ComponentDisplayMeta = append(s.ServiceDisplayMeta.ComponentDisplayMeta, ComponentDisplayMeta{
LogViewPort: viewport.Model{
Width: s.ServiceDisplayMeta.Width,
Height: 10,
},
})
s.ServiceDisplayMeta.ComponentDisplayMeta[i].Spinner = spinner.New()
s.ServiceDisplayMeta.ComponentDisplayMeta[i].Spinner.Style = SpinnerStyle
s.ServiceDisplayMeta.ComponentDisplayMeta[i].Spinner.Spinner = spinner.Points
}
}
// Handle key presses
case tea.KeyMsg:
anyToggled := false
for _, component := range s.ServiceDisplayMeta.ComponentDisplayMeta {
if component.Toggle {
anyToggled = true
break
}
}
switch msg.String() {
case "up":
if s.ServiceDisplayMeta.Cursor > 0 && !anyToggled {
s.ServiceDisplayMeta.Cursor--
}

case "down":
if s.ServiceDisplayMeta.Cursor < len(s.ServiceDisplayMeta.ComponentDisplayMeta)-1 && !anyToggled {
s.ServiceDisplayMeta.Cursor++
}

case "enter", " ":
if s.ServiceDisplayMeta.Cursor < len(s.ServiceDisplayMeta.ComponentDisplayMeta) {
s.ServiceDisplayMeta.ComponentDisplayMeta[s.ServiceDisplayMeta.Cursor].Toggle =
!s.ServiceDisplayMeta.ComponentDisplayMeta[s.ServiceDisplayMeta.Cursor].Toggle
}

case "q":
return s, tea.Quit
}

// Handle window resizes
case tea.WindowSizeMsg:
s.ServiceDisplayMeta.Height = msg.Height
s.ServiceDisplayMeta.Width = msg.Width
if s.ServiceDisplayMeta.Ready {
for i := range s.ServiceDisplayMeta.ComponentDisplayMeta {
s.ServiceDisplayMeta.ComponentDisplayMeta[i].LogViewPort.Width = msg.Width
s.ServiceDisplayMeta.ComponentDisplayMeta[i].LogViewPort.Height =
max(util.GetAvailableViewPortHeight(msg.Height, lipgloss.Height("text"), len(s.ServiceDisplayMeta.ComponentDisplayMeta)), 10)
}
}
case spinner.TickMsg:
spinnerUpdateCmds := s.updateSpinners(msg)
return s, tea.Batch(spinnerUpdateCmds...)
}

// Handle keyboard and mouse events in the viewport
updateCmds = append(updateCmds, s.tickSpinners()...)
updateCmds = append(updateCmds, s.updateViewPort(msg)...)

return s, tea.Batch(updateCmds...)
}

func (s *ServiceDeployModel) View() string {
if !s.ServiceDisplayMeta.Ready {
return H1Style.Render("Initializing service deployment...")
}
var builder strings.Builder

// Build Service View
serviceHeader := util.GetHeaderText(s.ServiceView.Name, s.ServiceView.Action, s.ServiceView.Status, "Service")
builder.WriteString(fmt.Sprintf("%s\n", H1Style.Render(serviceHeader)))
s.ServiceDisplayMeta.Progress.Width = lipgloss.Width(serviceHeader) + 6 // to accommodate the percentage text
builder.WriteString(fmt.Sprintf("%s\n", s.ServiceDisplayMeta.Progress.ViewAs(100.0)))
builder.WriteString(fmt.Sprintf("Trace Id: %s\n", TextStyle.Render(s.ServiceView.TraceID)))

for i, componentView := range s.ServiceView.ComponentsView {
componentHeader := util.GetHeaderText(componentView.Name, componentView.Action, componentView.Status, "Component")
var componentHeaderText string
if i == s.ServiceDisplayMeta.Cursor {
componentHeaderText = SelectedStyle(H2Style).Render(componentHeader)
} else {
componentHeaderText = H2Style.Render(componentHeader)
}
if s.ServiceView.ComponentsView[i].Status != "IN_PROGRESS" {
s.ServiceDisplayMeta.ComponentDisplayMeta[i].Spinner.Spinner = spinner.Pulse
}
builder.WriteString(fmt.Sprintf("%s %s\n", componentHeaderText, s.ServiceDisplayMeta.ComponentDisplayMeta[i].Spinner.View()))
if s.ServiceDisplayMeta.ComponentDisplayMeta[i].Toggle {
// Render logs
logsText := strings.Split(componentView.Content, "\\n")
s.ServiceDisplayMeta.ComponentDisplayMeta[i].LogViewPort.SetContent(InfoStyle.Render(strings.Join(logsText, "\n")))
builder.WriteString(fmt.Sprintf("%s \n", s.ServiceDisplayMeta.ComponentDisplayMeta[i].LogViewPort.View()))
}
}

// Add Footer with operating instructions
builder.WriteString("\n\n")
builder.WriteString(FooterStyle.Render("Use ↑ and ↓ to navigate components, Enter to toggle logs, q to quit"))

return builder.String()
}

func (s *ServiceDeployModel) updateViewPort(msg tea.Msg) []tea.Cmd {
var cmds []tea.Cmd
var cmd tea.Cmd
for i := range s.ServiceDisplayMeta.ComponentDisplayMeta {
s.ServiceDisplayMeta.ComponentDisplayMeta[i].LogViewPort, cmd =
s.ServiceDisplayMeta.ComponentDisplayMeta[i].LogViewPort.Update(msg)
cmds = append(cmds, cmd)
}
return cmds
}

func (s *ServiceDeployModel) updateSpinners(msg tea.Msg) []tea.Cmd {
var cmds []tea.Cmd
var cmd tea.Cmd
for i := range s.ServiceDisplayMeta.ComponentDisplayMeta {
s.ServiceDisplayMeta.ComponentDisplayMeta[i].Spinner, cmd =
s.ServiceDisplayMeta.ComponentDisplayMeta[i].Spinner.Update(msg)
cmds = append(cmds, cmd)
}
return cmds
}

func (s *ServiceDeployModel) tickSpinners() []tea.Cmd {
var cmds []tea.Cmd
for i := range s.ServiceDisplayMeta.ComponentDisplayMeta {
cmds = append(cmds, s.ServiceDisplayMeta.ComponentDisplayMeta[i].Spinner.Tick)
}
return cmds
}
Loading