8 Commits

Author SHA1 Message Date
479204cb42 adding subcommand aliases
All checks were successful
code scans / scans (push) Successful in 20s
2025-11-26 13:13:00 -05:00
64a0f7422a Merge pull request 'moving run command' (#11) from chore/cmd-structure-update into main
All checks were successful
code scans / scans (push) Successful in 20s
Reviewed-on: #11
2025-11-26 18:05:50 +00:00
b41b5e2be8 moving run command
All checks were successful
code scans / scans (push) Successful in 26s
code scans / scans (pull_request) Successful in 24s
- run command is no longer attached to the 'command' subcmd
- run is standalone
2025-11-26 13:04:14 -05:00
210e574f51 [ci] Adding gosec scans
All checks were successful
code scans / scans (push) Successful in 31s
2025-11-25 20:28:06 -05:00
0c68e1de9d [ci] runner image changes
Some checks failed
code scans / scans (push) Failing after 35s
- go no longer installed out of the gates
2025-11-25 20:23:09 -05:00
1768a1ecd3 [ci] runner update
Some checks failed
code scans / scans (push) Failing after 31s
2025-11-25 20:19:23 -05:00
7a09123677 server view command flags
Some checks failed
code scans / scans (push) Has been cancelled
- adding decode flag to server view command
- more idiomatic naming for db funcs
2025-11-25 20:18:01 -05:00
0e50a4908b readme update
All checks were successful
code scans / scans (push) Successful in 38s
2025-06-23 11:17:31 -04:00
16 changed files with 105 additions and 48 deletions

View File

@@ -1,17 +1,18 @@
name: "code scans" name: "code scans"
on: on:
push: push:
branches:
- main
tags:
- v*
pull_request: pull_request:
jobs: jobs:
scans: scans:
runs-on: fire runs-on: wind
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: "dependency scan and static code analysis" - name: "install go"
uses: https://code.jakeyoungdev.com/actions/donotpassgo@v1.1.0 uses: https://code.jakeyoungdev.com/actions/install-go@v0.1.3
- name: "static code analysis"
uses: securego/gosec@v2.22.10
with:
args: ./...

View File

@@ -82,7 +82,7 @@ Command: kill %s
``` ```
```bash ```bash
#runs the 'kill' command on 'player' #runs the 'kill' command on 'player'
mctl command run placeholder kill player mctl command run placeholder player
``` ```
Running the placeholder command with the arg 'player' will run 'kill player' on the server Running the placeholder command with the arg 'player' will run 'kill player' on the server

View File

@@ -32,12 +32,12 @@ func New(name string) (*Client, error) {
var srv model.Server var srv model.Server
if name != "" { if name != "" {
srv, err = db.GetServer(name) srv, err = db.Server(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else { } else {
srv, err = db.GetActiveServer() srv, err = db.ActiveServer()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -8,12 +8,14 @@ import (
"fmt" "fmt"
"os" "os"
"code.jakeyoungdev.com/jake/mctl/constants"
"code.jakeyoungdev.com/jake/mctl/database" "code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var addCmd = &cobra.Command{ var addCmd = &cobra.Command{
Use: "add", Use: "add",
Aliases: []string{"a"},
Example: "mctl command add", Example: "mctl command add",
Short: "Saves a new command to the database", Short: "Saves a new command to the database",
SilenceUsage: true, SilenceUsage: true,
@@ -42,8 +44,8 @@ var addCmd = &cobra.Command{
err = db.SaveCmd(cfgname, cfgcmd) err = db.SaveCmd(cfgname, cfgcmd)
if err != nil { if err != nil {
if err.Error() == ErrInit { if err.Error() == constants.ErrInit {
fmt.Println(ErrInitRsp) fmt.Println(constants.ErrInitRsp)
return return
} }
} }

View File

@@ -7,16 +7,10 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
const (
//sqlite doesn't have an error type for this error, but we want to catch when this error is thrown
//and provide the proper response. It should not be treated as an error if the db isn't setup.
ErrInit = "sqlite3: SQL logic error: no such table: commands"
ErrInitRsp = "The 'init' command must be run before mctl can be used"
)
// CommandCmd is such a cool name lol // CommandCmd is such a cool name lol
var CommandCmd = &cobra.Command{ var CommandCmd = &cobra.Command{
Use: "command", Use: "command",
Aliases: []string{"c"},
Example: "mctl command <subcommand>", Example: "mctl command <subcommand>",
} }

View File

@@ -7,6 +7,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"code.jakeyoungdev.com/jake/mctl/constants"
"code.jakeyoungdev.com/jake/mctl/database" "code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -23,8 +24,8 @@ var deleteCmd = &cobra.Command{
err = db.DeleteCmd(args[0]) err = db.DeleteCmd(args[0])
if err != nil { if err != nil {
if err.Error() == ErrInit { if err.Error() == constants.ErrInit {
fmt.Println(ErrInitRsp) fmt.Println(constants.ErrInitRsp)
return return
} }
} }

View File

@@ -6,12 +6,14 @@ package command
import ( import (
"fmt" "fmt"
"code.jakeyoungdev.com/jake/mctl/constants"
"code.jakeyoungdev.com/jake/mctl/database" "code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var viewCmd = &cobra.Command{ var viewCmd = &cobra.Command{
Use: "view", Use: "view",
Aliases: []string{"v"},
Example: "mctl command view", Example: "mctl command view",
Short: "view all saved commands", Short: "view all saved commands",
SilenceUsage: true, SilenceUsage: true,
@@ -20,10 +22,10 @@ var viewCmd = &cobra.Command{
cobra.CheckErr(err) cobra.CheckErr(err)
defer db.Close() defer db.Close()
ts, err := db.GetAllCmds() ts, err := db.AllCmds()
if err != nil { if err != nil {
if err.Error() == ErrInit { if err.Error() == constants.ErrInit {
fmt.Println(ErrInitRsp) fmt.Println(constants.ErrInitRsp)
return return
} }
} }

View File

@@ -9,7 +9,7 @@ import (
"os" "os"
"strings" "strings"
"code.jakeyoungdev.com/jake/mctl/cmd/command" "code.jakeyoungdev.com/jake/mctl/constants"
"code.jakeyoungdev.com/jake/mctl/database" "code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -32,8 +32,8 @@ var destroyCmd = &cobra.Command{
err = db.Destroy() err = db.Destroy()
if err != nil { if err != nil {
if err.Error() == command.ErrInit { if err.Error() == constants.ErrInit {
fmt.Println(command.ErrInitRsp) fmt.Println(constants.ErrInitRsp)
return return
} }
} }

View File

@@ -16,7 +16,7 @@ var rootCmd = &cobra.Command{
Use: "mctl", Use: "mctl",
Short: "A remote console client", Short: "A remote console client",
Long: `mctl is a terminal-friendly remote console client made to manage game servers.`, Long: `mctl is a terminal-friendly remote console client made to manage game servers.`,
Version: "v0.5.1", Version: "v0.7.1",
} }
// Execute adds all child commands to the root command and sets flags appropriately. // Execute adds all child commands to the root command and sets flags appropriately.

View File

@@ -1,7 +1,7 @@
/* /*
Copyright © 2025 Jake jake.young.dev@gmail.com Copyright © 2025 Jake jake.young.dev@gmail.com
*/ */
package command package cmd
import ( import (
"errors" "errors"
@@ -9,6 +9,7 @@ import (
"strings" "strings"
"code.jakeyoungdev.com/jake/mctl/client" "code.jakeyoungdev.com/jake/mctl/client"
"code.jakeyoungdev.com/jake/mctl/constants"
"code.jakeyoungdev.com/jake/mctl/database" "code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -19,7 +20,8 @@ var (
var runCmd = &cobra.Command{ var runCmd = &cobra.Command{
Use: "run", Use: "run",
Example: "mctl command run -s <server> <command> args...", Aliases: []string{"r"},
Example: "mctl run -s <server> <command> args...",
Short: "Runs a saved command on a server", Short: "Runs a saved command on a server",
Long: `Runs the named command with the provided args on the default/active server unless -s is specified`, Long: `Runs the named command with the provided args on the default/active server unless -s is specified`,
SilenceUsage: true, SilenceUsage: true,
@@ -32,10 +34,10 @@ var runCmd = &cobra.Command{
} }
defer db.Close() defer db.Close()
crun, err := db.GetCmd(cname) crun, err := db.Cmd(cname)
if err != nil { if err != nil {
if err.Error() == ErrInit { if err.Error() == constants.ErrInit {
fmt.Println(ErrInitRsp) fmt.Println(constants.ErrInitRsp)
return nil return nil
} }
return err return err
@@ -76,5 +78,5 @@ var runCmd = &cobra.Command{
func init() { func init() {
runCmd.Flags().StringVarP(&cfgserver, "server", "s", "", "server name") runCmd.Flags().StringVarP(&cfgserver, "server", "s", "", "server name")
CommandCmd.AddCommand(runCmd) rootCmd.AddCommand(runCmd)
} }

View File

@@ -18,6 +18,7 @@ import (
var addCmd = &cobra.Command{ var addCmd = &cobra.Command{
Use: "add", Use: "add",
Aliases: []string{"a"},
Example: "mctl server add", Example: "mctl server add",
Short: "Saves a new server configuration", Short: "Saves a new server configuration",
Long: `Saves server address, alias, port, and password to the database.`, Long: `Saves server address, alias, port, and password to the database.`,

View File

@@ -16,6 +16,7 @@ const (
var ServerCmd = &cobra.Command{ var ServerCmd = &cobra.Command{
Use: "server", Use: "server",
Aliases: []string{"s"},
Example: "mctl server <subcommand>", Example: "mctl server <subcommand>",
} }

View File

@@ -16,6 +16,7 @@ import (
var updateCmd = &cobra.Command{ var updateCmd = &cobra.Command{
Use: "update", Use: "update",
Aliases: []string{"u"},
Example: "mctl server update <name>", Example: "mctl server update <name>",
Short: "updates a saved servers password in the database", Short: "updates a saved servers password in the database",
SilenceUsage: true, SilenceUsage: true,

View File

@@ -4,42 +4,86 @@ Copyright © 2025 Jake jake.young.dev@gmail.com
package server package server
import ( import (
"encoding/base64"
"fmt" "fmt"
"code.jakeyoungdev.com/jake/mctl/database" "code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var decode bool
var viewCmd = &cobra.Command{ var viewCmd = &cobra.Command{
Use: "view", Use: "view",
Aliases: []string{"v"},
Example: "mctl server view", Example: "mctl server view",
Short: "view all saved servers", Short: "view all saved servers",
SilenceUsage: true, SilenceUsage: true,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
db, err := database.New() db, err := database.New()
cobra.CheckErr(err) if err != nil {
fmt.Println(err)
return
}
defer db.Close() defer db.Close()
ts, err := db.GetAllServers() if len(args) > 0 {
srvName := args[0]
s, err := db.Server(srvName)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("-----")
fmt.Printf("Name: %s\n", s.Name)
fmt.Printf("Address: %s\n", s.Server)
fmt.Printf("Port: %d\n", s.Port)
if decode {
pwb, err := base64.StdEncoding.DecodeString(s.Password)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Password: %s\n", string(pwb))
} else {
fmt.Println("Password: [REDACTED]")
}
fmt.Printf("Default: %t\n", s.Active)
return
}
ts, err := db.AllServers()
if err != nil { if err != nil {
if err.Error() == ErrInit { if err.Error() == ErrInit {
fmt.Println(ErrInitRsp) fmt.Println(ErrInitRsp)
return return
} else {
fmt.Println(err)
return
} }
} }
cobra.CheckErr(err)
for _, s := range ts { for _, s := range ts {
fmt.Println("-----") fmt.Println("-----")
fmt.Printf("Name: %s\n", s.Name) fmt.Printf("Name: %s\n", s.Name)
fmt.Printf("Address: %s\n", s.Server) fmt.Printf("Address: %s\n", s.Server)
fmt.Printf("Port: %d\n", s.Port) fmt.Printf("Port: %d\n", s.Port)
if decode {
pwb, err := base64.StdEncoding.DecodeString(s.Password)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Password: %s\n", string(pwb))
} else {
fmt.Println("Password: [REDACTED]") fmt.Println("Password: [REDACTED]")
}
fmt.Printf("Default: %t\n", s.Active) fmt.Printf("Default: %t\n", s.Active)
} }
}, },
} }
func init() { func init() {
viewCmd.Flags().BoolVarP(&decode, "decode", "d", false, "decodes server passwords")
ServerCmd.AddCommand(viewCmd) ServerCmd.AddCommand(viewCmd)
} }

8
constants/error.go Normal file
View File

@@ -0,0 +1,8 @@
package constants
const (
//sqlite doesn't have an error type for this error, but we want to catch when this error is thrown
//and provide the proper response. It should not be treated as an error if the db isn't setup.
ErrInit = "sqlite3: SQL logic error: no such table: commands"
ErrInitRsp = "The 'init' command must be run before mctl can be used"
)

View File

@@ -33,15 +33,15 @@ type Database interface {
//internals //internals
timeout() (context.Context, context.CancelFunc) timeout() (context.Context, context.CancelFunc)
//command methods //command methods
GetCmd(name string) (string, error) Cmd(name string) (string, error)
GetAllCmds() ([]model.Command, error) AllCmds() ([]model.Command, error)
SaveCmd(name, cmd string) error SaveCmd(name, cmd string) error
UpdateCmd(name, cmd string) error UpdateCmd(name, cmd string) error
DeleteCmd(name string) error DeleteCmd(name string) error
//server methods //server methods
GetServer(name string) (model.Server, error) Server(name string) (model.Server, error)
GetActiveServer() (model.Server, error) ActiveServer() (model.Server, error)
GetAllServers() ([]model.Server, error) AllServers() ([]model.Server, error)
SetActiveServer(name string) error SetActiveServer(name string) error
SaveServer(srv model.Server) error SaveServer(srv model.Server) error
UpdateServer(name, password string) error UpdateServer(name, password string) error
@@ -110,7 +110,7 @@ func (d *database) timeout() (context.Context, context.CancelFunc) {
} }
// gets command using name // gets command using name
func (d *database) GetCmd(name string) (string, error) { func (d *database) Cmd(name string) (string, error) {
query := ` query := `
SELECT SELECT
command command
@@ -135,7 +135,7 @@ func (d *database) GetCmd(name string) (string, error) {
} }
// gets all saved commands // gets all saved commands
func (d *database) GetAllCmds() ([]model.Command, error) { func (d *database) AllCmds() ([]model.Command, error) {
query := ` query := `
SELECT SELECT
name, name,
@@ -208,7 +208,7 @@ func (d *database) DeleteCmd(name string) error {
return err return err
} }
func (d *database) GetServer(name string) (model.Server, error) { func (d *database) Server(name string) (model.Server, error) {
query := ` query := `
SELECT SELECT
name, name,
@@ -235,7 +235,7 @@ func (d *database) GetServer(name string) (model.Server, error) {
return s, nil return s, nil
} }
func (d *database) GetActiveServer() (model.Server, error) { func (d *database) ActiveServer() (model.Server, error) {
query := ` query := `
SELECT SELECT
name, name,
@@ -259,7 +259,7 @@ func (d *database) GetActiveServer() (model.Server, error) {
return s, err return s, err
} }
func (d *database) GetAllServers() ([]model.Server, error) { func (d *database) AllServers() ([]model.Server, error) {
query := ` query := `
SELECT SELECT
name, name,