Commit b6f8e2b

HPCesia <me@hpcesia.com>
2026-07-04 16:44:26
Add repos sort config
1 parent e7f9a09
docs/config.example.toml
@@ -66,6 +66,15 @@ site_name = "Gitmal"
 
 
 
+# Multi-repo sort order. Options:
+#   "config"    — as written in the config file (default)
+#   "name-asc"  — alphabetically by name A–Z
+#   "name-desc" — alphabetically by name Z–A
+#   "time-asc"  — by last commit time, oldest first
+#   "time-desc" — by last commit time, newest first
+# Has no effect in single-repo mode.
+# sort = "name-asc"
+
 # -------- Global settings (optional, overrideable by CLI flags) --------
 # These options can also be passed on the command line (higher priority).
 # When omitted, the CLI flag default (or program default) is used.
@@ -108,3 +117,4 @@ site_name = "Gitmal"
 #   --git                 Generate Git dumb HTTP protocol files
 #   --inline-styles       Embed CSS in HTML instead of external files
 #   --output              Output directory (default "output")
+#   --sort=str            Multi-repo sort order: config, name[-asc|-desc], time[-asc|-desc]
config.go
@@ -29,6 +29,7 @@ type Config struct {
 	SiteName     string            `toml:"site_name"`
 	Repo         *SingleRepoConfig `toml:"repo"`
 	Repos        []RepoEntry       `toml:"repos"`
+	Sort         string            `toml:"sort"`
 	Theme        string            `toml:"theme"`
 	ThemeLight   string            `toml:"theme_light"`
 	ThemeDark    string            `toml:"theme_dark"`
main.go
@@ -7,7 +7,9 @@ import (
 	"path/filepath"
 	"regexp"
 	"runtime/pprof"
+	"sort"
 	"strings"
+	"time"
 
 	"github.com/antonmedv/gitmal/pkg/git"
 
@@ -28,6 +30,7 @@ var (
 	flagGzip          bool
 	flagGit           bool
 	flagInlineStyles  bool
+	flagSort          string
 )
 
 type Params struct {
@@ -81,6 +84,7 @@ func main() {
 	flag.BoolVar(&flagGzip, "gzip", false, "Compress all generated HTML files")
 	flag.BoolVar(&flagGit, "git", false, "Generate static files for Git dumb HTTP protocol")
 	flag.BoolVar(&flagInlineStyles, "inline-styles", false, "Keep all CSS inline in HTML instead of external files")
+	flag.StringVar(&flagSort, "sort", "", "Multi-repo sort order: config, name[-asc|-desc], time[-asc|-desc]")
 	flag.Usage = usage
 	flag.Parse()
 
@@ -133,6 +137,13 @@ func main() {
 					DefaultBranch: repo.DefaultBranch,
 				})
 			}
+			sortMode := flagSort
+			if !flag.CommandLine.Changed("sort") && cfg.Sort != "" {
+				sortMode = cfg.Sort
+			}
+			if sortMode != "" && sortMode != "config" {
+				sortGeneratables(generatables, sortMode)
+			}
 		} else if cfg.Repo != nil {
 			repoPath := cfg.Repo.Path
 			if len(flag.Args()) > 0 {
@@ -452,6 +463,38 @@ func validateRepoPath(path string) error {
 	return nil
 }
 
+func sortGeneratables(gs []repoGeneration, mode string) {
+	switch mode {
+	case "time-asc", "time", "time-desc":
+	case "name-asc", "name", "name-desc":
+	default:
+		fmt.Fprintf(os.Stderr, "warning: unknown sort mode %q, ignoring\n", mode)
+		return
+	}
+
+	if strings.HasPrefix(mode, "time") {
+		times := make(map[string]time.Time, len(gs))
+		for _, g := range gs {
+			times[g.RepoDir] = lastCommitTime(g.RepoDir, g.DefaultBranch)
+		}
+		asc := mode == "time-asc" || mode == "time"
+		sort.Slice(gs, func(i, j int) bool {
+			if asc {
+				return times[gs[i].RepoDir].Before(times[gs[j].RepoDir])
+			}
+			return times[gs[i].RepoDir].After(times[gs[j].RepoDir])
+		})
+		return
+	}
+	asc := mode == "name-asc" || mode == "name"
+	sort.Slice(gs, func(i, j int) bool {
+		if asc {
+			return strings.ToLower(gs[i].Name) < strings.ToLower(gs[j].Name)
+		}
+		return strings.ToLower(gs[i].Name) > strings.ToLower(gs[j].Name)
+	})
+}
+
 func cfgReposToEntries(gs []repoGeneration) []RepoEntry {
 	entries := make([]RepoEntry, len(gs))
 	for i, g := range gs {