Skip to content

Commit

Permalink
Merge pull request #13 from areed/areed/ch787/vendor-cli-ux
Browse files Browse the repository at this point in the history
Improve UX for vendor CLI
  • Loading branch information
areed authored Jul 30, 2017
2 parents 9720e2e + 9edc623 commit c971250
Show file tree
Hide file tree
Showing 74 changed files with 2,081 additions and 200 deletions.
13 changes: 12 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ get-spec-prod:
curl -o gen/spec/$$PKG.json \
https://api.replicated.com/vendor/v1/spec/$$PKG.json; \
done
curl -o gen/spec/v2.json https://api.replicated.com/vendor/v2/spec/swagger.json;

# generate the swagger specs from the local replicatedcom/vendor-api repo
get-spec-local:
Expand All @@ -36,7 +37,10 @@ get-spec-local:
swagger generate spec \
-b ../../replicatedcom/vendor-api/handlers/replv1/$$PKG \
-o gen/spec/$$PKG.json; \
done'
done \
&& swagger generate spec \
-b ../../replicatedcom/vendor-api/handlers/replv2 \
-o gen/spec/v2.json'

# generate from the specs in gen/spec, which come from either get-spec-prod or get-spec-local
gen-models:
Expand All @@ -49,6 +53,13 @@ gen-models:
-l go \
-o /local/gen/go/$$PKG; \
done
docker run --rm \
--volume `pwd`:/local \
swaggerapi/swagger-codegen-cli generate \
-Dmodels -DmodelsDocs=false \
-i /local/gen/spec/v2.json \
-l go \
-o /local/gen/go/v2;

build:
go build -o replicated cli/main.go
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ replicated channel ls --app my-app-slug --token e8d7ce8e3d3278a8b1255237e6310069
```

Set the following env vars to avoid passing them as arguments to each command.
* REPLICATED_APP_SLUG
* REPLICATED_APP - either an app slug or app ID
* REPLICATED_API_TOKEN

Then the above command would be simply
Expand All @@ -33,7 +33,7 @@ replicated channel ls
### CI Example
Creating a new release for every tagged build is a common use of the replicated command.

Assume the app's yaml config is checked in at replicated.yaml and you have configured TravisCI or CircleCI with your REPLICATED_APP_SLUG and REPLICATED_API_TOKEN environment variables.
Assume the app's yaml config is checked in at replicated.yaml and you have configured TravisCI or CircleCI with your REPLICATED_APP and REPLICATED_API_TOKEN environment variables.

Then add a release.sh script to your project something like this:

Expand Down Expand Up @@ -106,11 +106,11 @@ import (

func main() {
token := os.Getenv("REPLICATED_API_TOKEN")
appSlug := os.Getenv("REPLICATED_APP_SLUG")
appSlugOrID := os.Getenv("REPLICATED_APP")

api := client.New(token)

app, err := api.GetAppBySlug(appSlug)
app, err := api.GetApp(appSlugOrID)
if err != nil {
log.Fatal(err)
}
Expand Down
36 changes: 36 additions & 0 deletions cli/cmd/channel_adoption.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cmd

import (
"errors"

"github.com/replicatedhq/replicated/cli/print"
"github.com/spf13/cobra"
)

var channelAdoptionCmd = &cobra.Command{
Use: "adoption CHANNEL_ID",
Short: "Print channel adoption statistics by license type",
Long: "Print channel adoption statistics by license type",
}

func init() {
channelCmd.AddCommand(channelAdoptionCmd)
}

func (r *runners) channelAdoption(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("channel ID is required")
}
chanID := args[0]

appChan, _, err := r.api.GetChannel(r.appID, chanID)
if err != nil {
return err
}

if err = print.ChannelAdoption(r.w, appChan.Adoption); err != nil {
return err
}

return nil
}
36 changes: 36 additions & 0 deletions cli/cmd/channel_counts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cmd

import (
"errors"

"github.com/replicatedhq/replicated/cli/print"
"github.com/spf13/cobra"
)

var channelCountsCmd = &cobra.Command{
Use: "counts CHANNEL_ID",
Short: "Print channel license counts",
Long: "Print channel license counts",
}

func init() {
channelCmd.AddCommand(channelCountsCmd)
}

func (r *runners) channelCounts(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("channel ID is required")
}
chanID := args[0]

appChan, _, err := r.api.GetChannel(r.appID, chanID)
if err != nil {
return err
}

if err = print.LicenseCounts(r.w, appChan.LicenseCounts); err != nil {
return err
}

return nil
}
7 changes: 6 additions & 1 deletion cli/cmd/channel_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"github.com/replicatedhq/replicated/cli/print"
"github.com/replicatedhq/replicated/client"
"github.com/spf13/cobra"
)

Expand All @@ -25,7 +26,11 @@ func init() {
}

func (r *runners) channelCreate(cmd *cobra.Command, args []string) error {
allChannels, err := r.api.CreateChannel(r.appID, channelCreateName, channelCreateDescription)
opts := &client.ChannelOptions{
Name: channelCreateName,
Description: channelCreateDescription,
}
allChannels, err := r.api.CreateChannel(r.appID, opts)
if err != nil {
return err
}
Expand Down
24 changes: 1 addition & 23 deletions cli/cmd/channel_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cmd

import (
"errors"
"fmt"

"github.com/spf13/cobra"

Expand All @@ -26,7 +25,7 @@ func (r *runners) channelInspect(cmd *cobra.Command, args []string) error {
}
chanID := args[0]

appChan, releases, err := r.api.GetChannel(r.appID, chanID)
appChan, _, err := r.api.GetChannel(r.appID, chanID)
if err != nil {
return err
}
Expand All @@ -35,26 +34,5 @@ func (r *runners) channelInspect(cmd *cobra.Command, args []string) error {
return err
}

if _, err = fmt.Fprint(r.w, "\nADOPTION\n"); err != nil {
return err
}
if err = print.ChannelAdoption(r.w, &appChan.Adoption); err != nil {
return err
}

if _, err = fmt.Fprint(r.w, "\nLICENSE_COUNTS\n"); err != nil {
return err
}
if err = print.LicenseCounts(r.w, &appChan.LicenseCounts); err != nil {
return err
}

if _, err = fmt.Fprint(r.w, "\nRELEASES\n"); err != nil {
return err
}
if err = print.ChannelReleases(r.w, releases); err != nil {
return err
}

return nil
}
36 changes: 36 additions & 0 deletions cli/cmd/channel_releases.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cmd

import (
"errors"

"github.com/replicatedhq/replicated/cli/print"
"github.com/spf13/cobra"
)

var channelReleasesCmd = &cobra.Command{
Use: "releases CHANNEL_ID",
Short: "Print channel license counts",
Long: "Print channel license counts",
}

func init() {
channelCmd.AddCommand(channelReleasesCmd)
}

func (r *runners) channelReleases(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("channel ID is required")
}
chanID := args[0]

_, releases, err := r.api.GetChannel(r.appID, chanID)
if err != nil {
return err
}

if err = print.ChannelReleases(r.w, releases); err != nil {
return err
}

return nil
}
11 changes: 5 additions & 6 deletions cli/cmd/release_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"fmt"

"github.com/replicatedhq/replicated/client"
"github.com/spf13/cobra"
)

Expand All @@ -26,8 +27,10 @@ func (r *runners) releaseCreate(cmd *cobra.Command, args []string) error {
return fmt.Errorf("yaml is required")
}

// API does not accept yaml in create operation, so first create then udpate
release, err := r.api.CreateRelease(r.appID)
opts := &client.ReleaseOptions{
YAML: createReleaseYaml,
}
release, err := r.api.CreateRelease(r.appID, opts)
if err != nil {
return err
}
Expand All @@ -37,9 +40,5 @@ func (r *runners) releaseCreate(cmd *cobra.Command, args []string) error {
}
r.w.Flush()

if err := r.api.UpdateRelease(r.appID, release.Sequence, createReleaseYaml); err != nil {
return fmt.Errorf("Failure setting yaml config for release: %v", err)
}

return nil
}
4 changes: 3 additions & 1 deletion cli/cmd/release_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"strconv"

"github.com/replicatedhq/replicated/client"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -34,7 +35,8 @@ func (r *runners) releaseUpdate(cmd *cobra.Command, args []string) error {
return fmt.Errorf("invalid release sequence: %s", args[0])
}

if err := r.api.UpdateRelease(r.appID, seq, updateReleaseYaml); err != nil {
opts := &client.ReleaseOptions{YAML: updateReleaseYaml}
if err := r.api.UpdateRelease(r.appID, seq, opts); err != nil {
return fmt.Errorf("Failure setting new yaml config for release: %v", err)
}

Expand Down
46 changes: 37 additions & 9 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ const (
padChar = ' '
)

var appSlug string
var appSlugOrID string
var apiToken string
var apiOrigin = "https://api.replicated.com/vendor"

func init() {
RootCmd.PersistentFlags().StringVar(&appSlug, "app", "", "The app slug to use in all calls")
RootCmd.PersistentFlags().StringVar(&appSlugOrID, "app", "", "The app slug or app id to use in all calls")
RootCmd.PersistentFlags().StringVar(&apiToken, "token", "", "The API token to use to access your app in the Vendor API")

originFromEnv := os.Getenv("REPLICATED_API_ORIGIN")
Expand All @@ -39,6 +39,33 @@ var RootCmd = &cobra.Command{
Long: `The replicated CLI allows vendors to manage their apps' channels and releases.`,
}

