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

This commit is contained in:
ᴊᴏᴇ ᴄʜᴇɴ
2026-02-08 00:58:05 -05:00
committed by GitHub
parent 3c358ede6d
commit 630ae0b3b0
11 changed files with 135 additions and 127 deletions

View File

@@ -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")
}

View File

@@ -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)

View File

@@ -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
}
func boolFlag(name, usage string) cli.BoolFlag {
return cli.BoolFlag{
Name: name,
// 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 {
parts := strings.SplitN(name, ", ", 2)
f := &cli.BoolFlag{
Name: parts[0],
Usage: usage,
}
if len(parts) > 1 {
f.Aliases = []string{parts[1]}
}
return f
}

View File

@@ -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.

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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 {

View File

@@ -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

4
go.mod
View File

@@ -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

16
go.sum
View File

@@ -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=