main
1package cli
2
3import (
4 "flag"
5 "fmt"
6 "log/slog"
7 "os"
8 "path/filepath"
9
10 "forge/internal/gitmal"
11 "forge/internal/index"
12 "forge/internal/repo"
13)
14
15func runStatic(args []string) error {
16 fs := flag.NewFlagSet("forge static", flag.ContinueOnError)
17 fs.SetOutput(os.Stderr)
18
19 root := fs.String("root", "/opt/git", "path to bare repo root")
20 output := fs.String("output", "/var/www/git", "path to output directory")
21 all := fs.Bool("all", false, "discover and process all repos")
22
23 err := fs.Parse(args)
24 if err != nil {
25 return err
26 }
27
28 positional := fs.Args()
29
30 if *all && len(positional) > 0 {
31 fmt.Fprintln(os.Stderr, "error: --all cannot be combined with positional arguments")
32 return ErrUsage
33 }
34
35 if !*all && len(positional) == 0 {
36 fmt.Fprintln(os.Stderr, "error: provide a repo path or use --all")
37 fs.Usage()
38 return ErrUsage
39 }
40
41 if *all {
42 return staticAll(*root, *output)
43 }
44
45 return staticOne(*root, *output, positional[0])
46}
47
48func staticAll(root, output string) error {
49 repos, err := repo.Discover(root)
50 if err != nil {
51 return err
52 }
53
54 slog.Info("discovered repos", slog.Int("count", len(repos)))
55
56 var failed int
57 for _, r := range repos {
58 slog.Info("processing", slog.String("repo", r.RelPath))
59 outDir := filepath.Join(output, r.RelPath)
60 err := gitmal.Run(r.FullPath, r.Name, outDir)
61 if err != nil {
62 slog.Warn("skipping repo",
63 slog.String("repo", r.RelPath),
64 slog.String("error", err.Error()))
65 failed++
66 }
67 }
68
69 err = index.GenerateAll(output, repos)
70 if err != nil {
71 return err
72 }
73
74 slog.Info("indexes generated")
75
76 if failed > 0 {
77 return fmt.Errorf("gitmal failed for %d repo(s)", failed)
78 }
79 return nil
80}
81
82func staticOne(root, output, relPath string) error {
83 if filepath.IsAbs(relPath) {
84 fmt.Fprintln(os.Stderr, "error: repo path must be relative")
85 return ErrUsage
86 }
87
88 fullPath := filepath.Join(root, relPath)
89
90 bare, err := repo.IsBare(fullPath)
91 if err != nil {
92 return err
93 }
94 if !bare {
95 return fmt.Errorf("not a bare repo (path=%s): %w", fullPath, ErrUsage)
96 }
97
98 name := filepath.Base(relPath)
99 outDir := filepath.Join(output, relPath)
100
101 slog.Info("processing",
102 slog.String("repo", relPath),
103 slog.String("output", outDir))
104
105 err = gitmal.Run(fullPath, name, outDir)
106 if err != nil {
107 return err
108 }
109
110 return nil
111}