2020-12-02 07:56:04 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2020-12-02 07:56:04 +03:00
package doctor
import (
2022-01-20 02:26:57 +03:00
"context"
2020-12-02 07:56:04 +03:00
"fmt"
"os"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
"code.gitea.io/gitea/modules/setting"
)
type configurationFile struct {
Name string
Path string
IsDirectory bool
Required bool
Writable bool
}
func checkConfigurationFile ( logger log . Logger , autofix bool , fileOpts configurationFile ) error {
logger . Info ( ` %-26s %q ` , log . NewColoredValue ( fileOpts . Name + ":" , log . Reset ) , fileOpts . Path )
fi , err := os . Stat ( fileOpts . Path )
if err != nil {
if os . IsNotExist ( err ) && autofix && fileOpts . IsDirectory {
2022-01-20 20:46:10 +03:00
if err := os . MkdirAll ( fileOpts . Path , 0 o777 ) ; err != nil {
2020-12-02 07:56:04 +03:00
logger . Error ( " Directory does not exist and could not be created. ERROR: %v" , err )
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "Configuration directory: \"%q\" does not exist and could not be created. ERROR: %w" , fileOpts . Path , err )
2020-12-02 07:56:04 +03:00
}
fi , err = os . Stat ( fileOpts . Path )
}
}
if err != nil {
if fileOpts . Required {
logger . Error ( " Is REQUIRED but is not accessible. ERROR: %v" , err )
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "Configuration file \"%q\" is not accessible but is required. Error: %w" , fileOpts . Path , err )
2020-12-02 07:56:04 +03:00
}
logger . Warn ( " NOTICE: is not accessible (Error: %v)" , err )
// this is a non-critical error
return nil
}
if fileOpts . IsDirectory && ! fi . IsDir ( ) {
logger . Error ( " ERROR: not a directory" )
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "Configuration directory \"%q\" is not a directory. Error: %w" , fileOpts . Path , err )
2020-12-02 07:56:04 +03:00
} else if ! fileOpts . IsDirectory && ! fi . Mode ( ) . IsRegular ( ) {
logger . Error ( " ERROR: not a regular file" )
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "Configuration file \"%q\" is not a regular file. Error: %w" , fileOpts . Path , err )
2020-12-02 07:56:04 +03:00
} else if fileOpts . Writable {
if err := isWritableDir ( fileOpts . Path ) ; err != nil {
logger . Error ( " ERROR: is required to be writable but is not writable: %v" , err )
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "Configuration file \"%q\" is required to be writable but is not. Error: %w" , fileOpts . Path , err )
2020-12-02 07:56:04 +03:00
}
}
return nil
}
2022-01-20 02:26:57 +03:00
func checkConfigurationFiles ( ctx context . Context , logger log . Logger , autofix bool ) error {
2020-12-02 07:56:04 +03:00
if fi , err := os . Stat ( setting . CustomConf ) ; err != nil || ! fi . Mode ( ) . IsRegular ( ) {
logger . Error ( "Failed to find configuration file at '%s'." , setting . CustomConf )
logger . Error ( "If you've never ran Gitea yet, this is normal and '%s' will be created for you on first run." , setting . CustomConf )
logger . Error ( "Otherwise check that you are running this command from the correct path and/or provide a `--config` parameter." )
logger . Critical ( "Cannot proceed without a configuration file" )
return err
}
2023-02-19 19:12:01 +03:00
setting . InitProviderFromExistingFile ( )
setting . LoadCommonSettings ( )
2020-12-02 07:56:04 +03:00
configurationFiles := [ ] configurationFile {
{ "Configuration File Path" , setting . CustomConf , false , true , false } ,
{ "Repository Root Path" , setting . RepoRootPath , true , true , true } ,
{ "Data Root Path" , setting . AppDataPath , true , true , true } ,
{ "Custom File Root Path" , setting . CustomPath , true , false , false } ,
{ "Work directory" , setting . AppWorkPath , true , true , false } ,
2023-02-19 19:12:01 +03:00
{ "Log Root Path" , setting . Log . RootPath , true , true , true } ,
2020-12-02 07:56:04 +03:00
}
if options . IsDynamic ( ) {
configurationFiles = append ( configurationFiles , configurationFile { "Static File Root Path" , setting . StaticRootPath , true , true , false } )
}
numberOfErrors := 0
for _ , configurationFile := range configurationFiles {
if err := checkConfigurationFile ( logger , autofix , configurationFile ) ; err != nil {
numberOfErrors ++
}
}
if numberOfErrors > 0 {
logger . Critical ( "Please check your configuration files and try again." )
return fmt . Errorf ( "%d configuration files with errors" , numberOfErrors )
}
return nil
}
func isWritableDir ( path string ) error {
// There's no platform-independent way of checking if a directory is writable
// https://stackoverflow.com/questions/20026320/how-to-tell-if-folder-exists-and-is-writable
2021-09-22 08:38:34 +03:00
tmpFile , err := os . CreateTemp ( path , "doctors-order" )
2020-12-02 07:56:04 +03:00
if err != nil {
return err
}
if err := os . Remove ( tmpFile . Name ( ) ) ; err != nil {
fmt . Printf ( "Warning: can't remove temporary file: '%s'\n" , tmpFile . Name ( ) )
}
tmpFile . Close ( )
return nil
}
func init ( ) {
Register ( & Check {
Title : "Check paths and basic configuration" ,
Name : "paths" ,
IsDefault : true ,
Run : checkConfigurationFiles ,
AbortIfFailed : true ,
SkipDatabaseInitialization : true ,
Priority : 1 ,
} )
}