checkout: Ability to fallback to a different branch on failure

This is a feature request. Because it’s an advanced feature, I understand that it might not be directly implemented but some smaller features would unlock it.

Use case

At Open Collective, we have 2 main repositories, one for our API and one for our Frontend. In our CI, we have e2e tests which are requiring a checkout of both API and Frontend to run.

  • In the most simple scenario, we run a checkout of master in both projects
  • In the most common scenario, we run the e2e tests on feature branches of the API, and they run fine with the master Frontend because they don’t require any modifications of the Frontend
  • In an advanced scenario, if a feature needs a modification on both API and Frontend, we’re using by convention the same exact branch name on API and Frontend (ie: feat/new-events-page) and we want the e2e test to use checkouts of these branches.

Status Quo

We’re using some magic script that is implementing the functionality using downloaded tarballs (didn’t write that!) . https://github.com/opencollective/opencollective-api/blob/master/scripts/ci_checkout_frontend.sh

GitHub Actions to the rescue

We would like to use a nicely written GitHub Action for that. My initial implementation would be:

      - name: Checkout (frontend - matching branch)
        uses: actions/checkout@v2-beta
        with:
          repository: opencollective/opencollective-frontend
          path: opencollective-frontend
          ref: ${{ github.ref }}

      - name: Checkout (frontend - master)
        if: failure()
        uses: actions/checkout@v2-beta
        with:
          repository: opencollective/opencollective-frontend
          path: opencollective-frontend
          ref: master

https://github.com/opencollective/opencollective-api/pull/3031 https://github.com/opencollective/opencollective-api/pull/3031/checks?check_run_id=343540511

It’s almost working but unfortunately the first command is sending a CI failure and we can’t proceed normally after.

Feature Request

  1. Ability to skip the failure signal if the checkout doesn’t work
      - name: Checkout (frontend - matching branch)
        uses: actions/checkout@v2-beta
        with:
          repository: 'opencollective/opencollective-frontend'
          ref: ${{ github.ref }}
          silentFailure: true
  1. Ability to know if the checkout with a given id failed
      - name: Checkout (frontend - matching branch)
        uses: actions/checkout@v2-beta
        id: checkout-matching-branch
        with:
          repository: 'opencollective/opencollective-frontend'
          ref: ${{ github.ref }}
          silentFailure: true

      - name: Checkout (frontend - master)
        if: steps.checkout-matching-branch.outputs.failure === 'true'
        uses: actions/checkout@v2-beta
        with:
          repository: 'opencollective/opencollective-frontend'
          ref: master

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 10
  • Comments: 17 (9 by maintainers)

Most upvoted comments

I worked around this by testing for the existence of the branch using ‘git ls-remote’ then checking out the right branch. See: https://github.com/NLnetLabs/krill/commit/942f6a9fe9b43c2ddcd5adec261964359133d8e0

After reading all of this and the other issue I’m still not sure what would be the "best ™️ " way to achieve this 😅

Maybe someone can chime in with a most optimized example?

My use case is to be able to do this private repos btw; I came up with this and it seems to work. The individual steps are “easy”:

  • get the “short” branch name from the long GITHUB_REf
  • use octokit/request-action to fetch that branch from the other repo and remember the HTTP status code this needs continue-on-error: true
  • small bash if condition to check on the status code and figure out if that branch is available or not write decision which branch to checkout into a new env var
  • run action/checkout and use the env var from previous step

but it’s quite a lot of boilerplate:

      - name: Extract branch name
        run: echo "::set-output name=branch::$(echo ${GITHUB_REF#refs/heads/})"
        id: extract-branch

      - name: Try to retrieve branch from otherrepo
        uses: octokit/request-action@v2.x
        id: get_branch_otherrepo
        with:
          route: GET /repos/user/otherrepo/branches/${{ steps.extract-branch.outputs.branch }}
        env:
          GITHUB_TOKEN: ${{ secrets.TOKEN_WITH_ACCESS_TO_OTHER_REPO }}
        continue-on-error: true

      - name: Determine which otherrepo branch to checkout
        run: |
          if [[ '${{ steps.get_branch_otherrepo.outputs.status }}' = '200' ]]; then
            OTHERREPO_BRANCH="${{ steps.extract-branch.outputs.branch }}"
          else
            OTHERREPO_BRANCH=master
          fi
          echo "Otherrepo branch for checkout: $OTHERREPO_BRANCH"
          echo "OTHERREPO_BRANCH=$OTHERREPO_BRANCH" >> $GITHUB_ENV

      - name: Checkout otherrepo
        uses: actions/checkout@v2
        with:
          repository: user/otherrepo
          token: ${{ secrets.TOKEN_WITH_ACCESS_TO_OTHER_REPO }}
          path: tmp/otherreppo
          ref:  ${{ env.OTHERREPO_BRANCH }}

Before I started out, I had a brute force approach in that I

  • blindly ran action/checkout for otherrepo with the GITHUBREF of this repo
  • if the checkout didn’t work, run action/checkout for otherrepo on master

The downside was that action/checkout retries on errors and if the branch can’t be found, it retries twice and we lose ~30 seconds or so.

The elaborate approach I came up with has “0 seconds latency” basically.

Thanks for any pointers/input!

@znarf the second item is not something that really should be implemented in a single action, it would need to be implemented in the runner so it is consistently applied to all actions. We do have a feature on our backlog to add steps.<step id>.outcome to the context.