mrh1997
mrh1997

Reputation: 1138

Map an environment variable in Github Actions

I created a GitHub Actions Job with a strategy matrix that creates a set of environment variables. One of them is machine_architecture which is either 32 or 64.

In most steps I can use it directly i.e. via ${{ machine_architecture }}. But some steps requires strings like 'i386' vs 'x86_64'. Is there an easy way in github actions to create a map-object that I can use in expressions like:

map_object = { 32: "i386", 64: 'x86_64' }
...
${{ map_object[machine_architecture] }}

If not, what is the idiomatic way in github actions to solve that problem?

PS: I am aware, that I can set environment variables in steps, but the problem is, that these variables are only available for the following steps (i.e. not for usage in "run-on:" tag)

Upvotes: 20

Views: 13017

Answers (5)

Sparr
Sparr

Reputation: 7732

This is an awful solution that will only work in a workflow and not a custom action, but you could put your lookup table in a dummy matrix dimension.

jobs:
  varMap:
    strategy:
      matrix:
        lookup: [{ "32": "i386", "64": "x86_64" }]
        arch: [32, 64]
    runs-on: ubuntu-latest
    steps:
      - name: test
        run: echo "Testing ${{ matrix.lookup[format('{0}',matrix.arch)] }}"

Because lookup is of length 1, it doesn't cause any additional executions of the job, it's just always available alongside your real matrix. Matrix values are one of the few places that the workflow syntax allows real objects and arrays, possibly the only one that can be accessed in arbitrary expressions later in the job. The format call is to convert the number to a string, and wouldn't be necessary if arch was strings instead of integers.

This won't work in custom actions because they don't have matrix functionality. No part of the custom action yaml syntax allows arbitrary arrays/objects.

This may have undesirable effects on the apparent/visible names of your job and/or steps. Like I said, awful solution.

Upvotes: 0

mchlfchr
mchlfchr

Reputation: 4288

I don't need a matrix for this, but I need a map for lookups.

Couldn't get @PeterEvans answer to work with GitHub, so I adapted it slightly:

jobs:
  yourJobName:
    name: Cool Job
    runs-on: ubuntu-latest
    steps:
      - name: Get Machine Arc 
        run: |
          MACHINE_ARC_MAP=$(cat <<END
          {
            32: "i386", 
            64: "x86_64",
            999: ${{ secrets.SECRET_ARC }}
          }
          END
          )
          TARGET_ARC=$(echo $MACHINE_ARC_MAP | jq -r 'to_entries[] | select(.key=="${{ github.event.inputs.machine_architecture }}") | .value')
          echo "TARGET_ARC=$TARGET_ARC" >> $GITHUB_ENV
      
      - name: Echo Selected value
        run: |
          echo ${env.TARGET_VAR}

Upvotes: 2

mrh1997
mrh1997

Reputation: 1138

In the meantime I found a solution:

Although GitHub Actions has no syntax for directly creating a Mappings/Objects it can be done indirectly with fromJson():

${{ fromJson('{ 32: "i386", 64: "x86_64" }')[machine_architecture] }}

this fromJson() will create a mapping from int to string. the following []-operator resolves the int type "machine_architecture" to a string type.

Upvotes: 32

peterevans
peterevans

Reputation: 42220

Here is a way to do it with JSON and jq. It creates the step output ${{ steps.vars.outputs.arch }} which you can use in later steps.

jobs:
  varMap:
    strategy:
      matrix:
        machine_architecture: [32, 64]
    runs-on: ubuntu-latest
    steps:
      - name: Set arch var
        id: vars
        run: |
          echo ::set-output name=arch::\
          $(echo '{ "32": "i386", "64": "x86_64" }' | jq -r 'to_entries[] | select(.key=="${{ matrix.machine_architecture }}") | .value')

      - name: Test arch var
        run: echo "Testing ${{ steps.vars.outputs.arch }}"

Upvotes: 1

Javier Aviles
Javier Aviles

Reputation: 10815

How about your "map_object" is actually a file mapping machine_architecture values into the values you need, such:

32=i386
64=x86_64

or any other format you want to keep.

Then, your job can define it in a secondary variable as:

jobs:
  FirstJob:
    name: job 1
    runs-on: .....
    steps:
      - uses: ....
      - name: Define variables
        run: |
          cat $(cat MAP_OBJECT_FILE_NAME) | grep $(cat machine_architecture)= | > MACHINE_ARCHITECTURE_STRING

From there on, you would have the MACHINE_ARCHITECTURE_STRING variable available for the jobs you need. You could of course do it much more simple concatenating or whatever, but here you maintain the mapping in your code with the mapping file and is escalable.

Upvotes: 0

Related Questions