diff --git a/logcategory/log-category.go b/logcategory/log-category.go index 73c7f19..6608f16 100644 --- a/logcategory/log-category.go +++ b/logcategory/log-category.go @@ -1,67 +1,94 @@ +// SPDX-License-Identifier: MIT + package logcategory +import ( + "io" + "strings" +) + // LogsByCategory - Type to hold logs by each's category -type LogsByCategory struct { - CI []string - FIX []string - REFACTOR []string - FEATURE []string - DOCS []string - OTHER []string -} +type LogsByCategory map[string][]string -// GenerateMarkdown - Generate markdown output for the collected commits -func (logContainer *LogsByCategory) GenerateMarkdown() string { - markDownString := "" +func NewLogsByCategory() LogsByCategory { + return make(LogsByCategory, len(Categories)) +} +func (logs LogsByCategory) Add(category, message string) { + k := strings.ToLower(category) + logs[k] = append(logs[k], message) +} - markDownString += "# Changelog \n" +// Categories is the Poor Man's Ordered Map of category names and titles. +var Categories = [][2]string{ + {"ci", "CI Changes"}, + {"fix", "Fixes"}, + {"refactor", "Performance Fixes"}, + {"feature", "Feature fixes"}, + {"docs", "Doc Updates"}, + {"", "Other changes"}, +} - if len(logContainer.CI) > 0 { - markDownString += "\n\n## CI Changes \n" +// WriteMarkdown - Generate markdown output for the collected commits +func WriteMarkdown(w io.Writer, logs LogsByCategory) error { + ew := &errWriter{w: w} + ew.WriteString("# Changelog \n") - for _, item := range logContainer.CI { - markDownString += item + "\n" + seen := make(map[string]struct{}, len(Categories)) + var token struct{} + var otherPrinted bool + for _, kv := range Categories { + vv := logs[kv[0]] + if len(vv) != 0 { + seen[kv[0]] = token + ew.WriteString("\n\n## " + kv[1] + "\n") + for _, item := range vv { + ew.WriteString(item + "\n") + } + if kv[0] == "" { + otherPrinted = true + } } } - if len(logContainer.FIX) > 0 { - markDownString += "\n\n## Fixes \n" - for _, item := range logContainer.FIX { - markDownString += item + "\n" + for k, vv := range logs { + if _, ok := seen[k]; ok { + continue } - } - - if len(logContainer.REFACTOR) > 0 { - markDownString += "\n\n## Performance Fixes \n" - - for _, item := range logContainer.REFACTOR { - markDownString += item + "\n" + if !otherPrinted { + for _, kv := range Categories { + if kv[0] == "" { + ew.WriteString("\n\n## " + kv[1] + "\n") + otherPrinted = true + break + } + } } - } - - if len(logContainer.FEATURE) > 0 { - - markDownString += "\n\n## Feature Fixes \n" - for _, item := range logContainer.FEATURE { - markDownString += item + "\n" + for _, item := range vv { + ew.WriteString(item + "\n") } } - if len(logContainer.DOCS) > 0 { - - markDownString += "\n\n## Doc Updates \n" - for _, item := range logContainer.DOCS { - markDownString += item + "\n" - } - } + return ew.err +} - if len(logContainer.OTHER) > 0 { +type errWriter struct { + err error + w io.Writer +} - markDownString += "\n\n## Other Changes \n" - for _, item := range logContainer.OTHER { - markDownString += item + "\n" - } +func (ew *errWriter) Write(p []byte) (int, error) { + if ew.err != nil { + return 0, ew.err } - - return markDownString + n, err := ew.w.Write(p) + ew.err = err + return n, err +} +func (ew *errWriter) WriteString(s string) (int, error) { + if ew.err != nil { + return 0, ew.err + } + n, err := io.WriteString(ew.w, s) + ew.err = err + return n, err } diff --git a/main.go b/main.go index 114703c..bfbe221 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + package main import ( @@ -7,7 +9,7 @@ import ( "strings" "github.com/barelyhuman/commitlog/logcategory" - "github.com/barelyhuman/commitlog/utils" + "github.com/barelyhuman/commitlog/repo" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" ) @@ -24,109 +26,76 @@ func normalizeCommit(commitMessage string) string { } func main() { + if err := Main(); err != nil { + log.Fatalf("%+v", err) + } +} + +func Main() error { path := os.Args[1] r, err := git.PlainOpen(path) if err != nil { - log.Fatal("Error opening Repository: ", err) + return fmt.Errorf("opening repository: %w", err) } ref, err := r.Head() - if err != nil { - log.Fatal("Unable to get repository HEAD:", err) + return fmt.Errorf("get repository HEAD: %w", err) } cIter, err := r.Log(&git.LogOptions{From: ref.Hash()}) - if err != nil { - log.Fatal("Unable to get repository commits:", err) + return fmt.Errorf("get repository commits: %w", err) } var commits []*object.Commit - - err = cIter.ForEach(func(c *object.Commit) error { + if err = cIter.ForEach(func(c *object.Commit) error { commits = append(commits, c) return nil - }) - - if err != nil { - log.Fatal("Error getting commits : ", err) + }); err != nil { + return fmt.Errorf("getting commits: %w", err) } - logContainer := new(logcategory.LogsByCategory) - latestTag, _, err := utils.GetLatestTagFromRepository(r) - + logContainer := logcategory.NewLogsByCategory() + latestTag, _, err := repo.GetLatestTagFromRepository(r) if err != nil { - log.Fatal("Error Getting Tag Pairs", err) + return fmt.Errorf("getting tag pairs: %w", err) } - tillLatest := false - - if latestTag != nil { - if latestTag.Hash().String() == ref.Hash().String() { - tillLatest = false - } else { - tillLatest = true - } - } + tillLatest := latestTag != nil && latestTag.Hash().String() != ref.Hash().String() for _, c := range commits { - switch { - case strings.Contains(c.Message, "ci:"): - { - logContainer.CI = append(logContainer.CI, c.Hash.String()+" - "+normalizeCommit(c.Message)) - } - case strings.Contains(c.Message, "fix:"): - { - logContainer.FIX = append(logContainer.FIX, c.Hash.String()+" - "+normalizeCommit(c.Message)) - } - case strings.Contains(c.Message, "refactor:"): - { - logContainer.REFACTOR = append(logContainer.REFACTOR, c.Hash.String()+" - "+normalizeCommit(c.Message)) - } - case strings.Contains(c.Message, "feat:"): - { - logContainer.FEATURE = append(logContainer.FEATURE, c.Hash.String()+" - "+normalizeCommit(c.Message)) - } - case strings.Contains(c.Message, "feature:"): - { - logContainer.FEATURE = append(logContainer.FEATURE, c.Hash.String()+" - "+normalizeCommit(c.Message)) - } - case strings.Contains(c.Message, "docs:"): - { - logContainer.DOCS = append(logContainer.DOCS, c.Hash.String()+" - "+normalizeCommit(c.Message)) - } + s := c.Hash.String() + " - " + normalizeCommit(c.Message) + switch k := strings.SplitN(strings.TrimSpace(c.Message), ":", 2)[0]; k { + case "ci", "fix", "refactor", "feature", "docs": + logContainer.Add(k, s) + case "feat": + logContainer.Add("feature", s) default: - { - logContainer.OTHER = append(logContainer.OTHER, c.Hash.String()+" - "+normalizeCommit(c.Message)) - } + logContainer.Add("", s) } - if isCommitToNearestTag(r, c, tillLatest) { + if nearest, err := isCommitToNearestTag(r, c, tillLatest); err != nil { + return err + } else if nearest { break } } - fmt.Println(logContainer.GenerateMarkdown()) - + return logcategory.WriteMarkdown(os.Stdout, logContainer) } -func isCommitToNearestTag(repo *git.Repository, commit *object.Commit, tillLatest bool) bool { - latestTag, previousTag, err := utils.GetLatestTagFromRepository(repo) - +func isCommitToNearestTag(repository *git.Repository, commit *object.Commit, tillLatest bool) (bool, error) { + latestTag, previousTag, err := repo.GetLatestTagFromRepository(repository) if err != nil { - log.Fatal("Couldn't get latest tag...", err) - } - if err != nil { - log.Fatal("Couldn't access tag...", err) + return false, fmt.Errorf("get latest tag: %w", err) } - if latestTag != nil { - if tillLatest { - return latestTag.Hash().String() == commit.Hash.String() - } - return previousTag.Hash().String() == commit.Hash.String() - + if latestTag == nil { + return false, nil + } + if tillLatest { + return latestTag.Hash().String() == commit.Hash.String(), nil } - return false + return previousTag.Hash().String() == commit.Hash.String(), nil } diff --git a/utils/latest-tag.go b/repo/latest-tag.go similarity index 96% rename from utils/latest-tag.go rename to repo/latest-tag.go index ad3e1f3..32b277e 100644 --- a/utils/latest-tag.go +++ b/repo/latest-tag.go @@ -1,4 +1,6 @@ -package utils +// SPDX-License-Identifier: MIT + +package repo import ( "github.com/go-git/go-git/v5"