all repos

mugit @ f492c42

🐮 git server that your cow will love

mugit/internal/cli/cli.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
run gofumpt, 1 month ago
1
package cli
2
3
import (
4
	"context"
5
	"fmt"
6
	"log/slog"
7
	"os"
8
	"path/filepath"
9
10
	"github.com/urfave/cli/v3"
11
	"olexsmir.xyz/mugit/internal/config"
12
	"olexsmir.xyz/mugit/internal/git"
13
	"olexsmir.xyz/mugit/internal/ssh"
14
)
15
16
type Cli struct {
17
	cfg     *config.Config
18
	ssh     *ssh.Shell
19
	version string
20
}
21
22
func New(version string) *Cli {
23
	return &Cli{
24
		version: version,
25
	}
26
}
27
28
func (c *Cli) Run(ctx context.Context, args []string) error {
29
	cmd := &cli.Command{
30
		Name:                  "mugit",
31
		Usage:                 "a frontend for git repos",
32
		Version:               c.version,
33
		EnableShellCompletion: true,
34
		Flags: []cli.Flag{
35
			&cli.StringFlag{
36
				Name:    "config",
37
				Aliases: []string{"c"},
38
				Usage:   "path to config file",
39
			},
40
		},
41
		Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
42
			loadedCfg, err := config.Load(
43
				config.PathOrDefault(cmd.String("config")),
44
			)
45
			if err != nil {
46
				return ctx, err
47
			}
48
			c.cfg = loadedCfg
49
50
			if c.cfg.SSH.Enable {
51
				shell, err := ssh.NewShell(c.cfg)
52
				if err != nil {
53
					return ctx, err
54
				}
55
				c.ssh = shell
56
			}
57
58
			return ctx, nil
59
		},
60
		Commands: []*cli.Command{
61
			{
62
				Name:   "serve",
63
				Usage:  "starts the server",
64
				Action: c.serveAction,
65
			},
66
			{
67
				Name: "repo",
68
				Commands: []*cli.Command{
69
					{
70
						Name:   "new",
71
						Usage:  "create new repo",
72
						Action: c.repoNewAction,
73
						Arguments: []cli.Argument{
74
							&cli.StringArg{Name: "name"},
75
						},
76
						Flags: []cli.Flag{
77
							&cli.StringFlag{
78
								Name:  "mirror",
79
								Usage: "remote URL(only http/https) to mirror from",
80
							},
81
							&cli.StringFlag{
82
								Name:    "description",
83
								Usage:   "set repo description",
84
								Aliases: []string{"desc"},
85
							},
86
							&cli.BoolFlag{
87
								Name:  "private",
88
								Usage: "make the repository private",
89
							},
90
							&cli.StringFlag{
91
								Name:  "default",
92
								Usage: "set default branch",
93
							},
94
						},
95
					},
96
					{
97
						Name:   "description",
98
						Usage:  "get or set repo description",
99
						Action: c.repoDescriptionAction,
100
						Arguments: []cli.Argument{
101
							&cli.StringArg{Name: "name"},
102
						},
103
					},
104
					{
105
						Name:   "private",
106
						Usage:  "toggle private status of a repo",
107
						Action: c.repoPrivateAction,
108
						Arguments: []cli.Argument{
109
							&cli.StringArg{Name: "name"},
110
						},
111
					},
112
					{
113
						Name:   "set-default",
114
						Usage:  "switch repo's default branch",
115
						Action: c.repoDefaultAction,
116
						Arguments: []cli.Argument{
117
							&cli.StringArg{Name: "name"},
118
						},
119
					},
120
					{
121
						Name:   "sync",
122
						Usage:  "trigger sync for a mirror repository",
123
						Action: c.repoSyncAction,
124
						Arguments: []cli.Argument{
125
							&cli.StringArg{Name: "name"},
126
						},
127
					},
128
				},
129
			},
130
			{
131
				Name:        "shell",
132
				Description: "git over sshd",
133
				Action:      c.sshShellAction,
134
				Commands: []*cli.Command{
135
					{
136
						Name:   "keys",
137
						Action: c.sshAuthorizedKeysAction,
138
					},
139
				},
140
			},
141
		},
142
	}
143
	return cmd.Run(ctx, args)
144
}
145
146
func (c *Cli) setupLogger() error {
147
	logDir := filepath.Dir(c.cfg.SSH.LogFile)
148
	if err := os.MkdirAll(logDir, 0o755); err != nil {
149
		return fmt.Errorf("failed to create log directory: %w", err)
150
	}
151
152
	logOutput, err := os.OpenFile(c.cfg.SSH.LogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
153
	if err != nil {
154
		return fmt.Errorf("failed to open log file: %w", err)
155
	}
156
157
	slog.SetDefault(slog.New(slog.NewTextHandler(logOutput, nil)))
158
	return nil
159
}
160
161
func (c *Cli) openRepo(name string) (*git.Repo, error) {
162
	path, err := git.ResolvePath(c.cfg.Repo.Dir, name)
163
	if err != nil {
164
		return nil, err
165
	}
166
167
	repo, err := git.Open(path, "")
168
	if err != nil {
169
		return nil, fmt.Errorf("failed to open repo: %w", err)
170
	}
171
172
	return repo, nil
173
}