{{ template "body" . }} diff --git a/pkg/templates/repos.gohtml b/pkg/templates/repos.gohtml new file mode 100644 index 0000000..4a83e94 --- /dev/null +++ b/pkg/templates/repos.gohtml @@ -0,0 +1,141 @@ +{{- /*gotype: github.com/antonmedv/gitmal/pkg/templates.ReposParams*/ -}} +{{ define "head" }} + +{{ end }} + +{{ define "body" }} +

{{ .Heading }}

+

{{ .Total }} {{ if eq .Total 1 }}repository{{ else }}repositories{{ end }}

+ +{{ end }} diff --git a/pkg/templates/templates.go b/pkg/templates/templates.go index 91d4884..4049175 100644 --- a/pkg/templates/templates.go +++ b/pkg/templates/templates.go @@ -61,6 +61,10 @@ var CommitsListTemplate = Must(Must(layout.Clone()).Parse(commitsListContent)) var commitContent string var CommitTemplate = Must(Must(layout.Clone()).Parse(commitContent)) +//go:embed repos.gohtml +var reposContent string +var ReposTemplate = Must(Must(layout.Clone()).Parse(reposContent)) + //go:embed preview.gohtml var previewContent string var PreviewTemplate = Must(New("preview").Parse(previewContent)) @@ -164,6 +168,12 @@ type CommitParams struct { FileViews []FileView } +type CommitDetails struct { + TotalCommits int + LastCommitDate string + LastCommit git.Commit +} + type FileTreeParams struct { Nodes []*FileTree } @@ -201,6 +211,26 @@ type FileView struct { HTML HTML // pre-rendered HTML for diff of this file } +type RepoSummary struct { + Name string + Owner string + DisplayName string + Href string + DefaultBranch string + TotalCommits int + LastCommitDate string + LastCommitSubject string + BranchCount int + TagCount int +} + +type ReposParams struct { + LayoutParams + Heading string + Repos []RepoSummary + Total int +} + type PreviewCard struct { Name string Tone string diff --git a/post_process.go b/post_process.go index 392fa74..3e28186 100644 --- a/post_process.go +++ b/post_process.go @@ -19,24 +19,35 @@ import ( "github.com/antonmedv/gitmal/pkg/progress_bar" ) +func postProcessHTMLFile(path string, doMinify bool, doGzip bool) error { + return postProcessHTMLFiles([]string{path}, doMinify, doGzip) +} + func postProcessHTML(root string, doMinify bool, doGzip bool) error { // 1) Collect all HTML files first var files []string - if err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - if d.IsDir() { + if root != "" { + if err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return nil + } + if strings.HasSuffix(d.Name(), ".html") { + files = append(files, path) + } return nil + }); err != nil { + return err } - if strings.HasSuffix(d.Name(), ".html") { - files = append(files, path) - } - return nil - }); err != nil { - return err } + return postProcessHTMLFiles(files, doMinify, doGzip) +} + +func postProcessHTMLFiles(files []string, doMinify bool, doGzip bool) error { + if len(files) == 0 { return nil } diff --git a/repos.go b/repos.go new file mode 100644 index 0000000..19e90df --- /dev/null +++ b/repos.go @@ -0,0 +1,83 @@ +package main + +import ( + "os" + "path/filepath" + "sort" + "strings" + + "github.com/antonmedv/gitmal/pkg/templates" +) + +func generatePortalIndex(repos []templates.RepoSummary, outputRoot string, owner string, dark bool) error { + outputDir, err := filepath.Abs(outputRoot) + if err != nil { + return err + } + + sorted := make([]templates.RepoSummary, len(repos)) + copy(sorted, repos) + sort.Slice(sorted, func(i, j int) bool { + return sorted[i].DisplayName < sorted[j].DisplayName + }) + + heading := "Repositories" + title := "Repositories" + headerName := "Repositories" + if owner != "" { + heading = owner + title = owner + " / Repositories" + headerName = owner + } + + f, err := os.Create(filepath.Join(outputDir, "index.html")) + if err != nil { + return err + } + + err = templates.ReposTemplate.ExecuteTemplate(f, "layout.gohtml", templates.ReposParams{ + LayoutParams: templates.LayoutParams{ + Title: title, + Name: headerName, + Dark: dark, + RootHref: "./", + }, + Heading: heading, + Repos: sorted, + Total: len(sorted), + }) + if err != nil { + _ = f.Close() + return err + } + if err := f.Close(); err != nil { + return err + } + + return nil +} + +func repoDisplayName(owner, name string) string { + if owner != "" { + return owner + "/" + name + } + return name +} + +func buildRepoSummary(params Params, defaultBranch string, branches int, tags int, details templates.CommitDetails) templates.RepoSummary { + summary := templates.RepoSummary{ + Name: params.Name, + Owner: params.Owner, + DisplayName: repoDisplayName(params.Owner, params.Name), + Href: filepath.ToSlash(filepath.Join(params.Name, "index.html")), + DefaultBranch: defaultBranch, + BranchCount: branches, + TagCount: tags, + } + if details.TotalCommits > 0 { + summary.TotalCommits = details.TotalCommits + summary.LastCommitDate = details.LastCommitDate + summary.LastCommitSubject = strings.TrimSpace(details.LastCommit.Subject) + } + return summary +} diff --git a/utils.go b/utils.go index 2deacc6..3385e13 100644 --- a/utils.go +++ b/utils.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/antonmedv/gitmal/pkg/git" "github.com/antonmedv/gitmal/pkg/templates" @@ -124,3 +125,52 @@ func hasConflictingBranchNames(branches []git.Ref) (bool, git.Ref, git.Ref) { } return false, git.Ref{}, git.Ref{} } + + +func timeAgo(t time.Time) string { + if t.IsZero() { + return "" + } + d := time.Since(t) + + switch { + case d < time.Minute: + return "just now" + case d < time.Hour: + n := int(d.Minutes()) + if n == 1 { + return "1 min ago" + } + return fmt.Sprintf("%d mins ago", n) + case d < 24*time.Hour: + n := int(d.Hours()) + if n == 1 { + return "1 hr ago" + } + return fmt.Sprintf("%d hrs ago", n) + case d < 30*24*time.Hour: + n := int(d.Hours() / 24) + if n == 1 { + return "1 day ago" + } + return fmt.Sprintf("%d days ago", n) + case d < 365*24*time.Hour: + n := int(d.Hours() / (24 * 30)) + if n < 1 { + n = 1 + } + if n == 1 { + return "1 month ago" + } + return fmt.Sprintf("%d months ago", n) + default: + n := int(d.Hours() / (24 * 365)) + if n < 1 { + n = 1 + } + if n == 1 { + return "1 year ago" + } + return fmt.Sprintf("%d years ago", n) + } +}