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

Allow providing a list of characters to escape in PR titles #611

Merged
merged 47 commits into from Aug 27, 2020
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
f17c5c5
Update schema.js
Happypig375 Aug 1, 2020
8faac16
Update default-config.js
Happypig375 Aug 1, 2020
dd8e1a0
Update template.js
Happypig375 Aug 1, 2020
1b14e17
Update template.js
Happypig375 Aug 1, 2020
ec0dcc6
Update releases.js
Happypig375 Aug 1, 2020
b14560f
Update README.md
Happypig375 Aug 1, 2020
ff7f545
Update schema.test.js
Happypig375 Aug 1, 2020
3e23e99
Format tables correctly
Happypig375 Aug 1, 2020
9aebd72
Update schema.json
Happypig375 Aug 1, 2020
124721e
Update schema.js
Happypig375 Aug 1, 2020
54ffe45
Revert template.js
Happypig375 Aug 1, 2020
00d48d5
Update releases.js
Happypig375 Aug 1, 2020
ba486c5
Update schema.json
Happypig375 Aug 1, 2020
6c73b59
Create config-with-change-title-escapes.yml
Happypig375 Aug 1, 2020
67a3f3d
Create config-with-change-title-escapes-backticks.yml
Happypig375 Aug 1, 2020
f1e7147
Update config-with-change-title-escapes.yml
Happypig375 Aug 1, 2020
5a3ddc6
Update config-with-change-title-escapes-backticks.yml
Happypig375 Aug 1, 2020
10448f0
Update README.md
Happypig375 Aug 1, 2020
bacd06b
Update README.md
Happypig375 Aug 1, 2020
42eec9b
Update releases.js
Happypig375 Aug 1, 2020
97f257f
Update README.md
Happypig375 Aug 1, 2020
4a9da5e
Update releases.js
Happypig375 Aug 1, 2020
84cca5e
Update README.md
Happypig375 Aug 1, 2020
aceaae1
Update README.md
Happypig375 Aug 1, 2020
9e5ee68
Update releases.js
Happypig375 Aug 1, 2020
c622189
Delete config-with-change-title-escapes.yml
Happypig375 Aug 1, 2020
48c5345
Delete config-with-change-title-escapes-backticks.yml
Happypig375 Aug 1, 2020
2bdeb98
Update releases.js
Happypig375 Aug 1, 2020
2549de2
Create releases.test.js
Happypig375 Aug 1, 2020
1fc086f
Update releases.test.js
Happypig375 Aug 1, 2020
03836a7
Update releases.test.js
Happypig375 Aug 1, 2020
87c53ef
Update releases.test.js
Happypig375 Aug 1, 2020
5275a8e
Update releases.js
Happypig375 Aug 1, 2020
d2a2e9c
Update releases.test.js
Happypig375 Aug 1, 2020
d244358
Update releases.test.js
Happypig375 Aug 1, 2020
27cd024
Update releases.test.js
Happypig375 Aug 1, 2020
42869fe
Update README.md
Happypig375 Aug 1, 2020
1580564
Update releases.test.js
Happypig375 Aug 1, 2020
7302f99
Update releases.js
Happypig375 Aug 1, 2020
f424208
Update releases.test.js
Happypig375 Aug 1, 2020
13351ed
Update releases.test.js
Happypig375 Aug 1, 2020
0ace598
Update releases.test.js
Happypig375 Aug 1, 2020
7c50b8a
Update releases.test.js
Happypig375 Aug 1, 2020
36330e9
Update releases.test.js
Happypig375 Aug 1, 2020
fac2fe3
Update releases.test.js
Happypig375 Aug 1, 2020
05cc4ce
`yarn jest --updateSnapshot`
jetersen Aug 4, 2020
c5cec30
`yarn lint --fix`
jetersen Aug 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 26 additions & 24 deletions README.md
Expand Up @@ -71,6 +71,7 @@ categories:
- title: '🧰 Maintenance'
label: 'chore'
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
version-resolver:
major:
labels:
Expand All @@ -92,23 +93,24 @@ template: |

You can configure Release Drafter using the following key in your `.github/release-drafter.yml` file:

