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
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Superstack CLI

`superstack` is the command line interface to Superstack: log in, claim
devices, upload Lua code, and stream logs from your fleet. It is a single
static binary for managing Superstack from a terminal.
`superstack` is the command line interface to Superstack: log in, create
fleets, claim devices, and manage who can reach them. It is a single static
binary for managing Superstack from a terminal.

Uploading Lua code and streaming logs are not available yet.

## Install

Expand Down Expand Up @@ -60,11 +62,11 @@ static binary for managing Superstack from a terminal.
./superstack
```

1. Run the fast tests while you work. The login tests wait real seconds for
the poll interval, and `-short` skips them:
1. Run the tests while you work. Go caches per package, so a change to one
part leaves every other part cached:

```sh
CGO_ENABLED=0 go test -short ./...
CGO_ENABLED=0 go test ./...
```

1. Run every check before opening a pull request:
Expand Down
3 changes: 1 addition & 2 deletions internal/account/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ import (
"strings"

"github.com/siliconwitchery/superstack-cli/internal/api"
"github.com/siliconwitchery/superstack-cli/internal/dispatch"
)

func Balance(session api.Session, arguments []string) error {
positionals, jsonOutput := dispatch.TakeJsonFlag(arguments)
positionals, jsonOutput := api.TakeJsonFlag(arguments)

if len(positionals) > 1 {
return errors.New("account balance takes at most one fleet id")
Expand Down
17 changes: 17 additions & 0 deletions internal/api/arguments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package api

func TakeJsonFlag(arguments []string) ([]string, bool) {
positionals := []string{}
jsonOutput := false

for _, argument := range arguments {
if argument == "--json" {
jsonOutput = true
continue
}

positionals = append(positionals, argument)
}

return positionals, jsonOutput
}
14 changes: 0 additions & 14 deletions internal/api/devices.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,3 @@ func FetchDevices(session Session) ([]DeviceEntry, error) {

return devices, nil
}

func ValidImei(imei string) bool {
if len(imei) != 15 {
return false
}

for _, digit := range imei {
if digit < '0' || digit > '9' {
return false
}
}

return true
}
23 changes: 18 additions & 5 deletions internal/device/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"time"

"github.com/siliconwitchery/superstack-cli/internal/api"
"github.com/siliconwitchery/superstack-cli/internal/dispatch"
)

func Claim(session api.Session, arguments []string) error {
Expand All @@ -24,7 +23,7 @@ func Claim(session api.Session, arguments []string) error {

imei := arguments[0]

if !api.ValidImei(imei) {
if !validImei(imei) {
return errors.New("the IMEI is the 15-digit number printed on the device")
}

Expand Down Expand Up @@ -93,7 +92,7 @@ func Claim(session api.Session, arguments []string) error {
}

func List(session api.Session, arguments []string) error {
positionals, jsonOutput := dispatch.TakeJsonFlag(arguments)
positionals, jsonOutput := api.TakeJsonFlag(arguments)

if len(positionals) > 1 {
return errors.New("device list takes at most one fleet id")
Expand Down Expand Up @@ -262,7 +261,7 @@ func Rename(session api.Session, arguments []string) error {

imei := arguments[0]

if !api.ValidImei(imei) {
if !validImei(imei) {
return errors.New("the IMEI is the 15-digit number printed on the device")
}

Expand Down Expand Up @@ -310,7 +309,7 @@ func Release(session api.Session, arguments []string) error {

imei := arguments[0]

if !api.ValidImei(imei) {
if !validImei(imei) {
return errors.New("the IMEI is the 15-digit number printed on the device")
}

Expand Down Expand Up @@ -383,3 +382,17 @@ func Release(session api.Session, arguments []string) error {

return nil
}

func validImei(imei string) bool {
if len(imei) != 15 {
return false
}

for _, digit := range imei {
if digit < '0' || digit > '9' {
return false
}
}

return true
}
28 changes: 7 additions & 21 deletions internal/dispatch/dispatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import (
"github.com/siliconwitchery/superstack-cli/internal/api"
)

func TakeServerFlag(arguments []string) ([]string, string, error) {
func takeServerFlag(arguments []string) ([]string, string, error) {
remaining := []string{}

base := api.DefaultBase

for index := 0; index < len(arguments); index++ {
switch {
case arguments[index] == "--server":
if index+1 == len(arguments) || arguments[index+1] == "" {
if index+1 == len(arguments) {
return nil, "", errors.New("--server needs an address")
}

Expand All @@ -28,32 +28,18 @@ func TakeServerFlag(arguments []string) ([]string, string, error) {
case strings.HasPrefix(arguments[index], "--server="):
base = strings.TrimPrefix(arguments[index], "--server=")

if base == "" {
return nil, "", errors.New("--server needs an address")
}

default:
remaining = append(remaining, arguments[index])
}
}

return remaining, strings.TrimSuffix(base, "/"), nil
}

func TakeJsonFlag(arguments []string) ([]string, bool) {
positionals := []string{}
jsonOutput := false

for _, argument := range arguments {
if argument == "--json" {
jsonOutput = true
continue
}
base = strings.TrimRight(base, "/")

positionals = append(positionals, argument)
if base == "" {
return nil, "", errors.New("--server needs an address")
}

return positionals, jsonOutput
return remaining, base, nil
}

type Command struct {
Expand Down Expand Up @@ -141,7 +127,7 @@ func printHelp(session api.Session, sections []Section) {
}

func Dispatch(sections []Section, version string, arguments []string, in io.Reader, out io.Writer) error {
arguments, base, err := TakeServerFlag(arguments)
arguments, base, err := takeServerFlag(arguments)

if err != nil {
return err
Expand Down
7 changes: 6 additions & 1 deletion internal/dispatch/dispatch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,16 @@ func TestTakeServerFlag(t *testing.T) {
arguments: []string{"--server", "", "login"},
wantError: "needs an address",
},
{
name: "an address of nothing but slashes",
arguments: []string{"--server", "//", "login"},
wantError: "needs an address",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
remaining, base, err := TakeServerFlag(test.arguments)
remaining, base, err := takeServerFlag(test.arguments)

if test.wantError != "" {
if err == nil || !strings.Contains(err.Error(), test.wantError) {
Expand Down
3 changes: 1 addition & 2 deletions internal/fleet/fleet.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"strings"

"github.com/siliconwitchery/superstack-cli/internal/api"
"github.com/siliconwitchery/superstack-cli/internal/dispatch"
)

func Create(session api.Session, arguments []string) error {
Expand Down Expand Up @@ -62,7 +61,7 @@ func Create(session api.Session, arguments []string) error {
}

func List(session api.Session, arguments []string) error {
positionals, jsonOutput := dispatch.TakeJsonFlag(arguments)
positionals, jsonOutput := api.TakeJsonFlag(arguments)

if len(positionals) != 0 {
return errors.New("fleet list takes no arguments")
Expand Down
3 changes: 1 addition & 2 deletions internal/key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"strings"

"github.com/siliconwitchery/superstack-cli/internal/api"
"github.com/siliconwitchery/superstack-cli/internal/dispatch"
)

func Create(session api.Session, arguments []string) error {
Expand Down Expand Up @@ -69,7 +68,7 @@ func Create(session api.Session, arguments []string) error {
}

func List(session api.Session, arguments []string) error {
positionals, jsonOutput := dispatch.TakeJsonFlag(arguments)
positionals, jsonOutput := api.TakeJsonFlag(arguments)

if len(positionals) > 1 {
return errors.New("key list takes at most one fleet id")
Expand Down
4 changes: 0 additions & 4 deletions internal/login/login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,6 @@ func TestLogin(t *testing.T) {

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if testing.Short() && (len(test.pollAnswers) > 0 || strings.Contains(test.deviceAnswer, `"expires_in":0`)) {
t.Skip("the poll loop waits real seconds")
}

apitest.IsolateKeyStorage(t)

deviceInterval := test.deviceInterval
Expand Down
3 changes: 1 addition & 2 deletions internal/member/member.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"strings"

"github.com/siliconwitchery/superstack-cli/internal/api"
"github.com/siliconwitchery/superstack-cli/internal/dispatch"
)

func Add(session api.Session, arguments []string) error {
Expand Down Expand Up @@ -61,7 +60,7 @@ func Add(session api.Session, arguments []string) error {
}

func List(session api.Session, arguments []string) error {
positionals, jsonOutput := dispatch.TakeJsonFlag(arguments)
positionals, jsonOutput := api.TakeJsonFlag(arguments)

if len(positionals) != 1 {
return errors.New("member list takes a fleet id")
Expand Down