Search code examples
gitgithubgithub-actionsgithub-pages

How to push a new folder to GitHub and rebase automatically if necessary?


Scenario

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.

Problem

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.

Question

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?


Solution

  • 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.