all repos

mugit @ b407568d0a3ac5a14ba9b93f349903d11e7c224d

馃惍 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
Authored at: 2026-01-17 23:00:41 +0200
Change ID: spymymvukmmmwkrytuporvwntkkykzml
A config.yml
路路路
        
        1
        +server:

      
        
        2
        +  host: 0.0.0.0

      
        
        3
        +  port: 8008

      
        
        4
        +

      
        
        5
        +meta:

      
        
        6
        +  title: i like git

      
        
        7
        +  description: hey kid, come get your free software

      
        
        8
        +  host: git.olexsmir.xyz

      
        
        9
        +  templates_dir: /home/olex/code/mugit/templates

      
        
        10
        +

      
        
        11
        +repo:

      
        
        12
        +  dir: /home/olex/mugit-test/

      
        
        13
        +  readmes:

      
        
        14
        +    - README

      
        
        15
        +    - README.md

      
        
        16
        +    - readme

      
        
        17
        +    - readme.md

      
        
        18
        +    - readme.txt

      
        
        19
        +  masters:

      
        
        20
        +    - master

      
        
        21
        +    - main

      
        
        22
        +  private:

      
        
        23
        +    # repo also can be marked as private by:

      
        
        24
        +    # - putting `muprivate` file in the root of repo

      
        
        25
        +    # - adding `[mugit]\n  private = true`

      
        
        26
        +    - org

      
A go.mod
路路路
        
        1
        +module olexsmir.xyz/mugit

      
        
        2
        +

      
        
        3
        +go 1.25.3

      
        
        4
        +

      
        
        5
        +require gopkg.in/yaml.v2 v2.4.0

      
A go.sum
路路路
        
        1
        +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

      
        
        2
        +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

      
        
        3
        +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=

      
        
        4
        +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

      
A internal/config/config.go
路路路
        
        1
        +package config

      
        
        2
        +

      
        
        3
        +import (

      
        
        4
        +	"fmt"

      
        
        5
        +	"os"

      
        
        6
        +	"path/filepath"

      
        
        7
        +

      
        
        8
        +	"gopkg.in/yaml.v2"

      
        
        9
        +)

      
        
        10
        +

      
        
        11
        +type Config struct {

      
        
        12
        +	Server struct {

      
        
        13
        +		Host string `yaml:"host"`

      
        
        14
        +		Port int    `yaml:"port"`

      
        
        15
        +	} `yaml:"server"`

      
        
        16
        +	Meta struct {

      
        
        17
        +		Title        string `yaml:"title"`

      
        
        18
        +		Description  string `yaml:"description"`

      
        
        19
        +		Host         string `yaml:"host"`

      
        
        20
        +		ChromaTheme  string `yaml:"chroma_theme"`

      
        
        21
        +		TemplatesDir string `yaml:"templates_dir"`

      
        
        22
        +	} `yaml:"meta"`

      
        
        23
        +	Repo struct {

      
        
        24
        +		Dir     string   `yaml:"dir"`

      
        
        25
        +		Readmes []string `yaml:"readmes"`

      
        
        26
        +		Masters []string `yaml:"masters"`

      
        
        27
        +		Private []string `yaml:"private"`

      
        
        28
        +	} `yaml:"repo"`

      
        
        29
        +	SSH struct {

      
        
        30
        +		Keys []string `yaml:"keys"`

      
        
        31
        +	} `yaml:"ssh"`

      
        
        32
        +}

      
        
        33
        +

      
        
        34
        +func Load(fpath string) (*Config, error) {

      
        
        35
        +	configBytes, err := os.ReadFile(fpath)

      
        
        36
        +	if err != nil {

      
        
        37
        +		return nil, err

      
        
        38
        +	}

      
        
        39
        +

      
        
        40
        +	var config *Config

      
        
        41
        +	if cerr := yaml.Unmarshal(configBytes, &config); cerr != nil {

      
        
        42
        +		return nil, fmt.Errorf("parsing config: %w", cerr)

      
        
        43
        +	}

      
        
        44
        +

      
        
        45
        +	if config.Repo.Dir, err = filepath.Abs(config.Repo.Dir); err != nil {

      
        
        46
        +		return nil, err

      
        
        47
        +	}

      
        
        48
        +

      
        
        49
        +	if config.Meta.TemplatesDir, err = filepath.Abs(config.Meta.TemplatesDir); err != nil {

      
        
        50
        +		return nil, err

      
        
        51
        +	}

      
        
        52
        +

      
        
        53
        +	fmt.Println(config.Meta.TemplatesDir)

      
        
        54
        +

      
        
        55
        +	if verr := config.validate(); verr != nil {

      
        
        56
        +		return nil, verr

      
        
        57
        +	}

      
        
        58
        +

      
        
        59
        +	return config, nil

      
        
        60
        +}

      
        
        61
        +

      
        
        62
        +func (c Config) validate() error {

      
        
        63
        +	// var errs []error

      
        
        64
        +	// return errors.Join(errs...)

      
        
        65
        +	return nil

      
        
        66
        +}

      
