8 files changed,
148 insertions(+),
9 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-02-13 18:49:23 +0200
Change ID:
okxtnpqzpvnpsvyzmvwsqtzuqmwpznlu
Parent:
56a787b
M
flake.nix
@@ -23,7 +23,7 @@ mugit = pkgs.buildGoModule {
pname = "mugit"; version = version; src = ./.; - vendorHash = "sha256-VcNnosr9Co+MFEA36s4BIDmg/bx+/mTIHdgOaGJKhbc="; + vendorHash = "sha256-rY/O5padrE0cwwnvLIR3lM9xdpwloy0OFbp6/ge5gAc="; ldflags = [ "-s" "-w"
M
go.mod
@@ -7,6 +7,7 @@ github.com/bluekeyes/go-gitdiff v0.8.1
github.com/cyphar/filepath-securejoin v0.4.1 github.com/gliderlabs/ssh v0.3.8 github.com/go-git/go-git/v5 v5.16.4 + github.com/gorilla/feeds v1.2.0 github.com/urfave/cli/v3 v3.6.2 github.com/yuin/goldmark v1.7.16 golang.org/x/crypto v0.47.0
M
go.sum
@@ -36,6 +36,8 @@ github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc= +github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
M
internal/git/repo.go
@@ -167,7 +167,10 @@ return "Not displaying binary file", nil
} } -type Branch struct{ Name string } +type Branch struct { + Name string + LastUpdate time.Time +} func (g *Repo) Branches() ([]*Branch, error) { bi, err := g.r.Branches()@@ -177,8 +180,14 @@ }
var branches []*Branch err = bi.ForEach(func(r *plumbing.Reference) error { + cmt, cerr := g.r.CommitObject(r.Hash()) + if cerr != nil { + return cerr + } + branches = append(branches, &Branch{ - Name: r.Name().Short(), + Name: r.Name().Short(), + LastUpdate: cmt.Committer.When, }) return nil })
A
internal/handlers/feed.go
@@ -0,0 +1,105 @@
+package handlers + +import ( + "net/http" + + "github.com/gorilla/feeds" +) + +func (h *handlers) repoFeedHandler(w http.ResponseWriter, r *http.Request) { + name := getNormalizedName(r.PathValue("name")) + repo, err := h.openPublicRepo(name, "") + if err != nil { + h.write404(w, err) + return + } + + desc, err := repo.Description() + if err != nil { + h.write500(w, err) + return + } + + repoName := repo.Name() + feed := &feeds.Feed{ + Title: repoName, + Link: &feeds.Link{Href: h.c.Meta.Host + "/" + repoName}, + Description: desc, + } + + // branches + branches, err := repo.Branches() + if err != nil { + h.write500(w, err) + return + } + + for _, branch := range branches { + feed.Items = append(feed.Items, &feeds.Item{ + Id: "b:" + branch.Name, + Title: "branch: " + branch.Name, + Link: &feeds.Link{Href: h.c.Meta.Host + "/tree/" + branch.Name}, + Updated: branch.LastUpdate, + }) + } + + // tags + tags, err := repo.Tags() + if err != nil { + h.write500(w, err) + } + + for _, tag := range tags { + feed.Items = append(feed.Items, &feeds.Item{ + Id: "t:" + tag.Name(), + Title: "tag: " + tag.Name(), + Link: &feeds.Link{Href: h.c.Meta.Host + "/tree/" + tag.Name()}, + Description: desc, + Updated: tag.When(), + Content: tag.Message(), + }) + } + + rss, err := feed.ToRss() + if err != nil { + h.write500(w, err) + return + } + + w.Header().Set("Content-Type", "application/rss+xml") + w.Write([]byte(rss)) +} + +func (h *handlers) indexFeedHandler(w http.ResponseWriter, r *http.Request) { + repos, err := h.listPublicRepos() + if err != nil { + h.write500(w, err) + return + } + + feed := &feeds.Feed{ + Title: h.c.Meta.Host, + Link: &feeds.Link{Href: h.c.Meta.Host}, + Description: h.c.Meta.Description, + } + + for _, repo := range repos { + feed.Items = append(feed.Items, &feeds.Item{ + Title: repo.Name, + Link: &feeds.Link{Href: h.c.Meta.Host + "/" + repo.Name}, + Description: repo.Desc, + Id: repo.Name, + Updated: repo.LastCommit, + Content: repo.Desc, + }) + } + + rss, err := feed.ToRss() + if err != nil { + h.write500(w, err) + return + } + + w.Header().Set("Content-Type", "application/rss+xml") + w.Write([]byte(rss)) +}
M
internal/handlers/handlers.go
@@ -25,11 +25,13 @@ h := handlers{cfg, tmpls}
mux := http.NewServeMux() mux.HandleFunc("GET /", h.indexHandler) + mux.HandleFunc("GET /index.xml", h.indexFeedHandler) mux.HandleFunc("GET /static/{file}", h.serveStatic) mux.HandleFunc("GET /{name}", h.multiplex) mux.HandleFunc("POST /{name}", h.multiplex) mux.HandleFunc("GET /{name}/{rest...}", h.multiplex) mux.HandleFunc("POST /{name}/{rest...}", h.multiplex) + mux.HandleFunc("GET /{name}/feed/{$}", h.repoFeedHandler) mux.HandleFunc("GET /{name}/tree/{ref}/{rest...}", h.repoTreeHandler) mux.HandleFunc("GET /{name}/blob/{ref}/{rest...}", h.fileContentsHandler) mux.HandleFunc("GET /{name}/log/{ref}", h.logHandler)
M
web/templates/_head.html
@@ -6,4 +6,9 @@ <link rel="icon" href="/static/favicon.svg">
{{ if and .servername .gomod }} <meta name="go-import" content="{{ .servername}}/{{ .name }} git https://{{ .servername }}/{{ .name }}"> {{ end }} + {{ if and .servername .name }} + <link rel="alternate" type="application/rss" href="{{ .servername }}/{{ .name }}/feed"> + {{ else }} + <link rel="alternate" type="application/rss" href="{{ .servername }}/index.xml"> + {{ end }} {{ end }}