mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-27 00:23:41 +09:00 
			
		
		
		
	Improve doctor cli behavior (#28422)
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
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