Skip to content

Commit 500d42b

Browse files
committed
fix(doctor): print each check group as it completes
Every group was collected before anything was printed, so one slow check withheld the entire report including groups that had already passed. Order, formatting, summary and exit codes are unchanged.
1 parent 1de4e9e commit 500d42b

5 files changed

Lines changed: 84 additions & 29 deletions

File tree

pkg/cmd/doctor.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@ Exit codes:
5353
config.InitConfig()
5454
},
5555
Run: func(_ *cobra.Command, _ []string) {
56-
results := doctor.Run()
56+
doctor.PrintHeader(os.Stdout)
5757

58-
doctor.PrintResults(os.Stdout, results)
58+
results := doctor.Run(func(group doctor.CheckGroup) {
59+
doctor.PrintGroup(os.Stdout, group)
60+
})
5961

6062
failed := doctor.CountFailed(results)
6163
if failed > 0 {

pkg/doctor/doctor.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,22 @@
1717
// Package doctor provides diagnostic checks for Vikunja installations.
1818
package doctor
1919

20-
// Run executes all diagnostic checks and returns the results.
21-
func Run() []CheckGroup {
22-
groups := []CheckGroup{
23-
CheckSystem(),
24-
CheckConfig(),
25-
CheckDatabase(),
26-
CheckFiles(),
20+
// Run executes all diagnostic checks in order and returns the results. Each group is
21+
// passed to emit as soon as it completes, so a slow check does not withhold the
22+
// groups before it.
23+
func Run(emit func(CheckGroup)) []CheckGroup {
24+
var groups []CheckGroup
25+
collect := func(group CheckGroup) {
26+
groups = append(groups, group)
27+
emit(group)
2728
}
2829

29-
// Add optional service checks
30-
groups = append(groups, CheckOptionalServices()...)
30+
collect(CheckSystem())
31+
collect(CheckConfig())
32+
collect(CheckDatabase())
33+
collect(CheckFiles())
34+
35+
CheckOptionalServices(collect)
3136

3237
return groups
3338
}

pkg/doctor/output.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,20 @@ var (
2929
bold = color.New(color.Bold).SprintFunc()
3030
)
3131

32-
// PrintResults writes all check groups to the given writer with colored output.
33-
func PrintResults(w io.Writer, groups []CheckGroup) {
32+
// PrintHeader writes the report header to the given writer.
33+
func PrintHeader(w io.Writer) {
3434
fmt.Fprintln(w, bold("Vikunja Doctor"))
3535
fmt.Fprintln(w, "==============")
3636
fmt.Fprintln(w)
37+
}
3738

38-
for _, group := range groups {
39-
fmt.Fprintln(w, bold(group.Name))
40-
for _, result := range group.Results {
41-
printResult(w, result)
42-
}
43-
fmt.Fprintln(w)
39+
// PrintGroup writes a single check group to the given writer with colored output.
40+
func PrintGroup(w io.Writer, group CheckGroup) {
41+
fmt.Fprintln(w, bold(group.Name))
42+
for _, result := range group.Results {
43+
printResult(w, result)
4444
}
45+
fmt.Fprintln(w)
4546
}
4647

4748
func printResult(w io.Writer, result CheckResult) {

pkg/doctor/output_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Vikunja is a to-do list application to facilitate your life.
2+
// Copyright 2018-present Vikunja and contributors. All rights reserved.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package doctor
18+
19+
import (
20+
"bytes"
21+
"testing"
22+
23+
"github.com/stretchr/testify/assert"
24+
)
25+
26+
func TestPrintGroup(t *testing.T) {
27+
var buf bytes.Buffer
28+
29+
PrintHeader(&buf)
30+
PrintGroup(&buf, CheckGroup{
31+
Name: "Files (s3)",
32+
Results: []CheckResult{
33+
{Name: "Endpoint", Passed: true, Value: "http://localhost:9000"},
34+
{Name: "Initialization", Passed: false, Error: "S3 endpoint http://localhost:9000 did not respond within 12s"},
35+
{Name: "CORS", Passed: true, Value: "2 origins", Lines: []string{"a", "b"}},
36+
},
37+
})
38+
39+
assert.Equal(t, `Vikunja Doctor
40+
==============
41+
42+
Files (s3)
43+
✓ Endpoint: http://localhost:9000
44+
✗ Initialization: S3 endpoint http://localhost:9000 did not respond within 12s
45+
✓ CORS: 2 origins
46+
a
47+
b
48+
49+
`, buf.String())
50+
}

pkg/doctor/services.go

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,27 +31,24 @@ import (
3131
"code.vikunja.io/api/pkg/red"
3232
)
3333

34-
// CheckOptionalServices returns check groups for all enabled optional services.
35-
func CheckOptionalServices() []CheckGroup {
36-
var groups []CheckGroup
37-
34+
// CheckOptionalServices runs the checks for all enabled optional services, passing
35+
// each group to emit as it completes.
36+
func CheckOptionalServices(emit func(CheckGroup)) {
3837
if config.RedisEnabled.GetBool() {
39-
groups = append(groups, checkRedis())
38+
emit(checkRedis())
4039
}
4140

4241
if config.MailerEnabled.GetBool() {
43-
groups = append(groups, checkMailer())
42+
emit(checkMailer())
4443
}
4544

4645
if config.AuthLdapEnabled.GetBool() {
47-
groups = append(groups, checkLDAP())
46+
emit(checkLDAP())
4847
}
4948

5049
if config.AuthOpenIDEnabled.GetBool() {
51-
groups = append(groups, checkOpenID())
50+
emit(checkOpenID())
5251
}
53-
54-
return groups
5552
}
5653

5754
func checkRedis() CheckGroup {

0 commit comments

Comments
 (0)