So in my case I have a GitHub actions workflow that runs some tests on every push for every branch seperately. These tests generate an HTML report that can be deployed as a static website. I want to use GitHub Pages for the deployment of these reports, so my team can always have a look at them immediately after a worklfow run finished.
Since GitHub Pages are backed by a regular git repo I always need to commit and push new reports to that repo in my workflow definition. I also planned to have a seperate folder containing the HTML report for each workflow run individually to make newly pushed report folders completely indepent from each other.
As multiple pipelines are potentially running at the same time multiple branches/workflow runs could try to publish their reports on the same repo at the same time. This could lead to racing conditions where two different workflows try to push at the same time and one will fail because it's not up-to-date anymore. In reality those pushed reports are completely independent from each other, since they would push completely new folders that have no correlation ever.
Is it possible to push a completly new folder to a GitHub repo (or any git repo in general), so it would always succeed because there are no conflicts ever possible? Is there some dedicated git command to "infitely" rebase until it works or there is a conflict?
I'm assuming your github action do something like this:
# generate your html report here
exec ./bin/generate_report
# commit
git add .
git commit -m "auto generated at `date`"
git push origin master
Now you're concerned that multiple github action may run simultaneously, it might create a race condition and some of your push will be rejected because of not having same parent commit.
To solve that, you can use rebase and loop it until it successfully push like below:
# create branch name with unix timestamp
BRANCH_NAME="report-`date +%s`"
git branch $BRANCH_NAME
git checkout $BRANCH_NAME
# generate your html report here
exec ./bin/generate_report
# commit
git add .
git commit -m "auto generated $BRANCH_NAME"
# push to branch as backup
git push -u origin $BRANCH_NAME
# 'infinitely' pull,rebase,push until push is successful
PUSH_SUCCESS=0
while [ $PUSH_SUCCESS -eq 0 ]; do
git checkout master
git pull origin master
git checkout $BRANCH_NAME
git rebase master
git checkout master
git merge --ff-only $BRANCH_NAME
git push origin master
if [ $? -eq 0 ]; then
PUSH_SUCCESS=1
fi
done
With this, if in the time between pulling and rebasing, the master branch in remote already changed (e.g.: by other async action), you will just retry (pull-rebase-push) until success.
You can also improve it by exiting if the rebase failed, that way it won't be infinite loop when rebase failed for some unforeseen circumstances.