<commit_before>package main // import "github.com/CenturyLinkLabs/panamaxcli"

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/CenturyLinkLabs/panamax-remote-agent-go/client"
	"github.com/CenturyLinkLabs/panamaxcli/actions"
	"github.com/CenturyLinkLabs/panamaxcli/config"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
)

var (
	Config   config.Config
	Commands []cli.Command
)

func init() {
	client.DefaultHTTPTimeout = 10

	Commands = []cli.Command{
		{
			Name:    "remote",
			Aliases: []string{"re"},
			Usage:   "Manage remotes",
			Subcommands: []cli.Command{
				{
					Name:    "list",
					Aliases: []string{"l"},
					Usage:   "List remotes",
					Action:  remoteListAction,
				},
				{
					Name:    "describe",
					Aliases: []string{"d"},
					Usage:   "Describe a remote",
					Before:  actionRequiresArgument("remote name"),
					Action:  remoteDescribeAction,
				},
				{
					Name:        "add",
					Usage:       "Add a remote",
					Description: "Arguments are the name of the remote and optionally the path to the token file. If omitted, you will be prompted for a token.",
					Before:      actionRequiresArgument("remote name", "optional:token path"),
					Action:      remoteAddAction,
				},
				{
					Name:        "active",
					Usage:       "Set the active remote",
					Description: "Passing a remote name as an argument makes it the active remote.",
					Before:      actionRequiresArgument("remote name"),
					Action:      setActiveRemoteAction,
				},
				{
					Name:        "remove",
					Usage:       "Remove a remote",
					Description: "Argument is a remote name.",
					Before:      actionRequiresArgument("remote name"),
					Action:      removeRemoteAction,
				},
				{
					Name:        "token",
					Usage:       "Show the remote's token",
					Description: "Argument is a remote name.",
					Before:      actionRequiresArgument("remote name"),
					Action:      getTokenAction,
				},
			},
		},
		{
			Name:    "deployment",
			Aliases: []string{"de"},
			Usage:   "Manage deployments",
			Before:  actionRequiresActiveRemote,
			Subcommands: []cli.Command{
				{
					Name:    "list",
					Aliases: []string{"l"},
					Usage:   "List deployments",
					Action:  deploymentsListAction,
				},
				{
					Name:        "describe",
					Aliases:     []string{"d"},
					Usage:       "Describe a deployment",
					Description: "Argument is a deployment ID.",
					Before:      actionRequiresArgument("deployment ID"),
					Action:      describeDeploymentAction,
				},
				{
					Name:        "redeploy",
					Usage:       "Redeploy a deployment",
					Description: "Argument is a deployment ID.",
					Before:      actionRequiresArgument("deployment ID"),
					Action:      redeployDeploymentAction,
				},
				{
					Name:        "delete",
					Usage:       "Delete a deployment",
					Description: "Argument is a deployment ID.",
					Before:      actionRequiresArgument("deployment ID"),
					Action:      deleteDeploymentAction,
				},
			},
		},
	}
}

func main() {
	app := cli.NewApp()
	app.Name = "panamaxcli"
	app.Version = "0.0.1"
	app.Usage = "Panamax command-line utility."
	app.Authors = []cli.Author{{"CenturyLink Labs", "<EMAIL>"}}
	app.Commands = Commands
	app.Before = initializeApp
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "debug",
			Usage: "Enable verbose logging",
		},
	}

	app.Run(os.Args)
}

func initializeApp(c *cli.Context) error {
	if c.GlobalBool("debug") {
		// Remote Agent Client will write to logrus with helpful info!
		log.SetLevel(log.DebugLevel)
	}

	// Surprise! CLI wants an error from this method but, only uses it to abort
	// execution, not for display anywhere.
	if err := loadConfig(c); err != nil {
		log.Error(err)
	}

	return nil
}

