jmongard / Git.SemVersioning.Gradle

Gradle plugin for automatically versioning a project using semantic versioning and conventional commits with change log support based on git commit messages.
https://plugins.gradle.org/plugin/com.github.jmongard.git-semver-plugin
Apache License 2.0
38 stars 4 forks source link

Add a task to create "templatized" release notes #28

Closed sschuberth closed 8 months ago

sschuberth commented 9 months ago

I'm not sure if this would be out of scope, but as the commits since the last tag are determined anyway, maybe grouping them by type as part of writing out a release notes files in Markdown format would make sense. Such a Markdown file could then be used for the notes as part of GitHub releases. This would avoid users of this plugin to additionally take something like https://github.com/requarks/changelog-action into use, and ensure that the interpretation of Conventinal Commits is exactly the same for bumping the version and creating the notes.

sschuberth commented 9 months ago

Oh, I completely missed that there's now a printChangeLog task! Almost what I want it seems, I need to have a deeper look.

sschuberth commented 9 months ago

Do you think the ChangeLog layout could be made configurable, e.g. via Kotlin scripting or FreeMarker?

jmongard commented 9 months ago

The change log is somewhat configurable. I have not gotten around to document it as I'm still trying it out in my own projects. The default configuration is made to generate a change log in markdown similar to Micronauts change log

Output is limited to: Heading

Subheading changelist

Subheading changelist

Footer

import git.semver.plugin.gradle.ChangeLogFormatter

plugins {
    id("com.github.jmongard.git-semver-plugin") version "0.6.4"
}

semver {
    changeLogTexts = mutableMapOf(
        ChangeLogFormatter.OTHER_TYPE to "### Other Changes \uD83D\uDCA1",
        ChangeLogFormatter.MISSING_TYPE to "### Other Changes \uD83D\uDCA1",
        ChangeLogFormatter.BREAKING_CHANGE to "### Breaking Changes \uD83D\uDEE0",
        ChangeLogFormatter.HEADER to "## What's Changed",
        ChangeLogFormatter.CHANGE_PREFIX to "  - ",
        ChangeLogFormatter.CHANGE_LINE_SEPARATOR to "\n    ",
        ChangeLogFormatter.CHANGE_POSTFIX to "",
        "fix" to "### Bug Fixes \uD83D\uDC1E",
        "feat" to "### New Features \uD83C\uDF89",
        "test" to "### Tests ✅",
        "docs" to "### Docs \uD83D\uDCD6",
        "deps" to "### Dependency updates \uD83D\uDE80",
        "build" to "### Build \uD83D\uDC18 & CI ⚙\uFE0F",
        "ci" to "### Build \uD83D\uDC18 & CI ⚙\uFE0F",
        "chore" to "### Chores",
        "perf" to "### Performance Enhancements",
        "refactor" to "### Refactorings",
        "release" to "")
}

It should be possible to change this to output HTML if you like.

How would you like the output to be presented? What would be needed from a template file?

sschuberth commented 9 months ago

The default configuration is made to generate a change log in markdown similar to Micronauts change log

That also looks quite nice. So far I was looking at https://github.com/requarks/changelog-action/releases.

It should be possible to change this to output HTML if you like.

No HTML please 😉 Markdown is just fine, as I'm looking for submitting the generated text to GitHub releases.

How would you like the output to be presented? What would be needed from a template file?

What I like about https://github.com/requarks/changelog-action is that it also lists the short commit SHA1 (as a clickable link) and the resolved issue, if any.

And some grouping on scopes (per type) like JReleaser does would maybe also be nice.

sschuberth commented 9 months ago

Also, right now the full commit message seems to be printed instead of just its title.

sschuberth commented 9 months ago

Also, right now the full commit message seems to be printed instead of just its title.

This should address that.

sschuberth commented 9 months ago

The default configuration is made to generate a change log in markdown similar to Micronauts change log

That also looks quite nice. So far I was looking at https://github.com/requarks/changelog-action/releases.

For the record, I also like https://github.com/orhun/git-cliff/releases.

jmongard commented 9 months ago

This works for publishing release notes to Github using a linux runner. (I'm still having trouble with the emojis being replaced by "?" when using a windows runner. I think it's some encoding issue in powershell.)

    - name: Create GitHub release
      if: "startsWith(github.ref, 'refs/tags/')"
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        tag=$(git describe --tags --abbrev=0)
        version=$(./gradlew -q printVersion)
         ./gradlew -q printChangeLog | gh release create $tag -t "MyApp $version" -F -
sschuberth commented 9 months ago

This works for publishing release notes to Github using a linux runner.

Yes, thanks, I have something similar in place.

I think it's some encoding issue in powershell.

It's probably easier to debug when omitting some variable pieces and writing to a file (with UTF-8 encoding) instead of going through shell redirection...

Anyway, shall I close this issue, as the task is there, and there's just the open question about if and how the changelog layout should be made more configurable? That could be discussed in a separate issue.

sschuberth commented 9 months ago

if and how the changelog layout should be made more configurable?

I just found some inspiration.

