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

[Work in Progess] Contributors idea #1338

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
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
12 changes: 6 additions & 6 deletions __tests__/demo/demo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const token = process.env.GITHUB_TOKEN || ''
const githubRepository = new GithubRepository(token, undefined, '.')
it('Test custom changelog builder', async () => {
// define the configuration file to use.
// By default it retireves a configuration from a json file
// By default, it retrieves a configuration from a json file
// You can also quickly modify in code.
const configuration = mergeConfiguration(undefined, resolveConfiguration('', 'configs/configuration.json'))

Expand All @@ -24,17 +24,17 @@ it('Test custom changelog builder', async () => {
'mikepenz', // The owner of the repo to test
'release-changelog-builder-action-playground', // The repository name
'1.5.0', // `fromTag` The from tag name or the SHA1 of the from commit
'2.0.0', // `toTag` The to tag name or the SHA1 of the to commit
'4.2.2', // `toTag` The to tag name or the SHA1 of the to commit
false, // `includeOpen` Define if you want to include open PRs into the changelog
false, // `failOnError` Define if the action should fail on errors
false, // `ignorePrePrelease` used if no `fromTag` is defined to resolve the prior tag
false, // `ignorePrePrerelease` used if no `fromTag` is defined to resolve the prior tag
false, // `fetchViaCommits` enable to fetch via commits
false, // `fetchReviewers` Enables fetching of reviewers for building the changlog (does additional API requests)
false, // `fetchReviewers` Enables fetching of reviewers for building the changelog (does additional API requests)
false, // `fetchReleaseInformation` Enable to fetch release information (does additional API requests)
false, // `fetchReviews` Enable to fetch reviews of the PRs (does additional API requests)
'PR', // `mode` Set the mode to use [PR, COMMIT, HYBRID]. PR -> builds changelog using PRs, COMMIT -> using commits, HYBRID -> Uses both
false, // `exportCache` Exports the fethced information to the cahce. Not relevant for this test
false, // `exportOnly` Enables to only export the fetched information however not build a changleog
false, // `exportCache` Exports the fetched information to the cache. Not relevant for this test
false, // `exportOnly` Enables to only export the fetched information however not build a changelog
null, // `cache` Path to the cache. Not relevant for this test.
configuration // The configuration to use for building the changelog
)
Expand Down
36 changes: 24 additions & 12 deletions __tests__/transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ mergedPullRequests.push(
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
status: 'merged',
committers: []
},
{
number: 2,
Expand All @@ -67,7 +68,8 @@ mergedPullRequests.push(
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
status: 'merged',
committers: []
},
{
number: 3,
Expand All @@ -85,7 +87,8 @@ mergedPullRequests.push(
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
status: 'merged',
committers: []
},
{
number: 4,
Expand All @@ -103,7 +106,8 @@ mergedPullRequests.push(
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
status: 'merged',
committers: []
}
)

Expand All @@ -123,7 +127,8 @@ const pullRequestWithLabelInBody: PullRequestInfo = {
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
status: 'merged',
committers: []
}

const openPullRequest: PullRequestInfo = {
Expand All @@ -142,7 +147,8 @@ const openPullRequest: PullRequestInfo = {
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'open'
status: 'open',
committers: []
}

it('Extract label from title, combined regex', async () => {
Expand Down Expand Up @@ -256,7 +262,8 @@ pullRequestsWithLabels.push(
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
status: 'merged',
committers: []
},
{
number: 2,
Expand All @@ -274,7 +281,8 @@ pullRequestsWithLabels.push(
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
status: 'merged',
committers: []
},
{
number: 3,
Expand All @@ -292,7 +300,8 @@ pullRequestsWithLabels.push(
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
status: 'merged',
committers: []
},
{
number: 4,
Expand All @@ -310,7 +319,8 @@ pullRequestsWithLabels.push(
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
status: 'merged',
committers: []
}
)

Expand All @@ -332,7 +342,8 @@ openPullRequestsWithLabels.push(
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'open'
status: 'open',
committers: []
},
{
number: 7,
Expand All @@ -350,7 +361,8 @@ openPullRequestsWithLabels.push(
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'open'
status: 'open',
committers: []
}
)

Expand Down
3 changes: 2 additions & 1 deletion __tests__/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const buildPullRequeset = (number: number, title: string, labels: string[
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
status: 'merged',
committers: []
}
}
8 changes: 4 additions & 4 deletions src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export interface Category {
empty_content?: string // if the category has no matching PRs, this content will be used. If not set, the category will be skipped in the changelog.
categories?: Category[] // allows for nested categories, items matched for a child category won't show up in the parent
consume?: boolean // defines if the matched PR will be consumed by this category. Consumed PRs won't show up in any category *after*
entries?: string[] // array of single changelog entries, used to construc the changelog. (this is filled during the build)
entries?: string[] // array of single changelog entries, used to construct the changelog. (this is filled during the build)
}

/**
Expand Down Expand Up @@ -60,7 +60,7 @@ export interface TagResolver {
export interface Placeholder {
name: string // the name of the new placeholder
source: string // the src placeholder which will be used to apply the transformer on
transformer: Regex // the transformer to use to transform the original placeholder into the custom placheolder
transformer: Regex // the transformer to use to transform the original placeholder into the custom placeheolder
}

export const DefaultConfiguration: Configuration = {
Expand Down Expand Up @@ -94,13 +94,13 @@ export const DefaultConfiguration: Configuration = {
labels: []
}
], // the categories to support for the ordering
ignore_labels: ['ignore'], // list of lables being ignored from the changelog
ignore_labels: ['ignore'], // list of labels being ignored from the changelog
label_extractor: [], // extracts additional labels from the commit message given a regex
duplicate_filter: undefined, // extract an identifier from a PR used to detect duplicates, will keep the last match (depends on `sort`)
transformers: [], // transformers to apply on the PR description according to the `pr_template`
tag_resolver: {
// defines the logic on how to resolve the previous tag, only relevant if `fromTag` is not specified
method: 'semver', // defines which method to use, by default it will use `semver` (dropping all non matching tags). Alternative `sort` is also available.
method: 'semver', // defines which method to use, by default it will use `semver` (dropping all non-matching tags). Alternative `sort` is also available.
filter: undefined, // filter out all tags not matching the regex
transformer: undefined // transforms the tag name using the regex, run after the filter
},
Expand Down
4 changes: 2 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ async function run(): Promise<void> {
if (configurationJson) {
configJson = parseConfiguration(configurationJson)
if (configJson) {
core.info(`ℹ️ Retreived configuration via 'configurationJson'.`)
core.info(`ℹ️ Retrieved configuration via 'configurationJson'.`)
}
}
// read in the configuration from the file if possible
const configurationFile: string = core.getInput('configuration')
const configFile = resolveConfiguration(repositoryPath, configurationFile)
if (configFile) {
core.info(`ℹ️ Retreived configuration via 'configuration' (via file).`)
core.info(`ℹ️ Retrieved configuration via 'configuration' (via file).`)
}

if (!configJson && !configFile) {
Expand Down
3 changes: 2 additions & 1 deletion src/pr-collector/commits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ export function convertCommitsToPrs(options: Options, diffInfo: DiffInfo): [Diff
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'merged'
status: 'merged',
committers: []
}
})
return [diffInfo, prs]
Expand Down
30 changes: 28 additions & 2 deletions src/pr-collector/pullRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface PullRequestInfo {
approvedReviewers: string[]
reviews?: CommentInfo[]
status: 'open' | 'merged'
committers: string[]
}

export interface CommentInfo {
Expand Down Expand Up @@ -52,7 +53,8 @@ export const EMPTY_PULL_REQUEST_INFO: PullRequestInfo = {
assignees: [],
requestedReviewers: [],
approvedReviewers: [],
status: 'open'
status: 'open',
committers: []
}

export const EMPTY_COMMENT_INFO: CommentInfo = {
Expand Down Expand Up @@ -123,7 +125,22 @@ export class PullRequests {

const prCommits = filterCommits(commits, configuration.exclude_merge_branches)
core.info(`ℹ️ Retrieved ${prCommits.length} release commits for ${owner}/${repo}`)


const prCommittersMap = new Map<string, string[]>();
const uniqueAuthors = new Set<string>();

// Iterate over each entry in prCommittersMap
for (const [commitSha, authors] of prCommittersMap.entries()) {
// Iterate over each author in the array and add to the set
authors.forEach(author => {
uniqueAuthors.add(author);
});
}

// Convert Set to array (if needed)
const uniqueAuthorsList = Array.from(uniqueAuthors);

// create array of commits for this release
const releaseCommitHashes = prCommits.map(commit => {
return commit.sha
Expand Down Expand Up @@ -174,6 +191,15 @@ export class PullRequests {
pullRequests = allPullRequests
}

// Update each pull request with its committers
pullRequests.forEach(pr => {
if (prCommittersMap.has(pr.number)) {
pr.committers = prCommittersMap.get(pr.number);
} else {
pr.committers = [];
}
});

// retrieve base branches we allow
const baseBranches = configuration.base_branches
const baseBranchPatterns = baseBranches.map(baseBranch => {
Expand Down Expand Up @@ -205,7 +231,7 @@ export class PullRequests {
if (reviews && (reviews?.length || 0) > 0) {
core.info(`ℹ️ Retrieved ${reviews.length || 0} review(s) for PR ${owner}/${repo}/#${pr.number}`)

// backwards compatiblity
// backwards compatibility
pr.approvedReviewers = reviews.filter(r => r.state === 'APPROVED').map(r => r.author)
} else {
core.debug(`No reviewer(s) for PR ${owner}/${repo}/#${pr.number}`)
Expand Down
2 changes: 1 addition & 1 deletion src/pr-collector/regexUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export function transformStringToValue(value: string, extractor: RegexTransforme

function transformRegexr(regex: RegExp, source: string, target: string): Set<string> | null {
/**
* Util funtion extracted from regexr and is licensed under:
* Util function extracted from regexr and is licensed under:
*
* RegExr: Learn, Build, & Test RegEx
* Copyright (C) 2017 gskinner.com, inc.
Expand Down
5 changes: 3 additions & 2 deletions src/repositories/GithubRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,12 @@ export class GithubRepository extends BaseRepository {
labels: this.attachSpecialLabels(status, pr.labels?.map(lbl => lbl.name?.toLocaleLowerCase('en') || '') || []),
milestone: pr.milestone?.title || '',
body: pr.body || '',
assignees: pr.assignees?.map(asignee => asignee?.login || '') || [],
assignees: pr.assignees?.map(assignee => assignee?.login || '') || [],
requestedReviewers: pr.requested_reviewers?.map(reviewer => reviewer?.login || '') || [],
approvedReviewers: [],
reviews: undefined,
status
status,
committers: []
})

private mapComment = (comment: Unpacked<PullReviewsData>): CommentInfo => ({
Expand Down
14 changes: 12 additions & 2 deletions src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], o
const deduplicatedMap = new Map<string, PullRequestInfo>()
const unmatched: PullRequestInfo[] = []
for (const pr of prs) {
const extracted = extractValues(pr, extractor, 'dupliate_filter')
const extracted = extractValues(pr, extractor, 'duplicate_filter')
if (extracted !== null && extracted.length > 0) {
deduplicatedMap.set(extracted[0], pr)
} else {
Expand Down Expand Up @@ -275,6 +275,15 @@ export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], o
}
core.info(`✒️ Wrote ${ignoredPrs.length} ignored pull requests down`)

// create contributors list
let contributors: string[] = []
for (const pr of prs) {
contributors = contributors.concat(pr.committers)
}
const contributorsString = contributors.join(', ')

core.setOutput('contributors', contributorsString)

// fill template
const placeholderMap = new Map<string, string>()
placeholderMap.set('CHANGELOG', changelog)
Expand All @@ -286,6 +295,7 @@ export function buildChangelog(diffInfo: DiffInfo, origPrs: PullRequestInfo[], o
placeholderMap.set('UNCATEGORIZED_COUNT', uncategorizedPrs.length.toString())
placeholderMap.set('OPEN_COUNT', openPrs.length.toString())
placeholderMap.set('IGNORED_COUNT', ignoredPrs.length.toString())
placeholderMap.set('CONTRIBUTORS', contributors)
// code change placeholders
placeholderMap.set('CHANGED_FILES', diffInfo.changedFiles.toString())
placeholderMap.set('ADDITIONS', diffInfo.additions.toString())
Expand Down Expand Up @@ -513,7 +523,7 @@ function handlePlaceholder(
if (transformer?.pattern) {
const extractedValue = transformStringToOptionalValue(value, transformer)
// note: `.replace` will return the full string again if there was no match
// note: This is mostly backwards compatiblity
// note: This is mostly backwards compatibility
if (extractedValue && ((transformer.method && transformer.method !== 'replace') || extractedValue !== value)) {
if (placeholderPrMap) {
createOrSet(placeholderPrMap, placeholder.name, extractedValue)
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ function readConfiguration(filename: string): Configuration | undefined {
*/
export function parseConfiguration(config: string): Configuration | undefined {
try {
// for compatiblity with the `yml` file we require to use `#{{}}` instead of `${{}}` - replace it here.
// for compatibility with the `yml` file we require to use `#{{}}` instead of `${{}}` - replace it here.
const configurationJSON: Configuration = JSON.parse(config.replace(/\${{/g, '#{{'))
return configurationJSON
} catch (error) {
Expand Down