Skip to content

Commit 5d3770b

Browse files
authored
Merge pull request #75 from devtron-labs/multi-issue-fix
Multi issue fix
2 parents c968dbc + 99b7851 commit 5d3770b

File tree

1 file changed

+134
-74
lines changed

1 file changed

+134
-74
lines changed

.github/workflows/validateIssue.sh

Lines changed: 134 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#!/bin/bash
22

3+
# Print base and head repository information
34
echo "base or target repo : $BASE_REPO"
45
echo "head or source repo : $HEAD_REPO"
56

6-
if [[ $HEAD_REPO == $BASE_REPO ]]; then
7+
# Determine if the PR is from a forked repository
8+
if [[ $HEAD_REPO == $BASE_REPO ]]; then
79
export forked=false
810
else
911
export forked=true
@@ -14,112 +16,170 @@ if [[ "$TITLE" =~ ^(doc:|docs:|chore:|misc:|Release:|release:|Sync:|sync:) ]]; t
1416
echo "Skipping validation for docs/chore PR."
1517
echo "PR NUMBER-: $PRNUM "
1618
if [[ "$forked" == "false" ]]; then
19+
# If not a forked PR, remove 'Issue-verification-failed' and add 'Ready-to-Review' label
1720
gh pr edit $PRNUM --remove-label "PR:Issue-verification-failed"
1821
gh pr edit $PRNUM --add-label "PR:Ready-to-Review"
1922
fi
2023
exit 0
2124
fi
2225

2326
# Define all issue matching patterns
27+
# These patterns cover various ways issues can be linked in a PR body
2428
patterns=(
25-
"((Fixes|fixes|Resolves|resolves) #[0-9]+)"
26-
"((Fixes|fixes|Resolves|resolves) https://github.com/devtron-labs/(devtron|sprint-tasks|devops-sprint|devtron-enterprise)/issues/[0-9]+)"
27-
"((Fixes|fixes|Resolves|resolves):? https://github.com/devtron-labs/(devtron|sprint-tasks|devops-sprint|devtron-enterprise)/issues/[0-9]+)"
28-
"((Fixes|fixes|Resolves|resolves) devtron-labs/devtron#[0-9]+)"
29+
"((Fixes|fixes|Resolves|resolves) #[0-9]+)" # e.g., Fixes #123
30+
"((Fixes|fixes|Resolves|resolves) https://github.com/devtron-labs/(devtron|sprint-tasks|devops-sprint|devtron-enterprise)/issues/[0-9]+)" # e.g., Fixes https://github.com/devtron-labs/devtron/issues/123
31+
"((Fixes|fixes|Resolves|resolves):? https://github.com/devtron-labs/(devtron|sprint-tasks|devops-sprint|devtron-enterprise)/issues/[0-9]+)" # e.g., Fixes: https://github.com/devtron-labs/devtron/issues/123
32+
"((Fixes|fixes|Resolves|resolves) devtron-labs/devtron#[0-9]+)" # e.g., Fixes devtron-labs/devtron#123
2933
"((Fixes|fixes|Resolves|resolves) devtron-labs/sprint-tasks#[0-9]+)"
3034
"((Fixes|fixes|Resolves|resolves) devtron-labs/devops-sprint#[0-9]+)"
31-
"(Fixes|fixes|Resolves|resolves):?\\s+\\[#([0-9]+)\\]"
32-
"((Fixes|fixes|Resolves|resolves):? #devtron-labs/devops-sprint/issues/[0-9]+)"
35+
"(Fixes|fixes|Resolves|resolves):?\\s+\\[#([0-9]+)\\]" # e.g., Fixes [#123]
36+
"((Fixes|fixes|Resolves|resolves):? #devtron-labs/devops-sprint/issues/[0-9]+)" # e.g., Fixes: #devtron-labs/devops-sprint/issues/123
3337
"((Fixes|fixes|Resolves|resolves):? #devtron-labs/sprint-tasks/issues/[0-9]+)"
3438
)
3539

