From e01c7f359e1f98eca6b93f9dcb081e1311438af4 Mon Sep 17 00:00:00 2001 From: Andrey Kalmykov Date: Wed, 27 Nov 2024 19:22:54 +0600 Subject: [PATCH 1/4] Add custom help message and upgrade notification --- cmd/cmd_utils.go | 62 ++++++++++++++++++++++++++++++++++++++++++------ cmd/root.go | 8 +++++++ 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/cmd/cmd_utils.go b/cmd/cmd_utils.go index 2bdbdb8fb..005d5957b 100644 --- a/cmd/cmd_utils.go +++ b/cmd/cmd_utils.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path" + "regexp" "strings" "github.com/fatih/color" @@ -16,6 +17,7 @@ import ( cfg "github.com/cloudposse/atmos/pkg/config" "github.com/cloudposse/atmos/pkg/schema" u "github.com/cloudposse/atmos/pkg/utils" + "github.com/cloudposse/atmos/pkg/version" ) // ValidateConfig holds configuration options for Atmos validation. @@ -27,6 +29,8 @@ type ValidateConfig struct { type AtmosValidateOption func(*ValidateConfig) +var originalHelpFunc func(*cobra.Command, []string) + func WithStackValidation(check bool) AtmosValidateOption { return func(cfg *ValidateConfig) { cfg.CheckStack = check @@ -423,17 +427,61 @@ func printMessageForMissingAtmosConfig(cliConfig schema.CliConfiguration) { // printMessageToUpgradeToAtmosLatestRelease prints info on how to upgrade Atmos to the latest version func printMessageToUpgradeToAtmosLatestRelease(latestVersion string) { - c1 := color.New(color.FgCyan) c2 := color.New(color.FgGreen) - u.PrintMessageInColor(fmt.Sprintf("\nYour version of Atmos is out of date. The latest version is %s\n\n", latestVersion), c1) - u.PrintMessage("To upgrade Atmos, refer to the following links and documents:\n") + message := fmt.Sprintf("Atmos update available: %s. Enjoy the latest updates and improvements!", c2.Sprint(latestVersion)) + links := []string{ + fmt.Sprintf("%s: https://github.com/cloudposse/atmos/releases", c2.Sprint("Atmos Releases")), + fmt.Sprintf("%s: https://atmos.tools/install", c2.Sprint("Install Atmos")), + } + messageLines := append([]string{message}, links...) + maxWidth := 0 + for _, line := range messageLines { + lineLength := len(stripANSI(line)) + if lineLength > maxWidth { + maxWidth = lineLength + } + } + + // Calculate border width + padding := 2 + borderWidth := maxWidth + padding*2 + + // Print top border + c2.Println("┌" + strings.Repeat("─", borderWidth) + "┐") - u.PrintMessageInColor("Atmos Releases:\n", c2) - u.PrintMessage("https://github.com/cloudposse/atmos/releases\n") + // Print each line with padding + for _, line := range messageLines { + lineLength := len(stripANSI(line)) + spaces := borderWidth - lineLength + leftPadding := spaces / 2 + rightPadding := spaces - leftPadding + fmt.Printf("%s%s%s%s%s\n", c2.Sprint("│"), strings.Repeat(" ", leftPadding), line, strings.Repeat(" ", rightPadding), c2.Sprint("│")) + } + + // Print bottom border + c2.Println("└" + strings.Repeat("─", borderWidth) + "┘") +} - u.PrintMessageInColor("Install Atmos:\n", c2) - u.PrintMessage("https://atmos.tools/install\n") +// Function to strip ANSI color codes +func stripANSI(str string) string { + ansi := "\033\\[[0-9;]*m" + re := regexp.MustCompile(ansi) + return re.ReplaceAllString(str, "") +} + +// customHelpMessageToUpgradeToAtmosLatestRelease adds Atmos version info at the end of each help commnad +func customHelpMessageToUpgradeToAtmosLatestRelease(cmd *cobra.Command, args []string) { + originalHelpFunc(cmd, args) + // Check for the latest Atmos release on GitHub + latestReleaseTag, err := u.GetLatestGitHubRepoRelease("cloudposse", "atmos") + if err == nil && latestReleaseTag != "" { + latestRelease := strings.TrimPrefix(latestReleaseTag, "v") + currentRelease := strings.TrimPrefix(version.Version, "v") + if latestRelease != currentRelease { + printMessageToUpgradeToAtmosLatestRelease(latestRelease) + } + } } // Check Atmos is version command diff --git a/cmd/root.go b/cmd/root.go index d02cacf5d..97429869d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -69,6 +69,14 @@ func Execute() error { Flags: cc.Bold, }) + // Save the original help function to prevent infinite recursion when overriding it. + // This allows us to call the original help functionality within our custom help function. + originalHelpFunc = RootCmd.HelpFunc() + + // Override the help function with a custom one that adds an upgrade message after displaying help. + // This custom help function will call the original help function and then display the bordered message. + RootCmd.SetHelpFunc(customHelpMessageToUpgradeToAtmosLatestRelease) + // Check if the `help` flag is passed and print a styled Atmos logo to the terminal before printing the help err := RootCmd.ParseFlags(os.Args) if err != nil && errors.Is(err, pflag.ErrHelp) { From be49831405cdb7c4570dd63deee418f4a68cf57c Mon Sep 17 00:00:00 2001 From: Andrey Kalmykov Date: Wed, 27 Nov 2024 21:47:31 +0600 Subject: [PATCH 2/4] Updated wording --- cmd/cmd_utils.go | 4 +--- cmd/root.go | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/cmd_utils.go b/cmd/cmd_utils.go index 005d5957b..2a99c3bfd 100644 --- a/cmd/cmd_utils.go +++ b/cmd/cmd_utils.go @@ -29,8 +29,6 @@ type ValidateConfig struct { type AtmosValidateOption func(*ValidateConfig) -var originalHelpFunc func(*cobra.Command, []string) - func WithStackValidation(check bool) AtmosValidateOption { return func(cfg *ValidateConfig) { cfg.CheckStack = check @@ -429,7 +427,7 @@ func printMessageForMissingAtmosConfig(cliConfig schema.CliConfiguration) { func printMessageToUpgradeToAtmosLatestRelease(latestVersion string) { c2 := color.New(color.FgGreen) - message := fmt.Sprintf("Atmos update available: %s. Enjoy the latest updates and improvements!", c2.Sprint(latestVersion)) + message := fmt.Sprintf("%s %s >> %s", c2.Sprint("Update available!"), version.Version, c2.Sprint(latestVersion)) links := []string{ fmt.Sprintf("%s: https://github.com/cloudposse/atmos/releases", c2.Sprint("Atmos Releases")), fmt.Sprintf("%s: https://atmos.tools/install", c2.Sprint("Install Atmos")), diff --git a/cmd/root.go b/cmd/root.go index 97429869d..0027776c1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -18,6 +18,9 @@ import ( u "github.com/cloudposse/atmos/pkg/utils" ) +// originalHelpFunc holds Cobra's original help function to avoid recursion. +var originalHelpFunc func(*cobra.Command, []string) + // RootCmd represents the base command when called without any subcommands var RootCmd = &cobra.Command{ Use: "atmos", From 308fd1aa85df3cb8bc6ecc86d59c2897def0f2b6 Mon Sep 17 00:00:00 2001 From: Andrey Kalmykov Date: Fri, 29 Nov 2024 00:57:15 +0600 Subject: [PATCH 3/4] =?UTF-8?q?changing=20colors/adding=20=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/cmd_utils.go | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/cmd/cmd_utils.go b/cmd/cmd_utils.go index 2a99c3bfd..7726c5346 100644 --- a/cmd/cmd_utils.go +++ b/cmd/cmd_utils.go @@ -425,12 +425,17 @@ func printMessageForMissingAtmosConfig(cliConfig schema.CliConfiguration) { // printMessageToUpgradeToAtmosLatestRelease prints info on how to upgrade Atmos to the latest version func printMessageToUpgradeToAtmosLatestRelease(latestVersion string) { + //RGB values to define dark grey color + r1, g1, b1 := 105, 105, 105 + + c1 := color.RGB(r1, g1, b1) c2 := color.New(color.FgGreen) + c3 := color.New(color.FgCyan) - message := fmt.Sprintf("%s %s >> %s", c2.Sprint("Update available!"), version.Version, c2.Sprint(latestVersion)) + message := fmt.Sprintf("Update available! %s » %s", c1.Sprint(version.Version), c2.Sprint(latestVersion)) links := []string{ - fmt.Sprintf("%s: https://github.com/cloudposse/atmos/releases", c2.Sprint("Atmos Releases")), - fmt.Sprintf("%s: https://atmos.tools/install", c2.Sprint("Install Atmos")), + fmt.Sprintf("Atmos Releases: %s", c3.Sprint("https://github.com/cloudposse/atmos/releases")), + fmt.Sprintf("Install Atmos: %s", c3.Sprint("https://atmos.tools/install")), } messageLines := append([]string{message}, links...) maxWidth := 0 @@ -450,7 +455,7 @@ func printMessageToUpgradeToAtmosLatestRelease(latestVersion string) { // Print each line with padding for _, line := range messageLines { - lineLength := len(stripANSI(line)) + lineLength := calculateDisplayWidth(line) spaces := borderWidth - lineLength leftPadding := spaces / 2 rightPadding := spaces - leftPadding @@ -468,6 +473,20 @@ func stripANSI(str string) string { return re.ReplaceAllString(str, "") } +// Function to calculate display width, treating '»' as width 1 +func calculateDisplayWidth(str string) int { + stripped := stripANSI(str) + width := 0 + for _, r := range stripped { + if r == '»' { + width += 1 + } else { + width += 1 + } + } + return width +} + // customHelpMessageToUpgradeToAtmosLatestRelease adds Atmos version info at the end of each help commnad func customHelpMessageToUpgradeToAtmosLatestRelease(cmd *cobra.Command, args []string) { originalHelpFunc(cmd, args) From 9cd2676d0d113320cf526133a627c0c4503569f4 Mon Sep 17 00:00:00 2001 From: Andrey Kalmykov Date: Wed, 4 Dec 2024 20:07:38 +0600 Subject: [PATCH 4/4] lipgloss border --- cmd/cmd_utils.go | 82 +++++++++++++++--------------------------------- 1 file changed, 26 insertions(+), 56 deletions(-) diff --git a/cmd/cmd_utils.go b/cmd/cmd_utils.go index 7726c5346..a3d5dfc8b 100644 --- a/cmd/cmd_utils.go +++ b/cmd/cmd_utils.go @@ -6,9 +6,9 @@ import ( "fmt" "os" "path" - "regexp" "strings" + "github.com/charmbracelet/lipgloss" "github.com/fatih/color" "github.com/spf13/cobra" @@ -425,66 +425,36 @@ func printMessageForMissingAtmosConfig(cliConfig schema.CliConfiguration) { // printMessageToUpgradeToAtmosLatestRelease prints info on how to upgrade Atmos to the latest version func printMessageToUpgradeToAtmosLatestRelease(latestVersion string) { - //RGB values to define dark grey color - r1, g1, b1 := 105, 105, 105 - - c1 := color.RGB(r1, g1, b1) - c2 := color.New(color.FgGreen) - c3 := color.New(color.FgCyan) - - message := fmt.Sprintf("Update available! %s » %s", c1.Sprint(version.Version), c2.Sprint(latestVersion)) - links := []string{ - fmt.Sprintf("Atmos Releases: %s", c3.Sprint("https://github.com/cloudposse/atmos/releases")), - fmt.Sprintf("Install Atmos: %s", c3.Sprint("https://atmos.tools/install")), - } - messageLines := append([]string{message}, links...) - maxWidth := 0 - for _, line := range messageLines { - lineLength := len(stripANSI(line)) - if lineLength > maxWidth { - maxWidth = lineLength - } + // Define colors + c1 := lipgloss.NewStyle().Foreground(lipgloss.Color("8")) + c2 := lipgloss.NewStyle().Foreground(lipgloss.Color("10")) + c3 := lipgloss.NewStyle().Foreground(lipgloss.Color("14")) + + // Define content + message := lipgloss.NewStyle(). + Render(fmt.Sprintf("Update available! %s » %s", + c1.Render(version.Version), + c2.Render(latestVersion))) + + links := []string{lipgloss.NewStyle().Render(fmt.Sprintf("Atmos Releases: %s", c3.Render("https://github.com/cloudposse/atmos/releases"))), + lipgloss.NewStyle().Render(fmt.Sprintf("Install Atmos: %s", c3.Render("https://atmos.tools/install"))), } - // Calculate border width - padding := 2 - borderWidth := maxWidth + padding*2 - - // Print top border - c2.Println("┌" + strings.Repeat("─", borderWidth) + "┐") - - // Print each line with padding - for _, line := range messageLines { - lineLength := calculateDisplayWidth(line) - spaces := borderWidth - lineLength - leftPadding := spaces / 2 - rightPadding := spaces - leftPadding - fmt.Printf("%s%s%s%s%s\n", c2.Sprint("│"), strings.Repeat(" ", leftPadding), line, strings.Repeat(" ", rightPadding), c2.Sprint("│")) - } + messageLines := append([]string{message}, links...) + messageContent := strings.Join(messageLines, "\n") - // Print bottom border - c2.Println("└" + strings.Repeat("─", borderWidth) + "┘") -} + // Define box + boxStyle := lipgloss.NewStyle(). + Border(lipgloss.RoundedBorder()). + BorderForeground(lipgloss.Color("10")). + Padding(0, 1). + Align(lipgloss.Center) -// Function to strip ANSI color codes -func stripANSI(str string) string { - ansi := "\033\\[[0-9;]*m" - re := regexp.MustCompile(ansi) - return re.ReplaceAllString(str, "") -} + // Render the box + box := boxStyle.Render(messageContent) -// Function to calculate display width, treating '»' as width 1 -func calculateDisplayWidth(str string) int { - stripped := stripANSI(str) - width := 0 - for _, r := range stripped { - if r == '»' { - width += 1 - } else { - width += 1 - } - } - return width + // Print the box + fmt.Println(box) } // customHelpMessageToUpgradeToAtmosLatestRelease adds Atmos version info at the end of each help commnad