How to share data between jobs

To pass data, such as your BUILD_STAGE value, from one job to a subsequent job,
you have two primary methods in Gitea/GitHub Actions:

1. Use Job Outputs (Recommended for small string values) 

You can define the variable as an “output” of the first job and then reference that output in a dependent job using the needs context. 

Job 1 (build) – Sets the output:

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      build_stage_output: ${{ steps.set_stage.outputs.BUILD_STAGE }} <em># Map step output to job output</em>
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        
      - name: Set BUILD_STAGE environment variable and output
        id: set_stage <em># Give this step an ID</em>
        run: |
          COMMIT_MSG="${{ github.event.head_commit.message }}"
          BUILD_STAGE="DEV"

          if [[ "$COMMIT_MSG" == *"#ready-for-qa"* ]]; then
            BUILD_STAGE="QA"
          elif [[ "$COMMIT_MSG" == *"#ready-for-prod"* ]]; then
            BUILD_STAGE="PROD"
          fi
          
          # Write to GITHUB_OUTPUT to make it a step output
          echo "BUILD_STAGE=$BUILD_STAGE" >> $GITHUB_OUTPUT 
        shell: bash

Job 2 (deploy) – Reads the output:

  deploy:
    runs-on: ubuntu-latest
    needs: [build] <em># Declare dependency on the build job</em>
    steps:
      - name: Use BUILD_STAGE from previous job
        run: |
          # Access the variable using the needs context
          STAGE_VAR="${{ needs.build.outputs.build_stage_output }}"
          echo "The build stage from the previous job is: $STAGE_VAR"

2. Use Artifacts (Recommended for files or large data) 

If the data is large or a file (e.g., a build artifact or a configuration file), you can upload it in the first job and download it in the second job using the actions/upload-artifact and actions/download-artifact actions. 

Yes, you can use artifacts to pass data (in the form of files) between different jobs. This is the standard and correct method for cross-job data sharing in Gitea Actions. 

Here is an example demonstrating how to create a file containing the BUILD_STAGE in the first job, upload it as an artifact, and then download and read it in the second job.

Workflow Example using Artifacts

name: Conditional Build Stage with Artifacts

on: [push]

jobs:
  <em># --- Job 1: Build & Upload Artifact ---</em>
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Determine and save BUILD_STAGE to a file
        id: set_stage_file
        run: |
          COMMIT_MSG="${{ github.event.head_commit.message }}"
          BUILD_STAGE="DEV" # Default value

          if [[ "$COMMIT_MSG" == *"#ready-for-qa"* ]]; then
            BUILD_STAGE="QA"
          elif [[ "$COMMIT_MSG" == *"#ready-for-prod"* ]]; then
            BUILD_STAGE="PROD"
          fi
          
          # Create a directory and save the variable to a file
          mkdir -p ./artifact_output
          echo "$BUILD_STAGE" > ./artifact_output/build_stage.txt
          echo "Determined stage: $BUILD_STAGE"
        shell: bash

      - name: Upload BUILD_STAGE artifact
        uses: actions/upload-artifact@v4 <em># Use the upload-artifact action</em>
        with:
          name: build-stage-artifact <em># Name for the artifact</em>
          path: ./artifact_output/build_stage.txt <em># The file path to upload</em>


  <em># --- Job 2: Download & Use Artifact ---</em>
  deploy:
    runs-on: ubuntu-latest
    needs: [build] <em># This job MUST wait for the 'build' job to complete</em>
    steps:
      - name: Download BUILD_STAGE artifact
        uses: actions/download-artifact@v4 <em># Use the download-artifact action</em>
        with:
          name: build-stage-artifact <em># Must match the name used in the upload job</em>
          path: ./downloaded_artifact <em># Directory where the file will be downloaded</em>

      - name: Read the BUILD_STAGE from the file
        run: |
          # The file will be in the specified path from the download step
          STAGE_VALUE=$(cat ./downloaded_artifact/build_stage.txt)
          echo "The build stage passed from the previous job is: $STAGE_VALUE"
          
          # You can now use $STAGE_VALUE in subsequent commands within this step/job
          if [ "$STAGE_VALUE" == "PROD" ]; then
            echo "Deploying to Production environment..."
            # Add your production deployment logic here
          fi
        shell: bash

In a Gitea Actions workflow file (which is the Gitea equivalent of a deploy.yaml for actions), the env keyword can be placed at three different levels to define environment variables: 

  1. Workflow level: Defined at the top level of the YAML file, the environment variables are available to all jobs and steps within the workflow.
  2. Job level: Defined within a specific job, the variables are available to all steps within that job.
  3. Step level: Defined within a specific step, the variables are only available to that particular step. 

Example Syntax

Here is an example demonstrating all three locations:

