Back to Blog
Lesson 30 of the CI/CD: Continuous Integration from Scratch course
DevOpsAugust 5, 20263 min read

Custom Actions: Mastering Reusability in GitHub Actions

Stop repeating your CI/CD configuration. Learn how to create custom actions to encapsulate complex logic and keep your GitHub Actions workflows DRY.

GitHub ActionsCI/CDDevOpsAutomationInfrastructure as Code
Large bales of recycled cardboard stacked outdoors, ready for processing.

Previously in this course, we explored Conditional Execution: Mastering Logic in GitHub Actions. While if statements handle branching logic, they don't solve the problem of copy-pasting the same multi-step setup across multiple workflow files. Today, we’re moving from simple scripting to architectural reuse by building custom actions.

Why Custom Actions?

As your pipeline grows, you’ll find yourself repeating the same blocks of code: installing specific dependencies, configuring authentication, or setting up specialized build environments. If you need to change one parameter, you have to find and update every single workflow file.

Custom actions allow you to abstract these blocks into a single unit. This adheres to the DRY (Don't Repeat Yourself) principle, making your CI/CD configuration easier to maintain, test, and share across your organization.

Understanding Composite Actions

There are three types of GitHub Actions: Docker container actions, JavaScript actions, and composite actions.

For beginners, composite actions are the most accessible. They allow you to bundle multiple run steps into a single action using the same YAML syntax you already know from Workflow Syntax Deep Dive.

Worked Example: Creating a Setup Action

Let's imagine your project frequently needs to install a specific set of tools. Instead of writing the same run commands in every job, we will create a local action.

  1. Create the directory structure: Inside your repository, create a directory for your actions:

    Bash
    mkdir -p .github/actions/setup-environment
  2. Define the action metadata (action.yml): Every action requires an action.yml file to define its inputs and execution steps. Create .github/actions/setup-environment/action.yml:

    YAML
    name: 'Setup My Environment'
    description: 'Installs custom tools and configures environment'
    runs:
      using: "composite"
      steps:
        - name: Install dependencies
          run: echo "Installing custom tools..." && sleep 2
          shell: bash
        - name: Configure secrets
          run: echo "Configuring environment variables..."
          shell: bash
  3. Call the action in your workflow: Now, in your primary workflow file (e.g., .github/workflows/ci.yml), you can reference this local action using the uses keyword:

    YAML
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - name: Run my custom setup
            uses: ./.github/actions/setup-environment

Hands-on Exercise

Refactor your current repository pipeline. Take a set of at least two steps that you currently repeat in your workflows (like dependency installation or environment configuration) and move them into a new file at .github/actions/my-reusable-step/action.yml. Update your primary workflow to call this action instead of the original steps. Verify that your pipeline still passes by pushing your changes.

Common Pitfalls

  • Ignoring the shell keyword: Unlike standard job steps, every run step inside a composite action must explicitly define a shell (e.g., shell: bash). If you omit this, the action will fail.
  • Hardcoding values: If your action needs to behave differently depending on the context, use inputs in your action.yml to make it configurable rather than hardcoding values.
  • Path confusion: Remember that local actions are referenced relative to the repository root. If your action is in .github/actions/my-action, you must use ./.github/actions/my-action in your uses statement.

FAQ

Q: Can I share these actions with other repositories? A: Yes. While we built a local action today, you can move this folder to its own repository. Once public (or internal/private with appropriate permissions), you would change the uses syntax to point to that repository, like uses: my-org/my-action@v1.

Q: Are custom actions slower? A: No, composite actions are essentially expanded into your workflow at runtime. They add no measurable overhead compared to writing the steps manually.

Recap

Custom actions are the primary tool for maintaining DRY workflows in GitHub Actions. By bundling repetitive logic into composite actions, you centralize your configuration, reduce human error, and simplify your CI/CD maintenance. Remember that well-defined actions are the foundation of scaling infrastructure as code.

Up next: We will implement automated alerts by adding a notification step to our pipeline when a build fails.

Similar Posts