// Almost the same as the default but don't print help subcommand
var rootCmdUsageTmpl = `
Usage:{{if .Runnable}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
Available Commands:{{range .Commands}}{{if .IsAvailableCommand}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute(w io.Writer) error {
Expand All @@ -47,6 +74,9 @@ func Execute(w io.Writer) error {

channelCreateCmd.RunE = runCmds.channelCreate
channelInspectCmd.RunE = runCmds.channelInspect
channelAdoptionCmd.RunE = runCmds.channelAdoption
channelReleasesCmd.RunE = runCmds.channelReleases
channelCountsCmd.RunE = runCmds.channelCounts
channelLsCmd.RunE = runCmds.channelList
channelRmCmd.RunE = runCmds.channelRemove
releaseCreateCmd.RunE = runCmds.releaseCreate
Expand All @@ -55,6 +85,8 @@ func Execute(w io.Writer) error {
releaseUpdateCmd.RunE = runCmds.releaseUpdate
releasePromoteCmd.RunE = runCmds.releasePromote

RootCmd.SetUsageTemplate(rootCmdUsageTmpl)

RootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if apiToken == "" {
apiToken = os.Getenv("REPLICATED_API_TOKEN")
Expand All @@ -65,15 +97,11 @@ func Execute(w io.Writer) error {
api := client.NewHTTPClient(apiOrigin, apiToken)
runCmds.api = api

if appSlug == "" {
appSlug = os.Getenv("REPLICATED_APP_SLUG")
if appSlug == "" {
return errors.New("Please provide your app slug")
}
if appSlugOrID == "" {
appSlugOrID = os.Getenv("REPLICATED_APP")
}

// resolve app ID from slug
app, err := api.GetAppBySlug(appSlug)
app, err := api.GetApp(appSlugOrID)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit c971250

Please sign in to comment.