func loadConfig(c *cli.Context) error {
	// Stolen from: https://github.com/awslabs/aws-sdk-go/pull/136 Originally
	// cleaner with os/user.Current(), but that failed under cross-compilation on
	// non-linux platforms.
	dir := os.Getenv("HOME") // *nix
	if dir == "" {           // Windows
		dir = os.Getenv("USERPROFILE")
	}
	if dir == "" {
		return errors.New("Couldn't determine your home directory!")
	}

	fileConfig := config.FileConfig{Path: filepath.Join(dir, ".agents")}
	if err := fileConfig.Load(); err != nil {
		log.Error(err)
		return err
	}
	Config = config.Config(&fileConfig)

	return nil
}

func actionRequiresArgument(args ...string) func(c *cli.Context) error {
	return func(c *cli.Context) error {
		requiredCount := len(args)
		for i, arg := range args {
			if strings.HasPrefix(arg, "optional:") {
				requiredCount = i
			}
		}

		if len(c.Args()) < requiredCount || len(c.Args()) > len(args) {
			s := strings.Join(args, ", ")
			message := fmt.Sprintf("This command requires the following arguments: %s", s)
			log.Errorln(message)
			return errors.New(message)
		}

		return nil
	}
}

func actionRequiresActiveRemote(c *cli.Context) error {
	if Config.Active() == nil {
		message := "an active remote is required for this command"
		log.Errorln(message)
		return errors.New(message)
	}

	return nil
}

func remoteAddAction(c *cli.Context) {
	if len(c.Args()) == 1 {
		name := c.Args().First()
		fmt.Println("Paste your token:")
		reader := bufio.NewReader(os.Stdin)
		token, err := reader.ReadBytes('\n')
		if err != nil {
			log.Fatal(err)
		}

		output, err := actions.AddRemote(Config, name, token)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Println(output.ToPrettyOutput())<commit_msg>Show help for describe even without an active remote.<commit_after>package main // import "github.com/CenturyLinkLabs/panamaxcli"

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/CenturyLinkLabs/panamax-remote-agent-go/client"
	"github.com/CenturyLinkLabs/panamaxcli/actions"
	"github.com/CenturyLinkLabs/panamaxcli/config"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
)

var (
	Config   config.Config
	Commands []cli.Command
)

func init() {
	client.DefaultHTTPTimeout = 10

	Commands = []cli.Command{
		{
			Name:    "remote",
			Aliases: []string{"re"},
			Usage:   "Manage remotes",
			Subcommands: []cli.Command{
				{
					Name:    "list",
					Aliases: []string{"l"},
					Usage:   "List remotes",
					Action:  remoteListAction,
				},
				{
					Name:    "describe",
					Aliases: []string{"d"},
					Usage:   "Describe a remote",
					Before:  actionRequiresArgument("remote name"),
					Action:  remoteDescribeAction,
				},
				{
					Name:        "add",
					Usage:       "Add a remote",
					Description: "Arguments are the name of the remote and optionally the path to the token file. If omitted, you will be prompted for a token.",
					Before:      actionRequiresArgument("remote name", "optional:token path"),
					Action:      remoteAddAction,
				},
				{
					Name:        "active",
					Usage:       "Set the active remote",
					Description: "Passing a remote name as an argument makes it the active remote.",
					Before:      actionRequiresArgument("remote name"),
					Action:      setActiveRemoteAction,
				},
				{
					Name:        "remove",
					Usage:       "Remove a remote",
					Description: "Argument is a remote name.",
					Before:      actionRequiresArgument("remote name"),
					Action:      removeRemoteAction,
				},
				{
					Name:        "token",
					Usage:       "Show the remote's token",
					Description: "Argument is a remote name.",
					Before:      actionRequiresArgument("remote name"),
					Action:      getTokenAction,
				},
			},
		},
		{
			Name:    "deployment",
			Aliases: []string{"de"},
			Usage:   "Manage deployments",
			Before:  actionRequiresActiveRemote,
			Subcommands: []cli.Command{
				{
					Name:    "list",
					Aliases: []string{"l"},
					Usage:   "List deployments",
					Action:  deploymentsListAction,
				},
				{
					Name:        "describe",
					Aliases:     []string{"d"},
					Usage:       "Describe a deployment",
					Description: "Argument is a deployment ID.",
					Before:      actionRequiresArgument("deployment ID"),
					Action:      describeDeploymentAction,
				},
				{
					Name:        "redeploy",
					Usage:       "Redeploy a deployment",
					Description: "Argument is a deployment ID.",
					Before:      actionRequiresArgument("deployment ID"),
					Action:      redeployDeploymentAction,
				},
				{
					Name:        "delete",
					Usage:       "Delete a deployment",
					Description: "Argument is a deployment ID.",
					Before:      actionRequiresArgument("deployment ID"),
					Action:      deleteDeploymentAction,
				},
			},
		},
	}
}

func main() {
	app := cli.NewApp()
	app.Name = "panamaxcli"
	app.Version = "0.0.1"
	app.Usage = "Panamax command-line utility."
	app.Authors = []cli.Author{{"CenturyLink Labs", "<EMAIL>"}}
	app.Commands = Commands
	app.Before = initializeApp
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "debug",
			Usage: "Enable verbose logging",
		},
	}

	app.Run(os.Args)
}

