RSSAmplifier

Tarjei Husøy’s blag · May 31, 2020

Branch-specific variables for GitHub Actions

0
Sign in to vote or save

2020-05-31 00:04 · thusoy.com

When setting up a CI/CD pipeline you might find that you need to set some variables depending on which branch you’re on. With GitHub Actions there’s good support for setting global variables which can be overridden on a per-job or per-step basis, but setting branch-specific variables is a bit less straight forward. This post outlines the cleanest approach I’ve found for this so far.

First of all you want to declare the variables you want in a global lookup table as json.

env:
  lookup: |
    {
      "master": {
        "TARGET_ENV": "staging",
        "BUCKET": "example-staging"
      },
      "prod": {
        "TARGET_ENV": "prod",
        "BUCKET": "example-prod"
      }
    }

Then as one of the first steps in your job you want to utilize workflow commands to dynamically set environment variables based on the lookup table you defined. You can use any programming language you prefer to do this, you just need to load the lookup table, parse the current branch and output lines with ::set-env name=KEY::VALUE for each variable to set. I used node for this since json-parsing is easily available without any extra imports, which keeps the code for this small. There’s no existing environment variable for the current branch, but you can extract this from the GITHUB_REF which looks like refs/heads/<branch-name>. The code to do this:

const branch = process.env.GITHUB_REF.slice("refs/heads/".length);
const data = JSON.parse(process.env.lookup)[branch];
for (const key in data) {
    console.log(`::set-env name=${key}::${data[key]}`)
}

Should be pretty straight forward. Compacting this a bit we get the following oneliner we can make a job step out of

- name: Put branch-specific variables into env
  run: node -e 'let data = JSON.parse(process.env.lookup)[process.env.GITHUB_REF.slice("refs/heads/".length)]; for (let key in data) {console.log(`::set-env name=${key}::${data[key]}`)}'

Obviously this can be golfed much further if you want to. I think this preserves a decent balance between compactness and legibility, but if you really want to keep this short for some reason, we can remove the lets as they’re not strictly required, trim some whitespace, use single letter variable names, and replace the process.env lookups with passing in the strings from bash instead. That yields this fairly terse variant:

- name: Put branch-specific variables into env (compact version)
  run: node -e "d=JSON.parse('$lookup')['${GITHUB_REF:11}'];for(k in d){console.log('::set-env name='+k+'::'+d[k])}"

Whichever version you chose, you can now use the variables from your lookup table as you would usually in later steps:

- name: Some deploy step
  run: ./deploy.sh ${{ env.BUCKET }}

Putting it all together with a check to only deploy the target branches:

env:
  lookup: |
    {
      "master": {
        "BUCKET": "example-staging"
      },
      "prod": {
        "BUCKET": "example-prod"
      }
    }
jobs:
  deploy:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/prod'
    steps:
        - name: Put branch-specific variables into env (compact version)
          run: node -e "d=JSON.parse('$lookup')['${GITHUB_REF:11}'];for(k in d){console.log('::set-env name='+k+'::'+d[k])}"
        - name: Deploy
          run: ./deploy.sh ${{ env.BUCKET }}

Read the original on thusoy.com

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.