<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/kmagai/fugo/cmd/subcmd"
	"github.com/mitchellh/cli"
)

func run(args []string) int {
	style := &subcmd.Style{
		UI: &cli.ColoredUi{
			InfoColor:  cli.UiColorBlue,
			ErrorColor: cli.UiColorRed,
			Ui: &cli.BasicUi{
				Writer:      os.Stdout,
				ErrorWriter: os.Stderr,
				Reader:      os.Stdin,
			},
		}}

	return runCustom(args, commands(style))
}

func runCustom(args []string, comm map[string]cli.CommandFactory) int {
	// treat version as if it were a subcmd
	for _, arg := range args {
		if arg == "-v" || arg == "-version" || arg == "--version" {
			newArgs := make([]string, len(args)+1)
			newArgs[0] = "version"
			copy(newArgs[1:], args)
			args = newArgs
			break
		}
	}

	// default action to check
	if len(args) == 0 {
		checkArg := make([]string, 1)
		checkArg[0] = "check"
		args = checkArg
	}

	cli := &cli.CLI{
		Args:       args,
		commands:   comm,
		Version:    Version,
		HelpFunc:   cli.BasicHelpFunc(Name),
		HelpWriter: os.Stdout,
	}

	exitCode, err := cli.Run()
	if err != nil {
		fmt.Fprintf(os.Stdout, "Failed to execute: %s\n", err.Error())
	}

	return exitCode
}
<commit_msg>fix typo<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/kmagai/fugo/cmd/subcmd"
	"github.com/mitchellh/cli"
)

func run(args []string) int {
	style := &subcmd.Style{
		UI: &cli.ColoredUi{
			InfoColor:  cli.UiColorBlue,
			ErrorColor: cli.UiColorRed,
			Ui: &cli.BasicUi{
				Writer:      os.Stdout,
				ErrorWriter: os.Stderr,
				Reader:      os.Stdin,
			},
		}}

	return runCustom(args, commands(style))
}

func runCustom(args []string, comm map[string]cli.CommandFactory) int {
	// treat version as if it were a subcmd
	for _, arg := range args {
		if arg == "-v" || arg == "-version" || arg == "--version" {
			newArgs := make([]string, len(args)+1)
			newArgs[0] = "version"
			copy(newArgs[1:], args)
			args = newArgs
			break
		}
	}

	// default action to check
	if len(args) == 0 {
		checkArg := make([]string, 1)
		checkArg[0] = "check"
		args = checkArg
	}

	cli := &cli.CLI{
		Args:       args,
		Commands:   comm,
		Version:    Version,
		HelpFunc:   cli.BasicHelpFunc(Name),
		HelpWriter: os.Stdout,
	}

	exitCode, err := cli.Run()
	if err != nil {
		fmt.Fprintf(os.Stdout, "Failed to execute: %s\n", err.Error())
	}

	return exitCode
}
<|endoftext|>


<commit_before>package cmd

import (
	"flag"
	"fmt"
	"io"
	"os"
	"strings"
	"text/tabwriter"
	"text/template"

	"github.com/codegangsta/cli"
	"github.com/micro/go-micro/broker"
	"github.com/micro/go-micro/client"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/server"
	"github.com/micro/go-micro/transport"
)

var (
	Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "server_name",
			EnvVar: "MICRO_SERVER_NAME",
			Usage:  "Name of the server. go.micro.srv.example",
		},
		cli.StringFlag{
			Name:   "server_version",
			EnvVar: "MICRO_SERVER_VERSION",
			Usage:  "Version of the server. 1.1.0",
		},
		cli.StringFlag{
			Name:   "server_id",
			EnvVar: "MICRO_SERVER_ID",
			Usage:  "Id of the server. Auto-generated if not specified",
		},
		cli.StringFlag{
			Name:   "server_address",
			EnvVar: "MICRO_SERVER_ADDRESS",
			Value:  ":0",
			Usage:  "Bind address for the server. 127.0.0.1:8080",
		},
		cli.StringFlag{
			Name:   "server_advertise",
			EnvVar: "MICRO_SERVER_ADVERTISE",
			Usage:  "Used instead of the server_address when registering with discovery. 127.0.0.1:8080",
		},
		cli.StringSliceFlag{
			Name:   "server_metadata",
			EnvVar: "MICRO_SERVER_METADATA",
			Value:  &cli.StringSlice{},
			Usage:  "A list of key-value pairs defining metadata. version=1.0.0",
		},
		cli.StringFlag{
			Name:   "broker",
			EnvVar: "MICRO_BROKER",
			Value:  "http",
			Usage:  "Broker for pub/sub. http, nats, rabbitmq",
		},
		cli.StringFlag{
			Name:   "broker_address",
			EnvVar: "MICRO_BROKER_ADDRESS",
			Usage:  "Comma-separated list of broker addresses",
		},
		cli.StringFlag{
			Name:   "registry",
			EnvVar: "MICRO_REGISTRY",
			Value:  "consul",
			Usage:  "Registry for discovery. memory, consul, etcd, kubernetes",
		},
		cli.StringFlag{
			Name:   "registry_address",
			EnvVar: "MICRO_REGISTRY_ADDRESS",
			Usage:  "Comma-separated list of registry addresses",
		},
		cli.StringFlag{
			Name:   "transport",
			EnvVar: "MICRO_TRANSPORT",
			Value:  "http",
			Usage:  "Transport mechanism used; http, rabbitmq, nats",
		},
		cli.StringFlag{
			Name:   "transport_address",
			EnvVar: "MICRO_TRANSPORT_ADDRESS",
			Usage:  "Comma-separated list of transport addresses",
		},

		// logging flags
		cli.BoolFlag{
			Name:  "logtostderr",
			Usage: "log to standard error instead of files",
		},
		cli.BoolFlag{
			Name:  "alsologtostderr",
			Usage: "log to standard error as well as files",
		},
		cli.StringFlag{
			Name:  "log_dir",
			Usage: "log files will be written to this directory instead of the default temporary directory",
		},
		cli.StringFlag{
			Name:  "stderrthreshold",
			Usage: "logs at or above this threshold go to stderr",
		},
		cli.StringFlag{
			Name:  "v",
			Usage: "log level for V logs",
		},
		cli.StringFlag{
			Name:  "vmodule",
			Usage: "comma-separated list of pattern=N settings for file-filtered logging",
		},
		cli.StringFlag{
			Name:  "log_backtrace_at",
			Usage: "when logging hits line file:N, emit a stack trace",
		},
	}

	Brokers = map[string]func([]string, ...broker.Option) broker.Broker{
		"http": broker.NewBroker,
	}

	Registries = map[string]func([]string, ...registry.Option) registry.Registry{
		"consul": registry.NewRegistry,
	}

	Transports = map[string]func([]string, ...transport.Option) transport.Transport{
		"http": transport.NewTransport,
	}
)

func Setup(c *cli.Context) error {
	os.Args = os.Args[:1]

	flag.Set("logtostderr", fmt.Sprintf("%v", c.Bool("logtostderr")))
	flag.Set("alsologtostderr", fmt.Sprintf("%v", c.Bool("alsologtostderr")))
	flag.Set("stderrthreshold", c.String("stderrthreshold"))
	flag.Set("log_backtrace_at", c.String("log_backtrace_at"))
	flag.Set("log_dir", c.String("log_dir"))
	flag.Set("vmodule", c.String("vmodule"))
	flag.Set("v", c.String("v"))

	flag.Parse()

	if b, ok := Brokers[c.String("broker")]; ok {
		broker.DefaultBroker = b(strings.Split(c.String("broker_address"), ","))
	}

	if r, ok := Registries[c.String("registry")]; ok {
		registry.DefaultRegistry = r(strings.Split(c.String("registry_address"), ","))
	}

	if t, ok := Transports[c.String("transport")]; ok {
		transport.DefaultTransport = t(strings.Split(c.String("transport_address"), ","))
	}

	metadata := make(map[string]string)
	for _, d := range c.StringSlice("server_metadata") {
		var key, val string
		parts := strings.Split(d, "=")
		key = parts[0]
		if len(parts) > 1 {
			val = strings.Join(parts[1:], "=")
		}
		metadata[key] = val
	}

	server.DefaultServer = server.NewServer(
		server.Name(c.String("server_name")),
		server.Version(c.String("server_version")),
		server.Id(c.String("server_id")),
		server.Address(c.String("server_address")),
		server.Advertise(c.String("server_advertise")),
		server.Metadata(metadata),
	)

	client.DefaultClient = client.NewClient()

	return nil
}

func Init() {
	cli.AppHelpTemplate = `
GLOBAL OPTIONS:
   {{range .Flags}}{{.}}
   {{end}}
`

	cli.HelpPrinter = func(writer io.Writer, templ string, data interface{}) {
		w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
		t := template.Must(template.New("help").Parse(templ))
		err := t.Execute(w, data)
		if err != nil {
			panic(err)
		}
		w.Flush()
		os.Exit(2)
	}

	app := cli.NewApp()
	app.HideVersion = true
	app.Usage = "a go micro app"
	app.Action = func(c *cli.Context) {}
	app.Before = Setup
	app.Flags = Flags
	app.RunAndExitOnError()
}
<commit_msg>Add a ping function<commit_after>package cmd

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"os"
	"runtime"
	"strings"
	"text/tabwriter"
	"text/template"
	"time"

	"github.com/codegangsta/cli"
	"github.com/micro/go-micro/broker"
	"github.com/micro/go-micro/client"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/server"
	"github.com/micro/go-micro/transport"
	"github.com/pborman/uuid"
)

var (
	Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "server_name",
			EnvVar: "MICRO_SERVER_NAME",
			Usage:  "Name of the server. go.micro.srv.example",
		},
		cli.StringFlag{
			Name:   "server_version",
			EnvVar: "MICRO_SERVER_VERSION",
			Usage:  "Version of the server. 1.1.0",
		},
		cli.StringFlag{
			Name:   "server_id",
			EnvVar: "MICRO_SERVER_ID",
			Usage:  "Id of the server. Auto-generated if not specified",
		},
		cli.StringFlag{
			Name:   "server_address",
			EnvVar: "MICRO_SERVER_ADDRESS",
			Value:  ":0",
			Usage:  "Bind address for the server. 127.0.0.1:8080",
		},
		cli.StringFlag{
			Name:   "server_advertise",
			EnvVar: "MICRO_SERVER_ADVERTISE",
			Usage:  "Used instead of the server_address when registering with discovery. 127.0.0.1:8080",
		},
		cli.StringSliceFlag{
			Name:   "server_metadata",
			EnvVar: "MICRO_SERVER_METADATA",
			Value:  &cli.StringSlice{},
			Usage:  "A list of key-value pairs defining metadata. version=1.0.0",
		},
		cli.StringFlag{
			Name:   "broker",
			EnvVar: "MICRO_BROKER",
			Value:  "http",
			Usage:  "Broker for pub/sub. http, nats, rabbitmq",
		},
		cli.StringFlag{
			Name:   "broker_address",
			EnvVar: "MICRO_BROKER_ADDRESS",
			Usage:  "Comma-separated list of broker addresses",
		},
		cli.StringFlag{
			Name:   "registry",
			EnvVar: "MICRO_REGISTRY",
			Value:  "consul",
			Usage:  "Registry for discovery. memory, consul, etcd, kubernetes",
		},
		cli.StringFlag{
			Name:   "registry_address",
			EnvVar: "MICRO_REGISTRY_ADDRESS",
			Usage:  "Comma-separated list of registry addresses",
		},
		cli.StringFlag{
			Name:   "transport",
			EnvVar: "MICRO_TRANSPORT",
			Value:  "http",
			Usage:  "Transport mechanism used; http, rabbitmq, nats",
		},
		cli.StringFlag{
			Name:   "transport_address",
			EnvVar: "MICRO_TRANSPORT_ADDRESS",
			Usage:  "Comma-separated list of transport addresses",
		},

		cli.BoolFlag{
			Name:   "disable_ping",
			EnvVar: "MICRO_DISABLE_PING",
			Usage:  "Disable ping",
		},

		// logging flags
		cli.BoolFlag{
			Name:  "logtostderr",
			Usage: "log to standard error instead of files",
		},
		cli.BoolFlag{
			Name:  "alsologtostderr",
			Usage: "log to standard error as well as files",
		},
		cli.StringFlag{
			Name:  "log_dir",
			Usage: "log files will be written to this directory instead of the default temporary directory",
		},
		cli.StringFlag{
			Name:  "stderrthreshold",
			Usage: "logs at or above this threshold go to stderr",
		},
		cli.StringFlag{
			Name:  "v",
			Usage: "log level for V logs",
		},
		cli.StringFlag{
			Name:  "vmodule",
			Usage: "comma-separated list of pattern=N settings for file-filtered logging",
		},
		cli.StringFlag{
			Name:  "log_backtrace_at",
			Usage: "when logging hits line file:N, emit a stack trace",
		},
	}

	Brokers = map[string]func([]string, ...broker.Option) broker.Broker{
		"http": broker.NewBroker,
	}

	Registries = map[string]func([]string, ...registry.Option) registry.Registry{
		"consul": registry.NewRegistry,
	}

	Transports = map[string]func([]string, ...transport.Option) transport.Transport{
		"http": transport.NewTransport,
	}
)