func initializeApp(c *cli.Context) error {
	if c.GlobalBool("debug") {
		// Remote Agent Client will write to logrus with helpful info!
		log.SetLevel(log.DebugLevel)
	}

	// Surprise! CLI wants an error from this method but, only uses it to abort
	// execution, not for display anywhere.
	if err := loadConfig(c); err != nil {
		log.Error(err)
	}

	return nil
}

func loadConfig(c *cli.Context) error {
	// Stolen from: https://github.com/awslabs/aws-sdk-go/pull/136 Originally
	// cleaner with os/user.Current(), but that failed under cross-compilation on
	// non-linux platforms.
	dir := os.Getenv("HOME") // *nix
	if dir == "" {           // Windows
		dir = os.Getenv("USERPROFILE")
	}
	if dir == "" {
		return errors.New("Couldn't determine your home directory!")
	}

	fileConfig := config.FileConfig{Path: filepath.Join(dir, ".agents")}
	if err := fileConfig.Load(); err != nil {
		log.Error(err)
		return err
	}
	Config = config.Config(&fileConfig)

	return nil
}

func actionRequiresArgument(args ...string) func(c *cli.Context) error {
	return func(c *cli.Context) error {
		requiredCount := len(args)
		for i, arg := range args {
			if strings.HasPrefix(arg, "optional:") {
				requiredCount = i
			}
		}

		if len(c.Args()) < requiredCount || len(c.Args()) > len(args) {
			s := strings.Join(args, ", ")
			message := fmt.Sprintf("This command requires the following arguments: %s", s)
			log.Errorln(message)
			return errors.New(message)
		}

		return nil
	}
}

func actionRequiresActiveRemote(c *cli.Context) error {
	arg := c.Args().First()
	isHelp := (arg == "help" || arg == "h")
	if !isHelp && Config.Active() == nil {
		message := "an active remote is required for this command"
		log.Errorln(message)
		return errors.New(message)
	}

	return nil
}

func remoteAddAction(c *cli.Context) {
	if len(c.Args()) == 1 {
		name := c.Args().First()
		fmt.Println("Paste your token:")
		reader := bufio.NewReader(os.Stdin)
		token, err := reader.ReadBytes('\n')
		if err != nil {
			log.Fatal(err)
		}

		output, err := actions.AddRemote(Config, name, token)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Println(output.ToPrettyOutput())
{'fraction_non_alphanumeric': 0.13873663401922903, 'fraction_numerical': 0.0016173959924521521, 'mean_word_length': 2.8273727647867952, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3235897', 'n_tokens_mistral': 4310, 'n_tokens_neox': 3581, 'n_words': 913}