Skip to content
Open
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
11 changes: 11 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ aurs:
- "git"
package: |-
install -Dm755 "./shopware-cli" "${pkgdir}/usr/bin/shopware-cli"
ln -sf "shopware-cli" "${pkgdir}/usr/bin/swx"

# license
install -Dm644 "./LICENSE" "${pkgdir}/usr/share/licenses/shopware-cli/LICENSE"
Expand All @@ -185,6 +186,7 @@ nix:
name: "Shopware Bot"
email: github@shopware.com
post_install: |
ln -sf "$out/bin/shopware-cli" "$out/bin/swx"
installShellCompletion --cmd shopware-cli \
--bash <($out/bin/shopware-cli completion bash) \
--zsh <($out/bin/shopware-cli completion zsh) \
Expand All @@ -202,6 +204,9 @@ nfpms:
description: A cli which contains handy helpful commands for daily Shopware tasks
license: MIT
contents:
- src: /usr/bin/shopware-cli
dst: /usr/bin/swx
type: symlink
- src: ./completions/shopware-cli.bash
dst: /etc/bash_completion.d/shopware-cli
- src: ./completions/shopware-cli.fish
Expand All @@ -225,6 +230,12 @@ homebrew_casks:
homepage: https://shopware.com
description: Shopware CLI helps Shopware developers manage extensions
license: MIT
hooks:
post:
install: |
system_command "/bin/ln", args: ["-sf", "#{HOMEBREW_PREFIX}/bin/shopware-cli", "#{HOMEBREW_PREFIX}/bin/swx"]
uninstall: |
system_command "/bin/rm", args: ["-f", "#{HOMEBREW_PREFIX}/bin/swx"]
completions:
bash: completions/shopware-cli.bash
zsh: completions/shopware-cli.zsh
Expand Down
71 changes: 69 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package cmd
import (
"context"
"os"
"path"
"slices"
"strings"

"github.com/mattn/go-isatty"
"github.com/spf13/cobra"
Expand All @@ -30,15 +32,80 @@ var rootCmd = &cobra.Command{
}

func Execute(ctx context.Context) {
ctx = logging.WithLogger(ctx, logging.NewLogger(slices.Contains(os.Args, "--verbose")))
ctx = system.WithInteraction(ctx, !slices.Contains(os.Args, "--no-interaction") && !slices.Contains(os.Args, "-n") && isatty.IsTerminal(os.Stdin.Fd()))
rootCmd.Use = commandNameFromArgs(os.Args)
args := mapAliasArgs(os.Args)
ctx = logging.WithLogger(ctx, logging.NewLogger(slices.Contains(args, "--verbose")))
ctx = system.WithInteraction(ctx, !slices.Contains(args, "--no-interaction") && !slices.Contains(args, "-n") && isatty.IsTerminal(os.Stdin.Fd()))
accountApi.SetUserAgent("shopware-cli/" + version)
rootCmd.SetArgs(args)

if err := rootCmd.ExecuteContext(ctx); err != nil {
logging.FromContext(ctx).Fatalln(err)
}
}

func mapAliasArgs(argv []string) []string {
if len(argv) == 0 {
return nil
}

args := argv[1:]
if !isSwxAlias(argv[0]) {
return args
}

if len(args) > 0 {
// Let users generate completion scripts for `swx` itself.
if args[0] == "completion" {
return args
}

// Cobra shell completion calls these internal commands.
// Prefixing `project console` preserves swx-as-console behavior for completions.
if args[0] == "__complete" || args[0] == "__completeNoDesc" {
aliasedCompletionArgs := make([]string, 0, len(args)+2)
aliasedCompletionArgs = append(aliasedCompletionArgs, args[0], "project", "console")
aliasedCompletionArgs = append(aliasedCompletionArgs, args[1:]...)

return aliasedCompletionArgs
}
}

// When invoked via the `swx` symlink, forward everything to `project console`.
aliasedArgs := make([]string, 0, len(args)+3)
aliasedArgs = append(aliasedArgs, "project", "console")

if len(args) == 0 {
aliasedArgs = append(aliasedArgs, "list")
} else {
aliasedArgs = append(aliasedArgs, args...)
}

return aliasedArgs
}

func isSwxAlias(binaryPath string) bool {
return strings.EqualFold(commandNameFromBinaryPath(binaryPath), "swx")
}

func commandNameFromArgs(argv []string) string {
if len(argv) == 0 {
return rootCmd.Use
}

return commandNameFromBinaryPath(argv[0])
}

func commandNameFromBinaryPath(binaryPath string) string {
normalizedPath := strings.ReplaceAll(binaryPath, "\\", "/")
binaryName := strings.TrimSuffix(path.Base(normalizedPath), path.Ext(normalizedPath))
if binaryName == "" {
return rootCmd.Use
}

return binaryName
}

func init() {
rootCmd.SilenceErrors = true

Expand Down
87 changes: 87 additions & 0 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package cmd

import (
"testing"

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

func TestMapAliasArgs_NoArgs(t *testing.T) {
t.Parallel()
assert.Equal(t, []string{}, mapAliasArgs([]string{"shopware-cli"}))
}

func TestMapAliasArgs_RegularBinary(t *testing.T) {
t.Parallel()
args := mapAliasArgs([]string{"shopware-cli", "project", "console", "debug:router"})

assert.Equal(t, []string{"project", "console", "debug:router"}, args)
}

func TestMapAliasArgs_SwxAlias(t *testing.T) {
t.Parallel()
args := mapAliasArgs([]string{"/usr/local/bin/swx", "debug:router", "--env=prod"})

assert.Equal(t, []string{"project", "console", "debug:router", "--env=prod"}, args)
}

func TestMapAliasArgs_SwxAliasWithoutArgs(t *testing.T) {
t.Parallel()
args := mapAliasArgs([]string{"/usr/local/bin/swx"})

assert.Equal(t, []string{"project", "console", "list"}, args)
}

func TestMapAliasArgs_SwxExeAlias(t *testing.T) {
t.Parallel()
args := mapAliasArgs([]string{"C:\\tools\\swx.exe", "cache:clear"})

assert.Equal(t, []string{"project", "console", "cache:clear"}, args)
}

func TestMapAliasArgs_SwxCaseInsensitive(t *testing.T) {
t.Parallel()
args := mapAliasArgs([]string{"/usr/local/bin/SWX", "cache:clear"})

assert.Equal(t, []string{"project", "console", "cache:clear"}, args)
}

func TestMapAliasArgs_SwxCompletion(t *testing.T) {
t.Parallel()
args := mapAliasArgs([]string{"/usr/local/bin/swx", "completion", "bash"})

assert.Equal(t, []string{"completion", "bash"}, args)
}

func TestMapAliasArgs_SwxInternalCompletion(t *testing.T) {
t.Parallel()
args := mapAliasArgs([]string{"/usr/local/bin/swx", "__complete", "cache:clear"})

assert.Equal(t, []string{"__complete", "project", "console", "cache:clear"}, args)
}

func TestMapAliasArgs_SwxInternalCompletionNoDesc(t *testing.T) {
t.Parallel()
args := mapAliasArgs([]string{"/usr/local/bin/swx", "__completeNoDesc", "cache:clear"})

assert.Equal(t, []string{"__completeNoDesc", "project", "console", "cache:clear"}, args)
}

func TestMapAliasArgs_SwxHelp(t *testing.T) {
t.Parallel()
assert.Equal(t, []string{"project", "console", "--help"}, mapAliasArgs([]string{"/usr/local/bin/swx", "--help"}))
assert.Equal(t, []string{"project", "console", "-h"}, mapAliasArgs([]string{"/usr/local/bin/swx", "-h"}))
}

func TestMapAliasArgs_SwxVersion(t *testing.T) {
t.Parallel()
assert.Equal(t, []string{"project", "console", "--version"}, mapAliasArgs([]string{"/usr/local/bin/swx", "--version"}))
assert.Equal(t, []string{"project", "console", "-v"}, mapAliasArgs([]string{"/usr/local/bin/swx", "-v"}))
}

func TestCommandNameFromArgs(t *testing.T) {
t.Parallel()
assert.Equal(t, "shopware-cli", commandNameFromArgs([]string{"/usr/local/bin/shopware-cli"}))
assert.Equal(t, "swx", commandNameFromArgs([]string{"C:\\tools\\swx.exe"}))
assert.Equal(t, "shopware-cli", commandNameFromArgs(nil))
}