-
Notifications
You must be signed in to change notification settings - Fork 9
158 lines (138 loc) · 6.57 KB
/
validate-pr.yml
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
name: Validate PRs
on:
pull_request:
types: [opened, edited, synchronize, reopened]
permissions:
contents: read
env:
# https://cbea.ms/git-commit/#limit-50
MAX_PR_TITLE_LENGTH: 50
# Unique identifier for our bot's comments
BOT_COMMENT_IDENTIFIER: "<!-- pr-validator -->"
jobs:
check-title:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Get PR info
id: pr
uses: actions/github-script@v7
with:
script: |
// Get PR Title
const title = context.payload.pull_request.title;
core.setOutput('title', title);
// Get PR Comments
const allComments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
// Filter for comments that:
// 1. Are from a Bot
// 2. Contain our unique identifier
const botComments = allComments.data.filter(comment =>
comment.user.type === 'Bot' &&
comment.body.includes(process.env.BOT_COMMENT_IDENTIFIER)
);
core.setOutput('comments_json', JSON.stringify(botComments));
// Get Gitmojis
const gitmojiResponse = await github.request('GET /repos/{owner}/{repo}/contents/{path}', {
owner: 'carloscuesta',
repo: 'gitmoji',
path: 'packages/gitmojis/src/gitmojis.json'
});
const gitmojis = JSON.parse(Buffer.from(gitmojiResponse.data.content, 'base64').toString()).gitmojis;
core.setOutput('gitmojis_json', JSON.stringify(gitmojis));
- name: PR title should start with emoji
env:
PR_TITLE: ${{ steps.pr.outputs.title }}
PR_COMMENTS: ${{ steps.pr.outputs.comments_json }}
GITMOJIS: ${{ steps.pr.outputs.gitmojis_json }}
uses: actions/github-script@v7
if: always()
with:
script: |
const prTitle = process.env.PR_TITLE;
const comments = JSON.parse(process.env.PR_COMMENTS);
const gitmojis = JSON.parse(process.env.GITMOJIS);
const validEmojis = gitmojis.map(g => [g.emoji, g.code]);
const titleStartsWithValidEmoji = validEmojis.some(([emoji, code]) =>
prTitle.startsWith(emoji) || prTitle.startsWith(code)
);
// Find our bot's validation comment if it exists
const botComment = comments.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('Your PR title should start with an emoji!')
);
if (!titleStartsWithValidEmoji) {
// Only add a comment if we haven't already
if (!botComment) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `${process.env.BOT_COMMENT_IDENTIFIER}\n⚠️ Your PR title should start with an emoji!\n\nExample valid titles:\n- ✨ Add new feature (or :sparkles: Add new feature)\n- 🐛 Fix login bug (or :bug: Fix login bug)\n- 📝 Update documentation (or :memo: Update documentation)\n\nTo view all valid emojis, check out [gitmoji.dev](https://gitmoji.dev) for a comprehensive list of Git-friendly emojis!\n\nPlease update your PR title and try again.`
});
}
// Create warning annotation instead of failing
// Change to `core.setFailed()` to fail the check
core.warning(`PR title should start with an emoji! Current title: "${prTitle}". Use either Unicode emoji (🔥) or GitHub shortcode format (:fire:)`);
} else if (botComment) {
// If title is now valid and we have a comment, delete it
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id
});
}
- name: Check PR title length
env:
PR_TITLE: ${{ steps.pr.outputs.title }}
PR_COMMENTS: ${{ steps.pr.outputs.comments_json }}
MAX_LENGTH: ${{ env.MAX_PR_TITLE_LENGTH }}
uses: actions/github-script@v7
if: always()
with:
script: |
const prTitle = process.env.PR_TITLE;
const comments = JSON.parse(process.env.PR_COMMENTS);
const MAX_LENGTH = parseInt(process.env.MAX_LENGTH, 10);
const emojiRegex = /^(?:[\p{Emoji_Presentation}\p{Extended_Pictographic}]|:[a-z0-9_+-]+:)/u;
// Remove emoji prefix and its trailing space for length check
const titleWithoutEmoji = prTitle.replace(emojiRegex, '');
// Find our bot's validation comment if it exists
const botComment = comments.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('Your PR title is too long!')
);
const commentBody = `${process.env.BOT_COMMENT_IDENTIFIER}\n⚠️ Your PR title is too long!\n\nPR titles should be no longer than ${MAX_LENGTH} characters (excluding emoji). Your title is ${titleWithoutEmoji.length} characters long.\n\nPlease update your PR title to be more concise.`;
if (titleWithoutEmoji.length > MAX_LENGTH) {
if (!botComment) {
// Create new comment if none exists
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: commentBody
});
} else {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: commentBody
});
}
// Create warning annotation instead of failing
// Change to `core.setFailed()` to fail the check
core.warning(`PR title is too long (${titleWithoutEmoji.length}/${MAX_LENGTH} characters, excluding emoji)`);
} else if (botComment) {
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id
});
}