code.gitea.io/gitea@v1.22.3/services/doctor/doctor.go (about) 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package doctor 5 6 import ( 7 "context" 8 "fmt" 9 "os" 10 "sort" 11 "strings" 12 13 "code.gitea.io/gitea/models/db" 14 "code.gitea.io/gitea/modules/git" 15 "code.gitea.io/gitea/modules/log" 16 "code.gitea.io/gitea/modules/setting" 17 "code.gitea.io/gitea/modules/storage" 18 ) 19 20 // Check represents a Doctor check 21 type Check struct { 22 Title string 23 Name string 24 IsDefault bool 25 Run func(ctx context.Context, logger log.Logger, autofix bool) error 26 AbortIfFailed bool 27 SkipDatabaseInitialization bool 28 Priority int 29 InitStorage bool 30 } 31 32 func initDBSkipLogger(ctx context.Context) error { 33 setting.MustInstalled() 34 setting.LoadDBSetting() 35 if err := db.InitEngine(ctx); err != nil { 36 return fmt.Errorf("db.InitEngine: %w", err) 37 } 38 // some doctor sub-commands need to use git command 39 if err := git.InitFull(ctx); err != nil { 40 return fmt.Errorf("git.InitFull: %w", err) 41 } 42 return nil 43 } 44 45 type doctorCheckLogger struct { 46 colorize bool 47 } 48 49 var _ log.BaseLogger = (*doctorCheckLogger)(nil) 50 51 func (d *doctorCheckLogger) Log(skip int, level log.Level, format string, v ...any) { 52 _, _ = fmt.Fprintf(os.Stdout, format+"\n", v...) 53 } 54 55 func (d *doctorCheckLogger) GetLevel() log.Level { 56 return log.TRACE 57 } 58 59 type doctorCheckStepLogger struct { 60 colorize bool 61 } 62 63 var _ log.BaseLogger = (*doctorCheckStepLogger)(nil) 64 65 func (d *doctorCheckStepLogger) Log(skip int, level log.Level, format string, v ...any) { 66 levelChar := fmt.Sprintf("[%s]", strings.ToUpper(level.String()[0:1])) 67 var levelArg any = levelChar 68 if d.colorize { 69 levelArg = log.NewColoredValue(levelChar, level.ColorAttributes()...) 70 } 71 args := append([]any{levelArg}, v...) 72 _, _ = fmt.Fprintf(os.Stdout, " - %s "+format+"\n", args...) 73 } 74 75 func (d *doctorCheckStepLogger) GetLevel() log.Level { 76 return log.TRACE 77 } 78 79 // Checks is the list of available commands 80 var Checks []*Check 81 82 // RunChecks runs the doctor checks for the provided list 83 func RunChecks(ctx context.Context, colorize, autofix bool, checks []*Check) error { 84 SortChecks(checks) 85 // the checks output logs by a special logger, they do not use the default logger 86 logger := log.BaseLoggerToGeneralLogger(&doctorCheckLogger{colorize: colorize}) 87 loggerStep := log.BaseLoggerToGeneralLogger(&doctorCheckStepLogger{colorize: colorize}) 88 dbIsInit := false 89 storageIsInit := false 90 for i, check := range checks { 91 if !dbIsInit && !check.SkipDatabaseInitialization { 92 // Only open database after the most basic configuration check 93 if err := initDBSkipLogger(ctx); err != nil { 94 logger.Error("Error whilst initializing the database: %v", err) 95 logger.Error("Check if you are using the right config file. You can use a --config directive to specify one.") 96 return nil 97 } 98 dbIsInit = true 99 } 100 if !storageIsInit && check.InitStorage { 101 if err := storage.Init(); err != nil { 102 logger.Error("Error whilst initializing the storage: %v", err) 103 logger.Error("Check if you are using the right config file. You can use a --config directive to specify one.") 104 return nil 105 } 106 storageIsInit = true 107 } 108 logger.Info("\n[%d] %s", i+1, check.Title) 109 if err := check.Run(ctx, loggerStep, autofix); err != nil { 110 if check.AbortIfFailed { 111 logger.Critical("FAIL") 112 return err 113 } 114 logger.Error("ERROR") 115 } else { 116 logger.Info("OK") 117 } 118 } 119 logger.Info("\nAll done (checks: %d).", len(checks)) 120 return nil 121 } 122 123 // Register registers a command with the list 124 func Register(command *Check) { 125 Checks = append(Checks, command) 126 } 127 128 func SortChecks(checks []*Check) { 129 sort.SliceStable(checks, func(i, j int) bool { 130 if checks[i].Priority == checks[j].Priority { 131 return checks[i].Name < checks[j].Name 132 } 133 if checks[i].Priority == 0 { 134 return false 135 } 136 return checks[i].Priority < checks[j].Priority 137 }) 138 }