Skip to content

Commit

Permalink
Merge pull request #439 from hongaar/feature/configurable-commit-message
Browse files Browse the repository at this point in the history
feat: configurable commit message
  • Loading branch information
probot-auto-merge[bot] authored Oct 31, 2019
2 parents 9a43a4b + 6fef464 commit a28c98b
Show file tree
Hide file tree
Showing 12 changed files with 428 additions and 34 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,24 @@ For more information see https://help.github.com/articles/about-pull-request-mer
mergeMethod: merge
```

### `mergeCommitMessage` (default: none)

Optionally specify the merge commit message format. The following template tags
are supported:

* `{title}`: The pull request title at the moment it is merged
* `{body}`: The pull request body at the moment it is merged
* `{number}`: The pull request number
* `{branch}`: The name of the source branch
* `{commits}`: A list of merged commits

When this option is not set, the merge commit message is controlled by
GitHub and uses a combination of the title of the pull request when it was
opened (note that later changes to the title are ignored) and a list of
commits.

This settings is ignored when `mergeMethod` is set to `rebase`.

### `rules` (default: none)

Rules allow more flexiblity configuring conditions for automatically merging. Each rule is defined by
Expand Down
17 changes: 17 additions & 0 deletions auto-merge.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@ deleteBranchAfterMerge: true
# For more information see https://help.github.com/articles/about-pull-request-merges/
mergeMethod: merge

# Optionally specify the merge commit message format. The following template
# tags are supported:
# * {title}: The pull request title at the moment it is merged
# * {body}: The pull request body at the moment it is merged
# * {number}: The pull request number
# * {branch}: The name of the source branch
# * {commits}: A list of merged commits
# When this option is not set, the merge commit message is controlled by
# GitHub and uses a combination of the title of the pull request when it was
# opened (note that later changes to the title are ignored) and a list of
# commits.
# This settings is ignored when `mergeMethod` is set to `rebase`.
mergeCommitMessage: |
{title} (#{number})
{body}
# Blocking labels are the labels that can be attached to a pull request to make sure the pull request
# is not being automatically merged.
blockingLabels:
Expand Down
10 changes: 10 additions & 0 deletions query.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ query PullRequestQuery($owner:String!, $repo:String!, $pullRequestNumber:Int!) {
}
}
title
body
authorAssociation
baseRef {
repository {
Expand Down Expand Up @@ -61,6 +62,14 @@ query PullRequestQuery($owner:String!, $repo:String!, $pullRequestNumber:Int!) {
}
}
}
allCommits: commits(last: 100) {
nodes {
commit {
abbreviatedOid
messageHeadline
}
}
}
headRef {
repository {
owner {
Expand All @@ -74,6 +83,7 @@ query PullRequestQuery($owner:String!, $repo:String!, $pullRequestNumber:Int!) {
name
}
headRefOid
headRefName
repository {
branchProtectionRules(last: 100) {
nodes {
Expand Down
50 changes: 50 additions & 0 deletions src/commit-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { PullRequestInfo } from './models'
import { Config } from './config'

export function getCommitMessage (
pullRequestInfo: PullRequestInfo,
config: Config
) {
return config.mergeCommitMessage
? getCustomCommitMessage(pullRequestInfo, config.mergeCommitMessage)
: null
}

export function splitCommitMessage (message: string) {
const [title, ...body] = message.trim().split('\n')

return {
title: title.trim(),
body: body.join('\n').trim()
}
}

type Tag = 'title' | 'body' | 'number' | 'branch' | 'commits'

type Tags = {
[key in Tag]: (pullRequestInfo: PullRequestInfo) => string
}

function getCustomCommitMessage (
pullRequestInfo: PullRequestInfo,
template: string
) {
const tagResolvers: Tags = {
title: pullRequestInfo => pullRequestInfo.title,
body: pullRequestInfo => pullRequestInfo.body,
number: pullRequestInfo => pullRequestInfo.number.toString(),
branch: pullRequestInfo => pullRequestInfo.headRefName,
commits: pullRequestInfo => pullRequestInfo.allCommits.nodes
.map(node => `* ${node.commit.messageHeadline} (${node.commit.abbreviatedOid})`)
.join('\n')
}

return template.replace(/\{(\w+)\}/g, (match, tagName: Tag) => {
const tagResolver = tagResolvers[tagName]
if (tagResolver) {
return tagResolver(pullRequestInfo)
} else {
return match
}
})
}
4 changes: 3 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type Config = {
updateBranch: boolean,
deleteBranchAfterMerge: boolean,
mergeMethod: 'merge' | 'rebase' | 'squash'
mergeCommitMessage?: string
reportStatus: boolean
} & ConditionConfig

Expand Down Expand Up @@ -93,7 +94,8 @@ const configDecoder: Decoder<Config> = object({
constant<'merge'>('merge'),
constant<'rebase'>('rebase'),
constant<'squash'>('squash')
)
),
mergeCommitMessage: optional(string())
})

export function validateConfig (config: any) {
Expand Down
13 changes: 13 additions & 0 deletions src/github-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export function validatePullRequestQuery (pullRequestQuery: PullRequestQuery) {
labels: assertNotNullNodes(pullRequest.labels, 'No permission to labels of pull request',
labels => removeTypename(labels)
),
title: pullRequest.title,
body: pullRequest.body,
reviews: assertNotNullNodes(pullRequest.reviews, 'No permission to fetch reviews',
review => ({
...removeTypename(review),
Expand Down Expand Up @@ -96,6 +98,7 @@ export function validatePullRequestQuery (pullRequestQuery: PullRequestQuery) {
})
),
headRefOid: pullRequest.headRefOid as string,
headRefName: pullRequest.headRefName,
commits: assertNotNullNodes(pullRequest.commits, 'No permission to fetch commits',
commit => ({
...removeTypename(commit),
Expand All @@ -116,6 +119,16 @@ export function validatePullRequestQuery (pullRequestQuery: PullRequestQuery) {
}
})
),
allCommits: assertNotNullNodes(pullRequest.allCommits, 'No permission to fetch commits',
commit => ({
...removeTypename(commit),
commit: {
...removeTypename(commit.commit),
abbreviatedOid: commit.commit.abbreviatedOid,
messageHeadline: commit.commit.messageHeadline
}
})
),
repository: {
...removeTypename(pullRequest.repository),
branchProtectionRules: assertNotNullNodes(pullRequest.repository.branchProtectionRules, 'No permission to fetch branchProtectionRules',
Expand Down
94 changes: 74 additions & 20 deletions src/pull-request-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { getPullRequestStatus, PullRequestStatus } from './pull-request-status'
import { queryPullRequest } from './pull-request-query'
import { updateStatusReportCheck } from './status-report'
import { MergeStateStatus } from './query.graphql'
import { Config } from './config'
import { getCommitMessage, splitCommitMessage } from './commit-message'

export interface PullRequestContext extends HandlerContext {
reschedulePullRequest: () => void,
Expand Down Expand Up @@ -55,11 +57,11 @@ export type PullRequestAction = 'reschedule' | 'update_branch' | 'merge' | 'dele
export type PullRequestActions
= (
[]
| ['reschedule']
| ['update_branch', 'reschedule']
| ['merge']
| ['merge', 'delete_branch']
) & Array<PullRequestAction>
| ['reschedule']
| ['update_branch', 'reschedule']
| ['merge']
| ['merge', 'delete_branch']
) & Array<PullRequestAction>

export type PullRequestPlanCode
= 'closed'
Expand All @@ -81,6 +83,11 @@ export type PullRequestPlan = {
actions: PullRequestActions
}

type ExtraMergeParams = {
commit_title?: string
commit_message?: string
}

function getChecksMarkdown (pullRequestStatus: PullRequestStatus) {
return Object.entries(pullRequestStatus)
.map(([name, result]) => {
Expand All @@ -98,6 +105,27 @@ function getChecksMarkdown (pullRequestStatus: PullRequestStatus) {
.join('\n')
}

function markdownParagraphs (paragraphs: string[]) {
return paragraphs
.filter(paragraph => paragraph)
.join('\n\n')
}

function getCommitMessageMarkdown (pullRequestInfo: PullRequestInfo, config: Config) {
const commitMessage = getCommitMessage(pullRequestInfo, config)

if (commitMessage === null) {
return ''
}

const quotedCommitMessage = commitMessage.split('\n').map(line => `> ${line}`).join('\n')

return markdownParagraphs([
'Commit message preview:',
quotedCommitMessage
])
}

/**
* Determines which actions to take based on the pull request and the condition results
*/
Expand All @@ -123,15 +151,23 @@ export function getPullRequestPlan (
if (pendingConditions.length > 0) {
return {
code: 'pending_condition',
message: `There are pending conditions:\n\n${getChecksMarkdown(pullRequestStatus)}`,
message: markdownParagraphs([
'There are pending conditions:',
getChecksMarkdown(pullRequestStatus),
getCommitMessageMarkdown(pullRequestInfo, config)
]),
actions: ['reschedule']
}
}

if (failingConditions.length > 0) {
return {
code: 'failing_condition',
message: `There are failing conditions:\n\n${getChecksMarkdown(pullRequestStatus)}`,
message: markdownParagraphs([
'There are failing conditions:',
getChecksMarkdown(pullRequestStatus),
getCommitMessageMarkdown(pullRequestInfo, config)
]),
actions: []
}
}
Expand Down Expand Up @@ -181,13 +217,19 @@ export function getPullRequestPlan (
if (config.deleteBranchAfterMerge && !isInFork(pullRequestInfo)) {
return {
code: 'merge_and_delete',
message: 'Will merge the pull request and delete its branch',
message: markdownParagraphs([
'Will merge the pull request and delete its branch.',
getCommitMessageMarkdown(pullRequestInfo, config)
]),
actions: ['merge', 'delete_branch']
}
} else {
return {
code: 'merge',
message: 'Will merge the pull request',
message: markdownParagraphs([
'Will merge the pull request.',
getCommitMessageMarkdown(pullRequestInfo, config)
]),
actions: ['merge']
}
}
Expand Down Expand Up @@ -313,35 +355,47 @@ async function mergePullRequest (
pullRequestInfo: PullRequestInfo
) {
const { config } = context
const extraParams: ExtraMergeParams = {}
const pullRequestReference = getPullRequestReference(pullRequestInfo)
const commitMessage = getCommitMessage(pullRequestInfo, config)

if (commitMessage !== null) {
const commitMessageParams = splitCommitMessage(commitMessage)
extraParams.commit_title = commitMessageParams.title
extraParams.commit_message = commitMessageParams.body
}

// This presses the merge button.
result(
await context.github.pulls.merge({
owner: pullRequestReference.owner,
repo: pullRequestReference.repo,
pull_number: pullRequestReference.number,
merge_method: config.mergeMethod
merge_method: config.mergeMethod,
...extraParams
})
)
}

function getPlanTitle (plan: PullRequestPlan) {
return plan.actions.some(action => action === 'merge')
? 'Merging'
: plan.actions.some(action => action === 'update_branch')
? 'Updating branch'
: plan.actions.some(action => action === 'reschedule')
? 'Waiting'
: 'Not merging'
}

export async function handlePullRequestStatus (
context: PullRequestContext,
pullRequestInfo: PullRequestInfo,
pullRequestStatus: PullRequestStatus
) {
const plan = getPullRequestPlan(context, pullRequestInfo, pullRequestStatus)
const title = getPlanTitle(plan)

await updateStatusReportCheck(context, pullRequestInfo,
plan.actions.some(action => action === 'merge')
? 'Merging'
: plan.actions.some(action => action === 'update_branch')
? 'Updating branch'
: plan.actions.some(action => action === 'reschedule')
? 'Waiting'
: 'Not merging',
plan.message
)
await updateStatusReportCheck(context, pullRequestInfo, title, plan.message)

const { actions } = plan
context.log.debug('Actions:', actions)
Expand Down
Loading

0 comments on commit a28c98b

Please sign in to comment.