Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release #54

Merged
merged 3 commits into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .releaserc.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module.exports = {
{
releaseRules: {
patch: {
include: [':bento:', ':arrow_up:'],
include: [':bento:', ':arrow_up:', ':lock:'],
},
},
releaseNotes: {
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ interface EmojiArrayModifier {

All templates file are compiled and renderered by [`handlebars`](http://handlebarsjs.com/), therefore you may need to get familiar with the `.hbs` format before starting to customize your own templates.

`semver` is a boolean to define if releaseNotes should be based on Gitmoji only or on key semver associated to gitmoji used in commit to determine the next release tag.

`partials` is a map from the partial name to the content of the partial template.

`helpers` is a map from the helper name to the helper function. There is already a default helper `datetime` which takes a format string as the first argument and return a formatted current timestamp. See [npm/dateformat](https://www.npmjs.com/package/dateformat) for more information about how to format a timestamp and see [the default template](https://github.com/momocow/semantic-release-gitmoji/blob/master/lib/assets/templates/default-template.hbs#L2) as an example.
Expand All @@ -164,6 +166,7 @@ There are five variables that can be used in `issueResolution.template`:
```ts
interface ReleaseNotesOptions {
template?: TemplateContent
semver?: Boolean
partials?: Record<string, TemplateContent>
helpers?: Record<string, Function>
issueResolution?: {
Expand Down
1 change: 1 addition & 0 deletions lib/assets/default-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ module.exports = {
prerelease: []
},
releaseNotes: {
semver: false,
template: readFileAsync(path.join(TEMPLATE_DIR, 'default-template.hbs')),
partials: {
commitTemplate: readFileAsync(path.join(TEMPLATE_DIR, 'commit-template.hbs'))
Expand Down
28 changes: 28 additions & 0 deletions lib/assets/templates/default-template-semver.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{{#if compareUrl}}
# [v{{nextRelease.version}}]({{compareUrl}}) ({{datetime "UTC:yyyy-mm-dd"}})
{{else}}
# v{{nextRelease.version}} ({{datetime "UTC:yyyy-mm-dd"}})
{{/if}}

{{#with commits}}
{{#if major}}
## 💥 Breaking Changes
{{#each major}}
- {{> commitTemplate}}
{{/each}}
{{/if}}

{{#if minor}}
## ✨ New Features
{{#each minor}}
- {{> commitTemplate}}
{{/each}}
{{/if}}

{{#if patch}}
## 🐛 Fixes
{{#each patch}}
- {{> commitTemplate}}
{{/each}}
{{/if}}
{{/with}}
9 changes: 9 additions & 0 deletions lib/helper/get-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ const _mergeWith = require('lodash.mergewith')
const _cloneDeep = require('lodash.clonedeep')
const _uniq = require('lodash.uniq')

const path = require('path')
const fs = require('fs')
const { promisify } = require('util')
const readFileAsync = promisify(fs.readFile)

const DEFAULT_CONFIG = require('../assets/default-config')
const TEMPLATE_DIR = path.join(__dirname, '../assets/templates')

function normalizeEmojis (arr = []) {
return (Array.isArray(arr) ? arr : [arr])
Expand All @@ -25,5 +31,8 @@ function handleArrayConfig (dest, src) {
}

module.exports = function getConfig (userConfig) {
if (userConfig && 'releaseNotes' in userConfig && 'semver' in userConfig.releaseNotes && userConfig.releaseNotes.semver) {
DEFAULT_CONFIG.releaseNotes.template = readFileAsync(path.join(TEMPLATE_DIR, 'default-template-semver.hbs'))
}
return _mergeWith(_cloneDeep(DEFAULT_CONFIG), userConfig, handleArrayConfig)
}
29 changes: 24 additions & 5 deletions lib/helper/parse-commits.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const issueRegex = require('issue-regex')
const emojiRegex = require('emoji-regex/text')
const { emojify } = require('node-emoji')
const { gitmojis } = require('gitmojis')

const resolveIssueRef = require('./resolve-issue-ref')

Expand Down Expand Up @@ -34,9 +35,10 @@ function parseGitmoji ({ subject = '', message = '', body = '' } = {}, issues =
if (!matched || matched.index !== 0) return null

const gitmoji = matched[0]
const semver = gitmojis.find(item => item.emoji === gitmoji).semver || 'other'
subject = subject.replace(new RegExp('^' + gitmoji), '')

return { subject, message: subject + '\n\n' + body, gitmoji }
return { subject, message: subject + '\n\n' + body, gitmoji, semver }
}

module.exports = function parseCommits (commits = [], mixins = {}, options = {}) {
Expand All @@ -53,9 +55,9 @@ module.exports = function parseCommits (commits = [], mixins = {}, options = {})
}
})
.reduce((acc, commit) => {
if (commit.gitmoji) {
if (!Array.isArray(acc[commit.gitmoji])) acc[commit.gitmoji] = []
acc[commit.gitmoji].push(commit)
if (options.semver) {
if (!Array.isArray(acc[commit.semver])) acc[commit.semver] = []
acc[commit.semver].push(commit)

if (commit.task) {
// commits are in the descending order
Expand All @@ -67,7 +69,24 @@ module.exports = function parseCommits (commits = [], mixins = {}, options = {})
taskMap.get(commit.task).wip.push(commit)
}
}
return acc
} else {
if (commit.gitmoji) {
if (!Array.isArray(acc[commit.gitmoji])) acc[commit.gitmoji] = []
acc[commit.gitmoji].push(commit)

if (commit.task) {
// commits are in the descending order
if (!taskMap.has(commit.task) && commit.gitmoji !== '🚧') {
// it is the final commit if it has not been in the task map
taskMap.set(commit.task, commit)
} else if (taskMap.has(commit.task) && commit.gitmoji === '🚧') {
// the final commit exists so this commit is the releted wip commits
taskMap.get(commit.task).wip.push(commit)
}
}
}
return acc
}
return acc
}, {})
}
59 changes: 42 additions & 17 deletions lib/release-notes.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ module.exports = class ReleaseNotes {
/**
* @param {object} context
*/
constructor (context, { issueResolution, template, partials, helpers } = {}) {
constructor (context, { issueResolution, template, partials, helpers, semver } = {}) {
debug('Found %d commits', context.commits.length)

const { owner, name: repo, source } = parseGitUrl(context.options.repositoryUrl)

debug('Git remote: %s', source)
debug('Repository: %s/%s', owner, repo)

this._semver = semver
this._template = template
this._partials = partials
this._helpers = {
Expand All @@ -50,7 +51,8 @@ module.exports = class ReleaseNotes {
repo,
source,
commits: parseCommits(context.commits, { owner, repo, source }, {
issues: { owner, repo, source, ...issueResolution }
issues: { owner, repo, source, ...issueResolution },
semver: this._semver
})
}

Expand All @@ -67,20 +69,34 @@ module.exports = class ReleaseNotes {
// cache hits
if (this._rtype) return this._rtype

for (const RTYPE of RELEASE_TYPES) {
debug('Testing against release type "%s"', RTYPE)
for (let gitmoji of releaseSchema[RTYPE]) {
gitmoji = emojify(gitmoji)
debug('Testing against gitmoji "%s "', gitmoji)
if (this._semver) {
for (const RTYPE of RELEASE_TYPES) {
debug('Testing against release type "%s"', RTYPE)
if (
this._context.commits[gitmoji] &&
this._context.commits[gitmoji].length > 0
this._context.commits[RTYPE] &&
this._context.commits[RTYPE].length > 0
) {
debug('Release type is now "%s".', RTYPE)
this._rtype = RTYPE
return RTYPE
}
}
} else {
for (const RTYPE of RELEASE_TYPES) {
debug('Testing against release type "%s"', RTYPE)
for (let gitmoji of releaseSchema[RTYPE]) {
gitmoji = emojify(gitmoji)
debug('Testing against gitmoji "%s "', gitmoji)
if (
this._context.commits[gitmoji] &&
this._context.commits[gitmoji].length > 0
) {
debug('Release type is now "%s".', RTYPE)
this._rtype = RTYPE
return RTYPE
}
}
}
}
}

Expand Down Expand Up @@ -123,18 +139,27 @@ module.exports = class ReleaseNotes {
if (this._template instanceof Promise) {
this._template = await this._template
}

this._renderer = hbs.compile(this._template.toString())
}

toString () {
return this._renderer({
...this._context,
commits: proxifyCommitsMap(this._context.commits)
}, {
allowProtoPropertiesByDefault: true
})
.replace(/\n{3,}/g, '\n\n') // allow a blank line
if (this._semver) {
return this._renderer({
...this._context,
commits: this._context.commits
}, {
allowProtoPropertiesByDefault: true
})
.replace(/\n{3,}/g, '\n\n') // allow a blank line
} else {
return this._renderer({
...this._context,
commits: proxifyCommitsMap(this._context.commits)
}, {
allowProtoPropertiesByDefault: true
})
.replace(/\n{3,}/g, '\n\n') // allow a blank line
}
}

/**
Expand Down
Loading