jmongard commented 9 months ago

Yes, good inspiration but the mustache templates looks scary :-). I'm thinking perhaps a Kotlin DSL could be created for templates e.g.

semver {
    changeLogSettings {
        template {
            Heading

            BreakingChanges {
               CommitMessage
            }

            ChangeList {
                ChangeHeader
                ChangeItem {
                    CommitHash
                    Scope("%s: ")
                    Text
                }
            }

            Footer
        }
    }
}

I never created a DSL like this so I'm a bit unsure how it could be done

sschuberth commented 9 months ago

While I'm a big fan of Kotlin DSLs, for this use-case it could be a bit cumbersome to maintain. As soon as you'd introduce a new property in the data model, you'd need to also create an according DSL. That is, unless you use something like https://github.com/F43nd1r/autodsl or https://github.com/bipokot/Kabu 😆

jmongard commented 9 months ago

I'm currently experimenting with a way to configure the change log like this:

semver {
  groupVersionIncrements = false
  createReleaseTag = true
  createReleaseCommit = true
//changeLogFormat = ChangeLogFormat.scopeChangeLog
  changeLogFormat {
        appendLine("# My changelog").appendLine()

        withBreakingChanges {
            appendLine("## Breaking changes")
            format {
                "- ${fullHeader()}"
            }
            appendLine()
        }
        withType("fix") {
            appendLine("## Bug Fixes")
            format {
                "- ${scope()}${header()}"
            }
            appendLine()
        }
        withType("feat") {
            appendLine("## Features")
            format {
                "- ${scope()}${header()}"
            }
            appendLine()
        }
    }
}

and a more advanced example that first groups on type and then scope :

{
            appendLine(defaultHeader).appendLine()

            withType("release") {
                skip()
            }
            withBreakingChanges({
                appendLine(defaultBreakingChangeHeader).appendLine()
                groupByScope {
                    append("#### ").appendLine(key)
                    format {
                        "- ${hash()}${type()}${header()}"
                    }
                    appendLine()
                }
                withoutScope {
                    appendLine("#### Missing scope")
                    format {
                        "- ${hash()}${type()}${header()}"
                    }
                    appendLine()
                }
                appendLine()
            })
            groupBySorted({ defaultHeaderTexts[it.type].orEmpty() }, {
                appendLine(key).appendLine()
                groupByScope {
                    append("#### ").appendLine(key)
                    format {
                        "- ${hash()}${header()}"
                    }
                    appendLine()
                }
                withoutScope {
                    appendLine("#### Missing scope")
                    format {
                        "- ${hash()}${header()}"
                    }
                    appendLine()
                }
                appendLine()
            })
            otherwise({
                appendLine(defaultOtherChangeHeader).appendLine()
                groupByScope {
                    append("#### ").appendLine(key)
                    format {
                        "- ${hash()}${type()}${header()}"
                    }
                    appendLine()
                }
                withoutScope {
                    appendLine("#### Missing scope")
                    format {
                        "- ${hash()}${type()}${header()}"
                    }
                    appendLine()
                }
                appendLine()
            })
        }

The default format produces a change log very similar to the previous version. You can group and filter the commits any way you like and the use some predefined formatting methods to create the change log text for the commit.

It is still missing functionality to access the author and commit time. I guess if you want the GitHub user name you need to access the GitHub API but I don't think I want to make the plugin specific to GitHub.

sschuberth commented 8 months ago

How do you customize e.g. the top-most "What's Changed" header with the changeLogFormat {} approach?

jmongard commented 8 months ago

You either set the constant text

semver {
  ChangeLogFormat.defaultHeaderTexts[ChangeLogFormat.HEADER]="My Changelog Header"
  //Optionally use another predefined format
  changeLogFormat = ChangeLogFormat.scopeChangeLog
}

or if you have your custom format just append whatever you like:

semver {
  changeLogFormat {
      appendLine("# Test changelog").appendLine()
      withType("test") {
          appendLine("## Test")
          formatChanges {
              appendLine("- ${'$'}{scope()}${'$'}{header()}")
          }
          appendLine()
      }
  }
}

There is a bit of documentation to do for the change log feature :sweat_smile:

jmongard commented 8 months ago

Maybe it can changed to something prettier 🤔

sschuberth commented 8 months ago

Maybe it can changed to something prettier 🤔

I agree. It doesn't feel right to alter a mutable global map to do this. IMO header etc. should be (nested) properties on the semver / changeLogFormat extension that you can set like

semver {
    changeLogFormat {
        header = "What's New"
    }
}
jmongard commented 8 months ago

Changed in 0.9.0 so now you could set the text like this:

semver {
          changeLogTexts {
                   header = "# My Changelog"
                   breakingChange = "## Breaking"
                   otherChange = "## Other changelog"
                   headerTexts["feat"] = "## New Feature"
           }
}

How does it feel?

sschuberth commented 8 months ago

How does it feel?

Looks good!

sschuberth commented 8 months ago

While what's currently implemented in not the fully templatized approach I had in mind originally, I feel that it's configurable enough, so let me close this.