func init() {
	rand.Seed(time.Now().Unix())
}

// ping informs micro-services about this thing
func ping() {
	type Ping struct {
		Id        string
		Timestamp int64
		Product   string
		Version   string
		Arch      string
		Os        string
	}

	p := Ping{
		Id:      uuid.NewUUID().String(),
		Product: "go-micro",
		Version: "latest",
		Arch:    runtime.GOARCH,
		Os:      runtime.GOOS,
	}

	buf := bytes.NewBuffer(nil)
	cl := &http.Client{}

	fn := func() {
		p.Timestamp = time.Now().Unix()
		b, err := json.Marshal(p)
		if err != nil {
			return
		}
		buf.Reset()
		buf.Write(b)
		rsp, err := cl.Post("https://micro-services.co/_ping", "application/json", buf)
		if err != nil {
			return
		}
		rsp.Body.Close()
	}

	// don't ping unless this thing has lived for 30 seconds
	time.Sleep(time.Second * 30)

	// only ping every 24 hours, be non invasive
	for {
		fn()
		time.Sleep(time.Hour * 24)
	}
}

func Setup(c *cli.Context) error {
	os.Args = os.Args[:1]

	flag.Set("logtostderr", fmt.Sprintf("%v", c.Bool("logtostderr")))
	flag.Set("alsologtostderr", fmt.Sprintf("%v", c.Bool("alsologtostderr")))
	flag.Set("stderrthreshold", c.String("stderrthreshold"))
	flag.Set("log_backtrace_at", c.String("log_backtrace_at"))
	flag.Set("log_dir", c.String("log_dir"))
	flag.Set("vmodule", c.String("vmodule"))
	flag.Set("v", c.String("v"))

	flag.Parse()

	if b, ok := Brokers[c.String("broker")]; ok {
		broker.DefaultBroker = b(strings.Split(c.String("broker_address"), ","))
	}

	if r, ok := Registries[c.String("registry")]; ok {
		registry.DefaultRegistry = r(strings.Split(c.String("registry_address"), ","))
	}

	if t, ok := Transports[c.String("transport")]; ok {
		transport.DefaultTransport = t(strings.Split(c.String("transport_address"), ","))
	}

	metadata := make(map[string]string)
	for _, d := range c.StringSlice("server_metadata") {
		var key, val string
		parts := strings.Split(d, "=")
		key = parts[0]
		if len(parts) > 1 {
			val = strings.Join(parts[1:], "=")
		}
		metadata[key] = val
	}

	server.DefaultServer = server.NewServer(
		server.Name(c.String("server_name")),
		server.Version(c.String("server_version")),
		server.Id(c.String("server_id")),
		server.Address(c.String("server_address")),
		server.Advertise(c.String("server_advertise")),
		server.Metadata(metadata),
	)

	client.DefaultClient = client.NewClient()

	if !c.Bool("disable_ping") {
		go ping()
	}

	return nil
}

func Init() {
	cli.AppHelpTemplate = `
GLOBAL OPTIONS:
   {{range .Flags}}{{.}}
   {{end}}
`

	cli.HelpPrinter = func(writer io.Writer, templ string, data interface{}) {
		w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
		t := template.Must(template.New("help").Parse(templ))
		err := t.Execute(w, data)
		if err != nil {
			panic(err)
		}
		w.Flush()
		os.Exit(2)
	}

	app := cli.NewApp()
	app.HideVersion = true
	app.Usage = "a go micro app"
	app.Action = func(c *cli.Context) {}
	app.Before = Setup
	app.Flags = Flags
	app.RunAndExitOnError()
}
<|endoftext|>


<commit_before>// Copyright © 2020 Karim Radhouani <medkarimrdi@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"context"
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/google/gnxi/utils/xpath"
	"github.com/openconfig/gnmi/proto/gnmi"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"google.golang.org/grpc/metadata"
	"google.golang.org/protobuf/encoding/prototext"
)

// getCmd represents the get command
var getCmd = &cobra.Command{
	Use:   "get",
	Short: "run gnmi get on targets",

	RunE: func(cmd *cobra.Command, args []string) error {
		ctx, cancel := context.WithCancel(context.Background())
		defer cancel()
		setupCloseHandler(cancel)
		var err error
		addresses := viper.GetStringSlice("address")
		if len(addresses) == 0 {
			fmt.Println("no grpc server address specified")
			return nil
		}
		username := viper.GetString("username")
		if username == "" {
			if username, err = readUsername(); err != nil {
				return err
			}
		}
		password := viper.GetString("password")
		if password == "" {
			if password, err = readPassword(); err != nil {
				return err
			}
		}
		encodingVal, ok := gnmi.Encoding_value[strings.Replace(strings.ToUpper(viper.GetString("encoding")), "-", "_", -1)]
		if !ok {
			return fmt.Errorf("invalid encoding type '%s'", viper.GetString("encoding"))
		}
		req := &gnmi.GetRequest{
			UseModels: make([]*gnmi.ModelData, 0),
			Path:      make([]*gnmi.Path, 0),
			Encoding:  gnmi.Encoding(encodingVal),
		}
		prefix := viper.GetString("get-prefix")
		if prefix != "" {
			gnmiPrefix, err := xpath.ToGNMIPath(prefix)
			if err != nil {
				return fmt.Errorf("prefix parse error: %v", err)
			}
			req.Prefix = gnmiPrefix
		}
		paths := viper.GetStringSlice("get-path")
		for _, p := range paths {
			gnmiPath, err := xpath.ToGNMIPath(strings.TrimSpace(p))
			if err != nil {
				return fmt.Errorf("path parse error: %v", err)
			}
			req.Path = append(req.Path, gnmiPath)
		}
		dataType := viper.GetString("get-type")
		if dataType != "" {
			dti, ok := gnmi.GetRequest_DataType_value[strings.ToUpper(dataType)]
			if !ok {
				return fmt.Errorf("unknown data type %s", dataType)
			}
			req.Type = gnmi.GetRequest_DataType(dti)
		}
		wg := new(sync.WaitGroup)
		wg.Add(len(addresses))
		lock := new(sync.Mutex)
		for _, addr := range addresses {
			go getRequest(ctx, req, addr, username, password, wg, lock)
		}
		wg.Wait()
		return nil
	},
}

func getRequest(ctx context.Context, req *gnmi.GetRequest, address, username, password string, wg *sync.WaitGroup, lock *sync.Mutex) {
	defer wg.Done()
	_, _, err := net.SplitHostPort(address)
	if err != nil {
		if strings.Contains(err.Error(), "missing port in address") {
			address = net.JoinHostPort(address, defaultGrpcPort)
		} else {
			logger.Printf("error parsing address '%s': %v", address, err)
			return
		}
	}
	conn, err := createGrpcConn(address)
	if err != nil {
		logger.Printf("connection to %s failed: %v", address, err)
		return
	}
	client := gnmi.NewGNMIClient(conn)
	nctx, cancel := context.WithCancel(ctx)
	defer cancel()
	nctx = metadata.AppendToOutgoingContext(nctx, "username", username, "password", password)
	xreq := req
	models := viper.GetStringSlice("get-model")
	if len(models) > 0 {
		spModels, unspModels, err := filterModels(nctx, client, models)
		if err != nil {
			logger.Printf("failed getting supported models from '%s': %v", address, err)
			return
		}
		if len(unspModels) > 0 {
			logger.Printf("found unsupported models for target '%s': %+v", address, unspModels)
		}
		for _, m := range spModels {
			xreq.UseModels = append(xreq.UseModels, m)
		}
	}
	logger.Printf("sending gNMI GetRequest: prefix='%v', path='%v', type='%v', encoding='%v', models='%+v', extension='%+v' to %s", xreq.Prefix, xreq.Path, xreq.Type, xreq.Encoding, xreq.UseModels, xreq.Extension, address)
	response, err := client.Get(nctx, xreq)
	if err != nil {
		logger.Printf("failed sending GetRequest to %s: %v", address, err)
		return
	}
	lock.Lock()
	printGetResponse(address, response)
	lock.Unlock()
}

func printGetResponse(address string, response *gnmi.GetResponse) {
	printPrefix := ""
	addresses := viper.GetStringSlice("address")
	if len(addresses) > 1 && !viper.GetBool("no-prefix") {
		printPrefix = fmt.Sprintf("[%s] ", address)
	}
	if viper.GetString("format") == "textproto" {
		fmt.Printf("%s\n", indent(printPrefix, prototext.Format(response)))
		return
	}
	for _, notif := range response.Notification {
		msg := new(msg)
		msg.Timestamp = notif.Timestamp
		t := time.Unix(0, notif.Timestamp)
		msg.Time = &t
		msg.Prefix = gnmiPathToXPath(notif.Prefix)
		for i, upd := range notif.Update {
			if upd.Val == nil {
				if viper.GetBool("debug") {
					logger.Printf("DEBUG: got a nil val update: %+v", upd)
				}
				continue
			}
			pathElems := make([]string, 0, len(upd.Path.Elem))
			for _, pElem := range upd.Path.Elem {
				pathElems = append(pathElems, pElem.GetName())
			}
			value, err := getValue(upd.Val)
			if err != nil {
				logger.Println(err)
			}
			msg.Updates = append(msg.Updates,
				&update{
					Path:   gnmiPathToXPath(upd.Path),
					Values: make(map[string]interface{}),
				})
			msg.Updates[i].Values[strings.Join(pathElems, "/")] = value
		}
		dMsg, err := json.MarshalIndent(msg, printPrefix, "  ")
		if err != nil {
			logger.Printf("error marshling json msg:%v", err)
			return
		}
		fmt.Printf("%s%s\n", printPrefix, string(dMsg))
	}
	fmt.Println()
}

func stringInList(s string, l []string) bool {
	for _, ss := range l {
		if s == ss {
			return true
		}
	}
	return false
}

