1
0
Просмотр исходного кода

cmd: migrate from urfave/cli v1 to v3 (#8160)

ᴊᴏᴇ ᴄʜᴇɴ 3 дней назад
Родитель
Сommit
630ae0b3b0
11 измененных файлов с 135 добавлено и 127 удалено
  1. 23 23
      cmd/gogs/admin.go
  2. 10 10
      cmd/gogs/backup.go
  3. 30 7
      cmd/gogs/cmd.go
  4. 13 12
      cmd/gogs/hook.go
  5. 14 13
      cmd/gogs/import.go
  6. 16 14
      cmd/gogs/main.go
  7. 11 11
      cmd/gogs/restore.go
  8. 8 14
      cmd/gogs/serv.go
  9. 7 6
      cmd/gogs/web.go
  10. 1 3
      go.mod
  11. 2 14
      go.sum

+ 23 - 23
cmd/gogs/admin.go

@@ -7,7 +7,7 @@ import (
 	"runtime"
 
 	"github.com/cockroachdb/errors"
-	"github.com/urfave/cli"
+	"github.com/urfave/cli/v3"
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/database"
@@ -19,15 +19,15 @@ var (
 		Usage: "Perform admin operations on command line",
 		Description: `Allow using internal logic of Gogs without hacking into the source code
 to make automatic initialization process more smoothly`,
-		Subcommands: []cli.Command{
-			subcmdCreateUser,
-			subcmdDeleteInactivateUsers,
-			subcmdDeleteRepositoryArchives,
-			subcmdDeleteMissingRepositories,
-			subcmdGitGcRepos,
-			subcmdRewriteAuthorizedKeys,
-			subcmdSyncRepositoryHooks,
-			subcmdReinitMissingRepositories,
+		Commands: []*cli.Command{
+			&subcmdCreateUser,
+			&subcmdDeleteInactivateUsers,
+			&subcmdDeleteRepositoryArchives,
+			&subcmdDeleteMissingRepositories,
+			&subcmdGitGcRepos,
+			&subcmdRewriteAuthorizedKeys,
+			&subcmdSyncRepositoryHooks,
+			&subcmdReinitMissingRepositories,
 		},
 	}
 
@@ -129,16 +129,16 @@ to make automatic initialization process more smoothly`,
 	}
 )
 
-func runCreateUser(c *cli.Context) error {
-	if !c.IsSet("name") {
+func runCreateUser(ctx context.Context, cmd *cli.Command) error {
+	if !cmd.IsSet("name") {
 		return errors.New("Username is not specified")
-	} else if !c.IsSet("password") {
+	} else if !cmd.IsSet("password") {
 		return errors.New("Password is not specified")
-	} else if !c.IsSet("email") {
+	} else if !cmd.IsSet("email") {
 		return errors.New("Email is not specified")
 	}
 
-	err := conf.Init(c.String("config"))
+	err := conf.Init(configFromLineage(cmd))
 	if err != nil {
 		return errors.Wrap(err, "init configuration")
 	}
@@ -149,13 +149,13 @@ func runCreateUser(c *cli.Context) error {
 	}
 
 	user, err := database.Handle.Users().Create(
-		context.Background(),
-		c.String("name"),
-		c.String("email"),
+		ctx,
+		cmd.String("name"),
+		cmd.String("email"),
 		database.CreateUserOptions{
-			Password:  c.String("password"),
+			Password:  cmd.String("password"),
 			Activated: true,
-			Admin:     c.Bool("admin"),
+			Admin:     cmd.Bool("admin"),
 		},
 	)
 	if err != nil {
@@ -166,9 +166,9 @@ func runCreateUser(c *cli.Context) error {
 	return nil
 }
 
-func adminDashboardOperation(operation func() error, successMessage string) func(*cli.Context) error {
-	return func(c *cli.Context) error {
-		err := conf.Init(c.String("config"))
+func adminDashboardOperation(operation func() error, successMessage string) func(context.Context, *cli.Command) error {
+	return func(_ context.Context, cmd *cli.Command) error {
+		err := conf.Init(configFromLineage(cmd))
 		if err != nil {
 			return errors.Wrap(err, "init configuration")
 		}

+ 10 - 10
cmd/gogs/backup.go

@@ -11,7 +11,7 @@ import (
 
 	"github.com/cockroachdb/errors"
 	"github.com/unknwon/cae/zip"
-	"github.com/urfave/cli"
+	"github.com/urfave/cli/v3"
 	"gopkg.in/ini.v1"
 	log "unknwon.dev/clog/v2"
 
@@ -44,10 +44,10 @@ const (
 	archiveRootDir             = "gogs-backup"
 )
 
-func runBackup(c *cli.Context) error {
-	zip.Verbose = c.Bool("verbose")
+func runBackup(ctx context.Context, cmd *cli.Command) error {
+	zip.Verbose = cmd.Bool("verbose")
 
-	err := conf.Init(c.String("config"))
+	err := conf.Init(configFromLineage(cmd))
 	if err != nil {
 		return errors.Wrap(err, "init configuration")
 	}
@@ -58,7 +58,7 @@ func runBackup(c *cli.Context) error {
 		return errors.Wrap(err, "set engine")
 	}
 
-	tmpDir := c.String("tempdir")
+	tmpDir := cmd.String("tempdir")
 	if !osutil.Exist(tmpDir) {
 		log.Fatal("'--tempdir' does not exist: %s", tmpDir)
 	}
@@ -78,7 +78,7 @@ func runBackup(c *cli.Context) error {
 		log.Fatal("Failed to save metadata '%s': %v", metaFile, err)
 	}
 
-	archiveName := filepath.Join(c.String("target"), c.String("archive-name"))
+	archiveName := filepath.Join(cmd.String("target"), cmd.String("archive-name"))
 	log.Info("Packing backup files to: %s", archiveName)
 
 	z, err := zip.Create(archiveName)
@@ -91,14 +91,14 @@ func runBackup(c *cli.Context) error {
 
 	// Database
 	dbDir := filepath.Join(rootDir, "db")
-	if err = database.DumpDatabase(context.Background(), conn, dbDir, c.Bool("verbose")); err != nil {
+	if err = database.DumpDatabase(ctx, conn, dbDir, cmd.Bool("verbose")); err != nil {
 		log.Fatal("Failed to dump database: %v", err)
 	}
 	if err = z.AddDir(archiveRootDir+"/db", dbDir); err != nil {
 		log.Fatal("Failed to include 'db': %v", err)
 	}
 
-	if !c.Bool("database-only") {
+	if !cmd.Bool("database-only") {
 		// Custom files
 		err = addCustomDirToBackup(z)
 		if err != nil {
@@ -119,10 +119,10 @@ func runBackup(c *cli.Context) error {
 	}
 
 	// Repositories
-	if !c.Bool("exclude-repos") && !c.Bool("database-only") {
+	if !cmd.Bool("exclude-repos") && !cmd.Bool("database-only") {
 		reposDump := filepath.Join(rootDir, "repositories.zip")
 		log.Info("Dumping repositories in %q", conf.Repository.Root)
-		if c.Bool("exclude-mirror-repos") {
+		if cmd.Bool("exclude-mirror-repos") {
 			repos, err := database.GetNonMirrorRepositories()
 			if err != nil {
 				log.Fatal("Failed to get non-mirror repositories: %v", err)

+ 30 - 7
cmd/gogs/cmd.go

@@ -1,20 +1,43 @@
 package main
 
 import (
-	"github.com/urfave/cli"
+	"strings"
+
+	"github.com/urfave/cli/v3"
 )
 
-func stringFlag(name, value, usage string) cli.StringFlag {
-	return cli.StringFlag{
-		Name:  name,
+func stringFlag(name, value, usage string) *cli.StringFlag {
+	parts := strings.SplitN(name, ", ", 2)
+	f := &cli.StringFlag{
+		Name:  parts[0],
 		Value: value,
 		Usage: usage,
 	}
+	if len(parts) > 1 {
+		f.Aliases = []string{parts[1]}
+	}
+	return f
+}
+
+// configFromLineage walks the command lineage to find the --config flag value.
+// This is needed because subcommands may not directly see flags set on parent commands.
+func configFromLineage(cmd *cli.Command) string {
+	for _, c := range cmd.Lineage() {
+		if c.IsSet("config") {
+			return c.String("config")
+		}
+	}
+	return ""
 }
 
-func boolFlag(name, usage string) cli.BoolFlag {
-	return cli.BoolFlag{
-		Name:  name,
+func boolFlag(name, usage string) *cli.BoolFlag {
+	parts := strings.SplitN(name, ", ", 2)
+	f := &cli.BoolFlag{
+		Name:  parts[0],
 		Usage: usage,
 	}
+	if len(parts) > 1 {
+		f.Aliases = []string{parts[1]}
+	}
+	return f
 }

+ 13 - 12
cmd/gogs/hook.go

@@ -3,6 +3,7 @@ package main
 import (
 	"bufio"
 	"bytes"
+	"context"
 	"crypto/tls"
 	"fmt"
 	"net/url"
@@ -12,7 +13,7 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/urfave/cli"
+	"github.com/urfave/cli/v3"
 	log "unknwon.dev/clog/v2"
 
 	"github.com/gogs/git-module"
@@ -32,10 +33,10 @@ var (
 		Flags: []cli.Flag{
 			stringFlag("config, c", "", "Custom configuration file path"),
 		},
-		Subcommands: []cli.Command{
-			subcmdHookPreReceive,
-			subcmdHookUpadte,
-			subcmdHookPostReceive,
+		Commands: []*cli.Command{
+			&subcmdHookPreReceive,
+			&subcmdHookUpadte,
+			&subcmdHookPostReceive,
 		},
 	}
 
@@ -59,11 +60,11 @@ var (
 	}
 )
 
-func runHookPreReceive(c *cli.Context) error {
+func runHookPreReceive(_ context.Context, cmd *cli.Command) error {
 	if os.Getenv("SSH_ORIGINAL_COMMAND") == "" {
 		return nil
 	}
-	setup(c, "pre-receive.log", true)
+	setup(cmd, "pre-receive.log", true)
 
 	isWiki := strings.Contains(os.Getenv(database.EnvRepoCustomHooksPath), ".wiki.git/")
 
@@ -152,13 +153,13 @@ func runHookPreReceive(c *cli.Context) error {
 	return nil
 }
 
-func runHookUpdate(c *cli.Context) error {
+func runHookUpdate(_ context.Context, cmd *cli.Command) error {
 	if os.Getenv("SSH_ORIGINAL_COMMAND") == "" {
 		return nil
 	}
-	setup(c, "update.log", false)
+	setup(cmd, "update.log", false)
 
-	args := c.Args()
+	args := cmd.Args().Slice()
 	if len(args) != 3 {
 		fail("Arguments received are not equal to three", "Arguments received are not equal to three")
 	} else if args[0] == "" {
@@ -186,11 +187,11 @@ func runHookUpdate(c *cli.Context) error {
 	return nil
 }
 
-func runHookPostReceive(c *cli.Context) error {
+func runHookPostReceive(_ context.Context, cmd *cli.Command) error {
 	if os.Getenv("SSH_ORIGINAL_COMMAND") == "" {
 		return nil
 	}
-	setup(c, "post-receive.log", true)
+	setup(cmd, "post-receive.log", true)
 
 	// Post-receive hook does more than just gather Git information,
 	// so we need to setup additional services for email notifications.

+ 14 - 13
cmd/gogs/import.go

@@ -3,13 +3,14 @@ package main
 import (
 	"bufio"
 	"bytes"
+	"context"
 	"fmt"
 	"os"
 	"path/filepath"
 	"time"
 
 	"github.com/cockroachdb/errors"
-	"github.com/urfave/cli"
+	"github.com/urfave/cli/v3"
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/osutil"
@@ -21,8 +22,8 @@ var (
 		Usage: "Import portable data as local Gogs data",
 		Description: `Allow user import data from other Gogs installations to local instance
 without manually hacking the data files`,
-		Subcommands: []cli.Command{
-			subcmdImportLocale,
+		Commands: []*cli.Command{
+			&subcmdImportLocale,
 		},
 	}
 
@@ -38,19 +39,19 @@ without manually hacking the data files`,
 	}
 )
 
-func runImportLocale(c *cli.Context) error {
-	if !c.IsSet("source") {
+func runImportLocale(_ context.Context, cmd *cli.Command) error {
+	if !cmd.IsSet("source") {
 		return errors.New("source directory is not specified")
-	} else if !c.IsSet("target") {
+	} else if !cmd.IsSet("target") {
 		return errors.New("target directory is not specified")
 	}
-	if !osutil.IsDir(c.String("source")) {
-		return errors.Newf("source directory %q does not exist or is not a directory", c.String("source"))
-	} else if !osutil.IsDir(c.String("target")) {
-		return errors.Newf("target directory %q does not exist or is not a directory", c.String("target"))
+	if !osutil.IsDir(cmd.String("source")) {
+		return errors.Newf("source directory %q does not exist or is not a directory", cmd.String("source"))
+	} else if !osutil.IsDir(cmd.String("target")) {
+		return errors.Newf("target directory %q does not exist or is not a directory", cmd.String("target"))
 	}
 
-	err := conf.Init(c.String("config"))
+	err := conf.Init(configFromLineage(cmd))
 	if err != nil {
 		return errors.Wrap(err, "init configuration")
 	}
@@ -64,8 +65,8 @@ func runImportLocale(c *cli.Context) error {
 	// Cut out en-US.
 	for _, lang := range conf.I18n.Langs[1:] {
 		name := fmt.Sprintf("locale_%s.ini", lang)
-		source := filepath.Join(c.String("source"), name)
-		target := filepath.Join(c.String("target"), name)
+		source := filepath.Join(cmd.String("source"), name)
+		target := filepath.Join(cmd.String("target"), name)
 		if !osutil.IsFile(source) {
 			continue
 		}

+ 16 - 14
cmd/gogs/main.go

@@ -2,9 +2,10 @@
 package main
 
 import (
+	"context"
 	"os"
 
-	"github.com/urfave/cli"
+	"github.com/urfave/cli/v3"
 	log "unknwon.dev/clog/v2"
 
 	"gogs.io/gogs/internal/conf"
@@ -15,20 +16,21 @@ func init() {
 }
 
 func main() {
-	app := cli.NewApp()
-	app.Name = "Gogs"
-	app.Usage = "A painless self-hosted Git service"
-	app.Version = conf.App.Version
-	app.Commands = []cli.Command{
-		webCommand,
-		servCommand,
-		hookCommand,
-		adminCommand,
-		importCommand,
-		backupCommand,
-		restoreCommand,
+	cmd := &cli.Command{
+		Name:    "Gogs",
+		Usage:   "A painless self-hosted Git service",
+		Version: conf.App.Version,
+		Commands: []*cli.Command{
+			&webCommand,
+			&servCommand,
+			&hookCommand,
+			&adminCommand,
+			&importCommand,
+			&backupCommand,
+			&restoreCommand,
+		},
 	}
-	if err := app.Run(os.Args); err != nil {
+	if err := cmd.Run(context.Background(), os.Args); err != nil {
 		log.Fatal("Failed to start application: %v", err)
 	}
 }

+ 11 - 11
cmd/gogs/restore.go

@@ -8,7 +8,7 @@ import (
 
 	"github.com/cockroachdb/errors"
 	"github.com/unknwon/cae/zip"
-	"github.com/urfave/cli"
+	"github.com/urfave/cli/v3"
 	"gopkg.in/ini.v1"
 	log "unknwon.dev/clog/v2"
 
@@ -42,10 +42,10 @@ be skipped and remain unchanged.`,
 // format that is able to import.
 var lastSupportedVersionOfFormat = map[int]string{}
 
-func runRestore(c *cli.Context) error {
-	zip.Verbose = c.Bool("verbose")
+func runRestore(ctx context.Context, cmd *cli.Command) error {
+	zip.Verbose = cmd.Bool("verbose")
 
-	tmpDir := c.String("tempdir")
+	tmpDir := cmd.String("tempdir")
 	if !osutil.IsDir(tmpDir) {
 		log.Fatal("'--tempdir' does not exist: %s", tmpDir)
 	}
@@ -58,8 +58,8 @@ func runRestore(c *cli.Context) error {
 	}
 	defer func() { _ = os.RemoveAll(archivePath) }()
 
-	log.Info("Restoring backup from: %s", c.String("from"))
-	err = zip.ExtractTo(c.String("from"), tmpDir)
+	log.Info("Restoring backup from: %s", cmd.String("from"))
+	err = zip.ExtractTo(cmd.String("from"), tmpDir)
 	if err != nil {
 		log.Fatal("Failed to extract backup archive: %v", err)
 	}
@@ -90,8 +90,8 @@ func runRestore(c *cli.Context) error {
 	// Otherwise, it's optional to set config file flag.
 	configFile := filepath.Join(archivePath, "custom", "conf", "app.ini")
 	var customConf string
-	if c.IsSet("config") {
-		customConf = c.String("config")
+	if lineageConf := configFromLineage(cmd); lineageConf != "" {
+		customConf = lineageConf
 	} else if !osutil.IsFile(configFile) {
 		log.Fatal("'--config' is not specified and custom config file is not found in backup")
 	} else {
@@ -111,11 +111,11 @@ func runRestore(c *cli.Context) error {
 
 	// Database
 	dbDir := path.Join(archivePath, "db")
-	if err = database.ImportDatabase(context.Background(), conn, dbDir, c.Bool("verbose")); err != nil {
+	if err = database.ImportDatabase(ctx, conn, dbDir, cmd.Bool("verbose")); err != nil {
 		log.Fatal("Failed to import database: %v", err)
 	}
 
-	if !c.Bool("database-only") {
+	if !cmd.Bool("database-only") {
 		// Custom files
 		if osutil.IsDir(conf.CustomDir()) {
 			if err = os.Rename(conf.CustomDir(), conf.CustomDir()+".bak"); err != nil {
@@ -149,7 +149,7 @@ func runRestore(c *cli.Context) error {
 
 	// Repositories
 	reposPath := filepath.Join(archivePath, "repositories.zip")
-	if !c.Bool("exclude-repos") && !c.Bool("database-only") && osutil.IsFile(reposPath) {
+	if !cmd.Bool("exclude-repos") && !cmd.Bool("database-only") && osutil.IsFile(reposPath) {
 		if err := zip.ExtractTo(reposPath, filepath.Dir(conf.Repository.Root)); err != nil {
 			log.Fatal("Failed to extract 'repositories.zip': %v", err)
 		}

+ 8 - 14
cmd/gogs/serv.go

@@ -10,7 +10,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/urfave/cli"
+	"github.com/urfave/cli/v3"
 	log "unknwon.dev/clog/v2"
 
 	"gogs.io/gogs/internal/conf"
@@ -48,15 +48,10 @@ func fail(userMessage, errMessage string, args ...any) {
 	os.Exit(1)
 }
 
-func setup(c *cli.Context, logFile string, connectDB bool) {
+func setup(cmd *cli.Command, logFile string, connectDB bool) {
 	conf.HookMode = true
 
-	var customConf string
-	if c.IsSet("config") {
-		customConf = c.String("config")
-	} else if c.GlobalIsSet("config") {
-		customConf = c.GlobalString("config")
-	}
+	customConf := configFromLineage(cmd)
 
 	err := conf.Init(customConf)
 	if err != nil {
@@ -128,16 +123,15 @@ var allowedCommands = map[string]database.AccessMode{
 	"git-receive-pack":   database.AccessModeWrite,
 }
 
-func runServ(c *cli.Context) error {
-	ctx := context.Background()
-	setup(c, "serv.log", true)
+func runServ(ctx context.Context, cmd *cli.Command) error {
+	setup(cmd, "serv.log", true)
 
 	if conf.SSH.Disabled {
 		println("Gogs: SSH has been disabled")
 		return nil
 	}
 
-	if len(c.Args()) < 1 {
+	if cmd.Args().Len() < 1 {
 		fail("Not enough arguments", "Not enough arguments")
 	}
 
@@ -188,10 +182,10 @@ func runServ(c *cli.Context) error {
 	// Allow anonymous (user is nil) clone for public repositories.
 	var user *database.User
 
-	keyID, _ := strconv.ParseInt(strings.TrimPrefix(c.Args()[0], "key-"), 10, 64)
+	keyID, _ := strconv.ParseInt(strings.TrimPrefix(cmd.Args().Get(0), "key-"), 10, 64)
 	key, err := database.GetPublicKeyByID(keyID)
 	if err != nil {
-		fail("Invalid key ID", "Invalid key ID '%s': %v", c.Args()[0], err)
+		fail("Invalid key ID", "Invalid key ID '%s': %v", cmd.Args().Get(0), err)
 	}
 
 	if requestMode == database.AccessModeWrite || repo.IsPrivate {

+ 7 - 6
cmd/gogs/web.go

@@ -1,6 +1,7 @@
 package main
 
 import (
+	stdctx "context"
 	"crypto/tls"
 	"fmt"
 	"io"
@@ -20,7 +21,7 @@ import (
 	"github.com/go-macaron/session"
 	"github.com/go-macaron/toolbox"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
-	"github.com/urfave/cli"
+	"github.com/urfave/cli/v3"
 	"gopkg.in/macaron.v1"
 	log "unknwon.dev/clog/v2"
 
@@ -158,8 +159,8 @@ func newMacaron() *macaron.Macaron {
 	return m
 }
 
-func runWeb(c *cli.Context) error {
-	err := route.GlobalInit(c.String("config"))
+func runWeb(_ stdctx.Context, cmd *cli.Command) error {
+	err := route.GlobalInit(configFromLineage(cmd))
 	if err != nil {
 		log.Fatal("Failed to initialize application: %v", err)
 	}
@@ -697,10 +698,10 @@ func runWeb(c *cli.Context) error {
 	m.NotFound(route.NotFound)
 
 	// Flag for port number in case first time run conflict.
-	if c.IsSet("port") {
-		conf.Server.URL.Host = strings.Replace(conf.Server.URL.Host, ":"+conf.Server.URL.Port(), ":"+c.String("port"), 1)
+	if cmd.IsSet("port") {
+		conf.Server.URL.Host = strings.Replace(conf.Server.URL.Host, ":"+conf.Server.URL.Port(), ":"+cmd.String("port"), 1)
 		conf.Server.ExternalURL = conf.Server.URL.String()
-		conf.Server.HTTPPort = c.String("port")
+		conf.Server.HTTPPort = cmd.String("port")
 	}
 
 	var listenAddr string

+ 1 - 3
go.mod

@@ -43,7 +43,7 @@ require (
 	github.com/unknwon/com v1.0.1
 	github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6
 	github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e
-	github.com/urfave/cli v1.22.17
+	github.com/urfave/cli/v3 v3.6.2
 	golang.org/x/crypto v0.47.0
 	golang.org/x/image v0.35.0
 	golang.org/x/net v0.48.0
@@ -74,7 +74,6 @@ require (
 	github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
 	github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
 	github.com/cockroachdb/redact v1.1.5 // indirect
-	github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/denisenkom/go-mssqldb v0.12.0 // indirect
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
@@ -124,7 +123,6 @@ require (
 	github.com/prometheus/procfs v0.16.1 // indirect
 	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 	github.com/rogpeppe/go-internal v1.10.0 // indirect
-	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
 	github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
 	go.bobheadxi.dev/streamline v1.2.1 // indirect

+ 2 - 14
go.sum

@@ -15,7 +15,6 @@ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+
 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
 github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
 github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
 github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
 github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
@@ -61,8 +60,6 @@ github.com/couchbase/gomemcached v0.1.1/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9Hj
 github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
 github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
 github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc=
-github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
-github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -393,8 +390,6 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
 github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
-github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
 github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
 github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
@@ -420,19 +415,12 @@ github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cma
 github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
 github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
 github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
@@ -445,8 +433,8 @@ github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6 h1:sRrkJEHtNoaSvyXMbR
 github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ=
 github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e h1:Qf3QQl/zmEbWDajFEiisbKN83hLY+eq2MhbA0I1/two=
 github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e/go.mod h1:TBwoao3Q4Eb/cp+dHbXDfRTrZSsj/k7kLr2j1oWRWC0=
-github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
-github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
+github.com/urfave/cli/v3 v3.6.2 h1:lQuqiPrZ1cIz8hz+HcrG0TNZFxU70dPZ3Yl+pSrH9A8=
+github.com/urfave/cli/v3 v3.6.2/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=