A internal/git/git.go
路路路
        
        1
        +package git

      
        
        2
        +

      
        
        3
        +

      
        
        4
        +

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

      
A internal/handlers/handles.go
路路路
        
        1
        +package handlers

      
        
        2
        +

      
        
        3
        +import (

      
        
        4
        +	"html/template"

      
        
        5
        +	"net/http"

      
        
        6
        +	"path/filepath"

      
        
        7
        +

      
        
        8
        +	"olexsmir.xyz/mugit/internal/config"

      
        
        9
        +)

      
        
        10
        +

      
        
        11
        +type handlers struct {

      
        
        12
        +	c *config.Config

      
        
        13
        +	t *template.Template

      
        
        14
        +}

      
        
        15
        +

      
        
        16
        +func InitRoutes(cfg *config.Config) *http.ServeMux {

      
        
        17
        +	tmpls := template.Must(template.ParseGlob(

      
        
        18
        +		filepath.Join(cfg.Meta.TemplatesDir, "*"),

      
        
        19
        +	))

      
        
        20
        +	h := handlers{cfg, tmpls}

      
        
        21
        +

      
        
        22
        +	mux := http.NewServeMux()

      
        
        23
        +	mux.HandleFunc("GET /", h.index)

      
        
        24
        +

      
        
        25
        +	return mux

      
        
        26
        +}

      
A internal/handlers/routes.go
路路路
        
        1
        +package handlers

      
        
        2
        +

      
        
        3
        +import (

      
        
        4
        +	"log/slog"

      
        
        5
        +	"net/http"

      
        
        6
        +)

      
        
        7
        +

      
        
        8
        +func (h *handlers) index(w http.ResponseWriter, r *http.Request) {

      
        
        9
        +	data := make(map[string]any)

      
        
        10
        +	data["meta"] = h.c.Meta

      
        
        11
        +

      
        
        12
        +	w.WriteHeader(http.StatusOK)

      
        
        13
        +	if err := h.t.ExecuteTemplate(w, "index", nil); err != nil {

      
        
        14
        +		slog.Error("index template", "err", err)

      
        
        15
        +	}

      
        
        16
        +}

      
A internal/handlers/util.go
路路路
        
        1
        +package handlers

      
        
        2
        +

      
        
        3
        +import (

      
        
        4
        +	"log/slog"

      
        
        5
        +	"net/http"

      
        
        6
        +)

      
        
        7
        +

      
        
        8
        +func (h *handlers) write404(w http.ResponseWriter) {

      
        
        9
        +	w.WriteHeader(http.StatusNotFound)

      
        
        10
        +	if err := h.t.ExecuteTemplate(w, "404", nil); err != nil {

      
        
        11
        +		slog.Error("404 template", "err", err)

      
        
        12
        +	}

      
        
        13
        +}

      
        
        14
        +

      
        
        15
        +func (h *handlers) write500(w http.ResponseWriter) {

      
        
        16
        +	w.WriteHeader(http.StatusInternalServerError)

      
        
        17
        +	if err := h.t.ExecuteTemplate(w, "500", nil); err != nil {

      
        
        18
        +		slog.Error("500 template", "err", err)

      
        
        19
        +	}

      
        
        20
        +}

      
A main.go
路路路
        
        1
        +package main

      
        
        2
        +

      
        
        3
        +import (

      
        
        4
        +	"log"

      
        
        5
        +	"log/slog"

      
        
        6
        +	"net"

      
        
        7
        +	"net/http"

      
        
        8
        +	"os"

      
        
        9
        +	"strconv"

      
        
        10
        +

      
        
        11
        +	"olexsmir.xyz/mugit/internal/config"

      
        
        12
        +	"olexsmir.xyz/mugit/internal/handlers"

      
        
        13
        +)

      
        
        14
        +

      
        
        15
        +func main() {

      
        
        16
        +	if err := run(); err != nil {

      
        
        17
        +		log.Fatalf("main: %s", err)

      
        
        18
        +		os.Exit(1)

      
        
        19
        +	}

      
        
        20
        +}

      
        
        21
        +

      
        
        22
        +func run() error {

      
        
        23
        +	cfg, err := config.Load("/home/olex/code/mugit/config.yml")

      
        
        24
        +	if err != nil {

      
        
        25
        +		slog.Error("config error", "err", err)

      
        
        26
        +		return err

      
        
        27
        +	}

      
        
        28
        +

      
        
        29
        +	mux := handlers.InitRoutes(cfg)

      
        
        30
        +

      
        
        31
        +	port := strconv.Itoa(cfg.Server.Port)

      
        
        32
        +	err = http.ListenAndServe(net.JoinHostPort(cfg.Server.Host, port), mux)

      
        
        33
        +	if err != nil {

      
        
        34
        +		slog.Error("server error", "err", err)

      
        
        35
        +	}

      
        
        36
        +

      
        
        37
        +	return nil

      
        
        38
        +}

      
