Diff
1diff --git a/cmd/soft/root.go b/cmd/soft/root.go
2index 5fc9e4588e6dcc9fb57169b6ea1a857c1d899c44..af4c56c3f97d518e569e9b63aede54a2106808a6 100644
3--- a/cmd/soft/root.go
4+++ b/cmd/soft/root.go
5@@ -1,9 +1,9 @@
6 package main
7
8 import (
9+ "os"
10 "runtime/debug"
11
12- "github.com/charmbracelet/log"
13 _ "github.com/charmbracelet/soft-serve/log"
14 "github.com/spf13/cobra"
15 )
16@@ -18,12 +18,10 @@ var (
17 CommitSHA = ""
18
19 rootCmd = &cobra.Command{
20- Use: "soft",
21- Short: "A self-hostable Git server for the command line",
22- Long: "Soft Serve is a self-hostable Git server for the command line.",
23- RunE: func(cmd *cobra.Command, args []string) error {
24- return cmd.Help()
25- },
26+ Use: "soft",
27+ Short: "A self-hostable Git server for the command line",
28+ Long: "Soft Serve is a self-hostable Git server for the command line.",
29+ SilenceUsage: true,
30 }
31 )
32
33@@ -52,6 +50,6 @@ func init() {
34
35 func main() {
36 if err := rootCmd.Execute(); err != nil {
37- log.Fatal(err)
38+ os.Exit(1)
39 }
40 }
41diff --git a/cmd/soft/serve.go b/cmd/soft/serve.go
42index 332796394866e8b1fe6275985ccf771a589aa467..5841f2cdd36113eab15b5b07fd0b5084e82f4012 100644
43--- a/cmd/soft/serve.go
44+++ b/cmd/soft/serve.go
45@@ -25,18 +25,19 @@ var (
46 return err
47 }
48
49+ ctx := cmd.Context()
50 done := make(chan os.Signal, 1)
51 lch := make(chan error, 1)
52 go func() {
53 defer close(lch)
54 defer close(done)
55- lch <- s.Start()
56+ lch <- s.Start(ctx)
57 }()
58
59 signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
60 <-done
61
62- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
63+ ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
64 defer cancel()
65 if err := s.Shutdown(ctx); err != nil {
66 return err
67diff --git a/server/config/file.go b/server/config/file.go
68index f85c7247b564ce3d2c1f9a8ae65f70ff0849bccb..7079ff3033b4163c7ac07257e90750c722e7a928 100644
69--- a/server/config/file.go
70+++ b/server/config/file.go
71@@ -59,10 +59,10 @@ http:
72 # The address on which the HTTP server will listen.
73 listen_addr: "{{ .HTTP.ListenAddr }}"
74
75- # The relative path to the TLS private key.
76+ # The path to the TLS private key.
77 tls_key_path: "{{ .HTTP.TLSKeyPath }}"
78
79- # The relative path to the TLS certificate.
80+ # The path to the TLS certificate.
81 tls_cert_path: "{{ .HTTP.TLSCertPath }}"
82
83 # The public URL of the HTTP server.
84@@ -79,6 +79,6 @@ stats:
85
86 func newConfigFile(cfg *Config) string {
87 var b bytes.Buffer
88- configFileTmpl.Execute(&b, cfg)
89+ configFileTmpl.Execute(&b, cfg) // nolint: errcheck
90 return b.String()
91 }
92diff --git a/server/server.go b/server/server.go
93index de227c54287853bf5cbb73a7bdeb1e68b52809d9..992e0c7c13c0a656afd08a3c764481fe393d9b5a 100644
94--- a/server/server.go
95+++ b/server/server.go
96@@ -2,6 +2,7 @@ package server
97
98 import (
99 "context"
100+ "errors"
101 "net/http"
102 "path/filepath"
103
104@@ -101,33 +102,48 @@ func NewServer(cfg *config.Config) (*Server, error) {
105 return srv, nil
106 }
107
108+func start(ctx context.Context, fn func() error) error {
109+ errc := make(chan error, 1)
110+ go func() {
111+ errc <- fn()
112+ }()
113+
114+ select {
115+ case err := <-errc:
116+ return err
117+ case <-ctx.Done():
118+ return ctx.Err()
119+ }
120+}
121+
122 // Start starts the SSH server.
123-func (s *Server) Start() error {
124- var errg errgroup.Group
125+func (s *Server) Start(ctx context.Context) error {
126+ var errg *errgroup.Group
127+ errg, ctx = errgroup.WithContext(ctx)
128 errg.Go(func() error {
129 log.Print("Starting Git daemon", "addr", s.Config.Git.ListenAddr)
130- if err := s.GitDaemon.Start(); err != ErrServerClosed {
131+ if err := start(ctx, s.GitDaemon.Start); !errors.Is(err, ErrServerClosed) {
132 return err
133 }
134 return nil
135 })
136 errg.Go(func() error {
137 log.Print("Starting HTTP server", "addr", s.Config.HTTP.ListenAddr)
138- if err := s.HTTPServer.ListenAndServe(); err != http.ErrServerClosed {
139+ if err := start(ctx, s.HTTPServer.ListenAndServe); !errors.Is(err, http.ErrServerClosed) {
140 return err
141 }
142 return nil
143 })
144 errg.Go(func() error {
145 log.Print("Starting SSH server", "addr", s.Config.SSH.ListenAddr)
146- if err := s.SSHServer.ListenAndServe(); err != ssh.ErrServerClosed {
147+ if err := start(ctx, s.SSHServer.ListenAndServe); !errors.Is(err, ssh.ErrServerClosed) {
148 return err
149 }
150 return nil
151 })
152 errg.Go(func() error {
153 log.Print("Starting Stats server", "addr", s.Config.Stats.ListenAddr)
154- if err := s.StatsServer.ListenAndServe(); err != http.ErrServerClosed {
155+ if err := start(ctx, s.StatsServer.ListenAndServe); !errors.Is(err, http.ErrServerClosed) {
156 return err
157 }
158 return nil
159diff --git a/server/server_test.go b/server/server_test.go
160index 5505bec565280f8ed92c0487eed52c31c26c9910..07066d976412aacb4f1fb174609587a4731edac2 100644
161--- a/server/server_test.go
162+++ b/server/server_test.go
163@@ -1,6 +1,7 @@
164 package server
165
166 import (
167+ "context"
168 "fmt"
169 "net"
170 "path/filepath"
171@@ -38,7 +39,7 @@ func setupServer(tb testing.TB) (*Server, *config.Config, string) {
172 }
173 go func() {
174 tb.Log("starting server")
175- s.Start()
176+ s.Start(context.TODO())
177 }()
178 tb.Cleanup(func() {
179 s.Close()