-
Notifications
You must be signed in to change notification settings - Fork 4.3k
196 lines (165 loc) · 9.07 KB
/
Copy pathconventional-commit.yml
File metadata and controls
196 lines (165 loc) · 9.07 KB
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
name: "Lint PR title"
on:
pull_request:
types:
- opened
- edited
- synchronize
permissions:
pull-requests: write
jobs:
main:
name: Validate PR titles
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Install pnpm
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
# pnpm v11 requires Node.js >= 22.13. The default runner image ships Node
# 20, which causes `pnpm m ls` to crash with ERR_UNKNOWN_BUILTIN_MODULE
# (node:sqlite) and leaves the SCOPES env empty, failing this lint.
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22.22.1'
- name: Generate scopes
id: generate_scopes
run: |
scopes=$(pnpm m ls --json --depth=-1 | grep "name" | sed -e 's/.*\: \(.*\)/\1/' -e 's/@novu\///g' -e 's/[",]//g')
echo 'SCOPES<<EOF' >> $GITHUB_ENV
echo "$scopes" >> $GITHUB_ENV
echo 'EOF' >> $GITHUB_ENV
- name: Check if PR author is team member
id: check_team_member
run: |
author="${{ github.event.pull_request.user.login }}"
echo "Checking if $author is a team member..."
# Trusted bot accounts are treated as team members
trusted_bots=("cursor[bot]")
for bot in "${trusted_bots[@]}"; do
if [[ "$author" == "$bot" ]]; then
echo "is_team_member=true" >> $GITHUB_OUTPUT
echo "$author is a trusted bot, treating as team member"
exit 0
fi
done
# Use GitHub API to check user permissions
response=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/collaborators/$author/permission")
permission=$(echo "$response" | jq -r '.permission // "none"')
echo "Permission level: $permission"
if [[ "$permission" == "admin" || "$permission" == "write" ]]; then
echo "is_team_member=true" >> $GITHUB_OUTPUT
echo "$author is a team member"
else
echo "is_team_member=false" >> $GITHUB_OUTPUT
echo "$author is not a team member"
fi
- name: Extract Linear ticket from branch and auto-fix PR title
id: auto_fix_linear_ticket
if: steps.check_team_member.outputs.is_team_member == 'true'
env:
PR_TITLE: ${{ github.event.pull_request.title }}
BRANCH_NAME: ${{ github.event.pull_request.head.ref }}
run: |
branch_name="$BRANCH_NAME"
pr_title="$PR_TITLE"
echo "Branch name: $branch_name"
echo "Current PR title: $pr_title"
# Extract ticket ID from branch name (e.g., nv-6051 from nv-6051-bug-steps-liquidjs...)
if [[ "$branch_name" =~ ^([a-zA-Z]+-[0-9]+) ]]; then
ticket_id="${BASH_REMATCH[1]^^}" # Convert to uppercase
echo "Found ticket ID in branch: $ticket_id"
# Check if PR title already has the Linear ticket format
if [[ "$pr_title" =~ fixes\ [A-Z]+-[0-9]+$ ]]; then
echo "PR title already contains Linear ticket ID"
echo "needs_update=false" >> $GITHUB_OUTPUT
echo "linear_ticket_valid=true" >> $GITHUB_OUTPUT
else
# Auto-append the Linear ticket ID
new_title="$pr_title fixes $ticket_id"
echo "Auto-fixing PR title to: $new_title"
# Update PR title using GitHub API
curl -X PATCH \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}" \
-d "$(jq -n --arg title "$new_title" '{title: $title}')"
echo "needs_update=true" >> $GITHUB_OUTPUT
echo "linear_ticket_valid=true" >> $GITHUB_OUTPUT
echo "updated_title=$new_title" >> $GITHUB_OUTPUT
fi
else
echo "No Linear ticket ID found in branch name"
# Check if title has Linear ticket format manually added
if [[ "$pr_title" =~ fixes\ [A-Z]+-[0-9]+$ ]]; then
echo "linear_ticket_valid=true" >> $GITHUB_OUTPUT
else
echo "linear_ticket_valid=false" >> $GITHUB_OUTPUT
echo "linear_error_message=PR title must end with 'fixes TICKET-ID' (e.g., 'fixes NOV-123') or include ticket ID in branch name" >> $GITHUB_OUTPUT
fi
echo "needs_update=false" >> $GITHUB_OUTPUT
fi
- name: Validate Linear ticket ID for team members (fallback)
id: validate_linear_ticket
if: steps.check_team_member.outputs.is_team_member == 'true' && steps.auto_fix_linear_ticket.outputs.linear_ticket_valid != 'true'
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
pr_title="$PR_TITLE"
echo "Validating Linear ticket ID in PR title: $pr_title"
# Check if title ends with "fixes TICKET-ID" pattern
if [[ "$pr_title" =~ fixes\ [A-Z]+-[0-9]+$ ]]; then
echo "linear_ticket_valid=true" >> $GITHUB_OUTPUT
echo "Linear ticket ID format is valid"
else
echo "linear_ticket_valid=false" >> $GITHUB_OUTPUT
echo "Linear ticket ID format is invalid"
echo "linear_error_message=PR title must end with 'fixes TICKET-ID' (e.g., 'fixes NOV-123') or include ticket ID in branch name" >> $GITHUB_OUTPUT
fi
- uses: amannn/action-semantic-pull-request@e32d7e603df1aa1ba07e981f2a23455dee596825 # v5
id: lint_pr_title
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
requireScope: true
scopes: |
${{ env.SCOPES }}
- uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2
# Show success message when PR title was auto-fixed
if: steps.auto_fix_linear_ticket.outputs.needs_update == 'true'
with:
header: pr-title-auto-fixed
message: |
✅ **PR title automatically updated!**
I found the Linear ticket ID `${{ steps.auto_fix_linear_ticket.outputs.updated_title }}` in your branch name and automatically added it to your PR title.
**Updated title:** `${{ steps.auto_fix_linear_ticket.outputs.updated_title }}`
- uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2
# Show error if either conventional commit validation fails OR Linear ticket validation fails (for team members)
if: always() && (steps.lint_pr_title.outputs.error_message != null || (steps.check_team_member.outputs.is_team_member == 'true' && steps.auto_fix_linear_ticket.outputs.linear_ticket_valid != 'true' && steps.validate_linear_ticket.outputs.linear_ticket_valid != 'true'))
with:
header: pr-title-lint-error
message: |
Hey there and thank you for opening this pull request! 👋
We require pull request titles to follow specific formatting rules and it looks like your proposed title needs to be adjusted.
Your PR title is: `${{ github.event.pull_request.title }}`
**Requirements:**
1. Follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/)
2. ${{ steps.check_team_member.outputs.is_team_member == 'true' && 'As a team member, include Linear ticket ID at the end: `fixes TICKET-ID` or include it in your branch name' || '' }}
**Expected format:** `feat(scope): Add fancy new feature${{ steps.check_team_member.outputs.is_team_member == 'true' && ' fixes NOV-123' || '' }}`
**Details:**
${{ steps.lint_pr_title.outputs.error_message != null && steps.lint_pr_title.outputs.error_message || '' }}
${{ steps.validate_linear_ticket.outputs.linear_error_message != null && steps.validate_linear_ticket.outputs.linear_error_message || '' }}
# Delete previous comments when all issues have been resolved
- if: ${{ steps.lint_pr_title.outputs.error_message == null && (steps.check_team_member.outputs.is_team_member != 'true' || steps.auto_fix_linear_ticket.outputs.linear_ticket_valid == 'true' || steps.validate_linear_ticket.outputs.linear_ticket_valid == 'true') }}
uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2
with:
header: pr-title-lint-error
delete: true
# Delete auto-fix comment after a while (on subsequent updates)
- if: ${{ steps.auto_fix_linear_ticket.outputs.needs_update != 'true' }}
uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2
with:
header: pr-title-auto-fixed
delete: true