Skip to content
Merged
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
9 changes: 8 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ require (
github.com/alecthomas/assert v1.0.0
github.com/buildpacks/pack v0.30.0-pre1.0.20230418191058-1190b6c52128
github.com/c-bata/go-prompt v0.2.6
github.com/charmbracelet/bubbletea v0.24.1
github.com/chzyer/readline v1.5.1
github.com/containerd/console v1.0.3
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81
github.com/dnaeon/go-vcr v1.2.0
github.com/docker/docker v24.0.2+incompatible
github.com/dustin/go-humanize v1.0.1
Expand Down Expand Up @@ -70,6 +71,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sts v1.18.9 // indirect
github.com/aws/smithy-go v1.13.5 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20230322223720-077b4a917a90 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/buildpacks/imgutil v0.0.0-20230420161652-580610d0124b // indirect
github.com/buildpacks/lifecycle v0.17.0-pre.1 // indirect
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
Expand Down Expand Up @@ -115,6 +117,7 @@ require (
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-tty v0.0.4 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
Expand All @@ -124,6 +127,10 @@ require (
github.com/moby/sys/signal v0.7.0 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.1 // indirect
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
github.com/opencontainers/runc v1.1.6 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
Expand Down
20 changes: 18 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20230322223720-077b4a917a90 h1:GN8SzriwBUX5aagQft8cJ5sKUaXdHUdX8q2gS2mkom8=
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20230322223720-077b4a917a90/go.mod h1:V97RBAXo2x0elgRWnSfhWLDkwwEl7dNmmBRAvbwmEoA=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/buildpacks/imgutil v0.0.0-20230420161652-580610d0124b h1:TsOLD4J7TcHQ7aaEYTDd+nEMSku3vPqq6UIyOR8IG9Q=
github.com/buildpacks/imgutil v0.0.0-20230420161652-580610d0124b/go.mod h1:hgxVR7UpPvT5gATbRGM582oy048sUocDg6R6PMWAxow=
Expand All @@ -154,6 +156,8 @@ github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbletea v0.24.1 h1:LpdYfnu+Qc6XtvMz6d/6rRY71yttHTP5HtrjMgWvixc=
github.com/charmbracelet/bubbletea v0.24.1/go.mod h1:rK3g/2+T8vOSEkNHvtq40umJpeVYDn6bLaqbgzhL/hg=
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 h1:krfRl01rzPzxSxyLyrChD+U+MzsBXbm0OwYYB67uF+4=
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589/go.mod h1:OuDyvmLnMCwa2ep4Jkm6nyA0ocJuZlGyk2gGseVzERM=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
Expand All @@ -177,8 +181,8 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg=
github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc=
github.com/containerd/continuity v0.3.1-0.20230206214859-2a963a2f56e8 h1:EdSQb65ohzz4jsyPOhxfu3/+c9nnU0euk0otferwl9A=
Expand Down Expand Up @@ -412,9 +416,12 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
Expand All @@ -441,6 +448,14 @@ github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbD
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
Expand Down Expand Up @@ -471,6 +486,7 @@ github.com/quantumsheep/pack v0.30.0-pre1.0.20230505095233-37158bb223a4 h1:1NPuV
github.com/quantumsheep/pack v0.30.0-pre1.0.20230505095233-37158bb223a4/go.mod h1:JFPC0K0VzwZVW78VPN/CDD34lhTIpdAlR/zu+WhpnnM=
github.com/rivo/tview v0.0.0-20230406072732-e22ce9588bb4 h1:zX+lRcFRPX1jn8A11jxT0dEQhkmUM7pec+9NLK8MiTQ=
github.com/rivo/tview v0.0.0-20230406072732-e22ce9588bb4/go.mod h1:nVwGv4MP47T0jvlk7KuTTjjuSmrGO4JF0iaiNt4bufE=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
Expand Down
8 changes: 7 additions & 1 deletion internal/core/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,10 @@ type GoldenReplacement struct {
// This is the format for repl in (*regexp.Regexp).ReplaceAll
// You can use $ to represent groups $1, $2...
Replacement string

// OptionalMatch allow the golden to not contain the given patterns
// if false, the golden must contain the given pattern
OptionalMatch bool
}

// goldenReplacePatterns replace the list of patterns with their given replacement
Expand All @@ -608,7 +612,9 @@ func goldenReplacePatterns(golden string, replacements ...GoldenReplacement) (st

for _, replacement := range replacements {
if !replacement.Pattern.MatchString(changedGolden) {
matchFailed = append(matchFailed, replacement.Pattern.String())
if !replacement.OptionalMatch {
matchFailed = append(matchFailed, replacement.Pattern.String())
}
continue
}
changedGolden = replacement.Pattern.ReplaceAllString(changedGolden, replacement.Replacement)
Expand Down
94 changes: 94 additions & 0 deletions internal/interactive/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//go:build !wasm

package interactive

import (
"context"
"fmt"
"os"

tea "github.com/charmbracelet/bubbletea"
)

type ListPrompt struct {
// Prompt that will be printed when showing the list
Prompt string
Choices []string
// DefaultIndex is the element that will be selected when starting prompt
DefaultIndex int

cursor int
cancelled bool
}

func (m *ListPrompt) Init() tea.Cmd {
return nil
}

func (m *ListPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
// Key is pressed
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
m.cancelled = true
return m, tea.Quit
case "up", "k":
if m.cursor > 0 {
m.cursor--
}
case "down", "j":
if m.cursor < len(m.Choices)-1 {
m.cursor++
}
case "enter", " ":
return m, tea.Quit
}
}

return m, nil
}

func (m *ListPrompt) View() string {
s := m.Prompt + "\n\n"

for i, choice := range m.Choices {
if m.cursor == i {
s += fmt.Sprintf("> %s\n", choice)
} else {
s += fmt.Sprintf("%s\n", choice)
}
}

s += "\nPress enter or space for select.\n"

return s
}

// Execute start the prompt and return the selected index
func (m *ListPrompt) Execute(ctx context.Context) (int, error) {
m.cursor = m.DefaultIndex

opts := []tea.ProgramOption{
tea.WithContext(ctx),
}

if hasMockedResponse(ctx) {
opts = append(opts, tea.WithInput(&mockResponseReader{
ctx: ctx,
defaultReader: os.Stdin,
}))
}

p := tea.NewProgram(m, opts...)
_, err := p.Run()
if err != nil {
return -1, fmt.Errorf("error running prompt: %w", err)
}

if m.cancelled {
return -1, fmt.Errorf("prompt cancelled")
}

return m.cursor, nil
}
18 changes: 18 additions & 0 deletions internal/interactive/list_disabled.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//go:build wasm

package interactive

import (
"context"
"fmt"
)

type ListPrompt struct {
Prompt string
Choices []string
DefaultIndex int
}

func (m *ListPrompt) Execute(ctx context.Context) (int, error) {
return -1, fmt.Errorf("not implemented for current platform")
}
4 changes: 4 additions & 0 deletions internal/interactive/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ func popMockResponseFromContext(ctx context.Context) (string, bool) {
return str, true
}

func hasMockedResponse(ctx context.Context) bool {
return ctx.Value(contextKey) != nil
}

type mockResponseReader struct {
ctx context.Context
defaultReader io.ReadCloser
Expand Down
5 changes: 1 addition & 4 deletions internal/namespaces/init/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,7 @@ Default path for configuration file is based on the following priority order:

if args.ProjectID == "" {
args.ProjectID = getAPIKeyDefaultProjectID(ctx, args.AccessKey, args.SecretKey)
}

if args.ProjectID == "" {
args.ProjectID, err = promptProjectID(ctx)
args.ProjectID, err = promptProjectID(ctx, args.AccessKey, args.SecretKey, args.OrganizationID, args.ProjectID)
if err != nil {
return nil, err
}
Expand Down
46 changes: 46 additions & 0 deletions internal/namespaces/init/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package init
import (
"fmt"
"path"
"regexp"
"testing"

"github.com/alecthomas/assert"
Expand Down Expand Up @@ -198,3 +199,48 @@ func TestInit(t *testing.T) {
}))
})
}

func TestInit_Prompt(t *testing.T) {
promptResponse := []string{
"secret-key",
"access-key",
"organization-id",
" ",
}

t.Run("Simple", core.Test(&core.TestConfig{
Commands: GetCommands(),
BeforeFunc: core.BeforeFuncCombine(
baseBeforeFunc(),
func(ctx *core.BeforeFuncCtx) error {
promptResponse[0] = ctx.Meta["SecretKey"].(string)
promptResponse[1] = ctx.Meta["AccessKey"].(string)
promptResponse[2] = ctx.Meta["OrganizationID"].(string)

return nil
}),
TmpHomeDir: true,
Cmd: "scw init",
Check: core.TestCheckCombine(
core.TestCheckGoldenAndReplacePatterns(
core.GoldenReplacement{
Pattern: regexp.MustCompile("\\s\\sExcept for autocomplete: unsupported OS 'windows'\n"),
Replacement: "",
OptionalMatch: true,
},
core.GoldenReplacement{
Pattern: regexp.MustCompile(`Except for autocomplete: unsupported OS 'windows'\\n`),
Replacement: "",
OptionalMatch: true,
},
),
checkConfig(func(t *testing.T, ctx *core.CheckFuncCtx, config *scw.Config) {
secretKey, _ := ctx.Client.GetSecretKey()
assert.Equal(t, secretKey, *config.SecretKey)
assert.NotEmpty(t, *config.DefaultProjectID)
assert.Equal(t, *config.DefaultProjectID, *config.DefaultProjectID)
}),
),
PromptResponseMocks: promptResponse,
}))
}
46 changes: 35 additions & 11 deletions internal/namespaces/init/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (
"fmt"

"github.com/fatih/color"
"github.com/scaleway/scaleway-cli/v2/internal/core"
"github.com/scaleway/scaleway-cli/v2/internal/interactive"
"github.com/scaleway/scaleway-cli/v2/internal/terminal"
"github.com/scaleway/scaleway-sdk-go/api/account/v2"
"github.com/scaleway/scaleway-sdk-go/logger"
"github.com/scaleway/scaleway-sdk-go/scw"
"github.com/scaleway/scaleway-sdk-go/validation"
Expand All @@ -26,18 +28,40 @@ func promptOrganizationID(ctx context.Context) (string, error) {
})
}

