parent:
2214fef21788685c958b498f3e632bbbfc863ea3
nmyk <nick@nmyk.io>
2026-02-23T08:41:09-05:00
add last update time to index page sort repos by last update time in descending order
diff --git a/go.mod b/go.mod
index 3eb936d0087105b90116c36abdfd3d14facd05a8..c500eda0bc79434ce2d33a0d2046cd7bb9d818f9 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@
go 1.25.0
require (
+ github.com/dustin/go-humanize v1.0.1
github.com/go-git/go-git/v6 v6.0.0-20260220113129-c02711164eb8
github.com/peterbourgon/ff/v4 v4.0.0-beta.1
)
diff --git a/go.sum b/go.sum
index 6d7af3dd9640cd9a889a2e8ca20f74c91accb64b..27e40590f5a4ec1b031fe8d881173e738d291580 100644
--- a/go.sum
+++ b/go.sum
@@ -13,6 +13,8 @@ github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
diff --git a/internal/core/repo.go b/internal/core/repo.go
index f1230751ad3e44e8384331987d51f494dee89330..a2befcad3b24d4eeed2f859df06c36a28a4f6430 100644
--- a/internal/core/repo.go
+++ b/internal/core/repo.go
@@ -13,19 +13,21 @@ )
const dotGit = ".git"
-func isBareRepo(e os.DirEntry) bool {
- return (e.IsDir() && strings.HasSuffix(e.Name(), dotGit) && e.Name() != dotGit)
-}
-
-func List(dir string) ([]string, error) {
+func List(dir string) ([]*Repo, error) {
entries, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
- var repos []string
+ var repos []*Repo
for _, e := range entries {
- if isBareRepo(e) {
- repos = append(repos, strings.TrimSuffix(e.Name(), dotGit))
+ if e.Name() == dotGit {
+ continue
+ }
+ repo, err := Open(strings.TrimSuffix(e.Name(), dotGit))
+ if err == nil {
+ if _, err = repo.Worktree(); errors.Is(err, git.ErrIsBareRepository) {
+ repos = append(repos, repo)
+ }
}
}
if len(repos) == 0 {
@@ -36,6 +38,7 @@ }
type Repo struct {
*git.Repository
+ Name string
}
func Open(repoName string) (*Repo, error) {
@@ -46,7 +49,7 @@ repo, err := git.PlainOpen(repoName + dotGit)
if err != nil {
return nil, err
}
- return &Repo{repo}, nil
+ return &Repo{repo, repoName}, nil
}
func (repo *Repo) Load(entry *object.TreeEntry) (string, error) {
diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go
index e3eec2c72d657d479eca187d96bdaaae7257f6a1..8f04a45e151430b71265c951041141045ed8b3f5 100644
--- a/internal/handlers/handlers.go
+++ b/internal/handlers/handlers.go
@@ -13,6 +13,7 @@ "slices"
"strings"
"time"
+ "github.com/dustin/go-humanize"
"github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/filemode"
@@ -96,7 +97,13 @@
type IndexData struct {
Title string
Header string
- Repos []string
+ Repos []RepoMeta
+}
+
+type RepoMeta struct {
+ Name string
+ Updated string
+ updateTime time.Time
}
func (s Server) ListRepos(w http.ResponseWriter, _ *http.Request) {
@@ -105,10 +112,33 @@ if err != nil {
s.error(w, err)
return
}
+ var repoList []RepoMeta
+ for _, repo := range repos {
+ ref, err := repo.Head()
+ if err != nil {
+ s.error(w, err)
+ }
+ commit, err := repo.CommitObject(ref.Hash())
+ if err != nil {
+ s.error(w, err)
+ }
+ lastUpdatedTime := commit.Committer.When
+ lastUpdated := humanize.Time(lastUpdatedTime)
+ meta := RepoMeta{
+ repo.Name,
+ lastUpdated,
+ lastUpdatedTime,
+ }
+ repoList = append(repoList, meta)
+ }
+ slices.SortFunc(repoList, func(a RepoMeta, b RepoMeta) int {
+ // order by updateTime descending
+ return -1 * a.updateTime.Compare(b.updateTime)
+ })
data := IndexData{
Title: s.Hostname,
Header: s.Greeting,
- Repos: repos,
+ Repos: repoList,
}
s.Serve(w, "repos", data)
}
diff --git a/internal/handlers/static/style.css b/internal/handlers/static/style.css
index d444c08d5f2a27c0a080c6563935313a707a9ad0..cb240c30a65cea23274e756b55755adefb37234a 100644
--- a/internal/handlers/static/style.css
+++ b/internal/handlers/static/style.css
@@ -71,4 +71,8 @@
pre {
overflow-x: auto;
padding-bottom: 10px;
+}
+
+table {
+ width: 100%;
}
\ No newline at end of file
diff --git a/internal/handlers/templates/repos.tmpl b/internal/handlers/templates/repos.tmpl
index 28fb5e11c198177f67558eea8af2563c37090e0b..4dcbf3937e14b8b3b3fbc34733878f92aa396db4 100644
--- a/internal/handlers/templates/repos.tmpl
+++ b/internal/handlers/templates/repos.tmpl
@@ -2,11 +2,16 @@ {{define "title"}}{{.Title}}{{end}}
{{define "content"}}
<h1>{{.Title}}</h1>
<h4>{{.Header}}</h4>
-<ul>
+<table>
+<tbody>
{{range .Repos}}
-<li><a href="/{{.}}">{{.}}</a></li>
-{{end}}
-</ul>
+ <tr>
+ <td><a href="/{{.Name}}">{{.Name}}</a></td>
+ <td>{{.Updated}}</td>
+ </tr>
+ {{end}}
+</tbody>
+</table>
{{end}}
{{define "repos"}}