mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Backport #28422 by wxiaoguang 1. Do not sort the "checks" slice again and again when "Register", it just wastes CPU when the Gitea instance runs 2. If a check doesn't exist, tell the end user 3. Add some tests Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		@@ -14,6 +14,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	"code.gitea.io/gitea/models/migrations"
 | 
						"code.gitea.io/gitea/models/migrations"
 | 
				
			||||||
	migrate_base "code.gitea.io/gitea/models/migrations/base"
 | 
						migrate_base "code.gitea.io/gitea/models/migrations/base"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/container"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/doctor"
 | 
						"code.gitea.io/gitea/modules/doctor"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
@@ -22,6 +23,19 @@ import (
 | 
				
			|||||||
	"xorm.io/xorm"
 | 
						"xorm.io/xorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CmdDoctor represents the available doctor sub-command.
 | 
				
			||||||
 | 
					var CmdDoctor = &cli.Command{
 | 
				
			||||||
 | 
						Name:        "doctor",
 | 
				
			||||||
 | 
						Usage:       "Diagnose and optionally fix problems",
 | 
				
			||||||
 | 
						Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Subcommands: []*cli.Command{
 | 
				
			||||||
 | 
							cmdDoctorCheck,
 | 
				
			||||||
 | 
							cmdRecreateTable,
 | 
				
			||||||
 | 
							cmdDoctorConvert,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var cmdDoctorCheck = &cli.Command{
 | 
					var cmdDoctorCheck = &cli.Command{
 | 
				
			||||||
	Name:        "check",
 | 
						Name:        "check",
 | 
				
			||||||
	Usage:       "Diagnose and optionally fix problems",
 | 
						Usage:       "Diagnose and optionally fix problems",
 | 
				
			||||||
@@ -60,19 +74,6 @@ var cmdDoctorCheck = &cli.Command{
 | 
				
			|||||||
	},
 | 
						},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CmdDoctor represents the available doctor sub-command.
 | 
					 | 
				
			||||||
var CmdDoctor = &cli.Command{
 | 
					 | 
				
			||||||
	Name:        "doctor",
 | 
					 | 
				
			||||||
	Usage:       "Diagnose and optionally fix problems",
 | 
					 | 
				
			||||||
	Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Subcommands: []*cli.Command{
 | 
					 | 
				
			||||||
		cmdDoctorCheck,
 | 
					 | 
				
			||||||
		cmdRecreateTable,
 | 
					 | 
				
			||||||
		cmdDoctorConvert,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var cmdRecreateTable = &cli.Command{
 | 
					var cmdRecreateTable = &cli.Command{
 | 
				
			||||||
	Name:      "recreate-table",
 | 
						Name:      "recreate-table",
 | 
				
			||||||
	Usage:     "Recreate tables from XORM definitions and copy the data.",
 | 
						Usage:     "Recreate tables from XORM definitions and copy the data.",
 | 
				
			||||||
@@ -177,6 +178,7 @@ func runDoctorCheck(ctx *cli.Context) error {
 | 
				
			|||||||
	if ctx.IsSet("list") {
 | 
						if ctx.IsSet("list") {
 | 
				
			||||||
		w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
 | 
							w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
 | 
				
			||||||
		_, _ = w.Write([]byte("Default\tName\tTitle\n"))
 | 
							_, _ = w.Write([]byte("Default\tName\tTitle\n"))
 | 
				
			||||||
 | 
							doctor.SortChecks(doctor.Checks)
 | 
				
			||||||
		for _, check := range doctor.Checks {
 | 
							for _, check := range doctor.Checks {
 | 
				
			||||||
			if check.IsDefault {
 | 
								if check.IsDefault {
 | 
				
			||||||
				_, _ = w.Write([]byte{'*'})
 | 
									_, _ = w.Write([]byte{'*'})
 | 
				
			||||||
@@ -192,25 +194,19 @@ func runDoctorCheck(ctx *cli.Context) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	var checks []*doctor.Check
 | 
						var checks []*doctor.Check
 | 
				
			||||||
	if ctx.Bool("all") {
 | 
						if ctx.Bool("all") {
 | 
				
			||||||
		checks = doctor.Checks
 | 
							checks = make([]*doctor.Check, len(doctor.Checks))
 | 
				
			||||||
 | 
							copy(checks, doctor.Checks)
 | 
				
			||||||
	} else if ctx.IsSet("run") {
 | 
						} else if ctx.IsSet("run") {
 | 
				
			||||||
		addDefault := ctx.Bool("default")
 | 
							addDefault := ctx.Bool("default")
 | 
				
			||||||
		names := ctx.StringSlice("run")
 | 
							runNamesSet := container.SetOf(ctx.StringSlice("run")...)
 | 
				
			||||||
		for i, name := range names {
 | 
					 | 
				
			||||||
			names[i] = strings.ToLower(strings.TrimSpace(name))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for _, check := range doctor.Checks {
 | 
							for _, check := range doctor.Checks {
 | 
				
			||||||
			if addDefault && check.IsDefault {
 | 
								if (addDefault && check.IsDefault) || runNamesSet.Contains(check.Name) {
 | 
				
			||||||
				checks = append(checks, check)
 | 
									checks = append(checks, check)
 | 
				
			||||||
				continue
 | 
									runNamesSet.Remove(check.Name)
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			for _, name := range names {
 | 
					 | 
				
			||||||
				if name == check.Name {
 | 
					 | 
				
			||||||
					checks = append(checks, check)
 | 
					 | 
				
			||||||
					break
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if len(runNamesSet) > 0 {
 | 
				
			||||||
 | 
								return fmt.Errorf("unknown checks: %q", strings.Join(runNamesSet.Values(), ","))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		for _, check := range doctor.Checks {
 | 
							for _, check := range doctor.Checks {
 | 
				
			||||||
@@ -219,6 +215,5 @@ func runDoctorCheck(ctx *cli.Context) error {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks)
 | 
						return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										33
									
								
								cmd/doctor_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								cmd/doctor_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					// Copyright 2023 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MIT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package cmd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/doctor"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
						"github.com/urfave/cli/v2"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDoctorRun(t *testing.T) {
 | 
				
			||||||
 | 
						doctor.Register(&doctor.Check{
 | 
				
			||||||
 | 
							Title: "Test Check",
 | 
				
			||||||
 | 
							Name:  "test-check",
 | 
				
			||||||
 | 
							Run:   func(ctx context.Context, logger log.Logger, autofix bool) error { return nil },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							SkipDatabaseInitialization: true,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						app := cli.NewApp()
 | 
				
			||||||
 | 
						app.Commands = []*cli.Command{cmdDoctorCheck}
 | 
				
			||||||
 | 
						err := app.Run([]string{"./gitea", "check", "--run", "test-check"})
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						err = app.Run([]string{"./gitea", "check", "--run", "no-such"})
 | 
				
			||||||
 | 
						assert.ErrorContains(t, err, `unknown checks: "no-such"`)
 | 
				
			||||||
 | 
						err = app.Run([]string{"./gitea", "check", "--run", "test-check,no-such"})
 | 
				
			||||||
 | 
						assert.ErrorContains(t, err, `unknown checks: "no-such"`)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -79,6 +79,7 @@ var Checks []*Check
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// RunChecks runs the doctor checks for the provided list
 | 
					// RunChecks runs the doctor checks for the provided list
 | 
				
			||||||
func RunChecks(ctx context.Context, colorize, autofix bool, checks []*Check) error {
 | 
					func RunChecks(ctx context.Context, colorize, autofix bool, checks []*Check) error {
 | 
				
			||||||
 | 
						SortChecks(checks)
 | 
				
			||||||
	// the checks output logs by a special logger, they do not use the default logger
 | 
						// the checks output logs by a special logger, they do not use the default logger
 | 
				
			||||||
	logger := log.BaseLoggerToGeneralLogger(&doctorCheckLogger{colorize: colorize})
 | 
						logger := log.BaseLoggerToGeneralLogger(&doctorCheckLogger{colorize: colorize})
 | 
				
			||||||
	loggerStep := log.BaseLoggerToGeneralLogger(&doctorCheckStepLogger{colorize: colorize})
 | 
						loggerStep := log.BaseLoggerToGeneralLogger(&doctorCheckStepLogger{colorize: colorize})
 | 
				
			||||||
@@ -104,20 +105,23 @@ func RunChecks(ctx context.Context, colorize, autofix bool, checks []*Check) err
 | 
				
			|||||||
			logger.Info("OK")
 | 
								logger.Info("OK")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	logger.Info("\nAll done.")
 | 
						logger.Info("\nAll done (checks: %d).", len(checks))
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Register registers a command with the list
 | 
					// Register registers a command with the list
 | 
				
			||||||
func Register(command *Check) {
 | 
					func Register(command *Check) {
 | 
				
			||||||
	Checks = append(Checks, command)
 | 
						Checks = append(Checks, command)
 | 
				
			||||||
	sort.SliceStable(Checks, func(i, j int) bool {
 | 
					 | 
				
			||||||
		if Checks[i].Priority == Checks[j].Priority {
 | 
					 | 
				
			||||||
			return Checks[i].Name < Checks[j].Name
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		if Checks[i].Priority == 0 {
 | 
					
 | 
				
			||||||
 | 
					func SortChecks(checks []*Check) {
 | 
				
			||||||
 | 
						sort.SliceStable(checks, func(i, j int) bool {
 | 
				
			||||||
 | 
							if checks[i].Priority == checks[j].Priority {
 | 
				
			||||||
 | 
								return checks[i].Name < checks[j].Name
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if checks[i].Priority == 0 {
 | 
				
			||||||
			return false
 | 
								return false
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return Checks[i].Priority < Checks[j].Priority
 | 
							return checks[i].Priority < checks[j].Priority
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user