Skip to content

Add --tag-parser-pattern & --tag-parser-replacement for unconventional tag coercion into valid semver. #222

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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
103 changes: 59 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,41 @@ Usage: auto-changelog [options]

Options:

-o, --output [file] # output file, default: CHANGELOG.md
-c, --config [file] # config file location, default: .auto-changelog
-t, --template [template] # specify template to use [compact, keepachangelog, json], default: compact
-r, --remote [remote] # specify git remote to use for links, default: origin
-p, --package # use version from package.json as latest release
-v, --latest-version [version] # use specified version as latest release
-u, --unreleased # include section for unreleased changes
-l, --commit-limit [count] # number of commits to display per release, default: 3
-b, --backfill-limit [count] # number of commits to backfill empty releases with, default: 3
--commit-url [url] # override url for commits, use {id} for commit id
--issue-url [url] # override url for issues, use {id} for issue id
--merge-url [url] # override url for merges, use {id} for merge id
--compare-url [url] # override url for compares, use {from} and {to} for tags
--issue-pattern [regex] # override regex pattern for issues in commit messages
--breaking-pattern [regex] # regex pattern for breaking change commits
--merge-pattern [regex] # add custom regex pattern for merge commits
--ignore-commit-pattern [regex] # pattern to ignore when parsing commits
--tag-pattern [regex] # override regex pattern for version tags
--tag-prefix [prefix] # prefix used in version tags, default: v
--starting-version [tag] # specify earliest version to include in changelog
--starting-date [yyyy-mm-dd] # specify earliest date to include in changelog
--sort-commits [property] # sort commits by property [relevance, date, date-desc, subject, subject-desc], default: relevance
--release-summary # display tagged commit message body as release summary
--unreleased-only # only output unreleased changes
--hide-empty-releases # hide empty releases
--hide-credit # hide auto-changelog credit
--handlebars-setup [file] # handlebars setup file
--append-git-log [string] # string to append to git log command
--append-git-tag [string] # string to append to git tag command
--prepend # prepend changelog to output file
--stdout # output changelog to stdout
-V, --version # output the version number
-h, --help # output usage information
-o, --output [file] # output file, default: CHANGELOG.md
-c, --config [file] # config file location, default: .auto-changelog
-t, --template [template] # specify template to use [compact, keepachangelog, json], default: compact
-r, --remote [remote] # specify git remote to use for links, default: origin
-p, --package # use version from package.json as latest release
-v, --latest-version [version] # use specified version as latest release
-u, --unreleased # include section for unreleased changes
-l, --commit-limit [count] # number of commits to display per release, default: 3
-b, --backfill-limit [count] # number of commits to backfill empty releases with, default: 3
--commit-url [url] # override url for commits, use {id} for commit id
--issue-url [url] # override url for issues, use {id} for issue id
--merge-url [url] # override url for merges, use {id} for merge id
--compare-url [url] # override url for compares, use {from} and {to} for tags
--issue-pattern [regex] # override regex pattern for issues in commit messages
--breaking-pattern [regex] # regex pattern for breaking change commits
--merge-pattern [regex] # add custom regex pattern for merge commits
--ignore-commit-pattern [regex] # pattern to ignore when parsing commits
--tag-pattern [regex] # override regex pattern for version tags
--tag-prefix [prefix] # prefix used in version tags, default: v
--tag-parser-pattern [regex] # pattern used to capture values in tags for replacement
--tag-parser-replacement [string] # replaces captures supplied in the --tag-parser-pattern option to create valid semver tags
Comment on lines +43 to +44
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documents option usage.

--starting-version [tag] # specify earliest version to include in changelog
--starting-date [yyyy-mm-dd] # specify earliest date to include in changelog
--sort-commits [property] # sort commits by property [relevance, date, date-desc, subject, subject-desc], default: relevance
--release-summary # display tagged commit message body as release summary
--unreleased-only # only output unreleased changes
--hide-empty-releases # hide empty releases
--hide-credit # hide auto-changelog credit
--handlebars-setup [file] # handlebars setup file
--append-git-log [string] # string to append to git log command
--append-git-tag [string] # string to append to git tag command
--prepend # prepend changelog to output file
--stdout # output changelog to stdout
-V, --version # output the version number
-h, --help # output usage information


# Write log to CHANGELOG.md in current directory
Expand Down Expand Up @@ -158,15 +160,6 @@ You can also store config options in an `.auto-changelog` file in your project r

Note that any options set in `package.json` will take precedence over any set in `.auto-changelog`.

#### Tag prefixes

Use `--tag-prefix [prefix]` if you prefix your version tags with a certain string:

```bash
# When all versions are tagged like my-package/1.2.3
auto-changelog --tag-prefix my-package/
```

#### Tag patterns

