Skip to content

Commit 8a6bf93

Browse files
authored
backtracking (#189)
1 parent 12f6503 commit 8a6bf93

2 files changed

Lines changed: 151 additions & 1 deletion

File tree

.github/actions/github/branch-protection/lock/action.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ inputs:
88
token:
99
description: 'GitHub token with administration:write (repo admin) permission. Use a PAT; GITHUB_TOKEN cannot call the branch protection API.'
1010
required: true
11+
lock-branch:
12+
description: 'When true, sets lock_branch to prevent even PR merges (use during automated operations). When false (default), only direct pushes are blocked; PRs with required reviews can still be merged.'
13+
required: false
14+
default: 'false'
1115

1216
runs:
1317
using: composite
@@ -30,7 +34,7 @@ runs:
3034
"restrictions": null,
3135
"allow_force_pushes": false,
3236
"allow_deletions": false,
33-
"lock_branch": false
37+
"lock_branch": ${{ inputs.lock-branch }}
3438
}
3539
EOF
3640
then

.github/workflows/backtrack.yml

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
name: 'Backtrack'
2+
3+
on:
4+
pull_request:
5+
types:
6+
- closed
7+
branches:
8+
- 'preview/**'
9+
- 'release/**'
10+
11+
permissions:
12+
actions: read
13+
contents: write
14+
15+
concurrency:
16+
group: backtrack-${{ github.base_ref }}
17+
cancel-in-progress: false
18+
19+
jobs:
20+
backtrack:
21+
name: 'Backtrack changes from ${{ github.base_ref }}'
22+
if: ${{ github.event.pull_request.merged == true }}
23+
runs-on: ubuntu-latest
24+
steps:
25+
- name: 'Checkout ${{ github.base_ref }}'
26+
uses: actions/checkout@v6
27+
with:
28+
ref: ${{ github.base_ref }}
29+
fetch-depth: 0
30+
token: ${{ secrets.GH_ADMIN_TOKEN }}
31+
32+
- name: 'Configure git'
33+
run: |
34+
git config user.name "$(git log -n 1 --pretty=format:%an)"
35+
git config user.email "$(git log -n 1 --pretty=format:%ae)"
36+
37+
- name: 'Resolve backtrack targets'
38+
id: targets
39+
run: |
40+
base_ref="${{ github.base_ref }}"
41+
if [[ "$base_ref" == preview/* ]]; then
42+
version="${base_ref#preview/}"
43+
echo "preview-branch=" >> $GITHUB_OUTPUT # no intermediate branch; merge directly into develop
44+
echo "develop-branch=develop/$version" >> $GITHUB_OUTPUT
45+
echo "merge-source=$base_ref" >> $GITHUB_OUTPUT
46+
elif [[ "$base_ref" == release/* ]]; then
47+
version="${base_ref#release/}"
48+
echo "preview-branch=preview/$version" >> $GITHUB_OUTPUT
49+
echo "develop-branch=develop/$version" >> $GITHUB_OUTPUT
50+
echo "merge-source=preview/$version" >> $GITHUB_OUTPUT
51+
fi
52+
53+
- name: 'Check if develop branch exists'
54+
id: check-develop
55+
run: |
56+
git fetch origin
57+
if git ls-remote --exit-code --heads origin "${{ steps.targets.outputs.develop-branch }}" > /dev/null 2>&1; then
58+
echo "exists=true" >> $GITHUB_OUTPUT
59+
else
60+
echo "exists=false" >> $GITHUB_OUTPUT
61+
echo "::warning::Develop branch '${{ steps.targets.outputs.develop-branch }}' not found, skipping backtrack."
62+
fi
63+
64+
- name: 'Lock base branch'
65+
id: lock-base
66+
uses: './.github/actions/github/branch-protection/lock'
67+
with:
68+
branch: ${{ github.base_ref }}
69+
token: ${{ secrets.GH_ADMIN_TOKEN }}
70+
lock-branch: 'true'
71+
72+
- name: 'Lock preview branch'
73+
id: lock-preview
74+
if: ${{ steps.targets.outputs.preview-branch != '' }}
75+
uses: './.github/actions/github/branch-protection/lock'
76+
with:
77+
branch: ${{ steps.targets.outputs.preview-branch }}
78+
token: ${{ secrets.GH_ADMIN_TOKEN }}
79+
lock-branch: 'true'
80+
81+
- name: 'Lock develop branch'
82+
id: lock-develop
83+
if: ${{ steps.check-develop.outputs.exists == 'true' }}
84+
uses: './.github/actions/github/branch-protection/lock'
85+
with:
86+
branch: ${{ steps.targets.outputs.develop-branch }}
87+
token: ${{ secrets.GH_ADMIN_TOKEN }}
88+
lock-branch: 'true'
89+
90+
- name: 'Backtrack: merge ${{ github.base_ref }} into ${{ steps.targets.outputs.preview-branch }}'
91+
if: ${{ steps.targets.outputs.preview-branch != '' }}
92+
env:
93+
GH_TOKEN: ${{ secrets.GH_ADMIN_TOKEN }}
94+
run: |
95+
gh api --method POST /repos/${{ github.repository }}/merges \
96+
--field base="${{ steps.targets.outputs.preview-branch }}" \
97+
--field head="${{ github.base_ref }}" \
98+
--field commit_message="Backtrack: merge ${{ github.base_ref }} into ${{ steps.targets.outputs.preview-branch }}"
99+
100+
- name: 'Backtrack: merge ${{ steps.targets.outputs.merge-source }} into ${{ steps.targets.outputs.develop-branch }}'
101+
if: ${{ steps.check-develop.outputs.exists == 'true' }}
102+
env:
103+
GH_TOKEN: ${{ secrets.GH_ADMIN_TOKEN }}
104+
run: |
105+
gh api --method POST /repos/${{ github.repository }}/merges \
106+
--field base="${{ steps.targets.outputs.develop-branch }}" \
107+
--field head="${{ steps.targets.outputs.merge-source }}" \
108+
--field commit_message="Backtrack: merge ${{ steps.targets.outputs.merge-source }} into ${{ steps.targets.outputs.develop-branch }}"
109+
110+
- name: 'Restore protection: base branch'
111+
if: ${{ always() && steps.lock-base.outcome == 'success' }}
112+
uses: './.github/actions/github/branch-protection/lock'
113+
with:
114+
branch: ${{ github.base_ref }}
115+
token: ${{ secrets.GH_ADMIN_TOKEN }}
116+
lock-branch: 'false'
117+
118+
- name: 'Restore protection: preview branch'
119+
if: ${{ always() && steps.lock-preview.outcome == 'success' }}
120+
uses: './.github/actions/github/branch-protection/lock'
121+
with:
122+
branch: ${{ steps.targets.outputs.preview-branch }}
123+
token: ${{ secrets.GH_ADMIN_TOKEN }}
124+
lock-branch: 'false'
125+
126+
- name: 'Restore protection: develop branch'
127+
if: ${{ always() && steps.lock-develop.outcome == 'success' }}
128+
uses: './.github/actions/github/branch-protection/lock'
129+
with:
130+
branch: ${{ steps.targets.outputs.develop-branch }}
131+
token: ${{ secrets.GH_ADMIN_TOKEN }}
132+
lock-branch: 'false'
133+
134+
- name: 'Write backtrack summary'
135+
if: ${{ always() }}
136+
run: |
137+
echo "## 🔁 Backtrack Summary" >> $GITHUB_STEP_SUMMARY
138+
echo "" >> $GITHUB_STEP_SUMMARY
139+
echo "| Branch | Merged from |" >> $GITHUB_STEP_SUMMARY
140+
echo "|--------|-------------|" >> $GITHUB_STEP_SUMMARY
141+
if [[ "${{ steps.targets.outputs.preview-branch }}" != "" ]]; then
142+
echo "| \`${{ steps.targets.outputs.preview-branch }}\` | \`${{ github.base_ref }}\` |" >> $GITHUB_STEP_SUMMARY
143+
fi
144+
if [[ "${{ steps.check-develop.outputs.exists }}" == "true" ]]; then
145+
echo "| \`${{ steps.targets.outputs.develop-branch }}\` | \`${{ steps.targets.outputs.merge-source }}\` |" >> $GITHUB_STEP_SUMMARY
146+
fi

0 commit comments

Comments
 (0)