30

I want to read a JSON file and use a property in a string in a Github Actions YAML file. How do I do this? (I want the version of the package.json)

exoRift
  • 531
  • 1
  • 5
  • 13

9 Answers9

39

Use the built-in fromJson(value) (see here: https://docs.github.com/en/actions/learn-github-actions/expressions#fromjson)

Reading a file depends on the shell you're using. Here's an example for sh:

name: Test linux job
on:
  push

jobs:
  testJob:
    name: Test
    runs-on: ubuntu-latest
    steps:
      - id: set_var
        run: |
          content=`cat ./path/to/package.json`
          # the following lines are only required for multi line json
          content="${content//'%'/'%25'}"
          content="${content//$'\n'/'%0A'}"
          content="${content//$'\r'/'%0D'}"
          # end of optional handling for multi line json
          echo "::set-output name=packageJson::$content"
      - run: |
          echo "${{fromJson(steps.set_var.outputs.packageJson).version}}"

Multi line JSON handling as per https://github.com/orgs/community/discussions/26288#discussioncomment-3251220.

GitHub issue about set-env / set-output multi line handling: https://github.com/actions/toolkit/issues/403

riQQ
  • 9,878
  • 7
  • 49
  • 66
  • 2
    How to access the object's property after using fromJson? echo result.property or $result.property or ${ result.property } or ${{ result.property }}? – michael Jul 28 '21 at 14:11
  • michael's comment was asked as a separate question here: https://stackoverflow.com/questions/68557487/access-to-the-object-s-property-created-from-the-json-in-the-github-actions – riQQ Mar 01 '22 at 12:33
  • Can you provide Powershell alternative for Windows Runner for the multline json and matrix conversion? – wehelpdox Mar 24 '22 at 01:48
  • Since I don't care about preserving newlines, I did something like `cat package.json | tr '\n\r' ' '` although that doesn't solve encoding % signs properly. – Kimball Robinson Mar 25 '22 at 16:44
  • You can also use `jq` to quickly get json data: `cat package.json | jq '.version'` – Kimball Robinson Mar 25 '22 at 17:07
  • This didn't work for me until I changed the \r replacement to occur prior to the \n replacement. – The Geek Aug 15 '22 at 06:41
13

Use a multi line environment variable:

- run: |
    echo 'PACKAGE_JSON<<EOF' >> $GITHUB_ENV
    cat ./package.json >> $GITHUB_ENV
    echo 'EOF' >> $GITHUB_ENV
- run: |
    echo '${{ fromJson(env.PACKAGE_JSON).version }}'

This avoids any need for escaping.

dastrobu
  • 1,600
  • 1
  • 18
  • 32
  • Using your code verbatim makes my YAML lint complain: `Implicit keys need to be on a single line` – Reinsbrain Sep 07 '22 at 13:57
  • 1
    @Reinsbrain Something did not line out well after copy and pasting. Adding another indentation fixed that error for me. – Chiel Mar 31 '23 at 13:23
11

Below is a version of the example from Official GHA Docs that includes two changes:

  1. Loads json from a file (./your.json)
  2. Removes newline characters (Source)
  3. Uses fromJson to parse the output and set a matrix variable.
name: build
on: push
jobs:
  job1:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
    - id: set-matrix
      run: |
        JSON=$(cat ./your.json)
        echo "::set-output name=matrix::${JSON//'%'/'%25'}"

  job2:
    needs: job1
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{fromJson(needs.job1.outputs.matrix)}}
    steps:
    - run: build
jujule
  • 11,125
  • 3
  • 42
  • 63
Sandwich
  • 2,304
  • 3
  • 24
  • 30
9

Inspired by answer from @dastrobu which adds key/val to $GITHUB_ENV and using jq to transform/minify package.json to a single line:

- run: echo "PACKAGE_JSON=$(jq -c . < package.json)" >> $GITHUB_ENV
- run: echo '${{ fromJson(env.PACKAGE_JSON).version }}'
Reinsbrain
  • 2,235
  • 2
  • 23
  • 35
7

