Awesome
Git Changelog Lib
This is a library that can:
- Generate a changelog, or releasenotes, from a GIT repository.
- Determine next version, based on format of commits since last release.
It is fully configurable with a Mustache (Handlebars) template filled with a context of placeholders and helpers.
The changelog can:
- Be stored to file, like
CHANGELOG.md
. There are some templates used for testing available here. - Or, just rendered to a
String
.
It can integrate with Jira, Redmine, GitLab and/or GitHub to retrieve the title of issues.
Version | Java Version |
---|---|
version < 2.0.0 | 8 |
2.0.0 <= version < 2.2.0 | 11 |
2.2.0 <= version | 17 |
Usage
This software can be used with:
- A Gradle plugin.
- A Maven plugin.
- A Jenkins plugin.
- A command line tool.
- A Docker image.
- A GitHub Action
- A library.
There are examples of different templates in the code that are used for testing.
Template - Simple
Here is an example template.
# Changelog
Changelog for {{ownerName}} {{repoName}}.
{{#tags}}
## {{name}}
{{#issues}}
{{#hasIssue}}
{{#hasLink}}
### {{name}} [{{issue}}]({{link}}) {{title}} {{#hasIssueType}} *{{issueType}}* {{/hasIssueType}} {{#hasLabels}} {{#labels}} *{{.}}* {{/labels}} {{/hasLabels}}
{{/hasLink}}
{{^hasLink}}
### {{name}} {{issue}} {{title}} {{#hasIssueType}} *{{issueType}}* {{/hasIssueType}} {{#hasLabels}} {{#labels}} *{{.}}* {{/labels}} {{/hasLabels}}
{{/hasLink}}
{{/hasIssue}}
{{^hasIssue}}
### {{name}}
{{/hasIssue}}
{{#commits}}
**{{{messageTitle}}}**
{{#messageBodyItems}}
* {{.}}
{{/messageBodyItems}}
[{{hash}}](https://github.com/{{ownerName}}/{{repoName}}/commit/{{hash}}) {{authorName}} *{{commitTime}}*
{{/commits}}
{{/issues}}
{{/tags}}
Template - Conventional
If you are using conventional commits:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
You can use built in helpers to produce a nice changelog. You can add your own helpers (using Javascript or Java) as described here.
{{#tags}}
{{#ifReleaseTag .}}
## [{{name}}](https://gitlab.com/html-validate/html-validate/compare/{{name}}) ({{tagDate .}})
{{#ifContainsBreaking commits}}
### Breaking changes
{{#commits}}
{{#ifCommitBreaking .}}
- {{#eachCommitScope .}} **{{.}}** {{/eachCommitScope}} {{{commitDescription .}}} ([{{hash}}](https://github.com/{{ownerName}}/{{repoName}}/commit/{{hash}}))
{{/ifCommitBreaking}}
{{/commits}}
{{/ifContainsBreaking}}
{{#ifContainsType commits type='feat'}}
### Features
{{#commits}}
{{#ifCommitType . type='feat'}}
- {{#eachCommitScope .}} **{{.}}** {{/eachCommitScope}} {{{commitDescription .}}} ([{{hash}}](https://github.com/{{ownerName}}/{{repoName}}/commit/{{hash}}))
{{/ifCommitType}}
{{/commits}}
{{/ifContainsType}}
{{#ifContainsType commits type='fix'}}
### Bug Fixes
{{#commits}}
{{#ifCommitType . type='fix'}}
- {{#eachCommitScope .}} **{{.}}** {{/eachCommitScope}} {{{commitDescription .}}} ([{{hash}}](https://github.com/{{ownerName}}/{{repoName}}/commit/{{hash}}))
{{/ifCommitType}}
{{/commits}}
{{/ifContainsType}}
{{/ifReleaseTag}}
{{/tags}}
Partials
You can use partials in your templates.
changelog.hbs
{{#commits}}
{{> commit}}
{{/commits}}
commit.partial
## {{authorName}} - {{commitTime}}
[{{hashFull}}](https://server/{{hash}})
{{{message}}}
This is configured like:
gitChangelogApi
.withTemplateBaseDir("...")
.withTemplateSuffix(".partial"); //Optional, defaults to ".partial"
Helpers
Some helpers are implemented in this library. And users can also add more helpers as described in Handlebars. If you add your own helpers, using Javascript, you will need to add a scriptengine like Nashorn to your classpath.
ifReleaseTag <Tag>
Conditional, renders a block if given Tag
matches release-tag.
{{#tags}}
{{#ifReleaseTag .}}
"{{.}}" is a release tag
{{/ifReleaseTag}}
{{/tags}}
tagDate <Tag>
Renders date of Tag
on format YYYY-MM-DD
.
{{#tags}}
{{tagDate .}}
{{/tags}}
ifContainsIssueType <List<Issue>>
Conditional, renders a block if given List<Issue>
contains given type
.
{{#ifContainsIssueType issues type="Bug"}}
issues contains bugs
{{/ifContainsIssueType}}
ifContainsIssueTypeOtherThan <List<Issue>>
Conditional, renders a block if given List<Issue>
contains issues that don't match the given type
.
{{#ifContainsIssueTypeOtherThan issues type="fix"}}
commits contains other types than fix
{{/ifContainsIssueTypeOtherThan}}
ifContainsIssueLabel <List<Issue>> label="<value>"
Conditional, renders a block if given List<Issue>
contains given label
.
{{#ifContainsIssueLabel issues label='enhancement'}}
content here
{{/ifContainsIssueLabel}}
ifContainsIssueLabelOtherThan <List<Issue>>
Conditional, renders a block if given List<Issue>
contains labels that don't match the given label
.
{{#ifContainsIssueLabelOtherThan issues label='enhancement'}}
content here
{{/ifContainsIssueLabel}}
ifIssueLabel <label> label="<value>"
Conditional, renders a block if given label
matches the given value
.
{{#ifIssueLabel . label='enhancement'}}
Found a enhancement
{{/ifIssueLabel}}
ifContainsType <List<Commit>>
Conditional, renders a block if given List<Commits>
contains given type
.
{{#ifContainsType commits type="fix"}}
commits contains fixes
{{/ifContainsType}}
ifContainsScope <List<Commit>>
Conditional, renders a block if given List<Commits>
contains given scope
.
{{#ifContainsScope commits scope="deps"}}
commits contains deps
{{/ifContainsScope}}
ifContainsTypeAndScope <List<Commit>>
Conditional, renders a block if given List<Commits>
contains given type
and scope
.
{{#ifContainsTypeAndScope commits type="chore" scope="deps"}}
commits contains chore with deps
{{/ifContainsScope}}
ifContainsTypeOtherThan <List<Commit>>
Conditional, renders a block if given List<Commits>
contains commits that don't match the given type
.
{{#ifContainsTypeOtherThan commits type="fix"}}
commits contains other types than fix
{{/ifContainsTypeOtherThan}}
ifContainsBreaking <List<Commit>>
Conditional, renders a block if given List<Commits>
contains breaking
changes.
{{#ifContainsBreaking commits}}
commits contains fixes
{{/ifContainsBreaking}}
commitDate <Commit>
Renders date of Commit
on format YYYY-MM-DD
.
{{#commits}}
{{commitDate .}}
{{/commits}}
commitDescription <Commit>
Renders description of Commit
.
{{#commits}}
{{commitDescription .}}
{{/commits}}
revertedCommit <Commit>
Renders reverted commit refered to by Commit
.
{{#commits}}
{{revertedCommit .}}
{{/commits}}
ifIssueType <Issue> type="<type>"
Conditional, renders a block if given Issue
is of type
.
{{#issues}}
{{#ifIssueType . type="fix"}} is type fix {{/ifIssueType}}
{{/issues}}
ifIssueTypeOtherThan <Issue> type="<type>"
Conditional, renders a block if given Issue
is of type
.
{{#issues}}
{{#ifIssueTypeOtherThan . type="fix"}} is not type fix {{/ifIssueTypeOtherThan}}
{{/issues}}
ifCommitType <Commit> type="<type>"
Conditional, renders a block if given Commit
is of type
.
{{#commits}}
{{#ifCommitType . type="fix"}} is type fix {{/ifCommitType}}
{{/commits}}
ifCommitTypeOtherThan <Commit> type="<type>"
Conditional, renders a block if given Commit
is of type
.
{{#commits}}
{{#ifCommitTypeOtherThan . type="fix"}} is not type fix {{/ifCommitTypeOtherThan}}
{{/commits}}
ifCommitBreaking <Commit>
Conditional, renders a block if given Commit
is breaking
.
{{#commits}}
{{#ifCommitBreaking .}} is breaking {{/ifCommitBreaking}}
{{/commits}}
ifCommitScope <Commit> scope="utils"
Conditional, renders a block if given Commit
has scope
.
{{#commits}}
{{#ifCommitScope . scope="utils"}} is scope utils {{/ifCommitScope}}
{{/commits}}
ifCommitHasFooters <Commit>
Conditional, renders a block if given Commit
has footers
.
{{#commits}}
{{#ifCommitHasFooters .}} has footers {{/ifCommitHasFooters}}
{{/commits}}
ifCommitHasParagraphs <Commit>
Conditional, renders a block if given Commit
has paragraphs
.
{{#commits}}
{{#ifCommitHasParagraphs .}} has paragraphs {{/ifCommitHasParagraphs}}
{{/commits}}
eachCommitScope <Commit>
Renders block for each scope
in Commit
.
{{#commits}}
{{#eachCommitScope .}}
scope: {{.}}
{{/eachCommitScope}}
{{/commits}}
eachCommitRefs <Commit>
Renders block for each refs
in Commit
.
{{#commits}}
{{#eachCommitRefs .}}
references issue: {{.}}
{{/eachCommitRefs}}
{{/commits}}
eachCommitFixes <Commit>
Renders block for each fixes
in Commit
.
{{#commits}}
{{#eachCommitFixes .}}
fixes issue: {{.}}
{{/eachCommitFixes}}
{{/commits}}
eachCommitParagraph <Commit>
Renders block for each paragraph
in Commit
.
{{#commits}}
{{#eachCommitParagraph .}}
{{.}}
{{/eachCommitParagraph}}
{{/commits}}
eachCommitFooter <Commit>
Renders block for each footer
in Commit
.
{{#commits}}
{{#eachCommitFooter .}}
{{token}}
{{/eachCommitFooter}}
{{/commits}}
Optional tokenMatching
regex parameter filters footer tokens.
ifFooterHasValue <Footer>
Conditional, renders a block if given Footer
has value
.
{{#commits}}
{{#eachCommitFooter .}}
{{#ifFooterHasValue .}}
{{{value}}}
{{/ifFooterHasValue}}
{{/eachCommitFooter}}
{{/commits}}
ifEquals <a> <b>
Conditional, renders a block if a
equals b
.
{{#tags}}
{{name}} Unreleased ? {{#ifEquals name "Unreleased"}} ja {{else}} nej {{/ifEquals}}
{{/tags}}
ifMatches <a> <b>
Conditional, renders a block if a
matches regexp b
.
{{#eachCommitFixes .}}
{{#ifMatches . "^[A-Z]+-[0-9]+"}}
fixes : "{{subString . 0 3}}" and number {{subString . 4}}
{{/ifMatches}}
{{/eachCommitFixes}}
subString <a> <b> <c>
Works just like Java substring.
{{#eachCommitFixes .}}
{{#ifMatches . "^[A-Z]+-[0-9]+"}}
fixes : "{{subString . 0 3}}" and number {{subString . 4}}
{{/ifMatches}}
{{/eachCommitFixes}}
Context
The template is supplied with this context of prepopulated mustache/handlebars variables:
<details><summary>Click here to show context</summary> <p>(ownerName, repoName, urlParts - derived from the clone URL, git remote origin MUST BE SET)
- ownerName (for this repo it would be "tomasbjerre")
- repoName (for this repo it would be "git-changelog-lib")
- urlParts (for this repo it would be [git-changelog-lib, tomasbjerre, git@github.com])
* commits
- authorName
- authorEmailAddress
- commitTime
- hash
- hashFull
- merge (True if this is a merge-commit)
- message (The full message)
- messageTitle (Only the first line of the message)
- messageBody (Everything, except the title)
* messageBodyItems (List of strings, the lines after the title)
* tags
- name
- annotation
- tagTime
- hasTagTime
* commits
- authorName
- authorEmailAddress
- commitTime
- hash
- hashFull
- merge (True if this is a merge-commit)
- message (The full message)
- messageTitle (Only the first line of the message)
- messageBody (Everything, except the title)
* messageBodyItems (List of strings, the lines after the title)
* authors
- authorName
- authorEmail
* commits
- authorName
- authorEmailAddress
- commitTime
- hash
- hashFull
- merge (True if this is a merge-commit)
- message (The full message)
- messageTitle (Only the first line of the message)
- messageBody (Everything, except the title)
* messageBodyItems (List of strings, the lines after the title)
* issueTypes
- name (Like GitHub, GitLab, Jira, ...)
* issues
- name
- hasIssue
- issue
- hasLink
- link
- hasTitle
- title
- hasDescription
- description
- hasType
- type
- isJira
- isGitHub
- isGitLab
- isCustom
- isNoIssue
- hasLabels
- labels
- hasLinkedIssues
- linkedIssues
- hasAdditionalFields
- additionalFields
* commits
- authorName
- authorEmailAddress
- commitTime
- hash
- hashFull
- merge (True if this is a merge-commit)
- message (The full message)
- messageTitle (Only the first line of the message)
- messageBody (Everything, except the title)
* messageBodyItems (List of strings, the lines after the title)
* authors
- authorName
- authorEmail
* commits
- authorName
- authorEmailAddress
- commitTime
- hash
- hashFull
- merge (True if this is a merge-commit)
- message (The full message)
- messageTitle (Only the first line of the message)
- messageBody (Everything, except the title)
* messageBodyItems (List of strings, the lines after the title)
* issues
- name
- hasIssue
- issue
- hasLink
- link
- hasTitle
- title
- hasDescription
- description
- hasType
- type
- isJira
- isGitHub
- isGitLab
- isCustom
- isNoIssue
- hasLabels
- labels
- hasLinkedIssues
- linkedIssues
- hasAdditionalFields
- additionalFields
* commits
- authorName
- authorEmailAddress
- commitTime
- hash
- hashFull
- merge (True if this is a merge-commit)
- message (The full message)
- messageTitle (Only the first line of the message)
- messageBody (Everything, except the title)
* messageBodyItems (List of strings, the lines after the title)
* authors
- authorName
- authorEmail
* commits
- authorName
- authorEmailAddress
- commitTime
- hash
- hashFull
- merge (True if this is a merge-commit)
- message (The full message)
- messageTitle (Only the first line of the message)
- messageBody (Everything, except the title)
* messageBodyItems (List of strings, the lines after the title)
* authors
- authorName
- authorEmail
* commits
- authorName
- authorEmailAddress
- commitTime
- hash
- hashFull
- merge (True if this is a merge-commit)
- message (The full message)
- messageTitle (Only the first line of the message)
- messageBody (Everything, except the title)
* messageBodyItems (List of strings, the lines after the title)
* issues
- name
- hasIssue
- issue
- hasLink
- link
- hasTitle
- title
- hasDescription
- description
- hasType
- type
- isJira
- isGitHub
- isGitLab
- isCustom
- isNoIssue
- hasLabels
- labels
- hasLinkedIssues
- linkedIssues
- hasAdditionalFields
- additionalFields
* commits
- authorName
- authorEmailAddress
- commitTime
- hash
- hashFull
- merge (True if this is a merge-commit)
- message (The full message)
- messageTitle (Only the first line of the message)
- messageBody (Everything, except the title)
* messageBodyItems (List of strings, the lines after the title)
* authors
- authorName
- authorEmail
* commits
- authorName
- authorEmailAddress
- commitTime
- hash
- hashFull
- merge (True if this is a merge-commit)
- message (The full message)
- messageTitle (Only the first line of the message)
- messageBody (Everything, except the title)
* messageBodyItems (List of strings, the lines after the title)
</p>
</details>
Library
It has a builder for creating the changelog.
gitChangelogApiBuilder()
.withFromCommit(ZERO_COMMIT)
.withToRef("refs/heads/master")
.withTemplatePath("changelog.mustache")
.render();
It can be used to calculate next version number, based on commits:
def nextVersion = gitChangelogApiBuilder()
.withSemanticMajorVersionPattern("^[Bb]reaking")
.withSemanticMinorVersionPattern("[Ff]eature")
.getNextSemanticVersion();
println "Next version:" + nextVersion.toString();
println " Major:" + nextVersion.getMajor();
println " Minor:" + nextVersion.getMinor();
println " Patch:" + nextVersion.getPatch();
Settings can be supplied with the build or from a JSON config (documented here).