func init() {
	rootCmd.AddCommand(getCmd)

	getCmd.Flags().StringSliceP("path", "", []string{""}, "get request paths")
	getCmd.MarkFlagRequired("path")
	getCmd.Flags().StringP("prefix", "", "", "get request prefix")
	getCmd.Flags().StringSliceP("model", "", []string{""}, "get request model(s)")
	getCmd.Flags().StringP("type", "t", "ALL", "the type of data that is requested from the target. one of: ALL, CONFIG, STATE, OPERATIONAL")
	viper.BindPFlag("get-path", getCmd.Flags().Lookup("path"))
	viper.BindPFlag("get-prefix", getCmd.Flags().Lookup("prefix"))
	viper.BindPFlag("get-model", getCmd.Flags().Lookup("model"))
	viper.BindPFlag("get-type", getCmd.Flags().Lookup("type"))
}
<commit_msg>add createGetRequest func<commit_after>// Copyright © 2020 Karim Radhouani <medkarimrdi@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/google/gnxi/utils/xpath"
	"github.com/openconfig/gnmi/proto/gnmi"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"google.golang.org/grpc/metadata"
	"google.golang.org/protobuf/encoding/prototext"
)

// getCmd represents the get command
var getCmd = &cobra.Command{
	Use:   "get",
	Short: "run gnmi get on targets",

	RunE: func(cmd *cobra.Command, args []string) error {
		ctx, cancel := context.WithCancel(context.Background())
		defer cancel()
		setupCloseHandler(cancel)
		targets, err := getTargets()
		if err != nil {
			return err
		}
		req, err := createGetRequest()
		if err != nil {
			return err
		}
		wg := new(sync.WaitGroup)
		wg.Add(len(targets))
		lock := new(sync.Mutex)
		for _, target := range targets {
			go getRequest(ctx, req, target, wg, lock)
		}
		wg.Wait()
		return nil
	},
}

func getRequest(ctx context.Context, req *gnmi.GetRequest, target *target, wg *sync.WaitGroup, lock *sync.Mutex) {
	defer wg.Done()
	conn, err := createGrpcConn(target.Address)
	if err != nil {
		logger.Printf("connection to %s failed: %v", target.Address, err)
		return
	}
	client := gnmi.NewGNMIClient(conn)
	nctx, cancel := context.WithCancel(ctx)
	defer cancel()
	nctx = metadata.AppendToOutgoingContext(nctx, "username", target.Username, "password", target.Password)
	xreq := req
	models := viper.GetStringSlice("get-model")
	if len(models) > 0 {
		spModels, unspModels, err := filterModels(nctx, client, models)
		if err != nil {
			logger.Printf("failed getting supported models from '%s': %v", target.Address, err)
			return
		}
		if len(unspModels) > 0 {
			logger.Printf("found unsupported models for target '%s': %+v", target.Address, unspModels)
		}
		for _, m := range spModels {
			xreq.UseModels = append(xreq.UseModels, m)
		}
	}
	logger.Printf("sending gNMI GetRequest: prefix='%v', path='%v', type='%v', encoding='%v', models='%+v', extension='%+v' to %s",
		xreq.Prefix, xreq.Path, xreq.Type, xreq.Encoding, xreq.UseModels, xreq.Extension, target.Address)
	response, err := client.Get(nctx, xreq)
	if err != nil {
		logger.Printf("failed sending GetRequest to %s: %v", target.Address, err)
		return
	}
	lock.Lock()
	printGetResponse(target.Address, response)
	lock.Unlock()
}

func printGetResponse(address string, response *gnmi.GetResponse) {
	printPrefix := ""
	addresses := viper.GetStringSlice("address")
	if len(addresses) > 1 && !viper.GetBool("no-prefix") {
		printPrefix = fmt.Sprintf("[%s] ", address)
	}
	if viper.GetString("format") == "textproto" {
		fmt.Printf("%s\n", indent(printPrefix, prototext.Format(response)))
		return
	}
	for _, notif := range response.Notification {
		msg := new(msg)
		msg.Source = address
		msg.Timestamp = notif.Timestamp
		t := time.Unix(0, notif.Timestamp)
		msg.Time = &t
		msg.Prefix = gnmiPathToXPath(notif.Prefix)
		for i, upd := range notif.Update {
			if upd.Val == nil {
				if viper.GetBool("debug") {
					logger.Printf("DEBUG: got a nil val update: %+v", upd)
				}
				continue
			}
			pathElems := make([]string, 0, len(upd.Path.Elem))
			for _, pElem := range upd.Path.Elem {
				pathElems = append(pathElems, pElem.GetName())
			}
			value, err := getValue(upd.Val)
			if err != nil {
				logger.Println(err)
			}
			msg.Updates = append(msg.Updates,
				&update{
					Path:   gnmiPathToXPath(upd.Path),
					Values: make(map[string]interface{}),
				})
			msg.Updates[i].Values[strings.Join(pathElems, "/")] = value
		}
		dMsg, err := json.MarshalIndent(msg, printPrefix, "  ")
		if err != nil {
			logger.Printf("error marshling json msg:%v", err)
			return
		}
		fmt.Printf("%s%s\n", printPrefix, string(dMsg))
	}
	fmt.Println()
}

func stringInList(s string, l []string) bool {
	for _, ss := range l {
		if s == ss {
			return true
		}
	}
	return false
}

func init() {
	rootCmd.AddCommand(getCmd)

	getCmd.Flags().StringSliceP("path", "", []string{""}, "get request paths")
	getCmd.MarkFlagRequired("path")
	getCmd.Flags().StringP("prefix", "", "", "get request prefix")
	getCmd.Flags().StringSliceP("model", "", []string{""}, "get request model(s)")
	getCmd.Flags().StringP("type", "t", "ALL", "the type of data that is requested from the target. one of: ALL, CONFIG, STATE, OPERATIONAL")
	viper.BindPFlag("get-path", getCmd.Flags().Lookup("path"))
	viper.BindPFlag("get-prefix", getCmd.Flags().Lookup("prefix"))
	viper.BindPFlag("get-model", getCmd.Flags().Lookup("model"))
	viper.BindPFlag("get-type", getCmd.Flags().Lookup("type"))
}

func createGetRequest() (*gnmi.GetRequest, error) {
	encodingVal, ok := gnmi.Encoding_value[strings.Replace(strings.ToUpper(viper.GetString("encoding")), "-", "_", -1)]
	if !ok {
		return nil, fmt.Errorf("invalid encoding type '%s'", viper.GetString("encoding"))
	}
	paths := viper.GetStringSlice("get-path")
	req := &gnmi.GetRequest{
		UseModels: make([]*gnmi.ModelData, 0),
		Path:      make([]*gnmi.Path, 0, len(paths)),
		Encoding:  gnmi.Encoding(encodingVal),
	}
	prefix := viper.GetString("get-prefix")
	if prefix != "" {
		gnmiPrefix, err := xpath.ToGNMIPath(prefix)
		if err != nil {
			return nil, fmt.Errorf("prefix parse error: %v", err)
		}
		req.Prefix = gnmiPrefix
	}
	dataType := viper.GetString("get-type")
	if dataType != "" {
		dti, ok := gnmi.GetRequest_DataType_value[strings.ToUpper(dataType)]
		if !ok {
			return nil, fmt.Errorf("unknown data type %s", dataType)
		}
		req.Type = gnmi.GetRequest_DataType(dti)
	}
	for _, p := range paths {
		gnmiPath, err := xpath.ToGNMIPath(strings.TrimSpace(p))
		if err != nil {
			return nil, fmt.Errorf("path parse error: %v", err)
		}
		req.Path = append(req.Path, gnmiPath)
	}
	return req, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"code.cloudfoundry.org/buildpackapplifecycle"
	"code.cloudfoundry.org/buildpackapplifecycle/buildpackrunner"
	"code.cloudfoundry.org/buildpackapplifecycle/credhub"
	"code.cloudfoundry.org/buildpackapplifecycle/databaseuri"
	"code.cloudfoundry.org/buildpackapplifecycle/platformoptions"
	"code.cloudfoundry.org/goshims/osshim"
)

func main() {
	config := buildpackapplifecycle.NewLifecycleBuilderConfig([]string{}, false, false)

	if err := config.Parse(os.Args[1:len(os.Args)]); err != nil {
		println(err.Error())
		os.Exit(1)
	}

	if err := config.Validate(); err != nil {
		println(err.Error())
		usage()
	}

	if platformOptions, err := platformoptions.Get(os.Getenv("VCAP_PLATFORM_OPTIONS")); err != nil {
		fmt.Fprintf(os.Stderr, "Invalid platform options: %v", err)
		os.Exit(3)
	} else if platformOptions != nil && platformOptions.CredhubURI != "" {
		err = credhub.New(&osshim.OsShim{}).InterpolateServiceRefs(platformOptions.CredhubURI)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unable to interpolate credhub refs: %v", err)
			os.Exit(4)
		}
	}

	if os.Getenv("VCAP_SERVICES") != "" {
		dbUri := databaseuri.New()
		if creds, err := dbUri.Credentials([]byte(os.Getenv("VCAP_SERVICES"))); err == nil {
			databaseUrl := dbUri.Uri(creds)
			if databaseUrl != "" {
				os.Setenv("DATABASE_URL", databaseUrl)
			}
		}
	}

	runner := buildpackrunner.New(&config)
	defer runner.CleanUp()

	_, err := runner.Run()
	if err != nil {
		println(err.Error())
		os.Exit(buildpackapplifecycle.ExitCodeFromError(err))
	}
}

func usage() {
	flag.PrintDefaults()
	os.Exit(1)
}
<commit_msg>builder: Move runner logic in main into its own function<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	"code.cloudfoundry.org/buildpackapplifecycle"
	"code.cloudfoundry.org/buildpackapplifecycle/buildpackrunner"
	"code.cloudfoundry.org/buildpackapplifecycle/credhub"
	"code.cloudfoundry.org/buildpackapplifecycle/databaseuri"
	"code.cloudfoundry.org/buildpackapplifecycle/platformoptions"
	"code.cloudfoundry.org/goshims/osshim"
)

func main() {
	config := buildpackapplifecycle.NewLifecycleBuilderConfig([]string{}, false, false)

	if err := config.Parse(os.Args[1:len(os.Args)]); err != nil {
		println(err.Error())
		os.Exit(1)
	}

	if err := config.Validate(); err != nil {
		println(err.Error())
		usage()
	}

	if platformOptions, err := platformoptions.Get(os.Getenv("VCAP_PLATFORM_OPTIONS")); err != nil {
		fmt.Fprintf(os.Stderr, "Invalid platform options: %v", err)
		os.Exit(3)
	} else if platformOptions != nil && platformOptions.CredhubURI != "" {
		err = credhub.New(&osshim.OsShim{}).InterpolateServiceRefs(platformOptions.CredhubURI)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unable to interpolate credhub refs: %v", err)
			os.Exit(4)
		}
	}

	if os.Getenv("VCAP_SERVICES") != "" {
		dbUri := databaseuri.New()
		if creds, err := dbUri.Credentials([]byte(os.Getenv("VCAP_SERVICES"))); err == nil {
			databaseUrl := dbUri.Uri(creds)
			if databaseUrl != "" {
				os.Setenv("DATABASE_URL", databaseUrl)
			}
		}
	}
	if err := execRunner(&config); err != nil {
		println(err.Error())
		os.Exit(buildpackapplifecycle.ExitCodeFromError(err))
	}
}

func execRunner(config *buildpackapplifecycle.LifecycleBuilderConfig) error {
	runner := buildpackrunner.New(config)
	defer runner.CleanUp()

	_, err := runner.Run()
	return err
}

func usage() {
	flag.PrintDefaults()
	os.Exit(1)
}
<|endoftext|>


<commit_before>package speaker

import (
	"math"
	"sync"
	"time"

	"github.com/faiface/pixel/audio"
	"github.com/hajimehoshi/oto"
	"github.com/pkg/errors"
)

var (
	streamerMu sync.Mutex
	streamer   audio.Streamer
	samples    [][2]float64
	buf        []byte

	playerMu sync.Mutex
	player   *oto.Player
)

