Skip to content

Commit

Permalink
Merge branch 'main' into compare-file-hashes
Browse files Browse the repository at this point in the history
  • Loading branch information
pjcdawkins committed Jan 15, 2025
2 parents 42f9ff8 + 9df870d commit f6c29b6
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 84 deletions.
11 changes: 5 additions & 6 deletions cmd/platform/main.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package main

import (
"log"
"fmt"
"os"
"strings"

"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"

Expand All @@ -14,16 +13,16 @@ import (
)

func main() {
log.SetOutput(color.Error)

// Load configuration.
cnfYAML, err := config.LoadYAML()
if err != nil {
log.Fatal(err)
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
cnf, err := config.FromYAML(cnfYAML)
if err != nil {
log.Fatal(err)
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

// When Cobra starts, load Viper config from the environment.
Expand Down
24 changes: 7 additions & 17 deletions commands/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,31 @@ import (
"strings"

"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/platformsh/cli/internal/config"
"github.com/platformsh/cli/internal/legacy"
)

func newCompletionCommand(cnf *config.Config) *cobra.Command {
return &cobra.Command{
Use: "completion",
Short: "Print the completion script for your shell",
Args: cobra.MaximumNArgs(1),
Use: "completion",
Short: "Print the completion script for your shell",
Args: cobra.MaximumNArgs(1),
SilenceErrors: true,
Run: func(cmd *cobra.Command, args []string) {
completionArgs := []string{"_completion", "-g", "--program", cnf.Application.Executable}
if len(args) > 0 {
completionArgs = append(completionArgs, "--shell-type", args[0])
}
var b bytes.Buffer
c := &legacy.CLIWrapper{
Config: cnf,
Version: version,
CustomPharPath: viper.GetString("phar-path"),
Debug: viper.GetBool("debug"),
DisableInteraction: viper.GetBool("no-interaction"),
Stdout: &b,
Stderr: cmd.ErrOrStderr(),
Stdin: cmd.InOrStdin(),
}
c := makeLegacyCLIWrapper(cnf, &b, cmd.ErrOrStderr(), cmd.InOrStdin())

if err := c.Exec(cmd.Context(), completionArgs...); err != nil {
handleLegacyError(err)
exitWithError(err)
}

pharPath, err := c.PharPath()
if err != nil {
handleLegacyError(err)
exitWithError(err)
}

completions := strings.ReplaceAll(
Expand Down
30 changes: 7 additions & 23 deletions commands/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@ import (
"bytes"
"encoding/json"
"fmt"
"os"

"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/platformsh/cli/internal/config"
"github.com/platformsh/cli/internal/legacy"
)

func newListCommand(cnf *config.Config) *cobra.Command {
Expand All @@ -20,18 +17,6 @@ func newListCommand(cnf *config.Config) *cobra.Command {
Short: "Lists commands",
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
var b bytes.Buffer
c := &legacy.CLIWrapper{
Config: cnf,
Version: version,
CustomPharPath: viper.GetString("phar-path"),
Debug: viper.GetBool("debug"),
DisableInteraction: viper.GetBool("no-interaction"),
Stdout: &b,
Stderr: cmd.ErrOrStderr(),
Stdin: cmd.InOrStdin(),
}

arguments := []string{"list", "--format=json"}
if viper.GetBool("all") {
arguments = append(arguments, "--all")
Expand All @@ -40,15 +25,16 @@ func newListCommand(cnf *config.Config) *cobra.Command {
arguments = append(arguments, args[0])
}

var b bytes.Buffer
c := makeLegacyCLIWrapper(cnf, &b, cmd.ErrOrStderr(), cmd.InOrStdin())

if err := c.Exec(cmd.Context(), arguments...); err != nil {
handleLegacyError(err)
exitWithError(err)
}

var list List
if err := json.Unmarshal(b.Bytes(), &list); err != nil {
debugLog("%s\n", color.RedString(err.Error()))
os.Exit(1)
return
exitWithError(err)
}

// Override the application name and executable with our own config.
Expand Down Expand Up @@ -86,16 +72,14 @@ func newListCommand(cnf *config.Config) *cobra.Command {
c.Stdout = cmd.OutOrStdout()
arguments := []string{"list", "--format=" + format}
if err := c.Exec(cmd.Context(), arguments...); err != nil {
handleLegacyError(err)
exitWithError(err)
}
return
}

result, err := formatter.Format(&list, config.FromContext(cmd.Context()))
if err != nil {
debugLog("%s\n", color.RedString(err.Error()))
os.Exit(1)
return
exitWithError(err)
}

fmt.Fprintln(cmd.OutOrStdout(), string(result))
Expand Down
66 changes: 38 additions & 28 deletions commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
Expand Down Expand Up @@ -51,11 +50,19 @@ func newRootCommand(cnf *config.Config, assets *vendorization.VendorAssets) *cob
DisableFlagParsing: false,
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
SilenceUsage: true,
SilenceErrors: true,
SilenceErrors: false,
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
if viper.GetBool("quiet") && !viper.GetBool("debug") && !viper.GetBool("verbose") {
viper.Set("no-interaction", true)
cmd.SetErr(io.Discard)
} else {
// Ensure the command's output writers can handle colors.
if cmd.OutOrStdout() == os.Stdout {
cmd.SetOut(color.Output)
}
if cmd.ErrOrStderr() == os.Stderr {
cmd.SetErr(color.Error)
}
}
if viper.GetBool("version") {
versionCommand.Run(cmd, []string{})
Expand All @@ -69,13 +76,16 @@ func newRootCommand(cnf *config.Config, assets *vendorization.VendorAssets) *cob
}
},
Run: func(cmd *cobra.Command, _ []string) {
runLegacyCLI(cmd.Context(), cnf, cmd.OutOrStdout(), cmd.ErrOrStderr(), cmd.InOrStdin(), os.Args[1:])
c := makeLegacyCLIWrapper(cnf, cmd.OutOrStdout(), cmd.ErrOrStderr(), cmd.InOrStdin())
if err := c.Exec(cmd.Context(), os.Args[1:]...); err != nil {
exitWithError(err)
}
},
PersistentPostRun: func(_ *cobra.Command, _ []string) {
checkShellConfigLeftovers(cnf)
PersistentPostRun: func(cmd *cobra.Command, _ []string) {
checkShellConfigLeftovers(cmd.ErrOrStderr(), cnf)
select {
case rel := <-updateMessageChan:
printUpdateMessage(rel, cnf)
printUpdateMessage(cmd.ErrOrStderr(), rel, cnf)
default:
}
},
Expand All @@ -96,7 +106,10 @@ func newRootCommand(cnf *config.Config, assets *vendorization.VendorAssets) *cob
args = []string{"help"}
}

runLegacyCLI(cmd.Context(), cnf, cmd.OutOrStdout(), cmd.ErrOrStderr(), cmd.InOrStdin(), args)
c := makeLegacyCLIWrapper(cnf, cmd.OutOrStdout(), cmd.ErrOrStderr(), cmd.InOrStdin())
if err := c.Exec(cmd.Context(), args...); err != nil {
exitWithError(err)
}
})

cmd.PersistentFlags().BoolP("version", "V", false, fmt.Sprintf("Displays the %s version", cnf.Application.Name))
Expand Down Expand Up @@ -142,7 +155,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(w io.Writer, cnf *config.Config) {
start := fmt.Sprintf("# BEGIN SNIPPET: %s configuration", cnf.Application.Name)
end := "# END SNIPPET"
shellConfigSnippet := regexp.MustCompile(regexp.QuoteMeta(start) + "(?s).+?" + regexp.QuoteMeta(end))
Expand All @@ -168,23 +181,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",
color.YellowString("Warning:"),
fmt.Fprintf(w, "%s Your %s file contains code that is no longer needed for the New %s\n",
color.YellowString("Notice:"),
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"))
fmt.Fprintf(w, "%s %s\n", color.YellowString("Please remove the following lines from:"), shellConfigFile)
fmt.Fprintf(w, "\t%s\n", strings.ReplaceAll(string(shellConfigSnippet.Find(shellConfig)), "\n", "\n\t"))
}
}
}

func printUpdateMessage(newRelease *internal.ReleaseInfo, cnf *config.Config) {
func printUpdateMessage(w io.Writer, newRelease *internal.ReleaseInfo, cnf *config.Config) {
if newRelease == nil {
return
}

fmt.Fprintf(color.Error, "\n\n%s %s → %s\n",
fmt.Fprintf(w, "\n\n%s %s → %s\n",
color.YellowString(fmt.Sprintf("A new release of the %s is available:", cnf.Application.Name)),
color.CyanString(version),
color.CyanString(newRelease.Version),
Expand All @@ -193,19 +206,19 @@ func printUpdateMessage(newRelease *internal.ReleaseInfo, cnf *config.Config) {
executable, err := os.Executable()
if err == nil && cnf.Wrapper.HomebrewTap != "" && isUnderHomebrew(executable) {
fmt.Fprintf(
color.Error,
w,
"To upgrade, run: brew update && brew upgrade %s\n",
color.YellowString(cnf.Wrapper.HomebrewTap),
)
} else if cnf.Wrapper.GitHubRepo != "" {
fmt.Fprintf(
color.Error,
w,
"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))
fmt.Fprintf(w, "%s\n\n", color.YellowString(newRelease.URL))
}

func isUnderHomebrew(binary string) bool {
Expand All @@ -228,17 +241,20 @@ func debugLog(format string, v ...any) {
return
}

log.Printf(format, v...)
prefix := color.New(color.ReverseVideo).Sprintf("DEBUG")
fmt.Fprintf(color.Error, prefix+" "+strings.TrimSpace(format)+"\n", v...)
}

func handleLegacyError(err error) {
func exitWithError(err error) {
var execErr *exec.ExitError
if errors.As(err, &execErr) {
exitCode := execErr.ExitCode()
debugLog("%s\n", err)
debugLog(err.Error())
os.Exit(exitCode)
}
log.Println(color.RedString(err.Error()))
if !viper.GetBool("quiet") {
fmt.Fprintln(color.Error, color.RedString(err.Error()))
}
os.Exit(1)
}

Expand All @@ -248,16 +264,10 @@ func makeLegacyCLIWrapper(cnf *config.Config, stdout, stderr io.Writer, stdin io
Version: version,
CustomPharPath: viper.GetString("phar-path"),
Debug: viper.GetBool("debug"),
DebugLogFunc: debugLog,
DisableInteraction: viper.GetBool("no-interaction"),
Stdout: stdout,
Stderr: stderr,
Stdin: stdin,
}
}

func runLegacyCLI(ctx context.Context, cnf *config.Config, stdout, stderr io.Writer, stdin io.Reader, args []string) {
c := makeLegacyCLIWrapper(cnf, stdout, stderr, stdin)
if err := c.Exec(ctx, args...); err != nil {
handleLegacyError(err)
}
}
19 changes: 9 additions & 10 deletions internal/legacy/legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"fmt"
"io"
"io/fs"
"log"
"os"
"os/exec"
"path/filepath"
Expand Down Expand Up @@ -42,11 +41,18 @@ type CLIWrapper struct {
Version string
CustomPharPath string
Debug bool
DebugLogFunc func(string, ...any)
DisableInteraction bool

_cacheDir string
}

func (c *CLIWrapper) debug(msg string, args ...any) {
if c.DebugLogFunc != nil {
c.DebugLogFunc(msg, args...)
}
}

func (c *CLIWrapper) cacheDir() (string, error) {
if c._cacheDir == "" {
cd, err := c.Config.TempDir()
Expand Down Expand Up @@ -75,7 +81,7 @@ func (c *CLIWrapper) init() error {
if err := fileLock.Lock(); err != nil {
return fmt.Errorf("could not acquire lock: %w", err)
}
c.debugLog("lock acquired (%s): %s", time.Since(preLock), fileLock.Path())
c.debug("lock acquired (%s): %s", time.Since(preLock), fileLock.Path())
defer fileLock.Unlock() //nolint:errcheck

g := errgroup.Group{}
Expand Down Expand Up @@ -108,7 +114,7 @@ func (c *CLIWrapper) Exec(ctx context.Context, args ...string) error {
if err := c.init(); err != nil {
return fmt.Errorf("failed to initialize PHP CLI: %w", err)
}
c.debugLog("initialized PHP CLI (%s)", time.Since(preInit))
c.debug("initialized PHP CLI (%s)", time.Since(preInit))

cacheDir, err := c.cacheDir()
if err != nil {
Expand Down Expand Up @@ -193,10 +199,3 @@ func (c *CLIWrapper) pharPath(cacheDir string) string {

return filepath.Join(cacheDir, pharBasename)
}

// debugLog logs a debugging message, if debug is enabled
func (c *CLIWrapper) debugLog(msg string, v ...any) {
if c.Debug {
log.Printf(msg, v...)
}
}

0 comments on commit f6c29b6

Please sign in to comment.