From e8a2a07d646c0ebd93628bfe03e0a115e9c299c9 Mon Sep 17 00:00:00 2001 From: Patrick Dawkins Date: Thu, 2 Jan 2025 00:23:31 +0000 Subject: [PATCH] Color output should be to a colorable writer --- commands/config_install.go | 21 +++++++++++---------- commands/root.go | 38 ++++++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/commands/config_install.go b/commands/config_install.go index 95367567..28057a7b 100644 --- a/commands/config_install.go +++ b/commands/config_install.go @@ -96,16 +96,15 @@ func runConfigInstall(cmd *cobra.Command, args []string) error { } }() - cmd.PrintErrln() - cmd.PrintErrf( - "The following files have been created:\n"+ + printColorable( + cmd.ErrOrStderr(), + "\nThe following files have been created:\n"+ " - Configuration: %s\n"+ - " - Executable: %s\n", + " - Executable: %s\n\n"+ + "The configuration file will be auto-updated periodically in the background.\n", color.CyanString(replaceHomeDir(configFilePath)), color.CyanString(replaceHomeDir(binFilePath)), ) - cmd.PrintErrln() - cmd.PrintErrln("The configuration file will be auto-updated periodically in the background.") isInPath, err := alt.InPath(binDir) if err != nil { @@ -120,16 +119,18 @@ func runConfigInstall(cmd *cobra.Command, args []string) error { cmd.PrintErrln() if isInPath { - cmd.PrintErrln("Run the new executable with:", color.GreenString(filepath.Base(binFilePath))) + printColorable(cmd.ErrOrStderr(), "Run the new executable with: %s\n", color.GreenString(filepath.Base(binFilePath))) } else { - cmd.PrintErrf( + printColorable( + cmd.ErrOrStderr(), "Add the following directory to your %s: %s\n", envVarName, color.YellowString(replaceHomeDir(binDir)), ) cmd.PrintErrln() - cmd.PrintErrln( - "Then you will be able to run the new executable with:", + printColorable( + cmd.ErrOrStderr(), + "Then you will be able to run the new executable with: %s\n", color.YellowString(filepath.Base(binFilePath)), ) } diff --git a/commands/root.go b/commands/root.go index d850e48b..d47cdb17 100644 --- a/commands/root.go +++ b/commands/root.go @@ -13,6 +13,7 @@ import ( "strings" "github.com/fatih/color" + "github.com/mattn/go-colorable" "github.com/platformsh/platformify/commands" "github.com/platformsh/platformify/vendorization" "github.com/spf13/cobra" @@ -88,13 +89,13 @@ func newRootCommand(cnf *config.Config, assets *vendorization.VendorAssets) *cob Stdin: cmd.InOrStdin(), } if err := c.Init(); err != nil { - _, _ = fmt.Fprint(color.Error, color.RedString(err.Error())) + printColorable(cmd.ErrOrStderr(), color.RedString(err.Error())) os.Exit(1) return } if err := c.Exec(cmd.Context(), os.Args[1:]...); err != nil { - _, _ = fmt.Fprint(color.Error, color.RedString(err.Error())) + printColorable(cmd.ErrOrStderr(), color.RedString(err.Error())) exitCode := 1 var execErr *exec.ExitError if errors.As(err, &execErr) { @@ -103,11 +104,11 @@ func newRootCommand(cnf *config.Config, assets *vendorization.VendorAssets) *cob os.Exit(exitCode) } }, - PersistentPostRun: func(_ *cobra.Command, _ []string) { - checkShellConfigLeftovers(cnf) + PersistentPostRun: func(cmd *cobra.Command, _ []string) { + checkShellConfigLeftovers(cnf, cmd.ErrOrStderr()) select { case rel := <-updateMessageChan: - printUpdateMessage(rel, cnf) + printUpdateMessage(rel, cnf, cmd.ErrOrStderr()) default: } }, @@ -169,7 +170,7 @@ func newRootCommand(cnf *config.Config, assets *vendorization.VendorAssets) *cob } // checkShellConfigLeftovers checks .zshrc and .bashrc for any leftovers from the legacy CLI -func checkShellConfigLeftovers(cnf *config.Config) { +func checkShellConfigLeftovers(cnf *config.Config, stdErr io.Writer) { start := fmt.Sprintf("# BEGIN SNIPPET: %s configuration", cnf.Application.Name) end := "# END SNIPPET" shellConfigSnippet := regexp.MustCompile(regexp.QuoteMeta(start) + "(?s).+?" + regexp.QuoteMeta(end)) @@ -195,23 +196,23 @@ func checkShellConfigLeftovers(cnf *config.Config) { } if shellConfigSnippet.Match(shellConfig) { - fmt.Fprintf(color.Error, "%s Your %s file contains code that is no longer needed for the New %s\n", + printColorable(stdErr, "%s Your %s file contains code that is no longer needed for the New %s\n", color.YellowString("Warning:"), shellConfigFile, cnf.Application.Name, ) - fmt.Fprintf(color.Error, "%s %s\n", color.YellowString("Please remove the following lines from:"), shellConfigFile) - fmt.Fprintf(color.Error, "\t%s\n", strings.ReplaceAll(string(shellConfigSnippet.Find(shellConfig)), "\n", "\n\t")) + printColorable(stdErr, "%s %s\n", color.YellowString("Please remove the following lines from:"), shellConfigFile) + printColorable(stdErr, "\t%s\n", strings.ReplaceAll(string(shellConfigSnippet.Find(shellConfig)), "\n", "\n\t")) } } } -func printUpdateMessage(newRelease *internal.ReleaseInfo, cnf *config.Config) { +func printUpdateMessage(newRelease *internal.ReleaseInfo, cnf *config.Config, stdErr io.Writer) { if newRelease == nil { return } - fmt.Fprintf(color.Error, "\n\n%s %s → %s\n", + printColorable(stdErr, "\n\n%s %s → %s\n", color.YellowString(fmt.Sprintf("A new release of the %s is available:", cnf.Application.Name)), color.CyanString(config.Version), color.CyanString(newRelease.Version), @@ -219,20 +220,18 @@ func printUpdateMessage(newRelease *internal.ReleaseInfo, cnf *config.Config) { executable, err := os.Executable() if err == nil && cnf.Wrapper.HomebrewTap != "" && isUnderHomebrew(executable) { - fmt.Fprintf( - color.Error, + printColorable(stdErr, "To upgrade, run: brew update && brew upgrade %s\n", color.YellowString(cnf.Wrapper.HomebrewTap), ) } else if cnf.Wrapper.GitHubRepo != "" { - fmt.Fprintf( - color.Error, + printColorable(stdErr, "To upgrade, follow the instructions at: https://github.com/%s#upgrade\n", cnf.Wrapper.GitHubRepo, ) } - fmt.Fprintf(color.Error, "%s\n\n", color.YellowString(newRelease.URL)) + printColorable(stdErr, "%s\n\n", color.YellowString(newRelease.URL)) } func isUnderHomebrew(binary string) bool { @@ -258,3 +257,10 @@ func debugLog(format string, v ...any) { prefix := color.New(color.ReverseVideo).Sprintf("DEBUG") _, _ = fmt.Fprintf(color.Error, prefix+" "+strings.TrimSpace(format)+"\n", v...) } + +func printColorable(w io.Writer, format string, a ...any) { + if se, ok := w.(*os.File); ok { + w = colorable.NewColorable(se) + } + _, _ = fmt.Fprintf(w, format, a...) +}