| Refactoring pass number 2. - staticgit - A git static site generator, the site … | |
| Log | |
| Files | |
| Refs | |
| README | |
| --- | |
| commit 884ed087159dcf350421c692b8c530b7db260c28 | |
| parent 91bdb33c51f86b1de9afbcc1f73122c300049226 | |
| Author: Jay Scott <[email protected]> | |
| Date: Fri, 12 Jul 2024 08:26:18 +0100 | |
| Refactoring pass number 2. | |
| Diffstat: | |
| M main.go | 466 +++++++++++++++++++++++++++++… | |
| D sealgit.go | 460 -----------------------------… | |
| 2 files changed, 454 insertions(+), 472 deletions(-) | |
| --- | |
| diff --git a/main.go b/main.go | |
| @@ -3,15 +3,462 @@ package main | |
| import ( | |
| "flag" | |
| "fmt" | |
| + "html/template" | |
| "log" | |
| "os" | |
| + "path/filepath" | |
| + "regexp" | |
| + "sort" | |
| + "strings" | |
| + | |
| + "github.com/go-git/go-git/v5" | |
| + "github.com/go-git/go-git/v5/plumbing" | |
| + "github.com/go-git/go-git/v5/plumbing/object" | |
| +) | |
| + | |
| +type BranchInfo struct { | |
| + Name string | |
| + LastCommit string | |
| + LastCommitDate string | |
| +} | |
| + | |
| +type CommitInfo struct { | |
| + Hash string | |
| + Author string | |
| + Date string | |
| + Message string | |
| +} | |
| + | |
| +type RepoInfo struct { | |
| + Name string | |
| + Description string | |
| + LastCommit string | |
| +} | |
| + | |
| +const ( | |
| + baseTemplate = ` | |
| +<!DOCTYPE html> | |
| +<html> | |
| +<head> | |
| +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |
| +<meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| +<title>{{.Title}}</title> | |
| +<link rel="icon" type="image/png" href="{{.IconPath}}favicon.png" /> | |
| +<link rel="stylesheet" type="text/css" href="{{.IconPath}}style.css" /> | |
| +</head> | |
| +<body> | |
| +<table> | |
| +<tr><td><img src="{{.IconPath}}logo.png" alt="" width="32" height="32" /></td> | |
| +<td><span class="desc">{{.Title}}</span></td></tr><tr><td></td><td> | |
| +</td></tr> | |
| +</table> | |
| +<hr/> | |
| +<div id="content"> | |
| +{{template "content" .}} | |
| +</div> | |
| +</body> | |
| +</html> | |
| +` | |
| + | |
| + branchesContent = ` | |
| +{{define "content"}} | |
| +<h1>{{.RepoName}} - Branches</h1> | |
| +<table> | |
| +<thead> | |
| +<tr><td><b>Branch Name</b></td><td><b>Last Commit</b></td><td><b>Last Commit D… | |
| +</thead> | |
| +<tbody> | |
| +{{range .Branches}} | |
| +<tr><td>{{.Name}}</td><td>{{.LastCommit}}</td><td>{{.LastCommitDate}}</td></tr> | |
| +{{end}} | |
| +</tbody> | |
| +</table> | |
| +{{end}} | |
| +` | |
| + | |
| + commitHistoryContent = ` | |
| +{{define "content"}} | |
| +<h1>{{.RepoName}} - Commit History</h1> | |
| +<table> | |
| +<thead> | |
| +<tr><td><b>Hash</b></td><td><b>Author</b></td><td><b>Date</b></td><td><b>Messa… | |
| +</thead> | |
| +<tbody> | |
| +{{range .Commits}} | |
| +<tr><td>{{.Hash}}</td><td>{{.Author}}</td><td>{{.Date}}</td><td>{{.Message}}</… | |
| +{{end}} | |
| +</tbody> | |
| +</table> | |
| +{{end}} | |
| +` | |
| + | |
| + indexContent = ` | |
| +{{define "content"}} | |
| +<table id="index"> | |
| +<thead> | |
| +<tr><td><b>Name</b></td><td><b>Description</b></td><td><b>Last commit</b></td>… | |
| +</thead> | |
| +<tbody> | |
| +{{range $group, $repos := .Repos}} | |
| +<tr><td colspan="4"><b>{{if eq $group ""}} {{else}}<hr>{{end}}</b></td></tr> | |
| + {{range $repos}} | |
| + <tr> | |
| + <td>{{.Name}}</td> | |
| + <td>{{.Description}}</td> | |
| + <td>| {{.LastCommit}} | </td> | |
| + <td> | |
| + <a href="{{.Name}}/README.html">readme</a> | | |
| + <a href="{{.Name}}/commits.html">commits</a> | | |
| + <a href="{{.Name}}/branches.html">branches</a> | |
| + </td> | |
| + </tr> | |
| + {{end}} | |
| +{{end}} | |
| +</tbody> | |
| +</table> | |
| +{{end}} | |
| +` | |
| + readmeContent = ` | |
| +{{define "content"}} | |
| +<h1>{{.RepoName}}</h1> | |
| +<pre>{{.ReadmeContent}}</pre> | |
| +{{end}} | |
| +` | |
| +) | |
| + | |
| +var ( | |
| + reposPath string | |
| + groupFlag bool | |
| + ignoreDirs map[string]bool | |
| + outputRoot string | |
| + | |
| + branchesTmpl = template.Must(template.New("base").Parse(baseTempl… | |
| + commitHistoryTmpl = template.Must(template.New("base").Parse(baseTempl… | |
| + indexTmpl = template.Must(template.New("base").Parse(baseTempl… | |
| + readmeTmpl = template.Must(template.New("base").Parse(baseTempl… | |
| ) | |
| +func generateIndexHTML(repoInfos []RepoInfo) error { | |
| + groupedRepos := groupRepos(repoInfos) | |
| + indexOutputPath := filepath.Join(outputRoot, "index.html") | |
| + | |
| + indexFile, err := os.Create(indexOutputPath) | |
| + if err != nil { | |
| + return fmt.Errorf("failed to create index HTML file: %w", err) | |
| + } | |
| + defer indexFile.Close() | |
| + | |
| + return indexTmpl.Execute(indexFile, struct { | |
| + Title string | |
| + IconPath string | |
| + Repos map[string][]RepoInfo | |
| + }{ | |
| + Title: "git clone [email protected]:<reponame>", | |
| + IconPath: "./", | |
| + Repos: groupedRepos, | |
| + }) | |
| +} | |
| + | |
| +func getBranchInfo(repo *git.Repository) ([]BranchInfo, error) { | |
| + branches, err := repo.Branches() | |
| + if err != nil { | |
| + return nil, fmt.Errorf("failed to get branches: %w", err) | |
| + } | |
| + | |
| + var branchInfos []BranchInfo | |
| + err = branches.ForEach(func(branch *plumbing.Reference) error { | |
| + commit, err := repo.CommitObject(branch.Hash()) | |
| + if err != nil { | |
| + return fmt.Errorf("failed to get commit for branch %s:… | |
| + } | |
| + | |
| + branchInfos = append(branchInfos, BranchInfo{ | |
| + Name: branch.Name().Short(), | |
| + LastCommit: commit.Hash.String()[:7], | |
| + LastCommitDate: commit.Author.When.Format("02 Jan 2006… | |
| + }) | |
| + return nil | |
| + }) | |
| + | |
| + if err != nil { | |
| + return nil, fmt.Errorf("failed to iterate over branches: %w", … | |
| + } | |
| + | |
| + return branchInfos, nil | |
| +} | |
| + | |
| +func getCommitHistory(repo *git.Repository) ([]CommitInfo, error) { | |
| + ref, err := repo.Head() | |
| + if err != nil { | |
| + return nil, fmt.Errorf("failed to get HEAD reference: %w", err) | |
| + } | |
| + | |
| + commits, err := repo.Log(&git.LogOptions{From: ref.Hash()}) | |
| + if err != nil { | |
| + return nil, fmt.Errorf("failed to get commit log: %w", err) | |
| + } | |
| + | |
| + var commitHistory []CommitInfo | |
| + err = commits.ForEach(func(c *object.Commit) error { | |
| + commitHistory = append(commitHistory, CommitInfo{ | |
| + 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], // Get onl… | |
| + }) | |
| + return nil | |
| + }) | |
| + | |
| + if err != nil { | |
| + return nil, fmt.Errorf("failed to iterate over commits: %w", e… | |
| + } | |
| + | |
| + return commitHistory, nil | |
| +} | |
| + | |
| +func getDescription(repoPath string) string { | |
| + descPath := filepath.Join(repoPath, "description") | |
| + description, _ := os.ReadFile(descPath) | |
| + return strings.TrimSpace(string(description)) | |
| +} | |
| + | |
| +func getReadme(repoPath string) (string, error) { | |
| + readmeFiles := []string{"README.md", "README.txt", "README"} | |
| + repo, err := git.PlainOpen(repoPath) | |
| + if err != nil { | |
| + return "", fmt.Errorf("failed to open git repository: %w", err) | |
| + } | |
| + | |
| + headRef, err := repo.Head() | |
| + if err != nil { | |
| + return "", fmt.Errorf("failed to get HEAD reference: %w", err) | |
| + } | |
| + | |
| + commit, err := repo.CommitObject(headRef.Hash()) | |
| + if err != nil { | |
| + return "", fmt.Errorf("failed to get commit object: %w", err) | |
| + } | |
| + | |
| + tree, err := commit.Tree() | |
| + if err != nil { | |
| + return "", fmt.Errorf("failed to get tree: %w", err) | |
| + } | |
| + | |
| + for _, fileName := range readmeFiles { | |
| + file, err := tree.File(fileName) | |
| + if err != nil { | |
| + continue | |
| + } | |
| + | |
| + content, err := file.Contents() | |
| + if err != nil { | |
| + return "", fmt.Errorf("failed to read file contents: %… | |
| + } | |
| + | |
| + return content, nil | |
| + } | |
| + | |
| + return "No README found!", nil | |
| +} | |
| + | |
| +func getRepoInfo(repoPath string) (RepoInfo, error) { | |
| + repo, err := git.PlainOpen(repoPath) | |
| + if err != nil { | |
| + return RepoInfo{}, fmt.Errorf("failed to open repository: %w",… | |
| + } | |
| + | |
| + headRef, err := repo.Head() | |
| + if err != nil { | |
| + return RepoInfo{}, fmt.Errorf("failed to get HEAD reference: %… | |
| + } | |
| + | |
| + commit, err := repo.CommitObject(headRef.Hash()) | |
| + if err != nil { | |
| + return RepoInfo{}, fmt.Errorf("failed to get commit object: %w… | |
| + } | |
| + | |
| + description := getDescription(repoPath) | |
| + | |
| + return RepoInfo{ | |
| + Name: filepath.Base(repoPath), | |
| + Description: description, | |
| + LastCommit: commit.Committer.When.Format("02 Jan 2006"), | |
| + }, nil | |
| +} | |
| + | |
| +func groupRepos(repos []RepoInfo) map[string][]RepoInfo { | |
| + groupedRepos := make(map[string][]RepoInfo) | |
| + for _, repo := range repos { | |
| + group := getGroup(repo.Description) | |
| + groupedRepos[group] = append(groupedRepos[group], repo) | |
| + } | |
| + | |
| + for _, repoList := range groupedRepos { | |
| + sort.Slice(repoList, func(i, j int) bool { | |
| + return strings.ToLower(repoList[i].Name) < strings.ToL… | |
| + }) | |
| + } | |
| + | |
| + return groupedRepos | |
| +} | |
| + | |
| +func getGroup(description string) string { | |
| + if groupFlag { | |
| + groupRegex := regexp.MustCompile(`\[(.*?)\]`) | |
| + matches := groupRegex.FindStringSubmatch(description) | |
| + if len(matches) > 1 { | |
| + return matches[1] | |
| + } | |
| + } | |
| + return "" | |
| +} | |
| + | |
| +func parseIgnoreDirs(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 | |
| + } | |
| + } | |
| + return ignoreMap | |
| +} | |
| + | |
| +func processBranches(repoName, repoPath, outputDir string) error { | |
| + repo, err := git.PlainOpen(repoPath) | |
| + if err != nil { | |
| + return fmt.Errorf("failed to open git repository: %w", err) | |
| + } | |
| + | |
| + branches, err := getBranchInfo(repo) | |
| + if err != nil { | |
| + return fmt.Errorf("failed to get branch information: %w", err) | |
| + } | |
| + | |
| + outputPath := filepath.Join(outputDir, "branches.html") | |
| + | |
| + f, err := os.Create(outputPath) | |
| + if err != nil { | |
| + return fmt.Errorf("failed to create branches HTML file: %w", e… | |
| + } | |
| + defer f.Close() | |
| + | |
| + return branchesTmpl.Execute(f, struct { | |
| + Title string | |
| + IconPath string | |
| + RepoName string | |
| + Branches []BranchInfo | |
| + }{ | |
| + Title: repoName + " - Branches", | |
| + IconPath: "../", | |
| + RepoName: repoName, | |
| + Branches: branches, | |
| + }) | |
| +} | |
| + | |
| +func processCommitHistory(repoName, repoPath, outputDir string) error { | |
| + repo, err := git.PlainOpen(repoPath) | |
| + if err != nil { | |
| + return fmt.Errorf("failed to open git repository: %w", err) | |
| + } | |
| + | |
| + commits, err := getCommitHistory(repo) | |
| + if err != nil { | |
| + return fmt.Errorf("failed to get commit history: %w", err) | |
| + } | |
| + | |
| + outputPath := filepath.Join(outputDir, "commits.html") | |
| + | |
| + f, err := os.Create(outputPath) | |
| + if err != nil { | |
| + return fmt.Errorf("failed to create commit history HTML file: … | |
| + } | |
| + defer f.Close() | |
| + | |
| + return commitHistoryTmpl.Execute(f, struct { | |
| + Title string | |
| + IconPath string | |
| + RepoName string | |
| + Commits []CommitInfo | |
| + }{ | |
| + Title: repoName + " - History", | |
| + IconPath: "../", | |
| + RepoName: repoName, | |
| + Commits: commits, | |
| + }) | |
| +} | |
| + | |
| +func processReadme(repoName, repoPath, outputDir string) error { | |
| + readme, err := getReadme(repoPath) | |
| + if err != nil { | |
| + return fmt.Errorf("failed to get README: %w", err) | |
| + } | |
| + | |
| + outputPath := filepath.Join(outputDir, "README.html") | |
| + | |
| + f, err := os.Create(outputPath) | |
| + if err != nil { | |
| + return fmt.Errorf("failed to create README HTML file: %w", err) | |
| + } | |
| + defer f.Close() | |
| + | |
| + return readmeTmpl.Execute(f, struct { | |
| + Title string | |
| + IconPath string | |
| + RepoName string | |
| + ReadmeContent string | |
| + }{ | |
| + Title: repoName + " - Readme!", | |
| + IconPath: "../", | |
| + RepoName: repoName, | |
| + ReadmeContent: readme, | |
| + }) | |
| +} | |
| + | |
| +func processRepositories() error { | |
| + repos, err := os.ReadDir(reposPath) | |
| + if err != nil { | |
| + return fmt.Errorf("failed to read repos directory: %w", err) | |
| + } | |
| + | |
| + var repoInfos []RepoInfo | |
| + for _, r := range repos { | |
| + if r.IsDir() && !ignoreDirs[r.Name()] { | |
| + repoPath := filepath.Join(reposPath, r.Name()) | |
| + repoInfo, err := getRepoInfo(repoPath) | |
| + if err != nil { | |
| + fmt.Printf("Failed to get info for repo %s: %v… | |
| + continue | |
| + } | |
| + repoInfos = append(repoInfos, repoInfo) | |
| + | |
| + outputDir := filepath.Join(outputRoot, r.Name()) | |
| + if err := os.MkdirAll(outputDir, 0755); err != nil { | |
| + fmt.Printf("Failed to create output directory … | |
| + continue | |
| + } | |
| + | |
| + if err := processReadme(r.Name(), repoPath, outputDir)… | |
| + fmt.Printf("Failed to process README for repo … | |
| + } | |
| + | |
| + if err := processCommitHistory(r.Name(), repoPath, out… | |
| + fmt.Printf("Failed to process commit history f… | |
| + } | |
| + | |
| + if err := processBranches(r.Name(), repoPath, outputDi… | |
| + fmt.Printf("Failed to process branches for rep… | |
| + } | |
| + } | |
| + } | |
| + | |
| + return generateIndexHTML(repoInfos) | |
| +} | |
| + | |
| func main() { | |
| - reposPath := flag.String("p", "", "Path to the git repositories (requi… | |
| - groupFlag := flag.Bool("g", false, "Group repositories based on descri… | |
| - ignoreDirs := flag.String("i", "", "Directories to ignore (comma-separ… | |
| - outputRoot := flag.String("o", ".", "Root path where output directorie… | |
| + flag.StringVar(&reposPath, "p", "", "Path to the git repositories (req… | |
| + flag.BoolVar(&groupFlag, "g", false, "Group repositories based on desc… | |
| + ignoreFlag := flag.String("i", "", "Directories to ignore (comma-separ… | |
| + flag.StringVar(&outputRoot, "o", ".", "Root path where output director… | |
| flag.Usage = func() { | |
| fmt.Fprintf(os.Stderr, "Usage: %s [options]\n\n", os.Args[0]) | |
| @@ -23,19 +470,14 @@ func main() { | |
| flag.Parse() | |
| - if *reposPath == "" { | |
| + if reposPath == "" { | |
| flag.Usage() | |
| os.Exit(1) | |
| } | |
| - cfg := &Config{ | |
| - ReposPath: *reposPath, | |
| - GroupFlag: *groupFlag, | |
| - IgnoreDirs: parseIgnoreDirs(*ignoreDirs), | |
| - OutputRoot: *outputRoot, | |
| - } | |
| + ignoreDirs = parseIgnoreDirs(*ignoreFlag) | |
| - if err := processRepositories(cfg); err != nil { | |
| + if err := processRepositories(); err != nil { | |
| log.Fatalf("Error processing repositories: %v", err) | |
| } | |
| } | |
| diff --git a/sealgit.go b/sealgit.go | |
| @@ -1,460 +0,0 @@ | |
| -package main | |
| - | |
| -import ( | |
| - "fmt" | |
| - "html/template" | |
| - "os" | |
| - "path/filepath" | |
| - "regexp" | |
| - "sort" | |
| - "strings" | |
| - | |
| - git "github.com/go-git/go-git/v5" | |
| - "github.com/go-git/go-git/v5/plumbing" | |
| - "github.com/go-git/go-git/v5/plumbing/object" | |
| -) | |
| - | |
| -type BranchInfo struct { | |
| - Name string | |
| - LastCommit string | |
| - LastCommitDate string | |
| -} | |
| - | |
| -type CommitInfo struct { | |
| - Hash string | |
| - Author string | |
| - Date string | |
| - Message string | |
| -} | |
| - | |
| -type Config struct { | |
| - ReposPath string | |
| - GroupFlag bool | |
| - IgnoreDirs map[string]bool | |
| - OutputRoot string | |
| -} | |
| - | |
| -type RepoInfo struct { | |
| - Name string | |
| - Description string | |
| - LastCommit string | |
| - Group string | |
| -} | |
| - | |
| -const ( | |
| - baseTemplate = ` | |
| -<!DOCTYPE html> | |
| -<html> | |
| -<head> | |
| -<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |
| -<meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| -<title>{{.Title}}</title> | |
| -<link rel="icon" type="image/png" href="{{.IconPath}}favicon.png" /> | |
| -<link rel="stylesheet" type="text/css" href="{{.IconPath}}style.css" /> | |
| -</head> | |
| -<body> | |
| -<table> | |
| -<tr><td><img src="{{.IconPath}}logo.png" alt="" width="32" height="32" /></td> | |
| -<td><span class="desc">{{.Title}}</span></td></tr><tr><td></td><td> | |
| -</td></tr> | |
| -</table> | |
| -<hr/> | |
| -<div id="content"> | |
| -{{template "content" .}} | |
| -</div> | |
| -</body> | |
| -</html> | |
| -` | |
| - | |
| - branchesContent = ` | |
| -{{define "content"}} | |
| -<h1>{{.RepoName}} - Branches</h1> | |
| -<table> | |
| -<thead> | |
| -<tr><td><b>Branch Name</b></td><td><b>Last Commit</b></td><td><b>Last Commit D… | |
| -</thead> | |
| -<tbody> | |
| -{{range .Branches}} | |
| -<tr><td>{{.Name}}</td><td>{{.LastCommit}}</td><td>{{.LastCommitDate}}</td></tr> | |
| -{{end}} | |
| -</tbody> | |
| -</table> | |
| -{{end}} | |
| -` | |
| - | |
| - commitHistoryContent = ` | |
| -{{define "content"}} | |
| -<h1>{{.RepoName}} - Commit History</h1> | |
| -<table> | |
| -<thead> | |
| -<tr><td><b>Hash</b></td><td><b>Author</b></td><td><b>Date</b></td><td><b>Messa… | |
| -</thead> | |
| -<tbody> | |
| -{{range .Commits}} | |
| -<tr><td>{{.Hash}}</td><td>{{.Author}}</td><td>{{.Date}}</td><td>{{.Message}}</… | |
| -{{end}} | |
| -</tbody> | |
| -</table> | |
| -{{end}} | |
| -` | |
| - | |
| - indexContent = ` | |
| -{{define "content"}} | |
| -<table id="index"> | |
| -<thead> | |
| -<tr><td><b>Name</b></td><td><b>Description</b></td><td><b>Last commit</b></td>… | |
| -</thead> | |
| -<tbody> | |
| -{{range $group, $repos := .Repos}} | |
| -<tr><td colspan="4"><b>{{if eq $group ""}} {{else}}<hr>{{end}}</b></td></tr> | |
| - {{range $repos}} | |
| - <tr> | |
| - <td>{{.Name}}</td> | |
| - <td>{{.Description}}</td> | |
| - <td>| {{.LastCommit}} | </td> | |
| - <td> | |
| - <a href="{{.Name}}/README.html">readme</a> | | |
| - <a href="{{.Name}}/commits.html">commits</a> | | |
| - <a href="{{.Name}}/branches.html">branches</a> | |
| - </td> | |
| - </tr> | |
| - {{end}} | |
| -{{end}} | |
| -</tbody> | |
| -</table> | |
| -{{end}} | |
| -` | |
| - readmeContent = ` | |
| -{{define "content"}} | |
| -<h1>{{.RepoName}}</h1> | |
| -<pre>{{.ReadmeContent}}</pre> | |
| -{{end}} | |
| -` | |
| -) | |
| - | |
| -var ( | |
| - branchesTmpl = template.Must(template.New("base").Parse(baseTempl… | |
| - commitHistoryTmpl = template.Must(template.New("base").Parse(baseTempl… | |
| - indexTmpl = template.Must(template.New("base").Parse(baseTempl… | |
| - readmeTmpl = template.Must(template.New("base").Parse(baseTempl… | |
| -) | |
| - | |
| -func generateIndexHTML(cfg *Config, repoInfos []RepoInfo) error { | |
| - groupedRepos := groupRepos(repoInfos, cfg.GroupFlag) | |
| - indexOutputPath := filepath.Join(cfg.OutputRoot, "index.html") | |
| - | |
| - indexFile, err := os.Create(indexOutputPath) | |
| - if err != nil { | |
| - return fmt.Errorf("failed to create index HTML file: %w", err) | |
| - } | |
| - defer indexFile.Close() | |
| - | |
| - return indexTmpl.Execute(indexFile, struct { | |
| - Title string | |
| - IconPath string | |
| - Repos map[string][]RepoInfo | |
| - }{ | |
| - Title: "git clone [email protected]:<reponame>", | |
| - IconPath: "./", | |
| - Repos: groupedRepos, | |
| - }) | |
| -} | |
| - | |
| -func getBranchInfo(repo *git.Repository) ([]BranchInfo, error) { | |
| - branches, err := repo.Branches() | |
| - if err != nil { | |
| - return nil, fmt.Errorf("failed to get branches: %w", err) | |
| - } | |
| - | |
| - var branchInfos []BranchInfo | |
| - err = branches.ForEach(func(branch *plumbing.Reference) error { | |
| - commit, err := repo.CommitObject(branch.Hash()) | |
| - if err != nil { | |
| - return fmt.Errorf("failed to get commit for branch %s:… | |
| - } | |
| - | |
| - branchInfos = append(branchInfos, BranchInfo{ | |
| - Name: branch.Name().Short(), | |
| - LastCommit: commit.Hash.String()[:7], | |
| - LastCommitDate: commit.Author.When.Format("02 Jan 2006… | |
| - }) | |
| - return nil | |
| - }) | |
| - | |
| - if err != nil { | |
| - return nil, fmt.Errorf("failed to iterate over branches: %w", … | |
| - } | |
| - | |
| - return branchInfos, nil | |
| -} | |
| - | |
| -func getCommitHistory(repo *git.Repository) ([]CommitInfo, error) { | |
| - ref, err := repo.Head() | |
| - if err != nil { | |
| - return nil, fmt.Errorf("failed to get HEAD reference: %w", err) | |
| - } | |
| - | |
| - commits, err := repo.Log(&git.LogOptions{From: ref.Hash()}) | |
| - if err != nil { | |
| - return nil, fmt.Errorf("failed to get commit log: %w", err) | |
| - } | |
| - | |
| - var commitHistory []CommitInfo | |
| - err = commits.ForEach(func(c *object.Commit) error { | |
| - commitHistory = append(commitHistory, CommitInfo{ | |
| - 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], // Get onl… | |
| - }) | |
| - return nil | |
| - }) | |
| - | |
| - if err != nil { | |
| - return nil, fmt.Errorf("failed to iterate over commits: %w", e… | |
| - } | |
| - | |
| - return commitHistory, nil | |
| -} | |
| - | |
| -func getDescriptionAndGroup(repoPath string, groupFlag bool) (string, string) { | |
| - descPath := filepath.Join(repoPath, "description") | |
| - description, _ := os.ReadFile(descPath) | |
| - desc := strings.TrimSpace(string(description)) | |
| - groupRegex := regexp.MustCompile(`\[(.*?)\]`) | |
| - | |
| - var group string | |
| - if groupFlag { | |
| - matches := groupRegex.FindStringSubmatch(desc) | |
| - if len(matches) > 1 { | |
| - group = matches[1] | |
| - } | |
| - } | |
| - | |
| - return desc, group | |
| -} | |
| - | |
| -func getReadme(repoPath string) (string, error) { | |
| - readmeFiles := []string{"README.md", "README.txt", "README"} | |
| - repo, err := git.PlainOpen(repoPath) | |
| - if err != nil { | |
| - return "", fmt.Errorf("failed to open git repository: %w", err) | |
| - } | |
| - | |
| - headRef, err := repo.Head() | |
| - if err != nil { | |
| - return "", fmt.Errorf("failed to get HEAD reference: %w", err) | |
| - } | |
| - | |
| - commit, err := repo.CommitObject(headRef.Hash()) | |
| - if err != nil { | |
| - return "", fmt.Errorf("failed to get commit object: %w", err) | |
| - } | |
| - | |
| - tree, err := commit.Tree() | |
| - if err != nil { | |
| - return "", fmt.Errorf("failed to get tree: %w", err) | |
| - } | |
| - | |
| - for _, fileName := range readmeFiles { | |
| - file, err := tree.File(fileName) | |
| - if err != nil { | |
| - continue | |
| - } | |
| - | |
| - content, err := file.Contents() | |
| - if err != nil { | |
| - return "", fmt.Errorf("failed to read file contents: %… | |
| - } | |
| - | |
| - return content, nil | |
| - } | |
| - | |
| - return "No README found!", nil | |
| -} | |
| - | |
| -func getRepoInfo(repoPath string, groupFlag bool) (RepoInfo, error) { | |
| - repo, err := git.PlainOpen(repoPath) | |
| - if err != nil { | |
| - return RepoInfo{}, fmt.Errorf("failed to open repository: %w",… | |
| - } | |
| - | |
| - headRef, err := repo.Head() | |
| - if err != nil { | |
| - return RepoInfo{}, fmt.Errorf("failed to get HEAD reference: %… | |
| - } | |
| - | |
| - commit, err := repo.CommitObject(headRef.Hash()) | |
| - if err != nil { | |
| - return RepoInfo{}, fmt.Errorf("failed to get commit object: %w… | |
| - } | |
| - | |
| - description, group := getDescriptionAndGroup(repoPath, groupFlag) | |
| - | |
| - return RepoInfo{ | |
| - Name: filepath.Base(repoPath), | |
| - Description: description, | |
| - LastCommit: commit.Committer.When.Format("02 Jan 2006"), | |
| - Group: group, | |
| - }, nil | |
| -} | |
| - | |
| -func groupRepos(repos []RepoInfo, groupFlag bool) map[string][]RepoInfo { | |
| - groupedRepos := make(map[string][]RepoInfo) | |
| - for _, repo := range repos { | |
| - group := "" | |
| - if groupFlag { | |
| - group = repo.Group | |
| - } | |
| - groupedRepos[group] = append(groupedRepos[group], repo) | |
| - } | |
| - | |
| - for _, repoList := range groupedRepos { | |
| - sort.Slice(repoList, func(i, j int) bool { | |
| - return strings.ToLower(repoList[i].Name) < strings.ToL… | |
| - }) | |
| - } | |
| - | |
| - return groupedRepos | |
| -} | |
| - | |
| -func parseIgnoreDirs(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 | |
| - } | |
| - } | |
| - return ignoreMap | |
| -} | |
| - | |
| -func processBranches(cfg *Config, repoName, repoPath, outputDir string) error { | |
| - repo, err := git.PlainOpen(repoPath) | |
| - if err != nil { | |
| - return fmt.Errorf("failed to open git repository: %w", err) | |
| - } | |
| - | |
| - branches, err := getBranchInfo(repo) | |
| - if err != nil { | |
| - return fmt.Errorf("failed to get branch information: %w", err) | |
| - } | |
| - | |
| - outputPath := filepath.Join(outputDir, "branches.html") | |
| - | |
| - f, err := os.Create(outputPath) | |
| - if err != nil { | |
| - return fmt.Errorf("failed to create branches HTML file: %w", e… | |
| - } | |
| - defer f.Close() | |
| - | |
| - return branchesTmpl.Execute(f, struct { | |
| - Title string | |
| - IconPath string | |
| - RepoName string | |
| - Branches []BranchInfo | |
| - }{ | |
| - Title: repoName + " - Branches", | |
| - IconPath: "../", | |
| - RepoName: repoName, | |
| - Branches: branches, | |
| - }) | |
| -} | |
| - | |
| -func processCommitHistory(cfg *Config, repoName, repoPath, outputDir string) e… | |
| - repo, err := git.PlainOpen(repoPath) | |
| - if err != nil { | |
| - return fmt.Errorf("failed to open git repository: %w", err) | |
| - } | |
| - | |
| - commits, err := getCommitHistory(repo) | |
| - if err != nil { | |
| - return fmt.Errorf("failed to get commit history: %w", err) | |
| - } | |
| - | |
| - outputPath := filepath.Join(outputDir, "commits.html") | |
| - | |
| - f, err := os.Create(outputPath) | |
| - if err != nil { | |
| - return fmt.Errorf("failed to create commit history HTML file: … | |
| - } | |
| - defer f.Close() | |
| - | |
| - return commitHistoryTmpl.Execute(f, struct { | |
| - Title string | |
| - IconPath string | |
| - RepoName string | |
| - Commits []CommitInfo | |
| - }{ | |
| - Title: repoName + " - History", | |
| - IconPath: "../", | |
| - RepoName: repoName, | |
| - Commits: commits, | |
| - }) | |
| -} | |
| - | |
| -func processReadme(cfg *Config, repoName, repoPath, outputDir string) error { | |
| - readme, err := getReadme(repoPath) | |
| - if err != nil { | |
| - return fmt.Errorf("failed to get README: %w", err) | |
| - } | |
| - | |
| - outputPath := filepath.Join(outputDir, "README.html") | |
| - | |
| - f, err := os.Create(outputPath) | |
| - if err != nil { | |
| - return fmt.Errorf("failed to create README HTML file: %w", err) | |
| - } | |
| - defer f.Close() | |
| - | |
| - return readmeTmpl.Execute(f, struct { | |
| - Title string | |
| - IconPath string | |
| - RepoName string | |
| - ReadmeContent string | |
| - }{ | |
| - Title: repoName + " - Readme!", | |
| - IconPath: "../", | |
| - RepoName: repoName, | |
| - ReadmeContent: readme, | |
| - }) | |
| -} | |
| - | |
| -func processRepositories(cfg *Config) error { | |
| - repos, err := os.ReadDir(cfg.ReposPath) | |
| - if err != nil { | |
| - return fmt.Errorf("failed to read repos directory: %w", err) | |
| - } | |
| - | |
| - var repoInfos []RepoInfo | |
| - for _, r := range repos { | |
| - if r.IsDir() && !cfg.IgnoreDirs[r.Name()] { | |
| - repoPath := filepath.Join(cfg.ReposPath, r.Name()) | |
| - repoInfo, err := getRepoInfo(repoPath, cfg.GroupFlag) | |
| - if err != nil { | |
| - fmt.Printf("Failed to get info for repo %s: %v… | |
| - continue | |
| - } | |
| - repoInfos = append(repoInfos, repoInfo) | |
| - | |
| - outputDir := filepath.Join(cfg.OutputRoot, r.Name()) | |
| - if err := os.MkdirAll(outputDir, 0755); err != nil { | |
| - fmt.Printf("Failed to create output directory … | |
| - continue | |
| - } | |
| - | |
| - if err := processReadme(cfg, r.Name(), repoPath, outpu… | |
| - fmt.Printf("Failed to process README for repo … | |
| - } | |
| - | |
| - if err := processCommitHistory(cfg, r.Name(), repoPath… | |
| - fmt.Printf("Failed to process commit history f… | |
| - } | |
| - | |
| - if err := processBranches(cfg, r.Name(), repoPath, out… | |
| - fmt.Printf("Failed to process branches for rep… | |
| - } | |
| - } | |
| - } | |
| - | |
| - return generateIndexHTML(cfg, repoInfos) | |
| -} |