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	execManager := exec.NewManager(context.Background(), conf)
 93	pluginManager := plugin.NewManager(conf, repoManager, userManager, execManager)
 94	backgroundManager = background.NewManager(context.Background(), conf, repoManager, userManager, pluginManager)
 95	pluginManager.SetBackgroundManager(backgroundManager)
 96
 97	if !repoManager.Exists(conf.ForgeConfigName()) {
 98		go func() {
 99			if err := repoManager.CreateRootRepoIfNeeded(context.Background(), rootRemote); err != nil {
100				log.Error("Error in CreateRootRepoIfNeeded", err)
101			}
102		}()
103	} else {
104		rootRepo, err := repoManager.OpenForgeRepo(context.Background())
105		if err != nil {
106			log.Error("Can't open root repo", err)
107			return
108		}
109		c, err := rootRepo.Content(conf.PathFileForgeConfig())
110		if err != nil {
111			log.Error("Can't read root conf", err)
112			return
113		}
114		if err := conf.LoadFromFile(c); err != nil {
115			log.Error("Can't unmarshal root conf", err)
116			return
117		}
118		if needUpdateConf {
119			log.Debug("will write conf", logger.NewLoggerPair("branch", conf.DefaultBranch))
120			rootRepoWrite, err := rootRepo.WillWrite(plumbing.NewBranchReferenceName(conf.DefaultBranch))
121			if err != nil {
122				log.Error("can't open root repository in write mode", err)
123			}
124			confMarshalled, err := conf.Marshall()
125			if err != nil {
126				log.Error("can't marshal conf", err)
127			}
128			err = rootRepoWrite.Write(conf.PathFileForgeConfig(), confMarshalled)
129			if err != nil {
130				log.Error("can't write conf", err)
131			}
132			if _, err := rootRepoWrite.CommitAll("Conf updated", userManager.RootCommiter()); err != nil {
133				log.Error("can't commit new conf", err)
134			}
135			log.Warn("update conf done", logger.NewLoggerPair("branch", conf.DefaultBranch))
136		}
137		rootRepo.Close()
138	}
139
140	go func() {
141		if err := NewServerHttp(conf, repoManager).ListenAndServe(); err != nil {
142			log.Error("Http closed", err)
143		}
144	}()
145
146	if err := NewServerSsh(conf, repoManager, userManager, pluginManager, backgroundManager).ListenAndServe(); err != nil {
147		log.Error("Ssh closed", err)
148	}
149}