-
Notifications
You must be signed in to change notification settings - Fork 0
150 lines (133 loc) · 6.04 KB
/
check_pr_scope.yml
File metadata and controls
150 lines (133 loc) · 6.04 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
name: Check PR Scope
on:
pull_request:
types: [opened, synchronize, reopened, edited]
jobs:
check_scope:
runs-on: ubuntu-latest
outputs: # Define outputs for the job
outcome: ${{ steps.check_scope_step.outcome }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Fetch all history for all branches and tags
- name: Get PR title
id: pr_title
run: echo "title=$(jq -r .pull_request.title \"$GITHUB_EVENT_PATH\")" >> $GITHUB_OUTPUT
- name: Get changed files
id: changed_files
run: |
files=$(git diff --name-only origin/${{ github.base_ref }}...origin/${{ github.head_ref }})
echo "files<<EOF" >> $GITHUB_OUTPUT
echo "$files" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Check PR scope
id: check_scope_step # Add id to this step
run: |
pr_title="${{ steps.pr_title.outputs.title }}"
changed_files="${{ steps.changed_files.outputs.files }}"
echo "PR Title: $pr_title"
echo "Changed Files:"
echo "$changed_files"
# Extract scope from PR title (e.g., "fix(scope1,scope2): ...")
pr_scopes_str=$(echo "$pr_title" | grep -oP '^\w+\(\K[^\)]+' || echo "")
IFS=',' read -r -a pr_scopes <<< "$pr_scopes_str"
# Trim whitespace from scopes
pr_scopes=($(for scope in "${pr_scopes[@]}"; do echo "$scope" | xargs; done))
if [ ${#pr_scopes[@]} -eq 0 ] && [[ "$pr_scopes_str" != "*" ]]; then
error_message="PR title does not contain a valid scope. Please use the format 'type(scope): message' or 'type(*): message'."
echo "::error::$error_message"
comment_body="**Scope Check Failed!** 🚨\n\n${error_message}\n\nExample: \`feat(my-scope): add new feature\` or \`fix(*): resolve an issue\`."
echo "comment_body=$comment_body" >> $GITHUB_ENV
exit 1
fi
echo "PR Scopes: ${pr_scopes[@]}"
# Load scope rules
if [ ! -f "scope-rules.json" ]; then
echo "::error::scope-rules.json not found."
exit 1
fi
rules=$(cat scope-rules.json | jq -r '.rules')
required_scopes=()
while IFS= read -r file; do
matched_scope=""
for i in $(seq 0 $(($(echo "$rules" | jq length) - 1))); do
pattern=$(echo "$rules" | jq -r ".[$i].pattern")
scope_template=$(echo "$rules" | jq -r ".[$i].scope")
if [[ "$file" =~ $pattern ]]; then
# Handle scope template with capture groups (e.g., $1)
if [[ "$scope_template" == "\$1" ]]; then
# BASH_REMATCH[0] is the full match, BASH_REMATCH[1] is the first capture group
matched_scope="${BASH_REMATCH[1]}"
else
matched_scope="$scope_template"
fi
break
fi
done
if [ -n "$matched_scope" ]; then
# Add to required_scopes if not already present
if [[ ! " ${required_scopes[@]} " =~ " ${matched_scope} " ]]; then
required_scopes+=("$matched_scope")
fi
else
# If no rule matches, consider it an error or a default scope based on requirements
# For now, let's assume if a file doesn't match any rule, it's an issue.
# Or, define a default scope in scope-rules.json like { "pattern": ".*", "scope": "default" }
echo "::warning::No matching scope rule for file: $file"
fi
done <<< "$changed_files"
echo "Required Scopes based on changed files: ${required_scopes[@]}"
if [[ " ${pr_scopes[@]} " =~ " * " ]]; then
echo "Wildcard scope '*' in PR title allows all changes."
exit 0
fi
missing_scopes=()
for req_scope in "${required_scopes[@]}"; do
is_covered=false
for pr_scope in "${pr_scopes[@]}"; do
if [ "$req_scope" == "$pr_scope" ]; then
is_covered=true
break
fi
done
if [ "$is_covered" == false ]; then
missing_scopes+=("$req_scope")
fi
done
if [ ${#missing_scopes[@]} -gt 0 ]; then
echo "::error::PR title scopes do not cover all changed files. Missing scopes for: ${missing_scopes[@]}"
# Prepare message for PR comment
comment_body="**Scope Check Failed!** 🚨\n\nPR title scopes do not cover all changed files.\n\n"
comment_body+="Missing required scopes in PR title: \`${missing_scopes[@]}\`\n\n"
comment_body+="Please update your PR title to include these scopes. For example: \`type(${pr_scopes_str:+${pr_scopes_str},}${missing_scopes[@]}): your message\` or \`type(*): your message\` if a wildcard is appropriate.\n\n"
comment_body+="<details><summary>Details</summary>\n"
comment_body+="PR Title Scopes: \`${pr_scopes[@]}\`\n"
comment_body+="Required Scopes from changed files: \`${required_scopes[@]}\`\n"
comment_body+="Changed Files:\n"
comment_body+="\`\`\`\n${changed_files}\n\`\`\`\n"
comment_body+="</details>"
echo "comment_body=$comment_body" >> $GITHUB_ENV
exit 1
else
echo "PR title scopes are valid."
fi
comment_on_pr:
if: failure() && needs.check_scope.outputs.outcome == 'failure'
needs: check_scope
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Comment on PR
uses: actions/github-script@v6
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const body = `${process.env.comment_body}`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});