// Init initializes audio playback through speaker. Must be called before using this package. The
// value of audio.SampleRate must be set (or left to the default) before calling this function.
//
// The bufferSize argument specifies the length of the speaker's buffer. On calling Update, speaker
// pulls this amount of data from the playing Streamers and starts playing this data. Bigger
// bufferSize means lower CPU usage and more reliable playback. Lower bufferSize means better
// responsiveness and less delay.
func Init(bufferSize time.Duration) error {
	playerMu.Lock()
	defer playerMu.Unlock()

	if player != nil {
		player.Close()
		player = nil
	}

	numSamples := int(math.Ceil(bufferSize.Seconds() * audio.SampleRate))
	numBytes := numSamples * 4

	var err error
	player, err = oto.NewPlayer(int(audio.SampleRate), 2, 2, numBytes)
	if err != nil {
		return errors.Wrap(err, "failed to initialize speaker")
	}

	samples = make([][2]float64, numSamples)
	buf = make([]byte, numBytes)

	return nil
}

// Play starts playing the provided Streamer through the speaker.
func Play(s audio.Streamer) {
	streamerMu.Lock()
	streamer = s
	streamerMu.Unlock()
}

// Update pulls new data from the playing Streamers and sends it to the speaker. Blocks until the
// data is sent and started playing.
//
// This function should be called at least once the duration of bufferSize given in Init, but it's
// recommended to call it more frequently to avoid glitches.
func Update() error {
	// pull data from the streamer, if any
	streamerMu.Lock()
	n := 0
	if streamer != nil {
		var ok bool
		n, ok = streamer.Stream(samples)
		if !ok {
			streamer = nil
		}
	}
	streamerMu.Unlock()

	playerMu.Lock()
	// convert samples to bytes
	for i := range samples[:n] {
		for c := range samples[i] {
			val := samples[i][c]
			if val < -1 {
				val = -1
			}
			if val > +1 {
				val = +1
			}
			valInt16 := int16(val * (1 << 15))
			low := byte(valInt16 % (1 << 8))
			high := byte(valInt16 / (1 << 8))
			buf[i*4+c*2+0] = low
			buf[i*4+c*2+1] = high
		}
	}
	// fill the rest with silence
	for i := n * 4; i < len(buf); i++ {
		buf[i] = 0
	}
	// send data to speaker
	player.Write(buf)
	playerMu.Unlock()

	return nil
}
<commit_msg>actually fix the race condition<commit_after>package speaker

import (
	"math"
	"sync"
	"time"

	"github.com/faiface/pixel/audio"
	"github.com/hajimehoshi/oto"
	"github.com/pkg/errors"
)

var (
	streamerMu sync.Mutex
	streamer   audio.Streamer
	samples    [][2]float64
	buf        []byte

	playerMu sync.Mutex
	player   *oto.Player
)

// Init initializes audio playback through speaker. Must be called before using this package. The
// value of audio.SampleRate must be set (or left to the default) before calling this function.
//
// The bufferSize argument specifies the length of the speaker's buffer. On calling Update, speaker
// pulls this amount of data from the playing Streamers and starts playing this data. Bigger
// bufferSize means lower CPU usage and more reliable playback. Lower bufferSize means better
// responsiveness and less delay.
func Init(bufferSize time.Duration) error {
	playerMu.Lock()
	defer playerMu.Unlock()

	if player != nil {
		player.Close()
		player = nil
	}

	numSamples := int(math.Ceil(bufferSize.Seconds() * audio.SampleRate))
	numBytes := numSamples * 4

	var err error
	player, err = oto.NewPlayer(int(audio.SampleRate), 2, 2, numBytes)
	if err != nil {
		return errors.Wrap(err, "failed to initialize speaker")
	}

	samples = make([][2]float64, numSamples)
	buf = make([]byte, numBytes)

	return nil
}

// Play starts playing the provided Streamer through the speaker.
func Play(s audio.Streamer) {
	streamerMu.Lock()
	streamer = s
	streamerMu.Unlock()
}

// Update pulls new data from the playing Streamers and sends it to the speaker. Blocks until the
// data is sent and started playing.
//
// This function should be called at least once the duration of bufferSize given in Init, but it's
// recommended to call it more frequently to avoid glitches.
func Update() error {
	streamerMu.Lock()
	// pull data from the streamer, if any
	n := 0
	if streamer != nil {
		var ok bool
		n, ok = streamer.Stream(samples)
		if !ok {
			streamer = nil
		}
	}
	// convert samples to bytes
	for i := range samples[:n] {
		for c := range samples[i] {
			val := samples[i][c]
			if val < -1 {
				val = -1
			}
			if val > +1 {
				val = +1
			}
			valInt16 := int16(val * (1 << 15))
			low := byte(valInt16 % (1 << 8))
			high := byte(valInt16 / (1 << 8))
			buf[i*4+c*2+0] = low
			buf[i*4+c*2+1] = high
		}
	}
	// fill the rest with silence
	for i := n * 4; i < len(buf); i++ {
		buf[i] = 0
	}
	streamerMu.Unlock()

	// send data to speaker
	playerMu.Lock()
	player.Write(buf)
	playerMu.Unlock()

	return nil
}
<|endoftext|>


<commit_before>// run

// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Test that selects do not consume undue memory.

package main

import "runtime"

func sender(c chan int, n int) {
	for i := 0; i < n; i++ {
		c <- 1
	}
}

func receiver(c, dummy chan int, n int) {
	for i := 0; i < n; i++ {
		select {
		case <-c:
			// nothing
		case <-dummy:
			panic("dummy")
		}
	}
}

func main() {
	runtime.MemProfileRate = 0

	c := make(chan int)
	dummy := make(chan int)

	// warm up
	go sender(c, 100000)
	receiver(c, dummy, 100000)
	runtime.GC()
	memstats := new(runtime.MemStats)
	runtime.ReadMemStats(memstats)
	alloc := memstats.Alloc

	// second time shouldn't increase footprint by much
	go sender(c, 100000)
	receiver(c, dummy, 100000)
	runtime.GC()
	runtime.ReadMemStats(memstats)

	if memstats.Alloc-alloc > 1e5 {
		println("BUG: too much memory for 100,000 selects:", memstats.Alloc-alloc)
	}
}
<commit_msg>test: raise the allocation threshold for chan/select2.go failure<commit_after>// run

// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Test that selects do not consume undue memory.

package main

import "runtime"

func sender(c chan int, n int) {
	for i := 0; i < n; i++ {
		c <- 1
	}
}

func receiver(c, dummy chan int, n int) {
	for i := 0; i < n; i++ {
		select {
		case <-c:
			// nothing
		case <-dummy:
			panic("dummy")
		}
	}
}

func main() {
	runtime.MemProfileRate = 0

	c := make(chan int)
	dummy := make(chan int)

	// warm up
	go sender(c, 100000)
	receiver(c, dummy, 100000)
	runtime.GC()
	memstats := new(runtime.MemStats)
	runtime.ReadMemStats(memstats)
	alloc := memstats.Alloc

	// second time shouldn't increase footprint by much
	go sender(c, 100000)
	receiver(c, dummy, 100000)
	runtime.GC()
	runtime.ReadMemStats(memstats)

	if memstats.Alloc-alloc > 1.1e5 {
		println("BUG: too much memory for 100,000 selects:", memstats.Alloc-alloc)
	}
}
<|endoftext|>


<commit_before>package mailfull

import (
	"bufio"
	"os"
	"path/filepath"
)

// CatchAllUser represents a CatchAllUser.
type CatchAllUser struct {
	name string
}

// NewCatchAllUser creates a new CatchAllUser instance.
func NewCatchAllUser(name string) (*CatchAllUser, error) {
	if !validCatchAllUserName(name) {
		return nil, ErrInvalidCatchAllUserName
	}

	cu := &CatchAllUser{
		name: name,
	}

	return cu, nil
}

// Name returns name.
func (cu *CatchAllUser) Name() string {
	return cu.name
}

// CatchAllUser returns a CatchAllUser that the input name has.
func (r *Repository) CatchAllUser(domainName string) (*CatchAllUser, error) {
	domain, err := r.Domain(domainName)
	if err != nil {
		return nil, err
	}
	if domain == nil {
		return nil, ErrDomainNotExist
	}

	file, err := os.Open(filepath.Join(r.DirMailDataPath, domainName, FileNameCatchAllUser))
	if err != nil {
		return nil, err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	scanner.Scan()

	name := scanner.Text()

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	if name == "" {
		return nil, nil
	}

	catchAllUser, err := NewCatchAllUser(name)
	if err != nil {
		return nil, err
	}

	return catchAllUser, nil
}
<commit_msg>Implement set/unset a CatchAllUser<commit_after>package mailfull

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
)

// CatchAllUser represents a CatchAllUser.
type CatchAllUser struct {
	name string
}

// NewCatchAllUser creates a new CatchAllUser instance.
func NewCatchAllUser(name string) (*CatchAllUser, error) {
	if !validCatchAllUserName(name) {
		return nil, ErrInvalidCatchAllUserName
	}

	cu := &CatchAllUser{
		name: name,
	}

	return cu, nil
}

// Name returns name.
func (cu *CatchAllUser) Name() string {
	return cu.name
}

