1package main
2
3import (
4 "context"
5 "fmt"
6 "os"
7 "runtime/debug"
8
9 "github.com/charmbracelet/log"
10 "github.com/charmbracelet/soft-serve/cmd/soft/admin"
11 "github.com/charmbracelet/soft-serve/cmd/soft/browse"
12 "github.com/charmbracelet/soft-serve/cmd/soft/hook"
13 "github.com/charmbracelet/soft-serve/cmd/soft/serve"
14 "github.com/charmbracelet/soft-serve/pkg/config"
15 logr "github.com/charmbracelet/soft-serve/pkg/log"
16 "github.com/charmbracelet/soft-serve/pkg/version"
17 mcobra "github.com/muesli/mango-cobra"
18 "github.com/muesli/roff"
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 browse.Command.RunE(cmd, args)
43 },
44 }
45
46 manCmd = &cobra.Command{
47 Use: "man",
48 Short: "Generate man pages",
49 Args: cobra.NoArgs,
50 Hidden: true,
51 RunE: func(_ *cobra.Command, _ []string) error {
52 manPage, err := mcobra.NewManPage(1, rootCmd) //.
53 if err != nil {
54 return err
55 }
56
57 manPage = manPage.WithSection("Copyright", "(C) 2021-2023 Charmbracelet, Inc.\n"+
58 "Released under MIT license.")
59 fmt.Println(manPage.Build(roff.NewDocument()))
60 return nil
61 },
62 }
63)
64
65func init() {
66 rootCmd.AddCommand(
67 manCmd,
68 serve.Command,
69 hook.Command,
70 admin.Command,
71 browse.Command,
72 )
73 rootCmd.CompletionOptions.HiddenDefaultCmd = true
74
75 if len(CommitSHA) >= 7 {
76 vt := rootCmd.VersionTemplate()
77 rootCmd.SetVersionTemplate(vt[:len(vt)-1] + " (" + CommitSHA[0:7] + ")\n")
78 }
79 if Version == "" {
80 if info, ok := debug.ReadBuildInfo(); ok && info.Main.Sum != "" {
81 Version = info.Main.Version
82 } else {
83 Version = "unknown (built from source)"
84 }
85 }
86 rootCmd.Version = Version
87
88 version.Version = Version
89 version.CommitSHA = CommitSHA
90 version.CommitDate = CommitDate
91}
92
93func main() {
94 ctx := context.Background()
95 cfg := config.DefaultConfig()
96 if cfg.Exist() {
97 if err := cfg.Parse(); err != nil {
98 log.Fatal(err)
99 }
100 }
101
102 if err := cfg.ParseEnv(); err != nil {
103 log.Fatal(err)
104 }
105
106 ctx = config.WithContext(ctx, cfg)
107 logger, f, err := logr.NewLogger(cfg)
108 if err != nil {
109 log.Errorf("failed to create logger: %v", err)
110 }
111
112 ctx = log.WithContext(ctx, logger)
113 if f != nil {
114 defer f.Close() // nolint: errcheck
115 }
116
117 // Set global logger
118 log.SetDefault(logger)
119
120 var opts []maxprocs.Option
121 if config.IsVerbose() {
122 opts = append(opts, maxprocs.Logger(log.Debugf))
123 }
124
125 // Set the max number of processes to the number of CPUs
126 // This is useful when running soft serve in a container
127 if _, err := maxprocs.Set(opts...); err != nil {
128 log.Warn("couldn't set automaxprocs", "error", err)
129 }
130
131 if err := rootCmd.ExecuteContext(ctx); err != nil {
132 os.Exit(1)
133 }
134}