You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

paths.go 4.4KB

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