@@ -111,12 +111,12 @@ jobs:
|
||||
steps:
|
||||
- name: Build Changelog
|
||||
id: github_release
|
||||
uses: mikepenz/release-changelog-builder-action@v1
|
||||
uses: mikepenz/release-changelog-builder-action@v3
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create Release
|
||||
uses: actions/create-release@v1
|
||||
uses: actions/create-release@v3
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
@@ -179,7 +179,10 @@ This configuration is a `.json` file in the following format.
|
||||
"ignore_labels": [
|
||||
"ignore"
|
||||
],
|
||||
"sort": "ASC",
|
||||
"sort": {
|
||||
"order": "ASC",
|
||||
"on_property": "mergedAt"
|
||||
},
|
||||
"template": "${{CHANGELOG}}\n\n<details>\n<summary>Uncategorized</summary>\n\n${{UNCATEGORIZED}}\n</details>",
|
||||
"pr_template": "- ${{TITLE}}\n - PR: #${{NUMBER}}",
|
||||
"empty_template": "- no changes",
|
||||
@@ -327,7 +330,9 @@ Table of descriptions for the `configuration.json` options to configure the resu
|
||||
| category.exclude_labels | Similar to `labels`, an array of labels to match PRs against, but if a match occurs the PR is excluded from this category. |
|
||||
| category.exhaustive | Will require all labels defined within this category to be present on the matching PR. |
|
||||
| ignore_labels | An array of labels, to match pull request labels against. If any PR label overlaps, the pull request will be ignored from the changelog. This takes precedence over category labels |
|
||||
| sort | The sort order of pull requests. [ASC, DESC] |
|
||||
| sort | A `sort` specification, offering the ability to define sort order and property. |
|
||||
| sort.order | The sort order. Allowed values: `ASC`, `DESC` |
|
||||
| sort.on_property | The property to sort on. Allowed values: `mergedAt`, `title` |
|
||||
| template | Specifies the global template to pick for creating the changelog. See [Template placeholders](#template-placeholders) for possible values |
|
||||
| pr_template | Defines the per pull request template. See [PR Template placeholders](#pr-template-placeholders) for possible values |
|
||||
| empty_template | Template to pick if no changes are detected. See [Template placeholders](#template-placeholders) for possible values |
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@ Patches will be released to the latest major version.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report (suspected) security vulnerabilities to mikepenz+osss@gmail.com. If the issue is confirmed, we will release a patch as soon as possible depending on complexity.
|
||||
Please report (suspected) security vulnerabilities to opensource-sec@mikepenz.dev. If the issue is confirmed, we will release a patch as soon as possible depending on complexity.
|
||||
|
||||
@@ -172,6 +172,54 @@ it('Should match ordered DESC', async () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('Should match ordered by title ASC', async () => {
|
||||
const configuration = resolveConfiguration(
|
||||
'',
|
||||
'configs_test/configuration_sort_title_asc.json'
|
||||
)
|
||||
const releaseNotes = new ReleaseNotes(octokit, {
|
||||
owner: 'mikepenz',
|
||||
repo: 'release-changelog-builder-action',
|
||||
fromTag: 'v0.3.0',
|
||||
toTag: 'v0.5.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
|
||||
const changeLog = await releaseNotes.pull()
|
||||
console.log(changeLog)
|
||||
expect(changeLog).toStrictEqual(
|
||||
`## 🚀 Features\n\nEnhanced action logs\nImprove README\nImproved configuration failure handling\nImproved defaults if no configuration is provided\nIntroduce additional placeholders [milestone, labels, assignees, reviewers]\n\n## 🐛 Fixes\n\nImproved handling for non existing tags\n\n`
|
||||
)
|
||||
})
|
||||
|
||||
it('Should match ordered by title DESC', async () => {
|
||||
const configuration = resolveConfiguration(
|
||||
'',
|
||||
'configs_test/configuration_sort_title_desc.json'
|
||||
)
|
||||
const releaseNotes = new ReleaseNotes(octokit, {
|
||||
owner: 'mikepenz',
|
||||
repo: 'release-changelog-builder-action',
|
||||
fromTag: 'v0.3.0',
|
||||
toTag: 'v0.5.0',
|
||||
includeOpen: false,
|
||||
failOnError: false,
|
||||
fetchReviewers: false,
|
||||
commitMode: false,
|
||||
configuration
|
||||
})
|
||||
|
||||
const changeLog = await releaseNotes.pull()
|
||||
console.log(changeLog)
|
||||
expect(changeLog).toStrictEqual(
|
||||
`## 🚀 Features\n\nIntroduce additional placeholders [milestone, labels, assignees, reviewers]\nImproved defaults if no configuration is provided\nImproved configuration failure handling\nImprove README\nEnhanced action logs\n\n## 🐛 Fixes\n\nImproved handling for non existing tags\n\n`
|
||||
)
|
||||
})
|
||||
|
||||
it('Should ignore PRs not merged into develop branch', async () => {
|
||||
const configuration = resolveConfiguration(
|
||||
'',
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"sort": "DESC",
|
||||
"sort": {
|
||||
"order": "DESC",
|
||||
"on_property": "mergedAt"
|
||||
},
|
||||
"pr_template": "${{NUMBER}}",
|
||||
"max_pull_requests": 1000,
|
||||
"max_back_track_time_days": 1000
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"sort": {
|
||||
"order": "ASC",
|
||||
"on_property": "title"
|
||||
},
|
||||
"pr_template": "${{TITLE}}",
|
||||
"max_pull_requests": 1000,
|
||||
"max_back_track_time_days": 1000
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"sort": {
|
||||
"order": "DESC",
|
||||
"on_property": "title"
|
||||
},
|
||||
"pr_template": "${{TITLE}}",
|
||||
"max_pull_requests": 1000,
|
||||
"max_back_track_time_days": 1000
|
||||
}
|
||||
+2865
-2451
File diff suppressed because it is too large
Load Diff
+1
-1
File diff suppressed because one or more lines are too long
Generated
+2330
-2325
File diff suppressed because it is too large
Load Diff
+14
-13
@@ -32,28 +32,29 @@
|
||||
"author": "Mike Penz",
|
||||
"license": "Apache 2.0",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.6.0",
|
||||
"@actions/core": "^1.8.1",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/github": "^5.0.1",
|
||||
"@actions/github": "^5.0.2",
|
||||
"@octokit/rest": "^18.12.0",
|
||||
"@types/semver": "^7.3.9",
|
||||
"moment": "^2.29.2",
|
||||
"moment": "^2.29.3",
|
||||
"semver": "^7.3.7",
|
||||
"webpack": "^5.72.0"
|
||||
"tunnel": "^0.0.6",
|
||||
"webpack": "^5.72.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.4.1",
|
||||
"@typescript-eslint/parser": "^5.19.0",
|
||||
"@types/node": "^17.0.24",
|
||||
"@types/jest": "^27.5.1",
|
||||
"@types/node": "^17.0.33",
|
||||
"@typescript-eslint/parser": "^5.23.0",
|
||||
"@vercel/ncc": "^0.33.4",
|
||||
"eslint": "^8.13.0",
|
||||
"eslint": "^8.15.0",
|
||||
"eslint-plugin-github": "^4.3.6",
|
||||
"eslint-plugin-jest": "^26.1.4",
|
||||
"jest": "^27.5.1",
|
||||
"jest-circus": "^27.5.1",
|
||||
"eslint-plugin-jest": "^26.1.5",
|
||||
"jest": "^28.1.0",
|
||||
"jest-circus": "^28.1.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"prettier": "2.6.2",
|
||||
"ts-jest": "^27.1.4",
|
||||
"typescript": "^4.6.3"
|
||||
"ts-jest": "^28.0.2",
|
||||
"typescript": "^4.6.4"
|
||||
}
|
||||
}
|
||||
|
||||
+11
-2
@@ -3,7 +3,7 @@ export interface Configuration {
|
||||
max_pull_requests: number
|
||||
max_back_track_time_days: number
|
||||
exclude_merge_branches: string[]
|
||||
sort: string // "ASC" or "DESC"
|
||||
sort: Sort | string // "ASC" or "DESC"
|
||||
template: string
|
||||
pr_template: string
|
||||
empty_template: string
|
||||
@@ -23,6 +23,11 @@ export interface Category {
|
||||
exhaustive?: boolean // requires all labels to be present in the PR
|
||||
}
|
||||
|
||||
export interface Sort {
|
||||
order: 'ASC' | 'DESC' // the sorting order
|
||||
on_property: 'mergedAt' | 'title' // the property to sort on. (mergedAt falls back to createdAt)
|
||||
}
|
||||
|
||||
export interface Regex {
|
||||
pattern: string // the regex pattern to match
|
||||
flags?: string // the regex flag to use for RegExp
|
||||
@@ -55,7 +60,11 @@ export const DefaultConfiguration: Configuration = {
|
||||
max_pull_requests: 200, // the amount of pull requests to process
|
||||
max_back_track_time_days: 365, // allow max of 365 days back to check up on pull requests
|
||||
exclude_merge_branches: [], // branches to exclude from counting as PRs (e.g. YourOrg/qa, YourOrg/main)
|
||||
sort: 'ASC', // sorting order for filling the changelog (ASC or DESC) supported
|
||||
sort: {
|
||||
// defines the sorting logic for PRs
|
||||
order: 'ASC', // the sorting order
|
||||
on_property: 'mergedAt' // the property to sort on. (mergedAt falls back to createdAt)
|
||||
},
|
||||
template: '${{CHANGELOG}}', // the global template to host the changelog
|
||||
pr_template: '- ${{TITLE}}\n - PR: #${{NUMBER}}', // the per PR template to pick
|
||||
empty_template: '- no changes', // the template to use if no pull requests are found
|
||||
|
||||
+47
-22
@@ -2,6 +2,7 @@ import * as core from '@actions/core'
|
||||
import {Octokit, RestEndpointMethodTypes} from '@octokit/rest'
|
||||
import {Unpacked} from './utils'
|
||||
import moment from 'moment'
|
||||
import {Sort} from './configuration'
|
||||
|
||||
export interface PullRequestInfo {
|
||||
number: number
|
||||
@@ -89,11 +90,11 @@ export class PullRequests {
|
||||
}
|
||||
|
||||
// bail out early to not keep iterating on PRs super old
|
||||
return sortPullRequests(mergedPRs, true)
|
||||
return sortPrs(mergedPRs)
|
||||
}
|
||||
}
|
||||
|
||||
return sortPullRequests(mergedPRs, true)
|
||||
return sortPrs(mergedPRs)
|
||||
}
|
||||
|
||||
async getOpen(
|
||||
@@ -125,11 +126,11 @@ export class PullRequests {
|
||||
}
|
||||
|
||||
// bail out early to not keep iterating on PRs super old
|
||||
return sortPullRequests(openPrs, true)
|
||||
return sortPrs(openPrs)
|
||||
}
|
||||
}
|
||||
|
||||
return sortPullRequests(openPrs, true)
|
||||
return sortPrs(openPrs)
|
||||
}
|
||||
|
||||
async getReviewers(
|
||||
@@ -155,36 +156,60 @@ export class PullRequests {
|
||||
}
|
||||
}
|
||||
|
||||
function sortPrs(pullRequests: PullRequestInfo[]): PullRequestInfo[] {
|
||||
return sortPullRequests(pullRequests, {
|
||||
order: 'ASC',
|
||||
on_property: 'mergedAt'
|
||||
})
|
||||
}
|
||||
|
||||
export function sortPullRequests(
|
||||
pullRequests: PullRequestInfo[],
|
||||
ascending: Boolean
|
||||
sort: Sort | string
|
||||
): PullRequestInfo[] {
|
||||
if (ascending) {
|
||||
let sortConfig: Sort
|
||||
|
||||
// legacy handling to support string sort config
|
||||
if (typeof sort === 'string') {
|
||||
let order: 'ASC' | 'DESC' = 'ASC'
|
||||
if (sort.toUpperCase() === 'DESC') order = 'DESC'
|
||||
sortConfig = {order, on_property: 'mergedAt'}
|
||||
} else {
|
||||
sortConfig = sort
|
||||
}
|
||||
|
||||
if (sortConfig.order === 'ASC') {
|
||||
pullRequests.sort((a, b) => {
|
||||
const aa = a.mergedAt || a.createdAt
|
||||
const bb = b.mergedAt || b.createdAt
|
||||
if (aa.isBefore(bb)) {
|
||||
return -1
|
||||
} else if (bb.isBefore(aa)) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
return compare(a, b, sortConfig)
|
||||
})
|
||||
} else {
|
||||
pullRequests.sort((b, a) => {
|
||||
const aa = a.mergedAt || a.createdAt
|
||||
const bb = b.mergedAt || b.createdAt
|
||||
if (aa.isBefore(bb)) {
|
||||
return -1
|
||||
} else if (bb.isBefore(aa)) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
return compare(a, b, sortConfig)
|
||||
})
|
||||
}
|
||||
return pullRequests
|
||||
}
|
||||
|
||||
export function compare(
|
||||
a: PullRequestInfo,
|
||||
b: PullRequestInfo,
|
||||
sort: Sort
|
||||
): number {
|
||||
if (sort.on_property === 'mergedAt') {
|
||||
const aa = a.mergedAt || a.createdAt
|
||||
const bb = b.mergedAt || b.createdAt
|
||||
if (aa.isBefore(bb)) {
|
||||
return -1
|
||||
} else if (bb.isBefore(aa)) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
} else {
|
||||
// only else for now `label`
|
||||
return a.title.localeCompare(b.title)
|
||||
}
|
||||
}
|
||||
|
||||
// helper function to add a special open label to prs not merged.
|
||||
function attachSpeciaLabels(
|
||||
status: 'open' | 'merged',
|
||||
|
||||
+2
-3
@@ -15,8 +15,7 @@ export function buildChangelog(
|
||||
// sort to target order
|
||||
const config = options.configuration
|
||||
const sort = config.sort || DefaultConfiguration.sort
|
||||
const sortAsc = sort.toUpperCase() === 'ASC'
|
||||
prs = sortPullRequests(prs, sortAsc)
|
||||
prs = sortPullRequests(prs, sort)
|
||||
core.info(`ℹ️ Sorted all pull requests ascending: ${sort}`)
|
||||
|
||||
// drop duplicate pull requests
|
||||
@@ -44,7 +43,7 @@ export function buildChangelog(
|
||||
core.info(
|
||||
`ℹ️ Removed ${removedElements} pull requests during deduplication`
|
||||
)
|
||||
prs = sortPullRequests(deduplicatedPRs, sortAsc) // resort deduplicatedPRs
|
||||
prs = sortPullRequests(deduplicatedPRs, sort) // resort deduplicatedPRs
|
||||
} else {
|
||||
core.warning(`⚠️ Configured \`duplicate_filter\` invalid.`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user