forked from amannn/action-semantic-pull-request
/
index.js
161 lines (144 loc) · 5.51 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
const core = require('@actions/core');
const github = require('@actions/github');
const parseConfig = require('./parseConfig');
const validatePrTitle = require('./validatePrTitle');
module.exports = async function run() {
try {
const {
types,
scopes,
requireScope,
wip,
subjectPattern,
subjectPatternError,
validateSingleCommit,
validateSingleCommitMatchesPrTitle,
githubBaseUrl,
ignoreLabels
} = parseConfig();
const client = github.getOctokit(process.env.GITHUB_TOKEN, {
baseUrl: githubBaseUrl
});
const contextPullRequest = github.context.payload.pull_request;
if (!contextPullRequest) {
throw new Error(
"This action can only be invoked in `pull_request_target` or `pull_request` events. Otherwise the pull request can't be inferred."
);
}
const owner = contextPullRequest.base.user.login;
const repo = contextPullRequest.base.repo.name;
// The pull request info on the context isn't up to date. When
// the user updates the title and re-runs the workflow, it would
// be outdated. Therefore fetch the pull request via the REST API
// to ensure we use the current title.
const {data: pullRequest} = await client.rest.pulls.get({
owner,
repo,
pull_number: contextPullRequest.number
});
// Ignore errors if specified labels are added.
if (ignoreLabels) {
const labelNames = pullRequest.labels.map((label) => label.name);
for (const labelName of labelNames) {
if (ignoreLabels.includes(labelName)) {
core.info(
`Validation was skipped because the PR label "${labelName}" was found.`
);
return;
}
}
}
// Pull requests that start with "[WIP] " are excluded from the check.
const isWip = wip && /^\[WIP\]\s/.test(pullRequest.title);
let validationError;
if (!isWip) {
try {
await validatePrTitle(pullRequest.title, {
types,
scopes,
requireScope,
subjectPattern,
subjectPatternError
});
if (validateSingleCommit) {
const commits = [];
let nonMergeCommits = [];
for await (const response of client.paginate.iterator(
client.rest.pulls.listCommits,
{
owner,
repo,
pull_number: contextPullRequest.number
}
)) {
commits.push(...response.data);
// GitHub does not count merge commits when deciding whether to use
// the PR title or a commit message for the squash commit message.
nonMergeCommits = commits.filter(
(commit) => commit.parents.length < 2
);
// We only need two non-merge commits to know that the PR
// title won't be used.
if (nonMergeCommits.length >= 2) break;
}
// If there is only one (non merge) commit present, GitHub will use
// that commit rather than the PR title for the title of a squash
// commit. To make sure a semantic title is used for the squash
// commit, we need to validate the commit title.
if (nonMergeCommits.length === 1) {
try {
await validatePrTitle(nonMergeCommits[0].commit.message, {
types,
scopes,
requireScope,
subjectPattern,
subjectPatternError
});
} catch (error) {
throw new Error(
`Pull request has only one commit and it's not semantic; this may lead to a non-semantic commit in the base branch (see https://github.community/t/how-to-change-the-default-squash-merge-commit-message/1155). Amend the commit message to match the pull request title, or add another commit.`
);
}
if (validateSingleCommitMatchesPrTitle) {
const commitTitle =
nonMergeCommits[0].commit.message.split('\n')[0];
if (commitTitle !== pullRequest.title) {
throw new Error(
`The pull request has only one (non-merge) commit and in this case Github will use it as the default commit message when merging. The pull request title doesn't match the commit though ("${pullRequest.title}" vs. "${commitTitle}"). Please update the pull request title accordingly to avoid surprises.`
);
}
}
}
}
} catch (error) {
validationError = error;
}
}
if (wip) {
const newStatus =
isWip || validationError != null ? 'pending' : 'success';
// When setting the status to "pending", the checks don't
// complete. This can be used for WIP PRs in repositories
// which don't support draft pull requests.
// https://developer.github.com/v3/repos/statuses/#create-a-status
await client.request('POST /repos/:owner/:repo/statuses/:sha', {
owner,
repo,
sha: pullRequest.head.sha,
state: newStatus,
target_url: 'https://github.com/amannn/action-semantic-pull-request',
description: isWip
? 'This PR is marked with "[WIP]".'
: validationError
? 'PR title validation failed'
: 'Ready for review & merge.',
context: 'action-semantic-pull-request'
});
}
if (!isWip && validationError) {
throw validationError;
}
} catch (error) {
core.setFailed(error.message);
}
};