11name : Update from Template
22
33# This workflow keeps the repo up to date with changes from the template repo (REMOTE_URL)
4- # It duplicates the REMOTE_BRANCH (into UPDATE_BRANCH) and tries to merge it into the
4+ # It duplicates the REMOTE_BRANCH (into UPDATE_BRANCH) and tries to merge it into
55# this repos default branch (which is checked out here)
66# Note that this requires a PAT (Personal Access Token) - at best from a servicing account
7+ # PAT permissions: read:discussion, read:org, repo, workflow
78# Also note that you should have at least once merged the template repo into the current repo manually
89# otherwise a "refusing to merge unrelated histories" error might occur.
910
1011on :
1112 schedule :
1213 - cron : ' 55 2 * * 1'
1314 workflow_dispatch :
15+ inputs :
16+ no_automatic_merge :
17+ type : boolean
18+ description : ' No automatic merge'
19+ default : false
1420
1521env :
1622 UPDATE_BRANCH : update-from-template
23+ UPDATE_BRANCH_MERGED : update-from-template-merged
1724 REMOTE_URL : https://github.com/xdev-software/standard-maven-template.git
1825 REMOTE_BRANCH : master
1926
@@ -36,31 +43,34 @@ jobs:
3643
3744 - name : Init Git
3845 run : |
39- git config --global user.email "actions@ github.com"
40- git config --global user.name "GitHub Actions "
46+ git config --global user.email "[email protected] . github.com" 47+ git config --global user.name "XDEV Bot "
4148
42- - name : Main workflow
43- id : main
49+ - name : Manage branches
50+ id : manage-branches
4451 run : |
4552 echo "Adding remote template-repo"
4653 git remote add template ${{ env.REMOTE_URL }}
4754
4855 echo "Fetching remote template repo"
4956 git fetch template
5057
51- echo "Deleting local branch that will contain the updates - if present"
58+ echo "Deleting local branches that will contain the updates - if present"
5259 git branch -D ${{ env.UPDATE_BRANCH }} || true
60+ git branch -D ${{ env.UPDATE_BRANCH_MERGED }} || true
5361
5462 echo "Checking if the remote template repo has new commits"
5563 git rev-list ..template/${{ env.REMOTE_BRANCH }}
5664
5765 if [ $(git rev-list --count ..template/${{ env.REMOTE_BRANCH }}) -eq 0 ]; then
5866 echo "There are no commits new commits on the template repo"
5967
60- echo "Deleting origin branch that contains the updates - if present"
68+ echo "Deleting origin branch(es) that contain the updates - if present"
6169 git push -f origin --delete ${{ env.UPDATE_BRANCH }} || true
70+ git push -f origin --delete ${{ env.UPDATE_BRANCH_MERGED }} || true
6271
63- echo "abort=1" >> $GITHUB_OUTPUT
72+ echo "create_update_branch_pr=0" >> $GITHUB_OUTPUT
73+ echo "create_update_branch_merged_pr=0" >> $GITHUB_OUTPUT
6474 exit 0
6575 fi
6676
@@ -73,21 +83,198 @@ jobs:
7383 echo "Pushing update branch"
7484 git push -f -u origin ${{ env.UPDATE_BRANCH }}
7585
76- echo "Getting current branch"
77- current_branch =$(git branch --show-current)
78- echo "Current branch is $current_branch "
79- echo "current_branch=$current_branch " >> $GITHUB_OUTPUT
86+ echo "Getting base branch"
87+ base_branch =$(git branch --show-current)
88+ echo "Base branch is $base_branch "
89+ echo "base_branch=$base_branch " >> $GITHUB_OUTPUT
8090
81- echo "abort=0" >> $GITHUB_OUTPUT
91+ echo "Trying to create auto-merged branch ${{ env.UPDATE_BRANCH_MERGED }}"
92+ git branch ${{ env.UPDATE_BRANCH_MERGED }} ${{ env.UPDATE_BRANCH }}
93+ git checkout ${{ env.UPDATE_BRANCH_MERGED }}
8294
83- - name : pull-request
84- if : steps.main.outputs.abort == 0
95+ echo "Merging branch $base_branch into ${{ env.UPDATE_BRANCH_MERGED }}"
96+ git merge $base_branch && merge_exit_code=$? || merge_exit_code=$?
97+ if [ $merge_exit_code -ne 0 ]; then
98+ echo "Auto merge failed! Manual merge required"
99+ echo "::notice ::Auto merge failed - Manual merge required"
100+
101+ echo "Cleaning up failed merge"
102+ git merge --abort
103+ git checkout $base_branch
104+ git branch -D ${{ env.UPDATE_BRANCH_MERGED }} || true
105+
106+ echo "Deleting auto-merge branch - if present"
107+ git push -f origin --delete ${{ env.UPDATE_BRANCH_MERGED }} || true
108+
109+ echo "create_update_branch_pr=1" >> $GITHUB_OUTPUT
110+ echo "create_update_branch_merged_pr=0" >> $GITHUB_OUTPUT
111+ exit 0
112+ fi
113+
114+ echo "Post processing: Trying to automatically fill in template variables"
115+ find . -type f \
116+ -not -path "./.git/**" \
117+ -not -path "./.github/workflows/update-from-template.yml" -print0 \
118+ | xargs -0 sed -i "s/template-placeholder/${GITHUB_REPOSITORY#*/}/g"
119+
120+ git status
121+ git add --all
122+
123+ if [[ "$(git status --porcelain)" != "" ]]; then
124+ echo "Filled in template; Committing"
125+
126+ git commit -m "Fill in template"
127+ fi
128+
129+ echo "Pushing auto-merged branch"
130+ git push -f -u origin ${{ env.UPDATE_BRANCH_MERGED }}
131+
132+ echo "update_branch_merged_commit=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
133+
134+ echo "Restoring base branch $base_branch"
135+ git checkout $base_branch
136+
137+ echo "create_update_branch_pr=0" >> $GITHUB_OUTPUT
138+ echo "create_update_branch_merged_pr=1" >> $GITHUB_OUTPUT
139+ echo "try_close_update_branch_pr=1" >> $GITHUB_OUTPUT
140+
141+ - name : Create/Update PR update_branch
142+ if : steps.manage-branches.outputs.create_update_branch_pr == 1
85143 env :
86- GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
144+ GH_TOKEN : ${{ secrets.UPDATE_FROM_TEMPLATE_PAT }}
87145 run : |
88146 gh_pr_up() {
89147 gh pr create -H "${{ env.UPDATE_BRANCH }}" "$@" || (git checkout "${{ env.UPDATE_BRANCH }}" && gh pr edit "$@")
90148 }
91- gh_pr_up -B "${{ steps.main .outputs.current_branch }}" \
149+ gh_pr_up -B "${{ steps.manage-branches .outputs.base_branch }}" \
92150 --title "Update from template" \
93151 --body "An automated PR to sync changes from the template into this repo"
152+
153+ # Ensure that only a single PR is open (otherwise confusion and spam)
154+ - name : Close PR update_branch
155+ if : steps.manage-branches.outputs.try_close_update_branch_pr == 1
156+ env :
157+ GH_TOKEN : ${{ secrets.UPDATE_FROM_TEMPLATE_PAT }}
158+ run : |
159+ gh pr close "${{ env.UPDATE_BRANCH }}" || true
160+
161+ - name : Create/Update PR update_branch_merged
162+ if : steps.manage-branches.outputs.create_update_branch_merged_pr == 1
163+ env :
164+ GH_TOKEN : ${{ secrets.UPDATE_FROM_TEMPLATE_PAT }}
165+ run : |
166+ gh_pr_up() {
167+ gh pr create -H "${{ env.UPDATE_BRANCH_MERGED }}" "$@" || (git checkout "${{ env.UPDATE_BRANCH_MERGED }}" && gh pr edit "$@")
168+ }
169+ gh_pr_up -B "${{ steps.manage-branches.outputs.base_branch }}" \
170+ --title "Update from template (auto-merged)" \
171+ --body "An automated PR to sync changes from the template into this repo"
172+
173+ - name : Checking if auto-merge for PR update_branch_merged can be done
174+ id : auto-merge-check
175+ if : steps.manage-branches.outputs.create_update_branch_merged_pr == 1
176+ env :
177+ GH_TOKEN : ${{ secrets.UPDATE_FROM_TEMPLATE_PAT }}
178+ run : |
179+ not_failed_conclusion="skipped|neutral|success"
180+ not_relevant_app_slug="dependabot|github-pages"
181+
182+ echo "Waiting for workflows to start..."
183+ sleep 60s
184+
185+ for i in {1..15}; do
186+ echo "Checking if PR can be auto-merged. Try: $i"
187+
188+ echo "Fetching checks"
189+ cs_response=$(curl -sL \
190+ --fail-with-body \
191+ --connect-timeout 60 \
192+ --max-time 120 \
193+ -H "Accept: application/vnd.github+json" \
194+ -H "Authorization: Bearer $GH_TOKEN" \
195+ -H "X-GitHub-Api-Version: 2022-11-28" \
196+ https://api.github.com/repos/${{ github.repository }}/commits/${{ steps.manage-branches.outputs.update_branch_merged_commit }}/check-suites)
197+
198+ cs_data=$(echo $cs_response | jq '.check_suites[] | { conclusion: .conclusion, slug: .app.slug, check_runs_url: .check_runs_url }')
199+ echo $cs_data
200+
201+ if [[ -z "$cs_data" ]]; then
202+ echo "No check suite data - Assuming that there are no checks to run"
203+
204+ echo "perform=1" >> $GITHUB_OUTPUT
205+ exit 0
206+ fi
207+
208+ cs_failed=$(echo $cs_data | jq --arg x "$not_failed_conclusion" 'select ((.conclusion == null or (.conclusion | test($x))) | not)')
209+ if [[ -z "$cs_failed" ]]; then
210+ echo "No check failed so far; Checking if relevant checks are still running"
211+
212+ cs_relevant_still_running=$(echo $cs_data | jq --arg x "$not_relevant_app_slug" 'select (.conclusion == null and (.slug | test($x) | not))')
213+ if [[ -z $cs_relevant_still_running ]]; then
214+ echo "All relevant checks finished - PR can be merged"
215+
216+ echo "perform=1" >> $GITHUB_OUTPUT
217+ exit 0
218+ else
219+ echo "Relevant checks are still running"
220+ echo $cs_relevant_still_running
221+ fi
222+ else
223+ echo "Detected failed check"
224+ echo $cs_failed
225+
226+ echo "perform=0" >> $GITHUB_OUTPUT
227+ exit 0
228+ fi
229+
230+ echo "Waiting before next run..."
231+ sleep 60s
232+ done
233+
234+ echo "Timed out"
235+ echo "perform=0" >> $GITHUB_OUTPUT
236+
237+ - name : Auto-merge update_branch_merged
238+ if : steps.auto-merge-check.outputs.perform == 1
239+ run : |
240+ base_branch="${{ steps.manage-branches.outputs.base_branch }}"
241+ echo "Restoring base branch $base_branch"
242+ git checkout $base_branch
243+
244+ echo "Fetching..."
245+ git fetch
246+
247+ expected_commit="${{ steps.manage-branches.outputs.update_branch_merged_commit }}"
248+ actual_commit=$(git rev-parse origin/${{ env.UPDATE_BRANCH_MERGED }})
249+ if [[ "$expected_commit" != "$actual_commit" ]]; then
250+ echo "Branch ${{ env.UPDATE_BRANCH_MERGED }} contains unexpected commit $actual_commit"
251+ echo "Expected: $expected_commit"
252+
253+ exit 0
254+ fi
255+
256+ echo "Ensuring that current branch $base_branch is up-to-date"
257+ git pull
258+
259+ echo "Merging ${{ env.UPDATE_BRANCH_MERGED }} into $base_branch"
260+ git merge ${{ env.UPDATE_BRANCH_MERGED }} && merge_exit_code=$? || merge_exit_code=$?
261+ if [ $merge_exit_code -ne 0 ]; then
262+ echo "Unexpected merge failure $merge_exit_code - Requires manual resolution"
263+
264+ exit 0
265+ fi
266+
267+ if [[ "${{ inputs.no_automatic_merge }}" == "true" ]]; then
268+ echo "Exiting due no_automatic_merge"
269+
270+ exit 0
271+ fi
272+
273+ echo "Pushing"
274+ git push
275+
276+ echo "Cleaning up"
277+ git branch -D ${{ env.UPDATE_BRANCH }} || true
278+ git branch -D ${{ env.UPDATE_BRANCH_MERGED }} || true
279+ git push -f origin --delete ${{ env.UPDATE_BRANCH }} || true
280+ git push -f origin --delete ${{ env.UPDATE_BRANCH_MERGED }} || true
0 commit comments