Skip to content

some tweaks to stream to handle edge cases #384

some tweaks to stream to handle edge cases

some tweaks to stream to handle edge cases #384

name: Backport to stable
permissions:
contents: read
pull-requests: write
on:
push:
branches:
- dev
jobs:
backport:
name: Backport PRs with 'backport-to-stable' label to stable
runs-on: ubuntu-latest
if: github.event.commits[0].distinct == true
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0 # Needed for full git history
- name: Get merged PR info
id: prinfo
uses: actions/github-script@v8
with:
script: |
const pr = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'closed',
base: 'dev',
sort: 'updated',
direction: 'desc',
per_page: 10
});
const merged = pr.data.find(p => p.merge_commit_sha === context.payload.head_commit.id);
if (!merged) return core.setFailed('No merged PR found for this commit.');
core.setOutput('pr_number', merged.number);
core.setOutput('pr_title', merged.title);
core.setOutput('pr_labels', merged.labels.map(l => l.name).join(','));
core.setOutput('merge_commit_sha', merged.merge_commit_sha);
- name: Check for backport-to-stable label
id: checklabel
run: |
echo "PR labels: ${{ steps.prinfo.outputs.pr_labels }}"
if [[ "${{ steps.prinfo.outputs.pr_labels }}" == *"backport-to-stable"* ]]; then
echo "backport-to-stable label found, proceeding with backport."
echo "should_backport=true" >> $GITHUB_OUTPUT
else
echo "No backport-to-stable label found, skipping backport."
echo "should_backport=false" >> $GITHUB_OUTPUT
fi
- name: Set up Git user
if: steps.checklabel.outputs.should_backport == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Calculate next patch version
if: steps.checklabel.outputs.should_backport == 'true'
id: nextver
run: |
git fetch origin stable --tags
# Filter out beta/rc tags and get only stable versions (e.g., 2.5.5, v2.5.5)
latest_tag=$(git tag --merged origin/stable --sort=-v:refname | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
if [[ -z "$latest_tag" ]]; then
echo "No stable tags found on stable branch" >&2
exit 1
fi
echo "Latest stable tag: $latest_tag"
# Remove 'v' prefix if present
version="$latest_tag"
if [[ "$version" =~ ^v ]]; then
version="${version#v}"
fi
# Parse version components
if [[ "$version" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
major="${BASH_REMATCH[1]}"
minor="${BASH_REMATCH[2]}"
patch="${BASH_REMATCH[3]}"
else
echo "Invalid version format: $version" >&2
exit 1
fi
next_patch=$((patch + 1))
next_version="$major.$minor.$next_patch"
echo "Current version: $version"
echo "Next version: $next_version"
echo "next_patch_version=$next_version" >> $GITHUB_OUTPUT
- name: Create or update backport branch
if: steps.checklabel.outputs.should_backport == 'true'
id: create_or_update_backport_branch
run: |
next_version="${{ steps.nextver.outputs.next_patch_version }}"
branch_name="backport/$next_version"
echo "Creating/updating branch: $branch_name"
# Check if branch already exists on remote
git fetch origin $branch_name || true
if git show-ref --verify --quiet refs/remotes/origin/$branch_name; then
echo "Branch $branch_name already exists, checking out"
git checkout -B $branch_name origin/$branch_name
else
echo "Branch $branch_name does not exist, creating from stable"
git checkout -b $branch_name origin/stable
fi
echo "branch_name=$branch_name" >> $GITHUB_OUTPUT
- name: Cherry-pick commit
if: steps.checklabel.outputs.should_backport == 'true'
run: |
git cherry-pick ${{ steps.prinfo.outputs.merge_commit_sha }} || {
echo 'Cherry-pick failed, please resolve conflicts manually.'
exit 1
}
- name: Push backport branch
if: steps.checklabel.outputs.should_backport == 'true'
run: |
git push origin ${{ steps.create_or_update_backport_branch.outputs.branch_name }}:${{ steps.create_or_update_backport_branch.outputs.branch_name }} --force
- name: Create or update backport PR with cherry-picked commits
if: steps.checklabel.outputs.should_backport == 'true'
uses: actions/github-script@v8
with:
script: |
const pr_number = process.env.pr_number;
const pr_title = process.env.pr_title;
const next_patch_version = process.env.next_patch_version;
const branch = process.env.branch_name;
const cherry_commit = process.env.cherry_commit;
console.log(`Processing backport for PR #${pr_number}: ${pr_title}`);
console.log(`Next patch version: ${next_patch_version}`);
console.log(`Branch: ${branch}`);
console.log(`Cherry-pick commit: ${cherry_commit}`);
const prs = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
head: `${context.repo.owner}:${branch}`,
base: 'stable'
});
const commit_url = `https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${cherry_commit}`;
const commit_item = `- [${cherry_commit.substring(0,7)}](${commit_url}) - ${pr_title} (#${pr_number})`;
if (prs.data.length === 0) {
// Create new PR with initial commit in body
console.log('Creating new backport PR');
await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `[Backport to stable] ${next_patch_version}`,
head: branch,
base: 'stable',
body: `Automated backport PR for stable release ${next_patch_version} with cherry-picked commits:\n\n${commit_item}`
});
} else {
// Update PR body to append new commit if not already present
console.log('Updating existing backport PR');
const pr = prs.data[0];
let body = pr.body || '';
if (!body.includes(cherry_commit.substring(0,7))) {
// Try to find the start of the list
const listMatch = body.match(/(cherry-picked commits:\n\n)([\s\S]*)/);
if (listMatch) {
// Append to existing list
const before = listMatch[1];
const list = listMatch[2].trim();
const newList = list + '\n' + commit_item;
body = body.replace(/(cherry-picked commits:\n\n)([\s\S]*)/, before + newList);
} else {
// Add new list
body = body.trim() + `\n\nCherry-picked commits:\n\n${commit_item}`;
}
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
body
});
} else {
console.log('Commit already exists in PR body, skipping update');
}
}
env:
pr_number: ${{ steps.prinfo.outputs.pr_number }}
pr_title: ${{ steps.prinfo.outputs.pr_title }}
next_patch_version: ${{ steps.nextver.outputs.next_patch_version }}
branch_name: ${{ steps.create_or_update_backport_branch.outputs.branch_name }}
cherry_commit: ${{ steps.prinfo.outputs.merge_commit_sha }}