Fixing ordering of commits on details page, refactor pass. - staticgit - A git static site generator, the site you are viewing now!
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
 (DIR) commit e3b9e35e373c00b591b8907ce7d7c1d3d7d2ce71
 (DIR) parent 2c17ce4ce5c99e093bcc742c8815e7ad64e3b233
 (HTM) Author: Jay Scott <me@jay.scot>
       Date:   Sun, 14 Jul 2024 20:58:56 +0100
       
       Fixing ordering of commits on details page, refactor pass.
       
       Diffstat:
         M main.go                             |     280 ++++++++++++++++----------------
       
       1 file changed, 140 insertions(+), 140 deletions(-)
       ---
 (DIR) diff --git a/main.go b/main.go
       @@ -16,23 +16,23 @@ import (
                "github.com/go-git/go-git/v5/plumbing/object"
        )
        
       -type CommitInfo struct {
       +type Commit struct {
                Hash    string
                Author  string
                Date    string
       -        Message string
       +        Msg     string
                Added   int
                Removed int
        }
        
       -type RepoInfo struct {
       -        Name           string
       -        Description    string
       -        LastCommitTime time.Time
       +type Repo struct {
       +        Name    string
       +        Desc    string
       +        LastMod time.Time
        }
        
        const (
       -        base = `
       +        baseHtml = `
        <!DOCTYPE html>
        <html lang="en">
        <head>
       @@ -72,7 +72,7 @@ const (
        </html>
        `
        
       -        details = `
       +        detailHtml = `
        {{define "content"}}
          <pre>{{.ReadmeContent}}</pre>
        
       @@ -92,7 +92,7 @@ const (
              {{range .Commits}}
              <tr>
                <td>{{.Date}}</td>
       -        <td>{{.Message}}</td>
       +        <td>{{.Msg}}</td>
                <td>{{.Author}}</td>
                <td>{{.Hash}}</td>
                <td>+{{.Added}}</td>
       @@ -111,7 +111,7 @@ const (
        {{end}}
        `
        
       -        index = `
       +        indexHtml = `
        {{define "content"}}
          <table>
            <thead>
       @@ -125,8 +125,8 @@ const (
              {{range .Repos}}
              <tr>
                <td><a href="{{.Name}}/index.html">{{.Name}}</a></td>
       -        <td>{{.Description}}</td>
       -        <td>{{.LastCommitTime.Format "2006-01-02 15:04:05"}}</td>
       +        <td>{{.Desc}}</td>
       +        <td>{{.LastMod.Format "2006-01-02 15:04:05"}}</td>
              </tr>
              {{end}}
            </tbody>
       @@ -137,64 +137,64 @@ const (
        
        var (
                templates = map[string]*template.Template{
       -                "index":   template.Must(template.New("base").Parse(base + index)),
       -                "details": template.Must(template.New("base").Parse(base + details)),
       +                "index":   template.Must(template.New("base").Parse(baseHtml + indexHtml)),
       +                "details": template.Must(template.New("base").Parse(baseHtml + detailHtml)),
                }
        
       -        reposPath   string
       -        ignoreDirs  map[string]bool
       -        outputRoot  string
       -        commitLimit int
       +        repoDir    string
       +        ignoreDirs map[string]bool
       +        outDir     string
       +        maxCommits int
        )
        
       -func generateIndex(repoInfos []RepoInfo) error {
       -        sort.Slice(repoInfos, func(i, j int) bool {
       -                return repoInfos[i].LastCommitTime.After(repoInfos[j].LastCommitTime)
       +func genIndex(repos []Repo) error {
       +        sort.Slice(repos, func(i, j int) bool {
       +                return repos[i].LastMod.After(repos[j].LastMod)
                })
        
       -        indexOutputPath := filepath.Join(outputRoot, "index.html")
       +        path := filepath.Join(outDir, "index.html")
        
       -        indexFile, err := os.Create(indexOutputPath)
       +        f, err := os.Create(path)
                if err != nil {
       -                return fmt.Errorf("failed to create index HTML file: %w", err)
       +                return fmt.Errorf("create index HTML: %w", err)
                }
       -        defer indexFile.Close()
       +        defer f.Close()
        
       -        return templates["index"].Execute(indexFile, struct {
       +        return templates["index"].Execute(f, struct {
                        Title string
       -                Repos []RepoInfo
       +                Repos []Repo
                }{
                        Title: "Repos for days!",
       -                Repos: repoInfos,
       +                Repos: repos,
                })
        }
        
       -func generateRepo(repoName, repoPath, outputDir string) error {
       -        repo, err := git.PlainOpen(repoPath)
       +func genRepo(name, path, out string) error {
       +        repo, err := git.PlainOpen(path)
                if err != nil {
       -                return fmt.Errorf("failed to open git repository: %w", err)
       +                return fmt.Errorf("open git repo: %w", err)
                }
        
       -        readme, err := getReadme(repoPath)
       +        readme, err := readme(path)
                if err != nil {
       -                return fmt.Errorf("failed to get README: %w", err)
       +                return fmt.Errorf("get README: %w", err)
                }
        
       -        commits, err := getCommits(repo)
       +        cs, err := commits(repo)
                if err != nil {
       -                return fmt.Errorf("failed to get commit history: %w", err)
       +                return fmt.Errorf("get commits: %w", err)
                }
        
       -        files, err := getFiles(repo)
       +        fs, err := files(repo)
                if err != nil {
       -                return fmt.Errorf("failed to get repository files: %w", err)
       +                return fmt.Errorf("get files: %w", err)
                }
        
       -        outputPath := filepath.Join(outputDir, "index.html")
       +        path = filepath.Join(out, "index.html")
        
       -        f, err := os.Create(outputPath)
       +        f, err := os.Create(path)
                if err != nil {
       -                return fmt.Errorf("failed to create details HTML file: %w", err)
       +                return fmt.Errorf("create details HTML: %w", err)
                }
                defer f.Close()
        
       @@ -202,130 +202,130 @@ func generateRepo(repoName, repoPath, outputDir string) error {
                        Title         string
                        ReadmeContent string
                        Files         []string
       -                Commits       []CommitInfo
       +                Commits       []Commit
                }{
       -                Title:         "git clone git@git.jay.scot:" + repoName,
       +                Title:         "git clone git@git.jay.scot:" + name,
                        ReadmeContent: readme,
       -                Files:         files,
       -                Commits:       commits,
       +                Files:         fs,
       +                Commits:       cs,
                })
        }
        
       -func getCommits(repo *git.Repository) ([]CommitInfo, error) {
       -        commitIter, err := repo.CommitObjects()
       +func commits(repo *git.Repository) ([]Commit, error) {
       +        iter, err := repo.CommitObjects()
                if err != nil {
       -                return nil, fmt.Errorf("failed to get commit objects: %w", err)
       +                return nil, fmt.Errorf("get commit objects: %w", err)
                }
        
       -        var commitHistory []CommitInfo
       +        var cs []Commit
                count := 0
       -        reachedLimit := false
       +        limit := false
        
       -        err = commitIter.ForEach(func(c *object.Commit) error {
       -                if reachedLimit {
       +        err = iter.ForEach(func(c *object.Commit) error {
       +                if limit {
                                return nil
                        }
        
                        stats, err := c.Stats()
                        if err != nil {
       -                        return fmt.Errorf("failed to get commit stats: %w", err)
       +                        return fmt.Errorf("get commit stats: %w", err)
                        }
        
       -                added, removed := 0, 0
       +                add, del := 0, 0
                        for _, stat := range stats {
       -                        added += stat.Addition
       -                        removed += stat.Deletion
       +                        add += stat.Addition
       +                        del += stat.Deletion
                        }
        
       -                commitHistory = append(commitHistory, CommitInfo{
       +                cs = append(cs, Commit{
                                Hash:    c.Hash.String()[:7],
                                Author:  c.Author.Name,
                                Date:    c.Author.When.Format("02 Jan 2006 15:04:05"),
       -                        Message: strings.Split(c.Message, "\n")[0],
       -                        Added:   added,
       -                        Removed: removed,
       +                        Msg:     strings.Split(c.Message, "\n")[0],
       +                        Added:   add,
       +                        Removed: del,
                        })
        
                        count++
       -                if count >= commitLimit {
       -                        reachedLimit = true
       +                if count >= maxCommits {
       +                        limit = true
                        }
                        return nil
                })
        
                if err != nil {
       -                return nil, fmt.Errorf("failed to iterate over commits: %w", err)
       +                return nil, fmt.Errorf("iterate commits: %w", err)
                }
        
       -        return commitHistory, nil
       -}
       +        sort.Slice(cs, func(i, j int) bool {
       +                timeI, _ := time.Parse("02 Jan 2006 15:04:05", cs[i].Date)
       +                timeJ, _ := time.Parse("02 Jan 2006 15:04:05", cs[j].Date)
       +                return timeI.After(timeJ)
       +        })
        
       -func getDescription(repoPath string) string {
       -        descPath := filepath.Join(repoPath, "description")
       -        description, _ := os.ReadFile(descPath)
       -        return strings.TrimSpace(string(description))
       +        return cs, nil
        }
        
       -func getFiles(repo *git.Repository) ([]string, error) {
       +func files(repo *git.Repository) ([]string, error) {
                ref, err := repo.Head()
                if err != nil {
       -                return nil, fmt.Errorf("failed to get HEAD reference: %w", err)
       +                return nil, fmt.Errorf("get HEAD: %w", err)
                }
        
                commit, err := repo.CommitObject(ref.Hash())
                if err != nil {
       -                return nil, fmt.Errorf("failed to get commit object: %w", err)
       +                return nil, fmt.Errorf("get commit object: %w", err)
                }
        
                tree, err := commit.Tree()
                if err != nil {
       -                return nil, fmt.Errorf("failed to get tree: %w", err)
       +                return nil, fmt.Errorf("get tree: %w", err)
                }
        
       -        var files []string
       +        var fs []string
                err = tree.Files().ForEach(func(f *object.File) error {
       -                files = append(files, f.Name)
       +                fs = append(fs, f.Name)
                        return nil
                })
                if err != nil {
       -                return nil, fmt.Errorf("failed to iterate over files: %w", err)
       +                return nil, fmt.Errorf("iterate files: %w", err)
                }
        
       -        sort.Strings(files)
       -        return files, nil
       +        sort.Strings(fs)
       +        return fs, nil
        }
        
       -func getReadme(repoPath string) (string, error) {
       -        readmeFiles := []string{"README.md", "README.txt", "README"}
       -        repo, err := git.PlainOpen(repoPath)
       +func readme(path string) (string, error) {
       +        names := []string{"README.md", "README.txt", "README"}
       +        repo, err := git.PlainOpen(path)
                if err != nil {
       -                return "", fmt.Errorf("failed to open git repository: %w", err)
       +                return "", fmt.Errorf("open git repo: %w", err)
                }
        
       -        headRef, err := repo.Head()
       +        ref, err := repo.Head()
                if err != nil {
       -                return "", fmt.Errorf("failed to get HEAD reference: %w", err)
       +                return "", fmt.Errorf("get HEAD: %w", err)
                }
        
       -        commit, err := repo.CommitObject(headRef.Hash())
       +        commit, err := repo.CommitObject(ref.Hash())
                if err != nil {
       -                return "", fmt.Errorf("failed to get commit object: %w", err)
       +                return "", fmt.Errorf("get commit object: %w", err)
                }
        
                tree, err := commit.Tree()
                if err != nil {
       -                return "", fmt.Errorf("failed to get tree: %w", err)
       +                return "", fmt.Errorf("get tree: %w", err)
                }
        
       -        for _, fileName := range readmeFiles {
       -                file, err := tree.File(fileName)
       +        for _, name := range names {
       +                file, err := tree.File(name)
                        if err != nil {
                                continue
                        }
        
                        content, err := file.Contents()
                        if err != nil {
       -                        return "", fmt.Errorf("failed to read file contents: %w", err)
       +                        return "", fmt.Errorf("read file contents: %w", err)
                        }
        
                        return content, nil
       @@ -334,100 +334,100 @@ func getReadme(repoPath string) (string, error) {
                return "No README found!", nil
        }
        
       -func getRepo(repoPath string) (RepoInfo, error) {
       -        repo, err := git.PlainOpen(repoPath)
       +func repoInfo(path string) (Repo, error) {
       +        repo, err := git.PlainOpen(path)
                if err != nil {
       -                return RepoInfo{}, fmt.Errorf("failed to open repository: %w", err)
       +                return Repo{}, fmt.Errorf("open repo: %w", err)
                }
        
       -        headRef, err := repo.Head()
       +        ref, err := repo.Head()
                if err != nil {
       -                return RepoInfo{}, fmt.Errorf("failed to get HEAD reference: %w", err)
       +                return Repo{}, fmt.Errorf("get HEAD: %w", err)
                }
        
       -        commit, err := repo.CommitObject(headRef.Hash())
       +        commit, err := repo.CommitObject(ref.Hash())
                if err != nil {
       -                return RepoInfo{}, fmt.Errorf("failed to get commit object: %w", err)
       +                return Repo{}, fmt.Errorf("get commit object: %w", err)
                }
        
       -        description := getDescription(repoPath)
       +        content, _ := os.ReadFile(filepath.Join(path, "description"))
        
       -        return RepoInfo{
       -                Name:           filepath.Base(repoPath),
       -                Description:    description,
       -                LastCommitTime: commit.Committer.When,
       +        return Repo{
       +                Name:    filepath.Base(path),
       +                Desc:    strings.TrimSpace(string(content)),
       +                LastMod: commit.Committer.When,
                }, nil
        }
        
       -func parseIgnored(ignoreDirs string) map[string]bool {
       -        ignoreMap := make(map[string]bool)
       -        for _, dir := range strings.Split(ignoreDirs, ",") {
       -                if trimmedDir := strings.TrimSpace(dir); trimmedDir != "" {
       -                        ignoreMap[trimmedDir] = true
       +func ignoreList(dirs string) map[string]bool {
       +        ignore := make(map[string]bool)
       +        for _, dir := range strings.Split(dirs, ",") {
       +                if d := strings.TrimSpace(dir); d != "" {
       +                        ignore[d] = true
                        }
                }
       -        return ignoreMap
       +        return ignore
        }
        
       -func processRepos() error {
       -        repos, err := os.ReadDir(reposPath)
       +func build() error {
       +        dirs, err := os.ReadDir(repoDir)
                if err != nil {
       -                return fmt.Errorf("failed to read repos directory: %w", err)
       +                return fmt.Errorf("read repos dir: %w", err)
                }
        
                var wg sync.WaitGroup
       -        repoInfosChan := make(chan RepoInfo, len(repos))
       -        errorsChan := make(chan error, len(repos))
       +        repoChan := make(chan Repo, len(dirs))
       +        errChan := make(chan error, len(dirs))
        
       -        for _, r := range repos {
       -                if r.IsDir() && !ignoreDirs[r.Name()] {
       +        for _, d := range dirs {
       +                if d.IsDir() && !ignoreDirs[d.Name()] {
                                wg.Add(1)
       -                        go func(r os.DirEntry) {
       +                        go func(d os.DirEntry) {
                                        defer wg.Done()
       -                                repoPath := filepath.Join(reposPath, r.Name())
       -                                repoInfo, err := getRepo(repoPath)
       +                                path := filepath.Join(repoDir, d.Name())
       +                                repo, err := repoInfo(path)
                                        if err != nil {
       -                                        errorsChan <- fmt.Errorf("failed to get info for repo %s: %w", r.Name(), err)
       +                                        errChan <- fmt.Errorf("get info for %s: %w", d.Name(), err)
                                                return
                                        }
       -                                repoInfosChan <- repoInfo
       +                                repoChan <- repo
        
       -                                outputDir := filepath.Join(outputRoot, r.Name())
       -                                if err := os.MkdirAll(outputDir, 0755); err != nil {
       -                                        errorsChan <- fmt.Errorf("failed to create output directory for repo %s: %w", r.Name(), err)
       +                                out := filepath.Join(outDir, d.Name())
       +                                if err := os.MkdirAll(out, 0755); err != nil {
       +                                        errChan <- fmt.Errorf("create dir for %s: %w", d.Name(), err)
                                                return
                                        }
        
       -                                if err := generateRepo(r.Name(), repoPath, outputDir); err != nil {
       -                                        errorsChan <- fmt.Errorf("failed to process repo %s: %w", r.Name(), err)
       +                                if err := genRepo(d.Name(), path, out); err != nil {
       +                                        errChan <- fmt.Errorf("process %s: %w", d.Name(), err)
                                        }
       -                        }(r)
       +                        }(d)
                        }
                }
        
                go func() {
                        wg.Wait()
       -                close(repoInfosChan)
       -                close(errorsChan)
       +                close(repoChan)
       +                close(errChan)
                }()
        
       -        var repoInfos []RepoInfo
       -        for repoInfo := range repoInfosChan {
       -                repoInfos = append(repoInfos, repoInfo)
       +        var repos []Repo
       +        for repo := range repoChan {
       +                repos = append(repos, repo)
                }
        
       -        for err := range errorsChan {
       +        for err := range errChan {
                        fmt.Println(err)
                }
        
       -        return generateIndex(repoInfos)
       +        return genIndex(repos)
        }
        
        func main() {
       -        flag.StringVar(&reposPath, "p", "", "Path to the git repositories (required)")
       -        flag.StringVar(&outputRoot, "o", ".", "Root path where output directories will be created")
       -        flag.IntVar(&commitLimit, "c", 100, "Limit for the number of commits to display (default 100)")
       -        ignoreFlag := flag.String("i", "", "Directories to ignore (comma-separated)")
       +        flag.StringVar(&repoDir, "p", "", "Path to git repos (required)")
       +        flag.StringVar(&outDir, "o", ".", "Root path for output")
       +        flag.IntVar(&maxCommits, "c", 100, "Max commits to display (default 100)")
       +        ignore := flag.String("i", "", "Dirs to ignore (comma-separated)")
        
                flag.Usage = func() {
                        fmt.Fprintf(os.Stderr, "Usage: %s [options]\n\n", os.Args[0])
       @@ -439,14 +439,14 @@ func main() {
        
                flag.Parse()
        
       -        if reposPath == "" {
       +        if repoDir == "" {
                        flag.Usage()
                        os.Exit(1)
                }
        
       -        ignoreDirs = parseIgnored(*ignoreFlag)
       +        ignoreDirs = ignoreList(*ignore)
        
       -        if err := processRepos(); err != nil {
       -                log.Fatalf("Error processing repositories: %v", err)
       +        if err := build(); err != nil {
       +                log.Fatalf("Error building site: %v", err)
                }
        }