Permalink
| // section -> label | |
| const sectionLabelMap = { | |
| 'Core Changes': 'type: next', | |
| 'Documentation Changes': 'type: documentation', | |
| 'Example Changes': 'type: example', | |
| } | |
| const fallbackSection = 'Misc Changes' | |
| // -------------------------------------------------- | |
| const prNumberRegex = /\(#([-0-9]+)\)/ | |
| const getCommitPullRequest = async (commit, github) => { | |
| const match = prNumberRegex.exec(commit.title) | |
| if (!match) { | |
| return null | |
| } | |
| const number = parseInt(match[1], 10) | |
| if (!number) { | |
| return null | |
| } | |
| const { data } = await github.connection.pullRequests.get({ | |
| owner: github.repoDetails.user, | |
| repo: github.repoDetails.repo, | |
| number, | |
| }) | |
| return data | |
| } | |
| const getSectionForPullRequest = (pullRequest) => { | |
| const { labels } = pullRequest | |
| // sections defined first will take priority | |
| for (const [section, label] of Object.entries(sectionLabelMap)) { | |
| if (labels.some((prLabel) => prLabel.name === label)) { | |
| return section | |
| } | |
| } | |
| return null | |
| } | |
| const groupByLabels = async (commits, github) => { | |
| // Initialize the sections object with empty arrays | |
| const sections = Object.keys(sectionLabelMap).reduce((sections, section) => { | |
| sections[section] = [] | |
| return sections | |
| }, {}) | |
| sections.__fallback = [] | |
| for (const commit of commits) { | |
| const pullRequest = await getCommitPullRequest(commit, github) | |
| if (pullRequest) { | |
| const section = getSectionForPullRequest(pullRequest) | |
| if (section) { | |
| // Add the change to the respective section | |
| sections[section].push({ | |
| title: pullRequest.title, | |
| number: pullRequest.number, | |
| }) | |
| continue | |
| } | |
| // No section found, add it to the fallback section | |
| sections.__fallback.push({ | |
| title: pullRequest.title, | |
| number: pullRequest.number, | |
| }) | |
| continue | |
| } | |
| // No Pull Request found, add it to the fallback section but without the number | |
| sections.__fallback.push({ | |
| title: commit.title, | |
| }) | |
| } | |
| return sections | |
| } | |
| function cleanupPRTitle(title) { | |
| if (title.startsWith('[Docs] ')) { | |
| return title.replace('[Docs] ', '') | |
| } | |
| return title | |
| } | |
| const buildChangelog = (sections, authors) => { | |
| let text = '' | |
| for (const section in sections) { | |
| const changes = sections[section] | |
| // No changes in this section? Don't render it | |
| if (changes.length === 0) { | |
| continue | |
| } | |
| const title = section === '__fallback' ? fallbackSection : section | |
| text += `### ${title}\n\n` | |
| for (const change of changes) { | |
| const numberText = change.number != null ? `: #${change.number}` : '' | |
| text += `- ${cleanupPRTitle(change.title)}${numberText}\n` | |
| } | |
| text += '\n' | |
| } | |
| if (authors.size > 0) { | |
| text += '### Credits \n\n' | |
| text += 'Huge thanks to ' | |
| let index = 1 | |
| authors.forEach((author) => { | |
| // GitHub links usernames if prefixed with @ | |
| text += `@${author}` | |
| const penultimate = index === authors.size - 1 | |
| const notLast = index !== authors.size | |
| if (penultimate) { | |
| // Oxford comma is applied when list is bigger than 2 names | |
| if (authors.size > 2) { | |
| text += ',' | |
| } | |
| text += ' and ' | |
| } else if (notLast) { | |
| text += ', ' | |
| } | |
| index += 1 | |
| }) | |
| text += ' for helping!' | |
| text += '\n' | |
| } | |
| return text | |
| } | |
| module.exports = async (markdown, metadata) => { | |
| const { commits, authors, githubConnection, repoDetails } = metadata | |
| const github = { connection: githubConnection, repoDetails } | |
| const sections = await groupByLabels(commits.all, github) | |
| const changelog = buildChangelog(sections, authors) | |
| return changelog | |
| } |