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

feat: add pull requests' closing issues to releases #1165

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .vscode/settings.json
Expand Up @@ -4,5 +4,6 @@
},
"files.exclude": {
"dist/": true
}
},
"eslint.format.enable": true
}
93 changes: 55 additions & 38 deletions README.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions bin/generate-fixtures.js
Expand Up @@ -86,6 +86,9 @@ for (const repo of repos) {
withPullRequestURL: true,
withBaseRefName: true,
withHeadRefName: true,
withPullRequestClosingIssues: true,
withClosingIssueBody: true,
withClosingIssueURL: true,
},
})
)
Expand Down
113 changes: 90 additions & 23 deletions dist/index.js
Expand Up @@ -141470,6 +141470,11 @@ const findCommitsWithAssociatedPullRequestsQuery = /* GraphQL */ `
$after: String
$withBaseRefName: Boolean!
$withHeadRefName: Boolean!
$withPullRequestClosingIssues: Boolean!
$withClosingIssueTitle: Boolean!
$withClosingIssueAuthor: Boolean!
$withClosingIssueBody: Boolean!
$withClosingIssueURL: Boolean!
) {
repository(name: $name, owner: $owner) {
object(expression: $targetCommitish) {
Expand Down Expand Up @@ -141512,6 +141517,18 @@ const findCommitsWithAssociatedPullRequestsQuery = /* GraphQL */ `
merged
baseRefName @include(if: $withBaseRefName)
headRefName @include(if: $withHeadRefName)
closingIssuesReferences
@include(if: $withPullRequestClosingIssues) {
nodes {
number
title @include(if: $withClosingIssueTitle)
author @include(if: $withClosingIssueAuthor) {
login
}
body @include(if: $withClosingIssueBody)
url @include(if: $withClosingIssueURL)
}
}
}
}
}
Expand All @@ -141522,6 +141539,21 @@ const findCommitsWithAssociatedPullRequestsQuery = /* GraphQL */ `
}
`

const changeTemplateVariablesNotCompatibleWithClosingIssueTemplate = new Set([
'CLOSING_ISSUES', // REASON: would cause recusivity
])

const checkIfChangeTemplateVariableShouldBeQueried = (variable, config) => {
if (
changeTemplateVariablesNotCompatibleWithClosingIssueTemplate.has(variable)
)
return config['change-template'].includes(`$${variable}`)
return (
config['change-template'].includes(`$${variable}`) ||
config['closing-issue-template'].includes(`$PULL_REQUEST_${variable}`)
)
}

