Github Actions - Output
Tags: githubactions, cicd, github, devops
Published: 2023-08-22 19:44:14

## Introduction

As **workflows** are built and become more complex, there is a necessity to share information between **steps**, **jobs**, and even **workflows** themselves.

One of the most common ways to share values is by using **GitHub** outputs, due to their simplicity and being a built-in feature of **workflows**. 

This material aims to explore the usage of outputs, their limitations, and best practices. Even though it's straightforward, caution should be exercised in certain aspects.

## Output between Steps

The syntax to generate an output should follow the pattern:

`echo "<output_name>=<output_value>" >> $GITHUB_OUTPUT`

One of the simplest examples is sharing between **steps** within the same job.

```yaml
on: 
   push: 
  
 jobs: 
   step-outputs: 
     runs-on: ubuntu-latest 
     steps: 
       - name: Get runner name 
         id: runner 
         run: | 
           echo "name=ubuntu-latest" >> $GITHUB_OUTPUT 
  
       - name: Show output 
         run: echo ${{ steps.runner.outputs.name }}
```

In the above example, we have two **steps**: the first generates the output, and the second consumes it.

The first **step** has the attribute `id` with the value `runner`, allowing any other **step** within the same job to consume the generated output.

We can observe the consumption of the output in the second **step** using the following syntax:

`${{ steps.<id_previous_step>.outputs.<output_name> }}`

The result of the created workflow will display the value `ubuntu-latest`.

![share output between steps](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hzvyediubsfk5giucn1q.png)

## Output between Jobs

To share output between **jobs**, it's necessary to add the `output` property to the job that will generate the value.
 
This property should contain the reference to the **step** (as described above), and the job that will receive the value must have the **needs** property with the name of the preceding job.

Observe the example:

```yaml
on:
  push:

jobs:
  job-1:
    runs-on: ubuntu-latest
    outputs:
      runner-name: ${{ steps.runner.outputs.name }}
    steps:
      - name: Set runner name
        id: runner
        run: |
          echo "name=ubuntu-latest" >> $GITHUB_OUTPUT
  job-2:
    runs-on: ubuntu-latest
    needs: job-1
    steps:
      - name: Show job-1 output
        run: |
          echo "Output job-1: ${{ needs.job-1.outputs.runner-name}}"
```

## Output with actions/core

It's also possible to use output through the [github-script](https://github.com/actions/github-script) action.

This action is commonly used when integration with **GitHub** is required.

```yaml
on:
  push:

jobs:
  job-actions-core:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v6
        id: actions-core
        with:
          script: |
            core.setOutput('result', 'core')
      - name: show output actions-core
        run: |
          echo "${{ steps.actions-core.outputs.result }}"
```

## What Not to Do

### Overwriting the GITHUB_OUTPUT Variable

A common mistake to watch out for is overwriting the `GITHUB_OUTPUT` variable within the same **step**. If this happens, all previously saved outputs will be lost.

```yaml
on:
  push:

jobs:
  job-output-overwrite:
    runs-on: ubuntu-latest
    steps:
      - name: Overwrite output
        id: runner
        run: |
          echo "name=ubuntu-latest" >> $GITHUB_OUTPUT
          echo "so=linux" > $GITHUB_OUTPUT
      - name: show output
        run: |
          echo ${{ steps.runner.outputs.name }}
          echo ${{ steps.runner.outputs.so }}
```

![show result overwrite output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jvh5v6mi4dypwvez0r4g.png)

This occurs because the `GITHUB_OUTPUT` variable has a file as a reference.

Similar to the `/etc/environment` file, the `GITHUB_OUTPUT` file is loaded with each new **step**.

### Sharing Secrets

Values stored in secrets cannot be shared as output between **jobs**.

Let's assume there is a secret with the value `my-secret`. To add a secret, simply follow the path `Settings > Secrets and variables > Actions > New repository secret`.

![saving secret in repository settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7i0ymktizin0r2w0mj55.png)

Now let's assign the secret to an output.

```yaml
jobs:
  job-1-secret:
    runs-on: ubuntu-latest
    outputs:
      value: ${{ steps.step-1.outputs.value }}
    steps:
      - name: Set secret as output
        id: step-1
        run: |
          echo "value=${{ secrets.SECRET_OUTPUT }}" >> $GITHUB_OUTPUT

  job-2-secret:
    runs-on: ubuntu-latest
    needs: job-1-secret
    steps:
      - name: Show output job-1-secret
        run: |
          echo "Output with secret: ${{ needs.job-1-secret.outputs.value}}"
```

Upon executing the workflow, the following warning will be received.


![Warning for sharing secret](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ex3pi837r2rpqqxw0lcu.png)

### Deprecated Output

The syntax below works, but it's deprecated and should not be used as we do not know when it will cease to function.

```yaml
on:
  push:

jobs:
  job-deprecated-output:
    runs-on: ubuntu-latest
    steps:
      - name: Output deprecated
        id: runner
        run: echo "::set-output name=name::ubuntu-latest"
      - name: show output
        run: echo ${{ steps.runner.outputs.name }}
```

Upon execution, the following warning can be observed.

![deprecated output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ex2e8r4gcnslhfotki78.png)

For more information, refer to the **GitHub** post about [Deprecating save-state and set-output commands](https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/).

## Summary

In summary, **GitHub** workflows allow for sharing information between **steps**, **jobs**, and workflows through outputs. However, it's necessary to be mindful of potential syntax errors, value overwrites, and to stay informed about best practices for the tool by following the changelog.