- commit
- 6aa7aac
- parent
- 73b7bbe
- author
- Eric Bower
- date
- 2024-07-10 13:37:51 -0400 EDT
feat: config via toml
+11,
-11
1@@ -1,13 +1,13 @@
2 CF_API_TOKEN=
3
4-GIT_V4=
5-GIT_V6=
6-GIT_HTTP_V4=$GIT_V4:80
7-GIT_HTTP_V6=[$GIT_V6]:80
8-GIT_HTTPS_V4=$GIT_V4:443
9-GIT_HTTPS_V6=[$GIT_V6]:443
10-GIT_SSH_V4=$GIT_V4:22
11-GIT_SSH_V6=[$GIT_V6]:22
12-GIT_HOST=
13-GIT_SSH_PORT=2222
14-GIT_WEB_PORT=3000
15+GITPR_V4=
16+GITPR_V6=
17+GITPR_HTTP_V4=$GIT_V4:80
18+GITPR_HTTP_V6=[$GIT_V6]:80
19+GITPR_HTTPS_V4=$GIT_V4:443
20+GITPR_HTTPS_V6=[$GIT_V6]:443
21+GITPR_SSH_V4=$GIT_V4:22
22+GITPR_SSH_V6=[$GIT_V6]:22
23+GITPR_HOST=
24+GITPR_SSH_PORT=2222
25+GITPR_WEB_PORT=3000
+2,
-3
1@@ -45,7 +45,7 @@ WORKDIR /app
2 COPY --from=builder-web /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
3 COPY --from=builder-web /go/bin/web ./web
4
5-ENTRYPOINT ["/app/web"]
6+ENTRYPOINT ["/app/web", "--config", "${GITPR_CONFIG_PATH}"]
7
8 FROM scratch as release-ssh
9
10@@ -55,5 +55,4 @@ ENV TERM="xterm-256color"
11 COPY --from=builder-ssh /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
12 COPY --from=builder-ssh /go/bin/ssh ./ssh
13
14-
15-ENTRYPOINT ["/app/ssh"]
16+ENTRYPOINT ["/app/ssh", "--config", "${GITPR_CONFIG_PATH}"]
M
cfg.go
+96,
-36
1@@ -1,58 +1,118 @@
2 package git
3
4 import (
5- "os"
6+ "fmt"
7+ "log/slog"
8+ "strings"
9
10 "github.com/charmbracelet/ssh"
11+ "github.com/knadh/koanf/parsers/toml"
12+ "github.com/knadh/koanf/providers/env"
13+ "github.com/knadh/koanf/providers/file"
14+ "github.com/knadh/koanf/v2"
15 )
16
17 type Repo struct {
18- ID string
19- Desc string
20- CloneAddr string
21- DefaultBranch string
22+ ID string `koanf:"id"`
23+ Desc string `koanf:"desc"`
24+ CloneAddr string `koanf:"clone_addr"`
25+ DefaultBranch string `koanf:"default_branch"`
26 }
27
28-func NewRepo(id, cloneAddr string) *Repo {
29- return &Repo{
30- ID: id,
31- CloneAddr: cloneAddr,
32- DefaultBranch: "main",
33- }
34-}
35+var k = koanf.New(".")
36
37 type GitCfg struct {
38- DataPath string
39- Admins []ssh.PublicKey
40- Repos []*Repo
41- Url string
42- Host string
43- SshPort string
44- WebPort string
45+ DataPath string `koanf:"data"`
46+ Repos []*Repo `koanf:"repo"`
47+ Url string `koanf:"url"`
48+ Host string `koanf:"host"`
49+ SshPort string `koanf:"ssh_port"`
50+ WebPort string `koanf:"web_port"`
51+ AdminsStr []string `koanf:"admins"`
52+ Admins []ssh.PublicKey `koanf:"admins_pk"`
53+ Theme string `koanf:"theme"`
54+ Logger *slog.Logger
55 }
56
57-func NewGitCfg(dataPath, url string, repos []*Repo) *GitCfg {
58- host := os.Getenv("GIT_HOST")
59- if host == "" {
60- host = "0.0.0.0"
61+func NewGitCfg(fpath string, logger *slog.Logger) *GitCfg {
62+ logger.Info("loading configuration file", "fpath", fpath)
63+
64+ if err := k.Load(file.Provider(fpath), toml.Parser()); err != nil {
65+ panic(fmt.Sprintf("error loading config: %v", err))
66+ }
67+
68+ err := k.Load(env.Provider("GITPR_", ".", func(s string) string {
69+ keyword := strings.ToLower(strings.TrimPrefix(s, "GITPR_"))
70+ return keyword
71+ }), nil)
72+ if err != nil {
73+ panic(fmt.Sprintf("could not load environment variables: %v", err))
74 }
75
76- sshPort := os.Getenv("GIT_SSH_PORT")
77- if sshPort == "" {
78- sshPort = "2222"
79+ var out GitCfg
80+ err = k.UnmarshalWithConf("", &out, koanf.UnmarshalConf{Tag: "koanf"})
81+ if err != nil {
82+ panic(fmt.Sprintf("could not unmarshal config: %v", err))
83 }
84
85- webPort := os.Getenv("GIT_WEB_PORT")
86- if webPort == "" {
87- webPort = "3000"
88+ if len(out.AdminsStr) > 0 {
89+ keys, err := getAuthorizedKeys(out.AdminsStr)
90+ if err == nil {
91+ out.Admins = keys
92+ } else {
93+ panic(fmt.Sprintf("could not parse authorized keys file: %v", err))
94+ }
95+ } else {
96+ logger.Info("no admin specified in config so no one can submit a review!")
97 }
98
99- return &GitCfg{
100- DataPath: dataPath,
101- Url: url,
102- Repos: repos,
103- Host: host,
104- SshPort: sshPort,
105- WebPort: webPort,
106+ if out.DataPath == "" {
107+ out.DataPath = "data"
108 }
109+
110+ if out.Host == "" {
111+ out.Host = "0.0.0.0"
112+ }
113+
114+ if out.SshPort == "" {
115+ out.SshPort = "2222"
116+ }
117+
118+ if out.WebPort == "" {
119+ out.WebPort = "3000"
120+ }
121+
122+ if out.Theme == "" {
123+ out.Theme = "dracula"
124+ }
125+
126+ logger.Info(
127+ "config",
128+ "url", out.Url,
129+ "data", out.DataPath,
130+ "host", out.Host,
131+ "ssh_port", out.SshPort,
132+ "web_port", out.WebPort,
133+ "theme", out.Theme,
134+ )
135+
136+ for _, pubkey := range out.AdminsStr {
137+ logger.Info("admin", "pubkey", pubkey)
138+ }
139+
140+ for _, repo := range out.Repos {
141+ if repo.DefaultBranch == "" {
142+ repo.DefaultBranch = "main"
143+ }
144+ logger.Info(
145+ "repo",
146+ "id", repo.ID,
147+ "desc", repo.Desc,
148+ "clone_addr", repo.CloneAddr,
149+ "default_branch", repo.DefaultBranch,
150+ )
151+ }
152+
153+ out.Logger = logger
154+ return &out
155 }
+0,
-32
1@@ -1,32 +0,0 @@
2-package cmd
3-
4-import "github.com/picosh/git-pr"
5-
6-func NewPicoCfg() *git.GitCfg {
7- test := git.NewRepo("test", "git@github.com:picosh/test")
8- test.Desc = "A test repo to play around with Patch Requests"
9-
10- pico := git.NewRepo("pico", "git@github.com:picosh/pico")
11- pico.Desc = "hacker labs - open and managed web services leveraging ssh"
12-
13- pr := git.NewRepo("git-pr", "git@github.com:picosh/git-pr")
14- pr.Desc = "the easiest git collaboration tool"
15-
16- ptun := git.NewRepo("ptun", "git@github.com:picosh/ptun")
17- ptun.Desc = "passwordless authentication for the web"
18-
19- pobj := git.NewRepo("pobj", "git@github.com:picosh/pobj")
20- pobj.Desc = "rsync, scp, sftp for your object store"
21-
22- send := git.NewRepo("send", "git@github.com:picosh/send")
23- send.Desc = "ssh wish middleware for sending and receiving files from familiar tools (rsync, scp, sftp)"
24-
25- docs := git.NewRepo("docs", "git@github.com:picosh/docs")
26- docs.Desc = "pico.sh doc site"
27-
28- return git.NewGitCfg(
29- "ssh_data",
30- "pr.pico.sh",
31- []*git.Repo{test, pico, ptun, pobj, send, docs, pr},
32- )
33-}
+13,
-2
1@@ -1,10 +1,21 @@
2 package main
3
4 import (
5+ "flag"
6+ "log/slog"
7+ "os"
8+
9 git "github.com/picosh/git-pr"
10- "github.com/picosh/git-pr/cmd"
11 )
12
13 func main() {
14- git.GitSshServer(cmd.NewPicoCfg())
15+ fpath := flag.String("config", "git-pr.toml", "configuration toml file")
16+ flag.Parse()
17+ opts := &slog.HandlerOptions{
18+ AddSource: true,
19+ }
20+ logger := slog.New(
21+ slog.NewTextHandler(os.Stdout, opts),
22+ )
23+ git.GitSshServer(git.NewGitCfg(*fpath, logger))
24 }
+13,
-2
1@@ -1,10 +1,21 @@
2 package main
3
4 import (
5+ "flag"
6+ "log/slog"
7+ "os"
8+
9 git "github.com/picosh/git-pr"
10- "github.com/picosh/git-pr/cmd"
11 )
12
13 func main() {
14- git.StartWebServer(cmd.NewPicoCfg())
15+ fpath := flag.String("config", "git-pr.toml", "configuration toml file")
16+ flag.Parse()
17+ opts := &slog.HandlerOptions{
18+ AddSource: true,
19+ }
20+ logger := slog.New(
21+ slog.NewTextHandler(os.Stdout, opts),
22+ )
23+ git.StartWebServer(git.NewGitCfg(*fpath, logger))
24 }
+2,
-2
1@@ -3,9 +3,9 @@ services:
2 image: ghcr.io/picosh/pico/git-web:latest
3 restart: always
4 volumes:
5- - ./data/git-pr/data:/app/ssh_data
6+ - ./data/git-pr/data:/app/data
7 ssh:
8 image: ghcr.io/picosh/pico/git-ssh:latest
9 restart: always
10 volumes:
11- - ./data/git-pr/data:/app/ssh_data
12+ - ./data/git-pr/data:/app/data
+41,
-0
1@@ -0,0 +1,41 @@
2+url = "pr.pico.sh"
3+data = "ssh_data"
4+admins = [
5+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHM2RPNgcyt+Tpb77uj0oQYZWLadfB8x8mqJFy0C7Y8P",
6+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINlr0pScstAuTqs9Qr1KaMspHuFGO7cQMuvMMdJjbWG3"
7+]
8+
9+[[repo]]
10+id = "test"
11+clone_addr = "git@github.com:picosh/test"
12+desc = "test repo to play around with Patch Requests"
13+
14+[[repo]]
15+id = "pico"
16+clone_addr = "git@github.com:picosh/pico"
17+desc = "open and managed web services leveraging ssh"
18+
19+[[repo]]
20+id = "git-pr"
21+clone_addr = "git@github.com:picosh/git-pr"
22+desc = "the easiest git collaboration tool"
23+
24+[[repo]]
25+id = "tunkit"
26+clone_addr = "git@github.com:picosh/tunkit"
27+desc = "ssh tunnel tooling"
28+
29+[[repo]]
30+id = "pobj"
31+clone_addr = "git@github.com:picosh/pobj"
32+desc = "rsync, scp, sftp for your object store"
33+
34+[[repo]]
35+id = "send"
36+clone_addr = "git@github.com:picosh/send"
37+desc = "ssh wish middleware for sending and receiving files from familiar tools (rsync, scp, sftp)"
38+
39+[[repo]]
40+id = "docs"
41+clone_addr = "git@github.com:picosh/docs"
42+desc = "pico.sh doc site"
M
go.mod
+11,
-1
1@@ -10,6 +10,10 @@ require (
2 github.com/charmbracelet/wish v1.3.2
3 github.com/gorilla/feeds v1.1.2
4 github.com/jmoiron/sqlx v1.3.5
5+ github.com/knadh/koanf/parsers/toml v0.1.0
6+ github.com/knadh/koanf/providers/env v0.1.0
7+ github.com/knadh/koanf/providers/file v1.0.0
8+ github.com/knadh/koanf/v2 v2.1.1
9 github.com/urfave/cli/v2 v2.27.2
10 golang.org/x/crypto v0.21.0
11 modernc.org/sqlite v1.27.0
12@@ -29,17 +33,23 @@ require (
13 github.com/creack/pty v1.1.21 // indirect
14 github.com/dlclark/regexp2 v1.11.0 // indirect
15 github.com/dustin/go-humanize v1.0.1 // indirect
16+ github.com/fsnotify/fsnotify v1.7.0 // indirect
17 github.com/go-logfmt/logfmt v0.6.0 // indirect
18+ github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
19 github.com/google/uuid v1.4.0 // indirect
20 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
21+ github.com/knadh/koanf/maps v0.1.1 // indirect
22 github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
23 github.com/mattn/go-isatty v0.0.20 // indirect
24 github.com/mattn/go-localereader v0.0.1 // indirect
25 github.com/mattn/go-runewidth v0.0.15 // indirect
26+ github.com/mitchellh/copystructure v1.2.0 // indirect
27+ github.com/mitchellh/reflectwalk v1.0.2 // indirect
28 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
29 github.com/muesli/cancelreader v0.2.2 // indirect
30 github.com/muesli/reflow v0.3.0 // indirect
31 github.com/muesli/termenv v0.15.2 // indirect
32+ github.com/pelletier/go-toml v1.9.5 // indirect
33 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
34 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
35 github.com/rivo/uniseg v0.4.7 // indirect
36@@ -48,7 +58,7 @@ require (
37 golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect
38 golang.org/x/mod v0.14.0 // indirect
39 golang.org/x/sync v0.6.0 // indirect
40- golang.org/x/sys v0.18.0 // indirect
41+ golang.org/x/sys v0.21.0 // indirect
42 golang.org/x/term v0.18.0 // indirect
43 golang.org/x/text v0.14.0 // indirect
44 golang.org/x/tools v0.15.0 // indirect
M
go.sum
+22,
-2
1@@ -40,10 +40,14 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK
2 github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
3 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
4 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
5+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
6+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
7 github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
8 github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
9 github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
10 github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
11+github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c=
12+github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
13 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
14 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
15 github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
16@@ -58,6 +62,16 @@ github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
17 github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
18 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
19 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
20+github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
21+github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
22+github.com/knadh/koanf/parsers/toml v0.1.0 h1:S2hLqS4TgWZYj4/7mI5m1CQQcWurxUz6ODgOub/6LCI=
23+github.com/knadh/koanf/parsers/toml v0.1.0/go.mod h1:yUprhq6eo3GbyVXFFMdbfZSo928ksS+uo0FFqNMnO18=
24+github.com/knadh/koanf/providers/env v0.1.0 h1:LqKteXqfOWyx5Ab9VfGHmjY9BvRXi+clwyZozgVRiKg=
25+github.com/knadh/koanf/providers/env v0.1.0/go.mod h1:RE8K9GbACJkeEnkl8L/Qcj8p4ZyPXZIQ191HJi44ZaQ=
26+github.com/knadh/koanf/providers/file v1.0.0 h1:DtPvSQBeF+N0QLPMz0yf2bx0nFSxUcncpqQvzCxfCyk=
27+github.com/knadh/koanf/providers/file v1.0.0/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI=
28+github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM=
29+github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es=
30 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
31 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
32 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
33@@ -77,6 +91,10 @@ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
34 github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
35 github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
36 github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
37+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
38+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
39+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
40+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
41 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
42 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
43 github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
44@@ -85,6 +103,8 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
45 github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
46 github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
47 github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
48+github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
49+github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
50 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
51 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
52 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
53@@ -113,8 +133,8 @@ golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
54 golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
55 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
56 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
57-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
58-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
59+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
60+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
61 golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
62 golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
63 golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
M
ssh.go
+8,
-21
1@@ -3,7 +3,6 @@ package git
2 import (
3 "context"
4 "fmt"
5- "log/slog"
6 "os"
7 "os/signal"
8 "path/filepath"
9@@ -33,27 +32,14 @@ func authHandler(pr *PrCmd) func(ctx ssh.Context, key ssh.PublicKey) bool {
10 }
11
12 func GitSshServer(cfg *GitCfg) {
13- opts := &slog.HandlerOptions{
14- AddSource: true,
15- }
16- logger := slog.New(
17- slog.NewTextHandler(os.Stdout, opts),
18- )
19- dbh, err := Open(filepath.Join(cfg.DataPath, "pr.db"), logger)
20+ dbh, err := Open(filepath.Join(cfg.DataPath, "pr.db"), cfg.Logger)
21 if err != nil {
22 panic(err)
23 }
24
25- keys, err := getAuthorizedKeys(filepath.Join(cfg.DataPath, "authorized_keys"))
26- if err == nil {
27- cfg.Admins = keys
28- } else {
29- logger.Error("could not parse authorized keys file", "err", err)
30- }
31-
32 be := &Backend{
33 DB: dbh,
34- Logger: logger,
35+ Logger: cfg.Logger,
36 Cfg: cfg,
37 }
38 prCmd := &PrCmd{
39@@ -74,25 +60,26 @@ func GitSshServer(cfg *GitCfg) {
40 )
41
42 if err != nil {
43- logger.Error("could not create server", "err", err)
44+ cfg.Logger.Error("could not create server", "err", err)
45+ return
46 }
47
48 done := make(chan os.Signal, 1)
49 signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
50- logger.Info("starting SSH server", "host", cfg.Host, "port", cfg.SshPort)
51+ cfg.Logger.Info("starting SSH server", "host", cfg.Host, "port", cfg.SshPort)
52 go func() {
53 if err = s.ListenAndServe(); err != nil {
54- logger.Error("serve error", "err", err)
55+ cfg.Logger.Error("serve error", "err", err)
56 os.Exit(1)
57 }
58 }()
59
60 <-done
61- logger.Info("stopping SSH server")
62+ cfg.Logger.Info("stopping SSH server")
63 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
64 defer func() { cancel() }()
65 if err := s.Shutdown(ctx); err != nil {
66- logger.Error("shutdown", "err", err)
67+ cfg.Logger.Error("shutdown", "err", err)
68 os.Exit(1)
69 }
70 }
M
util.go
+5,
-24
1@@ -1,13 +1,8 @@
2 package git
3
4 import (
5- "bufio"
6- "bytes"
7- "errors"
8 "fmt"
9- "io"
10 "math/rand"
11- "os"
12 "strconv"
13 "strings"
14
15@@ -29,30 +24,16 @@ func truncateSha(sha string) string {
16 return sha[:7]
17 }
18
19-func getAuthorizedKeys(path string) ([]ssh.PublicKey, error) {
20+func getAuthorizedKeys(pubkeys []string) ([]ssh.PublicKey, error) {
21 keys := []ssh.PublicKey{}
22- f, err := os.Open(path)
23- if err != nil {
24- return keys, err
25- }
26- defer f.Close() // nolint: errcheck
27-
28- rd := bufio.NewReader(f)
29- for {
30- line, _, err := rd.ReadLine()
31- if err != nil {
32- if errors.Is(err, io.EOF) {
33- break
34- }
35- return keys, err
36- }
37- if strings.TrimSpace(string(line)) == "" {
38+ for _, pubkey := range pubkeys {
39+ if strings.TrimSpace(pubkey) == "" {
40 continue
41 }
42- if bytes.HasPrefix(line, []byte{'#'}) {
43+ if strings.HasPrefix(pubkey, "#") {
44 continue
45 }
46- upk, _, _, _, err := ssh.ParseAuthorizedKey(line)
47+ upk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubkey))
48 if err != nil {
49 return keys, err
50 }
M
web.go
+7,
-15
1@@ -484,17 +484,16 @@ func chromaStyleHandler(w http.ResponseWriter, r *http.Request) {
2
3 func StartWebServer(cfg *GitCfg) {
4 addr := fmt.Sprintf("%s:%s", cfg.Host, cfg.WebPort)
5- logger := slog.Default()
6
7- dbh, err := Open(filepath.Join(cfg.DataPath, "pr.db"), logger)
8+ dbh, err := Open(filepath.Join(cfg.DataPath, "pr.db"), cfg.Logger)
9 if err != nil {
10- logger.Error("could not open db", "err", err)
11+ cfg.Logger.Error("could not open db", "err", err)
12 return
13 }
14
15 be := &Backend{
16 DB: dbh,
17- Logger: logger,
18+ Logger: cfg.Logger,
19 Cfg: cfg,
20 }
21 prCmd := &PrCmd{
22@@ -507,16 +506,9 @@ func StartWebServer(cfg *GitCfg) {
23 web := &WebCtx{
24 Pr: prCmd,
25 Backend: be,
26- Logger: logger,
27+ Logger: cfg.Logger,
28 Formatter: formatter,
29- Theme: styles.Get("dracula"),
30- }
31-
32- keys, err := getAuthorizedKeys(filepath.Join(cfg.DataPath, "authorized_keys"))
33- if err == nil {
34- cfg.Admins = keys
35- } else {
36- logger.Error("could not parse authorized keys file", "err", err)
37+ Theme: styles.Get(cfg.Theme),
38 }
39
40 ctx := context.Background()
41@@ -532,9 +524,9 @@ func StartWebServer(cfg *GitCfg) {
42 http.HandleFunc("GET /syntax.css", ctxMdw(ctx, chromaStyleHandler))
43 http.HandleFunc("GET /rss", ctxMdw(ctx, rssHandler))
44
45- logger.Info("starting web server", "addr", addr)
46+ cfg.Logger.Info("starting web server", "addr", addr)
47 err = http.ListenAndServe(addr, nil)
48 if err != nil {
49- logger.Error("listen", "err", err)
50+ cfg.Logger.Error("listen", "err", err)
51 }
52 }