0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-11-07 23:00:54 +01:00

Fix cli "Before" handling (#35797) (#35808)

Backport #35797 by @wxiaoguang

Regression of #34973

Fix #35796

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Giteabot 2025-11-01 05:00:14 +08:00 committed by GitHub
parent d253e2055b
commit fddf6cd63f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 55 additions and 16 deletions

View File

@ -121,6 +121,12 @@ func globalBool(c *cli.Command, name string) bool {
// Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever. // Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever.
func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cli.Command) (context.Context, error) { func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cli.Command) (context.Context, error) {
return func(ctx context.Context, c *cli.Command) (context.Context, error) { return func(ctx context.Context, c *cli.Command) (context.Context, error) {
if setting.InstallLock {
// During config loading, there might also be logs (for example: deprecation warnings).
// It must make sure that console logger is set up before config is loaded.
log.Error("Config is loaded before console logger is setup, it will cause bugs. Please fix it.")
return nil, errors.New("console logger must be setup before config is loaded")
}
level := defaultLevel level := defaultLevel
if globalBool(c, "quiet") { if globalBool(c, "quiet") {
level = log.FATAL level = log.FATAL

View File

@ -19,7 +19,7 @@ import (
var CmdKeys = &cli.Command{ var CmdKeys = &cli.Command{
Name: "keys", Name: "keys",
Usage: "(internal) Should only be called by SSH server", Usage: "(internal) Should only be called by SSH server",
Hidden: true, // internal commands shouldn't not be visible Hidden: true, // internal commands shouldn't be visible
Description: "Queries the Gitea database to get the authorized command for a given ssh key fingerprint", Description: "Queries the Gitea database to get the authorized command for a given ssh key fingerprint",
Before: PrepareConsoleLoggerLevel(log.FATAL), Before: PrepareConsoleLoggerLevel(log.FATAL),
Action: runKeys, Action: runKeys,

View File

@ -50,11 +50,15 @@ DEFAULT CONFIGURATION:
func prepareSubcommandWithGlobalFlags(originCmd *cli.Command) { func prepareSubcommandWithGlobalFlags(originCmd *cli.Command) {
originBefore := originCmd.Before originBefore := originCmd.Before
originCmd.Before = func(ctx context.Context, cmd *cli.Command) (context.Context, error) { originCmd.Before = func(ctxOrig context.Context, cmd *cli.Command) (ctx context.Context, err error) {
prepareWorkPathAndCustomConf(cmd) ctx = ctxOrig
if originBefore != nil { if originBefore != nil {
return originBefore(ctx, cmd) ctx, err = originBefore(ctx, cmd)
if err != nil {
return ctx, err
}
} }
prepareWorkPathAndCustomConf(cmd)
return ctx, nil return ctx, nil
} }
} }

View File

@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v3"
@ -28,11 +29,11 @@ func makePathOutput(workPath, customPath, customConf string) string {
return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf) return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf)
} }
func newTestApp(testCmdAction cli.ActionFunc) *cli.Command { func newTestApp(testCmd cli.Command) *cli.Command {
app := NewMainApp(AppVersion{}) app := NewMainApp(AppVersion{})
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction} testCmd.Name = util.IfZero(testCmd.Name, "test-cmd")
prepareSubcommandWithGlobalFlags(testCmd) prepareSubcommandWithGlobalFlags(&testCmd)
app.Commands = append(app.Commands, testCmd) app.Commands = append(app.Commands, &testCmd)
app.DefaultCommand = testCmd.Name app.DefaultCommand = testCmd.Name
return app return app
} }
@ -156,9 +157,11 @@ func TestCliCmd(t *testing.T) {
for _, c := range cases { for _, c := range cases {
t.Run(c.cmd, func(t *testing.T) { t.Run(c.cmd, func(t *testing.T) {
app := newTestApp(func(ctx context.Context, cmd *cli.Command) error { app := newTestApp(cli.Command{
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf)) Action: func(ctx context.Context, cmd *cli.Command) error {
return nil _, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
return nil
},
}) })
for k, v := range c.env { for k, v := range c.env {
t.Setenv(k, v) t.Setenv(k, v)
@ -173,31 +176,54 @@ func TestCliCmd(t *testing.T) {
} }
func TestCliCmdError(t *testing.T) { func TestCliCmdError(t *testing.T) {
app := newTestApp(func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") }) app := newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") }})
r, err := runTestApp(app, "./gitea", "test-cmd") r, err := runTestApp(app, "./gitea", "test-cmd")
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode) assert.Equal(t, 1, r.ExitCode)
assert.Empty(t, r.Stdout) assert.Empty(t, r.Stdout)
assert.Equal(t, "Command error: normal error\n", r.Stderr) assert.Equal(t, "Command error: normal error\n", r.Stderr)
app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) }) app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) }})
r, err = runTestApp(app, "./gitea", "test-cmd") r, err = runTestApp(app, "./gitea", "test-cmd")
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, 2, r.ExitCode) assert.Equal(t, 2, r.ExitCode)
assert.Empty(t, r.Stdout) assert.Empty(t, r.Stdout)
assert.Equal(t, "exit error\n", r.Stderr) assert.Equal(t, "exit error\n", r.Stderr)
app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return nil }) app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }})
r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such") r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such")
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode) assert.Equal(t, 1, r.ExitCode)
assert.Empty(t, r.Stdout) assert.Empty(t, r.Stdout)
assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr) assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr)
app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return nil }) app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }})
r, err = runTestApp(app, "./gitea", "test-cmd") r, err = runTestApp(app, "./gitea", "test-cmd")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called
assert.Empty(t, r.Stdout) assert.Empty(t, r.Stdout)
assert.Empty(t, r.Stderr) assert.Empty(t, r.Stderr)
} }
func TestCliCmdBefore(t *testing.T) {
ctxNew := context.WithValue(context.Background(), any("key"), "value")
configValues := map[string]string{}
setting.CustomConf = "/tmp/any.ini"
var actionCtx context.Context
app := newTestApp(cli.Command{
Before: func(context.Context, *cli.Command) (context.Context, error) {
configValues["before"] = setting.CustomConf
return ctxNew, nil
},
Action: func(ctx context.Context, cmd *cli.Command) error {
configValues["action"] = setting.CustomConf
actionCtx = ctx
return nil
},
})
_, err := runTestApp(app, "./gitea", "--config", "/dev/null", "test-cmd")
assert.NoError(t, err)
assert.Equal(t, ctxNew, actionCtx)
assert.Equal(t, "/tmp/any.ini", configValues["before"], "BeforeFunc must be called before preparing config")
assert.Equal(t, "/dev/null", configValues["action"])
}

View File

@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/cmd" "code.gitea.io/gitea/cmd"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -36,13 +37,15 @@ func Test_CmdKeys(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
// FIXME: this test is not quite right. Each "command run" always re-initializes settings
defer test.MockVariableValue(&cmd.CmdKeys.Before, nil)() // don't re-initialize logger during the test
var stdout, stderr bytes.Buffer var stdout, stderr bytes.Buffer
app := &cli.Command{ app := &cli.Command{
Writer: &stdout, Writer: &stdout,
ErrWriter: &stderr, ErrWriter: &stderr,
Commands: []*cli.Command{cmd.CmdKeys}, Commands: []*cli.Command{cmd.CmdKeys},
} }
cmd.CmdKeys.HideHelp = true
err := app.Run(t.Context(), append([]string{"prog"}, tt.args...)) err := app.Run(t.Context(), append([]string{"prog"}, tt.args...))
if tt.wantErr { if tt.wantErr {
assert.Error(t, err) assert.Error(t, err)