diff --git a/README.md b/README.md index 7c8fab54..aa9ee465 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ - [Inspect](#bundle-inspect) - [Publish](#bundle-publish) - [Remove](#bundle-remove) + - [Logs](#logs) 1. [Bundle Architecture and Multi-Arch Support](#bundle-architecture-and-multi-arch-support) 1. [Configuration](#configuration) 1. [Sharing Variables](#sharing-variables) @@ -127,6 +128,13 @@ By default all the packages in the bundle are removed, but you can also remove o As an example: `uds remove uds-bundle-.tar.zst --packages init,nginx` +### Logs + +> [!NOTE] +> Only works with `uds deploy` for now, may work for other operations but isn't guaranteed. + +The `uds logs` command can be used to view the most recent logs of a bundle operation. Note that depending on your OS temporary directory and file settings, recent logs are purged after a certain amount of time, so this command may return an error if the logs are no longer available. + ## Bundle Architecture and Multi-Arch Support There are several ways to specify the architecture of a bundle: 1. Setting `--architecture` or `-a` flag during `uds ...` operations: `uds create --architecture arm64` diff --git a/go.mod b/go.mod index f63abb43..3f518a26 100644 --- a/go.mod +++ b/go.mod @@ -2,18 +2,14 @@ module github.com/defenseunicorns/uds-cli go 1.21.6 -replace github.com/defenseunicorns/zarf v0.32.5 => github.com/defenseunicorns/zarf v0.32.6-0.20240321202634-329a1ae1f69c - -replace github.com/defenseunicorns/maru-runner v0.0.2 => github.com/defenseunicorns/maru-runner v0.0.3-0.20240322171619-a7974e34d1d0 - require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b github.com/charmbracelet/bubbles v0.16.1 github.com/charmbracelet/bubbletea v0.25.0 github.com/charmbracelet/lipgloss v0.9.1 - github.com/defenseunicorns/maru-runner v0.0.2 - github.com/defenseunicorns/zarf v0.32.5 + github.com/defenseunicorns/maru-runner v0.1.0 + github.com/defenseunicorns/zarf v0.32.6 github.com/fatih/color v1.16.0 github.com/fsnotify/fsnotify v1.7.0 github.com/goccy/go-yaml v1.11.3 @@ -180,7 +176,7 @@ require ( github.com/distribution/reference v0.5.0 // indirect github.com/docker/cli v24.0.9+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker v24.0.7+incompatible // indirect + github.com/docker/docker v24.0.9+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect diff --git a/go.sum b/go.sum index 6b15e28c..a2471c50 100644 --- a/go.sum +++ b/go.sum @@ -598,10 +598,10 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/daviddengcn/go-colortext v1.0.0 h1:ANqDyC0ys6qCSvuEK7l3g5RaehL/Xck9EX8ATG8oKsE= github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c= -github.com/defenseunicorns/maru-runner v0.0.3-0.20240322171619-a7974e34d1d0 h1:HgtK7fLR1+3PeEgZlykkN40hfngrAZrY2Oc1gv3Enas= -github.com/defenseunicorns/maru-runner v0.0.3-0.20240322171619-a7974e34d1d0/go.mod h1:hyQyYIFMb1b284uVEIbhc9qySyVCDbYcjteniHuNKE0= -github.com/defenseunicorns/zarf v0.32.6-0.20240321202634-329a1ae1f69c h1:EtW8FLWEJ8ducXN7Y1Z7kAmLGuVeicMOSLknYcvV/Q8= -github.com/defenseunicorns/zarf v0.32.6-0.20240321202634-329a1ae1f69c/go.mod h1:EISjg8T4jZUgc1QAMkX/RNQ0PS4WegKcJDZwztvQ2Bw= +github.com/defenseunicorns/maru-runner v0.1.0 h1:LCrMzA+TaQMV2LdvMVrd319jDAIL9CuwhIes0vKf7Kg= +github.com/defenseunicorns/maru-runner v0.1.0/go.mod h1:/0Qu2O8WyZuYB+6LkUsgUwuHiQS8AgXU/Tw7Re0GaTk= +github.com/defenseunicorns/zarf v0.32.6 h1:zWfKmAo898P7OHosKQaiV4E6YjDrqpsryz0BqrQqgg4= +github.com/defenseunicorns/zarf v0.32.6/go.mod h1:EISjg8T4jZUgc1QAMkX/RNQ0PS4WegKcJDZwztvQ2Bw= github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M= github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk= github.com/depcheck-test/depcheck-test v0.0.0-20220607135614-199033aaa936 h1:foGzavPWwtoyBvjWyKJYDYsyzy+23iBV7NKTwdk+LRY= @@ -638,8 +638,8 @@ github.com/docker/cli v24.0.9+incompatible h1:OxbimnP/z+qVjDLpq9wbeFU3Nc30XhSe+L github.com/docker/cli v24.0.9+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= -github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= +github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8= github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= diff --git a/src/pkg/bundle/tui/common.go b/src/pkg/bundle/tui/common.go index 7168bd51..3cb54886 100644 --- a/src/pkg/bundle/tui/common.go +++ b/src/pkg/bundle/tui/common.go @@ -7,6 +7,12 @@ import ( "time" tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +var ( + // IndentStyle is the style for indenting text + IndentStyle = lipgloss.NewStyle().Padding(0, 4) ) func Pause() tea.Cmd { diff --git a/src/pkg/bundle/tui/deploy/handlers.go b/src/pkg/bundle/tui/deploy/handlers.go index 28e382c5..5f6ebe76 100644 --- a/src/pkg/bundle/tui/deploy/handlers.go +++ b/src/pkg/bundle/tui/deploy/handlers.go @@ -18,7 +18,7 @@ import ( zarfTypes "github.com/defenseunicorns/zarf/src/types" ) -func (m *Model) handleNewPackage(pkgName string, currentPkgIdx int) tea.Cmd { +func (m *model) handleNewPackage(pkgName string, currentPkgIdx int) tea.Cmd { // see if pkg has already been deployed deployedPkg, _ := c.GetDeployedPackage(pkgName) newPkg := pkgState{ @@ -49,11 +49,6 @@ func (m *Model) handleNewPackage(pkgName string, currentPkgIdx int) tea.Cmd { downloadSpinner.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) newPkg.downloadSpinner = downloadSpinner - // create spinner to track total bundle progress - bundleSpinner := spinner.New() - bundleSpinner.Spinner = spinner.Ellipsis - bundleSpinner.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) - m.packages = append(m.packages, newPkg) return tea.Batch(m.packages[m.pkgIdx].deploySpinner.Tick, m.packages[m.pkgIdx].verifySpinner.Tick, @@ -61,7 +56,7 @@ func (m *Model) handleNewPackage(pkgName string, currentPkgIdx int) tea.Cmd { ) } -func (m *Model) handlePreDeploy() tea.Cmd { +func (m *model) handlePreDeploy() tea.Cmd { cmd := func() tea.Msg { name, bundleYAML, source, err := m.bndlClient.PreDeployValidation() if err != nil { @@ -80,7 +75,7 @@ func (m *Model) handlePreDeploy() tea.Cmd { return cmd } -func (m *Model) handleDeploy() tea.Cmd { +func (m *model) handleDeploy() tea.Cmd { // ensure bundle deployment is confirmed and is only being deployed once if m.confirmed && !m.deploying { // run Deploy concurrently so we can update the TUI while it runs @@ -102,27 +97,26 @@ func (m *Model) handleDeploy() tea.Cmd { return nil } -func (m *Model) handleDone(err error) tea.Cmd { - //uds := " __ ______ _____\n / / / / __ \\/ ___/\n / / / / / / /\\__ \\ \n/ /_/ / /_/ /___/ / \n\\____/_____//____/ \n \n" +func (m *model) handleDone(err error) tea.Cmd { cmds := []tea.Cmd{tea.Println(), tea.Println(m.udsTitle()), tea.Println()} m.done = true // remove the current view - cmds = append(cmds, genSuccessOrFailCmds(m)...) + cmds = append(cmds, genSuccessCmds(m)...) if err != nil { hint := lightBlueText.Render("uds logs") message.Debug(err) // capture err in debug logs - errMsg := lipgloss.NewStyle().Padding(0, 4).Render(fmt.Sprintf("\nāŒ Error deploying bundle: %s\n\nRun %s to view deployment logs", lightGrayText.Render(err.Error()), hint) + "\n") + errMsg := tui.IndentStyle.Render(fmt.Sprintf("\nāŒ Error deploying bundle: %s\n\nRun %s to view deployment logs", lightGrayText.Render(err.Error()), hint) + "\n") cmds = []tea.Cmd{tea.Println(errMsg), tui.Pause(), tea.Quit} return tea.Sequence(cmds...) } styledBundleName := lipgloss.NewStyle().Foreground(lipgloss.Color("#FFF258")).Render(m.bundleName) - successMsg := tea.Println(lipgloss.NewStyle(). - Padding(0, 4). - Render(fmt.Sprintf("\nāœØ Bundle %s deployed successfully\n", styledBundleName))) + successMsg := tea.Println( + tui.IndentStyle. + Render(fmt.Sprintf("\nāœØ Bundle %s deployed successfully\n", styledBundleName))) cmds = append(cmds, successMsg, tui.Pause(), tea.Quit) return tea.Sequence(cmds...) } -func (m *Model) handleDeployTick() (tea.Model, tea.Cmd) { +func (m *model) handleDeployTick() (tea.Model, tea.Cmd) { // check if all pkgs are complete numComplete := 0 if len(m.packages) == m.totalPkgs { @@ -152,10 +146,10 @@ func (m *Model) handleDeployTick() (tea.Model, tea.Cmd) { if deployedPkg == nil { break } - // handle upgrade scenario by resetting the progress bar, otherwise increment it + // handle upgrade scenario by resetting the component progress, otherwise increment it if p.resetProgress { // if upgraded len(deployedPkg.DeployedComponents) will be equal to the number of components in the package - if deployedPkg != nil && len(deployedPkg.DeployedComponents) == 1 { + if deployedPkg != nil && len(deployedPkg.DeployedComponents) > 0 { m.packages[i].resetProgress = false } break @@ -172,9 +166,7 @@ func (m *Model) handleDeployTick() (tea.Model, tea.Cmd) { // always update logViewport content with logs file, _ := os.ReadFile(config.LogFileName) m.logViewport.SetContent(string(file)) - if !m.isScrolling { - m.logViewport.GotoBottom() - } + m.logViewport.GotoBottom() return m, tickCmd() } diff --git a/src/pkg/bundle/tui/deploy/model.go b/src/pkg/bundle/tui/deploy/model.go index 7df9a948..e9e98d24 100644 --- a/src/pkg/bundle/tui/deploy/model.go +++ b/src/pkg/bundle/tui/deploy/model.go @@ -14,6 +14,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/defenseunicorns/uds-cli/src/config" + "github.com/defenseunicorns/uds-cli/src/pkg/bundle/tui" "github.com/defenseunicorns/zarf/src/pkg/cluster" "golang.org/x/term" ) @@ -38,7 +39,6 @@ var ( c *cluster.Cluster logVpWidthScale = 0.9 logVpHeightScale = 0.4 - lineWidthScale = 0.75 ) // private interface to decouple tui pkg from bundle pkg @@ -52,20 +52,20 @@ type bndlClientShim interface { type pkgState struct { name string numComponents int - percLayersVerified int64 + percLayersVerified int componentStatuses []bool deploySpinner spinner.Model downloadSpinner spinner.Model verifySpinner spinner.Model complete bool resetProgress bool - percDownloaded int64 + percDownloaded int downloaded bool verified bool isRemote bool } -type Model struct { +type model struct { bndlClient bndlClientShim bundleYAML string doneChan chan int @@ -78,7 +78,6 @@ type Model struct { inProgress bool viewLogs bool logViewport viewport.Model - isScrolling bool errChan chan error yamlViewport viewport.Model isRemoteBundle bool @@ -87,7 +86,7 @@ type Model struct { validatingBundleSpinner spinner.Model } -func InitModel(client bndlClientShim) Model { +func InitModel(client bndlClientShim) model { var confirmed bool var inProgress bool var isRemoteBundle bool @@ -116,16 +115,12 @@ func InitModel(client bndlClientShim) Model { // set up logViewport for logs, adjust width and height of logViewport logViewport := viewport.New(int(float64(termWidth)*logVpWidthScale), int(float64(termHeight)*logVpHeightScale)) - logViewport.MouseWheelEnabled = true - logViewport.MouseWheelDelta = 1 // set up yamlViewport to ensure the preDeploy YAML is scrollable - numYamlLines := 10 - yamlViewport := viewport.New(termWidth, numYamlLines) - yamlViewport.MouseWheelEnabled = true - yamlViewport.MouseWheelDelta = 1 + numYAMLLines := 10 + yamlViewport := viewport.New(termWidth, numYAMLLines) - return Model{ + return model{ bndlClient: client, doneChan: make(chan int), errChan: make(chan error), @@ -139,13 +134,13 @@ func InitModel(client bndlClientShim) Model { } } -func (m *Model) Init() tea.Cmd { +func (m *model) Init() tea.Cmd { return func() tea.Msg { return doPreDeploy } } -func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { +func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { select { case err := <-m.errChan: cmd := m.handleDone(err) @@ -202,7 +197,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "n", "N": if !m.confirmed && !m.inProgress { m.done = true - quitMsg := tea.Println("\nšŸ‘‹ Deployment cancelled") + quitMsg := tea.Println(tui.IndentStyle.Render("\nšŸ‘‹ Deployment cancelled")) return m, tea.Sequence(quitMsg, tea.Println(), tea.Quit) } case "ctrl+c", "q": @@ -220,7 +215,6 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "l", "L": if m.inProgress && !m.viewLogs { m.viewLogs = true - m.isScrolling = false } else if m.inProgress { m.viewLogs = false } @@ -247,23 +241,23 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmd := m.handleNewPackage(pkgName, pkgIdx) return m, cmd case totalComponents: - if totalComponents, err := strconv.Atoi(strings.Split(msg, ":")[1]); err == nil { - m.packages[m.pkgIdx].numComponents = totalComponents - m.packages[m.pkgIdx].componentStatuses = make([]bool, totalComponents) + if tc, err := strconv.Atoi(strings.Split(msg, ":")[1]); err == nil { + m.packages[m.pkgIdx].numComponents = tc + m.packages[m.pkgIdx].componentStatuses = make([]bool, tc) } case totalPackages: if totalPkgs, err := strconv.Atoi(strings.Split(msg, ":")[1]); err == nil { m.totalPkgs = totalPkgs } case verifying: - if perc, err := strconv.ParseInt(strings.Split(msg, ":")[1], 10, 8); err == nil { + if perc, err := strconv.Atoi(strings.Split(msg, ":")[1]); err == nil { m.packages[m.pkgIdx].percLayersVerified = perc if perc == 100 { m.packages[m.pkgIdx].verified = true } } case downloading: - if perc, err := strconv.ParseInt(strings.Split(msg, ":")[1], 10, 8); err == nil { + if perc, err := strconv.Atoi(strings.Split(msg, ":")[1]); err == nil { m.packages[m.pkgIdx].percDownloaded = perc if perc == 100 { m.packages[m.pkgIdx].downloaded = true @@ -279,13 +273,13 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil } -func (m *Model) View() string { +func (m *model) View() string { if m.done { // no errors, clear the controlled Program's output return "" } else if m.validatingBundle { validatingBundleMsg := lightGrayText.Render("Validating bundle") - return lipgloss.NewStyle().Padding(0, 4).Render(fmt.Sprintf("\n%s %s", validatingBundleMsg, m.validatingBundleSpinner.View())) + return tui.IndentStyle.Render(fmt.Sprintf("\n%s %s", validatingBundleMsg, m.validatingBundleSpinner.View())) } else if m.viewLogs { return fmt.Sprintf("\n%s\n\n%s\n%s\n\n%s\n", m.udsTitle(), m.bundleDeployProgress(), logMsg, m.logView()) } else if m.confirmed { diff --git a/src/pkg/bundle/tui/deploy/views.go b/src/pkg/bundle/tui/deploy/views.go index d633acd2..bcdc2b3c 100644 --- a/src/pkg/bundle/tui/deploy/views.go +++ b/src/pkg/bundle/tui/deploy/views.go @@ -10,6 +10,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + "github.com/defenseunicorns/uds-cli/src/pkg/bundle/tui" "github.com/fatih/color" "github.com/goccy/go-yaml/lexer" "github.com/goccy/go-yaml/printer" @@ -26,7 +27,7 @@ var ( styledCheck = lipgloss.NewStyle().Foreground(lipgloss.Color("#00FF00")).Render("āœ”") lightBlueText = lipgloss.NewStyle().Foreground(LIGHTBLUE) lightGrayText = lipgloss.NewStyle().Foreground(LIGHTGRAY) - logMsg = lipgloss.NewStyle().Padding(0, 4).Render(fmt.Sprintf("\n%s %s", + logMsg = tui.IndentStyle.Render(fmt.Sprintf("\n%s %s", lightBlueText.Render(""), lightGrayText.Render("Toggle logs"))) ) @@ -38,14 +39,14 @@ var ( }() ) -func (m *Model) logView() string { +func (m *model) logView() string { headerMsg := fmt.Sprintf("%s %s", lightBlueText.Render(m.packages[m.pkgIdx].name), lightGrayText.Render("package logs")) - return lipgloss.NewStyle().Padding(0, 4).Render( + return tui.IndentStyle.Render( fmt.Sprintf("%s\n%s\n%s\n\n", m.logHeaderView(headerMsg), m.logViewport.View(), m.logFooterView()), ) } -func (m *Model) yamlHeaderView() string { +func (m *model) yamlHeaderView() string { upArrow := "ā–² " styledUpArrow := lipgloss.NewStyle().Foreground(LIGHTGRAY).Render(upArrow) if !m.yamlViewport.AtTop() { @@ -55,7 +56,7 @@ func (m *Model) yamlHeaderView() string { return lipgloss.JoinHorizontal(lipgloss.Center, styledUpArrow, headerLine) } -func (m *Model) yamlFooterView() string { +func (m *model) yamlFooterView() string { downArrow := "ā–¼ " styledDownArrow := lipgloss.NewStyle().Foreground(LIGHTGRAY).Render(downArrow) if !m.yamlViewport.AtBottom() { @@ -66,7 +67,7 @@ func (m *Model) yamlFooterView() string { return lipgloss.JoinHorizontal(lipgloss.Center, styledDownArrow, footerLine) } -func (m *Model) logHeaderView(msg string) string { +func (m *model) logHeaderView(msg string) string { title := titleStyle.Render(msg) if msg == "" { title = "" @@ -75,12 +76,12 @@ func (m *Model) logHeaderView(msg string) string { return lipgloss.JoinHorizontal(lipgloss.Center, title, headerLine) } -func (m *Model) logFooterView() string { +func (m *model) logFooterView() string { footerLine := strings.Repeat("ā”€", max(0, m.logViewport.Width)-1) return lipgloss.JoinHorizontal(lipgloss.Center, footerLine) } -func (m *Model) deployView() string { +func (m *model) deployView() string { view := "" for _, p := range m.packages { // count number of successful components @@ -101,9 +102,8 @@ func (m *Model) deployView() string { } if p.complete { - text = lipgloss.NewStyle(). + text = tui.IndentStyle. Align(lipgloss.Left). - Padding(0, 4). Render(fmt.Sprintf("%s Package %s deployed", styledCheck, lightBlueText.Render(p.name))) } @@ -118,14 +118,12 @@ func genLocalPkgText(p pkgState, numComponentsSuccess int) string { styledName := lightBlueText.Render(p.name) styledComponents := lightGrayText.Render(fmt.Sprintf("(%d / %d components)", min(numComponentsSuccess+1, p.numComponents), p.numComponents)) if p.numComponents > 0 { - text = lipgloss.NewStyle(). + text = tui.IndentStyle. Align(lipgloss.Left). - Padding(0, 4). Render(fmt.Sprintf("%s Package %s deploying %s", p.deploySpinner.View(), styledName, styledComponents)) } else { - text = lipgloss.NewStyle(). + text = tui.IndentStyle. Align(lipgloss.Left). - Padding(0, 4). Render(fmt.Sprintf("%s Package %s deploying", p.deploySpinner.View(), styledName)) } return text @@ -137,45 +135,40 @@ func genRemotePkgText(p pkgState, numComponentsSuccess int) string { styledComponents := lightGrayText.Render(fmt.Sprintf("(%d / %d components)", min(numComponentsSuccess+1, p.numComponents), p.numComponents)) if !p.verified { perc := lightGrayText.Render(fmt.Sprintf("(%d%%)", p.percLayersVerified)) - text = lipgloss.NewStyle(). + text = tui.IndentStyle. Align(lipgloss.Left). - Padding(0, 4). Render(fmt.Sprintf("%sVerifying %s package %s", p.verifySpinner.View(), styledName, perc)) } else if p.verified && !p.downloaded { perc := lightGrayText.Render(fmt.Sprintf("(%d%%)", p.percDownloaded)) - text = lipgloss.NewStyle(). + text = tui.IndentStyle. Align(lipgloss.Left). - Padding(0, 4). Render(fmt.Sprintf("%sDownloading %s package %s", p.downloadSpinner.View(), styledName, perc)) } else if p.downloaded && p.verified && p.numComponents > 0 { - text = lipgloss.NewStyle(). + text = tui.IndentStyle. Align(lipgloss.Left). - Padding(0, 4). Render(fmt.Sprintf("%sDeploying %s package %s", p.deploySpinner.View(), styledName, styledComponents)) } else { - text = lipgloss.NewStyle(). + text = tui.IndentStyle. Align(lipgloss.Left). - Padding(0, 4). Render(fmt.Sprintf("%sDeploying %s package", p.deploySpinner.View(), styledName)) } return text } -func (m *Model) preDeployView() string { - paddingStyle := lipgloss.NewStyle().Padding(0, 4) - header := paddingStyle.Render("šŸ“¦ Bundle Definition (ā–² / ā–¼)") - prompt := paddingStyle.Render("ā“ Deploy this bundle? (y/n)") - prettyYAML := paddingStyle.Render(colorPrintYAML(m.bundleYAML)) +func (m *model) preDeployView() string { + header := tui.IndentStyle.Render("šŸ“¦ Bundle Definition (ā–² / ā–¼)") + prompt := tui.IndentStyle.Render("ā“ Deploy this bundle? (y/n)") + prettyYAML := tui.IndentStyle.Render(colorPrintYAML(m.bundleYAML)) m.yamlViewport.SetContent(prettyYAML) // Concatenate header, highlighted YAML, and prompt return fmt.Sprintf("\n%s\n\n%s\n\n%s\n%s\n%s\n\n%s", m.udsTitle(), header, - lipgloss.NewStyle().Padding(0, 4).Render(m.yamlHeaderView()), - lipgloss.NewStyle().Padding(0, 4).Render(m.yamlViewport.View()), - lipgloss.NewStyle().Padding(0, 4).Render(m.yamlFooterView()), + tui.IndentStyle.Render(m.yamlHeaderView()), + tui.IndentStyle.Render(m.yamlViewport.View()), + tui.IndentStyle.Render(m.yamlFooterView()), prompt, ) } @@ -238,7 +231,7 @@ func yamlFormat(attr color.Attribute) string { } // udsTitle returns the title header for the UDS bundle -func (m *Model) udsTitle() string { +func (m *model) udsTitle() string { styledBundleName := lipgloss.NewStyle().Foreground(lipgloss.Color("#FFF258")).Render(m.bundleName + " ") title := " UDS Bundle: " styledTitle := lipgloss.NewStyle().Margin(0, 3). @@ -249,37 +242,19 @@ func (m *Model) udsTitle() string { return styledTitle } -// genSuccessOrFailCmds generates the success or failure messages for each package -func genSuccessOrFailCmds(m *Model) []tea.Cmd { +// genSuccessCmds generates the success or failure messages for each package +func genSuccessCmds(m *model) []tea.Cmd { var cmds []tea.Cmd for i := 0; i < len(m.packages); i++ { - if m.packages[i].complete { - successStyle := lipgloss.NewStyle().Padding(0, 4) - successMsg := fmt.Sprintf("%s Package %s deployed\n", styledCheck, lightBlueText.Render(m.packages[i].name)) - cmds = append(cmds, tea.Println(successStyle.Render(successMsg))) - } else { - failStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#FF0000")).Padding(0, 4) - failMsg := fmt.Sprintf("āŒ Package %s failed to deploy\n", m.packages[i].name) - cmds = append(cmds, tea.Println(failStyle.Render(failMsg))) - } + successMsg := fmt.Sprintf("%s Package %s deployed\n", styledCheck, lightBlueText.Render(m.packages[i].name)) + cmds = append(cmds, tea.Println(tui.IndentStyle.Render(successMsg))) } return cmds } -func (m *Model) bundleDeployProgress() string { +func (m *model) bundleDeployProgress() string { styledText := lightGrayText.Render("šŸ“¦ Deploying bundle package") styledPkgCounter := lightGrayText.Render(fmt.Sprintf("(%d / %d)", m.pkgIdx+1, m.totalPkgs)) msg := fmt.Sprintf("%s %s", styledText, styledPkgCounter) - return lipgloss.NewStyle().Padding(0, 4).Render(msg) + return tui.IndentStyle.Render(msg) } - -//func udsTitle(bundleName string) string { -// styledBundleName := lipgloss.NewStyle().Foreground(lipgloss.Color("#FFF258")).Render(" " + bundleName) -// title := fmt.Sprintf(" UDS Bundle:") -// styledTitle := lipgloss.NewStyle(). -// Margin(0, 0, 0, 3). -// //Background(lipgloss.Color("#6233f2")). -// Foreground(lipgloss.Color("#FFFFFF")). -// Render(fmt.Sprintf("%s", title)) -// return fmt.Sprintf("%s%s", styledTitle, styledBundleName) -//} diff --git a/src/pkg/bundler/fetcher/remote.go b/src/pkg/bundler/fetcher/remote.go index 31a7873c..c9ad92c4 100644 --- a/src/pkg/bundler/fetcher/remote.go +++ b/src/pkg/bundler/fetcher/remote.go @@ -26,6 +26,7 @@ import ( "oras.land/oras-go/v2" ) +// remoteFetcher fetches remote Zarf pkgs for local bundles type remoteFetcher struct { pkg types.Package cfg Config @@ -100,7 +101,7 @@ func (f *remoteFetcher) Fetch() ([]ocispec.Descriptor, error) { func (f *remoteFetcher) layersToLocalBundle(spinner *message.Spinner, currentPackageIter int, totalPackages int) ([]ocispec.Descriptor, error) { spinner.Updatef("Fetching %s package layer metadata (package %d of %d)", f.pkg.Name, currentPackageIter, totalPackages) // get only the layers that are required by the components - layersToCopy, err := utils.GetZarfLayers(*f.remote, f.pkg, f.pkgRootManifest) + layersToCopy, err := utils.GetZarfLayers(*f.remote, f.pkgRootManifest) if err != nil { return nil, err } diff --git a/src/pkg/bundler/pusher/remote.go b/src/pkg/bundler/pusher/remote.go index 1012157b..fa846f26 100644 --- a/src/pkg/bundler/pusher/remote.go +++ b/src/pkg/bundler/pusher/remote.go @@ -88,7 +88,7 @@ func (p *RemotePusher) PushManifest() (ocispec.Descriptor, error) { func (p *RemotePusher) LayersToRemoteBundle(spinner *message.Spinner, currentPackageIter int, totalPackages int) ([]ocispec.Descriptor, error) { spinner.Updatef("Fetching %s package layer metadata (package %d of %d)", p.pkg.Name, currentPackageIter, totalPackages) // get only the layers that are required by the components - layersToCopy, err := utils.GetZarfLayers(p.cfg.RemoteSrc, p.pkg, p.cfg.PkgRootManifest) + layersToCopy, err := utils.GetZarfLayers(p.cfg.RemoteSrc, p.cfg.PkgRootManifest) if err != nil { return nil, err } diff --git a/src/pkg/sources/remote.go b/src/pkg/sources/remote.go index f23631ed..727903b7 100644 --- a/src/pkg/sources/remote.go +++ b/src/pkg/sources/remote.go @@ -40,7 +40,7 @@ type RemoteBundle struct { } // LoadPackage loads a Zarf package from a remote bundle -func (r *RemoteBundle) LoadPackage(dst *layout.PackagePaths, _ filters.ComponentFilterStrategy, unarchiveAll bool) (zarfTypes.ZarfPackage, []string, error) { +func (r *RemoteBundle) LoadPackage(dst *layout.PackagePaths, filter filters.ComponentFilterStrategy, unarchiveAll bool) (zarfTypes.ZarfPackage, []string, error) { // todo: progress bar?? layers, err := r.downloadPkgFromRemoteBundle() if err != nil { @@ -52,6 +52,11 @@ func (r *RemoteBundle) LoadPackage(dst *layout.PackagePaths, _ filters.Component return zarfTypes.ZarfPackage{}, nil, err } + pkg.Components, err = filter.Apply(pkg) + if err != nil { + return pkg, nil, err + } + // record number of components to be deployed for TUI // todo: won't work for optional components...... deploy.Program.Send(fmt.Sprintf("totalComponents:%d", len(pkg.Components))) diff --git a/src/pkg/sources/tarball.go b/src/pkg/sources/tarball.go index 0733f04b..6a7ad0ef 100644 --- a/src/pkg/sources/tarball.go +++ b/src/pkg/sources/tarball.go @@ -52,13 +52,14 @@ func (t *TarballBundle) LoadPackage(dst *layout.PackagePaths, filter filters.Com if err = zarfUtils.ReadYaml(dst.ZarfYAML, &pkg); err != nil { return zarfTypes.ZarfPackage{}, nil, err } - dst.SetFromPaths(files) pkg.Components, err = filter.Apply(pkg) if err != nil { return pkg, nil, err } + dst.SetFromPaths(files) + // record number of components to be deployed for TUI // todo: won't work for optional components...... deploy.Program.Send(fmt.Sprintf("totalComponents:%d", len(pkg.Components))) @@ -239,9 +240,6 @@ func (t *TarballBundle) extractPkgFromBundle() ([]string, error) { return err } - // todo: everything is broken! seems like this fn isn't pulling all the layers - // try rebuilding the packages with the latest Zarf + rebuilding bundle - // try with old version of UDS CLI target, err := os.Create(layerDst) if err != nil { return err diff --git a/src/pkg/utils/oci.go b/src/pkg/utils/oci.go index 228861e5..0a2b2118 100644 --- a/src/pkg/utils/oci.go +++ b/src/pkg/utils/oci.go @@ -261,13 +261,21 @@ func EnsureOCIPrefix(source string) string { } // GetZarfLayers grabs the necessary Zarf pkg layers from a remote OCI registry -func GetZarfLayers(remote zoci.Remote, pkg types.Package, pkgRootManifest *oci.Manifest) ([]ocispec.Descriptor, error) { +func GetZarfLayers(remote zoci.Remote, pkgRootManifest *oci.Manifest) ([]ocispec.Descriptor, error) { + // todo: ensure we are only pulling non-optional components ctx := context.TODO() - var optComponents []zarfTypes.ZarfComponent - for _, c := range pkg.OptionalComponents { - optComponents = append(optComponents, zarfTypes.ZarfComponent{Name: c}) + var components []zarfTypes.ZarfComponent + for _, layer := range pkgRootManifest.Layers { + // infer component name from layer title annotation + titleAnnotation := layer.Annotations[ocispec.AnnotationTitle] + isComponent := strings.HasPrefix(titleAnnotation, "components/") && strings.HasSuffix(titleAnnotation, ".tar") + if isComponent { + afterComponents := strings.Split(titleAnnotation, "components/")[1] + componentName := strings.Split(afterComponents, ".tar")[0] + components = append(components, zarfTypes.ZarfComponent{Name: componentName}) + } } - layersFromComponents, err := remote.LayersFromRequestedComponents(ctx, optComponents) + layersFromComponents, err := remote.LayersFromRequestedComponents(ctx, components) if err != nil { return nil, err } diff --git a/src/pkg/utils/utils.go b/src/pkg/utils/utils.go index 6fec9d35..2f941205 100644 --- a/src/pkg/utils/utils.go +++ b/src/pkg/utils/utils.go @@ -54,8 +54,8 @@ func IsValidTarballPath(path string) bool { // ConfigureLogs sets up the log file, log cache and output for the CLI func ConfigureLogs(op string) error { - // don't configure UDS logs for vendored Zarf cmds - if op == "zarf COMMAND" { + // don't configure UDS logs for vendored cmds + if strings.HasPrefix(op, "zarf") || strings.HasPrefix(op, "run") { return nil } writer, err := message.UseLogFile("") diff --git a/src/test/bundles/04-init/uds-bundle.yaml b/src/test/bundles/04-init/uds-bundle.yaml index 7ecf2a1e..d6944dce 100644 --- a/src/test/bundles/04-init/uds-bundle.yaml +++ b/src/test/bundles/04-init/uds-bundle.yaml @@ -10,12 +10,12 @@ packages: - name: init path: "../../packages" # renovate: datasource=github-tags depName=defenseunicorns/zarf - ref: v0.32.5 + ref: v0.32.6 optionalComponents: - git-server - name: init repository: ghcr.io/defenseunicorns/packages/init # renovate: datasource=github-tags depName=defenseunicorns/zarf - ref: v0.32.5 + ref: v0.32.6 optionalComponents: - git-server diff --git a/src/test/e2e/main_test.go b/src/test/e2e/main_test.go index 2d6cfd77..b20e908e 100644 --- a/src/test/e2e/main_test.go +++ b/src/test/e2e/main_test.go @@ -116,7 +116,7 @@ func deployZarfInit(t *testing.T) { require.NoError(t, err) // Deploy - cmd = strings.Split(fmt.Sprintf("deploy %s --confirm -l=debug", bundlePath), " ") + cmd = strings.Split(fmt.Sprintf("deploy %s --confirm -l=debug --no-tea", bundlePath), " ") _, _, err = e2e.UDS(cmd...) require.NoError(t, err) } diff --git a/src/test/packages/gitrepo/zarf.yaml b/src/test/packages/gitrepo/zarf.yaml index 7fb5964b..8fb7aad1 100644 --- a/src/test/packages/gitrepo/zarf.yaml +++ b/src/test/packages/gitrepo/zarf.yaml @@ -14,15 +14,9 @@ components: - name: simple-nginx-deployment namespace: nginx files: - # remote manifests are specified with a URL and you can verify integrity of a manifest - # by adding a sha256sum to the end of the URL, separated by an @: - https://k8s.io/examples/application/deployment.yaml@c57f73449b26eae02ca2a549c388807d49ef6d3f2dc040a9bbb1290128d97157 - # this sha256 can be discovered using: - # zarf prepare sha256sum https://k8s.io/examples/application/deployment.yaml actions: onDeploy: - # the following checks were computed by viewing the success state of the package deployment - # and creating `wait` actions that match after: - wait: cluster: @@ -30,26 +24,5 @@ components: name: nginx-deployment namespace: nginx condition: available - # image discovery is supported in all manifests and charts using: - # zarf prepare find-images images: - docker.io/library/nginx:1.14.2 - - name: podinfo - required: true - charts: - - name: podinfo - version: 6.4.0 - namespace: podinfo - url: https://github.com/stefanprodan/podinfo.git - gitPath: charts/podinfo - images: - - ghcr.io/stefanprodan/podinfo:6.4.0 - actions: - onDeploy: - after: - - wait: - cluster: - kind: deployment - name: podinfo - namespace: podinfo - condition: available