Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
output/
gitmal
220 changes: 144 additions & 76 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

"github.com/antonmedv/gitmal/pkg/git"
"github.com/antonmedv/gitmal/pkg/templates"

flag "github.com/spf13/pflag"
)
Expand All @@ -23,6 +24,7 @@ var (
flagPreviewThemes bool
flagMinify bool
flagGzip bool
flagMultipleRepo bool
)

type Params struct {
Expand All @@ -36,127 +38,77 @@ type Params struct {
DefaultRef git.Ref
}

func main() {
if _, ok := os.LookupEnv("GITMAL_PPROF"); ok {
f, err := os.Create("cpu.prof")
if err != nil {
panic(err)
}
err = pprof.StartCPUProfile(f)
if err != nil {
panic(err)
}
defer f.Close()
defer pprof.StopCPUProfile()
memProf, err := os.Create("mem.prof")
if err != nil {
panic(err)
}
defer memProf.Close()
defer pprof.WriteHeapProfile(memProf)
}

_, noFiles := os.LookupEnv("NO_FILES")
_, noCommitsList := os.LookupEnv("NO_COMMITS_LIST")

flag.StringVar(&flagOwner, "owner", "", "Project owner")
flag.StringVar(&flagName, "name", "", "Project name")
flag.StringVar(&flagOutput, "output", "output", "Output directory for generated HTML files")
flag.StringVar(&flagBranches, "branches", "", "Regex for branches to include")
flag.StringVar(&flagDefaultBranch, "default-branch", "", "Default branch to use (autodetect master or main)")
flag.StringVar(&flagTheme, "theme", "github", "Style theme")
flag.BoolVar(&flagPreviewThemes, "preview-themes", false, "Preview available themes")
flag.BoolVar(&flagMinify, "minify", false, "Minify all generated HTML files")
flag.BoolVar(&flagGzip, "gzip", false, "Compress all generated HTML files")
flag.Usage = usage
flag.Parse()

input := "."
args := flag.Args()
if len(args) == 1 {
input = args[0]
}
if len(args) > 1 {
panic("Multiple repos not supported yet")
}

if flagPreviewThemes {
previewThemes()
os.Exit(0)
}

outputDir, err := filepath.Abs(flagOutput)
func processRepo(input string, outputRoot string, noFiles bool, noCommitsList bool) (templates.RepoSummary, error) {
outputDir, err := filepath.Abs(outputRoot)
if err != nil {
panic(err)
return templates.RepoSummary{}, err
}

absInput, err := filepath.Abs(input)
if err != nil {
panic(err)
return templates.RepoSummary{}, err
}
input = absInput

if flagName == "" {
flagName = filepath.Base(input)
flagName = strings.TrimSuffix(flagName, ".git")
repoName := flagName
if repoName == "" {
repoName = strings.TrimSuffix(filepath.Base(input), ".git")
}

themeColor, ok := themeStyles[flagTheme]
if !ok {
panic("Invalid theme: " + flagTheme)
return templates.RepoSummary{}, fmt.Errorf("invalid theme %q", flagTheme)
}

branchesFilter, err := regexp.Compile(flagBranches)
if err != nil {
panic(err)
return templates.RepoSummary{}, err
}

branches, err := git.Branches(input, branchesFilter, flagDefaultBranch)
if err != nil {
panic(err)
return templates.RepoSummary{}, err
}

tags, err := git.Tags(input)
if err != nil {
panic(err)
return templates.RepoSummary{}, err
}

if flagDefaultBranch == "" {
defaultBranch := flagDefaultBranch
if defaultBranch == "" {
if containsBranch(branches, "master") {
flagDefaultBranch = "master"
defaultBranch = "master"
} else if containsBranch(branches, "main") {
flagDefaultBranch = "main"
defaultBranch = "main"
} else {
echo("No default branch found. Specify one using --default-branch flag.")
os.Exit(1)
return templates.RepoSummary{}, fmt.Errorf("No default branch found. Specify one using --default-branch flag.")
}
}

if !containsBranch(branches, flagDefaultBranch) {
echo(fmt.Sprintf("Default branch %q not found.", flagDefaultBranch))
echo("Specify a valid branch using --default-branch flag.")
os.Exit(1)
if !containsBranch(branches, defaultBranch) {
return templates.RepoSummary{}, fmt.Errorf("Default branch %q not found", defaultBranch)
}

if yes, a, b := hasConflictingBranchNames(branches); yes {
echo(fmt.Sprintf("Conflicting branchs %q and %q, both want to use %q dir name.", a, b, a.DirName()))
os.Exit(1)
return templates.RepoSummary{}, fmt.Errorf("Conflicting branchs %q and %q, both want to use %q dir name.", a, b, a.DirName())
}

// Start generating pages

params := Params{
Owner: flagOwner,
Name: flagName,
Name: repoName,
RepoDir: input,
OutputDir: outputDir,
OutputDir: filepath.Join(outputDir, repoName),
Style: flagTheme,
Dark: themeColor == "dark",
DefaultRef: git.NewRef(flagDefaultBranch),
DefaultRef: git.NewRef(defaultBranch),
}

commits := make(map[string]git.Commit)
commitsFor := make(map[git.Ref][]git.Commit, len(branches))
commitsDetailsFor := make(map[git.Ref]templates.CommitDetails, len(branches))

for _, branch := range branches {
commitsFor[branch], err = git.Commits(branch, params.RepoDir)
Expand All @@ -171,6 +123,17 @@ func main() {
commit.Branch = branch
commits[commit.Hash] = commit
}


branchCommits := commitsFor[branch]
details := templates.CommitDetails{TotalCommits: len(branchCommits)}
if len(branchCommits) > 0 {
last := branchCommits[0]
details.LastCommit = last
details.LastCommitDate = timeAgo(last.Date)
}
commitsDetailsFor[branch] = details

}

// Add commits from tags
Expand All @@ -190,7 +153,7 @@ func main() {

echo(fmt.Sprintf("> %s: %d branches, %d tags, %d commits", params.Name, len(branches), len(tags), len(commits)))

if err := generateBranches(branches, flagDefaultBranch, params); err != nil {
if err := generateBranches(branches, defaultBranch, params); err != nil {
panic(err)
}

Expand All @@ -206,7 +169,7 @@ func main() {
panic(err)
}

if branch.String() == flagDefaultBranch {
if branch.String() == defaultBranch {
defaultBranchFiles = files
}

Expand All @@ -230,7 +193,7 @@ func main() {
}

// Back to the default branch
params.Ref = git.NewRef(flagDefaultBranch)
params.Ref = git.NewRef(defaultBranch)

// Commits pages generation
echo("> generating commits...")
Expand Down Expand Up @@ -261,6 +224,111 @@ func main() {
panic(err)
}
}

defaultRef := git.NewRef(defaultBranch)
return buildRepoSummary(params, defaultBranch, len(branches), len(tags), commitsDetailsFor[defaultRef]), nil
}

func main() {
if _, ok := os.LookupEnv("GITMAL_PPROF"); ok {
f, err := os.Create("cpu.prof")
if err != nil {
panic(err)
}
err = pprof.StartCPUProfile(f)
if err != nil {
panic(err)
}
defer f.Close()
defer pprof.StopCPUProfile()
memProf, err := os.Create("mem.prof")
if err != nil {
panic(err)
}
defer memProf.Close()
defer pprof.WriteHeapProfile(memProf)
}

_, noFiles := os.LookupEnv("NO_FILES")
_, noCommitsList := os.LookupEnv("NO_COMMITS_LIST")

flag.StringVar(&flagOwner, "owner", "", "Project owner")
flag.StringVar(&flagName, "name", "", "Project name")
flag.StringVar(&flagOutput, "output", "output", "Output directory for generated HTML files")
flag.StringVar(&flagBranches, "branches", "", "Regex for branches to include")
flag.StringVar(&flagDefaultBranch, "default-branch", "", "Default branch to use (autodetect master or main)")
flag.StringVar(&flagTheme, "theme", "github", "Style theme")
flag.BoolVar(&flagPreviewThemes, "preview-themes", false, "Preview available themes")
flag.BoolVar(&flagMinify, "minify", false, "Minify all generated HTML files")
flag.BoolVar(&flagGzip, "gzip", false, "Compress all generated HTML files")
flag.Usage = usage
flag.Parse()

args := flag.Args()

if len(args) == 0 {
args = []string{"."}
}

flagMultipleRepo = len(args) > 1

if flagPreviewThemes {
previewThemes()
os.Exit(0)
}

skippedRepos := []string{}
processedRepos := []string{}
repoSummaries := []templates.RepoSummary{}

themeColor, themeOK := themeStyles[flagTheme]
if !themeOK {
echo(fmt.Sprintf("invalid theme %q", flagTheme))
os.Exit(1)
}
themeDark := themeColor == "dark"

for _, repo := range args {
echo("Processing " + repo)

summary, err := processRepo(repo, flagOutput, noFiles, noCommitsList)
if err != nil {
echo(fmt.Sprintf("Error processing %s: %v", repo, err))
echo("Skipping " + repo)
echo("\n ===============================\n")
skippedRepos = append(skippedRepos, repo)
continue
}

echo("Done processing " + repo)
echo("\n ===============================\n")
processedRepos = append(processedRepos, repo)
repoSummaries = append(repoSummaries, summary)
}

if len(repoSummaries) >= 2 {
echo("> generating portal index...")
if err := generatePortalIndex(repoSummaries, flagOutput, flagOwner, themeDark); err != nil {
echo(fmt.Sprintf("Error generating portal index: %v", err))
os.Exit(1)
}
if flagMinify || flagGzip {
portalIndex := filepath.Join(flagOutput, "index.html")
echo("> post-processing portal index...")
if err := postProcessHTMLFile(portalIndex, flagMinify, flagGzip); err != nil {
echo(fmt.Sprintf("Error post-processing portal index: %v", err))
os.Exit(1)
}
}
}

echo(fmt.Sprintf("Processed %d repos, skipped %d repos", len(processedRepos), len(skippedRepos)))
if len(skippedRepos) > 0 {
echo("Skipped repos:")
for _, repo := range skippedRepos {
echo(" - " + repo)
}
}
}

func usage() {
Expand Down
3 changes: 3 additions & 0 deletions pkg/templates/layout.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@
</head>
<body>
{{ template "svg" . }}

{{ if .CurrentRefDir }}
<div class="menu">
<div class="menu-content">
<a href="{{ .RootHref }}index.html" class="project-name">{{ .Name }}</a>
Expand Down Expand Up @@ -307,6 +309,7 @@
</div>
</div>
</div>
{{ end }}
<main>
<div class="main-content">
{{ template "body" . }}
Expand Down
Loading