all repos

mugit @ f70e150377a19db58e4348197a9bf08f23c07a4f

🐮 git server that your cow will love

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

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