Skip to content

Commit 429239f

Browse files
feat: add command aliases (#2501)
Co-authored-by: Olivier Cano <kindermoumoute@users.noreply.github.com>
1 parent b2c2cf1 commit 429239f

33 files changed

+1950
-16
lines changed

.golangci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
#
44
# To execute linters in local the ./scripts/lint.sh script can be used.
55

6+
run:
7+
skip-dirs:
8+
- internal/pkg
9+
610
linters:
711
disable-all: true
812

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
2+
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
3+
This command help you create aliases and save it to your config
4+
5+
USAGE:
6+
scw alias create <alias ...> [arg=value ...]
7+
8+
EXAMPLES:
9+
Create a custom alias 'isl' for 'instance server list'
10+
scw alias create isl command="instance server list""
11+
12+
Add an alias to a verb
13+
scw alias create c command=create
14+
15+
ARGS:
16+
alias Alias name
17+
[command] Command to create an alias for
18+
19+
FLAGS:
20+
-h, --help help for create
21+
22+
GLOBAL FLAGS:
23+
-c, --config string The path to the config file
24+
-D, --debug Enable debug mode
25+
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
26+
-p, --profile string The config profile to use
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
2+
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
3+
Delete an alias
4+
5+
USAGE:
6+
scw alias delete <alias ...> [arg=value ...]
7+
8+
ARGS:
9+
alias alias name
10+
11+
FLAGS:
12+
-h, --help help for delete
13+
14+
GLOBAL FLAGS:
15+
-c, --config string The path to the config file
16+
-D, --debug Enable debug mode
17+
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
18+
-p, --profile string The config profile to use
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
2+
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
3+
List aliases and their commands
4+
5+
USAGE:
6+
scw alias list [arg=value ...]
7+
8+
ARGS:
9+
[order-by=command_asc] (command_asc | command_desc | alias_asc | alias_desc)
10+
[command] filter command
11+
[alias] filter alias
12+
13+
FLAGS:
14+
-h, --help help for list
15+
16+
GLOBAL FLAGS:
17+
-c, --config string The path to the config file
18+
-D, --debug Enable debug mode
19+
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
20+
-p, --profile string The config profile to use
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
2+
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
3+
This namespace allows you to manage your aliases
4+
Aliases are store in cli config file, Default path for this configuration file is based on the following priority order:
5+
6+
- $SCW_CLI_CONFIG_PATH
7+
- $XDG_CONFIG_HOME/scw/cli.yaml
8+
- $HOME/.config/scw/cli.yaml
9+
- $USERPROFILE/.config/scw/cli.yaml
10+
11+
You can use multiple aliases in one command
12+
aliases in your commands are evaluated and you get completion
13+
with: isl = instance server list
14+
"scw isl <TAB>" will complete as "scw instance server list <TAB>"
15+
"scw <TAB>" will complete "isl"
16+
17+
USAGE:
18+
scw alias <command>
19+
20+
EXAMPLES:
21+
Create a custom alias 'isl' for 'instance server list'
22+
scw alias create isl command="instance server list"
23+
24+
Create an alias for a verb
25+
scw alias create c command=create
26+
27+
AVAILABLE COMMANDS:
28+
create Create a new alias for a command
29+
delete Delete an alias
30+
list List aliases and their commands
31+
32+
FLAGS:
33+
-h, --help help for alias
34+
35+
GLOBAL FLAGS:
36+
-c, --config string The path to the config file
37+
-D, --debug Enable debug mode
38+
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
39+
-p, --profile string The config profile to use
40+
41+
Use "scw alias [command] --help" for more information about a command.

cmd/scw/testdata/test-main-usage-usage.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ USAGE:
55

66
AVAILABLE COMMANDS:
77
account User related data
8+
alias Alias related commands
89
apple-silicon Apple silicon API
910
autocomplete Autocomplete related commands
1011
baremetal Elastic metal API

docs/commands/alias.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<!-- DO NOT EDIT: this file is automatically generated using scw-doc-gen -->
2+
# Documentation for `scw alias`
3+
This namespace allows you to manage your aliases
4+
Aliases are store in cli config file, Default path for this configuration file is based on the following priority order:
5+
6+
- $SCW_CLI_CONFIG_PATH
7+
- $XDG_CONFIG_HOME/scw/cli.yaml
8+
- $HOME/.config/scw/cli.yaml
9+
- $USERPROFILE/.config/scw/cli.yaml
10+
11+
You can use multiple aliases in one command
12+
aliases in your commands are evaluated and you get completion
13+
with: isl = instance server list
14+
"scw isl <TAB>" will complete as "scw instance server list <TAB>"
15+
"scw <TAB>" will complete "isl"
16+
17+
18+
- [Create a new alias for a command](#create-a-new-alias-for-a-command)
19+
- [Delete an alias](#delete-an-alias)
20+
- [List aliases and their commands](#list-aliases-and-their-commands)
21+
22+
23+
## Create a new alias for a command
24+
25+
This command help you create aliases and save it to your config
26+
27+
This command help you create aliases and save it to your config
28+
29+
**Usage:**
30+
31+
```
32+
scw alias create <alias ...> [arg=value ...]
33+
```
34+
35+
36+
**Args:**
37+
38+
| Name | | Description |
39+
|------|---|-------------|
40+
| alias | Required | Alias name |
41+
| command | | Command to create an alias for |
42+
43+
44+
**Examples:**
45+
46+
47+
Create a custom alias 'isl' for 'instance server list'
48+
```
49+
scw alias create isl command="instance server list""
50+
```
51+
52+
Add an alias to a verb
53+
```
54+
scw alias create c command=create
55+
```
56+
57+
58+
59+
60+
## Delete an alias
61+
62+
63+
64+
65+
66+
**Usage:**
67+
68+
```
69+
scw alias delete <alias ...> [arg=value ...]
70+
```
71+
72+
73+
**Args:**
74+
75+
| Name | | Description |
76+
|------|---|-------------|
77+
| alias | | alias name |
78+
79+
80+
81+
## List aliases and their commands
82+
83+
84+
85+
86+
87+
**Usage:**
88+
89+
```
90+
scw alias list [arg=value ...]
91+
```
92+
93+
94+
**Args:**
95+
96+
| Name | | Description |
97+
|------|---|-------------|
98+
| order-by | Default: `command_asc`<br />One of: `command_asc`, `command_desc`, `alias_asc`, `alias_desc` | |
99+
| command | | filter command |
100+
| alias | | filter alias |
101+
102+
103+

internal/alias/alias.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package alias
2+
3+
import "strings"
4+
5+
type Alias struct {
6+
// alias' key
7+
Name string
8+
// whole command
9+
Command []string
10+
11+
// list of args in the command
12+
args []string
13+
}
14+
15+
func (a *Alias) computeArgs() {
16+
a.args = []string{}
17+
for _, cmd := range a.Command {
18+
argSplitterIndex := strings.Index(cmd, "=")
19+
if argSplitterIndex != -1 {
20+
a.args = append(a.args, cmd[:argSplitterIndex])
21+
}
22+
}
23+
}
24+
25+
func (a *Alias) Args() []string {
26+
if a.args == nil {
27+
a.computeArgs()
28+
}
29+
return a.args
30+
}

internal/alias/config.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package alias
2+
3+
type Config struct {
4+
// Aliases are raw aliases that allow to expand a command
5+
// "scw instance sl", sl may be an alias and would expand command
6+
// "scw instance server list"
7+
// key = sl
8+
// value = server, list
9+
Aliases map[string][]string `yaml:"aliases"`
10+
11+
// map of alias using their first word as key
12+
// value can contain multiple aliases with the same first word
13+
// key = instance
14+
// value = isl, isc
15+
aliasesByFirstWord map[string][]Alias
16+
}
17+
18+
func EmptyConfig() *Config {
19+
return &Config{
20+
Aliases: map[string][]string{},
21+
}
22+
}
23+
24+
// GetAlias return raw alias for a given string
25+
func (c *Config) GetAlias(name string) []string {
26+
alias, aliasExists := c.Aliases[name]
27+
if aliasExists {
28+
return alias
29+
}
30+
return nil
31+
}
32+
33+
// ResolveAliases resolve aliases in given command
34+
// "scw isl" may return "scw instance server list"
35+
func (c *Config) ResolveAliases(command []string) []string {
36+
expandedCommand := make([]string, 0, len(command))
37+
for _, arg := range command {
38+
if alias := c.GetAlias(arg); alias != nil {
39+
expandedCommand = append(expandedCommand, alias...)
40+
} else {
41+
expandedCommand = append(expandedCommand, arg)
42+
}
43+
}
44+
return expandedCommand
45+
}
46+
47+
// AddAlias add alias to config
48+
// return true if alias has been replaced
49+
func (c *Config) AddAlias(name string, command []string) bool {
50+
_, exists := c.Aliases[name]
51+
c.Aliases[name] = command
52+
return exists
53+
}
54+
55+
// DeleteAlias deletes an alias
56+
// return true if alias was deleted
57+
func (c *Config) DeleteAlias(name string) bool {
58+
_, exists := c.Aliases[name]
59+
delete(c.Aliases, name)
60+
return exists
61+
}
62+
63+
func (c *Config) fillAliasByFirstWord() {
64+
c.aliasesByFirstWord = make(map[string][]Alias, len(c.Aliases))
65+
for alias, cmd := range c.Aliases {
66+
if len(cmd) == 0 {
67+
continue
68+
}
69+
path := cmd[0]
70+
c.aliasesByFirstWord[path] = append(c.aliasesByFirstWord[path], Alias{
71+
Name: alias,
72+
Command: cmd,
73+
})
74+
}
75+
}
76+
77+
// ResolveAliasesByFirstWord return list of aliases that start with given first word
78+
// firstWord: instance
79+
// may return
80+
// isl => instance server list
81+
// isc => instance server create
82+
func (c *Config) ResolveAliasesByFirstWord(firstWord string) ([]Alias, bool) {
83+
if c.aliasesByFirstWord == nil {
84+
c.fillAliasByFirstWord()
85+
}
86+
alias, ok := c.aliasesByFirstWord[firstWord]
87+
return alias, ok
88+
}

internal/alias/config_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package alias
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/alecthomas/assert"
8+
)
9+
10+
func TestConfig_ResolveAliases(t *testing.T) {
11+
tests := []struct {
12+
Aliases map[string][]string
13+
Command []string
14+
Expected []string
15+
}{
16+
{
17+
Aliases: map[string][]string{
18+
"isl": {"instance", "server", "list"},
19+
},
20+
Command: []string{"isl"},
21+
Expected: []string{"instance", "server", "list"},
22+
},
23+
{
24+
Aliases: map[string][]string{
25+
"isl": {"instance", "server", "list"},
26+
},
27+
Command: []string{"scw", "isl"},
28+
Expected: []string{"scw", "instance", "server", "list"},
29+
},
30+
{
31+
Aliases: map[string][]string{
32+
"sl": {"server", "list"},
33+
},
34+
Command: []string{"instance", "sl", "zone=fr-par-1"},
35+
Expected: []string{"instance", "server", "list", "zone=fr-par-1"},
36+
},
37+
}
38+
for i, tt := range tests {
39+
t.Run(fmt.Sprintf("Resolve_TestCase%d", i), func(t *testing.T) {
40+
config := &Config{Aliases: tt.Aliases}
41+
actual := config.ResolveAliases(tt.Command)
42+
assert.Equal(t, tt.Expected, actual)
43+
})
44+
}
45+
}

0 commit comments

Comments
 (0)