serve.go

 1package main
 2
 3import (
 4	"context"
 5	"fmt"
 6	"os"
 7	"os/signal"
 8	"path/filepath"
 9	"syscall"
10	"time"
11
12	"github.com/charmbracelet/soft-serve/server"
13	"github.com/charmbracelet/soft-serve/server/config"
14	"github.com/spf13/cobra"
15)
16
17var (
18	serveCmd = &cobra.Command{
19		Use:   "serve",
20		Short: "Start the server",
21		Long:  "Start the server",
22		Args:  cobra.NoArgs,
23		RunE: func(cmd *cobra.Command, _ []string) error {
24			ctx := cmd.Context()
25
26			// Set up config
27			cfg := config.DefaultConfig()
28			if !cfg.Exist() {
29				if err := cfg.WriteConfig(); err != nil {
30					return fmt.Errorf("failed to write default config: %w", err)
31				}
32			}
33
34			ctx = config.WithContext(ctx, cfg)
35			cmd.SetContext(ctx)
36
37			// Create custom hooks directory if it doesn't exist
38			customHooksPath := filepath.Join(cfg.DataPath, "hooks")
39			if _, err := os.Stat(customHooksPath); err != nil && os.IsNotExist(err) {
40				os.MkdirAll(customHooksPath, os.ModePerm) // nolint: errcheck
41				// Generate update hook example without executable permissions
42				hookPath := filepath.Join(customHooksPath, "update.sample")
43				// nolint: gosec
44				if err := os.WriteFile(hookPath, []byte(updateHookExample), 0744); err != nil {
45					return fmt.Errorf("failed to generate update hook example: %w", err)
46				}
47			}
48
49			// Create log directory if it doesn't exist
50			logPath := filepath.Join(cfg.DataPath, "log")
51			if _, err := os.Stat(logPath); err != nil && os.IsNotExist(err) {
52				os.MkdirAll(logPath, os.ModePerm) // nolint: errcheck
53			}
54
55			s, err := server.NewServer(ctx)
56			if err != nil {
57				return fmt.Errorf("start server: %w", err)
58			}
59
60			done := make(chan os.Signal, 1)
61			lch := make(chan error, 1)
62			go func() {
63				defer close(lch)
64				defer close(done)
65				lch <- s.Start()
66			}()
67
68			signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
69			<-done
70
71			ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
72			defer cancel()
73			if err := s.Shutdown(ctx); err != nil {
74				return err
75			}
76
77			// wait for serve to finish
78			return <-lch
79		},
80	}
81)