-
Notifications
You must be signed in to change notification settings - Fork 8
152 lines (132 loc) · 6.42 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
name: Validate PRs
on:
pull_request:
types: [opened, edited, synchronize, reopened]
permissions:
contents: read
pull-requests: write
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
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, filtering for only this bot's 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', botComments);
// Get JSON of valid 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', gitmojis);
- name: PR title should start with emoji
uses: actions/github-script@v7
if: always()
with:
script: |
const prTitle = "${{ steps.pr.outputs.title }}";
const comments = ${{ steps.pr.outputs.comments }};
const gitmojis = ${{ steps.pr.outputs.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: PR title should not exceed ${{ env.MAX_PR_TITLE_LENGTH }} characters
uses: actions/github-script@v7
if: always()
with:
script: |
const prTitle = "${{ steps.pr.outputs.title }}";
const comments = ${{ steps.pr.outputs.comments }};
const MAX_LENGTH = ${{ env.MAX_PR_TITLE_LENGTH }};
// Match either:
// 1. Unicode emoji at start (using Unicode properties)
// 2. GitHub emoji shortcode format (e.g. :fire:)
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
});
}