name: Example Workflow
<em># Workflow-level env</em>
env:
  WORKFLOW_VAR: "This is a workflow variable"

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    <em># Job-level env</em>
    env:
      JOB_VAR: "This is a job variable"
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Use environment variables
        <em># Step-level env</em>
        env:
          STEP_VAR: "This is a step variable"
        run: |
          echo "Workflow var: ${{ env.WORKFLOW_VAR }}"
          echo "Job var: ${{ env.JOB_VAR }}"
          echo "Step var: ${{ env.STEP_VAR }}"

Key Points

  • The env keyword must be followed by a colon (:) and then indented key-value pairs for the variable names and their values.
  • Variables are accessed using the ${{ env.VARIABLE_NAME }} syntax within the workflow file itself, or using the standard shell syntax (e.g., $VARIABLE_NAME in bash, $env:VARIABLE_NAME in PowerShell) within the run step scripts.
  • Job-level variables take precedence over workflow-level variables if they have the same name, and step-level variables have the highest precedence.
  • For sensitive information, you should use Gitea Secrets instead of environment variables in the YAML file. Secrets are also accessed via a context, like ${{ secrets.MY_SECRET }}

The scope of env: defined directly under a - name: block (meaning at the step level) is strictly limited to that specific step’s execution environment [1].

Here’s exactly what that means:

  • Available within the step: Any environment variables defined here can be accessed by the run script or the uses action for that single step.
  • Not available in subsequent steps: As soon as that step finishes, those specific environment variables are removed from the environment and cannot be accessed by the next step in the job.
  • Overrides higher levels: If a variable with the same name was defined at the job or workflow level, the step-level definition takes precedence for the duration of that single step’s execution [1].

Example of Step-Level Scope

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Step 1 with specific env
        env:
          TEMP_VAR: "I am only here for Step 1"
        run: |
          echo "Inside Step 1: $TEMP_VAR" # This works

      - name: Step 2
        run: |
          echo "Inside Step 2: $TEMP_VAR" # This will output nothing (or an empty string)

If you need to make data generated or defined in one step available to subsequent steps,
you should use output parameters or environment files (using 
echo "::set-env name=VAR_NAME::value" in older runners
or 
echo "VAR_NAME=value" >> $GITHUB_ENV in newer runners/Gitea actions) instead of a simple env: block.

To make an env: variable available to all steps,
you can define it at either the workflow level or the job level

1. Define at the Workflow Level (Recommended for all steps in all jobs)

Place the env: block at the top level of your YAML file, directly under the name: or on: keywords. Variables defined here are available to every single job and step in the entire workflow. 

name: Example Workflow

env:
  <em># This variable is available to all jobs and all steps</em>
  GLOBAL_VAR: "I am everywhere"
  ANOTHER_VAR: "Hello World"

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Step 1 can use global var
        run: echo "Step 1: $GLOBAL_VAR"

      - name: Step 2 can also use global var
        run: echo "Step 2: $GLOBAL_VAR"

2. Define at the Job Level (Recommended for all steps within a specific job)

Place the env: block directly under a specific job ID (e.g., under build:).
Variables defined here are available to all steps within that specific job, but not to other jobs in the workflow. 

name: Example Workflow

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    <em># This variable is available to all steps within the 'build' job</em>
    env:
      JOB_VAR: "I am only in the build job"
    steps:
      - name: Step 1 in build job
        run: echo "Step 1: $JOB_VAR"

      - name: Step 2 in build job
        run: echo "Step 2: $JOB_VAR"

  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Step 1 in deploy job
        <em># This will fail or be empty because JOB_VAR is not in scope</em>
        run: echo "Step 1: $JOB_VAR"

Accessing the Variables

Within a run command using a shell (like bash on Linux/macOS or PowerShell on Windows),
you can typically access the variables using the standard shell syntax (e.g., $GLOBAL_VAR or %GLOBAL_VAR%).
For use outside of a run command (e.g., in conditional statements or other parts of the YAML structure),
use the expression syntax ${{ env.VARIABLE_NAME }}

Both $GITHUB_OUTPUT and $GITHUB_ENV are valid and serve distinct purposes in GitHub Actions workflows.

$GITHUB_ENV:

  • Used to set environment variables that are available to subsequent steps within the same job.
  • Changes made to $GITHUB_ENV in one step will not be accessible in other jobs in the workflow.
  • Example: echo "MY_VARIABLE=my_value" >> $GITHUB_ENV

$GITHUB_OUTPUT:

  • Used to set job outputs, which allow you to pass data between different jobs within the same workflow.
  • To use $GITHUB_OUTPUT, the step generating the output must have an id, and the job must define the output in its outputs block.
  • The consuming job must use the needs keyword to depend on the job that produces the output.
  • Example: echo "my_output_name=output_value" >> $GITHUB_OUTPUT

In summary:

  • Use $GITHUB_ENV when you need to share data between steps within a single job.
  • Use $GITHUB_OUTPUT when you need to share data between different jobs in a workflow.

Leave a Reply