Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/digitalocean/doctl into act…
Browse files Browse the repository at this point in the history
…ivation-logs
  • Loading branch information
ddebarros committed Oct 24, 2022
2 parents 37bf62a + 242b333 commit 1b9ce32
Show file tree
Hide file tree
Showing 176 changed files with 5,081 additions and 2,702 deletions.
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: gomod
directory: "/"
vendor: true
schedule:
interval: weekly
open-pull-requests-limit: 10
1 change: 1 addition & 0 deletions commands/activations.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ logs.`,
`Use `+"`"+`doctl serverless activations list`+"`"+` to list the activation records that are present in the cloud for previously
invoked functions.`,
Writer,
aliasOpt("ls"),
displayerType(&displayers.Activation{}),
)
AddIntFlag(list, "limit", "l", 30, "only return LIMIT number of activations (default 30, max 200)")
Expand Down
6 changes: 3 additions & 3 deletions commands/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ Only basic information is included with the text output format. For complete app
This permanently deletes the app and all its associated deployments.`,
Writer,
aliasOpt("d"),
aliasOpt("d", "rm"),
)
AddBoolFlag(deleteApp, doctl.ArgForce, doctl.ArgShortForce, false, "Delete the App without a confirmation prompt")

Expand Down Expand Up @@ -787,7 +787,7 @@ func appsTier() *Command {
},
}

CmdBuilder(cmd, RunAppsTierList, "list", "List all app tiers", `Use this command to list all the available app tiers.`, Writer)
CmdBuilder(cmd, RunAppsTierList, "list", "List all app tiers", `Use this command to list all the available app tiers.`, Writer, aliasOpt("ls"))
CmdBuilder(cmd, RunAppsTierGet, "get <tier slug>", "Retrieve an app tier", `Use this command to retrieve information about a specific app tier.`, Writer)

cmd.AddCommand(appsTierInstanceSize())
Expand Down Expand Up @@ -830,7 +830,7 @@ func appsTierInstanceSize() *Command {
},
}

CmdBuilder(cmd, RunAppsTierInstanceSizeList, "list", "List all app instance sizes", `Use this command to list all the available app instance sizes.`, Writer)
CmdBuilder(cmd, RunAppsTierInstanceSizeList, "list", "List all app instance sizes", `Use this command to list all the available app instance sizes.`, Writer, aliasOpt("ls"))
CmdBuilder(cmd, RunAppsTierInstanceSizeGet, "get <instance size slug>", "Retrieve an app instance size", `Use this command to retrieve information about a specific app instance size.`, Writer)

return cmd
Expand Down
7 changes: 7 additions & 0 deletions commands/apps_charm.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/digitalocean/doctl/commands/charm"
"github.com/digitalocean/doctl/commands/charm/template"
"github.com/digitalocean/doctl/internal/apps/builder"
"github.com/digitalocean/godo"
)

Expand All @@ -22,6 +23,12 @@ func (i componentListItem) Description() string {
}

