0
0
mirror of https://github.com/thegeeklab/git-sv.git synced 2024-11-21 12:00:40 +00:00

feat: add option to write changelogs to file (#6)

This commit is contained in:
Robert Kaussow 2023-10-16 00:29:02 +02:00 committed by GitHub
parent a222bd3430
commit 14ee7e71c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 172 additions and 86 deletions

View File

@ -57,7 +57,8 @@ func NewLogRange(t LogRangeType, start, end string) LogRange {
// Impl git command implementation.
type GitSV struct {
Config *Config
Settings *Settings
Config *Config
MessageProcessor sv.MessageProcessor
CommitProcessor sv.CommitProcessor
@ -68,7 +69,8 @@ type GitSV struct {
// New constructor.
func New() GitSV {
g := GitSV{
Config: NewConfig(configDir, configFilename),
Settings: &Settings{},
Config: NewConfig(configDir, configFilename),
}
g.MessageProcessor = sv.NewMessageProcessor(g.Config.CommitMessage, g.Config.Branches)

View File

@ -2,6 +2,7 @@ package commands
import (
"fmt"
"os"
"sort"
"github.com/thegeeklab/git-sv/v2/app"
@ -9,7 +10,7 @@ import (
"github.com/urfave/cli/v2"
)
func ChangelogFlags() []cli.Flag {
func ChangelogFlags(settings *app.ChangelogSettings) []cli.Flag {
return []cli.Flag{
&cli.IntFlag{
Name: "size",
@ -18,23 +19,31 @@ func ChangelogFlags() []cli.Flag {
Usage: "get changelog from last 'n' tags",
},
&cli.BoolFlag{
Name: "all",
Usage: "ignore size parameter, get changelog for every tag",
Name: "all",
Usage: "ignore size parameter, get changelog for every tag",
Destination: &settings.All,
},
&cli.BoolFlag{
Name: "add-next-version",
Usage: "add next version on change log (commits since last tag, but only if there is a new version to release)",
Name: "add-next",
Usage: "add next version on change log (commits since last tag, only if there is a new release)",
Destination: &settings.AddNext,
},
&cli.BoolFlag{
Name: "semantic-version-only",
Usage: "only show tags 'SemVer-ish'",
Name: "strict",
Usage: "only show tags 'SemVer-ish'",
Destination: &settings.Strict,
},
&cli.StringFlag{
Name: "o",
Aliases: []string{"output"},
Usage: "output file name. Omit to use standard output.",
Destination: &settings.Out,
},
}
}
func ChangelogHandler(
g app.GitSV,
) cli.ActionFunc {
//nolint:gocognit
func ChangelogHandler(g app.GitSV) cli.ActionFunc {
return func(c *cli.Context) error {
tags, err := g.Tags()
if err != nil {
@ -47,12 +56,7 @@ func ChangelogHandler(
var releaseNotes []sv.ReleaseNote
size := c.Int("size")
all := c.Bool("all")
addNextVersion := c.Bool("add-next-version")
semanticVersionOnly := c.Bool("semantic-version-only")
if addNextVersion {
if g.Settings.ChangelogSettings.AddNext {
rnVersion, updated, date, commits, uerr := getNextVersionInfo(g, g.CommitProcessor)
if uerr != nil {
return uerr
@ -64,7 +68,7 @@ func ChangelogHandler(
}
for i, tag := range tags {
if !all && i >= size {
if !g.Settings.ChangelogSettings.All && i >= g.Settings.ChangelogSettings.Size {
break
}
@ -73,13 +77,13 @@ func ChangelogHandler(
previousTag = tags[i+1].Name
}
if semanticVersionOnly && !sv.IsValidVersion(tag.Name) {
if g.Settings.ChangelogSettings.Strict && !sv.IsValidVersion(tag.Name) {
continue
}
commits, err := g.Log(app.NewLogRange(app.TagRange, previousTag, tag.Name))
if err != nil {
return fmt.Errorf("error getting git log from tag: %s, message: %w", tag.Name, err)
return fmt.Errorf("error getting git log from tag: %s: %w", tag.Name, err)
}
currentVer, _ := sv.ToVersion(tag.Name)
@ -88,10 +92,24 @@ func ChangelogHandler(
output, err := g.OutputFormatter.FormatChangelog(releaseNotes)
if err != nil {
return fmt.Errorf("could not format changelog, message: %w", err)
return fmt.Errorf("could not format changelog: %w", err)
}
fmt.Println(output)
if g.Settings.ChangelogSettings.Out == "" {
os.Stdout.WriteString(fmt.Sprintf("%s\n", output))
return nil
}
w, err := os.Create(g.Settings.ChangelogSettings.Out)
if err != nil {
return fmt.Errorf("could not write changelog: %w", err)
}
defer w.Close()
if _, err := w.Write(output); err != nil {
return err
}
return nil
}

View File

@ -100,7 +100,7 @@ func CommitHandler(g app.GitSV) cli.ActionFunc {
err = g.Commit(header, body, footer)
if err != nil {
return fmt.Errorf("error executing git commit, message: %w", err)
return fmt.Errorf("error executing git commit: %w", err)
}
return nil

View File

@ -69,7 +69,7 @@ func CommitLogHandler(g app.GitSV) cli.ActionFunc {
}
if err != nil {
return fmt.Errorf("error getting git log, message: %w", err)
return fmt.Errorf("error getting git log: %w", err)
}
for _, commit := range commits {

View File

@ -2,28 +2,38 @@ package commands
import (
"fmt"
"os"
"time"
"github.com/thegeeklab/git-sv/v2/app"
"github.com/urfave/cli/v2"
)
func CommitNotesFlags() []cli.Flag {
func CommitNotesFlags(settings *app.CommitNotesSettings) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "r", Aliases: []string{"range"},
Usage: "type of range of commits, use: tag, date or hash",
Required: true,
Usage: "type of range of commits, use: tag, date or hash",
Required: true,
Destination: &settings.Range,
},
&cli.StringFlag{
Name: "s",
Aliases: []string{"start"},
Usage: "start range of git log revision range, if date, the value is used on since flag instead",
Name: "s",
Aliases: []string{"start"},
Usage: "start range of git log revision range, if date, the value is used on since flag instead",
Destination: &settings.Start,
},
&cli.StringFlag{
Name: "e",
Aliases: []string{"end"},
Usage: "end range of git log revision range, if date, the value is used on until flag instead",
Name: "e",
Aliases: []string{"end"},
Usage: "end range of git log revision range, if date, the value is used on until flag instead",
Destination: &settings.End,
},
&cli.StringFlag{
Name: "o",
Aliases: []string{"output"},
Usage: "output file name. Omit to use standard output.",
Destination: &settings.Out,
},
}
}
@ -32,16 +42,16 @@ func CommitNotesHandler(g app.GitSV) cli.ActionFunc {
return func(c *cli.Context) error {
var date time.Time
rangeFlag := c.String("r")
rangeFlag := g.Settings.CommitNotesSettings.Range
lr, err := logRange(g, rangeFlag, c.String("s"), c.String("e"))
lr, err := logRange(g, rangeFlag, g.Settings.CommitNotesSettings.Start, g.Settings.CommitNotesSettings.End)
if err != nil {
return err
}
commits, err := g.Log(lr)
if err != nil {
return fmt.Errorf("error getting git log from range: %s, message: %w", rangeFlag, err)
return fmt.Errorf("error getting git log from range: %s: %w", rangeFlag, err)
}
if len(commits) > 0 {
@ -50,10 +60,24 @@ func CommitNotesHandler(g app.GitSV) cli.ActionFunc {
output, err := g.OutputFormatter.FormatReleaseNote(g.ReleasenotesProcessor.Create(nil, "", date, commits))
if err != nil {
return fmt.Errorf("could not format release notes, message: %w", err)
return fmt.Errorf("could not format commit notes: %w", err)
}
fmt.Println(output)
if g.Settings.CommitNotesSettings.Out == "" {
os.Stdout.WriteString(fmt.Sprintf("%s\n", output))
return nil
}
w, err := os.Create(g.Settings.CommitNotesSettings.Out)
if err != nil {
return fmt.Errorf("could not write commit notes: %w", err)
}
defer w.Close()
if _, err := w.Write(output); err != nil {
return err
}
return nil
}

View File

@ -14,7 +14,7 @@ func CurrentVersionHandler(gsv app.GitSV) cli.ActionFunc {
currentVer, err := sv.ToVersion(lastTag)
if err != nil {
return fmt.Errorf("error parsing version: %s from git tag, message: %w", lastTag, err)
return fmt.Errorf("error parsing version: %s from git tag: %w", lastTag, err)
}
fmt.Printf("%d.%d.%d\n", currentVer.Major(), currentVer.Minor(), currentVer.Patch())

View File

@ -14,12 +14,12 @@ func NextVersionHandler(g app.GitSV) cli.ActionFunc {
currentVer, err := sv.ToVersion(lastTag)
if err != nil {
return fmt.Errorf("error parsing version: %s from git tag, message: %w", lastTag, err)
return fmt.Errorf("error parsing version: %s from git tag: %w", lastTag, err)
}
commits, err := g.Log(app.NewLogRange(app.TagRange, lastTag, ""))
if err != nil {
return fmt.Errorf("error getting git log, message: %w", err)
return fmt.Errorf("error getting git log: %w", err)
}
nextVer, _ := g.CommitProcessor.NextVersion(currentVer, commits)

View File

@ -2,6 +2,7 @@ package commands
import (
"fmt"
"os"
"time"
"github.com/Masterminds/semver/v3"
@ -10,12 +11,19 @@ import (
"github.com/urfave/cli/v2"
)
func ReleaseNotesFlags() []cli.Flag {
func ReleaseNotesFlags(settings *app.ReleaseNotesSettings) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "t",
Aliases: []string{"tag"},
Usage: "get release note from tag",
Name: "t",
Aliases: []string{"tag"},
Usage: "get release note from tag",
Destination: &settings.Tag,
},
&cli.StringFlag{
Name: "o",
Aliases: []string{"output"},
Usage: "output file name. Omit to use standard output.",
Destination: &settings.Out,
},
}
}
@ -30,7 +38,7 @@ func ReleaseNotesHandler(g app.GitSV) cli.ActionFunc {
err error
)
if tag = c.String("t"); tag != "" {
if tag = g.Settings.ReleaseNotesSettings.Tag; tag != "" {
rnVersion, date, commits, err = getTagVersionInfo(g, tag)
} else {
// TODO: should generate release notes if version was not updated?
@ -48,7 +56,21 @@ func ReleaseNotesHandler(g app.GitSV) cli.ActionFunc {
return fmt.Errorf("could not format release notes: %w", err)
}
fmt.Println(output)
if g.Settings.ReleaseNotesSettings.Out == "" {
os.Stdout.WriteString(fmt.Sprintf("%s\n", output))
return nil
}
w, err := os.Create(g.Settings.ReleaseNotesSettings.Out)
if err != nil {
return fmt.Errorf("could not write release notes: %w", err)
}
defer w.Close()
if _, err := w.Write(output); err != nil {
return err
}
return nil
}

View File

@ -14,12 +14,12 @@ func TagHandler(g app.GitSV) cli.ActionFunc {
currentVer, err := sv.ToVersion(lastTag)
if err != nil {
return fmt.Errorf("error parsing version: %s from git tag, message: %w", lastTag, err)
return fmt.Errorf("error parsing version: %s from git tag: %w", lastTag, err)
}
commits, err := g.Log(app.NewLogRange(app.TagRange, lastTag, ""))
if err != nil {
return fmt.Errorf("error getting git log, message: %w", err)
return fmt.Errorf("error getting git log: %w", err)
}
nextVer, _ := g.CommitProcessor.NextVersion(currentVer, commits)
@ -28,7 +28,7 @@ func TagHandler(g app.GitSV) cli.ActionFunc {
fmt.Println(tagname)
if err != nil {
return fmt.Errorf("error generating tag version: %s, message: %w", nextVer.String(), err)
return fmt.Errorf("error generating tag version: %s: %w", nextVer.String(), err)
}
return nil

View File

@ -83,12 +83,12 @@ func getTagVersionInfo(gsv app.GitSV, tag string) (*semver.Version, time.Time, [
previousTag, currentTag, err := getTags(gsv, tag)
if err != nil {
return nil, time.Time{}, nil, fmt.Errorf("error listing tags, message: %w", err)
return nil, time.Time{}, nil, fmt.Errorf("error listing tags: %w", err)
}
commits, err := gsv.Log(app.NewLogRange(app.TagRange, previousTag, tag))
if err != nil {
return nil, time.Time{}, nil, fmt.Errorf("error getting git log from tag: %s, message: %w", tag, err)
return nil, time.Time{}, nil, fmt.Errorf("error getting git log from tag: %s: %w", tag, err)
}
return tagVersion, currentTag.Date, commits, nil
@ -101,7 +101,7 @@ func getNextVersionInfo(
commits, err := gsv.Log(app.NewLogRange(app.TagRange, lastTag, ""))
if err != nil {
return nil, false, time.Time{}, nil, fmt.Errorf("error getting git log, message: %w", err)
return nil, false, time.Time{}, nil, fmt.Errorf("error getting git log: %w", err)
}
currentVer, _ := sv.ToVersion(lastTag)

View File

@ -12,6 +12,34 @@ import (
"gopkg.in/yaml.v3"
)
type Settings struct {
LogLevel string
ChangelogSettings ChangelogSettings
ReleaseNotesSettings ReleaseNotesSettings
CommitNotesSettings CommitNotesSettings
}
type ChangelogSettings struct {
Size int
All bool
AddNext bool
Strict bool
Out string
}
type ReleaseNotesSettings struct {
Tag string
Out string
}
type CommitNotesSettings struct {
Range string
Start string
End string
Out string
}
// Config cli yaml config.
type Config struct {
Version string `yaml:"version"`
@ -23,6 +51,12 @@ type Config struct {
CommitMessage sv.CommitMessageConfig `yaml:"commit-message"`
}
// TagConfig tag preferences.
type TagConfig struct {
Pattern *string `yaml:"pattern"`
Filter *string `yaml:"filter"`
}
func NewConfig(configDir, configFilename string) *Config {
workDir, _ := os.Getwd()
cfg := GetDefault()
@ -184,19 +218,3 @@ func migrateReleaseNotes(headers map[string]string) []sv.ReleaseNotesSectionConf
return sections
}
// ==== Message ====
// CommitMessageConfig config a commit message.
// ==== Branches ====
// ==== Versioning ====
// ==== Tag ====
// TagConfig tag preferences.
type TagConfig struct {
Pattern *string `yaml:"pattern"`
Filter *string `yaml:"filter"`
}

View File

@ -20,6 +20,7 @@ var (
func main() {
gsv := app.New()
cli.VersionPrinter = func(c *cli.Context) {
fmt.Printf("%s version=%s date=%s\n", c.App.Name, c.App.Version, BuildDate)
}
@ -30,15 +31,16 @@ func main() {
Version: BuildVersion,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "log-level",
Usage: "log level",
Value: "info",
Name: "log-level",
Usage: "log level",
Value: "info",
Destination: &gsv.Settings.LogLevel,
},
},
Before: func(ctx *cli.Context) error {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
lvl, err := zerolog.ParseLevel(ctx.String("log-level"))
lvl, err := zerolog.ParseLevel(gsv.Settings.LogLevel)
if err != nil {
return err
}
@ -95,21 +97,21 @@ When flag range is "date", if "end" is YYYY-MM-DD the range will be inclusive.`,
for more info. When flag range is "tag" and start is empty, last tag created will be used instead.
When flag range is "date", if "end" is YYYY-MM-DD the range will be inclusive.`,
Action: commands.CommitNotesHandler(gsv),
Flags: commands.CommitNotesFlags(),
Flags: commands.CommitNotesFlags(&gsv.Settings.CommitNotesSettings),
},
{
Name: "release-notes",
Aliases: []string{"rn"},
Usage: "generate release notes",
Action: commands.ReleaseNotesHandler(gsv),
Flags: commands.ReleaseNotesFlags(),
Flags: commands.ReleaseNotesFlags(&gsv.Settings.ReleaseNotesSettings),
},
{
Name: "changelog",
Aliases: []string{"cgl"},
Usage: "generate changelog",
Action: commands.ChangelogHandler(gsv),
Flags: commands.ChangelogFlags(),
Flags: commands.ChangelogFlags(&gsv.Settings.ChangelogSettings),
},
{
Name: "tag",

View File

@ -21,8 +21,8 @@ type releaseNoteTemplateVariables struct {
// OutputFormatter output formatter interface.
type OutputFormatter interface {
FormatReleaseNote(releasenote sv.ReleaseNote) (string, error)
FormatChangelog(releasenotes []sv.ReleaseNote) (string, error)
FormatReleaseNote(releasenote sv.ReleaseNote) ([]byte, error)
FormatChangelog(releasenotes []sv.ReleaseNote) ([]byte, error)
}
// BaseOutputFormatter formater for release note and changelog.
@ -36,17 +36,17 @@ func NewOutputFormatter(tpls *template.Template) *BaseOutputFormatter {
}
// FormatReleaseNote format a release note.
func (p BaseOutputFormatter) FormatReleaseNote(releasenote sv.ReleaseNote) (string, error) {
func (p BaseOutputFormatter) FormatReleaseNote(releasenote sv.ReleaseNote) ([]byte, error) {
var b bytes.Buffer
if err := p.templates.ExecuteTemplate(&b, "releasenotes-md.tpl", releaseNoteVariables(releasenote)); err != nil {
return "", err
return b.Bytes(), err
}
return b.String(), nil
return b.Bytes(), nil
}
// FormatChangelog format a changelog.
func (p BaseOutputFormatter) FormatChangelog(releasenotes []sv.ReleaseNote) (string, error) {
func (p BaseOutputFormatter) FormatChangelog(releasenotes []sv.ReleaseNote) ([]byte, error) {
templateVars := make([]releaseNoteTemplateVariables, len(releasenotes))
for i, v := range releasenotes {
templateVars[i] = releaseNoteVariables(v)
@ -54,10 +54,10 @@ func (p BaseOutputFormatter) FormatChangelog(releasenotes []sv.ReleaseNote) (str
var b bytes.Buffer
if err := p.templates.ExecuteTemplate(&b, "changelog-md.tpl", templateVars); err != nil {
return "", err
return b.Bytes(), err
}
return b.String(), nil
return b.Bytes(), nil
}
func releaseNoteVariables(releasenote sv.ReleaseNote) releaseNoteTemplateVariables {

View File

@ -56,7 +56,7 @@ func TestBaseOutputFormatter_FormatReleaseNote(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := NewOutputFormatter(tmpls).FormatReleaseNote(tt.input)
if got != tt.want {
if string(got) != tt.want {
t.Errorf("BaseOutputFormatter.FormatReleaseNote() = %v, want %v", got, tt.want)
}