Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ Environment variables include:
- `UPSUN_CLI_TOKEN`: an API token, for non-interactive use such as CI. An API
token can act as the account that created it, so use a separate machine
account to limit its access. Interactively, prefer `upsun auth:api-token-login`.
- `UPSUN_CLI_DEBUG=1`: enable debug output. This can print HTTP request details,
- `UPSUN_CLI_DEBUG=1`: enable debug output, like the `--debug` flag. This can print HTTP request details,
including access tokens.
- `UPSUN_CLI_DEFAULT_TIMEOUT`: the timeout in seconds for most API requests
(default 30).
Expand Down
5 changes: 5 additions & 0 deletions commands/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package commands

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

"github.com/upsun/cli/internal/config"
"github.com/upsun/cli/internal/legacy"
Expand All @@ -13,6 +14,10 @@ func newHelpCommand(cnf *config.Config) *cobra.Command {
// Disable flag parsing so flags like --format are preserved for the legacy CLI.
DisableFlagParsing: true,
Run: func(cmd *cobra.Command, args []string) {
args, debug := stripDebugFlag(args)
if debug != nil {
viper.Set("debug", *debug)
}
loadLegacyCmds := enabledLegacyCommands(cnf, legacy.Commands)
if expanded, ok, err := expandAbbreviation(cmd.Root(), loadLegacyCmds, args); err != nil {
debugLogf("Failed to load the legacy command index: %s", err)
Expand Down
29 changes: 28 additions & 1 deletion commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"path/filepath"
"regexp"
"slices"
"strconv"
"strings"

"github.com/fatih/color"
Expand Down Expand Up @@ -107,7 +108,8 @@ func newRootCommand(cnf *config.Config, assets *vendorization.VendorAssets) *cob
},
Run: func(cmd *cobra.Command, _ []string) {
c := makeLegacyCLIWrapper(cnf, cmd.OutOrStdout(), cmd.ErrOrStderr(), cmd.InOrStdin())
if err := c.Exec(cmd.Context(), os.Args[1:]...); err != nil {
args, _ := stripDebugFlag(os.Args[1:])
if err := c.Exec(cmd.Context(), args...); err != nil {
exitWithError(err)
}
},
Expand All @@ -127,6 +129,7 @@ func newRootCommand(cnf *config.Config, assets *vendorization.VendorAssets) *cob
}

// Others will be passed to the legacy CLI's help command.
args, _ = stripDebugFlag(args)
if !slices.Contains(args, "--help") && !slices.Contains(args, "-h") {
args = append([]string{"help"}, args...)
}
Expand Down Expand Up @@ -294,6 +297,29 @@ func debugLogf(format string, v ...any) {
fmt.Fprintf(color.Error, prefix+" "+strings.TrimSpace(format)+"\n", v...)
}

// stripDebugFlag removes the --debug flag, which the legacy CLI does not accept, from args before any "--"
// separator. It returns the flag's value, or nil if it was not given.
func stripDebugFlag(args []string) (stripped []string, debug *bool) {
stripped = make([]string, 0, len(args))
for i, arg := range args {
if arg == "--" {
return append(stripped, args[i:]...), debug
}
if arg == "--debug" {
debug = new(true)
continue
}
if v, ok := strings.CutPrefix(arg, "--debug="); ok {
if b, err := strconv.ParseBool(v); err == nil {
debug = new(b)
continue
}
}
stripped = append(stripped, arg)
}
return stripped, debug
}

func exitWithError(err error) {
var execErr *exec.ExitError
if errors.As(err, &execErr) {
Expand All @@ -311,6 +337,7 @@ func makeLegacyCLIWrapper(cnf *config.Config, stdout, stderr io.Writer, stdin io
return &legacy.CLIWrapper{
Config: cnf,
Version: config.Version,
Debug: viper.GetBool("debug"),
DebugLogFunc: debugLogf,
DisableInteraction: viper.GetBool("no-interaction"),
Stdout: stdout,
Expand Down
25 changes: 25 additions & 0 deletions commands/root_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package commands

import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -52,3 +53,27 @@ func TestShellQuote(t *testing.T) {
assert.Equal(t, c.want, shellQuote(c.in))
}
}

func TestStripDebugFlag(t *testing.T) {
cases := []struct {
args []string
want []string
wantDebug *bool
}{
{[]string{"cc"}, []string{"cc"}, nil},
{[]string{"cc", "--debug"}, []string{"cc"}, new(true)},
{[]string{"--debug", "cc", "-v"}, []string{"cc", "-v"}, new(true)},
{[]string{"cc", "--debug=1"}, []string{"cc"}, new(true)},
{[]string{"cc", "--debug=false"}, []string{"cc"}, new(false)},
{[]string{"cc", "--debug=foo"}, []string{"cc", "--debug=foo"}, nil},
{[]string{"ssh", "--", "cmd", "--debug"}, []string{"ssh", "--", "cmd", "--debug"}, nil},
{[]string{"ssh", "--debug", "--", "--debug"}, []string{"ssh", "--", "--debug"}, new(true)},
}
for _, c := range cases {
t.Run(strings.Join(c.args, " "), func(t *testing.T) {
got, debug := stripDebugFlag(c.args)
assert.Equal(t, c.want, got)
assert.Equal(t, c.wantDebug, debug)
})
}
}
39 changes: 39 additions & 0 deletions integration-tests/debug_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package tests

import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestDebugFlag checks that --debug enables debug logging and is not forwarded to the legacy CLI.
func TestDebugFlag(t *testing.T) {
f := newCommandFactory(t, "", "")

cases := []struct {
args []string
expected string
}{
{[]string{"cc", "--debug"}, ""},
{[]string{"--debug", "cc"}, ""},
{[]string{"cc", "--debug", "--help"}, "Command: clear-cache"},
{[]string{"--help", "--debug", "cc"}, "Command: clear-cache"},
{[]string{"help", "cc", "--debug"}, "Command: clear-cache"},
{[]string{"help", "--debug", "cc"}, "Command: clear-cache"},
{[]string{"--debug", "p:init", "--help"}, "Command: project:init"},
}
for _, c := range cases {
t.Run(strings.Join(c.args, " "), func(t *testing.T) {
stdOut, stdErr, err := f.RunCombinedOutput(c.args...)
require.NoError(t, err, stdErr)
assert.NotContains(t, stdErr, `"--debug" option does not exist`)
assert.Contains(t, stdOut, c.expected)
if c.expected != "Command: project:init" {
// Logged by the Go wrapper when it starts the legacy CLI.
assert.Contains(t, stdErr, "Initialized PHP CLI")
}
})
}
}
6 changes: 6 additions & 0 deletions internal/legacy/legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ func (c *CLIWrapper) Exec(ctx context.Context, args ...string) error {
if c.DisableInteraction {
cmd.Env = append(cmd.Env, envPrefix+"NO_INTERACTION=1")
}
// Override any inherited value, so that --debug=false also applies to the legacy CLI.
if c.Debug {
cmd.Env = append(cmd.Env, envPrefix+"DEBUG=1")
} else {
cmd.Env = append(cmd.Env, envPrefix+"DEBUG=0")
}
if c.ForceColor {
cmd.Env = append(cmd.Env, "CLICOLOR_FORCE=1")
}
Expand Down
Loading