Search code examples
githubgithub-actionsgithub-enterprise

GitHub Environments deploying staging on tag push fails


I am trying to deploy infrastructure as code from main branch on multiple environments with GitHub environments. I want to deploy whenever there is merge/push to main in development env, but when there is a tag on the commit like r2022-09-07 deploy the code on a staging env. but it fails every time due to the protection rule.

This is the error I get when the code needs to be deployed on staging:
enter image description here

This is the ci.yml workflow I have for deploying on multiple env from main branch using GitHub env.
name: Lint, Compile and Deploy

on:
  push:
    branches: [main]
    tags:
    - 'r*'
  pull_request:

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: install deps
        run: yarn --frozen-lockfile

      - run: yarn lint
      - run: yarn prettier
      - run: yarn compile
      - run: yarn synth
      - run: yarn test

  # CD: ci -> dev -> staging -> production

  ## only deploy to dev from main branch
  deploy-dev:
    if: ${{ github.ref_name == 'main' }}
    needs: ci
    runs-on: ubuntu-latest
    environment:
      name: Dev
      url: https://...
    env:
      STACK: ...
      AAD_TENANT: ...
      ARM_TENANT_ID: ...
      ARM_ACCESS_KEY: ${{ secrets.ARM_ACCESS_KEY }}
      ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
      ARM_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
    steps:
      - uses: actions/checkout@v3
      - run: yarn --frozen-lockfile --production
      - run: |
          az login --service-principal --tenant $AAD_TENANT \
            --username  "${{ secrets.AZURE_CLIENT_ID }}" --password "${{ secrets.AZURE_CLIENT_SECRET }}"
          yarn deploy $STACK --auto-approve

  ## deploy to staging only from main branch, if a commit has a tag starting with `r` (for ex. r2022-09-07)
  deploy-staging:
    if: ${{ startsWith(github.ref, 'refs/tags/r') }}
    runs-on: ubuntu-latest
    environment:
      name: Staging
      URL: ....
    env:
      STACK: ...
      AAD_TENANT: ...
      ARM_TENANT_ID: ...
      ARM_ACCESS_KEY: ${{ secrets.ARM_ACCESS_KEY }}
      ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
      ARM_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
    steps:
      - uses: actions/checkout@v3
      - run: yarn --frozen-lockfile --production
      - run: |
          az login --service-principal --tenant $AAD_TENANT \
            --username  "${{ secrets.AZURE_CLIENT_ID }}" --password "${{ secrets.AZURE_CLIENT_SECRET }}"
          yarn deploy $STACK --auto-approve  

Staging env protection rules configs:
enter image description here

I was following the official GitHub docs but didn't find anything specific for this case, any idea what should be fixed in the above yaml?


Solution

  • Based on your last screenshot, push events on the main branch are going to be permitted to use the Staging environment.

    I've been playing around with Environments too and had my own question, which lead me to yours!

    My suggestion would be to remove branch protections and then use workflow logic to call the specific Environment:

    on:
      push:
        branches: [main]
        tags:
        - 'r*'
      pull_request:
    
    jobs:
      ci:
        if: startsWith(github.ref_name, 'r*') || github.ref_name == 'main' 
        runs-on: ubuntu-latest
        environment: Staging
        steps:
          ...
    

    Edit based on comments made by OP on 10/12/2022

    If you want to deploy to dev when you only push to main:

    on:
      push:
        branches: [main]
    
    jobs:
      ci-dev-only:
        if: github.ref_name == main
        runs-on: ubuntu-latest
        environment: dev
        steps:
          ...
    
      ci-staging:
        if: github.ref_type == tag
        runs-on: ubuntu-latest
        environment: staging
        steps:
          ...
    
      ci-prod:
        if: github.ref_type == tag && startsWith(github.ref_name, 'r*')
        runs-on: ubuntu-latest
        environment: prod
        steps:
          ...
    

    Keep in mind that tags are branch agnostic. You can't pin them on a branch.

    That all being said, I think releasing to dev from your main branch is an anti-pattern. While there are some use cases that use main as a development branch, deployments to dev should be done in a branch. The reason being is that your main branch should be your source of truth. If your code is likely to change between your last push to main to when you tag it, it really should be done in a branch.

    A better pattern would be that you push to staging on main, and then production on a tag.

    But if you have a business case for your pattern, feel free to ignore me.