You can easily use the Script action for this.

  - name: "Read JSON"
    uses: actions/github-script@v6
    id: check-env
    with:
      result-encoding: string
      script: |
        try {
          const fs = require('fs')
          const jsonString = fs.readFileSync('./dir/file.json')
          var apps = JSON.parse(jsonString)
        } catch(err) {
          core.error("Error while reading or parsing the JSON")
          core.setFailed(err)
        }
crazyoptimist
  • 125
  • 1
  • 14
ycr
  • 12,828
  • 2
  • 25
  • 45
5
on: [push, pull_request] 
name: Build
jobs:
  build:
    name: Example
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with: 
          path: './'      
      - run: |
          echo "`jq '.base_config[0].value="Alpha-21"' config.json `" > config.json
          echo "`jq '.base_config[1].value="1.2.14"' config.json`" > config.json
          echo "`jq '.base_config[2].value="29/12/2020"' config.json `" > config.json
     
      - uses: EndBug/add-and-commit@v6
        with:
          message: 'Add the version and date'
          add: '*.json --force'
          cwd: './' 
          token: ${{ secrets.TOKEN }} 
riQQ
  • 9,878
  • 7
  • 49
  • 66
arun
  • 51
  • 1
  • 1
2

I once used this to get the value from the json data. Hope this helps

  - name: fetch the json value
    run: |
         githubjson=`cat $GITHUB_EVENT_PATH`
         echo $githubjson
         number=`echo $(jq -r '.number' <<< "$githubjson")`
         PRTitle=`echo $(jq -r '.pull_request.title' <<< "$githubjson")`
         PRUrl=`echo $(jq -r '.pull_request.html_url' <<< "$githubjson")`
         PRBody=`echo $(jq -r '.pull_request.body' <<< "$githubjson")`
Chandan Gupta
  • 452
  • 4
  • 15
1

TL;DR

This answer may be helpful to you if you

  1. have a multi-line JSON file
  2. are using GitHub-Actions on self-hosted servers
  3. need to read the JSON file to decide which self-hosted server you want your jobs to be executed on.
  4. are trying to share contents of a JSON file between multiple jobs.

The code snippet is at the end.

--

I had a somewhat complex scenario where we use several self-hosted GitHub runners. I needed to read from a field in a large configuration JSON file to decide which platform I want the rest of the jobs to be run on.

So the below code starts with a job named configure, which reads from a config.json file in the repository and sets it in the output, the next jobs which depend on this can read from the output.

I tried most of the answers in here and in this post and combined the ones that worked, so thanks to all these people for their comments.

jobs:
  configure:
    runs-on: ubuntu-latest # at first we still don't know which self-hosted server, so we just use ubuntu latest and read the JSON file
    outputs: # here we use the outputs from steps, and set outputs for the job `configure`
      config: ${{ steps.read-json.outputs.config }}
      platform: ${{ steps.get-attribute.outputs.platform }}
    steps:
      - name: Checkout to repository
        uses: actions/checkout@v3  
      - name: Read JSON file # this puts the whole JSON file in the read-json step output
        id: read-json
        run: | # to read multi-line JSON file and not bother with escaping
          echo "config<<EOF" >> $GITHUB_OUTPUT
          cat ./config.json >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT
      
      - name: Add platform in output # for simpler use, you can add a single field in the output as well.
        id: read-attribute
        run: echo "platform=${{fromJson(steps.read-json.outputs.config).platform}}" >> "$GITHUB_OUTPUT"

  job2:
    needs: configure
    runs-on: ${{needs.configure.outputs.platform}}

    steps:
      - name: Log JSON file
        run: echo "${{needs.configure.outputs.config}}"
      - name: Log an attribute in JSON file
        run: echo "${{fromJson(needs.configure.outputs.config).attribute}}"
      
ganjim
  • 1,234
  • 1
  • 16
  • 30
0

With Powershell:

- name: Read json
  id: read-json
  shell: pwsh
  run: |
    $json = Get-Content yourfile.json | ConvertFrom-Json
    echo "::set-output name=prop::$(echo $json.prop)"

- run: echo ${{ steps.read-json.outputs.prop}}
cleison
  • 1,425
  • 1
  • 13
  • 14