// CatchAllUser returns a CatchAllUser that the input name has.
func (r *Repository) CatchAllUser(domainName string) (*CatchAllUser, error) {
	domain, err := r.Domain(domainName)
	if err != nil {
		return nil, err
	}
	if domain == nil {
		return nil, ErrDomainNotExist
	}

	file, err := os.Open(filepath.Join(r.DirMailDataPath, domainName, FileNameCatchAllUser))
	if err != nil {
		return nil, err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	scanner.Scan()

	name := scanner.Text()

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	if name == "" {
		return nil, nil
	}

	catchAllUser, err := NewCatchAllUser(name)
	if err != nil {
		return nil, err
	}

	return catchAllUser, nil
}

// CatchAllUserSet sets a CatchAllUser to the input Domain.
func (r *Repository) CatchAllUserSet(domainName string, catchAllUser *CatchAllUser) error {
	existUser, err := r.User(domainName, catchAllUser.Name())
	if err != nil {
		return err
	}
	if existUser == nil {
		return ErrUserNotExist
	}

	file, err := os.OpenFile(filepath.Join(r.DirMailDataPath, domainName, FileNameCatchAllUser), os.O_RDWR|os.O_TRUNC, 0666)
	if err != nil {
		return err
	}
	defer file.Close()

	if _, err := fmt.Fprintf(file, "%s\n", catchAllUser.Name()); err != nil {
		return err
	}

	return nil
}

// CatchAllUserUnset removes a CatchAllUser from the input Domain.
func (r *Repository) CatchAllUserUnset(domainName string) error {
	existDomain, err := r.Domain(domainName)
	if err != nil {
		return err
	}
	if existDomain == nil {
		return ErrDomainNotExist
	}

	file, err := os.OpenFile(filepath.Join(r.DirMailDataPath, domainName, FileNameCatchAllUser), os.O_RDWR|os.O_TRUNC, 0666)
	if err != nil {
		return err
	}
	file.Close()

	return nil
}
<|endoftext|>


<commit_before>package help

import (
	"fmt"
	"io"
	"os"
	"strings"
	"text/tabwriter"
	"text/template"
	"unicode/utf8"

	"github.com/cloudfoundry/cli/cf"
	"github.com/cloudfoundry/cli/cf/commandregistry"
	"github.com/cloudfoundry/cli/cf/configuration/pluginconfig"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/terminal"
)

type appPresenter struct {
	Name     string
	Usage    string
	Version  string
	Compiled string
	Commands []groupedCommands
}

type groupedCommands struct {
	Name             string
	CommandSubGroups [][]cmdPresenter
}

type cmdPresenter struct {
	Name        string
	Description string
}

func ShowHelp(writer io.Writer, helpTemplate string) {
	translatedTemplatedHelp := T(strings.Replace(helpTemplate, "{{", "[[", -1))
	translatedTemplatedHelp = strings.Replace(translatedTemplatedHelp, "[[", "{{", -1)

	showAppHelp(writer, translatedTemplatedHelp)
}

func showAppHelp(writer io.Writer, helpTemplate string) {
	presenter := newAppPresenter()

	w := tabwriter.NewWriter(writer, 0, 8, 1, '\t', 0)
	t := template.Must(template.New("help").Parse(helpTemplate))
	err := t.Execute(w, presenter)
	if err != nil {
		fmt.Println("error", err)
	}
	w.Flush()
}

func newAppPresenter() appPresenter {
	var presenter appPresenter

	pluginConfig := pluginconfig.NewPluginConfig(func(err error) {
		//fail silently when running help
	})
	plugins := pluginConfig.Plugins()

	maxNameLen := commandregistry.Commands.MaxCommandNameLength()
	maxNameLen = maxPluginCommandNameLength(plugins, maxNameLen)

	presentCommand := func(commandName string) (presenter cmdPresenter) {
		cmd := commandregistry.Commands.FindCommand(commandName)
		presenter.Name = cmd.MetaData().Name
		padding := strings.Repeat(" ", maxNameLen-utf8.RuneCountInString(presenter.Name))
		presenter.Name = presenter.Name + padding
		presenter.Description = cmd.MetaData().Description
		return
	}

	presentPluginCommands := func() []cmdPresenter {
		var presenters []cmdPresenter
		var pluginPresenter cmdPresenter

		for _, pluginMetadata := range plugins {
			for _, cmd := range pluginMetadata.Commands {
				pluginPresenter.Name = cmd.Name

				padding := strings.Repeat(" ", maxNameLen-utf8.RuneCountInString(pluginPresenter.Name))
				pluginPresenter.Name = pluginPresenter.Name + padding
				pluginPresenter.Description = cmd.HelpText
				presenters = append(presenters, pluginPresenter)
			}
		}

		return presenters
	}

	presenter.Name = os.Args[0]
	presenter.Usage = T("A command line tool to interact with Cloud Foundry")
	presenter.Version = cf.Version + "-" + cf.BuiltOnDate
	presenter.Commands = []groupedCommands{
		{
			Name: T("GETTING STARTED"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("help"),
					presentCommand("version"),
					presentCommand("login"),
					presentCommand("logout"),
					presentCommand("passwd"),
					presentCommand("target"),
				}, {
					presentCommand("api"),
					presentCommand("auth"),
				},
			},
		}, {
			Name: T("APPS"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("apps"),
					presentCommand("app"),
				}, {
					presentCommand("push"),
					presentCommand("scale"),
					presentCommand("delete"),
					presentCommand("rename"),
				}, {
					presentCommand("start"),
					presentCommand("stop"),
					presentCommand("restart"),
					presentCommand("restage"),
					presentCommand("restart-app-instance"),
				}, {
					presentCommand("events"),
					presentCommand("files"),
					presentCommand("logs"),
				}, {
					presentCommand("env"),
					presentCommand("set-env"),
					presentCommand("unset-env"),
				}, {
					presentCommand("stacks"),
					presentCommand("stack"),
				}, {
					presentCommand("copy-source"),
				}, {
					presentCommand("create-app-manifest"),
				}, {
					presentCommand("get-health-check"),
					presentCommand("set-health-check"),
					presentCommand("enable-ssh"),
					presentCommand("disable-ssh"),
					presentCommand("ssh-enabled"),
					presentCommand("ssh"),
				},
			},
		}, {
			Name: T("SERVICES"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("marketplace"),
					presentCommand("services"),
					presentCommand("service"),
				}, {
					presentCommand("create-service"),
					presentCommand("update-service"),
					presentCommand("delete-service"),
					presentCommand("rename-service"),
				}, {
					presentCommand("create-service-key"),
					presentCommand("service-keys"),
					presentCommand("service-key"),
					presentCommand("delete-service-key"),
				}, {
					presentCommand("bind-service"),
					presentCommand("unbind-service"),
				}, {
					presentCommand("bind-route-service"),
					presentCommand("unbind-route-service"),
				}, {
					presentCommand("create-user-provided-service"),
					presentCommand("update-user-provided-service"),
				},
			},
		}, {
			Name: T("ORGS"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("orgs"),
					presentCommand("org"),
				}, {
					presentCommand("create-org"),
					presentCommand("delete-org"),
					presentCommand("rename-org"),
				},
			},
		}, {
			Name: T("SPACES"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("spaces"),
					presentCommand("space"),
				}, {
					presentCommand("create-space"),
					presentCommand("delete-space"),
					presentCommand("rename-space"),
				}, {
					presentCommand("allow-space-ssh"),
					presentCommand("disallow-space-ssh"),
					presentCommand("space-ssh-allowed"),
				},
			},
		}, {
			Name: T("DOMAINS"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("domains"),
					presentCommand("create-domain"),
					presentCommand("delete-domain"),
					presentCommand("create-shared-domain"),
					presentCommand("delete-shared-domain"),
				},
				{
					presentCommand("router-groups"),
				},
			},
		}, {
			Name: T("ROUTES"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("routes"),
					presentCommand("create-route"),
					presentCommand("check-route"),
					presentCommand("map-route"),
					presentCommand("unmap-route"),
					presentCommand("delete-route"),
					presentCommand("delete-orphaned-routes"),
				},
			},
		}, {
			Name: T("BUILDPACKS"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("buildpacks"),
					presentCommand("create-buildpack"),
					presentCommand("update-buildpack"),
					presentCommand("rename-buildpack"),
					presentCommand("delete-buildpack"),
				},
			},
		}, {
			Name: T("USER ADMIN"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("create-user"),
					presentCommand("delete-user"),
				}, {
					presentCommand("org-users"),
					presentCommand("set-org-role"),
					presentCommand("unset-org-role"),
				}, {
					presentCommand("space-users"),
					presentCommand("set-space-role"),
					presentCommand("unset-space-role"),
				},
			},
		}, {
			Name: T("ORG ADMIN"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("quotas"),
					presentCommand("quota"),
					presentCommand("set-quota"),
				}, {
					presentCommand("create-quota"),
					presentCommand("delete-quota"),
					presentCommand("update-quota"),
				},
				{
					presentCommand("share-private-domain"),
					presentCommand("unshare-private-domain"),
				},
			},
		}, {
			Name: T("SPACE ADMIN"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("space-quotas"),
					presentCommand("space-quota"),
					presentCommand("create-space-quota"),
					presentCommand("update-space-quota"),
					presentCommand("delete-space-quota"),
					presentCommand("set-space-quota"),
					presentCommand("unset-space-quota"),
				},
			},
		}, {
			Name: T("SERVICE ADMIN"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("service-auth-tokens"),
					presentCommand("create-service-auth-token"),
					presentCommand("update-service-auth-token"),
					presentCommand("delete-service-auth-token"),
				}, {
					presentCommand("service-brokers"),
					presentCommand("create-service-broker"),
					presentCommand("update-service-broker"),
					presentCommand("delete-service-broker"),
					presentCommand("rename-service-broker"),
				}, {
					presentCommand("migrate-service-instances"),
					presentCommand("purge-service-offering"),
					presentCommand("purge-service-instance"),
				}, {
					presentCommand("service-access"),
					presentCommand("enable-service-access"),
					presentCommand("disable-service-access"),
				},
			},
		}, {
			Name: T("SECURITY GROUP"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("security-group"),
					presentCommand("security-groups"),
					presentCommand("create-security-group"),
					presentCommand("update-security-group"),
					presentCommand("delete-security-group"),
					presentCommand("bind-security-group"),
					presentCommand("unbind-security-group"),
				}, {
					presentCommand("bind-staging-security-group"),
					presentCommand("staging-security-groups"),
					presentCommand("unbind-staging-security-group"),
				}, {
					presentCommand("bind-running-security-group"),
					presentCommand("running-security-groups"),
					presentCommand("unbind-running-security-group"),
				},
			},
		}, {
			Name: T("ENVIRONMENT VARIABLE GROUPS"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("running-environment-variable-group"),
					presentCommand("staging-environment-variable-group"),
					presentCommand("set-staging-environment-variable-group"),
					presentCommand("set-running-environment-variable-group"),
				},
			},
		},
		{
			Name: T("FEATURE FLAGS"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("feature-flags"),
					presentCommand("feature-flag"),
					presentCommand("enable-feature-flag"),
					presentCommand("disable-feature-flag"),
				},
			},
		}, {
			Name: T("ADVANCED"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("curl"),
					presentCommand("config"),
					presentCommand("oauth-token"),
					presentCommand("ssh-code"),
				},
			},
		}, {
			Name: T("ADD/REMOVE PLUGIN REPOSITORY"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("add-plugin-repo"),
					presentCommand("remove-plugin-repo"),
					presentCommand("list-plugin-repos"),
					presentCommand("repo-plugins"),
				},
			},
		}, {
			Name: T("ADD/REMOVE PLUGIN"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("plugins"),
					presentCommand("install-plugin"),
					presentCommand("uninstall-plugin"),
				},
			},
		}, {
			Name: T("INSTALLED PLUGIN COMMANDS"),
			CommandSubGroups: [][]cmdPresenter{
				presentPluginCommands(),
			},
		},
	}

	return presenter
}

func (p appPresenter) Title(name string) string {
	return terminal.HeaderColor(name)
}

func (c groupedCommands) SubTitle(name string) string {
	return terminal.HeaderColor(name + ":")
}

func maxPluginCommandNameLength(plugins map[string]pluginconfig.PluginMetadata, maxNameLen int) int {
	for _, pluginMetadata := range plugins {
		for _, cmd := range pluginMetadata.Commands {
			if nameLen := utf8.RuneCountInString(cmd.Name); nameLen > maxNameLen {
				maxNameLen = nameLen
			}
		}
	}

	return maxNameLen
}
<commit_msg>Explicitly ignore failed Flush<commit_after>package help

import (
	"fmt"
	"io"
	"os"
	"strings"
	"text/tabwriter"
	"text/template"
	"unicode/utf8"

	"github.com/cloudfoundry/cli/cf"
	"github.com/cloudfoundry/cli/cf/commandregistry"
	"github.com/cloudfoundry/cli/cf/configuration/pluginconfig"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/terminal"
)

type appPresenter struct {
	Name     string
	Usage    string
	Version  string
	Compiled string
	Commands []groupedCommands
}

type groupedCommands struct {
	Name             string
	CommandSubGroups [][]cmdPresenter
}

type cmdPresenter struct {
	Name        string
	Description string
}

func ShowHelp(writer io.Writer, helpTemplate string) {
	translatedTemplatedHelp := T(strings.Replace(helpTemplate, "{{", "[[", -1))
	translatedTemplatedHelp = strings.Replace(translatedTemplatedHelp, "[[", "{{", -1)

	showAppHelp(writer, translatedTemplatedHelp)
}

func showAppHelp(writer io.Writer, helpTemplate string) {
	presenter := newAppPresenter()

	w := tabwriter.NewWriter(writer, 0, 8, 1, '\t', 0)
	t := template.Must(template.New("help").Parse(helpTemplate))
	err := t.Execute(w, presenter)
	if err != nil {
		fmt.Println("error", err)
	}
	_ = w.Flush()
}

