mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 05:36:46 +00:00
chore(actions): Automated backport to release branches (#65)
* chore(actions): automated backport to release branches * nit
This commit is contained in:
parent
74b165a362
commit
58a72d4087
2 changed files with 242 additions and 0 deletions
106
.github/workflows/BACKPORT_README.md
vendored
Normal file
106
.github/workflows/BACKPORT_README.md
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
# Backport Workflow Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
This repository uses an automated backport workflow to cherry-pick merged pull requests from the `main` branch to release branches. The workflow is powered by the [korthout/backport-action](https://github.com/marketplace/actions/backport-merged-pull-requests-to-selected-branches).
|
||||
|
||||
## How It Works
|
||||
|
||||
### Automatic Backporting via Labels
|
||||
|
||||
1. **Add a backport label** to your pull request before or after merging:
|
||||
- Label format: `backport/{version}`
|
||||
- Examples: `backport/v1.0.0`, `backport/v2.3.1`, `backport/1.0`
|
||||
|
||||
2. **When the PR is merged to main**, the workflow automatically:
|
||||
- Detects all `backport/*` labels
|
||||
- Transforms them to target `release/{version}` branches
|
||||
- Creates new pull requests with cherry-picked commits to each target branch
|
||||
|
||||
3. **Target branches** must exist:
|
||||
- For label `backport/v1.0.0` → targets branch `release/v1.0.0`
|
||||
- For label `backport/2.0` → targets branch `release/2.0`
|
||||
- The release branch must already exist in the repository
|
||||
|
||||
### Manual Backporting via Comments
|
||||
|
||||
You can also trigger backports manually by commenting on a merged PR:
|
||||
- Comment `/backport` on any merged pull request
|
||||
- The workflow will look for backport labels and create the appropriate PRs
|
||||
|
||||
## Example Workflow
|
||||
|
||||
1. Create a pull request to `main` with your changes
|
||||
2. Add label: `backport/v1.2.3`
|
||||
3. Get the PR reviewed and merged
|
||||
4. The bot automatically:
|
||||
- Creates a new branch: `backport-{PR-number}-to-release-v1.2.3`
|
||||
- Cherry-picks the commits from your merged PR
|
||||
- Opens a new PR from that branch to `release/v1.2.3`
|
||||
- Adds labels: `backport`, `automated`
|
||||
- Assigns the original PR author
|
||||
|
||||
## Handling Multiple Backports
|
||||
|
||||
You can backport to multiple release branches by adding multiple labels:
|
||||
- `backport/v1.0.0` - backports to `release/v1.0.0`
|
||||
- `backport/v2.0.0` - backports to `release/v2.0.0`
|
||||
- `backport/v3.0.0` - backports to `release/v3.0.0`
|
||||
|
||||
Each label will create a separate backport PR.
|
||||
|
||||
## Conflict Resolution
|
||||
|
||||
When cherry-pick conflicts occur:
|
||||
1. The bot creates a **draft PR** with the conflicts committed
|
||||
2. The draft PR includes instructions on how to resolve the conflicts
|
||||
3. You need to:
|
||||
- Check out the backport branch locally
|
||||
- Resolve the conflicts
|
||||
- Push the resolved changes
|
||||
- Mark the PR as ready for review
|
||||
|
||||
## Backport PR Format
|
||||
|
||||
Backport PRs are created with:
|
||||
- **Title**: `[Backport release/{version}] {original PR title}`
|
||||
- **Description**: Includes original PR information, author, and description
|
||||
- **Labels**: `backport`, `automated`
|
||||
- **Assignee**: Original PR author
|
||||
|
||||
## Prerequisites
|
||||
|
||||
For the workflow to function correctly:
|
||||
1. Release branches must follow the naming pattern: `release/{version}`
|
||||
2. The workflow must have write permissions (already configured)
|
||||
3. Release branches must exist before attempting to backport
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Backport not triggering
|
||||
- Ensure the PR is merged to `main` (not just closed)
|
||||
- Verify the label format is exactly `backport/{version}`
|
||||
- Check that the target `release/{version}` branch exists
|
||||
|
||||
### Conflicts in backport
|
||||
- The bot will create a draft PR with conflicts
|
||||
- Follow the instructions in the PR to resolve conflicts locally
|
||||
|
||||
### Multiple commits or merge commits
|
||||
- The workflow is configured to skip merge commits
|
||||
- Only non-merge commits will be cherry-picked
|
||||
- If you need merge commits, update the `merge_commits` setting in the workflow
|
||||
|
||||
## Configuration
|
||||
|
||||
The workflow is defined in `.github/workflows/backport.yml`. Key settings:
|
||||
- **Label pattern**: `backport/{version}`
|
||||
- **Target branch pattern**: `release/{version}`
|
||||
- **Conflict strategy**: Create draft PRs with conflicts
|
||||
- **Merge commits**: Skipped (only cherry-picks non-merge commits)
|
||||
|
||||
## Security
|
||||
|
||||
- The workflow uses `GITHUB_TOKEN` for authentication
|
||||
- Runs on `pull_request_target` to handle forks securely
|
||||
- Only triggers on merged PRs to prevent abuse
|
||||
136
.github/workflows/backport.yml
vendored
Normal file
136
.github/workflows/backport.yml
vendored
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
name: Backport merged pull request
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: write # Required to push branches
|
||||
pull-requests: write # Required to create pull requests
|
||||
|
||||
jobs:
|
||||
backport:
|
||||
name: Backport pull request
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Only run when:
|
||||
# - A pull request to main is merged (not just closed)
|
||||
# - OR a comment starting with '/backport' is created by someone other than the bot
|
||||
if: >
|
||||
(
|
||||
github.event_name == 'pull_request_target' &&
|
||||
github.event.pull_request.merged &&
|
||||
github.event.pull_request.base.ref == 'main'
|
||||
) || (
|
||||
github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
github.event.comment.user.id != 97796249 &&
|
||||
startsWith(github.event.comment.body, '/backport')
|
||||
)
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# Fetch all history for all branches
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Extract backport targets from labels
|
||||
id: extract-targets
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const labels = context.payload.pull_request?.labels || [];
|
||||
const backportLabels = labels
|
||||
.map(label => label.name)
|
||||
.filter(name => name.startsWith('backport/'))
|
||||
.map(name => {
|
||||
// Extract version from "backport/{version}"
|
||||
const version = name.replace('backport/', '');
|
||||
// Transform to "release/{version}"
|
||||
return `release/${version}`;
|
||||
});
|
||||
|
||||
if (backportLabels.length > 0) {
|
||||
console.log(`Found backport targets: ${backportLabels.join(', ')}`);
|
||||
// Join with spaces as the backport action expects
|
||||
core.setOutput('targets', backportLabels.join(' '));
|
||||
core.setOutput('has-targets', 'true');
|
||||
} else {
|
||||
console.log('No backport labels found');
|
||||
core.setOutput('has-targets', 'false');
|
||||
}
|
||||
|
||||
// Also output the original labels for the action
|
||||
const originalLabels = labels
|
||||
.map(label => label.name)
|
||||
.filter(name => name.startsWith('backport/'))
|
||||
.map(name => {
|
||||
// Transform "backport/{version}" to "backport release/{version}"
|
||||
const version = name.replace('backport/', '');
|
||||
return `backport release/${version}`;
|
||||
})
|
||||
.join(',');
|
||||
|
||||
core.setOutput('transformed-labels', originalLabels);
|
||||
|
||||
- name: Create backport pull requests
|
||||
if: steps.extract-targets.outputs.has-targets == 'true'
|
||||
uses: korthout/backport-action@v3
|
||||
with:
|
||||
# GitHub token for authentication
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Directly specify target branches extracted from labels
|
||||
target_branches: ${{ steps.extract-targets.outputs.targets }}
|
||||
|
||||
# Disable label pattern matching since we're using target_branches
|
||||
label_pattern: ""
|
||||
|
||||
# Branch name template for the backport branch
|
||||
# This creates branches like: backport-123-to-release-v1.0.0
|
||||
branch_name: "backport-${pull_number}-to-${target_branch}"
|
||||
|
||||
# Title template for the backport PR
|
||||
pull_title: "[Backport ${target_branch}] ${pull_title}"
|
||||
|
||||
# Description template for the backport PR
|
||||
pull_description: |
|
||||
# Backport to ${target_branch}
|
||||
|
||||
This is an automatic backport of pull request #${pull_number} to `${target_branch}`.
|
||||
|
||||
## Original PR Information
|
||||
- **Title**: ${pull_title}
|
||||
- **Author**: @${pull_author}
|
||||
- **Original PR**: #${pull_number}
|
||||
|
||||
## Original Description
|
||||
${pull_description}
|
||||
|
||||
## Related Issues
|
||||
${issue_refs}
|
||||
|
||||
---
|
||||
*This pull request was created automatically by the backport action.*
|
||||
|
||||
# Labels to add to the backport PR
|
||||
add_labels: "automated"
|
||||
|
||||
# Add original PR author as assignee
|
||||
add_author_as_assignee: true
|
||||
|
||||
# What to do when there are conflicts
|
||||
# Options: "fail" or "draft_commit_conflicts"
|
||||
# Using draft_commit_conflicts so conflicts don't block the process
|
||||
conflicts_strategy: "draft_commit_conflicts"
|
||||
|
||||
# Cherry-picking strategy
|
||||
# Options: "auto", "recursive", "ort"
|
||||
cherry_picking: "auto"
|
||||
|
||||
# How to handle merge commits
|
||||
# Options: "fail" or "skip"
|
||||
# Using skip to handle PRs that were kept in sync with base via merge commits
|
||||
merge_commits: "skip"
|
||||
Loading…
Reference in a new issue