@@ -2,7 +2,7 @@ name: Automatic Backport
22
33on :
44 pull_request_target :
5- types : [closed] # fires when PR is closed (merged)
5+ types : [closed, labeled ] # fires when PR is closed (merged) or labeled
66
77concurrency :
88 group : backport-${{ github.workflow }}-${{ github.event.pull_request.number }}
@@ -13,41 +13,117 @@ permissions:
1313 pull-requests : write
1414
1515jobs :
16- backport :
16+ # Determine which backports are needed
17+ prepare :
1718 if : |
1819 github.event.pull_request.merged == true &&
19- contains(github.event.pull_request.labels.*.name, 'backport')
20+ (
21+ contains(github.event.pull_request.labels.*.name, 'backport') ||
22+ contains(github.event.pull_request.labels.*.name, 'backport-previous') ||
23+ (github.event.action == 'labeled' && (github.event.label.name == 'backport' || github.event.label.name == 'backport-previous'))
24+ )
2025 runs-on : [self-hosted]
21-
26+ outputs :
27+ backport_current : ${{ steps.labels.outputs.backport }}
28+ backport_previous : ${{ steps.labels.outputs.backport_previous }}
29+ current_branch : ${{ steps.branches.outputs.current_branch }}
30+ previous_branch : ${{ steps.branches.outputs.previous_branch }}
2231 steps :
23- # 1. Decide which maintenance branch should receive the back‑port
24- - name : Determine target maintenance branch
25- id : target
32+ - name : Check which labels are present
33+ id : labels
2634 uses : actions/github-script@v7
2735 with :
2836 script : |
29- let rel;
37+ const pr = context.payload.pull_request;
38+ const labels = pr.labels.map(l => l.name);
39+ const isBackport = labels.includes('backport');
40+ const isBackportPrevious = labels.includes('backport-previous');
41+
42+ core.setOutput('backport', isBackport ? 'true' : 'false');
43+ core.setOutput('backport_previous', isBackportPrevious ? 'true' : 'false');
44+
45+ console.log(`backport label: ${isBackport}, backport-previous label: ${isBackportPrevious}`);
46+
47+ - name : Determine target branches
48+ id : branches
49+ uses : actions/github-script@v7
50+ with :
51+ script : |
52+ // Get latest release
53+ let latestRelease;
3054 try {
31- rel = await github.rest.repos.getLatestRelease({
55+ latestRelease = await github.rest.repos.getLatestRelease({
3256 owner: context.repo.owner,
3357 repo: context.repo.repo
3458 });
3559 } catch (e) {
3660 core.setFailed('No existing releases found; cannot determine backport target.');
3761 return;
3862 }
39- const [maj, min] = rel.data.tag_name.replace(/^v/, '').split('.');
40- const branch = `release-${maj}.${min}`;
41- core.setOutput('branch', branch);
42- console.log(`Latest release ${rel.data.tag_name}; backporting to ${branch}`);
63+
64+ const [maj, min] = latestRelease.data.tag_name.replace(/^v/, '').split('.');
65+ const currentBranch = `release-${maj}.${min}`;
66+ const prevMin = parseInt(min) - 1;
67+ const previousBranch = prevMin >= 0 ? `release-${maj}.${prevMin}` : '';
68+
69+ core.setOutput('current_branch', currentBranch);
70+ core.setOutput('previous_branch', previousBranch);
71+
72+ console.log(`Current branch: ${currentBranch}, Previous branch: ${previousBranch || 'N/A'}`);
73+
74+ // Verify previous branch exists if we need it
75+ if (previousBranch && '${{ steps.labels.outputs.backport_previous }}' === 'true') {
76+ try {
77+ await github.rest.repos.getBranch({
78+ owner: context.repo.owner,
79+ repo: context.repo.repo,
80+ branch: previousBranch
81+ });
82+ console.log(`Previous branch ${previousBranch} exists`);
83+ } catch (e) {
84+ core.setFailed(`Previous branch ${previousBranch} does not exist.`);
85+ return;
86+ }
87+ }
88+
89+ backport :
90+ needs : prepare
91+ if : |
92+ github.event.pull_request.merged == true &&
93+ (needs.prepare.outputs.backport_current == 'true' || needs.prepare.outputs.backport_previous == 'true')
94+ runs-on : [self-hosted]
95+ strategy :
96+ matrix :
97+ backport_type : [current, previous]
98+ steps :
99+ # 1. Determine target branch based on matrix
100+ - name : Set target branch
101+ id : target
102+ if : |
103+ (matrix.backport_type == 'current' && needs.prepare.outputs.backport_current == 'true') ||
104+ (matrix.backport_type == 'previous' && needs.prepare.outputs.backport_previous == 'true')
105+ run : |
106+ if [ "${{ matrix.backport_type }}" == "current" ]; then
107+ echo "branch=${{ needs.prepare.outputs.current_branch }}" >> $GITHUB_OUTPUT
108+ echo "Target branch: ${{ needs.prepare.outputs.current_branch }}"
109+ else
110+ echo "branch=${{ needs.prepare.outputs.previous_branch }}" >> $GITHUB_OUTPUT
111+ echo "Target branch: ${{ needs.prepare.outputs.previous_branch }}"
112+ fi
113+
43114 # 2. Checkout (required by backport‑action)
44115 - name : Checkout repository
116+ if : steps.target.outcome == 'success'
45117 uses : actions/checkout@v4
46118
47119 # 3. Create the back‑port pull request
48120 - name : Create back‑port PR
49- uses : korthout/backport-action@v3
121+ id : backport
122+ if : steps.target.outcome == 'success'
123+ 50124 with :
51125 github_token : ${{ secrets.GITHUB_TOKEN }}
52126 label_pattern : ' ' # don't read labels for targets
53127 target_branches : ${{ steps.target.outputs.branch }}
128+ merge_commits : skip
129+ conflict_resolution : draft_commit_conflicts
0 commit comments