36-
# Extract issue number and repo from PR body
37-
extract_issue_number() {
38-
local pattern="$1" # Get the pattern as the first argument to the function
39-
40-
# Check if PR_BODY matches the provided pattern using Bash's =~ regex operator
41-
if [[ "$PR_BODY" =~ $pattern ]]; then
42-
echo "matched for this pattern $pattern"
43-
44-
issue_num=$(echo "$PR_BODY" | grep -oE "$pattern" | grep -oE "[0-9]+")
45-
46-
# Extract the repository name (e.g., devtron-labs/devtron) from PR_BODY using grep
47-
repo=$(echo "$PR_BODY" | grep -oE "devtron-labs/[a-zA-Z0-9_-]+")
48-
echo "Extracted issue number: $issue_num from repo: $repo"
49-
50-
return 0 # Return success
51-
else
52-
echo "No match for the pattern $pattern"
53-
fi
54-
return 1 # Return failure if no match
40+
# Function to extract all unique issue numbers and their corresponding repositories from PR_BODY
41+
# It iterates through defined patterns and extracts all matches.
42+
# Returns a list of "issue_num,repo" pairs, one per line, to standard output.
43+
# All diagnostic messages are redirected to stderr to prevent interference with readarray.
44+
extract_all_issues() {
45+
# Removed 'local' keyword for array declaration
46+
found_issues=()
47+
# Removed 'local' keyword for variable declaration
48+
default_repo="devtron-labs/devtron" # Default repository if not explicitly mentioned in the link
49+
50+
# Loop through each defined pattern
51+
for pattern in "${patterns[@]}"; do
52+
# Use grep -oE to find all non-overlapping matches for the current pattern in PR_BODY
53+
matches=$(echo "$PR_BODY" | grep -oE "$pattern")
54+
55+
# If matches are found for the current pattern
56+
if [[ -n "$matches" ]]; then
57+
# IMPORTANT: Redirect all diagnostic echo statements to stderr (>&2)
58+
echo "Matched for pattern: $pattern" >&2
59+
# Read each match into the 'match' variable
60+
while IFS= read -r match; do
61+
# Extract the issue number (sequence of digits) from the matched string
62+
# Removed 'local' keyword for variable declaration
63+
current_issue_num=$(echo "$match" | grep -oE "[0-9]+")
64+
# Extract the repository name (e.g., devtron-labs/devtron) from the matched string
65+
# Removed 'local' keyword for variable declaration
66+
current_repo=$(echo "$match" | grep -oE "devtron-labs/[a-zA-Z0-9_-]+")
67+
68+
# If no specific repository is found in the link, use the default
69+
if [[ -z "$current_repo" ]]; then
70+
current_repo="$default_repo"
71+
fi
72+
73+
# If a valid issue number was extracted, add it to the list
74+
if [[ -n "$current_issue_num" ]]; then
75+
found_issues+=("$current_issue_num,$current_repo")
76+
# IMPORTANT: Redirect all diagnostic echo statements to stderr (>&2)
77+
echo "Extracted issue: $current_issue_num from repo: $current_repo" >&2
78+
fi
79+
done <<< "$matches" # Use a here-string to feed matches into the while loop
80+
fi
81+
done
82+
# Print unique issue-repo pairs, sorted, to standard output for readarray
83+
printf "%s\n" "${found_issues[@]}" | sort -u
5584
}
5685

57-
issue_num=""
58-
repo="devtron-labs/devtron" # Default repo
59-
for pattern in "${patterns[@]}"; do
60-
echo "Now checking for $pattern"
61-
extract_issue_number "$pattern" && break
62-
done
86+
# Call the function to extract all unique issue-repo pairs and store them in an array
87+
readarray -t all_issues < <(extract_all_issues)
6388

64-
if [[ -z "$issue_num" ]]; then
65-
echo "No valid issue number found."
89+
# Check if any issues were found in the PR body
90+
if [[ ${#all_issues[@]} -eq 0 ]]; then
91+
echo "No valid issue number found in PR body."
6692
if [[ "$forked" == "false" ]]; then
93+
# If not a forked PR, add 'Issue-verification-failed' and remove 'Ready-to-Review' label
6794
gh pr edit $PRNUM --add-label "PR:Issue-verification-failed"
6895
gh pr edit $PRNUM --remove-label "PR:Ready-to-Review"
6996
fi
7097
exit 1
7198
fi
7299

73-
# Form the issue API URL dynamically
74-
issue_api_url="https://api.github.com/repos/$repo/issues/$issue_num"
75-
echo "API URL: $issue_api_url"
100+
# Initialize a flag to track overall validation status and a string to collect failed links
101+
all_issues_valid=true
102+
failed_issue_links=""
76103

77-
if [[ $repo == "devtron-labs/devtron" || $repo == "devtron-labs/devtron-services" || $repo == "devtron-labs/dashboard" ]]; then
78-
echo "No extra arguments needed: public repository detected."
79-
response=$(curl -s -w "%{http_code}" "$issue_api_url") # Get the response body and status code in one go
80-
else
81-
echo "Adding extra arguments for authentication: private repository detected."
82-
response=$(curl -s -w "%{http_code}" --header "Authorization: Bearer $GH_PR_VALIDATOR_TOKEN" \
83-
--header "Accept: application/vnd.github+json" "$issue_api_url")
84-
fi
104+
# Loop through each unique issue-repo pair found
105+
for issue_repo_pair in "${all_issues[@]}"; do
106+
# Split the pair into issue_num and repo using comma as delimiter
107+
# Variables are now global, no 'local' needed here
108+
IFS=',' read -r issue_num repo <<< "$issue_repo_pair"
85109

86-
# Extract HTTP status code from the response
87-
response_code=$(echo "$response" | tail -n 1) # Status code is the last line
88-
response_body=$(echo "$response" | head -n -1) # The body is everything except the last line (status code)
110+
echo "Validating issue number: #$issue_num in repo: $repo"
89111

90-
echo "Response Code: $response_code"
91-
html_url=$(echo "$response_body" | jq -r '.html_url') # Extract html_url from the JSON response
112+
# Form the GitHub API URL for the issue
113+
issue_api_url="https://api.github.com/repos/$repo/issues/$issue_num"
114+
echo "API URL: $issue_api_url"
92115

93-
# Check if the html_url contains "pull-request"
94-
if [[ "$html_url" == *"pull"* ]]; then
95-
echo "The issue URL contains a pull-request link, marking as invalid."
96-
gh pr comment $PRNUM --body "PR is linked to a pull request URL, which is invalid. Please update the issue link."
116+
# Variables are now global, no 'local' needed here
117+
response=""
118+
response_code=""
119+
response_body=""
97120

98-
if [[ "$forked" == "false" ]]; then
99-
# Apply 'Issue-verification-failed' label and remove 'Ready-to-Review' label.
100-
gh pr edit $PRNUM --add-label "PR:Issue-verification-failed"
101-
gh pr edit $PRNUM --remove-label "PR:Ready-to-Review"
121+
# Determine if the repository is public or private to apply authentication
122+
if [[ "$repo" == "devtron-labs/devtron" || "$repo" == "devtron-labs/devtron-services" || "$repo" == "devtron-labs/dashboard" ]]; then
123+
echo "No extra arguments needed: public repository detected."
124+
# Use curl to get the response body and HTTP status code
125+
response=$(curl -s -w "%{http_code}" "$issue_api_url")
126+
else
127+
echo "Adding extra arguments for authentication: private repository detected."
128+
# Use curl with authentication headers for private repositories
129+
response=$(curl -s -w "%{http_code}" --header "Authorization: Bearer $GH_PR_VALIDATOR_TOKEN" \
130+
--header "Accept: application/vnd.github+json" "$issue_api_url")
102131
fi
103-
exit 1
104-
fi
105132

106-
# If response_code is 200, proceed with validating the issue
107-
if [[ "$response_code" -eq 200 ]]; then
108-
echo "Issue Number: #$issue_num is valid and exists in Repo: $repo."
109-
if [[ "$forked" == "false" ]]; then
110-
gh pr edit $PRNUM --remove-label "PR:Issue-verification-failed"
111-
gh pr edit $PRNUM --add-label "PR:Ready-to-Review"
133+
# Extract HTTP status code (last line of curl output) and response body
134+
response_code=$(echo "$response" | tail -n 1)
135+
response_body=$(echo "$response" | head -n -1)
136+
137+
echo "Response Code: $response_code"
138+
# Extract the 'html_url' from the JSON response body using jq
139+
# Variable is now global, no 'local' needed here
140+
html_url=$(echo "$response_body" | jq -r '.html_url')
141+
142+
# Check if the extracted URL points to a pull request instead of an issue
143+
if [[ "$html_url" == *"pull"* ]]; then
144+
echo "The issue URL contains a pull-request link, marking as invalid."
145+
# Append error message to the failed_issue_links string
146+
failed_issue_links+="Issue #$issue_num in $repo is linked to a pull request URL, which is invalid.\n"
147+
all_issues_valid=false # Set overall status to invalid
148+
elif [[ "$response_code" -eq 200 ]]; then
149+
# If HTTP status code is 200, the issue is valid
150+
echo "Issue Number: #$issue_num is valid and exists in Repo: $repo."
151+
else
152+
# If issue not found or invalid HTTP status code
153+
echo "Issue not found. Invalid URL or issue number: #$issue_num in $repo."
154+
failed_issue_links+="Issue #$issue_num in $repo is not found or invalid (HTTP $response_code).\n"
155+
all_issues_valid=false # Set overall status to invalid
112156
fi
113-
echo "PR:Ready-to-Review, exiting gracefully"
114-
exit 0
115-
else
116-
echo "Issue not found. Invalid URL or issue number."
117-
gh pr comment $PRNUM --body "PR is not linked to a valid issue. Please update the issue link."
157+
done
118158

119-
if [[ "$forked" == "false" ]]; then
120-
# Apply 'Issue-verification-failed' label and remove 'Ready-to-Review' label.
159+
# Final label application and comments based on the overall validation status
160+
if [[ "$forked" == "false" ]]; then
161+
# If not a forked PR, modify labels and add comments
162+
if [[ "$all_issues_valid" == "true" ]]; then
163+
# All issues are valid, remove 'Issue-verification-failed' and add 'Ready-to-Review'
164+
gh pr edit $PRNUM --remove-label "PR:Issue-verification-failed"
165+
gh pr edit $PRNUM --add-label "PR:Ready-to-Review"
166+
echo "All linked issues are valid. PR:Ready-to-Review."
167+
exit 0
168+
else
169+
# Some issues are invalid, add a comment with details and update labels
170+
gh pr comment $PRNUM --body "Some linked issues are invalid. Please update the issue links:\n$failed_issue_links"
121171
gh pr edit $PRNUM --add-label "PR:Issue-verification-failed"
122172
gh pr edit $PRNUM --remove-label "PR:Ready-to-Review"
173+
echo "Some linked issues are invalid. PR:Issue-verification-failed."
174+
exit 1
175+
fi
176+
else
177+
# For forked PRs, just output the status, do not modify labels
178+
if [[ "$all_issues_valid" == "true" ]]; then
179+
echo "All linked issues are valid for forked PR."
180+
exit 0
181+
else
182+
echo "Some linked issues are invalid for forked PR:\n$failed_issue_links"
183+
exit 1
123184
fi
124-
exit 1
125185
fi

0 commit comments

Comments
 (0)