Mastering GitHub Actions: Creating Reusable Workflows

In modern DevOps practices, the "Don't Repeat Yourself" (DRY) principle is essential for maintaining clean and efficient CI/CD pipelines. As your project grows, you might find yourself copying and pasting the same job configurations across multiple repositories or workflow files. GitHub Actions provides a powerful feature called Reusable Workflows to solve this exact problem.

Reusable workflows allow you to define a standardized process once and call it from multiple other workflows. This reduces maintenance overhead, ensures consistency across teams, and simplifies the management of complex CI/CD logic.

Understanding the Architecture

In a reusable workflow setup, there are two primary components:

  • The Reusable (Called) Workflow: The template file that contains the jobs and logic.
  • The Caller Workflow: The workflow that triggers or "calls" the reusable template.
[ Caller Workflow ] 
      |
      |-- triggers on: push/pull_request
      |
      |-- calls: .github/workflows/reusable-build.yml
            |
            V
    [ Reusable Workflow ]
      |-- runs-on: ubuntu-latest
      |-- steps: [ Checkout, Setup Java, Build ]
    

The workflow_call Trigger

To make a workflow reusable, you must use the workflow_call trigger in the on section of your YAML file. This tells GitHub that this specific workflow is designed to be invoked by other workflows.

Defining a Reusable Workflow

Here is an example of a reusable workflow designed to build a Java application using Maven. This file would typically be stored in .github/workflows/maven-build.yml.

name: Reusable Maven Build

on:
  workflow_call:
    inputs:
      java-version:
        required: true
        type: string
      maven-command:
        required: false
        type: string
        default: 'clean install'
    secrets:
      SONAR_TOKEN:
        required: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          java-version: ${{ inputs.java-version }}
          distribution: 'temurin'
      - name: Build with Maven
        run: mvn ${{ inputs.maven-command }}
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
    

How to Call a Reusable Workflow

Once you have defined your template, you can call it from a Caller Workflow. The caller workflow uses the uses keyword at the job level rather than the step level.

name: Main CI Pipeline

on:
  push:
    branches: [ main ]

jobs:
  call-build-workflow:
    uses: ./.github/workflows/maven-build.yml
    with:
      java-version: '17'
      maven-command: 'clean package'
    secrets:
      SONAR_TOKEN: ${{ secrets.GLOBAL_SONAR_TOKEN }}
    

Important Path Rules

  • Local Files: Use ./.github/workflows/filename.yml if the reusable workflow is in the same repository.
  • Remote Files: Use {owner}/{repo}/.github/workflows/{filename}.yml@{ref} to call a workflow from another repository (e.g., my-org/shared-actions/.github/workflows/build.yml@v1).

Passing Inputs and Secrets

Reusable workflows can accept inputs and secrets to make them dynamic.

  • Inputs: Used for non-sensitive data like version numbers, environment names, or boolean flags. Defined under inputs in the workflow_call section.
  • Secrets: Used for sensitive data like API keys or database passwords. These must be explicitly passed from the caller to the reusable workflow using the secrets keyword.
  • Inheriting Secrets: You can use secrets: inherit in the caller workflow to automatically pass all secrets to the reusable workflow without listing them individually.

Real-World Use Cases

Reusable workflows are highly beneficial in the following scenarios:

  • Standardized Java Builds: Ensure every microservice in your organization uses the same version of Maven, Java, and security scanning tools.
  • Multi-Environment Deployment: Create one deployment workflow and call it multiple times with different inputs for "Staging", "QA", and "Production".
  • Compliance and Security: Force all repositories to run a specific security audit workflow before merging code.

Common Mistakes to Avoid

  • Nesting Limits: GitHub allows you to nest reusable workflows up to 4 levels deep. Exceeding this will cause an error.
  • Environment Context: Remember that env variables defined at the top level of a caller workflow are not automatically available in the reusable workflow. You must pass them as inputs.
  • Checkout Step: You must still include the actions/checkout step inside the reusable workflow if you need access to the repository's code.
  • Permissions: If the reusable workflow is in a different repository, ensure the repository settings allow access to "Internal" or "Public" workflows.

Interview Notes for DevOps Engineers

  • Question: What is the difference between a Composite Action and a Reusable Workflow?
  • Answer: Composite Actions are for grouping multiple steps into one action, while Reusable Workflows allow you to reuse entire jobs and can be seen in the GitHub UI as separate workflow runs. Reusable workflows also support secrets more natively.
  • Question: How do you handle secrets when calling a workflow from another repository?
  • Answer: You can pass them individually using the secrets block or use secrets: inherit if both repositories belong to the same organization and have the appropriate access.
  • Question: Can a reusable workflow contain multiple jobs?
  • Answer: Yes, a reusable workflow can define multiple jobs, and they will all be executed when called by the caller workflow.

Summary

Creating reusable workflows is a milestone in mastering GitHub Actions. By leveraging the workflow_call trigger, you can build a library of standardized CI/CD templates that promote consistency and ease of maintenance. Whether you are managing a single large project or hundreds of microservices, reusability is the key to scaling your automation effectively.

In the next lesson, we will explore Managing Environments and Deployment Gates to add manual approvals to our reusable pipelines.