GitRoot
craft your forge, build your project, grow your community freely
1// SPDX-FileCopyrightText: 2025 Romain Maneschi <romain@gitroot.dev>
2//
3// SPDX-License-Identifier: EUPL-1.2
4
5package main
6
7import (
8 "context"
9 "flag"
10 "fmt"
11 "os"
12 "path/filepath"
13
14 "github.com/go-git/go-git/v5/plumbing"
15 "github.com/goccy/go-yaml"
16 "gitroot.dev/server/background"
17 "gitroot.dev/server/configuration"
18 "gitroot.dev/server/exec"
19 "gitroot.dev/server/logger"
20 "gitroot.dev/server/plugin"
21 "gitroot.dev/server/repository"
22 "gitroot.dev/server/user"
23)
24
25var version string = "next" // set at build time
26var commit string // set at build time
27
28func main() {
29 log := logger.NewLoggerCtx("root", context.Background())
30 var rootPath string
31 flag.StringVar(&rootPath, "data", "./data", "path to store data")
32 var rootRemote string
33 flag.StringVar(&rootRemote, "remote", "", "optional repository for root repo, only used at init")
34 var showVersion bool
35 flag.BoolVar(&showVersion, "version", false, "show current version and build time")
36 var initConfig string
37 flag.StringVar(&initConfig, "initConfig", "", "generate a config file to configure GitRoot before the first start")
38 var config string
39 flag.StringVar(&config, "config", "", "use a config file to configure GitRoot before the first start")
40 flag.Parse()
41
42 conf := configuration.NewConfiguration(rootPath)
43
44 if config != "" {
45 configContent, err := os.ReadFile(config)
46 if err != nil {
47 log.Error("can't read conf", err, logger.NewLoggerPair("path", config))
48 os.Exit(1)
49 return
50 }
51 conf.LoadFromFile(configContent)
52 }
53
54 if showVersion || initConfig != "" {
55 if showVersion {
56 if commit == "" {
57 commit = plumbing.ZeroHash.String()
58 }
59 log.Warn(fmt.Sprintf("Version `%s` for commit `%s`", version, commit))
60 }
61 if initConfig != "" {
62 confMarshalled, err := yaml.Marshal(conf)
63 if err != nil {
64 log.Error("can't marshal conf", err)
65 os.Exit(1)
66 return
67 }
68 if err := os.WriteFile(initConfig, confMarshalled, 0666); err != nil {
69 log.Error("can't write initConfif file", err, logger.NewLoggerPair("path", initConfig))
70 os.Exit(1)
71 return
72 }
73 log.Warn(fmt.Sprintf("File %s created, please edit it and launch gitroot with --config=%s to use it", initConfig, initConfig))
74 }
75 return
76 }
77
78 needUpdateConf, err := conf.LoadFromOrStoreToRootConf(filepath.Join(rootPath, "gitroot.yml"), version)
79 if err != nil {
80 log.Error("Initialisation of root conf failed", err)
81 os.Exit(1)
82 }
83
84 userManager, err := user.NewManager(conf)
85 if err != nil {
86 log.Error("Initialisation failed", err)
87 os.Exit(1)
88 }
89
90 var backgroundManager *background.Manager
91 repoManager := repository.NewManager(conf, userManager)
92
93 if !repoManager.Exists(conf.ForgeConfigName()) {
94 go func() {
95 if err := repoManager.CreateRootRepoIfNeeded(context.Background(), rootRemote); err != nil {
96 log.Error("Error in CreateRootRepoIfNeeded", err)
97 }
98 }()
99 } else {
100 rootRepo, err := repoManager.OpenForgeRepo(context.Background())
101 if err != nil {
102 log.Error("Can't open root repo", err)
103 return
104 }
105 c, err := rootRepo.Content(conf.PathFileForgeConfig())
106 if err != nil {
107 log.Error("Can't read root conf", err)
108 return
109 }
110 if err := conf.LoadFromFile(c); err != nil {
111 log.Error("Can't unmarshal root conf", err)
112 return
113 }
114 if needUpdateConf {
115 log.Debug("will write conf", logger.NewLoggerPair("branch", conf.DefaultBranch))
116 rootRepoWrite, err := rootRepo.WillWrite(plumbing.NewBranchReferenceName(conf.DefaultBranch))
117 if err != nil {
118 log.Error("can't open root repository in write mode", err)
119 }
120 confMarshalled, err := conf.Marshall()
121 if err != nil {
122 log.Error("can't marshal conf", err)
123 }
124 err = rootRepoWrite.Write(conf.PathFileForgeConfig(), confMarshalled)
125 if err != nil {
126 log.Error("can't write conf", err)
127 }
128 if _, err := rootRepoWrite.CommitAll("Conf updated", userManager.RootCommiter()); err != nil {
129 log.Error("can't commit new conf", err)
130 }
131 log.Warn("update conf done", logger.NewLoggerPair("branch", conf.DefaultBranch))
132 }
133 rootRepo.Close()
134 }
135
136 execManager := exec.NewManager(context.Background(), conf, userManager)
137 pluginManager := plugin.NewManager(conf, repoManager, userManager, execManager)
138 backgroundManager = background.NewManager(context.Background(), conf, repoManager, userManager, pluginManager)
139 pluginManager.SetBackgroundManager(backgroundManager)
140
141 go func() {
142 if err := NewServerHttp(conf, repoManager).ListenAndServe(); err != nil {
143 log.Error("Http closed", err)
144 }
145 }()
146
147 if err := NewServerSsh(conf, repoManager, userManager, pluginManager, backgroundManager).ListenAndServe(); err != nil {
148 log.Error("Ssh closed", err)
149 }
150}