@@ -8,12 +8,15 @@ import (
8
8
"bufio"
9
9
"errors"
10
10
"fmt"
11
+ "io/ioutil"
11
12
"os"
12
13
"os/exec"
13
14
"path/filepath"
14
15
"regexp"
15
16
"strings"
16
17
18
+ "code.gitea.io/gitea/modules/log"
19
+ "code.gitea.io/gitea/modules/options"
17
20
"code.gitea.io/gitea/modules/setting"
18
21
19
22
"github.com/urfave/cli"
@@ -22,18 +25,27 @@ import (
22
25
// CmdDoctor represents the available doctor sub-command.
23
26
var CmdDoctor = cli.Command {
24
27
Name : "doctor" ,
25
- Usage : "Diagnose the problems" ,
26
- Description : "A command to diagnose the problems of current gitea instance according the given configuration." ,
28
+ Usage : "Diagnose problems" ,
29
+ Description : "A command to diagnose problems with the current Gitea instance according to the given configuration." ,
27
30
Action : runDoctor ,
28
31
}
29
32
30
33
type check struct {
31
- title string
32
- f func (ctx * cli.Context ) ([]string , error )
34
+ title string
35
+ f func (ctx * cli.Context ) ([]string , error )
36
+ abortIfFailed bool
37
+ skipDatabaseInit bool
33
38
}
34
39
35
40
// checklist represents list for all checks
36
41
var checklist = []check {
42
+ {
43
+ // NOTE: this check should be the first in the list
44
+ title : "Check paths and basic configuration" ,
45
+ f : runDoctorPathInfo ,
46
+ abortIfFailed : true ,
47
+ skipDatabaseInit : true ,
48
+ },
37
49
{
38
50
title : "Check if OpenSSH authorized_keys file id correct" ,
39
51
f : runDoctorLocationMoved ,
@@ -42,21 +54,33 @@ var checklist = []check{
42
54
}
43
55
44
56
func runDoctor (ctx * cli.Context ) error {
45
- err := initDB ()
46
- fmt . Println ( "Using app.ini at" , setting . CustomConf )
47
- if err != nil {
48
- fmt . Println ( err )
49
- fmt . Println ( "Check if you are using the right config file. You can use a --config directive to specify one." )
50
- return nil
51
- }
57
+
58
+ // Silence the console logger
59
+ // TODO: Redirect all logs into `doctor.log` ignoring any other log configuration
60
+ log . DelNamedLogger ( "console" )
61
+ log . DelNamedLogger ( log . DEFAULT )
62
+
63
+ dbIsInit := false
52
64
53
65
for i , check := range checklist {
66
+ if ! dbIsInit && ! check .skipDatabaseInit {
67
+ // Only open database after the most basic configuration check
68
+ if err := initDB (); err != nil {
69
+ fmt .Println (err )
70
+ fmt .Println ("Check if you are using the right config file. You can use a --config directive to specify one." )
71
+ return nil
72
+ }
73
+ dbIsInit = true
74
+ }
54
75
fmt .Println ("[" , i + 1 , "]" , check .title )
55
- if messages , err := check .f (ctx ); err != nil {
76
+ messages , err := check .f (ctx )
77
+ for _ , message := range messages {
78
+ fmt .Println ("-" , message )
79
+ }
80
+ if err != nil {
56
81
fmt .Println ("Error:" , err )
57
- } else if len (messages ) > 0 {
58
- for _ , message := range messages {
59
- fmt .Println ("-" , message )
82
+ if check .abortIfFailed {
83
+ return nil
60
84
}
61
85
} else {
62
86
fmt .Println ("OK." )
@@ -74,6 +98,79 @@ func exePath() (string, error) {
74
98
return filepath .Abs (file )
75
99
}
76
100
101
+ func runDoctorPathInfo (ctx * cli.Context ) ([]string , error ) {
102
+
103
+ res := make ([]string , 0 , 10 )
104
+
105
+ if fi , err := os .Stat (setting .CustomConf ); err != nil || ! fi .Mode ().IsRegular () {
106
+ res = append (res , fmt .Sprintf ("Failed to find configuration file at '%s'." , setting .CustomConf ))
107
+ res = append (res , fmt .Sprintf ("If you've never ran Gitea yet, this is normal and '%s' will be created for you on first run." , setting .CustomConf ))
108
+ res = append (res , "Otherwise check that you are running this command from the correct path and/or provide a `--config` parameter." )
109
+ return res , fmt .Errorf ("can't proceed without a configuration file" )
110
+ }
111
+
112
+ setting .NewContext ()
113
+
114
+ fail := false
115
+
116
+ check := func (name , path string , is_dir , required , is_write bool ) {
117
+ res = append (res , fmt .Sprintf ("%-25s '%s'" , name + ":" , path ))
118
+ if fi , err := os .Stat (path ); err != nil {
119
+ if required {
120
+ res = append (res , fmt .Sprintf (" ERROR: %v" , err ))
121
+ fail = true
122
+ } else {
123
+ res = append (res , fmt .Sprintf (" NOTICE: not accessible (%v)" , err ))
124
+ }
125
+ } else if is_dir && ! fi .IsDir () {
126
+ res = append (res , " ERROR: not a directory" )
127
+ fail = true
128
+ } else if ! is_dir && ! fi .Mode ().IsRegular () {
129
+ res = append (res , " ERROR: not a regular file" )
130
+ fail = true
131
+ } else if is_write {
132
+ if err := runDoctorWritableDir (path ); err != nil {
133
+ res = append (res , fmt .Sprintf (" ERROR: not writable: %v" , err ))
134
+ fail = true
135
+ }
136
+ }
137
+ }
138
+
139
+ // Note print paths inside quotes to make any leading/trailing spaces evident
140
+ check ("Configuration File Path" , setting .CustomConf , false , true , false )
141
+ check ("Repository Root Path" , setting .RepoRootPath , true , true , true )
142
+ check ("Data Root Path" , setting .AppDataPath , true , true , true )
143
+ check ("Custom File Root Path" , setting .CustomPath , true , false , false )
144
+ check ("Work directory" , setting .AppWorkPath , true , true , false )
145
+ check ("Log Root Path" , setting .LogRootPath , true , true , true )
146
+
147
+ if options .IsDynamic () {
148
+ // Do not check/report on StaticRootPath if data is embedded in Gitea (-tags bindata)
149
+ check ("Static File Root Path" , setting .StaticRootPath , true , true , false )
150
+ }
151
+
152
+ if fail {
153
+ return res , fmt .Errorf ("please check your configuration file and try again" )
154
+ }
155
+
156
+ return res , nil
157
+ }
158
+
159
+ func runDoctorWritableDir (path string ) error {
160
+ // There's no platform-independent way of checking if a directory is writable
161
+ // https://stackoverflow.com/questions/20026320/how-to-tell-if-folder-exists-and-is-writable
162
+
163
+ tmpFile , err := ioutil .TempFile (path , "doctors-order" )
164
+ if err != nil {
165
+ return err
166
+ }
167
+ if err := os .Remove (tmpFile .Name ()); err != nil {
168
+ fmt .Printf ("Warning: can't remove temporary file: '%s'\n " , tmpFile .Name ())
169
+ }
170
+ tmpFile .Close ()
171
+ return nil
172
+ }
173
+
77
174
func runDoctorLocationMoved (ctx * cli.Context ) ([]string , error ) {
78
175
if setting .SSH .StartBuiltinServer || ! setting .SSH .CreateAuthorizedKeysFile {
79
176
return nil , nil
0 commit comments