| Key | Required | Description |
| --------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `template` | Required | The template for the body of the draft release. Use [template variables](#template-variables) to insert values. |
| `name-template` | Optional | The template for the name of the draft release. For example: `"v$NEXT_PATCH_VERSION"`. |
| `tag-template` | Optional | The template for the tag of the draft release. For example: `"v$NEXT_PATCH_VERSION"`. |
| `version-template` | Optional | The template to use when calculating the next version number for the release. Useful for projects that don't use semantic versioning. Default: `"$MAJOR.$MINOR.$PATCH"` |
| `change-template` | Optional | The template to use for each merged pull request. Use [change template variables](#change-template-variables) to insert values. Default: `"* $TITLE (#$NUMBER) @$AUTHOR"`. |
| `no-changes-template` | Optional | The template to use for when there’s no changes. Default: `"* No changes"`. |
| `references` | Optional | The references to listen for configuration updates to `.github/release-drafter.yml`. Refer to [References](#references) to learn more about this |
| `categories` | Optional | Categorize pull requests using labels. Refer to [Categorize Pull Requests](#categorize-pull-requests) to learn more about this option. |
| `exclude-labels` | Optional | Exclude pull requests using labels. Refer to [Exclude Pull Requests](#exclude-pull-requests) to learn more about this option. |
| `include-labels` | Optional | Include only the specified pull requests using labels. Refer to [Include Pull Requests](#include-pull-requests) to learn more about this option. |
| `replacers` | Optional | Search and replace content in the generated changelog body. Refer to [Replacers](#replacers) to learn more about this option. |
| `sort-by` | Optional | Sort changelog by merged_at or title. Can be one of: `merged_at`, `title`. Default: `merged_at`. |
| `sort-direction` | Optional | Sort changelog in ascending or descending order. Can be one of: `ascending`, `descending`. Default: `descending`. |
| `prerelease` | Optional | Mark the draft release as pre-release. Default `false`. |
| `version-resolver` | Optional | Adjust the `$RESOLVED_VERSION` variable using labels. Refer to [Version Resolver](#version-resolver) to learn more about this |
| Key | Required | Description |
| ---------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `template` | Required | The template for the body of the draft release. Use [template variables](#template-variables) to insert values. |
| `name-template` | Optional | The template for the name of the draft release. For example: `"v$NEXT_PATCH_VERSION"`. |
| `tag-template` | Optional | The template for the tag of the draft release. For example: `"v$NEXT_PATCH_VERSION"`. |
| `version-template` | Optional | The template to use when calculating the next version number for the release. Useful for projects that don't use semantic versioning. Default: `"$MAJOR.$MINOR.$PATCH"` |
| `change-template` | Optional | The template to use for each merged pull request. Use [change template variables](#change-template-variables) to insert values. Default: `"* $TITLE (#$NUMBER) @$AUTHOR"`. |
| `change-title-escapes` | Optional | Characters to escape in `$TITLE` when inserting into `change-template` so that they are not interpreted as Markdown format characters. Default: `""` |
| `no-changes-template` | Optional | The template to use for when there’s no changes. Default: `"* No changes"`. |
| `references` | Optional | The references to listen for configuration updates to `.github/release-drafter.yml`. Refer to [References](#references) to learn more about this |
| `categories` | Optional | Categorize pull requests using labels. Refer to [Categorize Pull Requests](#categorize-pull-requests) to learn more about this option. |
| `exclude-labels` | Optional | Exclude pull requests using labels. Refer to [Exclude Pull Requests](#exclude-pull-requests) to learn more about this option. |
| `include-labels` | Optional | Include only the specified pull requests using labels. Refer to [Include Pull Requests](#include-pull-requests) to learn more about this option. |
| `replacers` | Optional | Search and replace content in the generated changelog body. Refer to [Replacers](#replacers) to learn more about this option. |
| `sort-by` | Optional | Sort changelog by merged_at or title. Can be one of: `merged_at`, `title`. Default: `merged_at`. |
| `sort-direction` | Optional | Sort changelog in ascending or descending order. Can be one of: `ascending`, `descending`. Default: `descending`. |
| `prerelease` | Optional | Mark the draft release as pre-release. Default `false`. |
| `version-resolver` | Optional | Adjust the `$RESOLVED_VERSION` variable using labels. Refer to [Version Resolver](#version-resolver) to learn more about this |

Release Drafter also supports [Probot Config](https://github.com/probot/probot-config), if you want to store your configuration files in a central repository. This allows you to share configurations between projects, and create a organization-wide configuration file by creating a repository named `.github` with the file `.github/release-drafter.yml`.

Expand Down Expand Up @@ -169,13 +171,13 @@ If a pull requests is found with the label `major`/`minor`/`patch`, the correspo

You can use any of the following variables in `change-template`:

| Variable | Description |
| --------- | --------------------------------------------------------------------------- |
| `$NUMBER` | The number of the pull request, e.g. `42`. |
| `$TITLE` | The title of the pull request, e.g. `Add alien technology`. |
| `$AUTHOR` | The pull request author’s username, e.g. `gracehopper`. |
| `$BODY` | The body of the pull request e.g. `Fixed spelling mistake`. |
| `$URL` | The URL of the pull request e.g. `https://github.com/octocat/repo/pull/42`. |
| Variable | Description |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `$NUMBER` | The number of the pull request, e.g. `42`. |
| `$TITLE` | The title of the pull request, e.g. `Add alien technology`. Any characters excluding @ and # matching `change-title-escapes` will be prepended with a backslash so that they will appear verbatim instead of being interpreted as markdown format characters. @s and #s if present in `change-title-escapes` will be appended with an HTML comment so that they don't become mentions. |
| `$AUTHOR` | The pull request author’s username, e.g. `gracehopper`. |
| `$BODY` | The body of the pull request e.g. `Fixed spelling mistake`. |
| `$URL` | The URL of the pull request e.g. `https://github.com/octocat/repo/pull/42`. |

## References

Expand Down
1 change: 1 addition & 0 deletions lib/default-config.js
Expand Up @@ -4,6 +4,7 @@ const DEFAULT_CONFIG = Object.freeze({
'name-template': '',
'tag-template': '',
'change-template': `* $TITLE (#$NUMBER) @$AUTHOR`,
'change-title-escapes': '',
'no-changes-template': `* No changes`,
'version-template': `$MAJOR.$MINOR.$PATCH`,
'version-resolver': {
Expand Down
13 changes: 12 additions & 1 deletion lib/releases.js
@@ -1,4 +1,5 @@
const compareVersions = require('compare-versions')
const regexEscape = require('escape-string-regexp')

const { getVersionInfo } = require('./versions')
const { template } = require('./template')
Expand Down Expand Up @@ -155,11 +156,18 @@ const generateChangeLog = (mergedPullRequests, config) => {
categorizedPullRequests,
] = categorizePullRequests(mergedPullRequests, config)

const escapeTitle = (title) =>
// If config['change-title-escapes'] contains backticks, then they will be escaped along with content contained inside backticks
// If not, the entire backtick block is matched so that it will become a markdown code block without escaping any of its content
title.replace(new RegExp(`[${regexEscape(config['change-title-escapes'])}]|\`.*?\``, 'g'), (match) =>
match.length > 1 ? match
Happypig375 marked this conversation as resolved.
Show resolved Hide resolved
: match == '@' || match == '#' ? `${match}<!---->` : `\\${match}`)

const pullRequestToString = (pullRequests) =>
pullRequests
.map((pullRequest) =>
template(config['change-template'], {
$TITLE: pullRequest.title,
$TITLE: escapeTitle(pullRequest.title),
$NUMBER: pullRequest.number,
$AUTHOR: pullRequest.author ? pullRequest.author.login : 'ghost',
$BODY: pullRequest.body,
Expand Down Expand Up @@ -214,6 +222,9 @@ const resolveVersionKeyIncrement = (mergedPullRequests, config) => {
return versionKey || config['version-resolver'].default
}


module.exports.generateChangeLog = generateChangeLog

module.exports.generateReleaseInfo = ({
commits,
config,
Expand Down
4 changes: 4 additions & 0 deletions lib/schema.js
Expand Up @@ -17,6 +17,10 @@ const schema = (context) => {
'change-template': Joi.string().default(
DEFAULT_CONFIG['change-template']
),

'change-title-escapes': Joi.string()
.allow('')
.default(DEFAULT_CONFIG['change-title-escapes']),

'no-changes-template': Joi.string().default(
DEFAULT_CONFIG['no-changes-template']
Expand Down
12 changes: 12 additions & 0 deletions schema.json
Expand Up @@ -15,6 +15,18 @@
"default": "* $TITLE (#$NUMBER) @$AUTHOR",
"type": "string"
},
"change-title-escapes": {
"anyOf": [
{
"type": "string",
"enum": [""]
},
{
"default": "",
"type": "string"
}
]
},
"no-changes-template": {
"default": "* No changes",
"type": "string"
Expand Down
68 changes: 68 additions & 0 deletions test/releases.test.js
@@ -0,0 +1,68 @@
const { generateChangeLog } = require('../lib/releases')
const each = require('jest-each').default
const { DEFAULT_CONFIG } = require('../lib/default-config')

const pullRequests = [
{ title: 'A1', number: 1, body: 'A1 body', url: 'https://github.com', labels:{ nodes:[{ name: 'bug'}] } },
{ title: 'B2', number: 2, body: 'B2 body', url: 'https://github.com', labels:{ nodes:[{ name: 'feature' }] } },
{ title: 'Adds missing <example>', number: 3, body: 'Adds missing <example> body', url: 'https://github.com', labels:{ nodes:[{ name: 'bug'}] }, author:{ login:'jetersen' } },
{ title: '`#code_block`', number: 4, body: '`#code block` body', url: 'https://github.com', labels:{ nodes:[{ name: 'bug'}] }, author:{ login:'jetersen' } },
{ title: 'Fixes #4', number: 5, body: 'Fixes #4 body', url: 'https://github.com', labels:{ nodes:[{ name: 'bug'}] }, author:{ login:'Happypig375' } },
{ title: '2*2 should equal to 4*1', number: 6, body: '2*2 should equal to 4*1 body', url: 'https://github.com', labels:{ nodes:[{ name: 'bug'}] }, author:{ login:'jetersen' } },
{ title: 'Rename __confgs\\confg.yml to __configs\\config.yml', number: 7, body: 'Rename __confg to __config body', url: 'https://github.com', labels:{ nodes:[{ name: 'bugfix'}] }, author:{ login:'ghost' } },
{ title: 'Adds @nullable annotations to the 1*1+2*4 test in `tests.java`', number: 0, body: 'Adds @nullable annotations to the 1*1+2*4 test in `tests.java`', url: 'https://github.com', labels:{ nodes:[{ name: 'feature'}] }, author:{ login:'Happypig375' } },
]
const baseConfig = {
...DEFAULT_CONFIG,
template: '$CHANGES',
references: ['master'],
}

describe('releases', () => {
describe('generateChangeLog', () => {
it('does not escape titles without setting change-title-escapes', () => {
const changelog = generateChangeLog(pullRequests, baseConfig)
expect(changelog).toEqual('* A1 (#1) @ghost\n* B2 (#2) @ghost\n* Adds missing <example> (#3) @jetersen\n* `#code_block` (#4) @jetersen\n* Fixes #4 (#5) @Happypig375\n* 2*2 should equal to 4*1 (#6) @jetersen\n* Rename __confgs\\confg.yml to __configs\\config.yml (#7) @ghost\n* Adds @nullable annotations to the 1*1+2*4 test in `tests.java` (#0) @Happypig375')
})
it('escapes titles with \\s correctly', () => {
const config = {
...baseConfig,
'change-title-escapes': '\\'
}
const changelog = generateChangeLog(pullRequests, config)
expect(changelog).toEqual('* A1 (#1) @ghost\n* B2 (#2) @ghost\n* Adds missing <example> (#3) @jetersen\n* `#code_block` (#4) @jetersen\n* Fixes #4 (#5) @Happypig375\n* 2*2 should equal to 4*1 (#6) @jetersen\n* Rename __confgs\\\\confg.yml to __configs\\\\config.yml (#7) @ghost\n* Adds @nullable annotations to the 1*1+2*4 test in `tests.java` (#0) @Happypig375')
})
it('escapes titles with \\<*_& correctly', () => {
const config = {
...baseConfig,
'change-title-escapes': '\\<*_&'
}
const changelog = generateChangeLog(pullRequests, config)
expect(changelog).toEqual('* A1 (#1) @ghost\n* B2 (#2) @ghost\n* Adds missing \\<example> (#3) @jetersen\n* `#code_block` (#4) @jetersen\n* Fixes #4 (#5) @Happypig375\n* 2\\*2 should equal to 4\\*1 (#6) @jetersen\n* Rename \\_\\_confgs\\\\confg.yml to \\_\\_configs\\\\config.yml (#7) @ghost\n* Adds @nullable annotations to the 1\\*1+2\\*4 test in `tests.java` (#0) @Happypig375')
})
it('escapes titles with @s correctly', () => {
const config = {
...baseConfig,
'change-title-escapes': '@'
}
const changelog = generateChangeLog(pullRequests, config)
expect(changelog).toEqual('* A1 (#1) @ghost\n* B2 (#2) @ghost\n* Adds missing <example> (#3) @jetersen\n* `#code_block` (#4) @jetersen\n* Fixes #4 (#5) @Happypig375\n* 2*2 should equal to 4*1 (#6) @jetersen\n* Rename __confgs\\confg.yml to __configs\\config.yml (#7) @ghost\n* Adds @<!---->nullable annotations to the 1*1+2*4 test in `tests.java` (#0) @Happypig375')
})
it('escapes titles with @s and #s correctly', () => {
const config = {
...baseConfig,
'change-title-escapes': '@#'
}
const changelog = generateChangeLog(pullRequests, config)
expect(changelog).toEqual('* A1 (#1) @ghost\n* B2 (#2) @ghost\n* Adds missing <example> (#3) @jetersen\n* `#code_block` (#4) @jetersen\n* Fixes #<!---->4 (#5) @Happypig375\n* 2*2 should equal to 4*1 (#6) @jetersen\n* Rename __confgs\\confg.yml to __configs\\config.yml (#7) @ghost\n* Adds @<!---->nullable annotations to the 1*1+2*4 test in `tests.java` (#0) @Happypig375')
})
it('escapes titles with \\<@*_&`# correctly', () => {
const config = {
...baseConfig,
'change-title-escapes': '\\<@*_&`#'
}
const changelog = generateChangeLog(pullRequests, config)
expect(changelog).toEqual('* A1 (#1) @ghost\n* B2 (#2) @ghost\n* Adds missing \\<example> (#3) @jetersen\n* \\`#<!---->code\\_block\\` (#4) @jetersen\n* Fixes #<!---->4 (#5) @Happypig375\n* 2\\*2 should equal to 4\\*1 (#6) @jetersen\n* Rename \\_\\_confgs\\\\confg.yml to \\_\\_configs\\\\config.yml (#7) @ghost\n* Adds @<!---->nullable annotations to the 1\\*1+2\\*4 test in \\`tests.java\\` (#0) @Happypig375')
Copy link
Member

Choose a reason for hiding this comment

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

replace toEqual with toMatchInlineSnapshot

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does toMatchInlineSnapshot allow indentation? There is

expect(body).toMatchInlineSnapshot(`
Object {
"body": "# What's Changed
* Add documentation (#5) @TimonVS
* Update dependencies (#4) @TimonVS
* Bug fixes (#3) @TimonVS
* Add big feature (#2) @TimonVS
* 👽 Add alien technology (#1) @TimonVS
",
"draft": true,
"name": "",
"prerelease": false,
"tag_name": "",
}
`)

but this PR's tests seem to fail.

Copy link
Member

@jetersen jetersen Aug 1, 2020

Choose a reason for hiding this comment

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

Because it is inside a object :)

When you use toMatchInlineSnapshot on a string, it cannot tolerate it 😅

Copy link
Member

Choose a reason for hiding this comment

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

You can also use toMatchSnapshot which will create a file where indentation shouldn't be an issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will wrap it in an object

Copy link
Contributor Author

Choose a reason for hiding this comment

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

    - Snapshot  - 13
    + Received  + 10

    -
    -         Object {
    -           "log": "
    -         * A1 (#1) @ghost
    -         * B2 (#2) @ghost
    -         * Adds missing \<example> (#3) @jetersen
    -         * \`\#code\_block\` (#4) @jetersen
    -         * Fixes #<!--->4 (#5) @Happypig375
    -         * 2\*2 should equal to 4\*1 (#6) @jetersen
    -         * Rename \_\_confgs\\confg.yml to \_\_configs\\config.yml (#7) @ghost
    -         * Adds @<!--->nullable annotations to the 1\*1+2\*4 test in `tests.java` (#0) @Happypig375
    -         ",
    -         }
    + Object {
    +   "log": "* A1 (#1) @ghost
    + * B2 (#2) @ghost
    + * Adds missing \\<example> (#3) @jetersen
    + * \\`#<!---->code\\_block\\` (#4) @jetersen
    + * Fixes #<!---->4 (#5) @Happypig375
    + * 2\\*2 should equal to 4\\*1 (#6) @jetersen
    + * Rename \\_\\_confgs\\\\confg.yml to \\_\\_configs\\\\config.yml (#7) @ghost
    + * Adds @<!---->nullable annotations to the 1\\*1+2\\*4 test in \\`tests.java\\` (#0) @Happypig375",
    + }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll throw away indentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

    - Snapshot  - 0
    + Received  + 0

???

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Trying to use toMatchInlineSnapshot is literally a waste of time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jetersen Any suggestions?

})
})
})
2 changes: 2 additions & 0 deletions test/schema.test.js
Expand Up @@ -28,6 +28,8 @@ const invalidConfigs = [
[{ template: '' }, 'is not allowed to be empty'],
[{ 'change-template': ['* $TITLE (#$NUMBER) @$AUTHOR'] }, 'must be a string'],
[{ 'change-template': null }, 'must be a string'],
[{ 'change-title-escapes': ['\<_*'] }, 'must be a string'],
[{ 'change-title-escapes': null }, 'must be a string'],
[{ replacers: [{ search: 123 }] }, 'must be a regexp or a string'],
[{ replacers: [{ search: '123' }] }, 'is required'],
[
Expand Down