A templates/404.html
路路路
        
        1
        +{{ define "404 "}}

      
        
        2
        +<html>

      
        
        3
        +  <title>404</title>

      
        
        4
        +{{ template "head" . }}

      
        
        5
        +  <body>

      
        
        6
        +    {{ template "nav" . }}

      
        
        7
        +    <main>

      
        
        8
        +      <h3>404 &mdash; nothing like that here.</h3>

      
        
        9
        +    </main>

      
        
        10
        +  </body>

      
        
        11
        +</html>

      
        
        12
        +{{ end }}

      
A templates/500.html
路路路
        
        1
        +{{ define "500" }}

      
        
        2
        +<html>

      
        
        3
        +  <title>500</title>

      
        
        4
        +{{ template "head" . }}

      
        
        5
        +  <body>

      
        
        6
        +    {{ template "nav" . }}

      
        
        7
        +    <main>

      
        
        8
        +      <h3>500 &mdash; something broke!</h3>

      
        
        9
        +    </main>

      
        
        10
        +  </body>

      
        
        11
        +</html>

      
        
        12
        +{{ end }}

      
A templates/_head.html
路路路
        
        1
        +{{ define "head" }}

      
        
        2
        +  <head>

      
        
        3
        +    <meta charset="utf-8">

      
        
        4
        +    <meta name="viewport" content="width=device-width, initial-scale=1">

      
        
        5
        +    <link rel="stylesheet" href="/static/style.css" type="text/css">

      
        
        6
        +    <!-- TODO: icon -->

      
        
        7
        +    {{ if .parent }}

      
        
        8
        +    <title>{{ .meta.Title }} &mdash; {{ .name }} ({{ .ref }}): {{ .parent }}/</title>

      
        
        9
        +

      
        
        10
        +    {{ else if .path }}

      
        
        11
        +    <title>{{ .meta.Title }} &mdash; {{ .name }} ({{ .ref }}): {{ .path }}</title>

      
        
        12
        +    {{ else if .files }}

      
        
        13
        +    <title>{{ .meta.Title }} &mdash; {{ .name }} ({{ .ref }})</title>

      
        
        14
        +    {{ else if .commit }}

      
        
        15
        +    <title>{{ .meta.Title }} &mdash; {{ .name }}: {{ .commit.This }}</title>

      
        
        16
        +    {{ else if .branches }}

      
        
        17
        +    <title>{{ .meta.Title }} &mdash; {{ .name }}: refs</title>

      
        
        18
        +    {{ else if .commits }}

      
        
        19
        +    {{ if .log }}

      
        
        20
        +    <title>{{ .meta.Title }} &mdash; {{ .name }}: log</title>

      
        
        21
        +    {{ else }}

      
        
        22
        +    <title>{{ .meta.Title }} &mdash; {{ .name }}</title>

      
        
        23
        +    {{ end }}

      
        
        24
        +    {{ else }}

      
        
        25
        +    <title>{{ .meta.Title }}</title>

      
        
        26
        +    {{ end }}

      
        
        27
        +    <!-- {{ if and .servername .gomod }} -->

      
        
        28
        +    <!-- <meta name="go-import" content="{{ .servername}}/{{ .name }} git https://{{ .servername }}/{{ .name }}"> -->

      
        
        29
        +    <!-- {{ end }} -->

      
        
        30
        +  </head>

      
        
        31
        +{{ end }}

      
A templates/_nav.html
路路路
        
        1
        +{{ define "nav" }}

      
        
        2
        +  <nav>

      
        
        3
        +    <ul>

      
        
        4
        +    {{ if .name }}

      
        
        5
        +    <li><a href="/{{ .name }}">summary</a>

      
        
        6
        +    <li><a href="/{{ .name }}/refs">refs</a>

      
        
        7
        +      {{ if .ref }}

      
        
        8
        +      <li><a href="/{{ .name }}/tree/{{ .ref }}/">tree</a>

      
        
        9
        +      <li><a href="/{{ .name }}/log/{{ .ref }}">log</a>

      
        
        10
        +      {{ end }}

      
        
        11
        +    {{ end }}

      
        
        12
        +    </ul>

      
        
        13
        +  </nav>

      
        
        14
        +{{ end }}

      
A templates/index.html
路路路
        
        1
        +{{ define "index" }}

      
        
        2
        +<html>

      
        
        3
        +{{ template "head" . }}

      
        
        4
        +  <header>

      
        
        5
        +    <h1>{{ .meta.Title }}</h1>

      
        
        6
        +    <h2>{{ .meta.Description }}</h2>

      
        
        7
        +  </header>

      
        
        8
        +  <body>

      
        
        9
        +        hi

      
        
        10
        +  </body>

      
        
        11
        +</html>

      
        
        12
        +{{ end }}