func promptProjectID(ctx context.Context) (string, error) {
func promptProjectID(ctx context.Context, accessKey string, secretKey string, organizationID string, defaultProjectID string) (string, error) {
client := core.ExtractClient(ctx)
api := account.NewAPI(client)

res, err := api.ListProjects(&account.ListProjectsRequest{
OrganizationID: organizationID,
}, scw.WithAllPages(), scw.WithContext(ctx), scw.WithAuthRequest(accessKey, secretKey))
if err != nil {
return "", fmt.Errorf("failed to list projects: %w", err)
}

defaultIndex := 0

projects := make([]string, len(res.Projects))
for i := range res.Projects {
if res.Projects[i].ID == defaultProjectID {
defaultIndex = i
}
projects[i] = fmt.Sprintf("%s (%s)", res.Projects[i].Name, res.Projects[i].ID)
}

prompt := interactive.ListPrompt{
Prompt: "Choose your default project ID",
Choices: projects,
DefaultIndex: defaultIndex,
}

_, _ = interactive.Println()
return interactive.PromptStringWithConfig(&interactive.PromptStringConfig{
Ctx: ctx,
Prompt: "Default project ID",
ValidateFunc: func(s string) error {
if !validation.IsUUID(s) {
return fmt.Errorf("given project ID is not a valid UUID")
}
return nil
},
})
index, err := prompt.Execute(ctx)
if err != nil {
return "", err
}

return res.Projects[index].ID, nil
}

func promptTelemetry(ctx context.Context) (*bool, error) {
Expand Down
Loading