mirror of
https://github.com/octoleo/syncthing.git
synced 2025-01-03 15:17:25 +00:00
cmd/syncthing: Refactor cli subcommand to allow flags (#7454)
This commit is contained in:
parent
1814f4693d
commit
40fbdc87ce
@ -10,33 +10,39 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/AudriusButkevicius/recli"
|
"github.com/AudriusButkevicius/recli"
|
||||||
|
"github.com/alecthomas/kong"
|
||||||
"github.com/flynn-archive/go-shlex"
|
"github.com/flynn-archive/go-shlex"
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/lib/config"
|
"github.com/syncthing/syncthing/lib/config"
|
||||||
"github.com/syncthing/syncthing/lib/events"
|
"github.com/syncthing/syncthing/lib/events"
|
||||||
"github.com/syncthing/syncthing/lib/locations"
|
"github.com/syncthing/syncthing/lib/locations"
|
||||||
"github.com/syncthing/syncthing/lib/protocol"
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
"github.com/syncthing/syncthing/lib/svcutil"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CLI struct {
|
type preCli struct {
|
||||||
GUIAddress string `name:"gui-address" placeholder:"URL" help:"Override GUI address (e.g. \"http://192.0.2.42:8443\")"`
|
GUIAddress string `name:"gui-address"`
|
||||||
GUIAPIKey string `name:"gui-apikey" placeholder:"API-KEY" help:"Override GUI API key"`
|
GUIAPIKey string `name:"gui-apikey"`
|
||||||
HomeDir string `name:"home" placeholder:"PATH" help:"Set configuration and data directory"`
|
HomeDir string `name:"home"`
|
||||||
ConfDir string `name:"conf" placeholder:"PATH" help:"Set configuration directory (config and keys)"`
|
ConfDir string `name:"conf"`
|
||||||
Args []string `arg:"" optional:""`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CLI) Run() error {
|
func Run() error {
|
||||||
|
// This is somewhat a hack around a chicken and egg problem. We need to set
|
||||||
|
// the home directory and potentially other flags to know where the
|
||||||
|
// syncthing instance is running in order to get it's config ... which we
|
||||||
|
// then use to construct the actual CLI ... at which point it's too late to
|
||||||
|
// add flags there...
|
||||||
|
c := preCli{}
|
||||||
|
parseFlags(&c)
|
||||||
|
|
||||||
// Not set as default above because the strings can be really long.
|
// Not set as default above because the strings can be really long.
|
||||||
var err error
|
var err error
|
||||||
homeSet := c.HomeDir != ""
|
homeSet := c.HomeDir != ""
|
||||||
@ -50,8 +56,7 @@ func (c *CLI) Run() error {
|
|||||||
err = locations.SetBaseDir(locations.ConfigBaseDir, c.ConfDir)
|
err = locations.SetBaseDir(locations.ConfigBaseDir, c.ConfDir)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Command line options:", err)
|
return errors.Wrap(err, "Command line options:")
|
||||||
os.Exit(svcutil.ExitError.AsInt())
|
|
||||||
}
|
}
|
||||||
guiCfg := config.GUIConfiguration{
|
guiCfg := config.GUIConfiguration{
|
||||||
RawAddress: c.GUIAddress,
|
RawAddress: c.GUIAddress,
|
||||||
@ -136,15 +141,15 @@ func (c *CLI) Run() error {
|
|||||||
|
|
||||||
// Construct the actual CLI
|
// Construct the actual CLI
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Name = "syncthing cli"
|
|
||||||
app.HelpName = app.Name
|
|
||||||
app.Author = "The Syncthing Authors"
|
app.Author = "The Syncthing Authors"
|
||||||
app.Usage = "Syncthing command line interface"
|
|
||||||
app.Flags = fakeFlags
|
|
||||||
app.Metadata = map[string]interface{}{
|
app.Metadata = map[string]interface{}{
|
||||||
"client": client,
|
"client": client,
|
||||||
}
|
}
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{{
|
||||||
|
Name: "cli",
|
||||||
|
Usage: "Syncthing command line interface",
|
||||||
|
Flags: fakeFlags,
|
||||||
|
Subcommands: []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "config",
|
Name: "config",
|
||||||
HideHelp: true,
|
HideHelp: true,
|
||||||
@ -154,10 +159,8 @@ func (c *CLI) Run() error {
|
|||||||
showCommand,
|
showCommand,
|
||||||
operationCommand,
|
operationCommand,
|
||||||
errorsCommand,
|
errorsCommand,
|
||||||
}
|
},
|
||||||
|
}}
|
||||||
// It expects to be give os.Args which has argv[0] set to executable name, so fake it.
|
|
||||||
c.Args = append([]string{"cli"}, c.Args...)
|
|
||||||
|
|
||||||
tty := isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd())
|
tty := isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd())
|
||||||
if !tty {
|
if !tty {
|
||||||
@ -171,7 +174,7 @@ func (c *CLI) Run() error {
|
|||||||
if len(input) == 0 {
|
if len(input) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = app.Run(append(c.Args, input...))
|
err = app.Run(append(os.Args, input...))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -181,7 +184,7 @@ func (c *CLI) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = app.Run(c.Args)
|
err = app.Run(os.Args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -206,3 +209,28 @@ func (c *CLI) Run() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseFlags(c *preCli) error {
|
||||||
|
// kong only needs to parse the global arguments after "cli" and before the
|
||||||
|
// subcommand (if any).
|
||||||
|
if len(os.Args) <= 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
args := os.Args[2:]
|
||||||
|
for i := 0; i < len(args); i++ {
|
||||||
|
if !strings.HasPrefix(args[i], "--") {
|
||||||
|
args = args[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !strings.Contains(args[i], "=") {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We don't want kong to print anything nor os.Exit (e.g. on -h)
|
||||||
|
parser, err := kong.New(c, kong.Writers(ioutil.Discard, ioutil.Discard), kong.Exit(func(int) {}))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = parser.Parse(args)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@ -131,10 +131,11 @@ var (
|
|||||||
|
|
||||||
// The entrypoint struct is the main entry point for the command line parser. The
|
// The entrypoint struct is the main entry point for the command line parser. The
|
||||||
// commands and options here are top level commands to syncthing.
|
// commands and options here are top level commands to syncthing.
|
||||||
|
// Cli is just a placeholder for the help text (see main).
|
||||||
var entrypoint struct {
|
var entrypoint struct {
|
||||||
Serve serveOptions `cmd:"" help:"Run Syncthing"`
|
Serve serveOptions `cmd:"" help:"Run Syncthing"`
|
||||||
Decrypt decrypt.CLI `cmd:"" help:"Decrypt or verify an encrypted folder"`
|
Decrypt decrypt.CLI `cmd:"" help:"Decrypt or verify an encrypted folder"`
|
||||||
Cli cli.CLI `cmd:"" help:"Command line interface for Syncthing"`
|
Cli struct{} `cmd:"" help:"Command line interface for Syncthing"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// serveOptions are the options for the `syncthing serve` command.
|
// serveOptions are the options for the `syncthing serve` command.
|
||||||
@ -211,6 +212,17 @@ func defaultVars() kong.Vars {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// The "cli" subcommand uses a different command line parser, and e.g. help
|
||||||
|
// gets mangled when integrating it as a subcommand -> detect it here at the
|
||||||
|
// beginning.
|
||||||
|
if os.Args[1] == "cli" {
|
||||||
|
if err := cli.Run(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// First some massaging of the raw command line to fit the new model.
|
// First some massaging of the raw command line to fit the new model.
|
||||||
// Basically this means adding the default command at the front, and
|
// Basically this means adding the default command at the front, and
|
||||||
// converting -options to --options.
|
// converting -options to --options.
|
||||||
@ -248,10 +260,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func helpHandler(options kong.HelpOptions, ctx *kong.Context) error {
|
func helpHandler(options kong.HelpOptions, ctx *kong.Context) error {
|
||||||
// If we're looking for CLI help, pass the arguments down to the CLI library to print it's own help.
|
|
||||||
if ctx.Command() == "cli" {
|
|
||||||
return ctx.Run()
|
|
||||||
}
|
|
||||||
if err := kong.DefaultHelpPrinter(options, ctx); err != nil {
|
if err := kong.DefaultHelpPrinter(options, ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user