func newAppPresenter() appPresenter {
	var presenter appPresenter

	pluginConfig := pluginconfig.NewPluginConfig(func(err error) {
		//fail silently when running help
	})
	plugins := pluginConfig.Plugins()

	maxNameLen := commandregistry.Commands.MaxCommandNameLength()
	maxNameLen = maxPluginCommandNameLength(plugins, maxNameLen)

	presentCommand := func(commandName string) (presenter cmdPresenter) {
		cmd := commandregistry.Commands.FindCommand(commandName)
		presenter.Name = cmd.MetaData().Name
		padding := strings.Repeat(" ", maxNameLen-utf8.RuneCountInString(presenter.Name))
		presenter.Name = presenter.Name + padding
		presenter.Description = cmd.MetaData().Description
		return
	}

	presentPluginCommands := func() []cmdPresenter {
		var presenters []cmdPresenter
		var pluginPresenter cmdPresenter

		for _, pluginMetadata := range plugins {
			for _, cmd := range pluginMetadata.Commands {
				pluginPresenter.Name = cmd.Name

				padding := strings.Repeat(" ", maxNameLen-utf8.RuneCountInString(pluginPresenter.Name))
				pluginPresenter.Name = pluginPresenter.Name + padding
				pluginPresenter.Description = cmd.HelpText
				presenters = append(presenters, pluginPresenter)
			}
		}

		return presenters
	}

	presenter.Name = os.Args[0]
	presenter.Usage = T("A command line tool to interact with Cloud Foundry")
	presenter.Version = cf.Version + "-" + cf.BuiltOnDate
	presenter.Commands = []groupedCommands{
		{
			Name: T("GETTING STARTED"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("help"),
					presentCommand("version"),
					presentCommand("login"),
					presentCommand("logout"),
					presentCommand("passwd"),
					presentCommand("target"),
				}, {
					presentCommand("api"),
					presentCommand("auth"),
				},
			},
		}, {
			Name: T("APPS"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("apps"),
					presentCommand("app"),
				}, {
					presentCommand("push"),
					presentCommand("scale"),
					presentCommand("delete"),
					presentCommand("rename"),
				}, {
					presentCommand("start"),
					presentCommand("stop"),
					presentCommand("restart"),
					presentCommand("restage"),
					presentCommand("restart-app-instance"),
				}, {
					presentCommand("events"),
					presentCommand("files"),
					presentCommand("logs"),
				}, {
					presentCommand("env"),
					presentCommand("set-env"),
					presentCommand("unset-env"),
				}, {
					presentCommand("stacks"),
					presentCommand("stack"),
				}, {
					presentCommand("copy-source"),
				}, {
					presentCommand("create-app-manifest"),
				}, {
					presentCommand("get-health-check"),
					presentCommand("set-health-check"),
					presentCommand("enable-ssh"),
					presentCommand("disable-ssh"),
					presentCommand("ssh-enabled"),
					presentCommand("ssh"),
				},
			},
		}, {
			Name: T("SERVICES"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("marketplace"),
					presentCommand("services"),
					presentCommand("service"),
				}, {
					presentCommand("create-service"),
					presentCommand("update-service"),
					presentCommand("delete-service"),
					presentCommand("rename-service"),
				}, {
					presentCommand("create-service-key"),
					presentCommand("service-keys"),
					presentCommand("service-key"),
					presentCommand("delete-service-key"),
				}, {
					presentCommand("bind-service"),
					presentCommand("unbind-service"),
				}, {
					presentCommand("bind-route-service"),
					presentCommand("unbind-route-service"),
				}, {
					presentCommand("create-user-provided-service"),
					presentCommand("update-user-provided-service"),
				},
			},
		}, {
			Name: T("ORGS"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("orgs"),
					presentCommand("org"),
				}, {
					presentCommand("create-org"),
					presentCommand("delete-org"),
					presentCommand("rename-org"),
				},
			},
		}, {
			Name: T("SPACES"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("spaces"),
					presentCommand("space"),
				}, {
					presentCommand("create-space"),
					presentCommand("delete-space"),
					presentCommand("rename-space"),
				}, {
					presentCommand("allow-space-ssh"),
					presentCommand("disallow-space-ssh"),
					presentCommand("space-ssh-allowed"),
				},
			},
		}, {
			Name: T("DOMAINS"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("domains"),
					presentCommand("create-domain"),
					presentCommand("delete-domain"),
					presentCommand("create-shared-domain"),
					presentCommand("delete-shared-domain"),
				},
				{
					presentCommand("router-groups"),
				},
			},
		}, {
			Name: T("ROUTES"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("routes"),
					presentCommand("create-route"),
					presentCommand("check-route"),
					presentCommand("map-route"),
					presentCommand("unmap-route"),
					presentCommand("delete-route"),
					presentCommand("delete-orphaned-routes"),
				},
			},
		}, {
			Name: T("BUILDPACKS"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("buildpacks"),
					presentCommand("create-buildpack"),
					presentCommand("update-buildpack"),
					presentCommand("rename-buildpack"),
					presentCommand("delete-buildpack"),
				},
			},
		}, {
			Name: T("USER ADMIN"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("create-user"),
					presentCommand("delete-user"),
				}, {
					presentCommand("org-users"),
					presentCommand("set-org-role"),
					presentCommand("unset-org-role"),
				}, {
					presentCommand("space-users"),
					presentCommand("set-space-role"),
					presentCommand("unset-space-role"),
				},
			},
		}, {
			Name: T("ORG ADMIN"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("quotas"),
					presentCommand("quota"),
					presentCommand("set-quota"),
				}, {
					presentCommand("create-quota"),
					presentCommand("delete-quota"),
					presentCommand("update-quota"),
				},
				{
					presentCommand("share-private-domain"),
					presentCommand("unshare-private-domain"),
				},
			},
		}, {
			Name: T("SPACE ADMIN"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("space-quotas"),
					presentCommand("space-quota"),
					presentCommand("create-space-quota"),
					presentCommand("update-space-quota"),
					presentCommand("delete-space-quota"),
					presentCommand("set-space-quota"),
					presentCommand("unset-space-quota"),
				},
			},
		}, {
			Name: T("SERVICE ADMIN"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("service-auth-tokens"),
					presentCommand("create-service-auth-token"),
					presentCommand("update-service-auth-token"),
					presentCommand("delete-service-auth-token"),
				}, {
					presentCommand("service-brokers"),
					presentCommand("create-service-broker"),
					presentCommand("update-service-broker"),
					presentCommand("delete-service-broker"),
					presentCommand("rename-service-broker"),
				}, {
					presentCommand("migrate-service-instances"),
					presentCommand("purge-service-offering"),
					presentCommand("purge-service-instance"),
				}, {
					presentCommand("service-access"),
					presentCommand("enable-service-access"),
					presentCommand("disable-service-access"),
				},
			},
		}, {
			Name: T("SECURITY GROUP"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("security-group"),
					presentCommand("security-groups"),
					presentCommand("create-security-group"),
					presentCommand("update-security-group"),
					presentCommand("delete-security-group"),
					presentCommand("bind-security-group"),
					presentCommand("unbind-security-group"),
				}, {
					presentCommand("bind-staging-security-group"),
					presentCommand("staging-security-groups"),
					presentCommand("unbind-staging-security-group"),
				}, {
					presentCommand("bind-running-security-group"),
					presentCommand("running-security-groups"),
					presentCommand("unbind-running-security-group"),
				},
			},
		}, {
			Name: T("ENVIRONMENT VARIABLE GROUPS"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("running-environment-variable-group"),
					presentCommand("staging-environment-variable-group"),
					presentCommand("set-staging-environment-variable-group"),
					presentCommand("set-running-environment-variable-group"),
				},
			},
		},
		{
			Name: T("FEATURE FLAGS"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("feature-flags"),
					presentCommand("feature-flag"),
					presentCommand("enable-feature-flag"),
					presentCommand("disable-feature-flag"),
				},
			},
		}, {
			Name: T("ADVANCED"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("curl"),
					presentCommand("config"),
					presentCommand("oauth-token"),
					presentCommand("ssh-code"),
				},
			},
		}, {
			Name: T("ADD/REMOVE PLUGIN REPOSITORY"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("add-plugin-repo"),
					presentCommand("remove-plugin-repo"),
					presentCommand("list-plugin-repos"),
					presentCommand("repo-plugins"),
				},
			},
		}, {
			Name: T("ADD/REMOVE PLUGIN"),
			CommandSubGroups: [][]cmdPresenter{
				{
					presentCommand("plugins"),
					presentCommand("install-plugin"),
					presentCommand("uninstall-plugin"),
				},
			},
		}, {
			Name: T("INSTALLED PLUGIN COMMANDS"),
			CommandSubGroups: [][]cmdPresenter{
				presentPluginCommands(),
			},
		},
	}

	return presenter
}

func (p appPresenter) Title(name string) string {
	return terminal.HeaderColor(name)
}

func (c groupedCommands) SubTitle(name string) string {
	return terminal.HeaderColor(name + ":")
}

func maxPluginCommandNameLength(plugins map[string]pluginconfig.PluginMetadata, maxNameLen int) int {
	for _, pluginMetadata := range plugins {
		for _, cmd := range pluginMetadata.Commands {
			if nameLen := utf8.RuneCountInString(cmd.Name); nameLen > maxNameLen {
				maxNameLen = nameLen
			}
		}
	}

	return maxNameLen
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha256"
	"encoding/hex"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"bytes"
	"sort"
)

func cmdGet(c *cli.Context) {

	verbose = c.GlobalBool("verbose")

	path := c.Args().Get(0)
	key := c.String("key")
	files := strings.Split(c.String("file"), ",")
	envs := strings.Split(c.String("env"), ",")

	if len(path) < 1 {
		path = "." // default to current directory
	}

	if len(key) < 1 {
		key = strings.ToLower(filepath.Base(files[0]))
	}
	hash := checksum(files, envs)
	fullPath := filepath.Join(CachePath, strings.Join([]string{key, hash}, "-")) + ".tar.gz"

	// get cache if exists
	if _, err := os.Stat(fullPath); err == nil {
		printInfo("Fetching cache '%s'. Please, wait...", key)

		args := []string{
			"-xzf",
			fullPath,
			"-C",
			path,
		}

		err := exec.Command("tar", args...).Run()
		checkError(err)
	}
}

func cmdSet(c *cli.Context) {

	verbose = c.GlobalBool("verbose")

	path := c.Args().Get(0)
	key := c.String("key")
	files := strings.Split(c.String("file"), ",")
	envs := strings.Split(c.String("env"), ",")

	if len(path) < 1 {
		printFatal("Path to content is not provided as an argument.")
	}

	if _, err := os.Stat(path); os.IsNotExist(err) {
		printFatal("Content '%s' does not exist.", path)
	}

	if _, err := os.Stat(CachePath); os.IsNotExist(err) {
		if os.MkdirAll(CachePath, dirMode) != nil {
			printFatal("Couldn't create cache directory. "+
				"Is the %s directory writable?", CachePath)
		}
	}

	if len(key) < 1 {
		key = strings.ToLower(filepath.Base(files[0]))
	}

	hash := checksum(files, envs)
	fullPath := filepath.Join(CachePath, strings.Join([]string{key, hash}, "-")) + ".tar.gz"

	// cache contents only if it doesn't exist already
	if _, err := os.Stat(fullPath); os.IsNotExist(err) {
		printInfo("Caching '%s'. Please, wait...", key)

		args := []string{
			"-czf",
			fullPath,
			path,
		}

		err := exec.Command("tar", args...).Run()
		checkError(err)
	}
	go clean(key)
}

/*
 * Calculates SHA256 checksum of an array of files and/or env vars
 *
 * Returns The String checksum of all files
 */
