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