Skip to content

Commit f8e375e

Browse files
authored
feat: allow pass custom tag (#63)
1 parent 36e996b commit f8e375e

File tree

4 files changed

+61
-18
lines changed

4 files changed

+61
-18
lines changed

cli.mjs

100644100755
File mode changed.

src/config.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import type { ChangelogOptions, ResolvedChangelogOptions } from './types'
2-
import { getCurrentGitBranch, getFirstGitCommit, getGitHubRepo, getLastMatchingTag, isPrerelease } from './git'
2+
import { getCurrentGitBranch, getFirstGitCommit, getGitHubRepo, getLastMatchingTag, getSafeTagTemplate, isPrerelease } from './git'
33

44
export function defineConfig(config: ChangelogOptions) {
55
return config
66
}
77

8-
const defaultConfig: ChangelogOptions = {
8+
const defaultConfig = {
99
scopeMap: {},
1010
types: {
1111
feat: { title: '🚀 Features' },
@@ -18,7 +18,8 @@ const defaultConfig: ChangelogOptions = {
1818
contributors: true,
1919
capitalize: true,
2020
group: true,
21-
}
21+
tag: 'v%s',
22+
} satisfies ChangelogOptions
2223

2324
export async function resolveConfig(options: ChangelogOptions) {
2425
const { loadConfig } = await import('c12')
@@ -33,7 +34,12 @@ export async function resolveConfig(options: ChangelogOptions) {
3334
config.baseUrlApi = config.baseUrlApi ?? 'api.github.com'
3435
config.to = config.to || await getCurrentGitBranch()
3536
config.tagFilter = config.tagFilter ?? (() => true)
36-
config.from = config.from || await getLastMatchingTag(config.to, config.tagFilter) || await getFirstGitCommit()
37+
config.tag = getSafeTagTemplate(config.tag ?? defaultConfig.tag)
38+
config.from = config.from || await getLastMatchingTag(
39+
config.to,
40+
config.tagFilter,
41+
config.tag,
42+
) || await getFirstGitCommit()
3743
// @ts-expect-error backward compatibility
3844
config.repo = config.repo || config.github || await getGitHubRepo(config.baseUrl)
3945
// @ts-expect-error backward compatibility

src/git.ts

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,37 +18,64 @@ export async function isRepoShallow() {
1818
return (await execCommand('git', ['rev-parse', '--is-shallow-repository'])).trim() === 'true'
1919
}
2020

21-
export async function getGitTags() {
22-
return (await execCommand('git', ['--no-pager', 'tag', '-l', '--sort=creatordate']).then(r => r.split('\n')))
23-
.reverse()
21+
export function getSafeTagTemplate(template: string) {
22+
return template.includes('%s') ? template : `${template}%s`
2423
}
2524

26-
function getTagWithoutPrefix(tag: string) {
27-
return tag.replace(/^v/, '')
25+
function getVersionString(template: string, tag: string) {
26+
const pattern = template.replace(/%s/g, '(.+)')
27+
const regex = new RegExp(`^${pattern}$`)
28+
const match = regex.exec(tag)
29+
return match ? match[1] : tag
2830
}
2931

30-
export async function getLastMatchingTag(inputTag: string, tagFilter: (tag: string) => boolean) {
31-
const inputTagWithoutPrefix = getTagWithoutPrefix(inputTag)
32-
const isVersion = semver.valid(inputTagWithoutPrefix) !== null
33-
const isPrerelease = semver.prerelease(inputTag) !== null
32+
export async function getGitTags() {
33+
const output = await execCommand('git', [
34+
'log',
35+
'--simplify-by-decoration',
36+
'--pretty=format:"%d"',
37+
])
38+
39+
const tagRegex = /tag: ([^,)]+)/g
40+
const tagList: string[] = []
41+
let match
42+
43+
while (match !== null) {
44+
const tag = match?.[1].trim()
45+
if (tag) {
46+
tagList.push(tag)
47+
}
48+
match = tagRegex.exec(output)
49+
}
50+
51+
return tagList
52+
}
53+
54+
export async function getLastMatchingTag(
55+
inputTag: string,
56+
tagFilter: (tag: string) => boolean,
57+
tagTemplate: string,
58+
) {
59+
const inputVersionString = getVersionString(tagTemplate, inputTag)
60+
const isVersion = semver.valid(inputVersionString) !== null
61+
const isPrerelease = semver.prerelease(inputVersionString) !== null
3462
const tags = await getGitTags()
3563
const filteredTags = tags.filter(tagFilter)
3664

3765
let tag: string | undefined
3866
// Doing a stable release, find the last stable release to compare with
3967
if (!isPrerelease && isVersion) {
4068
tag = filteredTags.find((tag) => {
41-
const tagWithoutPrefix = getTagWithoutPrefix(tag)
69+
const versionString = getVersionString(tagTemplate, tag)
4270

43-
return tagWithoutPrefix !== inputTagWithoutPrefix
44-
&& semver.valid(tagWithoutPrefix) !== null
45-
&& semver.prerelease(tagWithoutPrefix) === null
71+
return versionString !== inputVersionString
72+
&& semver.valid(versionString) !== null
73+
&& semver.prerelease(versionString) === null
4674
})
4775
}
4876

4977
// Fallback to the last tag, that are not the input tag
5078
tag ||= filteredTags.find(tag => tag !== inputTag)
51-
5279
return tag
5380
}
5481

src/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,16 @@ export interface ChangelogOptions extends Partial<ChangelogenOptions> {
8484
* Release repository, defaults to `repo`
8585
*/
8686
releaseRepo?: RepoConfig | string
87+
88+
/**
89+
* Can be set to a custom tag string
90+
* Any `%s` placeholders in the tag string will be replaced
91+
* If the tag string does _not_ contain any `%s` placeholders,
92+
* then the version number will be appended to the tag.
93+
*
94+
* @default `v%s`.
95+
*/
96+
tag?: string
8797
}
8898

8999
export type ResolvedChangelogOptions = Required<ChangelogOptions>

0 commit comments

Comments
 (0)