diff --git a/README.md b/README.md
index d7130ee..0dc5202 100644
--- a/README.md
+++ b/README.md
@@ -61,10 +61,6 @@ Specify the action as part of your GitHub actions workflow:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
-By default the action will try to automatically retrieve the `tag` from the current commit and automatically resolve the `tag` before. Automatic previous tag resolving is done using `semver`.
-
-If you require a different versioning scheme please open an issue [issue](https://github.com/mikepenz/release-changelog-builder-action/issues). Alternative you can always specifically supply the `fromTag` via the [configuration](#advanced-workflow-specification).
-
### Action outputs
After action execution it will return the `changelog` and additional information as step output. You can use it in any follow-up step by referencing the output by referencing it via the id of the step. For example `build_changelog`.
@@ -135,40 +131,45 @@ The action supports flexible configuration options to modify vast areas of its b
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
+๐ก By default not specifying `fromTag` or `toTag` will resolve `toTag` from either the `ref` or alternatively fallback to the latest tag from git. `fromTag` is resolved by sorting tags using [semver](https://semver.org/). Check the [configuration](#configuration-specification) for alternatives.
+
This configuration is a `.json` file in the following format.
```json
{
"categories": [
- {
- "title": "## ๐ Features",
- "labels": ["feature"]
- },
- {
- "title": "## ๐ Fixes",
- "labels": ["fix"]
- },
- {
- "title": "## ๐งช Tests",
- "labels": ["test"]
- }
+ {
+ "title": "## ๐ Features",
+ "labels": ["feature"]
+ },
+ {
+ "title": "## ๐ Fixes",
+ "labels": ["fix"]
+ },
+ {
+ "title": "## ๐งช Tests",
+ "labels": ["test"]
+ }
],
"sort": "ASC",
"template": "${{CHANGELOG}}\n\n\nUncategorized
\n\n${{UNCATEGORIZED}}\n ",
"pr_template": "- ${{TITLE}}\n - PR: #${{NUMBER}}",
"empty_template": "- no changes",
"transformers": [
- {
- "pattern": "[\\-\\*] (\\[(...|TEST|CI|SKIP)\\])( )?(.+?)\n(.+?[\\-\\*] )(.+)",
- "target": "- $4\n - $6"
- }
+ {
+ "pattern": "[\\-\\*] (\\[(...|TEST|CI|SKIP)\\])( )?(.+?)\n(.+?[\\-\\*] )(.+)",
+ "target": "- $4\n - $6"
+ }
],
"max_tags_to_fetch": 200,
"max_pull_requests": 200,
"max_back_track_time_days": 90,
"exclude_merge_branches": [
- "Owner/qa"
- ]
+ "Owner/qa"
+ ],
+ "tag_resolver": {
+ "method": "semver"
+ }
}
```
@@ -266,6 +267,8 @@ Table of descriptions for the `configuration.json` options.
| max_pull_requests | The maximum amount of pull requests to load from the API. Loaded paginated with 30 per page |
| max_back_track_time_days | Defines the max amount of days to go back in time per changelog |
| exclude_merge_branches | An array of branches to be ignored from processing as merge commits |
+| tag_resolver | Section to provide configuration for the tag resolving logic. Used if no `fromTag` is provided |
+| tag_resolver.method | Defines the method to use. Current options are: `semver`, `sort`. Default: `semver` |
## Contribute ๐งฌ
diff --git a/__tests__/tags.test.ts b/__tests__/tags.test.ts
index d82b09c..edaeab1 100644
--- a/__tests__/tags.test.ts
+++ b/__tests__/tags.test.ts
@@ -2,7 +2,7 @@ import { resolveConfiguration } from '../src/utils';
import { ReleaseNotesBuilder } from '../src/releaseNotesBuilder';
import { TagInfo, sortTags } from '../src/tags';
-it('Should order tags correctly', async () => {
+it('Should order tags correctly using semver', async () => {
jest.setTimeout(180000)
const tags: TagInfo[] = [
@@ -16,7 +16,10 @@ it('Should order tags correctly', async () => {
{ name: "v2020.3.0", commit: "" }
]
- const sorted = sortTags(tags).map(function (tag) {
+ const tagResolver = {
+ method: "semver"
+ }
+ const sorted = sortTags(tags, tagResolver).map(function (tag) {
return tag.name
}).join(",")
@@ -24,7 +27,7 @@ it('Should order tags correctly', async () => {
})
-it('Should order tags correctly', async () => {
+it('Should order tags correctly using semver', async () => {
jest.setTimeout(180000)
const tags: TagInfo[] = [
@@ -43,9 +46,42 @@ it('Should order tags correctly', async () => {
{ name: "1000.0.0", commit: "" },
]
- const sorted = sortTags(tags).map(function (tag) {
+ const tagResolver = {
+ method: "non-existing-method"
+ }
+ const sorted = sortTags(tags, tagResolver).map(function (tag) {
return tag.name
}).join(",")
expect(sorted).toStrictEqual(`1000.0.0,100.0.0,20.0.2,10.1.0,10.1.0-2,10.0.0,2.0.0,1.0.0,1.0.0-a01,0.1.0,0.1.0-b01,0.0.1,0.0.1-rc01`)
+})
+
+
+it('Should order tags alphabetical', async () => {
+ jest.setTimeout(180000)
+
+ const tags: TagInfo[] = [
+ { name: "0.0.1", commit: "" },
+ { name: "0.0.1-rc01", commit: "" },
+ { name: "0.1.0-b01", commit: "" },
+ { name: "1.0.0", commit: "" },
+ { name: "a", commit: "" },
+ { name: "1.0.0-a01", commit: "" },
+ { name: "2.0.0", commit: "" },
+ { name: "10.0.0", commit: "" },
+ { name: "v1", commit: "" },
+ { name: "10.1.0", commit: "" },
+ { name: "10.1.0-2", commit: "" },
+ { name: "20.0.2", commit: "" },
+ { name: "1000.0.0", commit: "" },
+ ]
+
+ const tagResolver = {
+ method: "sort"
+ }
+ const sorted = sortTags(tags, tagResolver).map(function (tag) {
+ return tag.name
+ }).join(",")
+
+ expect(sorted).toStrictEqual(`a,20.0.2,2.0.0,1000.0.0,10.1.0,10.1.0-2,10.0.0,1.0.0,1.0.0-a01,v1,0.1.0-b01,0.0.1,0.0.1-rc01`)
})
\ No newline at end of file
diff --git a/src/configuration.ts b/src/configuration.ts
index e2c25af..4e421f3 100644
--- a/src/configuration.ts
+++ b/src/configuration.ts
@@ -9,6 +9,7 @@ export interface Configuration {
empty_template: string
categories: Category[]
transformers: Transformer[]
+ tag_resolver: TagResolver
}
export interface Category {
@@ -21,6 +22,10 @@ export interface Transformer {
target: string
}
+export interface TagResolver {
+ method: string // semver, sort
+}
+
export const DefaultConfiguration: Configuration = {
max_tags_to_fetch: 200, // the amount of tags to fetch from the github API
max_pull_requests: 200, // the amount of pull requests to process
@@ -44,5 +49,9 @@ export const DefaultConfiguration: Configuration = {
labels: ['test']
}
], // the categories to support for the ordering
- transformers: [] // transformers to apply on the PR description according to the `pr_template`
+ 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.
+ }
}
diff --git a/src/releaseNotesBuilder.ts b/src/releaseNotesBuilder.ts
index 2b9aafd..a229a79 100644
--- a/src/releaseNotesBuilder.ts
+++ b/src/releaseNotesBuilder.ts
@@ -83,7 +83,8 @@ export class ReleaseNotesBuilder {
this.toTag,
this.ignorePreReleases,
this.configuration.max_tags_to_fetch ||
- DefaultConfiguration.max_tags_to_fetch
+ DefaultConfiguration.max_tags_to_fetch,
+ this.configuration.tag_resolver || DefaultConfiguration.tag_resolver
)
if (previousTag == null) {
failOrError(
diff --git a/src/tags.ts b/src/tags.ts
index 4bd1c17..07c7b5d 100755
--- a/src/tags.ts
+++ b/src/tags.ts
@@ -2,6 +2,7 @@ import {Octokit, RestEndpointMethodTypes} from '@octokit/rest'
import * as core from '@actions/core'
import * as semver from 'semver'
import {SemVer} from 'semver'
+import {TagResolver} from './configuration'
export interface TagInfo {
name: string
@@ -52,9 +53,13 @@ export class Tags {
repo: string,
tag: string,
ignorePreReleases: boolean,
- maxTagsToFetch: number
+ maxTagsToFetch: number,
+ tagResolver: TagResolver
): Promise {
- const tags = sortTags(await this.getTags(owner, repo, maxTagsToFetch))
+ const tags = sortTags(
+ await this.getTags(owner, repo, maxTagsToFetch),
+ tagResolver
+ )
try {
const length = tags.length
@@ -94,12 +99,22 @@ export class Tags {
2020.3.1-a01
2020.3.0
*/
-export function sortTags(tags: TagInfo[]): TagInfo[] {
+export function sortTags(tags: TagInfo[], tagResolver: TagResolver): TagInfo[] {
+ if (tagResolver.method === 'sort') {
+ return stringSorting(tags)
+ } else {
+ return semVerSorting(tags)
+ }
+}
+
+function semVerSorting(tags: TagInfo[]): TagInfo[] {
// filter out tags which do not follow semver
const validatedTags = tags.filter(tag => {
const isValid = semver.valid(tag.name) !== null
- if(!isValid) {
- core.debug(`โ ๏ธ dropped tag ${tag.name} because it is not a valid semver tag`)
+ if (!isValid) {
+ core.debug(
+ `โ ๏ธ dropped tag ${tag.name} because it is not a valid semver tag`
+ )
}
return isValid
})
@@ -110,3 +125,22 @@ export function sortTags(tags: TagInfo[]): TagInfo[] {
})
return validatedTags
}
+
+function stringSorting(tags: TagInfo[]): TagInfo[] {
+ return tags.sort((b, a) => {
+ const partsA = a.name.replace(/^v/, '').split('-')
+ const partsB = b.name.replace(/^v/, '').split('-')
+ const versionCompare = partsA[0].localeCompare(partsB[0])
+ if (versionCompare !== 0) {
+ return versionCompare
+ } else {
+ if (partsA.length === 1) {
+ return 0
+ } else if (partsB.length === 1) {
+ return 1
+ } else {
+ return partsA[1].localeCompare(partsB[1])
+ }
+ }
+ })
+}