Create Pre-Release PR from Milestone #37
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Create Pre-Release PR from Milestone | |
permissions: | |
contents: write | |
pull-requests: write | |
issues: write | |
on: | |
workflow_dispatch: | |
inputs: | |
milestone_name: | |
description: 'Milestone name to collect closed PRs from' | |
required: true | |
default: 'v3.8.2' | |
target_branch: | |
description: 'Target branch to merge the consolidated PR' | |
required: true | |
default: 'pre-release-v3.8.2' | |
schedule: | |
- cron: '0 10 * * 0' | |
env: | |
MILESTONE_NAME: ${{ github.event.inputs.milestone_name || 'v3.8.2' }} | |
TARGET_BRANCH: ${{ github.event.inputs.target_branch || 'pre-release-v3.8.2' }} | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
BOT_TOKEN: ${{ secrets.BOT_TOKEN }} | |
LABEL_NAME: cherry-picked | |
TEMP_DIR: "./temp" # Temporary directory for storing PR number and other info | |
jobs: | |
cherry_pick_milestone_prs: | |
runs-on: ubuntu-latest | |
steps: | |
# Ensure temporary directory is created globally | |
- name: Setup temp directory | |
run: mkdir -p ${{ env.TEMP_DIR }} | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
token: ${{ secrets.BOT_TOKEN }} | |
- name: Setup Git User for OpenIM-Robot | |
run: | | |
git config --global user.email "[email protected]" | |
git config --global user.name "OpenIM-Robot" | |
- name: Fetch Milestone ID and Filter PR Numbers | |
env: | |
MILESTONE_NAME: ${{ env.MILESTONE_NAME }} | |
run: | | |
# Fetch milestones | |
milestones=$(curl -s -H "Authorization: token $BOT_TOKEN" \ | |
-H "Accept: application/vnd.github+json" \ | |
"https://api.github.com/repos/${{ github.repository }}/milestones") | |
milestone_id=$(echo "$milestones" | grep -B3 "\"title\": \"$MILESTONE_NAME\"" | grep '"number":' | head -n1 | grep -o '[0-9]\+') | |
if [ -z "$milestone_id" ]; then | |
echo "Milestone '$MILESTONE_NAME' not found. Exiting." | |
exit 1 | |
fi | |
echo "Milestone ID: $milestone_id" | |
echo "MILESTONE_ID=$milestone_id" >> $GITHUB_ENV | |
# Fetch issues for the milestone and filter PRs without 'cherry-picked' label | |
issues=$(curl -s -H "Authorization: token $BOT_TOKEN" \ | |
-H "Accept: application/vnd.github+json" \ | |
"https://api.github.com/repos/${{ github.repository }}/issues?milestone=$milestone_id&state=closed&per_page=100") | |
> ${{ env.TEMP_DIR }}/pr_numbers.txt | |
for pr_number in $(echo "$issues" | jq -r '.[] | select(.pull_request != null) | .number'); do | |
labels=$(curl -s -H "Authorization: token $BOT_TOKEN" \ | |
-H "Accept: application/vnd.github+json" \ | |
"https://api.github.com/repos/${{ github.repository }}/issues/$pr_number/labels" | jq -r '.[].name') | |
if ! echo "$labels" | grep -q "${LABEL_NAME}"; then | |
echo "PR #$pr_number does not have the 'cherry-picked' label. Adding to the list." | |
echo "$pr_number" >> ${{ env.TEMP_DIR }}/pr_numbers.txt | |
else | |
echo "PR #$pr_number already has the 'cherry-picked' label. Skipping." | |
fi | |
done | |
echo "Filtered PR numbers:" | |
cat ${{ env.TEMP_DIR }}/pr_numbers.txt || echo "No closed PR numbers found for milestone." | |
- name: Fetch Merge Commits for PRs and Generate Title and Body | |
run: | | |
> ${{ env.TEMP_DIR }}/commit_hashes.txt | |
> ${{ env.TEMP_DIR }}/pr_title.txt | |
> ${{ env.TEMP_DIR }}/pr_body.txt | |
# Add Description to pr_body.txt | |
echo "### Description:" >> ${{ env.TEMP_DIR }}/pr_body.txt | |
echo "Merging PRs from milestone \`$MILESTONE_NAME\` into target branch \`$TARGET_BRANCH\`." >> ${{ env.TEMP_DIR }}/pr_body.txt | |
echo "" >> ${{ env.TEMP_DIR }}/pr_body.txt | |
echo "### Need Merge PRs:" >> ${{ env.TEMP_DIR }}/pr_body.txt | |
pr_numbers_in_title="" | |
for pr_number in $(cat ${{ env.TEMP_DIR }}/pr_numbers.txt); do | |
echo "Processing PR #$pr_number" | |
pr_details=$(curl -s -H "Authorization: token $BOT_TOKEN" \ | |
-H "Accept: application/vnd.github+json" \ | |
"https://api.github.com/repos/${{ github.repository }}/pulls/$pr_number") | |
pr_title=$(echo "$pr_details" | jq -r '.title') | |
merge_commit=$(echo "$pr_details" | jq -r '.merge_commit_sha') | |
short_commit_hash=$(echo "$merge_commit" | cut -c 1-7) | |
# Add formatted PR info to pr_body.txt | |
echo "- $pr_title: (#$pr_number) ($short_commit_hash)" >> ${{ env.TEMP_DIR }}/pr_body.txt | |
if [ "$merge_commit" != "null" ]; then | |
echo "$merge_commit" >> ${{ env.TEMP_DIR }}/commit_hashes.txt | |
echo "#$pr_number" >> ${{ env.TEMP_DIR }}/pr_title.txt | |
pr_numbers_in_title="$pr_numbers_in_title #$pr_number" | |
fi | |
done | |
commit_hashes=$(cat ${{ env.TEMP_DIR }}/commit_hashes.txt | tr '\n' ' ') | |
first_commit_hash=$(head -n 1 ${{ env.TEMP_DIR }}/commit_hashes.txt) | |
cherry_pick_branch="cherry-pick-${first_commit_hash:0:7}" | |
echo "COMMIT_HASHES=$commit_hashes" >> $GITHUB_ENV | |
echo "CHERRY_PICK_BRANCH=$cherry_pick_branch" >> $GITHUB_ENV | |
echo "pr_numbers_in_title=$pr_numbers_in_title" >> $GITHUB_ENV | |
- name: Pull and Cherry-pick Commits, Then Push | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
BOT_TOKEN: ${{ secrets.BOT_TOKEN }} | |
run: | | |
git fetch origin | |
git checkout $TARGET_BRANCH | |
git pull origin $TARGET_BRANCH | |
git checkout -b $CHERRY_PICK_BRANCH | |
for commit_hash in $COMMIT_HASHES; do | |
echo "Attempting to cherry-pick commit $commit_hash" | |
if ! git cherry-pick "$commit_hash" --strategy=recursive -X theirs; then | |
echo "Conflict detected for $commit_hash. Resolving with incoming changes." | |
conflict_files=$(git diff --name-only --diff-filter=U) | |
echo "Conflicting files:" | |
echo "$conflict_files" | |
for file in $conflict_files; do | |
if [ -f "$file" ]; then | |
echo "Resolving conflict for $file" | |
git add "$file" | |
else | |
echo "File $file has been deleted. Skipping." | |
git rm "$file" | |
fi | |
done | |
echo "Conflicts resolved. Continuing cherry-pick." | |
git cherry-pick --continue | |
else | |
echo "Cherry-pick successful for commit $commit_hash." | |
fi | |
done | |
git remote set-url origin "https://${BOT_TOKEN}@github.com/${{ github.repository }}.git" | |
git push origin $CHERRY_PICK_BRANCH --force | |
- name: Create Pull Request | |
run: | | |
pr_title="deps: Merge ${{ env.pr_numbers_in_title }} PRs into $TARGET_BRANCH" | |
pr_body=$(cat ${{ env.TEMP_DIR }}/pr_body.txt) | |
echo "Prepared PR title:" | |
echo "$pr_title" | |
echo "Prepared PR body:" | |
echo "$pr_body" | |
response=$(curl -s -X POST -H "Authorization: token $BOT_TOKEN" \ | |
-H "Accept: application/vnd.github+json" \ | |
https://api.github.com/repos/${{ github.repository }}/pulls \ | |
-d "$(jq -n --arg title "$pr_title" \ | |
--arg head "$CHERRY_PICK_BRANCH" \ | |
--arg base "$TARGET_BRANCH" \ | |
--arg body "$pr_body" \ | |
'{title: $title, head: $head, base: $base, body: $body}')") | |
pr_number=$(echo "$response" | jq -r '.number') | |
echo "$pr_number" > ${{ env.TEMP_DIR }}/create_pr_number | |
- name: Add Label to Created Pull Request | |
run: | | |
pr_number=$(cat ${{ env.TEMP_DIR }}/create_pr_number) | |
echo "Adding 'milestone-merge' label to PR #$pr_number" | |
curl -s -X POST -H "Authorization: token $BOT_TOKEN" \ | |
-H "Accept: application/vnd.github+json" \ | |
https://api.github.com/repos/${{ github.repository }}/issues/$pr_number/labels \ | |
-d "$(jq -n --arg label "milestone-merge" '{labels: [$label]}')" |