func checksum(files []string, envs []string) string {
	if len(files[0]) < 1 {
		printFatal("At least one dependency file is not provided.")
	}

	var buffer bytes.Buffer

	// first go thru files
	sort.Strings(files)
	for _, file := range files {
		if _, err := os.Stat(file); os.IsNotExist(err) {
			printFatal("Dependency file '%s' does not exist.", file)
		}
		contents, err := ioutil.ReadFile(file)
		checkError(err)
		buffer.Write(contents)
	}

	// then check any environment variables
	sort.Strings(envs)
	for _, v := range envs {
		envVar := os.Getenv(v)
		if len(envVar) > 0 {
			buffer.WriteString(envVar)
		}
	}

	hasher := sha256.New()
	hasher.Write(buffer.Bytes())
	return hex.EncodeToString(hasher.Sum(nil))
}

/*
 * Cleans old caches
 */
func clean(key string) {
	files := fileSorter(CachePath, "^"+key+"-")

	for index, file := range files {
		if index+1 > CacheKeep {
			os.Remove(file.Path)
		}
	}
}
<commit_msg>Minor update to the error text<commit_after>package main

import (
	"crypto/sha256"
	"encoding/hex"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"bytes"
	"sort"
)

func cmdGet(c *cli.Context) {

	verbose = c.GlobalBool("verbose")

	path := c.Args().Get(0)
	key := c.String("key")
	files := strings.Split(c.String("file"), ",")
	envs := strings.Split(c.String("env"), ",")

	if len(path) < 1 {
		path = "." // default to current directory
	}

	if len(key) < 1 {
		key = strings.ToLower(filepath.Base(files[0]))
	}
	hash := checksum(files, envs)
	fullPath := filepath.Join(CachePath, strings.Join([]string{key, hash}, "-")) + ".tar.gz"

	// get cache if exists
	if _, err := os.Stat(fullPath); err == nil {
		printInfo("Fetching cache '%s'. Please, wait...", key)

		args := []string{
			"-xzf",
			fullPath,
			"-C",
			path,
		}

		err := exec.Command("tar", args...).Run()
		checkError(err)
	}
}

func cmdSet(c *cli.Context) {

	verbose = c.GlobalBool("verbose")

	path := c.Args().Get(0)
	key := c.String("key")
	files := strings.Split(c.String("file"), ",")
	envs := strings.Split(c.String("env"), ",")

	if len(path) < 1 {
		printFatal("Path to content is not provided as an argument.")
	}

	if _, err := os.Stat(path); os.IsNotExist(err) {
		printFatal("Content '%s' does not exist.", path)
	}

	if _, err := os.Stat(CachePath); os.IsNotExist(err) {
		if os.MkdirAll(CachePath, dirMode) != nil {
			printFatal("Couldn't create cache directory. "+
				"Is the %s directory writable?", CachePath)
		}
	}

	if len(key) < 1 {
		key = strings.ToLower(filepath.Base(files[0]))
	}

	hash := checksum(files, envs)
	fullPath := filepath.Join(CachePath, strings.Join([]string{key, hash}, "-")) + ".tar.gz"

	// cache contents only if it doesn't exist already
	if _, err := os.Stat(fullPath); os.IsNotExist(err) {
		printInfo("Caching '%s'. Please, wait...", key)

		args := []string{
			"-czf",
			fullPath,
			path,
		}

		err := exec.Command("tar", args...).Run()
		checkError(err)
	}
	go clean(key)
}

/*
 * Calculates SHA256 checksum of an array of files and/or env vars
 *
 * Returns The String checksum of all files
 */
func checksum(files []string, envs []string) string {
	if len(files[0]) < 1 {
		printFatal("At least one dependency file is required.")
	}

	var buffer bytes.Buffer

	// first go thru files
	sort.Strings(files)
	for _, file := range files {
		if _, err := os.Stat(file); os.IsNotExist(err) {
			printFatal("Dependency file '%s' does not exist.", file)
		}
		contents, err := ioutil.ReadFile(file)
		checkError(err)
		buffer.Write(contents)
	}

	// then check any environment variables
	sort.Strings(envs)
	for _, v := range envs {
		envVar := os.Getenv(v)
		if len(envVar) > 0 {
			buffer.WriteString(envVar)
		}
	}

	hasher := sha256.New()
	hasher.Write(buffer.Bytes())
	return hex.EncodeToString(hasher.Sum(nil))
}

/*
 * Cleans old caches
 */
func clean(key string) {
	files := fileSorter(CachePath, "^"+key+"-")

	for index, file := range files {
		if index+1 > CacheKeep {
			os.Remove(file.Path)
		}
	}
}
<|endoftext|>


<commit_before>package cli

import (
	"flag"
	"fmt"
	"io/ioutil"
	"sort"
	"strings"
)

// Command is a subcommand for a cli.App.
type Command struct {
	// The name of the command
	Name string
	// short name of the command. Typically one character (deprecated, use `Aliases`)
	ShortName string
	// A list of aliases for the command
	Aliases []string
	// A short description of the usage of this command
	Usage string
	// Custom text to show on USAGE section of help
	UsageText string
	// A longer explanation of how the command works
	Description string
	// A short description of the arguments of this command
	ArgsUsage string
	// The category the command is part of
	Category string
	// The function to call when checking for bash command completions
	BashComplete BashCompleteFunc
	// An action to execute before any sub-subcommands are run, but after the context is ready
	// If a non-nil error is returned, no sub-subcommands are run
	Before BeforeFunc
	// An action to execute after any subcommands are run, but after the subcommand has finished
	// It is run even if Action() panics
	After AfterFunc
	// The function to call when this command is invoked
	Action interface{}
	// TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind
	// of deprecation period has passed, maybe?

	// Execute this function if a usage error occurs.
	OnUsageError OnUsageErrorFunc
	// List of child commands
	Subcommands Commands
	// List of flags to parse
	Flags []Flag
	// Treat all flags as normal arguments if true
	SkipFlagParsing bool
	// Skip argument reordering which attempts to move flags before arguments,
	// but only works if all flags appear after all arguments. This behavior was
	// removed n version 2 since it only works under specific conditions so we
	// backport here by exposing it as an option for compatibility.
	SkipArgReorder bool
	// Boolean to hide built-in help command
	HideHelp bool
	// Boolean to hide this command from help or completion
	Hidden bool
	// Boolean to enable short-option handling so user can combine several
	// single-character bool arguments into one
	// i.e. foobar -o -v -> foobar -ov
	UseShortOptionHandling bool

	// Full name of command for help, defaults to full command name, including parent commands.
	HelpName        string
	commandNamePath []string

	// CustomHelpTemplate the text template for the command help topic.
	// cli.go uses text/template to render templates. You can
	// render custom help text by setting this variable.
	CustomHelpTemplate string
}

type CommandsByName []Command

func (c CommandsByName) Len() int {
	return len(c)
}

func (c CommandsByName) Less(i, j int) bool {
	return lexicographicLess(c[i].Name, c[j].Name)
}

func (c CommandsByName) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}

// FullName returns the full name of the command.
// For subcommands this ensures that parent commands are part of the command path
func (c Command) FullName() string {
	if c.commandNamePath == nil {
		return c.Name
	}
	return strings.Join(c.commandNamePath, " ")
}

// Commands is a slice of Command
type Commands []Command

// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
func (c Command) Run(ctx *Context) (err error) {
	if len(c.Subcommands) > 0 {
		return c.startApp(ctx)
	}

	if !c.HideHelp && (HelpFlag != BoolFlag{}) {
		// append help to flags
		c.Flags = append(
			c.Flags,
			HelpFlag,
		)
	}

	set, err := c.parseFlags(ctx.Args().Tail())

	context := NewContext(ctx.App, set, ctx)
	context.Command = c
	if checkCommandCompletions(context, c.Name) {
		return nil
	}

	if err != nil {
		if c.OnUsageError != nil {
			err := c.OnUsageError(context, err, false)
			context.App.handleExitCoder(context, err)
			return err
		}
		fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
		fmt.Fprintln(context.App.Writer)
		ShowCommandHelp(context, c.Name)
		return err
	}

	if checkCommandHelp(context, c.Name) {
		return nil
	}

	if c.After != nil {
		defer func() {
			afterErr := c.After(context)
			if afterErr != nil {
				context.App.handleExitCoder(context, err)
				if err != nil {
					err = NewMultiError(err, afterErr)
				} else {
					err = afterErr
				}
			}
		}()
	}

	if c.Before != nil {
		err = c.Before(context)
		if err != nil {
			ShowCommandHelp(context, c.Name)
			context.App.handleExitCoder(context, err)
			return err
		}
	}

	if c.Action == nil {
		c.Action = helpSubcommand.Action
	}

	err = HandleAction(c.Action, context)

	if err != nil {
		context.App.handleExitCoder(context, err)
	}
	return err
}

func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) {
	set, err := flagSet(c.Name, c.Flags)
	if err != nil {
		return nil, err
	}
	set.SetOutput(ioutil.Discard)

	if c.SkipFlagParsing {
		return set, set.Parse(append([]string{"--"}, args...))
	}

	if !c.SkipArgReorder {
		args = reorderArgs(args)
	}

PARSE:
	err = set.Parse(args)
	if err != nil {
		if c.UseShortOptionHandling {
			// To enable short-option handling (e.g., "-it" vs "-i -t")
			// we have to iteratively catch parsing errors.  This way
			// we achieve LR parsing without transforming any arguments.
			// Otherwise, there is no way we can discriminate combined
			// short options from common arguments that should be left
			// untouched.
			errStr := err.Error()
			trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: ")
			if errStr == trimmed {
				return nil, err
			}
			// regenerate the initial args with the split short opts
			newArgs := Args{}
			for i, arg := range args {
				if arg != trimmed {
					newArgs = append(newArgs, arg)
					continue
				}
				shortOpts := translateShortOptions(set, Args{trimmed})
				if len(shortOpts) == 1 {
					return nil, err
				}
				// add each short option and all remaining arguments
				newArgs = append(newArgs, shortOpts...)
				newArgs = append(newArgs, args[i+1:]...)
				args = newArgs
				// now reset the flagset parse again
				set, err = flagSet(c.Name, c.Flags)
				if err != nil {
					return nil, err
				}
				set.SetOutput(ioutil.Discard)
				goto PARSE
			}
		}
		return nil, err
	}

	err = normalizeFlags(c.Flags, set)
	if err != nil {
		return nil, err
	}

	return set, nil
}

// reorderArgs moves all flags before arguments as this is what flag expects
func reorderArgs(args []string) []string {
	var nonflags, flags []string

	readFlagValue := false
	for i, arg := range args {
		if arg == "--" {
			nonflags = append(nonflags, args[i:]...)
			break
		}

		if readFlagValue && !strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") {
			readFlagValue = false
			flags = append(flags, arg)
			continue
		}
		readFlagValue = false

		if arg != "-" && strings.HasPrefix(arg, "-") {
			flags = append(flags, arg)

			readFlagValue = !strings.Contains(arg, "=")
		} else {
			nonflags = append(nonflags, arg)
		}
	}

	return append(flags, nonflags...)
}

func translateShortOptions(set *flag.FlagSet, flagArgs Args) []string {
	allCharsFlags := func(s string) bool {
		for i := range s {
			f := set.Lookup(string(s[i]))
			if f == nil {
				return false
			}
		}
		return true
	}

	// separate combined flags
	var flagArgsSeparated []string
	for _, flagArg := range flagArgs {
		if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) > 2 {
			if !allCharsFlags(flagArg[1:]) {
				flagArgsSeparated = append(flagArgsSeparated, flagArg)
				continue
			}
			for _, flagChar := range flagArg[1:] {
				flagArgsSeparated = append(flagArgsSeparated, "-"+string(flagChar))
			}
		} else {
			flagArgsSeparated = append(flagArgsSeparated, flagArg)
		}
	}
	return flagArgsSeparated
}

// Names returns the names including short names and aliases.
func (c Command) Names() []string {
	names := []string{c.Name}

	if c.ShortName != "" {
		names = append(names, c.ShortName)
	}

	return append(names, c.Aliases...)
}

