mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
2978 files. The fork is 2026-07-15 (rebrand: gitea.dev -> github.com/hanzoai/git), so 2026 is the year, and "Copyright 2026 Hanzo AI, Inc." was already the form used by the handful of files that had ours. Shape is now: // Copyright 2026 Hanzo AI, Inc. All rights reserved. // Copyright 2015 The Gogs Authors. All rights reserved. // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT Ours on top, theirs retained verbatim. Retained because MIT requires it, and separately because they are true — those authors did write that code. Not renamed to "The Git Authors": Git is a different project under a different license, and putting its name here would attribute this code to people who have nothing to do with it. Adding our line is the fix; overwriting theirs would have been both a licence violation and a false statement. gofmt still reports import-order drift in some of these files. It predates this change (verified against a stash) and is left alone rather than burying a one-line-per-file legal change under 3000 files of unrelated reordering. Build clean. Co-authored-by: Hanzo Dev <[email protected]>
195 lines
5.7 KiB
Go
195 lines
5.7 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/hanzoai/git/modules/log"
|
|
"github.com/hanzoai/git/modules/setting"
|
|
|
|
"github.com/urfave/cli/v3"
|
|
)
|
|
|
|
var cliHelpPrinterOld = cli.HelpPrinter
|
|
|
|
func init() {
|
|
cli.HelpPrinter = cliHelpPrinterNew
|
|
}
|
|
|
|
// cliHelpPrinterNew helps to print "DEFAULT CONFIGURATION" for the following cases ( "-c" can apper in any position):
|
|
// * ./gitd -c /dev/null -h
|
|
// * ./gitd -c help /dev/null help
|
|
// * ./gitd help -c /dev/null
|
|
// * ./gitd help -c /dev/null web
|
|
// * ./gitd help web -c /dev/null
|
|
// * ./gitd web help -c /dev/null
|
|
// * ./gitd web -h -c /dev/null
|
|
func cliHelpPrinterNew(out io.Writer, templ string, data any) {
|
|
cmd, _ := data.(*cli.Command)
|
|
if cmd != nil {
|
|
prepareWorkPathAndCustomConf(cmd)
|
|
}
|
|
cliHelpPrinterOld(out, templ, data)
|
|
if setting.CustomConf != "" {
|
|
_, _ = fmt.Fprintf(out, `
|
|
DEFAULT CONFIGURATION:
|
|
AppPath: %s
|
|
WorkPath: %s
|
|
CustomPath: %s
|
|
ConfigFile: %s
|
|
|
|
`, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf)
|
|
}
|
|
}
|
|
|
|
func PrepareSubcommandWithGlobalFlags(originCmd *cli.Command) {
|
|
originBefore := originCmd.Before
|
|
originCmd.Before = func(ctxOrig context.Context, cmd *cli.Command) (ctx context.Context, err error) {
|
|
ctx = ctxOrig
|
|
if originBefore != nil {
|
|
ctx, err = originBefore(ctx, cmd)
|
|
if err != nil {
|
|
return ctx, err
|
|
}
|
|
}
|
|
prepareWorkPathAndCustomConf(cmd)
|
|
return ctx, nil
|
|
}
|
|
}
|
|
|
|
// prepareWorkPathAndCustomConf tries to prepare the work path, custom path and custom config from various inputs:
|
|
// command line flags, environment variables, config file
|
|
func prepareWorkPathAndCustomConf(cmd *cli.Command) {
|
|
var args setting.ArgWorkPathAndCustomConf
|
|
if cmd.IsSet("work-path") {
|
|
args.WorkPath = cmd.String("work-path")
|
|
}
|
|
if cmd.IsSet("custom-path") {
|
|
args.CustomPath = cmd.String("custom-path")
|
|
}
|
|
if cmd.IsSet("config") {
|
|
args.CustomConf = cmd.String("config")
|
|
}
|
|
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
|
|
}
|
|
|
|
type AppVersion struct {
|
|
Version string
|
|
Extra string
|
|
}
|
|
|
|
func NewMainApp(appVer AppVersion) *cli.Command {
|
|
app := &cli.Command{}
|
|
app.Name = "gitd" // must be lower-cased because it appears in the "USAGE" section like "gitd doctor [command [command options]]"
|
|
app.Usage = "A painless self-hosted Git service"
|
|
app.Description = `Hanzo Git program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.`
|
|
app.Version = appVer.Version + appVer.Extra
|
|
app.EnableShellCompletion = true
|
|
app.Flags = []cli.Flag{
|
|
&cli.StringFlag{
|
|
Name: "work-path",
|
|
Aliases: []string{"w"},
|
|
TakesFile: true,
|
|
Usage: "Set Hanzo Git's working path (defaults to the Hanzo Git's binary directory)",
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "config",
|
|
Aliases: []string{"c"},
|
|
TakesFile: true,
|
|
Value: setting.CustomConf,
|
|
Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "custom-path",
|
|
Aliases: []string{"C"},
|
|
TakesFile: true,
|
|
Usage: "Set custom path (defaults to '{WorkPath}/custom')",
|
|
},
|
|
}
|
|
webCmd := newWebCommand()
|
|
// these sub-commands need to use a config file
|
|
subCmdWithConfig := []*cli.Command{
|
|
webCmd,
|
|
newServCommand(),
|
|
newHookCommand(),
|
|
NewKeysCommand(),
|
|
newDumpCommand(),
|
|
newAdminCommand(),
|
|
newMigrateCommand(),
|
|
newDoctorCommand(),
|
|
newManagerCommand(),
|
|
newEmbeddedCommand(),
|
|
newMigrateStorageCommand(),
|
|
newDumpRepositoryCommand(),
|
|
newRestoreRepositoryCommand(),
|
|
newActionsCommand(),
|
|
}
|
|
|
|
// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
|
|
subCmdStandalone := []*cli.Command{
|
|
cmdConfig(),
|
|
cmdCert(),
|
|
newGenerateCommand(),
|
|
newDocsCommand(),
|
|
}
|
|
|
|
// TODO: we should eventually drop the default command,
|
|
// but not sure whether it would break Windows users who used to double-click the EXE to run.
|
|
app.DefaultCommand = webCmd.Name
|
|
|
|
app.Before = PrepareConsoleLoggerLevel(log.INFO)
|
|
for i := range subCmdWithConfig {
|
|
PrepareSubcommandWithGlobalFlags(subCmdWithConfig[i])
|
|
}
|
|
app.Commands = append(app.Commands, subCmdWithConfig...)
|
|
app.Commands = append(app.Commands, subCmdStandalone...)
|
|
|
|
setting.UnsetUnnecessaryEnvVars()
|
|
return app
|
|
}
|
|
|
|
// usageErr marks a usage error already reported by cliOnUsageError, so RunMainApp does not print it again.
|
|
type usageErr struct{ err error }
|
|
|
|
func (e usageErr) Error() string { return e.err.Error() }
|
|
func (e usageErr) Unwrap() error { return e.err }
|
|
|
|
// cliOnUsageError reports usage errors itself instead of letting urfave/cli dump the full help to stdout (since urfave/cli v3.10).
|
|
func cliOnUsageError(_ context.Context, cmd *cli.Command, err error, _ bool) error {
|
|
_, _ = fmt.Fprintf(cmd.Root().ErrWriter, "Incorrect Usage: %s\n", err.Error())
|
|
return usageErr{err}
|
|
}
|
|
|
|
func setCLIOnUsageError(cmd *cli.Command) {
|
|
_ = cmd.Walk(func(c *cli.Command) error {
|
|
c.OnUsageError = cliOnUsageError
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func RunMainApp(app *cli.Command, args ...string) error {
|
|
ctx, cancel := installSignals()
|
|
defer cancel()
|
|
setCLIOnUsageError(app)
|
|
// the completion subcommands are built during app.Run, after the Walk above, so cover them via this hook
|
|
app.ConfigureShellCompletionCommand = setCLIOnUsageError
|
|
err := app.Run(ctx, args)
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if _, ok := errors.AsType[usageErr](err); ok {
|
|
cli.OsExiter(1) // cliOnUsageError already reported it
|
|
return err
|
|
}
|
|
_, _ = fmt.Fprintf(app.ErrWriter, "Command error: %v\n", err)
|
|
cli.OsExiter(1)
|
|
return err
|
|
}
|