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/repo"
14 "github.com/charmbracelet/soft-serve/cmd/soft/serve"
15 "github.com/charmbracelet/soft-serve/cmd/soft/settings"
16 "github.com/charmbracelet/soft-serve/cmd/soft/shell"
17 "github.com/charmbracelet/soft-serve/cmd/soft/user"
18 "github.com/charmbracelet/soft-serve/pkg/config"
19 logr "github.com/charmbracelet/soft-serve/pkg/log"
20 "github.com/charmbracelet/soft-serve/pkg/version"
21 mcobra "github.com/muesli/mango-cobra"
22 "github.com/muesli/roff"
23 "github.com/spf13/cobra"
24 "go.uber.org/automaxprocs/maxprocs"
25)
26
27var (
28 // Version contains the application version number. It's set via ldflags
29 // when building.
30 Version = ""
31
32 // CommitSHA contains the SHA of the commit that this application was built
33 // against. It's set via ldflags when building.
34 CommitSHA = ""
35
36 // CommitDate contains the date of the commit that this application was
37 // built against. It's set via ldflags when building.
38 CommitDate = ""
39
40 // When this flag is set, the user will be checked for access to the
41 // repository before the command is run during cmd.CheckUserHasAccess.
42 strict bool
43
44 rootCmd = &cobra.Command{
45 Use: "soft",
46 Short: "A self-hostable Git server for the command line",
47 Long: "Soft Serve is a self-hostable Git server for the command line.",
48 SilenceUsage: true,
49 RunE: func(cmd *cobra.Command, args []string) error {
50 return browse.Command.RunE(cmd, args)
51 },
52 }
53
54 manCmd = &cobra.Command{
55 Use: "man",
56 Short: "Generate man pages",
57 Args: cobra.NoArgs,
58 Hidden: true,
59 RunE: func(_ *cobra.Command, _ []string) error {
60 manPage, err := mcobra.NewManPage(1, rootCmd)
61 if err != nil {
62 return err
63 }
64
65 manPage = manPage.WithSection("Copyright", "(C) 2021-2023 Charmbracelet, Inc.\n"+
66 "Released under MIT license.")
67 fmt.Println(manPage.Build(roff.NewDocument()))
68 return nil
69 },
70 }
71)
72
73func init() {
74 rootCmd.AddCommand(
75 admin.Command,
76 browse.Command,
77 hook.Command,
78 manCmd,
79 repo.Command,
80 serve.Command,
81 settings.Command,
82 shell.Command,
83 user.Command,
84 )
85 rootCmd.CompletionOptions.HiddenDefaultCmd = true
86 rootCmd.PersistentFlags().BoolVarP(&strict, "strict", "", false, "Check if the user has access to the command")
87
88 if len(CommitSHA) >= 7 {
89 vt := rootCmd.VersionTemplate()
90 rootCmd.SetVersionTemplate(vt[:len(vt)-1] + " (" + CommitSHA[0:7] + ")\n")
91 }
92 if Version == "" {
93 if info, ok := debug.ReadBuildInfo(); ok && info.Main.Sum != "" {
94 Version = info.Main.Version
95 } else {
96 Version = "unknown (built from source)"
97 }
98 }
99 rootCmd.Version = Version
100
101 version.Version = Version
102 version.CommitSHA = CommitSHA
103 version.CommitDate = CommitDate
104}
105
106func main() {
107 ctx := context.Background()
108 cfg := config.DefaultConfig()
109 if cfg.Exist() {
110 if err := cfg.Parse(); err != nil {
111 log.Fatal(err)
112 }
113 }
114
115 if err := cfg.ParseEnv(); err != nil {
116 log.Fatal(err)
117 }
118
119 ctx = config.WithContext(ctx, cfg)
120 logger, f, err := logr.NewLogger(cfg)
121 if err != nil {
122 log.Errorf("failed to create logger: %v", err)
123 }
124
125 ctx = log.WithContext(ctx, logger)
126 if f != nil {
127 defer f.Close() // nolint: errcheck
128 }
129
130 // Set global logger
131 log.SetDefault(logger)
132
133 var opts []maxprocs.Option
134 if config.IsVerbose() {
135 opts = append(opts, maxprocs.Logger(log.Debugf))
136 }
137
138 // Set the max number of processes to the number of CPUs
139 // This is useful when running soft serve in a container
140 if _, err := maxprocs.Set(opts...); err != nil {
141 log.Warn("couldn't set automaxprocs", "error", err)
142 }
143
144 if err := rootCmd.ExecuteContext(ctx); err != nil {
145 os.Exit(1)
146 }
147}