Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ USAGE:

ARGS:
tag-id UUID of the tag
[timeout] Maximum time to handle the request
[region=fr-par] Region to target. If none is passed will use default region from the config (fr-par | nl-ams | pl-waw)

DEPRECATED ARGS:
Expand Down
9 changes: 5 additions & 4 deletions core/cobra_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"reflect"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -84,16 +85,16 @@ func testGetCommands() *core.Commands {
ArgsType: reflect.TypeOf(args.RawArgs{}),
AllowAnonymousClient: true,
Run: func(_ context.Context, argsI any) (i any, e error) {
res := ""
rawArgs := *argsI.(*args.RawArgs)
res := make([]string, 0, len(rawArgs))
for i, arg := range rawArgs {
res += arg
res = append(res, arg)
if i != len(rawArgs)-1 {
res += " "
res = append(res, " ")
}
}

return res, nil
return strings.Join(res, ""), nil
},
},
&core.Command{
Expand Down
8 changes: 8 additions & 0 deletions core/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,14 @@ func ExecBeforeCmdArgs(args []string) BeforeFunc {
}
}

// ExecBeforeCmdWithResult executes the given command and returns its result.
func ExecBeforeCmdWithResult(ctx *BeforeFuncCtx, cmd string) any {
args := cmdToArgs(ctx.Meta, cmd)
ctx.Logger.Debugf("ExecBeforeCmd: args=%s\n", args)

return ctx.ExecuteCmd(args)
}

