all repos

mugit @ 5480fecc5931f4fe96f0f56f88df41d2439d7632

🐮 git server that your cow will love

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

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