const findCommitsWithAssociatedPullRequests = async ({
context,
targetCommitish,
Expand All @@ -141533,10 +141565,21 @@ const findCommitsWithAssociatedPullRequests = async ({
name: repo,
owner,
targetCommitish,
withPullRequestBody: config['change-template'].includes('$BODY'),
withPullRequestURL: config['change-template'].includes('$URL'),
withBaseRefName: config['change-template'].includes('$BASE_REF_NAME'),
withHeadRefName: config['change-template'].includes('$HEAD_REF_NAME'),
withPullRequestBody: checkIfChangeTemplateVariableShouldBeQueried('BODY'),
withPullRequestURL: checkIfChangeTemplateVariableShouldBeQueried('URL'),
withBaseRefName:
checkIfChangeTemplateVariableShouldBeQueried('BASE_REF_NAME'),
withHeadRefName:
checkIfChangeTemplateVariableShouldBeQueried('HEAD_REF_NAME'),
withPullRequestClosingIssues:
checkIfChangeTemplateVariableShouldBeQueried('CLOSING_ISSUES'),
withClosingIssueNumber:
config['closing-issue-template'].includes('$NUMBER'),
withClosingIssueTitle: config['closing-issue-template'].includes('$TITLE'),
withClosingIssueAuthor:
config['closing-issue-template'].includes('$AUTHOR'),
withClosingIssueBody: config['closing-issue-template'].includes('$BODY'),
withClosingIssueURL: config['closing-issue-template'].includes('$URL'),
}
const includePaths = config['include-paths']
const dataPath = ['repository', 'object', 'history']
Expand Down Expand Up @@ -142017,6 +142060,18 @@ const categorizePullRequests = (pullRequests, config) => {
return [uncategorizedPullRequests, categorizedPullRequests]
}

const escapeTitle = (title, escapes) =>
// If config['change-title-escapes'] or config['closing-issue-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(escapes)}]|\`.*?\``, 'g'),
(match) => {
if (match.length > 1) return match
if (match == '@' || match == '#') return `${match}<!---->`
return `\\${match}`
}
)

const generateChangeLog = (mergedPullRequests, config) => {
if (mergedPullRequests.length === 0) {
return config['no-changes-template']
Expand All @@ -142025,34 +142080,46 @@ const generateChangeLog = (mergedPullRequests, config) => {
const [uncategorizedPullRequests, 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) => {
if (match.length > 1) return match
if (match == '@' || match == '#') return `${match}<!---->`
return `\\${match}`
}
)

const pullRequestToString = (pullRequests) =>
pullRequests
.map((pullRequest) =>
template(config['change-template'], {
$TITLE: escapeTitle(pullRequest.title),
.map((pullRequest) => {
const closingIssuesTemplate = pullRequest.closingIssuesReferences
.map((issue) =>
template(config['closing-issue-template'], {
$TITLE: escapeTitle(
issue.title,
config['closing-issue-title-escapes']
),
$NUMBER: issue.number,
$AUTHOR: issue.author ? issue.author.login : 'ghost',
$BODY: issue.body,
$URL: issue.url,
$PULL_REQUEST_NUMBER: pullRequest.number,
$PULL_REQUEST_AUTHOR: pullRequest.author
? pullRequest.author.login
: 'ghost',
$PULL_REQUEST_BODY: pullRequest.body,
$PULL_REQUEST_URL: pullRequest.url,
$PULL_REQUEST_BASE_REF_NAME: pullRequest.baseRefName,
$PULL_REQUEST_HEAD_REF_NAME: pullRequest.headRefName,
})
)
.join(config['closing-issues-separator'])

return template(config['change-template'], {
$TITLE: escapeTitle(
pullRequest.title,
config['change-title-escapes']
),
$NUMBER: pullRequest.number,
$AUTHOR: pullRequest.author ? pullRequest.author.login : 'ghost',
$BODY: pullRequest.body,
$URL: pullRequest.url,
$BASE_REF_NAME: pullRequest.baseRefName,
$HEAD_REF_NAME: pullRequest.headRefName,
$CLOSING_ISSUES: closingIssuesTemplate,
})
)
})
.join('\n')

const changeLog = []
Expand Down
51 changes: 47 additions & 4 deletions lib/commits.js
Expand Up @@ -40,6 +40,11 @@ const findCommitsWithAssociatedPullRequestsQuery = /* GraphQL */ `
$after: String
$withBaseRefName: Boolean!
$withHeadRefName: Boolean!
$withPullRequestClosingIssues: Boolean!
$withClosingIssueTitle: Boolean!
$withClosingIssueAuthor: Boolean!
$withClosingIssueBody: Boolean!
$withClosingIssueURL: Boolean!
) {
repository(name: $name, owner: $owner) {
object(expression: $targetCommitish) {
Expand Down Expand Up @@ -82,6 +87,18 @@ const findCommitsWithAssociatedPullRequestsQuery = /* GraphQL */ `
merged
baseRefName @include(if: $withBaseRefName)
headRefName @include(if: $withHeadRefName)
closingIssuesReferences
@include(if: $withPullRequestClosingIssues) {
nodes {
number
title @include(if: $withClosingIssueTitle)
author @include(if: $withClosingIssueAuthor) {
login
}
body @include(if: $withClosingIssueBody)
url @include(if: $withClosingIssueURL)
}
}
}
}
}
Expand All @@ -92,6 +109,21 @@ const findCommitsWithAssociatedPullRequestsQuery = /* GraphQL */ `
}
`

const changeTemplateVariablesNotCompatibleWithClosingIssueTemplate = new Set([
'CLOSING_ISSUES', // REASON: would cause recusivity
])

const checkIfChangeTemplateVariableShouldBeQueried = (variable, config) => {
if (
changeTemplateVariablesNotCompatibleWithClosingIssueTemplate.has(variable)
)
return config['change-template'].includes(`$${variable}`)
return (
config['change-template'].includes(`$${variable}`) ||
config['closing-issue-template'].includes(`$PULL_REQUEST_${variable}`)
)
}

const findCommitsWithAssociatedPullRequests = async ({
context,
targetCommitish,
Expand All @@ -103,10 +135,21 @@ const findCommitsWithAssociatedPullRequests = async ({
name: repo,
owner,
targetCommitish,
withPullRequestBody: config['change-template'].includes('$BODY'),
withPullRequestURL: config['change-template'].includes('$URL'),
withBaseRefName: config['change-template'].includes('$BASE_REF_NAME'),
withHeadRefName: config['change-template'].includes('$HEAD_REF_NAME'),
withPullRequestBody: checkIfChangeTemplateVariableShouldBeQueried('BODY'),
withPullRequestURL: checkIfChangeTemplateVariableShouldBeQueried('URL'),
withBaseRefName:
checkIfChangeTemplateVariableShouldBeQueried('BASE_REF_NAME'),
withHeadRefName:
checkIfChangeTemplateVariableShouldBeQueried('HEAD_REF_NAME'),
withPullRequestClosingIssues:
checkIfChangeTemplateVariableShouldBeQueried('CLOSING_ISSUES'),
withClosingIssueNumber:
config['closing-issue-template'].includes('$NUMBER'),
withClosingIssueTitle: config['closing-issue-template'].includes('$TITLE'),
withClosingIssueAuthor:
config['closing-issue-template'].includes('$AUTHOR'),
withClosingIssueBody: config['closing-issue-template'].includes('$BODY'),
withClosingIssueURL: config['closing-issue-template'].includes('$URL'),
}
const includePaths = config['include-paths']
const dataPath = ['repository', 'object', 'history']
Expand Down
62 changes: 43 additions & 19 deletions lib/releases.js
Expand Up @@ -197,6 +197,18 @@ const categorizePullRequests = (pullRequests, config) => {
return [uncategorizedPullRequests, categorizedPullRequests]
}

const escapeTitle = (title, escapes) =>
// If config['change-title-escapes'] or config['closing-issue-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(escapes)}]|\`.*?\``, 'g'),
(match) => {
if (match.length > 1) return match
if (match == '@' || match == '#') return `${match}<!---->`
return `\\${match}`
}
)

const generateChangeLog = (mergedPullRequests, config) => {
if (mergedPullRequests.length === 0) {
return config['no-changes-template']
Expand All @@ -205,34 +217,46 @@ const generateChangeLog = (mergedPullRequests, config) => {
const [uncategorizedPullRequests, 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) => {
if (match.length > 1) return match
if (match == '@' || match == '#') return `${match}<!---->`
return `\\${match}`
}
)

const pullRequestToString = (pullRequests) =>
pullRequests
.map((pullRequest) =>
template(config['change-template'], {
$TITLE: escapeTitle(pullRequest.title),
.map((pullRequest) => {
const closingIssuesTemplate = pullRequest.closingIssuesReferences
.map((issue) =>
template(config['closing-issue-template'], {
$TITLE: escapeTitle(
issue.title,
config['closing-issue-title-escapes']
),
$NUMBER: issue.number,
$AUTHOR: issue.author ? issue.author.login : 'ghost',
$BODY: issue.body,
$URL: issue.url,
$PULL_REQUEST_NUMBER: pullRequest.number,
$PULL_REQUEST_AUTHOR: pullRequest.author
? pullRequest.author.login
: 'ghost',
$PULL_REQUEST_BODY: pullRequest.body,
$PULL_REQUEST_URL: pullRequest.url,
$PULL_REQUEST_BASE_REF_NAME: pullRequest.baseRefName,
$PULL_REQUEST_HEAD_REF_NAME: pullRequest.headRefName,
})
)
.join(config['closing-issues-separator'])

return template(config['change-template'], {
$TITLE: escapeTitle(
pullRequest.title,
config['change-title-escapes']
),
$NUMBER: pullRequest.number,
$AUTHOR: pullRequest.author ? pullRequest.author.login : 'ghost',
$BODY: pullRequest.body,
$URL: pullRequest.url,
$BASE_REF_NAME: pullRequest.baseRefName,
$HEAD_REF_NAME: pullRequest.headRefName,
$CLOSING_ISSUES: closingIssuesTemplate,
})
)
})
.join('\n')

const changeLog = []
Expand Down