all repos

mugit @ b407568

🐮 git server that your cow will love
15 files changed, 287 insertions(+), 0 deletions(-)
init
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-01-21 01:49:14 +0200
Change ID: spymymvukmmmwkrytuporvwntkkykzml
A config.yml

@@ -0,0 +1,26 @@

+server: + host: 0.0.0.0 + port: 8008 + +meta: + title: i like git + description: hey kid, come get your free software + host: git.olexsmir.xyz + templates_dir: /home/olex/code/mugit/templates + +repo: + dir: /home/olex/mugit-test/ + readmes: + - README + - README.md + - readme + - readme.md + - readme.txt + masters: + - master + - main + private: + # repo also can be marked as private by: + # - putting `muprivate` file in the root of repo + # - adding `[mugit]\n private = true` + - org
A go.mod

@@ -0,0 +1,5 @@

+module olexsmir.xyz/mugit + +go 1.25.3 + +require gopkg.in/yaml.v2 v2.4.0
A go.sum

@@ -0,0 +1,4 @@

+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
A internal/config/config.go

@@ -0,0 +1,66 @@

+package config + +import ( + "fmt" + "os" + "path/filepath" + + "gopkg.in/yaml.v2" +) + +type Config struct { + Server struct { + Host string `yaml:"host"` + Port int `yaml:"port"` + } `yaml:"server"` + Meta struct { + Title string `yaml:"title"` + Description string `yaml:"description"` + Host string `yaml:"host"` + ChromaTheme string `yaml:"chroma_theme"` + TemplatesDir string `yaml:"templates_dir"` + } `yaml:"meta"` + Repo struct { + Dir string `yaml:"dir"` + Readmes []string `yaml:"readmes"` + Masters []string `yaml:"masters"` + Private []string `yaml:"private"` + } `yaml:"repo"` + SSH struct { + Keys []string `yaml:"keys"` + } `yaml:"ssh"` +} + +func Load(fpath string) (*Config, error) { + configBytes, err := os.ReadFile(fpath) + if err != nil { + return nil, err + } + + var config *Config + if cerr := yaml.Unmarshal(configBytes, &config); cerr != nil { + return nil, fmt.Errorf("parsing config: %w", cerr) + } + + if config.Repo.Dir, err = filepath.Abs(config.Repo.Dir); err != nil { + return nil, err + } + + if config.Meta.TemplatesDir, err = filepath.Abs(config.Meta.TemplatesDir); err != nil { + return nil, err + } + + fmt.Println(config.Meta.TemplatesDir) + + if verr := config.validate(); verr != nil { + return nil, verr + } + + return config, nil +} + +func (c Config) validate() error { + // var errs []error + // return errors.Join(errs...) + return nil +}
A internal/git/git.go

@@ -0,0 +1,4 @@

+package git + + +
A internal/handlers/git.go

@@ -0,0 +1,1 @@

+package handlers
A internal/handlers/handles.go

@@ -0,0 +1,26 @@

+package handlers + +import ( + "html/template" + "net/http" + "path/filepath" + + "olexsmir.xyz/mugit/internal/config" +) + +type handlers struct { + c *config.Config + t *template.Template +} + +func InitRoutes(cfg *config.Config) *http.ServeMux { + tmpls := template.Must(template.ParseGlob( + filepath.Join(cfg.Meta.TemplatesDir, "*"), + )) + h := handlers{cfg, tmpls} + + mux := http.NewServeMux() + mux.HandleFunc("GET /", h.index) + + return mux +}
A internal/handlers/routes.go

@@ -0,0 +1,16 @@

+package handlers + +import ( + "log/slog" + "net/http" +) + +func (h *handlers) index(w http.ResponseWriter, r *http.Request) { + data := make(map[string]any) + data["meta"] = h.c.Meta + + w.WriteHeader(http.StatusOK) + if err := h.t.ExecuteTemplate(w, "index", nil); err != nil { + slog.Error("index template", "err", err) + } +}
A internal/handlers/util.go

@@ -0,0 +1,20 @@

+package handlers + +import ( + "log/slog" + "net/http" +) + +func (h *handlers) write404(w http.ResponseWriter) { + w.WriteHeader(http.StatusNotFound) + if err := h.t.ExecuteTemplate(w, "404", nil); err != nil { + slog.Error("404 template", "err", err) + } +} + +func (h *handlers) write500(w http.ResponseWriter) { + w.WriteHeader(http.StatusInternalServerError) + if err := h.t.ExecuteTemplate(w, "500", nil); err != nil { + slog.Error("500 template", "err", err) + } +}
A main.go

@@ -0,0 +1,38 @@

+package main + +import ( + "log" + "log/slog" + "net" + "net/http" + "os" + "strconv" + + "olexsmir.xyz/mugit/internal/config" + "olexsmir.xyz/mugit/internal/handlers" +) + +func main() { + if err := run(); err != nil { + log.Fatalf("main: %s", err) + os.Exit(1) + } +} + +func run() error { + cfg, err := config.Load("/home/olex/code/mugit/config.yml") + if err != nil { + slog.Error("config error", "err", err) + return err + } + + mux := handlers.InitRoutes(cfg) + + port := strconv.Itoa(cfg.Server.Port) + err = http.ListenAndServe(net.JoinHostPort(cfg.Server.Host, port), mux) + if err != nil { + slog.Error("server error", "err", err) + } + + return nil +}
A templates/404.html

@@ -0,0 +1,12 @@

+{{ define "404 "}} +<html> + <title>404</title> +{{ template "head" . }} + <body> + {{ template "nav" . }} + <main> + <h3>404 &mdash; nothing like that here.</h3> + </main> + </body> +</html> +{{ end }}
A templates/500.html

@@ -0,0 +1,12 @@

+{{ define "500" }} +<html> + <title>500</title> +{{ template "head" . }} + <body> + {{ template "nav" . }} + <main> + <h3>500 &mdash; something broke!</h3> + </main> + </body> +</html> +{{ end }}
A templates/_head.html

@@ -0,0 +1,31 @@

+{{ define "head" }} + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel="stylesheet" href="/static/style.css" type="text/css"> + <!-- TODO: icon --> + {{ if .parent }} + <title>{{ .meta.Title }} &mdash; {{ .name }} ({{ .ref }}): {{ .parent }}/</title> + + {{ else if .path }} + <title>{{ .meta.Title }} &mdash; {{ .name }} ({{ .ref }}): {{ .path }}</title> + {{ else if .files }} + <title>{{ .meta.Title }} &mdash; {{ .name }} ({{ .ref }})</title> + {{ else if .commit }} + <title>{{ .meta.Title }} &mdash; {{ .name }}: {{ .commit.This }}</title> + {{ else if .branches }} + <title>{{ .meta.Title }} &mdash; {{ .name }}: refs</title> + {{ else if .commits }} + {{ if .log }} + <title>{{ .meta.Title }} &mdash; {{ .name }}: log</title> + {{ else }} + <title>{{ .meta.Title }} &mdash; {{ .name }}</title> + {{ end }} + {{ else }} + <title>{{ .meta.Title }}</title> + {{ end }} + <!-- {{ if and .servername .gomod }} --> + <!-- <meta name="go-import" content="{{ .servername}}/{{ .name }} git https://{{ .servername }}/{{ .name }}"> --> + <!-- {{ end }} --> + </head> +{{ end }}
A templates/_nav.html

@@ -0,0 +1,14 @@

+{{ define "nav" }} + <nav> + <ul> + {{ if .name }} + <li><a href="/{{ .name }}">summary</a> + <li><a href="/{{ .name }}/refs">refs</a> + {{ if .ref }} + <li><a href="/{{ .name }}/tree/{{ .ref }}/">tree</a> + <li><a href="/{{ .name }}/log/{{ .ref }}">log</a> + {{ end }} + {{ end }} + </ul> + </nav> +{{ end }}
A templates/index.html

@@ -0,0 +1,12 @@

+{{ define "index" }} +<html> +{{ template "head" . }} + <header> + <h1>{{ .meta.Title }}</h1> + <h2>{{ .meta.Description }}</h2> + </header> + <body> + hi + </body> +</html> +{{ end }}