changeloguru/internal/cli/action_generate.go

138 lines
3.3 KiB
Go

package cli
import (
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/haunt98/changeloguru/internal/changelog"
"github.com/haunt98/changeloguru/internal/convention"
"github.com/haunt98/changeloguru/internal/git"
"github.com/haunt98/changeloguru/internal/markdown"
"github.com/pkg/diff"
"github.com/pkg/diff/write"
"github.com/urfave/cli/v2"
"golang.org/x/mod/semver"
)
func (a *action) RunGenerate(c *cli.Context) error {
// Show help if there is nothing
if c.NumFlags() == 0 {
return cli.ShowAppHelp(c)
}
a.getFlags(c)
commits, err := a.getCommits()
if err != nil {
return err
}
conventionalCommits := a.getConventionalCommits(commits)
if err := a.generateChangelog(conventionalCommits); err != nil {
return err
}
return nil
}
func (a *action) getCommits() ([]git.Commit, error) {
r, err := git.NewRepository(a.flags.repository)
if err != nil {
return nil, err
}
return r.Log(a.flags.from, a.flags.to)
}
func (a *action) getConventionalCommits(commits []git.Commit) []convention.Commit {
conventionalCommits := make([]convention.Commit, 0, len(commits))
for _, commit := range commits {
conventionalCommit, err := convention.NewCommit(commit)
if err != nil {
a.log("failed to new conventional commits %+v: %s", commit, err)
continue
}
conventionalCommits = append(conventionalCommits, conventionalCommit)
}
return conventionalCommits
}
func (a *action) generateChangelog(commits []convention.Commit) error {
realOutput := a.getRealOutput()
version, err := a.getVersion()
if err != nil {
return err
}
switch a.flags.filetype {
case markdownFiletype:
return a.generateMarkdownChangelog(realOutput, version, commits)
default:
return fmt.Errorf("unknown filetype %s", a.flags.filetype)
}
}
func (a *action) getRealOutput() string {
nameWithExt := a.flags.filename + "." + a.flags.filetype
realOutput := filepath.Join(a.flags.output, nameWithExt)
return realOutput
}
func (a *action) getVersion() (string, error) {
if a.flags.version == "" {
return "", fmt.Errorf("empty version")
}
if !strings.HasPrefix(a.flags.version, "v") {
a.flags.version = "v" + a.flags.version
}
if !semver.IsValid(a.flags.version) {
return "", fmt.Errorf("invalid semver %s", a.flags.version)
}
a.log("version %s", a.flags.version)
return a.flags.version, nil
}
func (a *action) generateMarkdownChangelog(output, version string, commits []convention.Commit) error {
// If changelog file already exist, parse markdown from exist file
var oldNodes []markdown.Node
bytes, err := os.ReadFile(output)
if err == nil {
oldNodes = changelog.ParseMarkdown(string(bytes))
}
// Generate markdown from commits
newNodes := changelog.GenerateMarkdown(commits, a.flags.scopes, version, time.Now())
// Final changelog with new commits above old commits
nodes := append(newNodes, oldNodes...)
changelogText := markdown.GenerateText(nodes)
// Demo run
if a.flags.dryRun {
if err := diff.Text("old", "new", string(bytes), changelogText, os.Stdout, write.TerminalColor()); err != nil {
return fmt.Errorf("failed to diff old and new changelog: %w", err)
}
return nil
}
// Actually writing to changelog file
if err := os.WriteFile(output, []byte(changelogText), 0644); err != nil {
return fmt.Errorf("failed to write file %s: %w", output, err)
}
return nil
}