// HasName returns true if Command.Name or Command.ShortName matches given name
func (c Command) HasName(name string) bool {
	for _, n := range c.Names() {
		if n == name {
			return true
		}
	}
	return false
}

func (c Command) startApp(ctx *Context) error {
	app := NewApp()
	app.Metadata = ctx.App.Metadata
	// set the name and usage
	app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
	if c.HelpName == "" {
		app.HelpName = c.HelpName
	} else {
		app.HelpName = app.Name
	}

	app.Usage = c.Usage
	app.Description = c.Description
	app.ArgsUsage = c.ArgsUsage

	// set CommandNotFound
	app.CommandNotFound = ctx.App.CommandNotFound
	app.CustomAppHelpTemplate = c.CustomHelpTemplate

	// set the flags and commands
	app.Commands = c.Subcommands
	app.Flags = c.Flags
	app.HideHelp = c.HideHelp

	app.Version = ctx.App.Version
	app.HideVersion = ctx.App.HideVersion
	app.Compiled = ctx.App.Compiled
	app.Author = ctx.App.Author
	app.Email = ctx.App.Email
	app.Writer = ctx.App.Writer
	app.ErrWriter = ctx.App.ErrWriter

	app.categories = CommandCategories{}
	for _, command := range c.Subcommands {
		app.categories = app.categories.AddCommand(command.Category, command)
	}

	sort.Sort(app.categories)

	// bash completion
	app.EnableBashCompletion = ctx.App.EnableBashCompletion
	if c.BashComplete != nil {
		app.BashComplete = c.BashComplete
	}

	// set the actions
	app.Before = c.Before
	app.After = c.After
	if c.Action != nil {
		app.Action = c.Action
	} else {
		app.Action = helpSubcommand.Action
	}
	app.OnUsageError = c.OnUsageError

	for index, cc := range app.Commands {
		app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
	}

	return app.RunAsSubcommand(ctx)
}

// VisibleFlags returns a slice of the Flags with Hidden=false
func (c Command) VisibleFlags() []Flag {
	return visibleFlags(c.Flags)
}
<commit_msg>reduce diff<commit_after>package cli

import (
	"flag"
	"fmt"
	"io/ioutil"
	"sort"
	"strings"
)

// Command is a subcommand for a cli.App.
type Command struct {
	// The name of the command
	Name string
	// short name of the command. Typically one character (deprecated, use `Aliases`)
	ShortName string
	// A list of aliases for the command
	Aliases []string
	// A short description of the usage of this command
	Usage string
	// Custom text to show on USAGE section of help
	UsageText string
	// A longer explanation of how the command works
	Description string
	// A short description of the arguments of this command
	ArgsUsage string
	// The category the command is part of
	Category string
	// The function to call when checking for bash command completions
	BashComplete BashCompleteFunc
	// An action to execute before any sub-subcommands are run, but after the context is ready
	// If a non-nil error is returned, no sub-subcommands are run
	Before BeforeFunc
	// An action to execute after any subcommands are run, but after the subcommand has finished
	// It is run even if Action() panics
	After AfterFunc
	// The function to call when this command is invoked
	Action interface{}
	// TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind
	// of deprecation period has passed, maybe?

	// Execute this function if a usage error occurs.
	OnUsageError OnUsageErrorFunc
	// List of child commands
	Subcommands Commands
	// List of flags to parse
	Flags []Flag
	// Treat all flags as normal arguments if true
	SkipFlagParsing bool
	// Skip argument reordering which attempts to move flags before arguments,
	// but only works if all flags appear after all arguments. This behavior was
	// removed n version 2 since it only works under specific conditions so we
	// backport here by exposing it as an option for compatibility.
	SkipArgReorder bool
	// Boolean to hide built-in help command
	HideHelp bool
	// Boolean to hide this command from help or completion
	Hidden bool
	// Boolean to enable short-option handling so user can combine several
	// single-character bool arguments into one
	// i.e. foobar -o -v -> foobar -ov
	UseShortOptionHandling bool

	// Full name of command for help, defaults to full command name, including parent commands.
	HelpName        string
	commandNamePath []string

	// CustomHelpTemplate the text template for the command help topic.
	// cli.go uses text/template to render templates. You can
	// render custom help text by setting this variable.
	CustomHelpTemplate string
}

type CommandsByName []Command

func (c CommandsByName) Len() int {
	return len(c)
}

func (c CommandsByName) Less(i, j int) bool {
	return lexicographicLess(c[i].Name, c[j].Name)
}

func (c CommandsByName) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}

// FullName returns the full name of the command.
// For subcommands this ensures that parent commands are part of the command path
func (c Command) FullName() string {
	if c.commandNamePath == nil {
		return c.Name
	}
	return strings.Join(c.commandNamePath, " ")
}

// Commands is a slice of Command
type Commands []Command

// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
func (c Command) Run(ctx *Context) (err error) {
	if len(c.Subcommands) > 0 {
		return c.startApp(ctx)
	}

	if !c.HideHelp && (HelpFlag != BoolFlag{}) {
		// append help to flags
		c.Flags = append(
			c.Flags,
			HelpFlag,
		)
	}

	set, err := c.parseFlags(ctx.Args().Tail())

	context := NewContext(ctx.App, set, ctx)
	context.Command = c
	if checkCommandCompletions(context, c.Name) {
		return nil
	}

	if err != nil {
		if c.OnUsageError != nil {
			err := c.OnUsageError(context, err, false)
			context.App.handleExitCoder(context, err)
			return err
		}
		fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
		fmt.Fprintln(context.App.Writer)
		ShowCommandHelp(context, c.Name)
		return err
	}

	if checkCommandHelp(context, c.Name) {
		return nil
	}

	if c.After != nil {
		defer func() {
			afterErr := c.After(context)
			if afterErr != nil {
				context.App.handleExitCoder(context, err)
				if err != nil {
					err = NewMultiError(err, afterErr)
				} else {
					err = afterErr
				}
			}
		}()
	}

	if c.Before != nil {
		err = c.Before(context)
		if err != nil {
			ShowCommandHelp(context, c.Name)
			context.App.handleExitCoder(context, err)
			return err
		}
	}

	if c.Action == nil {
		c.Action = helpSubcommand.Action
	}

	err = HandleAction(c.Action, context)

	if err != nil {
		context.App.handleExitCoder(context, err)
	}
	return err
}

func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) {
	set, err := flagSet(c.Name, c.Flags)
	if err != nil {
		return nil, err
	}
	set.SetOutput(ioutil.Discard)

	if c.SkipFlagParsing {
		return set, set.Parse(append([]string{"--"}, args...))
	}

	if !c.SkipArgReorder {
		args = reorderArgs(args)
	}

PARSE:
	err = set.Parse(args)
	if err != nil {
		if c.UseShortOptionHandling {
			// To enable short-option handling (e.g., "-it" vs "-i -t")
			// we have to iteratively catch parsing errors.  This way
			// we achieve LR parsing without transforming any arguments.
			// Otherwise, there is no way we can discriminate combined
			// short options from common arguments that should be left
			// untouched.
			errStr := err.Error()
			trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: ")
			if errStr == trimmed {
				return nil, err
			}
			// regenerate the initial args with the split short opts
			newArgs := Args{}
			for i, arg := range args {
				if arg != trimmed {
					newArgs = append(newArgs, arg)
					continue
				}
				shortOpts := translateShortOptions(set, Args{trimmed})
				if len(shortOpts) == 1 {
					return nil, err
				}
				// add each short option and all remaining arguments
				newArgs = append(newArgs, shortOpts...)
				newArgs = append(newArgs, args[i+1:]...)
				args = newArgs
				// now reset the flagset parse again
				set, err = flagSet(c.Name, c.Flags)
				if err != nil {
					return nil, err
				}
				set.SetOutput(ioutil.Discard)
				goto PARSE
			}
		}
		return nil, err
	}

	err = normalizeFlags(c.Flags, set)
	if err != nil {
		return nil, err
	}

	return set, nil
}

// reorderArgs moves all flags before arguments as this is what flag expects
func reorderArgs(args []string) []string {
	var nonflags, flags []string

	readFlagValue := false
	for i, arg := range args {
		if arg == "--" {
			nonflags = append(nonflags, args[i:]...)
			break
		}

		if readFlagValue && !strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") {
			readFlagValue = false
			flags = append(flags, arg)
			continue
		}
		readFlagValue = false

		if arg != "-" && strings.HasPrefix(arg, "-") {
			flags = append(flags, arg)

			readFlagValue = !strings.Contains(arg, "=")
		} else {
			nonflags = append(nonflags, arg)
		}
	}

	return append(flags, nonflags...)
}

func translateShortOptions(set *flag.FlagSet, flagArgs Args) []string {
	allCharsFlags := func (s string) bool {
		for i := range s {
			f := set.Lookup(string(s[i]))
			if f == nil {
				return false
			}
		}
		return true
	}

	// separate combined flags
	var flagArgsSeparated []string
	for _, flagArg := range flagArgs {
		if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) > 2 {
			if !allCharsFlags(flagArg[1:]) {
				flagArgsSeparated = append(flagArgsSeparated, flagArg)
				continue
			}
			for _, flagChar := range flagArg[1:] {
				flagArgsSeparated = append(flagArgsSeparated, "-"+string(flagChar))
			}
		} else {
			flagArgsSeparated = append(flagArgsSeparated, flagArg)
		}
	}
	return flagArgsSeparated
}

// Names returns the names including short names and aliases.
func (c Command) Names() []string {
	names := []string{c.Name}

	if c.ShortName != "" {
		names = append(names, c.ShortName)
	}

	return append(names, c.Aliases...)
}

// HasName returns true if Command.Name or Command.ShortName matches given name
func (c Command) HasName(name string) bool {
	for _, n := range c.Names() {
		if n == name {
			return true
		}
	}
	return false
}

func (c Command) startApp(ctx *Context) error {
	app := NewApp()
	app.Metadata = ctx.App.Metadata
	// set the name and usage
	app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
	if c.HelpName == "" {
		app.HelpName = c.HelpName
	} else {
		app.HelpName = app.Name
	}

	app.Usage = c.Usage
	app.Description = c.Description
	app.ArgsUsage = c.ArgsUsage

	// set CommandNotFound
	app.CommandNotFound = ctx.App.CommandNotFound
	app.CustomAppHelpTemplate = c.CustomHelpTemplate

	// set the flags and commands
	app.Commands = c.Subcommands
	app.Flags = c.Flags
	app.HideHelp = c.HideHelp

	app.Version = ctx.App.Version
	app.HideVersion = ctx.App.HideVersion
	app.Compiled = ctx.App.Compiled
	app.Author = ctx.App.Author
	app.Email = ctx.App.Email
	app.Writer = ctx.App.Writer
	app.ErrWriter = ctx.App.ErrWriter

	app.categories = CommandCategories{}
	for _, command := range c.Subcommands {
		app.categories = app.categories.AddCommand(command.Category, command)
	}

	sort.Sort(app.categories)

	// bash completion
	app.EnableBashCompletion = ctx.App.EnableBashCompletion
	if c.BashComplete != nil {
		app.BashComplete = c.BashComplete
	}

	// set the actions
	app.Before = c.Before
	app.After = c.After
	if c.Action != nil {
		app.Action = c.Action
	} else {
		app.Action = helpSubcommand.Action
	}
	app.OnUsageError = c.OnUsageError

	for index, cc := range app.Commands {
		app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
	}

	return app.RunAsSubcommand(ctx)
}

// VisibleFlags returns a slice of the Flags with Hidden=false
func (c Command) VisibleFlags() []Flag {
	return visibleFlags(c.Flags)
}
<|endoftext|>