By default, `auto-changelog` looks for valid semver tags to build a list of releases. If you are using another format (or want to include all tags), use `--tag-pattern [regex]`:
Expand All @@ -179,6 +172,28 @@ auto-changelog --tag-pattern build-\d+
auto-changelog --tag-pattern .+
```

Including tags using this method does not gurantee correct tag order however since the sorting compares semver versions rather than their date or alphabetical order. For proper custom tag order, please refer to the [Tag parsing](#tag-parsing) section. Note, if a tag can be properly coerced into a valid semver string via the parsing options, the `--tag-pattern` is no longer needed.

#### Tag parsing

There are 2 mechanisms that help auto-changelog properly parse your tags.

Use `--tag-prefix [prefix]` if you prefix your version tags with a certain string:

```bash
# When all versions are tagged like "my-package/1.2.3"
auto-changelog --tag-prefix my-package/
```

Alternatively, if you have an unconventional tagging pattern, you can provide a capture pattern and a replacement string through the `--tag-parser-pattern [regex]` and `--tag-parser-replacement [string]` options.

```bash
# When all versions are tagged like "[email protected] build123"
auto-changelog --tag-parser-pattern "^my-package@(\d+)\.(\d+) build(\d+)$" --tag-parser-replacement "\$1.\$2.0-build.\$3"
```

Note, both options need to be supplied for this to work. Also note, `$` signs in the replacement string must be escaped.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documents parsing strategies and their differences.

#### Breaking changes

If you use a common pattern in your commit messages for breaking changes, use `--breaking-pattern` to highlight those commits as breaking changes in your changelog. Breaking change commits will always be listed as part of a release, regardless of any `--commit-limit` set.
Expand Down Expand Up @@ -283,7 +298,7 @@ Use `{{#commit-list}}` to render a list of commits depending on certain patterns
| `heading` | A heading for the list, only renders if at least one commit matches |
| `message` | A regex pattern to match against the entire commit message |
| `subject` | A regex pattern to match against the commit subject only |
| `exclude` | A regex pattern to exclude from the list – useful for avoiding listing commits more than once |
| `exclude` | A regex pattern to exclude from the list – useful for avoiding listing commits more than once |

#### Replacing text

Expand Down Expand Up @@ -332,4 +347,4 @@ The command parses your git commit history and generates a changelog based on ta

#### Why do I need it?

Because keeping a changelog can be tedious and difficult to get right. If you don’t have the patience for a hand-crafted, bespoke changelog then this makes keeping one rather easy. It also can be [automated if you’re feeling extra lazy](#what-you-might-do-if-youre-clever).
Because keeping a changelog can be tedious and difficult to get right. If you don’t have the patience for a hand-crafted, bespoke changelog then this makes keeping one rather easy. It also can be [automated if you’re feeling extra lazy](#what-you-might-do-if-youre-clever).
2 changes: 2 additions & 0 deletions src/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const getOptions = async argv => {
.option('--ignore-commit-pattern <regex>', 'pattern to ignore when parsing commits')
.option('--tag-pattern <regex>', 'override regex pattern for version tags')
.option('--tag-prefix <prefix>', 'prefix used in version tags')
.option('--tag-parser-pattern <regex>', 'pattern used to capture values in tags for replacement')
.option('--tag-parser-replacement <string>', 'replaces captures supplied in the --tag-parser-pattern option to create valid semver tags')
Comment on lines +47 to +48
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adds options to commander.

.option('--starting-version <tag>', 'specify earliest version to include in changelog')
.option('--starting-date <yyyy-mm-dd>', 'specify earliest date to include in changelog')
.option('--sort-commits <property>', `sort commits by property [relevance, date, date-desc], default: ${DEFAULT_OPTIONS.sortCommits}`)
Expand Down
18 changes: 14 additions & 4 deletions src/tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@ const getLimit = (tags, { unreleasedOnly, startingVersion, startingDate }) => {
return tags.length
}

const parseTag = ({ tagPrefix }) => string => {
const parseTag = (options) => string => {
const [tag, date] = string.split(DIVIDER)
return {
tag,
date,
title: tag,
version: inferSemver(tag.replace(tagPrefix, ''))
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moves the tagPrefix replacement logic into the inferSemver.

version: inferSemver(tag, options)
}
}

const enrichTag = ({ getCompareLink, tagPattern }) => (t, index, tags) => {
const enrichTag = ({ getCompareLink }) => (t, index, tags) => {
const previous = tags[index + 1]
return {
isoDate: t.date.slice(0, 10),
Expand Down Expand Up @@ -94,15 +94,25 @@ const sortTags = ({ version: a }, { version: b }) => {
return a < b ? 1 : -1
}

const inferSemver = tag => {
const inferSemver = (tag, { tagPrefix, tagParserPattern, tagParserReplacement }) => {
if (!!tagParserPattern && !!tagParserReplacement) {
return tag.replace(new RegExp(tagParserPattern), tagParserReplacement)
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adds the logic for the pattern/replacement options.


if (!!tagPrefix && tag.startsWith(tagPrefix)) {
tag = tag.replace(new RegExp(`^${tagPrefix}`), '')
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are removing the "prefix" via the tagPrefix option, actually check if the tag "starts with" the supply string and only remove it from the beginning.


if (/^v?\d+$/.test(tag)) {
// v1 becomes v1.0.0
return `${tag}.0.0`
}

if (/^v?\d+\.\d+$/.test(tag)) {
// v1.0 becomes v1.0.0
return `${tag}.0`
}

return tag
}

Expand Down
15 changes: 15 additions & 0 deletions test/tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,19 @@ describe('fetchTags', () => {
'v0.1.0'
])
})

it('supports tag parsing through --tag-parser-pattern and --tag-parser-replacement', async () => {
mock('cmd', () => Promise.resolve([
'version 0.1.0 || build 2345---2000-02-01',
'version 2.1.0 || build 2355---2000-02-01',
'version 3.2.1 || build 2367---2000-02-01'
].join('\n')))

const tags = await fetchTags({ ...options, tagParserPattern: '^version (\\d+)\\.(\\d+)\\.(\\d+) \\|\\| build (\\d+)$', tagParserReplacement: '$1.$2.$3-build.$4' })
expect(tags.map(t => t.version)).to.deep.equal([
'3.2.1-build.2367',
'2.1.0-build.2355',
'0.1.0-build.2345'
])
})
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adds test.

})