mirror of
https://github.com/onyx-and-iris/q3rcon.git
synced 2026-03-02 17:09:19 +00:00
Reimplement the CLI with flags first package.
CLI configuration may now be managed with env vars. improved CLI help output
This commit is contained in:
parent
0b9546ee0e
commit
fce6fa43fc
1
.gitignore
vendored
1
.gitignore
vendored
@ -24,6 +24,7 @@ go.work.sum
|
|||||||
|
|
||||||
# env file
|
# env file
|
||||||
.env
|
.env
|
||||||
|
.envrc
|
||||||
|
|
||||||
# Added by goreleaser init:
|
# Added by goreleaser init:
|
||||||
dist/
|
dist/
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -11,12 +12,41 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/charmbracelet/log"
|
"github.com/charmbracelet/log"
|
||||||
|
"github.com/peterbourgon/ff/v4"
|
||||||
|
"github.com/peterbourgon/ff/v4/ffhelp"
|
||||||
|
|
||||||
"github.com/onyx-and-iris/q3rcon"
|
"github.com/onyx-and-iris/q3rcon"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string // Version will be set at build time
|
var version string // Version will be set at build time
|
||||||
|
|
||||||
|
type Flags struct {
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
Rconpass string
|
||||||
|
Interactive bool
|
||||||
|
LogLevel string
|
||||||
|
Version bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Flags) Validate() error {
|
||||||
|
if f.Port < 1024 || f.Port > 65535 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"invalid port value, got: (%d) expected: in range 1024-65535",
|
||||||
|
f.Port,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(f.Rconpass) < 8 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"invalid rcon password, got: (%s) expected: at least 8 characters",
|
||||||
|
f.Rconpass,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var exitCode int
|
var exitCode int
|
||||||
|
|
||||||
@ -39,71 +69,67 @@ func main() {
|
|||||||
|
|
||||||
// run executes the main logic of the application and returns a cleanup function and an error if any.
|
// run executes the main logic of the application and returns a cleanup function and an error if any.
|
||||||
func run() (func(), error) {
|
func run() (func(), error) {
|
||||||
var (
|
var flags Flags
|
||||||
host string
|
|
||||||
port int
|
|
||||||
rconpass string
|
|
||||||
interactive bool
|
|
||||||
loglevel string
|
|
||||||
versionFlag bool
|
|
||||||
)
|
|
||||||
|
|
||||||
flag.StringVar(&host, "host", "localhost", "hostname of the gameserver")
|
fs := ff.NewFlagSet("q3rcon - A command-line RCON client for Quake 3 Arena")
|
||||||
flag.StringVar(&host, "H", "localhost", "hostname of the gameserver (shorthand)")
|
fs.StringVar(&flags.Host, 'H', "host", "localhost", "hostname of the gameserver")
|
||||||
flag.IntVar(&port, "port", 28960, "port on which the gameserver resides, default is 28960")
|
fs.IntVar(
|
||||||
flag.IntVar(
|
&flags.Port,
|
||||||
&port,
|
'p',
|
||||||
"p",
|
"port",
|
||||||
28960,
|
28960,
|
||||||
"port on which the gameserver resides, default is 28960 (shorthand)",
|
"port on which the gameserver resides, default is 28960",
|
||||||
)
|
)
|
||||||
flag.StringVar(&rconpass, "rconpass", os.Getenv("RCON_PASS"), "rcon password of the gameserver")
|
fs.StringVar(
|
||||||
flag.StringVar(
|
&flags.Rconpass,
|
||||||
&rconpass,
|
'r',
|
||||||
"r",
|
"rconpass",
|
||||||
os.Getenv("RCON_PASS"),
|
"",
|
||||||
"rcon password of the gameserver (shorthand)",
|
"rcon password of the gameserver",
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.BoolVar(&interactive, "interactive", false, "run in interactive mode")
|
fs.BoolVar(&flags.Interactive, 'i', "interactive", "run in interactive mode")
|
||||||
flag.BoolVar(&interactive, "i", false, "run in interactive mode (shorthand)")
|
fs.StringVar(
|
||||||
|
&flags.LogLevel,
|
||||||
|
'l',
|
||||||
|
"loglevel",
|
||||||
|
"info",
|
||||||
|
"Log level (debug, info, warn, error, fatal, panic)",
|
||||||
|
)
|
||||||
|
fs.BoolVar(&flags.Version, 'v', "version", "print version information and exit")
|
||||||
|
|
||||||
flag.StringVar(&loglevel, "loglevel", "warn", "log level")
|
err := ff.Parse(fs, os.Args[1:],
|
||||||
flag.StringVar(&loglevel, "l", "warn", "log level (shorthand)")
|
ff.WithEnvVarPrefix("Q3RCON"),
|
||||||
|
)
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, ff.ErrHelp):
|
||||||
|
fmt.Fprintf(os.Stderr, "%s\n", ffhelp.Flags(fs, "q3rcon [flags] <vban commands>"))
|
||||||
|
return nil, nil
|
||||||
|
case err != nil:
|
||||||
|
return nil, fmt.Errorf("failed to parse flags: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
flag.BoolVar(&versionFlag, "version", false, "print version information and exit")
|
if flags.Version {
|
||||||
flag.BoolVar(&versionFlag, "v", false, "print version information and exit (shorthand)")
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if versionFlag {
|
|
||||||
fmt.Printf("q3rcon version: %s\n", versionFromBuild())
|
fmt.Printf("q3rcon version: %s\n", versionFromBuild())
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
level, err := log.ParseLevel(loglevel)
|
if err := flags.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
level, err := log.ParseLevel(flags.LogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid log level: %s", loglevel)
|
return nil, fmt.Errorf("invalid log level: %s", flags.LogLevel)
|
||||||
}
|
}
|
||||||
log.SetLevel(level)
|
log.SetLevel(level)
|
||||||
|
|
||||||
if port < 1024 || port > 65535 {
|
client, closer, err := connectRcon(flags.Host, flags.Port, flags.Rconpass)
|
||||||
return nil, fmt.Errorf("invalid port value, got: (%d) expected: in range 1024-65535", port)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(rconpass) < 8 {
|
|
||||||
return nil, fmt.Errorf(
|
|
||||||
"invalid rcon password, got: (%s) expected: at least 8 characters",
|
|
||||||
rconpass,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
client, closer, err := connectRcon(host, port, rconpass)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to connect to rcon: %w", err)
|
return nil, fmt.Errorf("failed to connect to rcon: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if interactive {
|
if flags.Interactive {
|
||||||
fmt.Printf("Enter 'Q' to exit.\n>> ")
|
fmt.Printf("Enter 'Q' to exit.\n>> ")
|
||||||
err := interactiveMode(client, os.Stdin)
|
err := interactiveMode(client, os.Stdin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
1
go.mod
1
go.mod
@ -4,6 +4,7 @@ go 1.25.0
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/charmbracelet/log v0.4.2
|
github.com/charmbracelet/log v0.4.2
|
||||||
|
github.com/peterbourgon/ff/v4 v4.0.0-beta.1
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
6
go.sum
6
go.sum
@ -25,6 +25,10 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T
|
|||||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||||
|
github.com/peterbourgon/ff/v4 v4.0.0-beta.1 h1:hV8qRu3V7YfiSMsBSfPfdcznAvPQd3jI5zDddSrDoUc=
|
||||||
|
github.com/peterbourgon/ff/v4 v4.0.0-beta.1/go.mod h1:onQJUKipvCyFmZ1rIYwFAh1BhPOvftb1uhvSI7krNLc=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
@ -45,6 +49,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user