Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to fallback to a different branch on failure #105

Closed
znarf opened this issue Dec 11, 2019 · 17 comments · May be fixed by #106
Closed

Ability to fallback to a different branch on failure #105

znarf opened this issue Dec 11, 2019 · 17 comments · May be fixed by #106
Assignees
Labels
enhancement New feature or request external

Comments

@znarf
Copy link

znarf commented Dec 11, 2019

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

opencollective/opencollective-api#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
@znarf znarf changed the title Ability to fallback on a different branch on failure Ability to fallback to a different branch on failure Dec 11, 2019
@chrispat
Copy link
Member

chrispat commented Dec 12, 2019

@znarf You can always set continue-on-error for the step and a failure will not cause the overall workflow to fail https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepscontinue-on-error.

For the second item we do have a feature on the backlog (no current timeframe) to have the outcome of a step be part of the steps context.

@znarf
Copy link
Author

znarf commented Dec 12, 2019

@chrispat Thank you for pointing on continue-on-error, I missed that. That could effectively replace the first item.

For the second item, I guess that should not be complicated to implement, I shared a proof of concept as Pull Request, let me know what you think.

@chrispat
Copy link
Member

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

@znarf
Copy link
Author

znarf commented Dec 12, 2019

@chrispat all right, let me know if there is an open issue so I can have a look.

@chrispat
Copy link
Member

@znarf unfortunately those issues are not in a public repo.

@znarf
Copy link
Author

znarf commented Dec 12, 2019

@chrispat Would the work be in https://github.com/actions/toolkit ? If yes, which of the packages https://github.com/actions/toolkit/tree/master/packages would be affected?

@ericsciple ericsciple added the enhancement New feature or request label Dec 13, 2019
@chrispat
Copy link
Member

@znarf the work has to happen in the runner which is they only place where state is stored between executions of different actions.

@ericsciple ericsciple self-assigned this Jan 10, 2020
@ericsciple
Copy link
Contributor

this should be a small change. i'll open an ADR soon.

@ericsciple
Copy link
Contributor

Thinking more about this, i believe you will get hit with a retry if you rely on failure.

A better solution might be to check if the branch exists (query REST API), and set an output that you can use in the if condition to determine whether to checkout from the other repo.

Something like this:

on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - id: check-branch
        uses: octokit/request-action@v2.x
        with:
          route: GET /repos/:owner/:repo/git/ref/:ref
          owner: some-owner
          repo: some-repo
          ref: heads/master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        continue-on-error: true
      - if: steps.check-branch.outputs.status == 200
        run: echo Branch exists
      - if: steps.check-branch.outputs.status != 200
        run: echo Branch does not exist

However when 404, it appears octokit/request-action creates error annotations so i'm not sure thats what you want but it's close. Might be a good starting point.

@ericsciple
Copy link
Contributor

also i noticed it doesnt appear to set any outputs when 404 (not even status)

@ximon18
Copy link

ximon18 commented Jan 22, 2020

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

@znarf
Copy link
Author

znarf commented Jan 22, 2020

@ericsciple thank you for the elegant solution, I'll try that.

@ericsciple
Copy link
Contributor

closing. fwiw proposal here for adding step outcome actions/runner#274

@negebauer
Copy link

negebauer commented Aug 10, 2020

i"m having an issue with the steps check. If I have the following:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Test failed
        id: failed
        run: exit 1
      - name: Test not failed
        id: notfailed
        if: success() || failure()
        run: exit 0
      - name: Should run
        id: bar
        if: steps.notfailed.outcome == 'success'
        run: exit 0

The Should run step is skipped, even thought steps.notfailed.outcome == 'success' evaluates to true. (did an echo of it and checked a dump to confirm)

But if I write the step as

      - name: Should run
        id: bar
        if: steps.notfailed.outcome == 'success' || success()
        run: exit 0

It works. It's like the step check is not enough? (Given that success() is false since the first step failed).

@chrispat
Copy link
Member

Any if block that does not contain a status function automatically has success() added with an &&. We do this because we believe in most cases customers only want steps to run if the overall job is successful.

@negebauer
Copy link

Maybe the documentation can be improved? I didn't understand what you mentioned from reading it, but reading your message it's super clear.

You can use the following status check functions as expressions in if conditionals. If your if expression does not contain any of the status functions it will automatically result with success(). For more information about if conditionals, see "Workflow syntax for GitHub Actions."

Maybe replace

If your if expression does not contain any of the status functions it will automatically result with success()

With your message here

Any if block that does not contain a status function automatically has success() added with an &&.

@mfn
Copy link

mfn commented Aug 26, 2021

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request external
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants