diff --git a/go.mod b/go.mod index fd3d09d..0cbb38a 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,6 @@ require ( github.com/charmbracelet/bubbletea v0.24.0 github.com/charmbracelet/glamour v0.6.0 github.com/charmbracelet/lipgloss v0.7.1 - github.com/fsnotify/fsnotify v1.4.7 github.com/go-git/go-git/v5 v5.6.1 github.com/lrstanley/bubblezone v0.0.0-20230303230241-08f906ff62a9 github.com/muesli/mango-cobra v1.2.0 diff --git a/go.sum b/go.sum index 117dc22..4882386 100644 --- a/go.sum +++ b/go.sum @@ -91,7 +91,6 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= diff --git a/internal/lib/ansible/ansible.go b/internal/lib/ansible/ansible.go index 6768209..49844fa 100644 --- a/internal/lib/ansible/ansible.go +++ b/internal/lib/ansible/ansible.go @@ -9,30 +9,30 @@ import ( "github.com/apenella/go-ansible/pkg/playbook" "github.com/kirychukyurii/wdeploy/internal/config" "github.com/kirychukyurii/wdeploy/internal/lib/logger" - "os" + "io" "path/filepath" ) -var playbookFileName = "playbook.yml" +const ( + playbookFileName = "playbook.yml" + unixyStdoutCallback = "unixy" +) type Executor struct { cfg config.Config logger logger.Logger + writer io.Writer } -func NewExecutor(cfg config.Config, logger logger.Logger) Executor { +func NewExecutor(cfg config.Config, logger logger.Logger, writer io.Writer) Executor { return Executor{ cfg: cfg, logger: logger, + writer: writer, } } func (e Executor) RunPlaybook() error { - f, err := os.OpenFile(e.cfg.GetAnsibleLogLocation(), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - e.logger.Zap.Error(err) - } - ansiblePlaybookConnectionOptions := &options.AnsibleConnectionOptions{ SSHCommonArgs: e.cfg.AnsibleSSHExtraArgs, } @@ -45,8 +45,9 @@ func (e Executor) RunPlaybook() error { executorTimeMeasurement := measure.NewExecutorTimeMeasurement( execute.NewDefaultExecute( execute.WithEnvVar("ANSIBLE_FORCE_COLOR", "true"), - execute.WithWrite(f), - execute.WithWriteError(f), + execute.WithEnvVar("ANSIBLE_STDOUT_CALLBACK", unixyStdoutCallback), + execute.WithWrite(e.writer), + execute.WithWriteError(e.writer), ), ) @@ -55,10 +56,9 @@ func (e Executor) RunPlaybook() error { ConnectionOptions: ansiblePlaybookConnectionOptions, Options: ansiblePlaybookOptions, Exec: executorTimeMeasurement, - StdoutCallback: "yaml", } - err = pb.Run(context.TODO()) + err := pb.Run(context.TODO()) if err != nil { e.logger.Zap.Error(err) } diff --git a/internal/tui/pages/deploy/deploy.go b/internal/tui/pages/deploy/deploy.go index 02c56ed..ede472d 100644 --- a/internal/tui/pages/deploy/deploy.go +++ b/internal/tui/pages/deploy/deploy.go @@ -1,6 +1,7 @@ package deploy import ( + "bufio" "fmt" "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" @@ -15,6 +16,7 @@ import ( "github.com/kirychukyurii/wdeploy/internal/tui/components/footer" "github.com/kirychukyurii/wdeploy/internal/tui/components/statusbar" "github.com/kirychukyurii/wdeploy/internal/tui/components/tabs" + "io" ) /* @@ -64,6 +66,7 @@ type Inventory struct { activeTab tab tabs *tabs.Tabs panes []common.Component + sub chan string cfg config.Config logger logger.Logger @@ -95,6 +98,7 @@ func New(c common.Common, cfg config.Config, logger logger.Logger) *Inventory { statusbar: sb, tabs: tb, panes: panes, + sub: make(chan string), cfg: cfg, logger: logger, } @@ -115,8 +119,6 @@ func (r *Inventory) SetSize(width, height int) { for _, p := range r.panes { p.SetSize(width, height-hm) } - - r.logger.Zap.Debug(fmt.Sprintf("width=%d height=%d hm=%d height-hm=%d", width, height, hm, height-hm)) } func (r *Inventory) commonHelp() []key.Binding { @@ -150,13 +152,12 @@ func (r *Inventory) Init() tea.Cmd { return tea.Batch( r.tabs.Init(), r.statusbar.Init(), + waitForActivity(r.sub), ) } // Update implements tea.Model. func (r *Inventory) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - r.logger.Zap.Debugf("Inventory.Update() msg.%T=%s", msg, msg) - cmds := make([]tea.Cmd, 0) switch msg := msg.(type) { @@ -171,9 +172,7 @@ func (r *Inventory) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //r.tabs.Update() // TODO - _ = r.deploy() - - r.logger.Zap.Debug(fmt.Sprintf("msg=%d", msg)) + _ = r.deploy(r.sub) } case RepoMsg: r.activeTab = 0 @@ -183,6 +182,7 @@ func (r *Inventory) Update(msg tea.Msg) (tea.Model, tea.Cmd) { r.updateStatusBarCmd, r.updateModels(msg), ) + case tabs.SelectTabMsg: r.activeTab = tab(msg) t, cmd := r.tabs.Update(msg) @@ -387,12 +387,64 @@ func backCmd() tea.Msg { return BackMsg{} } -func (r *Inventory) deploy() error { +func (i *Inventory) deploy(sub chan string) error { + reader, writer := io.Pipe() + + go func() { + r := bufio.NewReader(reader) + + for { + line, err := readLine(r) + if err != nil { + if err != io.EOF { + i.logger.Zap.Error(err) + } + + break + } + + i.logger.Zap.Info(line) + sub <- line + } + }() + go func() { - executor := ansible.NewExecutor(r.cfg, r.logger) + executor := ansible.NewExecutor(i.cfg, i.logger, writer) _ = executor.RunPlaybook() }() return nil } + +func readLine(r *bufio.Reader) (string, error) { + var line []byte + for { + l, more, err := r.ReadLine() + if err != nil { + return "", err + } + + // Avoid the copy if the first call produced a full line. + if line == nil && !more { + return string(l), nil + } + + line = append(line, l...) + if !more { + break + } + } + + return string(line), nil +} + +// A command that waits for the activity on a channel. +func waitForActivity(sub chan string) tea.Cmd { + return func() tea.Msg { + return LogMsg{ + message: <-sub, + sub: sub, + } + } +} diff --git a/internal/tui/pages/deploy/log_tab.go b/internal/tui/pages/deploy/log_tab.go index 30a32bb..4a2c04c 100644 --- a/internal/tui/pages/deploy/log_tab.go +++ b/internal/tui/pages/deploy/log_tab.go @@ -6,7 +6,6 @@ import ( "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/fsnotify/fsnotify" "github.com/kirychukyurii/wdeploy/internal/config" "github.com/kirychukyurii/wdeploy/internal/lib/file" "github.com/kirychukyurii/wdeploy/internal/lib/logger" @@ -15,7 +14,10 @@ import ( "github.com/kirychukyurii/wdeploy/internal/tui/components/code" ) -type LogMsg struct{} +type LogMsg struct { + message string + sub chan string +} // LogContentMsg is a message that contains the content of a file. type LogContentMsg struct { @@ -33,7 +35,7 @@ type Log struct { lineNumber bool // path string - sub chan struct{} // where we'll receive activity notifications + sub chan string // where we'll receive activity notifications cfg config.Config logger logger.Logger @@ -45,7 +47,7 @@ func NewLog(common common.Common, cfg config.Config, logger logger.Logger) *Log s.Spinner = spinner.Dot f := &Log{ - sub: make(chan struct{}), + sub: make(chan string), common: common, code: code.New(common, "", ""), spinner: s, @@ -102,20 +104,15 @@ func (r *Log) FullHelp() [][]key.Binding { // Init implements tea.Model. func (r *Log) Init() tea.Cmd { r.currentContent.content, _ = file.ReadFileContent(r.cfg.GetAnsibleLogLocation()) - r.code.GotoBottom() return tea.Batch( r.code.SetContent(r.currentContent.content, ".yml"), - r.fileWatcher(r.sub), - r.waitForActivity(r.sub), // wait for activity ) } // Update implements tea.Model. func (r *Log) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - r.logger.Zap.Debugf("Log.Update() msg.%T=%s", msg, msg) - cmds := make([]tea.Cmd, 0) switch msg := msg.(type) { @@ -129,12 +126,10 @@ func (r *Log) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case LogMsg: - r.logger.Zap.Debug(fmt.Sprintf("Log.Update().LogContentMsg: %s", msg)) - r.currentContent.content, _ = file.ReadFileContent(r.cfg.GetAnsibleLogLocation()) - + r.currentContent.content += fmt.Sprintln(msg.message) r.code.SetContent(r.currentContent.content, ".yml") r.code.GotoBottom() - cmds = append(cmds, r.waitForActivity(r.sub), updateStatusBarCmd) + cmds = append(cmds, waitForActivity(msg.sub)) case RepoMsg: r.repo = action.Action(msg) @@ -176,64 +171,3 @@ func (r *Log) StatusBarInfo() string { func (r *Log) StatusBarBranch() string { return fmt.Sprintf("v%s", r.cfg.WebitelVersion) } - -/* -func (r *Log) initSpinner() tea.Cmd { - return r.spinner.Tick -} - -func (r *Log) deployWebitel() tea.Cmd { - r.initSpinner() - - return nil -} -*/ - -func (r *Log) fileWatcher(sub chan struct{}) tea.Cmd { - r.logger.Zap.Debug("setup watcher") - watcher, err := fsnotify.NewWatcher() - if err != nil { - r.logger.Zap.Error(err) - } - - // provide the file name along with path to be watched - err = watcher.Add(fmt.Sprintf("%s/ansible.log", r.cfg.LogDirectory)) - if err != nil { - r.logger.Zap.Error(err) - } - - // done := make(chan bool) - // use goroutine to start the watcher - return func() tea.Msg { - defer func(watcher *fsnotify.Watcher) { - r.logger.Zap.Debug("close watcher") - if tempErr := watcher.Close(); tempErr != nil { - err = tempErr - } - }(watcher) - - for { - select { - case event := <-watcher.Events: - // monitor only for write events - if event.Op&fsnotify.Write == fsnotify.Write { - sub <- struct{}{} - - r.logger.Zap.Debug(fmt.Sprintf("Modified file: %s", event.Name)) - } - case err := <-watcher.Errors: - if err != nil { - r.logger.Zap.Error(err) - } - } - } - } - -} - -// A command that waits for the activity on a channel. -func (r *Log) waitForActivity(sub chan struct{}) tea.Cmd { - return func() tea.Msg { - return LogMsg(<-sub) - } -} diff --git a/internal/tui/pages/deploy/view_tab.go b/internal/tui/pages/deploy/view_tab.go index 1bef8aa..49fe248 100644 --- a/internal/tui/pages/deploy/view_tab.go +++ b/internal/tui/pages/deploy/view_tab.go @@ -72,8 +72,6 @@ func (r *View) SetSize(width, height int) { r.code.SetSize(width, height-hm-3) r.dialog.SetSize(width, hm) - - r.logger.Zap.Debug(fmt.Sprintf("width=%d height=%d hm=%d", width, height, r.common.Styles.Dialog.Box.GetHeight())) } // ShortHelp implements help.KeyMap. diff --git a/internal/tui/pages/hosts/hosts.go b/internal/tui/pages/hosts/hosts.go index f51023b..6fe1680 100644 --- a/internal/tui/pages/hosts/hosts.go +++ b/internal/tui/pages/hosts/hosts.go @@ -147,7 +147,6 @@ func (r *Inventory) Init() tea.Cmd { // Update implements tea.Model. func (r *Inventory) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmds := make([]tea.Cmd, 0) - r.logger.Zap.Debug(msg) switch msg := msg.(type) { case RepoMsg: r.activeTab = 0 diff --git a/internal/tui/pages/vars/vars.go b/internal/tui/pages/vars/vars.go index 6106331..f1301d0 100644 --- a/internal/tui/pages/vars/vars.go +++ b/internal/tui/pages/vars/vars.go @@ -147,7 +147,6 @@ func (r *Repo) Init() tea.Cmd { // Update implements tea.Model. func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmds := make([]tea.Cmd, 0) - r.logger.Zap.Debug(msg) switch msg := msg.(type) { case RepoMsg: r.activeTab = 0 diff --git a/internal/tui/tui.go b/internal/tui/tui.go index 2db3be4..dbc4189 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -187,9 +187,9 @@ func (ui *UI) IsFiltering() bool { // Update implements tea.Model. func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - cmds := make([]tea.Cmd, 0) ui.logger.Zap.Debugf("Update() msg.%T=%s", msg, msg) + cmds := make([]tea.Cmd, 0) switch msg := msg.(type) { case tea.WindowSizeMsg: ui.SetSize(msg.Width, msg.Height) @@ -202,8 +202,6 @@ func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case tea.KeyMsg, tea.MouseMsg: - ui.logger.Zap.Debugf("Update() msg.%T=%s", msg, msg) - ui.logger.Zap.Debugf("Update() ui.activePage=%d", ui.activePage) switch msg := msg.(type) { case tea.KeyMsg: switch { @@ -219,7 +217,6 @@ func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if !ui.IsFiltering() { // Stop bubble-zone background workers. ui.common.Zone.Close() - ui.logger.Zap.Debug("Update() received a quit command") return ui, tea.Quit } @@ -248,8 +245,6 @@ func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case footer.ToggleFooterMsg: - ui.logger.Zap.Debugf("Update() msg.%T=%s", msg, msg) - ui.logger.Zap.Debugf("Update() ui.activePage=%d", ui.activePage) ui.footer.SetShowAll(!ui.footer.ShowAll()) // Show the footer when on repo page and shot all help. if ui.error == nil && ui.activePage == varsPage { @@ -261,23 +256,21 @@ func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case selector.SelectMsg: switch msg.ID() { case "vars": - ui.logger.Zap.Debugf("Update() ui.activePage=%d", ui.activePage) ui.activePage = varsPage ui.showFooter = ui.footer.ShowAll() case "hosts": - ui.logger.Zap.Debugf("Update() ui.activePage=%d", ui.activePage) ui.activePage = hostsPage ui.showFooter = ui.footer.ShowAll() case "deploy": ui.activePage = deployPage ui.showFooter = ui.footer.ShowAll() } - - case selector.ActiveMsg: - ui.logger.Zap.Debugf("Update() ui.activePage=%d", ui.activePage) - + /* + case selector.ActiveMsg: + ui.logger.Zap.Debugf("Update() ui.activePage=%d", ui.activePage) + */ } - ui.logger.Zap.Debugf("Update() ui.activePage=%d", ui.activePage) + //ui.activePage = varsPage /* case vars.RepoMsg: @@ -295,7 +288,7 @@ func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { /* case selector.SelectMsg: ui.logger.Zap.Debugf("Update() ui.activePage=%d", ui.activePage) - + */ /* case selector.SelectMsg: