1package main
  2
  3import (
  4	"context"
  5	"errors"
  6	"fmt"
  7	"io/fs"
  8	"os"
  9	"runtime/debug"
 10
 11	"github.com/charmbracelet/log"
 12	"github.com/charmbracelet/soft-serve/server/backend"
 13	"github.com/charmbracelet/soft-serve/server/config"
 14	"github.com/charmbracelet/soft-serve/server/db"
 15	logr "github.com/charmbracelet/soft-serve/server/log"
 16	"github.com/charmbracelet/soft-serve/server/store"
 17	"github.com/charmbracelet/soft-serve/server/store/database"
 18	"github.com/charmbracelet/soft-serve/server/version"
 19	"github.com/spf13/cobra"
 20	"go.uber.org/automaxprocs/maxprocs"
 21)
 22
 23var (
 24	// Version contains the application version number. It's set via ldflags
 25	// when building.
 26	Version = ""
 27
 28	// CommitSHA contains the SHA of the commit that this application was built
 29	// against. It's set via ldflags when building.
 30	CommitSHA = ""
 31
 32	// CommitDate contains the date of the commit that this application was
 33	// built against. It's set via ldflags when building.
 34	CommitDate = ""
 35
 36	rootCmd = &cobra.Command{
 37		Use:          "soft",
 38		Short:        "A self-hostable Git server for the command line",
 39		Long:         "Soft Serve is a self-hostable Git server for the command line.",
 40		SilenceUsage: true,
 41		RunE: func(cmd *cobra.Command, args []string) error {
 42			return browseCmd.RunE(cmd, args)
 43		},
 44	}
 45)
 46
 47func init() {
 48	rootCmd.AddCommand(
 49		serveCmd,
 50		manCmd,
 51		hookCmd,
 52		migrateConfig,
 53		adminCmd,
 54	)
 55	rootCmd.CompletionOptions.HiddenDefaultCmd = true
 56
 57	if len(CommitSHA) >= 7 {
 58		vt := rootCmd.VersionTemplate()
 59		rootCmd.SetVersionTemplate(vt[:len(vt)-1] + " (" + CommitSHA[0:7] + ")\n")
 60	}
 61	if Version == "" {
 62		if info, ok := debug.ReadBuildInfo(); ok && info.Main.Sum != "" {
 63			Version = info.Main.Version
 64		} else {
 65			Version = "unknown (built from source)"
 66		}
 67	}
 68	rootCmd.Version = Version
 69
 70	version.Version = Version
 71	version.CommitSHA = CommitSHA
 72	version.CommitDate = CommitDate
 73}
 74
 75func main() {
 76	ctx := context.Background()
 77	cfg := config.DefaultConfig()
 78	if cfg.Exist() {
 79		if err := cfg.Parse(); err != nil {
 80			log.Fatal(err)
 81		}
 82	}
 83
 84	if err := cfg.ParseEnv(); err != nil {
 85		log.Fatal(err)
 86	}
 87
 88	ctx = config.WithContext(ctx, cfg)
 89	logger, f, err := logr.NewLogger(cfg)
 90	if err != nil {
 91		log.Errorf("failed to create logger: %v", err)
 92	}
 93
 94	ctx = log.WithContext(ctx, logger)
 95	if f != nil {
 96		defer f.Close() // nolint: errcheck
 97	}
 98
 99	// Set global logger
100	log.SetDefault(logger)
101
102	var opts []maxprocs.Option
103	if config.IsVerbose() {
104		opts = append(opts, maxprocs.Logger(log.Debugf))
105	}
106
107	// Set the max number of processes to the number of CPUs
108	// This is useful when running soft serve in a container
109	if _, err := maxprocs.Set(opts...); err != nil {
110		log.Warn("couldn't set automaxprocs", "error", err)
111	}
112
113	if err := rootCmd.ExecuteContext(ctx); err != nil {
114		os.Exit(1)
115	}
116}
117
118func initBackendContext(cmd *cobra.Command, _ []string) error {
119	ctx := cmd.Context()
120	cfg := config.FromContext(ctx)
121	if _, err := os.Stat(cfg.DataPath); errors.Is(err, fs.ErrNotExist) {
122		if err := os.MkdirAll(cfg.DataPath, os.ModePerm); err != nil {
123			return fmt.Errorf("create data directory: %w", err)
124		}
125	}
126	dbx, err := db.Open(ctx, cfg.DB.Driver, cfg.DB.DataSource)
127	if err != nil {
128		return fmt.Errorf("open database: %w", err)
129	}
130
131	ctx = db.WithContext(ctx, dbx)
132	dbstore := database.New(ctx, dbx)
133	ctx = store.WithContext(ctx, dbstore)
134	be := backend.New(ctx, cfg, dbx)
135	ctx = backend.WithContext(ctx, be)
136
137	cmd.SetContext(ctx)
138
139	return nil
140}
141
142func closeDBContext(cmd *cobra.Command, _ []string) error {
143	ctx := cmd.Context()
144	dbx := db.FromContext(ctx)
145	if dbx != nil {
146		if err := dbx.Close(); err != nil {
147			return fmt.Errorf("close database: %w", err)
148		}
149	}
150
151	return nil
152}