GitHub Actions

Reusable Workflows and Reporting

Step 4 Using Outputs in Reusable Workflow

In this guide, you’ll learn how to expose and consume outputs from a reusable GitHub Actions workflow. By defining outputs under workflow_call and mapping job outputs, downstream jobs can reference values—such as your application’s ingress URL—without extra scripting.


Background

Our dev-integration-testing job was failing because it tried to read an application URL that wasn’t exposed by the reusable workflow. Although dev-integration-testing depends on dev-deploy, by default reusable-workflow outputs are not forwarded to the caller. We’ll fix this by:

  1. Declaring outputs in the reusable workflow.
  2. Mapping those outputs from a job step.
  3. Consuming the mapped outputs in the caller workflow.

Warning

If you don’t define outputs in workflow_call, any values you set inside the workflow won’t be available to the caller.


1. Defining Outputs in Your Reusable Workflow

First, update your reusable workflow (reuse-deployment.yml) to include an outputs block under on.workflow_call. Then map job-level outputs to that workflow output:

name: Deployment - Reusable Workflow

on:
  workflow_call:
    inputs:
      environment:
        description: "Deployment environment"
        required: true
        type: string
      kubectl-version:
        description: "kubectl CLI version"
        required: false
        type: string
        default: "latest"
      mongodb-uri:
        description: "MongoDB connection URI"
        required: true
        type: string
    secrets:
      k8s-kubeconfig:
        required: true
      mongodb-password:
        required: true
    outputs:
      application-url:
        description: "URL for the deployed application"
        value: ${{ jobs.reuse-deploy.outputs.APP_INGRESS_URL }}

jobs:
  reuse-deploy:
    runs-on: ubuntu-latest
    environment:
      name: ${{ inputs.environment }}
      url: https://${{ steps.set-ingress-host.outputs.APP_INGRESS_HOST }}
    outputs:
      APP_INGRESS_URL: ${{ steps.set-ingress-host.outputs.APP_INGRESS_HOST }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install kubectl CLI
        uses: azure/setup-kubectl@v3
        with:
          version: ${{ inputs.kubectl-version }}

      - name: Set Kubeconfig context
        uses: azure/k8s-set-context@v3
        with:
          kubeconfig: ${{ secrets.k8s-kubeconfig }}

      - name: Obtain ingress host
        id: set-ingress-host
        run: |
          # Simulate fetching the ingress host
          echo "::set-output name=APP_INGRESS_HOST::my-app.${{ inputs.environment }}.example.com"

Inputs and Secrets Reference

InputDescriptionTypeRequiredDefault
environmentDeployment environmentstringtrue
kubectl-versionkubectl CLI versionstringfalselatest
mongodb-uriMongoDB connection URIstringtrue
SecretDescriptionRequired
k8s-kubeconfigKubernetes cluster authenticationtrue
mongodb-passwordPassword for MongoDBtrue

Note

We use ${{ jobs.reuse-deploy.outputs.APP_INGRESS_URL }} to map the job’s output to the workflow’s application-url.


2. Original Caller Workflow (Before)

Here’s how the caller workflow referenced APP_INGRESS_URL before the change:

jobs:
  dev-deploy:
    needs: docker
    if: contains(github.ref, 'feature/')
    uses: ./.github/workflows/reuse-deployment.yml
    with:
      mongodb-uri: ${{ vars.MONGO_URI }}
      environment: development
      k8s-manifest-dir: kubernetes/development/
    secrets:
      k8s-kubeconfig: ${{ secrets.KUBECONFIG }}
      mongodb-password: ${{ secrets.MONGO_PASSWORD }}

  dev-integration-testing:
    name: Dev Integration Testing
    needs: dev-deploy
    if: contains(github.ref, 'feature/')
    runs-on: ubuntu-latest
    steps:
      - name: Test URL Output using curl and jq
        env:
          URL: ${{ needs.dev-deploy.outputs.APP_INGRESS_URL }}  # Not defined
        run: |
          echo $URL
          curl https://$URL/live -s -k | jq -r .status | grep -i live

Since APP_INGRESS_URL wasn’t declared in the reusable workflow’s outputs, dev-integration-testing failed.


3. Consuming Outputs in the Caller Workflow

Update your caller workflow to use the new application-url output:

jobs:
  dev-deploy:
    needs: docker
    if: contains(github.ref, 'feature/')
    uses: ./.github/workflows/reuse-deployment.yml
    with:
      mongodb-uri: ${{ vars.MONGO_URI }}
      environment: development
      k8s-manifest-dir: kubernetes/development
    secrets:
      k8s-kubeconfig: ${{ secrets.KUBECONFIG }}
      mongodb-password: ${{ secrets.MONGO_PASSWORD }}

  dev-integration-testing:
    name: Dev Integration Testing
    needs: dev-deploy
    if: contains(github.ref, 'feature/')
    runs-on: ubuntu-latest
    steps:
      - name: Test URL Output using curl and jq
        env:
          URL: ${{ needs.dev-deploy.outputs.application-url }}
        run: |
          echo "Application URL: $URL"
          curl https://$URL/live -s -k | jq -r .status | grep -i live

  prod-deploy:
    needs: docker
    if: github.ref == 'refs/heads/main'
    uses: ./.github/workflows/reuse-deployment.yml
    with:
      mongodb-uri: ${{ vars.MONGO_URI }}
      environment: production
      k8s-manifest-dir: kubernetes/production
    secrets:
      k8s-kubeconfig: ${{ secrets.KUBECONFIG }}
      mongodb-password: ${{ secrets.MONGO_PASSWORD }}

  prod-integration-testing:
    name: Prod Integration Testing
    needs: prod-deploy
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - name: Test URL Output using curl and jq
        env:
          URL: ${{ needs.prod-deploy.outputs.application-url }}
        run: |
          echo "Application URL: $URL"
          curl https://$URL/live -s | jq -r .status | grep -i live

4. Testing the Changes

Once you commit these updates:

echo $URL
echo "-----------------------------"
curl https://$URL/live -s -k | jq -r .status | grep -i live

Your logs should resemble:

Application URL: my-app.development.example.com
Status: live

Now both integration-testing jobs will successfully read the ingress URL from the reusable workflow.


Watch Video

Watch video content

Practice Lab

Practice lab

Previous
Step 3 Using Inputs in Reusable Workflow