if buildable, ok := i.spec.(godo.AppBuildableComponentSpec); ok {
if builder.IsDockerBuild(buildable) {
desc[0] += " [dockerfile]"
} else if builder.IsCNBBuild(buildable) {
desc[0] += " [buildpacks]"
}

if sourceDir := buildable.GetSourceDir(); sourceDir != "" {
desc = append(desc, template.String(`located in ./{{highlight .}}`, sourceDir))
}
Expand Down
20 changes: 12 additions & 8 deletions commands/apps_dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ import (

const (
// AppsDevDefaultEnvFile is the default env file path.
AppsDevDefaultEnvFile = ".env"
AppsDevDefaultEnvFile = ".env"
appDevConfigFileNamespace = "dev"
)

// AppsDev creates the apps dev command subtree.
Expand Down Expand Up @@ -64,7 +65,7 @@ func AppsDev() *Command {
"Build an app component",
heredoc.Docf(`
[BETA] Build an app component locally.
The component name is optional unless running non-interactively.
All command line flags as optional. You may specify flags to be applied to the current build
Expand Down Expand Up @@ -220,7 +221,7 @@ func RunAppsDevBuild(c *CmdConfig) error {
{{warning (print crossmark " functions builds are coming soon!")}}
please use {{highlight "doctl serverless deploy"}} to build functions in the meantime.
`), nil)
return fmt.Errorf("not supported")
}
Expand Down Expand Up @@ -316,7 +317,7 @@ func RunAppsDevBuild(c *CmdConfig) error {
`building {{lower (snakeToTitle .componentSpec.GetType)}} {{highlight .componentSpec.GetName}} {{muted (print "(" .appName ")")}}`,
map[string]any{
"componentSpec": componentSpec,
"appName": ws.Config.AppName,
"appName": ws.Config.AppSpec.GetName(),
},
)
template.Print(`{{success checkmark}} {{.}}{{nl 2}}`, buildingComponentLine)
Expand Down Expand Up @@ -438,10 +439,10 @@ func RunAppsDevBuild(c *CmdConfig) error {
tmpl := `
{{success checkmark}} successfully built {{success .component}} in {{highlight (duration .dur)}}
{{success checkmark}} created container image {{success .img}}
{{pointerRight}} push your image to a container registry using {{highlight "docker push"}}
{{pointerRight}} or run it locally using {{highlight "docker run"}}; for example:
{{muted promptPrefix}} {{highlight (printf "docker run %s--rm %s%s" .port_env .port_arg .img)}}`

if _, ok := componentSpec.(godo.AppRoutableComponentSpec); ok {
Expand Down Expand Up @@ -481,7 +482,10 @@ func fileExists(path ...string) bool {
}

func appDevWorkspace(cmdConfig *CmdConfig) (*workspace.AppDev, error) {
devConfigFilePath, err := cmdConfig.Doit.GetString(cmdConfig.NS, doctl.ArgAppDevConfig)
// The setting is nested under the "dev" namespace, i.e. dev.config.set.dev-config
// This is needed to prevent a conflict with the base config setting.
ns := fmt.Sprintf("%s.%s", appDevConfigFileNamespace, cmdConfig.NS)
devConfigFilePath, err := cmdConfig.Doit.GetString(ns, doctl.ArgAppDevConfig)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -602,7 +606,7 @@ func appsDevBuildSpecRequired(ws *workspace.AppDev, appsService do.AppsService)
template.Print(heredoc.Doc(`
{{error (print crossmark " no app spec found.")}}
an app spec is required to start a local build. make sure doctl is run in the correct directory where your app code is.
`,
), nil)

Expand Down
2 changes: 1 addition & 1 deletion commands/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func Auth() *Command {
If you work with a just one account, you can call ` + "`" + `doctl auth init` + "`" + ` and supply the token when prompted. This creates an authentication context named ` + "`" + `default` + "`" + `.
To switch between multiple DigitalOcean accounts, including team accounts, you can create named contexts by using ` + "`" + `doctl auth init --context <name>` + "`" + `, then providing a token when prompted. This saves the token under the name you provide. To switch between accounts, use ` + "`" + `doctl auth switch --context <name>` + "`" + `.,
To switch between multiple DigitalOcean accounts, including team accounts, you can create named contexts by using ` + "`" + `doctl auth init --context <name>` + "`" + `, then providing a token when prompted. This saves the token under the name you provide. To switch between accounts, use ` + "`" + `doctl auth switch --context <name>` + "`" + `.
To remove accounts from the configuration file, you can run ` + "`" + `doctl auth remove --context <name>` + "`" + `. This removes the token under the name you provide.`,
},
Expand Down
49 changes: 49 additions & 0 deletions commands/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ limitations under the License.
package commands

import (
"bufio"
"bytes"
"io"
"io/ioutil"
"path/filepath"
"testing"

"errors"
Expand All @@ -25,6 +27,7 @@ import (
"github.com/digitalocean/doctl/do"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
yaml "gopkg.in/yaml.v2"
)

func TestAuthCommand(t *testing.T) {
Expand Down Expand Up @@ -54,6 +57,50 @@ func TestAuthInit(t *testing.T) {
})
}

func TestAuthInitConfig(t *testing.T) {
cfw := cfgFileWriter
viper.Set(doctl.ArgAccessToken, nil)
defer func() {
cfgFileWriter = cfw
}()

retrieveUserTokenFunc := func() (string, error) {
return "valid-token", nil
}

var buf bytes.Buffer
cfgFileWriter = func() (io.WriteCloser, error) {
return &nopWriteCloser{
Writer: bufio.NewWriter(&buf),
}, nil
}

withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
tm.account.EXPECT().Get().Return(&do.Account{}, nil)

err := RunAuthInit(retrieveUserTokenFunc)(config)
assert.NoError(t, err)

var configFile testConfig
err = yaml.Unmarshal(buf.Bytes(), &configFile)
assert.NoError(t, err)
defaultCfgFile := filepath.Join(defaultConfigHome(), defaultConfigName)
assert.Equal(t, configFile["config"], defaultCfgFile, "unexpected setting for 'config'")

// Ensure that the dev.config.set.dev-config setting is correct to prevent
// a conflict with the base config setting.
devConfig := configFile["dev"]
devConfigSetting := devConfig.(map[interface{}]interface{})["config"]
expectedConfigSetting := map[interface{}]interface{}(
map[interface{}]interface{}{
"set": map[interface{}]interface{}{"dev-config": ""},
"unset": map[interface{}]interface{}{"dev-config": ""},
},
)
assert.Equal(t, devConfigSetting, expectedConfigSetting, "unexpected setting for 'dev.config'")
})
}

func TestAuthInitWithProvidedToken(t *testing.T) {
cfw := cfgFileWriter
viper.Set(doctl.ArgAccessToken, "valid-token")
Expand Down Expand Up @@ -141,6 +188,8 @@ func Test_displayAuthContexts(t *testing.T) {
}
}

type testConfig map[string]interface{}

type nopWriteCloser struct {
io.Writer
}
Expand Down
27 changes: 27 additions & 0 deletions commands/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ limitations under the License.
package commands

import (
"github.com/spf13/cobra"
"io/ioutil"
"sort"
"testing"
Expand Down Expand Up @@ -285,3 +286,29 @@ func withTestClient(t *testing.T, tFn testFn) {

tFn(config, tm)
}

func assertCommandAliases(t *testing.T, cmd *cobra.Command) {
for _, c := range cmd.Commands() {
if c.Name() == "list" {
assert.Contains(t, c.Aliases, "ls", "Missing 'ls' alias for 'list' command.")
}
if c.Name() == "delete" {
assert.Contains(t, c.Aliases, "rm", "Missing 'rm' alias for 'delete' command.")
}
}
}

func recurseCommand(t *testing.T, cmd *cobra.Command) {
t.Run(cmd.Name(), func(t *testing.T) {
assertCommandAliases(t, cmd)
})
for _, c := range cmd.Commands() {
recurseCommand(t, c)
}
}

func TestCommandAliases(t *testing.T) {
for _, cmd := range DoitCmd.Commands() {
recurseCommand(t, cmd)
}
}
42 changes: 13 additions & 29 deletions commands/confirmation.go
Original file line number Diff line number Diff line change
@@ -1,43 +1,27 @@
package commands

import (
"bufio"
"fmt"
"io"
"os"
"strings"
)

// retrieveUserInput is a function that can retrieve user input in form of string. By default,
// it will prompt the user. In test, you can replace this with code that returns the appropriate response.
var retrieveUserInput = func(message string) (string, error) {
return readUserInput(os.Stdin, message)
}

// readUserInput is similar to retrieveUserInput but takes an explicit
// io.Reader to read user input from. It is meant to allow simplified testing
// as to-be-read inputs can be injected conveniently.
func readUserInput(in io.Reader, message string) (string, error) {
reader := bufio.NewReader(in)
warnConfirm("Are you sure you want to " + message + " (y/N) ? ")
answer, err := reader.ReadString('\n')
if err != nil {
return "", err
}

answer = strings.TrimRight(answer, "\r\n")

return strings.ToLower(answer), nil
}
"github.com/digitalocean/doctl/commands/charm/confirm"
"github.com/digitalocean/doctl/commands/charm/template"
)

// AskForConfirm parses and verifies user input for confirmation.
func AskForConfirm(message string) error {
answer, err := retrieveUserInput(message)
if !Interactive {
warn("Requires confirmation. Use the `--force` flag to continue without confirmation.")
return ErrExitSilently
}
choice, err := confirm.New(
template.String("Are you sure you want to {{.}}", message),
confirm.WithDefaultChoice(confirm.No),
).Prompt()
if err != nil {
return fmt.Errorf("Unable to parse users input: %s", err)
return err
}

if answer != "y" && answer != "ye" && answer != "yes" {
if choice != confirm.Yes {
return fmt.Errorf("Invalid user input")
}

Expand Down
Loading

0 comments on commit 1b9ce32

Please sign in to comment.