Federated via graft
1.6 kB
77 lines
1// Command sync runs the Forgejo <-> Radicle sync daemon: on an interval, it
2// mirrors git content, issues and patches for every repo pair listed in the
3// config file, then sleeps until the next pass.
4package main
5
6import (
7 "flag"
8 "log/slog"
9 "os"
10 "time"
11
12 "graft/internal/config"
13 "graft/internal/state"
14 "graft/internal/sync"
15)
16
17func main() {
18 configPath := flag.String("config", "/etc/graft/config.yaml", "path to config.yaml")
19 once := flag.Bool("once", false, "run a single pass and exit, instead of looping")
20 flag.Parse()
21
22 log := slog.New(slog.NewJSONHandler(os.Stdout, nil))
23
24 cfg, err := config.Load(*configPath)
25 if err != nil {
26 log.Error("load config", "err", err)
27 os.Exit(1)
28 }
29
30 st, err := state.Open(cfg.StateDB)
31 if err != nil {
32 log.Error("open state db", "err", err)
33 os.Exit(1)
34 }
35 defer st.Close()
36
37 stateDir := cfg.StateDB
38 if idx := lastSlash(stateDir); idx >= 0 {
39 stateDir = stateDir[:idx]
40 }
41
42 syncers := make([]*sync.RepoSyncer, 0, len(cfg.Repos))
43 for _, pair := range cfg.Repos {
44 rs, err := sync.New(pair, st, sync.WorkDirFor(stateDir, pair.Name))
45 if err != nil {
46 log.Error("set up repo pair", "pair", pair.Name, "err", err)
47 os.Exit(1)
48 }
49 syncers = append(syncers, rs)
50 }
51
52 runAll := func() {
53 for _, rs := range syncers {
54 rs.Run(log)
55 }
56 }
57
58 runAll()
59 if *once {
60 return
61 }
62
63 ticker := time.NewTicker(cfg.SyncInterval)
64 defer ticker.Stop()
65 for range ticker.C {
66 runAll()
67 }
68}
69
70func lastSlash(s string) int {
71 for i := len(s) - 1; i >= 0; i-- {
72 if s[i] == '/' {
73 return i
74 }
75 }
76 return -1
77}