diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 0000000000..f8bc58040d --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,121 @@ +name: Backport + +on: + pull_request_target: + types: [closed, labeled] + +permissions: + contents: write + pull-requests: write + issues: read + +jobs: + backport: + name: Backport PR + runs-on: ubuntu-latest + # Only run on merged PRs with backport labels. + # Labels must match pattern: backport-v* (e.g., backport-v0.20.x-branch). + # This excludes labels like "backport candidate" or "backport-candidate". + if: | + github.event.pull_request.merged == true && + contains(join(github.event.pull_request.labels.*.name, ','), 'backport-v') + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.base.ref }} + + - name: Validate target branches exist + id: validate + shell: bash + run: | + # Extract all backport labels + labels='${{ toJSON(github.event.pull_request.labels.*.name) }}' + echo "All labels: $labels" + + # Parse labels and extract branch names + # Only match labels starting with "backport-v" to exclude labels like + # "backport candidate" or "backport-candidate" + backport_labels=$(echo "$labels" | jq -r '.[] | select(startswith("backport-v"))') + + if [ -z "$backport_labels" ]; then + echo "::error::No valid backport labels found (must start with 'backport-v')" + exit 1 + fi + + echo "Found backport labels:" + echo "$backport_labels" + + # Check each target branch exists + missing_branches=() + valid_branches=() + while IFS= read -r label; do + # Extract branch name (everything after "backport-") + branch_name="${label#backport-}" + echo "Checking if branch exists: $branch_name" + + # Check if branch exists in remote + if ! git ls-remote --heads origin "$branch_name" | grep -q "$branch_name"; then + echo "::warning::Target branch '$branch_name' does not exist (from label '$label')" + missing_branches+=("$branch_name") + else + echo "✓ Branch '$branch_name' exists" + valid_branches+=("$branch_name") + fi + done <<< "$backport_labels" + + # Report validation results + if [ ${#missing_branches[@]} -gt 0 ]; then + echo "::warning::The following target branches do not exist and will be skipped: ${missing_branches[*]}" + echo "::warning::Please check the branch names or create the branches before retrying" + fi + + # Only fail if ALL branches are invalid + if [ ${#valid_branches[@]} -eq 0 ]; then + echo "::error::No valid target branches found. All backport labels reference non-existent branches." + exit 1 + fi + + echo "✓ Found ${#valid_branches[@]} valid target branch(es): ${valid_branches[*]}" + if [ ${#missing_branches[@]} -gt 0 ]; then + echo "⚠ Skipping ${#missing_branches[@]} invalid branch(es): ${missing_branches[*]}" + fi + + - name: Create backport PRs + # Uses version v3.4, we pin to a hash here. For more details to + # available versions, see: + # https://github.com/korthout/backport-action/releases. + uses: korthout/backport-action@d07416681cab29bf2661702f925f020aaa962997 + with: + # Automatically detect target branches from labels. + # Labels must be in format: backport-v0.20.x-branch (must start + # with "backport-v"). This excludes labels like "backport candidate" + # or "backport-candidate". The pattern extracts everything after + # "backport-" as the branch name. + label_pattern: '^backport-(v.+)$' + + # GitHub token for creating PRs. + github_token: ${{ secrets.GITHUB_TOKEN }} + + # PR title format - shows it's a backport with original PR number. + pull_title: '[${target_branch}] Backport #${pull_number}: ${pull_title}' + + # PR description template - links back to original PR. + pull_description: |- + Backport of #${pull_number} + + --- + + ${pull_description} + + # Automatically add labels to backport PRs. + # The 'no-changelog' label skips the release notes check in CI. + labels: no-changelog + + # Merge strategy - skip merge commits, use cherry-pick only. + merge_commits: skip + + # If conflicts occur, create a draft PR with conflict markers. + experimental: '{"conflict_resolution": "draft_commit_conflicts"}' diff --git a/docs/backport-workflow.md b/docs/backport-workflow.md new file mode 100644 index 0000000000..7030994b48 --- /dev/null +++ b/docs/backport-workflow.md @@ -0,0 +1,526 @@ +# Automated Backport Workflow + +This document describes the automated backport workflow for the LND project. + +## Table of Contents + +1. [Overview](#overview) +2. [How to Use](#how-to-use) +3. [Workflow Triggers](#workflow-triggers) +4. [Label Format](#label-format) +5. [Workflow Steps](#workflow-steps) +6. [Handling Conflicts](#handling-conflicts) +7. [Multiple Backports](#multiple-backports) +8. [Technical Details](#technical-details) +9. [Troubleshooting](#troubleshooting) + +## Overview + +The automated backport workflow simplifies the process of backporting merged PRs from the `master` branch to release branches (e.g., `v0.20.x-branch`, `v0.19.x-branch`). + +Instead of manually creating branches, cherry-picking commits, and creating PRs, maintainers can simply add a label to the master PR, and the workflow handles the rest. + +## How to Use + +### Basic Usage + +1. **Merge a PR to master** (or have it already merged) +2. **Add a backport label** in the format: `backport-v-branch` + - Example: `backport-v0.20.x-branch` +3. **The workflow automatically**: + - Validates the target branch exists + - Cherry-picks the commits + - Creates a new PR targeting the release branch + - Adds the `no-changelog` label (since release notes are in the master PR) + +### Example Scenario + +``` +Day 1, 10:00 - PR #1234 "Fix critical bug" merged to master +Day 1, 10:30 - Add label: backport-v0.20.x-branch +Day 1, 10:31 - Workflow creates PR #1235 automatically + Title: "[v0.20.x-branch] Backport #1234: Fix critical bug" + Base: v0.20.x-branch + Labels: no-changelog +Day 1, 14:00 - Maintainer reviews and merges PR #1235 +``` + +## Workflow Triggers + +The backport workflow triggers in two scenarios: + +### Scenario 1: Label Before Merge + +``` +1. Open PR #1234 +2. Add label: backport-v0.20.x-branch +3. Review and approve PR +4. Merge PR #1234 +5. → Workflow triggers on PR close event +6. → Backport PR #1235 created immediately +``` + +### Scenario 2: Label After Merge + +``` +1. Open PR #1234 +2. Review, approve, and merge PR #1234 +3. Later... decide it needs backporting +4. Add label: backport-v0.20.x-branch +5. → Workflow triggers on label event +6. → Backport PR #1235 created immediately +``` + +Both scenarios work identically. + +## Label Format + +### Valid Labels + +Labels **must** start with `backport-v` to trigger the workflow: + +- ✅ `backport-v0.20.x-branch` → backports to `v0.20.x-branch` +- ✅ `backport-v0.19.x-branch` → backports to `v0.19.x-branch` +- ✅ `backport-v0.18.x-beta-branch` → backports to `v0.18.x-beta-branch` + +### Invalid Labels (Will NOT Trigger) + +These labels are ignored by the workflow: + +- ❌ `backport candidate` - discussion label only +- ❌ `backport-candidate` - doesn't start with `backport-v` +- ❌ `backport-needed` - doesn't start with `backport-v` +- ❌ `needs-backport` - wrong prefix + +This allows you to use discussion labels without accidentally triggering backports. + +### Label to Branch Mapping + +The label format directly maps to the target branch: + +``` +Label: backport-v0.20.x-branch + ↓ (removes "backport-" prefix) +Branch: v0.20.x-branch +``` + +## Workflow Steps + +The workflow executes the following steps when triggered: + +### Step 1: Checkout Repository + +```yaml +- Fetches the full git history +- Checks out the base branch (usually master) +``` + +### Step 2: Validate Target Branches + +```bash +For each backport label: + 1. Extract branch name from label + backport-v0.20.x-branch → v0.20.x-branch + + 2. Check if branch exists in remote repository + git ls-remote --heads origin v0.20.x-branch + + 3. If branch doesn't exist: + - Log error message + - Add branch to missing_branches list + + 4. After checking all labels: + - If any branches are missing → FAIL workflow + - If all branches exist → Continue +``` + +**Example validation output:** + +``` +All labels: ["backport-v0.20.x-branch", "bug-fix", "backport-v0.19.x-branch"] +Found backport labels: +backport-v0.20.x-branch +backport-v0.19.x-branch + +Checking if branch exists: v0.20.x-branch +✓ Branch 'v0.20.x-branch' exists + +Checking if branch exists: v0.19.x-branch +✓ Branch 'v0.19.x-branch' exists + +✓ All target branches validated successfully +``` + +### Step 3: Create Backport PRs + +For each valid backport label, the workflow: + +1. **Creates a new branch** + - Branch name: `backport--to-` + - Example: `backport-1234-to-v0.20.x-branch` + - Based on: the target release branch + +2. **Cherry-picks commits** + - Uses `git cherry-pick` (not merge or rebase) + - Cherry-picks all commits from the original PR + - Preserves commit messages and authorship + - Skips merge commits + +3. **Creates a new PR** + - Title: `[v0.20.x-branch] Backport #1234: ` + - Base branch: `v0.20.x-branch` + - Head branch: `backport-1234-to-v0.20.x-branch` + - Labels: `no-changelog` (automatically added) + +4. **PR Description** + ```markdown + Backport of #1234 + + Original PR: https://github.com/lightningnetwork/lnd/pull/1234 + + --- + + [Original PR description here] + ``` + +## Handling Conflicts + +The workflow handles merge conflicts gracefully using the `draft_commit_conflicts` strategy. + +### When Cherry-pick Succeeds + +``` +1. Cherry-pick completes cleanly +2. Creates regular PR (ready for review) +3. PR is NOT in draft mode +4. Maintainer can review and merge immediately +``` + +### When Cherry-pick Has Conflicts + +``` +1. Cherry-pick encounters conflicts +2. Workflow commits the conflict markers: + <<<<<<< HEAD + [code from release branch] + ======= + [code from master PR] + >>>>>>> commit-hash + +3. Creates DRAFT PR +4. PR description indicates there were conflicts +5. Manual resolution required: + a. git fetch origin + b. git checkout backport-1234-to-v0.20.x-branch + c. Resolve conflicts in affected files + d. git add + e. git commit -m "Resolve backport conflicts" + f. git push origin backport-1234-to-v0.20.x-branch + g. Mark PR as "Ready for review" in GitHub UI +6. Maintainer reviews and merges +``` + +### Conflict Resolution Best Practices + +- **Review the original PR**: Understand what changed +- **Check the release branch**: Understand why conflicts occurred +- **Test after resolving**: Run tests locally before pushing +- **Update commit message**: Explain what conflicts were resolved and how +- **Request review**: Don't merge without review, even after resolving conflicts + +## Multiple Backports + +You can backport to multiple release branches simultaneously by adding multiple labels. + +### Example: Backport to Two Branches + +``` +PR #1234 merged with labels: +- backport-v0.20.x-branch +- backport-v0.19.x-branch + +Workflow creates TWO backport PRs: + +PR #1235: + Title: [v0.20.x-branch] Backport #1234: Original title + Base: v0.20.x-branch + Labels: no-changelog + +PR #1236: + Title: [v0.19.x-branch] Backport #1234: Original title + Base: v0.19.x-branch + Labels: no-changelog +``` + +### Independent Processing + +Each backport is processed independently: + +- One backport may succeed while another has conflicts +- One backport may fail validation while another succeeds +- Each backport creates a separate branch and PR +- Each backport PR is reviewed and merged independently + +### Example with Mixed Results + +``` +PR #1234 with labels: +- backport-v0.20.x-branch → ✓ Clean cherry-pick, regular PR created +- backport-v0.19.x-branch → ✗ Conflicts, draft PR created +- backport-v0.99.x-branch → ✗ Branch doesn't exist, workflow fails +``` + +In this case: +1. PR #1235 to v0.20.x-branch is ready for review +2. PR #1236 to v0.19.x-branch needs conflict resolution +3. No PR created for v0.99.x-branch (validation failed) +4. Remove the incorrect label and add the correct one. The workflow will + re-trigger when you add the new label. + +## Technical Details + +### Workflow File + +Location: `.github/workflows/backport.yml` + +### Trigger Events + +```yaml +on: + pull_request_target: + types: [closed, labeled] +``` + +- **closed**: Triggers when PR is closed (checks if merged) +- **labeled**: Triggers when any label is added + +### Permissions Required + +```yaml +permissions: + contents: write # Create branches and commits + pull-requests: write # Create and manage PRs + issues: read # Read PR metadata +``` + +### Workflow Condition + +```yaml +if: | + github.event.pull_request.merged == true && + contains(join(github.event.pull_request.labels.*.name, ','), 'backport-v') +``` + +Only runs when: +1. PR is actually merged (not just closed) +2. At least one label contains `backport-v` + +### Label Pattern + +```yaml +label_pattern: '^backport-(v.+)$' +``` + +Regex explanation: +- `^` - Start of string +- `backport-` - Literal text +- `(v.+)` - Capture group: "v" followed by one or more characters +- `$` - End of string + +Examples: +- `backport-v0.20.x-branch` → matches, captures `v0.20.x-branch` +- `backport-v0.19.x-branch` → matches, captures `v0.19.x-branch` +- `backport-candidate` → doesn't match (no "v" after dash) + +### Cherry-pick Strategy + +```yaml +merge_commits: skip +``` + +- Uses `git cherry-pick` for clean history +- Skips merge commits (only cherry-picks actual changes) +- Preserves original commit messages and authorship +- Maintains PGP signatures where present + +### Conflict Resolution Strategy + +```yaml +experimental: | + conflict_resolution: draft_commit_conflicts +``` + +- Creates draft PR with conflict markers +- Allows manual resolution +- Preserves all context and metadata + +## Troubleshooting + +### Problem: Workflow Doesn't Trigger + +**Symptoms:** +- Added `backport-v0.20.x-branch` label +- No workflow run appears in Actions tab + +**Possible causes:** + +1. **Label format is wrong** + - ❌ `backport-0.20.x-branch` (missing "v") + - ✅ `backport-v0.20.x-branch` + +2. **PR is not merged** + - Workflow only runs on merged PRs + - Check PR status + +3. **Workflow is disabled** + - Check `.github/workflows/backport.yml` exists + - Check workflow is enabled in Settings → Actions + +### Problem: Workflow Fails with "Branch doesn't exist" + +**Error message:** +``` +Error: Target branch 'v0.21.x-branch' does not exist (from label 'backport-v0.21.x-branch') +Error: The following target branches do not exist: v0.21.x-branch +Error: Please ensure the branch exists before adding the backport label +``` + +**Solution:** + +1. **Verify branch name:** + ```bash + git ls-remote --heads origin | grep v0.21 + ``` + +2. **Check available release branches:** + ```bash + git branch -r | grep origin/v0 | grep -v fork + ``` + +3. **Fix the label:** + - Remove incorrect label + - Add correct label with existing branch name + +### Problem: Cherry-pick Has Conflicts + +**Symptoms:** +- Backport PR created as DRAFT +- PR description mentions conflicts +- Branch has files with conflict markers + +**Solution:** + +1. **Fetch and checkout the branch:** + ```bash + git fetch origin + git checkout backport-1234-to-v0.20.x-branch + ``` + +2. **Find conflicted files:** + ```bash + grep -r "<<<<<<< HEAD" . + ``` + +3. **Resolve each conflict:** + - Open the file in an editor + - Review both versions: + ``` + <<<<<<< HEAD + [Release branch version] + ======= + [Master PR version] + >>>>>>> commit-hash + ``` + - Choose the correct code or merge both + - Remove conflict markers + +4. **Commit the resolution:** + ```bash + git add + git commit -m "Resolve backport conflicts for PR #1234 + + Conflicts occurred due to [explanation]. + Resolution: [describe what you did]" + git push origin backport-1234-to-v0.20.x-branch + ``` + +5. **Mark PR ready for review:** + - Go to PR on GitHub + - Click "Ready for review" + +### Problem: Multiple Labels but Only One Backport Created + +**Symptoms:** +- Added `backport-v0.20.x-branch` and `backport-v0.19.x-branch` +- Only one PR created + +**Possible causes:** + +1. **One label format is wrong** + - Check both labels start with `backport-v` + - Fix incorrect label, workflow will retry + +2. **One branch doesn't exist** + - Check workflow logs for validation errors + - Verify both branches exist + +3. **Workflow still running** + - Check Actions tab for in-progress runs + - Wait for workflow to complete + +### Problem: Backport PR Missing `no-changelog` Label + +**Symptoms:** +- Backport PR created successfully +- CI fails on changelog check + +**Solution:** + +1. **Manually add the label:** + - Add `no-changelog` label to the backport PR + +2. **Check workflow configuration:** + - Verify `.github/workflows/backport.yml` has: + ```yaml + labels: no-changelog + ``` + +3. **Re-run the workflow:** + - Remove and re-add the backport label on original PR + - New backport PR will have correct label + +### Getting Help + +If you encounter issues not covered here: + +1. **Check workflow logs:** + - Go to Actions tab + - Click on the failed workflow run + - Review step-by-step logs + +2. **Check workflow file:** + - `.github/workflows/backport.yml` + - Verify configuration matches this documentation + +3. **Manual backport:** + - If automated backport fails repeatedly, you can backport manually: + ```bash + # Create branch from target release branch + git checkout v0.20.x-branch + git checkout -b manual-backport-123-to-v0.20.x + + # Cherry-pick commits from the original PR + git cherry-pick + + # Resolve any conflicts, then: + git add . + git commit + git push origin manual-backport-123-to-v0.20.x + + # Create PR targeting v0.20.x-branch with no-changelog label + gh pr create --base v0.20.x-branch --label no-changelog + ``` + +4. **Report issues:** + - If you find a bug in the workflow + - Open an issue with workflow logs and details diff --git a/docs/code_contribution_guidelines.md b/docs/code_contribution_guidelines.md index e17ca66af5..dab291bb4d 100644 --- a/docs/code_contribution_guidelines.md +++ b/docs/code_contribution_guidelines.md @@ -10,6 +10,7 @@ 1. [Code Review](#code-review) 1. [Rework Code (if needed)](#rework-code-if-needed) 1. [Acceptance](#acceptance) + 1. [Backporting Changes](#backporting-changes) 1. [Review Bot](#review-bot) 7. [Contribution Standards](#contribution-standards) 1. [Contribution Checklist](#contribution-checklist) @@ -232,6 +233,23 @@ these signatures intact, we prefer using merge commits. PR proposers can use Rejoice as you will now be listed as a [contributor](https://github.com/lightningnetwork/lnd/graphs/contributors)! +## Backporting Changes + +After a PR is merged to master, it may need to be backported to release branches +(e.g., `v0.20.x-branch`) to include the fix or feature in upcoming patch releases. + +The project uses an **automated backport workflow** to simplify this process. Simply +add a label like `backport-v0.20.x-branch` to your merged PR, and a GitHub Action +will automatically create a backport PR for you. + +For complete documentation on the automated backport workflow, including: +- How to use backport labels +- Handling merge conflicts +- Multiple backports +- Troubleshooting + +See [backport-workflow.md](backport-workflow.md) + ## Review Bot In order to keep the review flow going, Lightning Labs uses a bot to remind