Skip to content

Update the README to explain how this project works #3

Update the README to explain how this project works

Update the README to explain how this project works #3

Workflow file for this run

name: Send PR Diff Patch
on:
issue_comment:
branches:
- "main"
- "main-aie"
types: [created]
pull_request_review:
branches:
- "main"
- "main-aie"
types: [submitted]
jobs:
check-target-branch:
runs-on: self-hosted
outputs:
is_correct_branch: ${{ steps.check-branch.outputs.is_correct_branch }}
pr_number: ${{ steps.set-pr-number.outputs.pr_number }} # Output the PR number here for other jobs
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set PR Number
continue-on-error: true
id: set-pr-number
run: |
if [ "${{ github.event_name }}" == "issue_comment" ]; then
echo "::set-output name=pr_number::${{ github.event.issue.number }}"
elif [ "${{ github.event_name }}" == "pull_request_review" ]; then
echo "::set-output name=pr_number::${{ github.event.pull_request.number }}"
fi
- name: Retrieve PR Data for Branch Check
id: check-branch
env:
GH_TOKEN: ${{ secrets.GHB_TKN }}
run: |
PR_DATA=$(gh api repos/${{ github.repository }}/pulls/${{ steps.set-pr-number.outputs.pr_number }})
TARGET_BRANCH=$(echo "$PR_DATA" | jq -r '.base.ref')
echo "::set-output name=is_correct_branch::$(echo "$TARGET_BRANCH" | grep -E '^(main|main-aie)$')"
check-approvals:
runs-on: self-hosted
needs: check-target-branch
if: needs.check-target-branch.outputs.is_correct_branch
outputs:
all_conditions_met: ${{ steps.approval-check.outputs.all_conditions_met }}
first_approver_name: ${{ steps.approval-check.outputs.first_approver_name }}
first_approver_login: ${{ steps.approval-check.outputs.first_approver_login }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set PR Number
continue-on-error: true
id: set-pr-number
run: |
if [ "${{ github.event_name }}" == "issue_comment" ]; then
echo "::set-output name=pr_number::${{ github.event.issue.number }}"
else
echo "::set-output name=pr_number::${{ github.event.pull_request.number }}"
fi
- name: Retrieve PR Data for Issue Comments
if: github.event_name == 'issue_comment'
id: retrieve-pr-data
env:
GH_TOKEN: ${{ secrets.GHB_TKN }}
run: |
PR_DATA=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }})
PR_BASE_REF=$(echo "$PR_DATA" | jq -r .base.ref)
echo "::set-output name=target_branch::$PR_BASE_REF"
- name: Check if all reviewers approved
if: (github.event_name == 'issue_comment' && github.event.comment.body == 'approved' && (steps.retrieve-pr-data.outputs.target_branch == 'main' || steps.retrieve-pr-data.outputs.target_branch == 'main-aie')) ||
(github.event_name == 'pull_request_review' && (github.event.pull_request.base.ref == 'main' || github.event.pull_request.base.ref == 'main-aie') && github.event.review.state == 'approved')
continue-on-error: true
id: approval-check
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
sleep 5
PR_NUMBER=${{ steps.set-pr-number.outputs.pr_number }} # Use the output from set-pr-number step here
echo "PR Number: $PR_NUMBER"
REVIEWS_JSON=$(gh api repos/${{ github.repository }}/pulls/${PR_NUMBER}/reviews)
APPROVED_REVIEWERS=$(echo "$REVIEWS_JSON" | jq '[.[] | select(.state=="APPROVED") | .user.login] | unique')
echo "APPROVED_REVIEWERS are $APPROVED_REVIEWERS"
# Check if there is at least one approval
if [ $(echo "$APPROVED_REVIEWERS" | jq 'length') -le 0 ]; then
#echo "Need one developer approval and review bot approval, but not found."
echo "Need one developer approval, but not found."
gh pr comment $PR_NUMBER --body "**[GITHUB ACTIONS]** :x: The process of generating and sending the patch email failed. Atleast one developer reviewer approval needed! :x: ."
exit 1
fi
# Check if developer reviewer approved
# FixMe Disable Review bot mandatory approve check to addressing review bot service broken issue
if [ $(echo "$APPROVED_REVIEWERS" | jq 'length') -eq 1 ]; then
if echo "$APPROVED_REVIEWERS" | jq -r '.[]' | grep -q 'bot'; then
echo "Developer Reviewer approvals not found."
gh pr comment $PR_NUMBER --body "**[GITHUB ACTIONS]** :x: The process of generating and sending the patch email failed. Review bot approval needed! :x: ."
exit 1
fi
fi
# Fetch the login of the first approver
FIRST_APPROVER_LOGIN=$(echo "$REVIEWS_JSON" | jq -r '[.[] | select(.state=="APPROVED")][0].user.login')
echo "FIRST APPROVED LOGIN: $FIRST_APPROVER_LOGIN"
# Fetch the URL for the first approver's details
FIRST_APPROVER_URL=$(echo "$REVIEWS_JSON" | jq -r '[.[] | select(.state=="APPROVED")][0].user.url')
echo "FIRST APPROVED URL: $FIRST_APPROVER_URL"
sleep 5
# Now, make a curl request to the first approver's URL to fetch their full name
FIRST_APPROVER_NAME=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" $FIRST_APPROVER_URL | jq -r '.name')
echo "FIRST APPROVED NAME: $FIRST_APPROVER_NAME"
sleep 5
DISTINCT_REVIEWERS=$(echo "$REVIEWS_JSON" | jq '[.[] | .user.login] | unique')
echo "TOTAL DISTINCT REVIEWERS: $DISTINCT_REVIEWERS"
# Fetch PR's SHA
PR_SHA=$(gh api repos/${{ github.repository }}/pulls/${PR_NUMBER} --jq ".head.sha")
echo "PR SHA: $PR_SHA"
# Fetch combined status of all checks for the PR's commit
sleep 5
COMMIT_STATUS=$(gh api repos/${{ github.repository }}/commits/${PR_SHA}/status --jq ".state")
echo "Commit Status: $COMMIT_STATUS"
MAX_RETRIES=10
RETRY_COUNT=0
SLEEP_DURATION=40
while [[ "$COMMIT_STATUS" == "pending" && $RETRY_COUNT -lt $MAX_RETRIES ]]; do
echo "Checks are still running. Waiting for them to complete..."
sleep $SLEEP_DURATION
COMMIT_STATUS=$(gh api repos/${{ github.repository }}/commits/${PR_SHA}/status --jq ".state")
echo "Commit Status after retry #${RETRY_COUNT} is ${COMMIT_STATUS}"
RETRY_COUNT=$((RETRY_COUNT + 1))
done
if [[ "$RETRY_COUNT" == "$MAX_RETRIES" ]]; then
echo "Max retries reached. Please check the checks manually."
gh pr comment $PR_NUMBER --body "[GITHUB ACTIONS] :x: The process of generating and sending the patch email failed. Please ensure all checks are passing before approving. :x: "
exit 1
fi
# Check if PR is mergeable
PR_MERGEABLE=$(gh api repos/${{ github.repository }}/pulls/${PR_NUMBER} --jq ".mergeable_state")
echo "PR Mergeable Status: $PR_MERGEABLE"
if [[ "$PR_MERGEABLE" == "behind" || "$PR_MERGEABLE" == "dirty" ]]; then
echo "PR has merge conflicts. Source repo is behind the target repo."
echo "::set-output name=all_conditions_met::false"
if [ "${{ github.event_name }}" == "issue_comment" ]; then
gh pr comment $PR_NUMBER --body "**[GITHUB ACTIONS]** :x: This PR is not up-to-date with the destination. Please pull/update! :x: "
fi
exit 1
fi
echo "APPROVED_COUNT $APPROVED_COUNT"
echo "TOTAL_REVIEWERS $TOTAL_REVIEWERS"
echo "COMMIT_STATUS $COMMIT_STATUS"
# Convert arrays to strings for comparison
APPROVED_REVIEWERS_STR="${APPROVED_REVIEWERS[*]}"
DISTINCT_REVIEWERS_STR="${DISTINCT_REVIEWERS[*]}"
echo "APPROVED_REVIEWERS_STR: $APPROVED_REVIEWERS_STR"
echo "DISTINCT_REVIEWERS_STR: $DISTINCT_REVIEWERS_STR"
# Convert arrays to strings for comparison
APPROVED_REVIEWERS_STR="${APPROVED_REVIEWERS[*]}"
DISTINCT_REVIEWERS_STR="${DISTINCT_REVIEWERS[*]}"
echo "APPROVED_REVIEWERS_STR: $APPROVED_REVIEWERS_STR"
echo "DISTINCT_REVIEWERS_STR: $DISTINCT_REVIEWERS_STR"
# Check if there is at least one approving review
if [[ -n "$APPROVED_REVIEWERS_STR" && "$COMMIT_STATUS" == "success" ]]; then
echo "PR CAN BE APPROVED"
echo "::set-output name=all_conditions_met::true"
echo "::set-output name=first_approver_name::$FIRST_APPROVER_NAME"
echo "::set-output name=first_approver_login::$FIRST_APPROVER_LOGIN"
elif [[ -z "$APPROVED_REVIEWERS_STR" ]]; then
echo "PR CANNOT BE APPROVED"
echo "::set-output name=all_conditions_met::false"
gh pr comment $PR_NUMBER --body "**[GITHUB ACTIONS]** :x: No reviewers have approved. At least one approving review is required. :x:"
elif [[ "$COMMIT_STATUS" != "success" ]]; then
echo "PR CANNOT BE APPROVED"
echo "::set-output name=all_conditions_met::false"
gh pr comment $PR_NUMBER --body "**[GITHUB ACTIONS]** :x: The commit status is not successful. Please ensure all checks are passing before approving. :x:"
else
echo "PR CANNOT BE APPROVED"
echo "::set-output name=all_conditions_met::false"
gh pr comment $PR_NUMBER --body "**[GITHUB ACTIONS]** :x: An unknown issue has occurred. Please check the PR conditions and try again. :x:"
fi
- name: Generate and Send Patch Email
continue-on-error: true
if: steps.approval-check.outputs.all_conditions_met == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Extract Information from GitHub Event
PR_NUMBER=${{ steps.set-pr-number.outputs.pr_number }}
echo "PR Number: $PR_NUMBER"
REPO_FULL_NAME=$(jq --raw-output .repository.full_name "$GITHUB_EVENT_PATH")
echo "Repository Full Name: $REPO_FULL_NAME"
# Fetch PR details using gh API with PR_NUMBER
PR_DETAILS=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER)
# Extract Issue (PR) Title
ISSUE_TITLE=$(echo "$PR_DETAILS" | jq --raw-output .title)
echo "Issue Title: $ISSUE_TITLE"
# Extract Pull Request URL
PULL_REQ_URL=$(echo "$PR_DETAILS" | jq --raw-output .html_url)
echo "Pull Request URL: $PULL_REQ_URL"
# Get the last review from the pull request
LAST_REVIEW=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER/reviews -q ".[-1]")
APPROVER_NAME=$(echo "$LAST_REVIEW" | jq --raw-output .user.login)
echo "Approver Name: $APPROVER_NAME"
# Fetch PR Patch
# Extract source and target branch names
SOURCE_BRANCH=$(echo "$PR_DETAILS" | jq --raw-output .head.ref)
TARGET_BRANCH=$(echo "$PR_DETAILS" | jq --raw-output .base.ref)
# Clone the target repository
git clone https://gitenterprise.xilinx.com/${{ github.repository }}.git target-repo
cd target-repo
# Fetch PR details using gh API with PR_NUMBER
PR_DETAILS=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER)
# Extract Pull Request Description
PR_DESCRIPTION=$(echo "$PR_DETAILS" | jq -r '.body')
echo "Pull Request Description: $PR_DESCRIPTION"
# Extract source repository URL and source branch name
SOURCE_REPO_URL=$(echo "$PR_DETAILS" | jq --raw-output .head.repo.clone_url)
SOURCE_BRANCH=$(echo "$PR_DETAILS" | jq --raw-output .head.ref)
TARGET_BRANCH=$(echo "$PR_DETAILS" | jq --raw-output .base.ref)
# Checkout the target branch
git checkout $TARGET_BRANCH
# Fetch the PR's merge commit
git fetch origin pull/$PR_NUMBER/merge:pr-merge
# Create a diff summary and save it to a file
git diff --stat $TARGET_BRANCH pr-merge > ../diff-summary.txt
# Create a patch file with the difference between the target branch and the PR's merge commit
git diff $TARGET_BRANCH pr-merge > ../pr_1.patch
# Move back to the original directory
cd ..
# Extract all the approved reviewers' logins
sleep 5
REVIEWS_JSON=$(gh api repos/${{ github.repository }}/pulls/${PR_NUMBER}/reviews)
APPROVED_REVIEWERS_LOGINS=$(echo "$REVIEWS_JSON" | jq -r '[.[] | select(.state=="APPROVED") | .user.login] | unique | .[]')
for LOGIN in $APPROVED_REVIEWERS_LOGINS; do
# bypass the review bot which contain the name "bot"
if [[ "$LOGIN" == *bot* ]]; then
continue
fi
EMAIL="${LOGIN}@amd.com"
CC_EMAILS_ARRAY+=( "--cc" "${EMAIL}" )
done
# Fetch submitter details
SUBMITTER_LOGIN=$(echo "$PR_DETAILS" | jq -r ".user.login")
echo "Submitter Login: $SUBMITTER_LOGIN"
SUBMITTER_DETAILS_URL=$(echo "$PR_DETAILS" | jq -r ".user.url")
echo "Submitter Details URL: $SUBMITTER_DETAILS_URL"
sleep 5
SUBMITTER_DETAILS=$(curl -H "Authorization: token $GITHUB_TOKEN" -s $SUBMITTER_DETAILS_URL)
echo "$SUBMITTER_DETAILS"
SUBMITTER_NAME=$(echo "$SUBMITTER_DETAILS" | jq -r ".name // .login") # Use login as a fallback
echo "Submitter Name: $SUBMITTER_NAME"
# Format Submitter Name, replacing spaces with dots
FORMATTED_SUBMITTER_NAME=$(echo "$SUBMITTER_NAME" | tr ' ' '.')
# Build submitter email ID
SUBMITTER_EMAIL="${FORMATTED_SUBMITTER_NAME}@amd.com"
echo "Submitter Email: $SUBMITTER_EMAIL"
echo "PR Details: $PR_DETAILS"
PR_BODY=$(echo "$PR_DETAILS" | jq -r '.body')
echo "PR Body: $PR_BODY"
ID_INFOS=$(echo "$PR_BODY" | grep -oiP '\b(cr|pr|jira)-\d+\b' | tr '\n' ',' | sed 's/,$//')
echo "ID Infos: $ID_INFOS"
if [ -z "$ID_INFOS" ]; then
echo "No valid ID number found in PR body"
# gh pr comment $PR_NUMBER --body "**[GITHUB ACTIONS]** :x: No valid ID number was tagged in the PR body. Please tag a CR, PR or JIRA ID tag before approving. :x:"
# exit 1
else
echo "ID Infos: $ID_INFOS"
fi
SIGN_OFF="Signed-off-by: ${SUBMITTER_NAME} <${SUBMITTER_EMAIL}>"
# Modify PR Patch with additional details
# Insert PR Description at the top of the patch
echo -e "From: ${SUBMITTER_NAME} <${SUBMITTER_EMAIL}>\nSubject: [AIENGINE PATCH] ${ISSUE_TITLE}\n" >> pr.patch
echo -e "PR Description:\n$PR_DESCRIPTION\n\n" >> pr.patch
echo -e "---\nBRANCH: ${TARGET_BRANCH}\n---\n$ID_INFOS\n---\n$SIGN_OFF\n---\nPR URL: $PULL_REQ_URL\n---\n" >> pr.patch
cat diff-summary.txt >> pr.patch # Write the contents of diff-summary.txt to pr.patch
echo -e "\n" >> pr.patch # Append a newline to pr.patch
cat pr_1.patch >> pr.patch # Append the contents of pr_1.patch to pr.patch
echo "pr.patch +++++++++++++++++++++++++++++++++++"
cat pr.patch
echo "pr.patch -----------------------------------"
# Store the original git config
ORIGINAL_GIT_USER_EMAIL=$(git config --global user.email)
ORIGINAL_GIT_USER_NAME=$(git config --global user.name)
# Configure git with the new user details
git config --global user.email "${SUBMITTER_EMAIL}"
git config --global user.name "${SUBMITTER_NAME}"
# Send the email with the patch
git send-email --confirm=never --force --to "[email protected]" "${CC_EMAILS_ARRAY[@]}" --attach pr.patch
# Comment on the PR
gh pr comment $PR_NUMBER --body "**[GITHUB ACTIONS]** :white_check_mark: Patch approved! Patch E-Mail/s Sent out! :white_check_mark:"
# Restore the original git config
git config --global user.email "${ORIGINAL_GIT_USER_EMAIL}"
git config --global user.name "${ORIGINAL_GIT_USER_NAME}"
- name: Close Pull Request
if: steps.approval-check.outputs.all_conditions_met == 'true' # Only run this step if the patch was approved and sent
run: |
PR_NUMBER=${{ steps.set-pr-number.outputs.pr_number }}
echo "Closing PR Number: $PR_NUMBER"
gh pr close $PR_NUMBER --repo ${{ github.repository }}
echo "Pull Request #$PR_NUMBER has been closed."
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}