Skip to content

Port PR 18: RFC Feedback Fixes #5

Port PR 18: RFC Feedback Fixes

Port PR 18: RFC Feedback Fixes #5

Workflow file for this run

# create a backport/forwardport of a PR when "/backport <milestone>" is commented
name: Port PR
run-name: "Port PR ${{ github.event.issue.number }}: ${{ github.event.issue.title }}"
on:
issue_comment:
types:
- created
env:
ORIGINAL_ISSUE_NUMBER: ${{ github.event.issue.number }}
jobs:
port-pr:
permissions:
pull-requests: write
runs-on: ubuntu-latest
if: ${{ github.event.issue.pull_request && (startsWith(github.event.comment.body, '/backport') || startsWith(github.event.comment.body, '/forwardport')) }}
steps:
- name: Check org membership or repo ownership
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Check if the repository owner is an organization
is_org=$(gh api users/${GITHUB_REPOSITORY_OWNER} | jq -r '.type == "Organization"')
if [[ "$is_org" == "true" ]]; then
# Check if the actor is a member of the organization
# User's membership must be set public
if gh api orgs/${GITHUB_REPOSITORY_OWNER}/members --paginate | jq -e --arg GITHUB_ACTOR "$GITHUB_ACTOR" '.[] | select(.login == $GITHUB_ACTOR)' > /dev/null; then
echo "${GITHUB_ACTOR} is a member"
echo "is_member=true" >> $GITHUB_ENV
else
echo "${GITHUB_ACTOR} is not a member of ${GITHUB_REPOSITORY_OWNER}" >> $GITHUB_STEP_SUMMARY
echo "is_member=false" >> $GITHUB_ENV
fi
else
# If the owner is not an organization, treat it as an individual repo
if [[ "$GITHUB_REPOSITORY_OWNER" == "$GITHUB_ACTOR" ]]; then
echo "${GITHUB_ACTOR} is the repository owner"
echo "is_member=true" >> $GITHUB_ENV
else
echo "${GITHUB_ACTOR} is not the repository owner" >> $GITHUB_STEP_SUMMARY
echo "is_member=false" >> $GITHUB_ENV
fi
fi
- name: Check milestone
if: ${{ env.is_member == 'true' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COMMENT_BODY: ${{ github.event.comment.body }}
run: |
BODY_MILESTONE=$(echo "${COMMENT_BODY}" | awk '{ print $2 }')
# Sanitize input
MILESTONE=${BODY_MILESTONE//[^a-zA-Z0-9\-\.]/}
if gh api repos/${GITHUB_REPOSITORY}/milestones --paginate | jq -e --arg MILESTONE "$MILESTONE" '.[] | select(.title == $MILESTONE)' > /dev/null; then
echo "Milestone ${MILESTONE} exists" >> $GITHUB_STEP_SUMMARY
echo "milestone_exists=true" >> $GITHUB_ENV
echo "milestone=${MILESTONE}" >> $GITHUB_ENV
else
echo "Milestone ${MILESTONE} does not exist" >> $GITHUB_STEP_SUMMARY
echo "milestone_exists=false" >> $GITHUB_ENV
fi
- name: Get target branch
if: ${{ env.is_member == 'true' }}
env:
COMMENT_BODY: ${{ github.event.comment.body }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TYPE=$(echo "${COMMENT_BODY}" | awk '{ print $1 }' | sed -e 's_/__')
echo "Type: ${TYPE}" >> $GITHUB_STEP_SUMMARY
echo "type=${TYPE}" >> $GITHUB_ENV
TARGET_BRANCH=$(echo "${COMMENT_BODY}" | awk '{ print $3 }')
echo "Target branch: ${TARGET_BRANCH}" >> $GITHUB_STEP_SUMMARY
echo "target_branch=${TARGET_BRANCH}" >> $GITHUB_ENV
if gh api repos/${GITHUB_REPOSITORY}/branches --paginate | jq -e --arg TARGET_BRANCH "$TARGET_BRANCH" '.[] | select(.name == $TARGET_BRANCH)' > /dev/null; then
echo "target_branch_exists=true" >> $GITHUB_ENV
else
gh issue comment -R ${GITHUB_REPOSITORY} ${ORIGINAL_ISSUE_NUMBER} --body "Not creating port issue, target ${TARGET_BRANCH} does not exist"
echo "target_branch_exists=false" >> $GITHUB_ENV
fi
- name: Checkout
if: ${{ env.is_member == 'true' && env.target_branch_exists == 'true' }}
uses: actions/checkout@v4
with:
ref: ${{ env.target_branch }}
fetch-depth: '0'
token: ${{ secrets.GITHUB_TOKEN }}
- name: Port PR
if: ${{ env.is_member == 'true' && env.target_branch_exists == 'true' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TYPE: ${{ env.type }}
TARGET_BRANCH: ${{ env.target_branch }}
MILESTONE: ${{ env.milestone }}
run: |
PATCH_FILE=$(mktemp)
gh pr diff $ORIGINAL_ISSUE_NUMBER --patch > $PATCH_FILE
BRANCH="gha-portpr-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
echo "branch=${BRANCH}" >> $GITHUB_ENV
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "Rancher PR Port Bot"
git checkout -b $BRANCH
if ! git am -3 "$PATCH_FILE" > error.log 2>&1; then
ERROR_MESSAGE=$(cat error.log)
FORMATTED_ERROR_MESSAGE=$(printf "\n\`\`\`\n%s\n\`\`\`" "$ERROR_MESSAGE")
gh issue comment ${ORIGINAL_ISSUE_NUMBER} --body "Not creating port PR, there was an error running git am -3: $FORMATTED_ERROR_MESSAGE"
echo "Port PR not created." >> $GITHUB_STEP_SUMMARY
else
git push origin $BRANCH
ORIGINAL_PR=$(gh pr view ${ORIGINAL_ISSUE_NUMBER} --json title,body,assignees)
ORIGINAL_TITLE=$(echo "${ORIGINAL_PR}" | jq -r .title)
ORIGINAL_ASSIGNEE=$(echo "${ORIGINAL_PR}" | jq -r '.assignee.login // empty')
BODY=$(mktemp)
echo -e "This is an automated request to port PR #${ORIGINAL_ISSUE_NUMBER} by @${GITHUB_ACTOR}\n\n" > $BODY
echo -e "Original PR body:\n\n" >> $BODY
echo "${ORIGINAL_PR}" | jq -r .body >> $BODY
ASSIGNEES=$(echo "${ORIGINAL_PR}" | jq -r .assignees[].login)
if [ -n "$ASSIGNEES" ]; then
echo "Checking if assignee is member before assigning"
DELIMITER=""
NEW_ASSIGNEES=""
for ASSIGNEE in $ASSIGNEES; do
if gh api orgs/${GITHUB_REPOSITORY_OWNER}/members --paginate | jq -e --arg GITHUB_ACTOR "$GITHUB_ACTOR" '.[] | select(.login == $GITHUB_ACTOR)' > /dev/null; then
echo "${ASSIGNEE} is a member, adding to assignees"
NEW_ASSIGNEES="${NEW_ASSIGNEES}${DELIMITER}${ASSIGNEE}"
DELIMITER=","
fi
done
if [ -n "$NEW_ASSIGNEES" ]; then
echo "Assignees for new issue: ${NEW_ASSIGNEES}"
additional_cmd+=("--assignee")
additional_cmd+=("${NEW_ASSIGNEES}")
fi
fi
NAMED_TARGET=${MILESTONE}
if [ -z "${MILESTONE}" ]; then
NAMED_TARGET="${TARGET_BRANCH}"
fi
NEW_PR=$(gh pr create --title="[${TYPE} ${NAMED_TARGET}] ${ORIGINAL_TITLE}" --body-file="${BODY}" --head "${BRANCH}" --base "${TARGET_BRANCH}" --milestone "${MILESTONE}" "${additional_cmd[@]}")
echo "Port PR created: ${NEW_PR}" >> $GITHUB_STEP_SUMMARY
fi