// ExecAfterCmd executes the given before command.
func ExecAfterCmd(cmd string) AfterFunc {
return func(ctx *AfterFuncCtx) error {
Expand Down
1 change: 1 addition & 0 deletions docs/commands/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ scw registry tag delete <tag-id ...> [arg=value ...]
| Name | | Description |
|------|---|-------------|
| tag-id | Required | UUID of the tag |
| timeout | | Maximum time to handle the request |
| ~~force~~ | Deprecated | If two tags share the same digest the deletion will fail unless this parameter is set to true (deprecated) |
| region | Default: `fr-par`<br />One of: `fr-par`, `nl-ams`, `pl-waw` | Region to target. If none is passed will use default region from the config |

Expand Down
3 changes: 0 additions & 3 deletions internal/gotty/resize_windows.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
//go:build windows
// +build windows

package gotty

func subscribeToResize(resizeChan chan bool) func() {
Expand Down
8 changes: 5 additions & 3 deletions internal/interactive/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"os"
"strings"

tea "github.com/charmbracelet/bubbletea"
)
Expand Down Expand Up @@ -53,15 +54,16 @@ func (m *ListPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

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

choices := make([]string, 0, len(m.Choices))
for i, choice := range m.Choices {
if m.cursor == i {
s += fmt.Sprintf("> %s\n", choice)
choices = append(choices, fmt.Sprintf("> %s\n", choice))
} else {
s += choice + "\n"
choices = append(choices, choice+"\n")
}
}

s += strings.Join(choices, "")
s += "\nPress enter or space for select.\n"

return s
Expand Down
6 changes: 3 additions & 3 deletions internal/interactive/print.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ func RemoveIndent(str string) string {
}

func makeStr(char string, length int) string {
str := ""
chars := make([]string, 0, length)
for range length {
str += char
chars = append(chars, char)
}

return str
return strings.Join(chars, "")
}
6 changes: 3 additions & 3 deletions internal/namespaces/autocomplete/autocomplete.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,14 +450,14 @@ func autocompleteScriptCommand() *core.Command {

func TrimText(str string) string {
foundFirstNonEmptyLine := false
strToRemove := ""
toRemove := []string(nil)
lines := strings.Split(str, "\n")
for i, line := range lines {
if !foundFirstNonEmptyLine {
if len(line) > 0 {
for _, c := range line {
if c == ' ' || c == '\t' {
strToRemove += string(c)
toRemove = append(toRemove, string(c))

continue
}
Expand All @@ -467,7 +467,7 @@ func TrimText(str string) string {
foundFirstNonEmptyLine = true
}
}
for _, c := range strToRemove {
for _, c := range strings.Join(toRemove, "") {
lines[i] = strings.Replace(lines[i], string(c), "", 1)
}
}
Expand Down
8 changes: 5 additions & 3 deletions internal/namespaces/init/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"path"
"regexp"
"strings"
"testing"

"github.com/scaleway/scaleway-cli/v2/core"
Expand All @@ -26,12 +27,13 @@ func checkConfig(
}

func appendArgs(prefix string, args map[string]string) string {
cmd := prefix
cmd := make([]string, 1, len(args)+1)
cmd[0] = prefix
for k, v := range args {
cmd += fmt.Sprintf(" %s=%s", k, v)
cmd = append(cmd, fmt.Sprintf("%s=%s", k, v))
}

return cmd
return strings.Join(cmd, " ")
}

func beforeFuncSaveConfig(config *scw.Config) core.BeforeFunc {
Expand Down
10 changes: 7 additions & 3 deletions internal/namespaces/instance/v1/custom_security_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,12 +314,16 @@ func securityGroupDeleteBuilder(c *core.Command) *core.Command {
}

// Create detail message.
hint := "Attach all these instances to another security-group before deleting this one:"
hint := make([]string, 1, len(sg.SecurityGroup.Servers)+1)
hint[0] = "Attach all these instances to another security-group before deleting this one:"
for _, s := range sg.SecurityGroup.Servers {
hint += "\nscw instance server update " + s.ID + " security-group.id=$NEW_SECURITY_GROUP_ID"
hint = append(
hint,
"scw instance server update "+s.ID+" security-group.id=$NEW_SECURITY_GROUP_ID",
)
}

newError.Hint = hint
newError.Hint = strings.Join(hint, "\n")

return nil, newError
}
Expand Down
1 change: 1 addition & 0 deletions internal/namespaces/registry/v1/custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func GetCommands() *core.Commands {
registryInstallDockerHelperCommand(),
))

cmds.MustFind("registry", "tag", "delete").Override(tagDeleteBuilder)
cmds.MustFind("registry", "tag", "get").Override(tagGetBuilder)
cmds.MustFind("registry", "tag", "list").Override(tagListBuilder)

Expand Down
48 changes: 44 additions & 4 deletions internal/namespaces/registry/v1/custom_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ package registry
import (
"context"
"fmt"
"reflect"
"time"

"github.com/fatih/color"
"github.com/scaleway/scaleway-cli/v2/core"
"github.com/scaleway/scaleway-cli/v2/core/human"
"github.com/scaleway/scaleway-sdk-go/api/registry/v1"
"github.com/scaleway/scaleway-sdk-go/logger"
"github.com/scaleway/scaleway-sdk-go/scw"
)

//
Expand All @@ -25,7 +28,7 @@ var (
}
)

type customTag struct {
type CustomTag struct {
registry.Tag
FullName string
}
Expand Down Expand Up @@ -59,7 +62,7 @@ func tagGetBuilder(c *core.Command) *core.Command {
return getTagResp, nil
}

res := customTag{
res := CustomTag{
Tag: *tag,
FullName: fmt.Sprintf("%s/%s:%s", namespace.Endpoint, image.Name, tag.Name),
}
Expand Down Expand Up @@ -112,9 +115,9 @@ func tagListBuilder(c *core.Command) *core.Command {
return listTagResp, err
}

var customRes []customTag
var customRes []CustomTag
for _, tag := range listTagResp.([]*registry.Tag) {
customRes = append(customRes, customTag{
customRes = append(customRes, CustomTag{
Tag: *tag,
FullName: fmt.Sprintf("%s/%s:%s",
namespace.Endpoint,
Expand All @@ -129,3 +132,40 @@ func tagListBuilder(c *core.Command) *core.Command {

return c
}

type customTagDeleteArgs struct {
registry.DeleteTagRequest
Timeout *string
}

func tagDeleteBuilder(c *core.Command) *core.Command {
c.ArgsType = reflect.TypeOf(customTagDeleteArgs{})
c.ArgSpecs.AddBefore("force", &core.ArgSpec{
Name: "timeout",
Short: "Maximum time to handle the request",
Required: false,
Positional: false,
})

c.Run = func(ctx context.Context, argsI any) (any, error) {
client := core.ExtractClient(ctx)
api := registry.NewAPI(client)
args := argsI.(*customTagDeleteArgs)

if args.Timeout == nil {
return api.DeleteTag(&args.DeleteTagRequest, scw.WithContext(ctx))
}

timeout, err := time.ParseDuration(*args.Timeout)
if err != nil {
return nil, err
}

ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout)
defer cancel()

return api.DeleteTag(&args.DeleteTagRequest, scw.WithContext(ctxWithTimeout))
}

return c
}
100 changes: 100 additions & 0 deletions internal/namespaces/registry/v1/custom_tag_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package registry_test

import (
"fmt"
"testing"

"github.com/scaleway/scaleway-cli/v2/core"
"github.com/scaleway/scaleway-cli/v2/internal/namespaces/registry/v1"
"github.com/scaleway/scaleway-cli/v2/internal/testhelpers"
"github.com/stretchr/testify/assert"
)

func Test_RegistryTagDelete(t *testing.T) {
registryNamespaceMetaKey := "RegistryNamespace"
helloWorldImage := "hello-world:latest"
helloWorldImageMetaKey := "HelloWorldImage"
tagIDMetaKey := "TagID"

t.Run("timeout-ok", core.Test(&core.TestConfig{
Commands: registry.GetCommands(),
BeforeFunc: core.BeforeFuncCombine(
core.ExecStoreBeforeCmd(
registryNamespaceMetaKey,
fmt.Sprintf("scw registry namespace create name=%s is-public=false",
core.GetRandomName("test-rg-tag-delete"),
),
),
core.BeforeFuncWhenUpdatingCassette(
core.BeforeFuncCombine(
core.ExecBeforeCmd("scw registry login"),
testhelpers.PushRegistryImage(helloWorldImage, registryNamespaceMetaKey),
),
),
testhelpers.StoreImageIdentifierInMeta(
registryNamespaceMetaKey,
helloWorldImage,
helloWorldImageMetaKey,
),
testhelpers.StoreTagIDInMeta(registryNamespaceMetaKey, helloWorldImage, tagIDMetaKey),
),
Cmd: fmt.Sprintf("scw registry tag delete {{ .%s }} timeout=1s", tagIDMetaKey),
Check: core.TestCheckCombine(
core.TestCheckGolden(),
core.TestCheckExitCode(0),
),
AfterFunc: func(ctx *core.AfterFuncCtx) error {
return core.ExecAfterCmd(
fmt.Sprintf(
"scw registry namespace delete {{ .%s.ID }}",
registryNamespaceMetaKey,
),
)(
ctx,
)
},
}))

t.Run("timeout-too-short", core.Test(&core.TestConfig{
Commands: registry.GetCommands(),
BeforeFunc: core.BeforeFuncCombine(
core.ExecStoreBeforeCmd(
registryNamespaceMetaKey,
fmt.Sprintf("scw registry namespace create name=%s is-public=false",
core.GetRandomName("test-rg-tag-delete"),
),
),
core.BeforeFuncWhenUpdatingCassette(
core.BeforeFuncCombine(
core.ExecBeforeCmd("scw registry login"),
testhelpers.PushRegistryImage(helloWorldImage, registryNamespaceMetaKey),
),
),
testhelpers.StoreImageIdentifierInMeta(
registryNamespaceMetaKey,
helloWorldImage,
helloWorldImageMetaKey,
),
testhelpers.StoreTagIDInMeta(registryNamespaceMetaKey, helloWorldImage, tagIDMetaKey),
),
Cmd: fmt.Sprintf("scw registry tag delete {{ .%s }} timeout=1µs", tagIDMetaKey),
Check: core.TestCheckCombine(
core.TestCheckGolden(),
core.TestCheckExitCode(1),
func(t *testing.T, ctx *core.CheckFuncCtx) {
t.Helper()
assert.Contains(t, string(ctx.Stderr), "context deadline exceeded")
},
),
AfterFunc: func(ctx *core.AfterFuncCtx) error {
return core.ExecAfterCmd(
fmt.Sprintf(
"scw registry namespace delete {{ .%s.ID }}",
registryNamespaceMetaKey,
),
)(
ctx,
)
},
}))
}
Loading
Loading