code.gitea.io/gitea@v1.19.3/modules/doctor/paths.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 11 "code.gitea.io/gitea/modules/log" 12 "code.gitea.io/gitea/modules/options" 13 "code.gitea.io/gitea/modules/setting" 14 ) 15 16 type configurationFile struct { 17 Name string 18 Path string 19 IsDirectory bool 20 Required bool 21 Writable bool 22 } 23 24 func checkConfigurationFile(logger log.Logger, autofix bool, fileOpts configurationFile) error { 25 logger.Info(`%-26s %q`, log.NewColoredValue(fileOpts.Name+":", log.Reset), fileOpts.Path) 26 fi, err := os.Stat(fileOpts.Path) 27 if err != nil { 28 if os.IsNotExist(err) && autofix && fileOpts.IsDirectory { 29 if err := os.MkdirAll(fileOpts.Path, 0o777); err != nil { 30 logger.Error(" Directory does not exist and could not be created. ERROR: %v", err) 31 return fmt.Errorf("Configuration directory: \"%q\" does not exist and could not be created. ERROR: %w", fileOpts.Path, err) 32 } 33 fi, err = os.Stat(fileOpts.Path) 34 } 35 } 36 if err != nil { 37 if fileOpts.Required { 38 logger.Error(" Is REQUIRED but is not accessible. ERROR: %v", err) 39 return fmt.Errorf("Configuration file \"%q\" is not accessible but is required. Error: %w", fileOpts.Path, err) 40 } 41 logger.Warn(" NOTICE: is not accessible (Error: %v)", err) 42 // this is a non-critical error 43 return nil 44 } 45 46 if fileOpts.IsDirectory && !fi.IsDir() { 47 logger.Error(" ERROR: not a directory") 48 return fmt.Errorf("Configuration directory \"%q\" is not a directory. Error: %w", fileOpts.Path, err) 49 } else if !fileOpts.IsDirectory && !fi.Mode().IsRegular() { 50 logger.Error(" ERROR: not a regular file") 51 return fmt.Errorf("Configuration file \"%q\" is not a regular file. Error: %w", fileOpts.Path, err) 52 } else if fileOpts.Writable { 53 if err := isWritableDir(fileOpts.Path); err != nil { 54 logger.Error(" ERROR: is required to be writable but is not writable: %v", err) 55 return fmt.Errorf("Configuration file \"%q\" is required to be writable but is not. Error: %w", fileOpts.Path, err) 56 } 57 } 58 return nil 59 } 60 61 func checkConfigurationFiles(ctx context.Context, logger log.Logger, autofix bool) error { 62 if fi, err := os.Stat(setting.CustomConf); err != nil || !fi.Mode().IsRegular() { 63 logger.Error("Failed to find configuration file at '%s'.", setting.CustomConf) 64 logger.Error("If you've never ran Gitea yet, this is normal and '%s' will be created for you on first run.", setting.CustomConf) 65 logger.Error("Otherwise check that you are running this command from the correct path and/or provide a `--config` parameter.") 66 logger.Critical("Cannot proceed without a configuration file") 67 return err 68 } 69 70 setting.InitProviderFromExistingFile() 71 setting.LoadCommonSettings() 72 73 configurationFiles := []configurationFile{ 74 {"Configuration File Path", setting.CustomConf, false, true, false}, 75 {"Repository Root Path", setting.RepoRootPath, true, true, true}, 76 {"Data Root Path", setting.AppDataPath, true, true, true}, 77 {"Custom File Root Path", setting.CustomPath, true, false, false}, 78 {"Work directory", setting.AppWorkPath, true, true, false}, 79 {"Log Root Path", setting.Log.RootPath, true, true, true}, 80 } 81 82 if options.IsDynamic() { 83 configurationFiles = append(configurationFiles, configurationFile{"Static File Root Path", setting.StaticRootPath, true, true, false}) 84 } 85 86 numberOfErrors := 0 87 for _, configurationFile := range configurationFiles { 88 if err := checkConfigurationFile(logger, autofix, configurationFile); err != nil { 89 numberOfErrors++ 90 } 91 } 92 93 if numberOfErrors > 0 { 94 logger.Critical("Please check your configuration files and try again.") 95 return fmt.Errorf("%d configuration files with errors", numberOfErrors) 96 } 97 98 return nil 99 } 100 101 func isWritableDir(path string) error { 102 // There's no platform-independent way of checking if a directory is writable 103 // https://stackoverflow.com/questions/20026320/how-to-tell-if-folder-exists-and-is-writable 104 105 tmpFile, err := os.CreateTemp(path, "doctors-order") 106 if err != nil { 107 return err 108 } 109 if err := os.Remove(tmpFile.Name()); err != nil { 110 fmt.Printf("Warning: can't remove temporary file: '%s'\n", tmpFile.Name()) 111 } 112 tmpFile.Close() 113 return nil 114 } 115 116 func init() { 117 Register(&Check{ 118 Title: "Check paths and basic configuration", 119 Name: "paths", 120 IsDefault: true, 121 Run: checkConfigurationFiles, 122 AbortIfFailed: true, 123 SkipDatabaseInitialization: true, 124 Priority: 1, 125 }) 126 }