Compare commits

..

3 Commits

Author SHA1 Message Date
a74980caa1 deps: add urfave/cli v3 checksums 2026-01-13 07:46:05 +01:00
816f08a20a deps: add urfave/cli v3 2026-01-13 07:45:53 +01:00
0cec152486 feat: migrate cli to urfave/cli v3 2026-01-13 07:45:18 +01:00
4 changed files with 135 additions and 28 deletions

View File

@@ -1,14 +1,24 @@
package main package main
import ( import (
"context"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"io"
"os" "os"
"sync"
"goyco/cmd/goyco/commands" "goyco/cmd/goyco/commands"
"goyco/internal/config"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/urfave/cli/v3"
)
var (
helpPrinterOnce sync.Once
defaultHelpPrinter func(io.Writer, string, interface{})
) )
func loadDotEnv() { func loadDotEnv() {
@@ -55,3 +65,121 @@ func printRunUsage() {
fmt.Fprintln(os.Stderr, "Usage: goyco run") fmt.Fprintln(os.Stderr, "Usage: goyco run")
fmt.Fprintln(os.Stderr, "\nStart the web application in foreground.") fmt.Fprintln(os.Stderr, "\nStart the web application in foreground.")
} }
func buildRootCommand(cfg *config.Config) *cli.Command {
helpPrinterOnce.Do(func() {
defaultHelpPrinter = cli.HelpPrinter
cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
if cmd, ok := data.(*cli.Command); ok && cmd.Root() == cmd {
printRootUsage()
return
}
defaultHelpPrinter(w, templ, data)
}
})
root := &cli.Command{
Name: "goyco",
Usage: "Y Combinator-style news aggregation platform API",
UsageText: "goyco <command> [<args>]",
HideVersion: true,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "json",
Usage: "output results in JSON format",
Value: cfg.CLI.JSONOutputDefault,
},
},
Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
commands.SetJSONOutput(cmd.Bool("json"))
return ctx, nil
},
Action: func(_ context.Context, cmd *cli.Command) error {
if cmd.NArg() == 0 {
printRootUsage()
return nil
}
printRootUsage()
return fmt.Errorf("unknown command: %s", cmd.Args().First())
},
Commands: []*cli.Command{
{
Name: "run",
Usage: "start the web application in foreground",
SkipFlagParsing: true,
Action: func(_ context.Context, cmd *cli.Command) error {
return handleRunCommand(cfg, cmd.Args().Slice())
},
},
{
Name: "start",
Usage: "start the web application in background",
SkipFlagParsing: true,
Action: func(_ context.Context, cmd *cli.Command) error {
return commands.HandleStartCommand(cfg, cmd.Args().Slice())
},
},
{
Name: "stop",
Usage: "stop the daemon",
SkipFlagParsing: true,
Action: func(_ context.Context, cmd *cli.Command) error {
return commands.HandleStopCommand(cfg, cmd.Args().Slice())
},
},
{
Name: "status",
Usage: "check if the daemon is running",
SkipFlagParsing: true,
Action: func(_ context.Context, cmd *cli.Command) error {
return commands.HandleStatusCommand(cfg, cmd.Name, cmd.Args().Slice())
},
},
{
Name: "migrate",
Aliases: []string{"migrations"},
Usage: "apply database migrations",
SkipFlagParsing: true,
Action: func(_ context.Context, cmd *cli.Command) error {
return commands.HandleMigrateCommand(cfg, cmd.Name, cmd.Args().Slice())
},
},
{
Name: "user",
Usage: "manage users (create, update, delete, lock, list)",
SkipFlagParsing: true,
Action: func(_ context.Context, cmd *cli.Command) error {
return commands.HandleUserCommand(cfg, cmd.Name, cmd.Args().Slice())
},
},
{
Name: "post",
Usage: "manage posts (delete, list, search)",
SkipFlagParsing: true,
Action: func(_ context.Context, cmd *cli.Command) error {
return commands.HandlePostCommand(cfg, cmd.Name, cmd.Args().Slice())
},
},
{
Name: "prune",
Usage: "hard delete users and posts (posts, all)",
SkipFlagParsing: true,
Action: func(_ context.Context, cmd *cli.Command) error {
return commands.HandlePruneCommand(cfg, cmd.Name, cmd.Args().Slice())
},
},
{
Name: "seed",
Usage: "seed database with random data",
SkipFlagParsing: true,
Action: func(_ context.Context, cmd *cli.Command) error {
return commands.HandleSeedCommand(cfg, cmd.Name, cmd.Args().Slice())
},
},
},
Writer: os.Stdout,
ErrWriter: os.Stderr,
}
return root
}

View File

@@ -12,8 +12,8 @@
package main package main
import ( import (
"context"
"errors" "errors"
"flag"
"fmt" "fmt"
"log" "log"
"os" "os"
@@ -63,33 +63,9 @@ func run(args []string) error {
docs.SwaggerInfo.Schemes = append(docs.SwaggerInfo.Schemes, "https") docs.SwaggerInfo.Schemes = append(docs.SwaggerInfo.Schemes, "https")
} }
rootFS := flag.NewFlagSet("goyco", flag.ContinueOnError) root := buildRootCommand(cfg)
rootFS.SetOutput(os.Stderr) runArgs := append([]string{os.Args[0]}, args...)
rootFS.Usage = printRootUsage return root.Run(context.Background(), runArgs)
showHelp := rootFS.Bool("help", false, "show this help message")
jsonOutput := rootFS.Bool("json", cfg.CLI.JSONOutputDefault, "output results in JSON format")
if err := rootFS.Parse(args); err != nil {
if errors.Is(err, flag.ErrHelp) {
return nil
}
return fmt.Errorf("failed to parse arguments: %w", err)
}
if *showHelp {
printRootUsage()
return nil
}
commands.SetJSONOutput(*jsonOutput)
remaining := rootFS.Args()
if len(remaining) == 0 {
printRootUsage()
return nil
}
return dispatchCommand(cfg, remaining[0], remaining[1:])
} }
func dispatchCommand(cfg *config.Config, name string, args []string) error { func dispatchCommand(cfg *config.Config, name string, args []string) error {

1
go.mod
View File

@@ -12,6 +12,7 @@ require (
github.com/stretchr/testify v1.11.1 github.com/stretchr/testify v1.11.1
github.com/swaggo/http-swagger v1.3.4 github.com/swaggo/http-swagger v1.3.4
github.com/swaggo/swag v1.16.6 github.com/swaggo/swag v1.16.6
github.com/urfave/cli/v3 v3.6.1
golang.org/x/crypto v0.43.0 golang.org/x/crypto v0.43.0
golang.org/x/net v0.46.0 golang.org/x/net v0.46.0
gorm.io/driver/postgres v1.6.0 gorm.io/driver/postgres v1.6.0

2
go.sum
View File

@@ -116,3 +116,5 @@ gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
github.com/urfave/cli/v3 v3.6.1 h1:j8Qq8NyUawj/7rTYdBGrxcH7A/j7/G8Q5LhWEW4G3Mo=
github.com/urfave/cli/v3 v3.6.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=