Photo by Johannes Plenio

Reuse your actions and avoid duplication in your project using GitHub Actions

Capitalize on your workflows

Posted by Damien Aicheh on 05/23/2022 · 7 mins

When you set up your continuous integration and continious delivery you always have multiples environments to target. For instance:

  • dev
  • staging
  • production

Your application will be deployed in each environment with the same scripts. The only thing that change are the workflows parameters. So you will probably end up with duplication. So how to mutualize your actions to save some time?

Use case

You have a web application that you build and then deploy to each environment. Your GitHub workflow located at .github/workflows/main.yml will look like this:

First, you build the project and package it to produce an artifact:

name: Reusable templates

on:
  push:
    branches: [main]

jobs:

build_and_package:
  steps:
    - name: checkout branch
      uses: actions/checkout@v2
    
    - run: echo Your build commands here
      shell: bash

    - run: echo Then your package commands here
      shell: bash

Next, you put all the jobs to deploy your application after:


deploy_dev:
  runs-on: ubuntu-latest
  name: Deploy to dev
  needs: build_and_package

  steps:
    - name: Checkout the code
      uses: actions/checkout@v2

    - run: |
        echo Run on env : Dev
      shell: bash

    # Other steps here...

    - run: |
        echo Add your actions to deploy to the correct environment using ${{ secrets.DEV_DEPLOYMENT_TOKEN }}
      shell: bash

deploy_staging:
  runs-on: ubuntu-latest
  name: Deploy to staging
  needs: deploy_staging

  steps:
    - name: Checkout the code
      uses: actions/checkout@v2

    - run: |
        echo Run on env : Staging
      shell: bash

    # Other steps here...

    - run: |
        echo Add your actions to deploy to the correct environment using ${{ secrets.STAGING_DEPLOYMENT_TOKEN }}
      shell: bash

# Same process for production, eluded for clarity

As you can see, we copy paste the job and replace the secrets to match the specific environment. How can we improve this?

Create local composites

To avoid duplication of the deployment jobs we can use the GitHub Action concept called composite. The idea is that you can create generic part of your workflow so that you can save time and thus avoid errors.

So let’s create one for the deployment section. In our workflows let’s create a folder called templates and inside it another one called deploy_to_cloud. Then inside this last folder let’s add a file called action.yml.

The name of the file is important if you don’t specify it like this your composite will not be found. That’s why it’s important to put this file in a folder with an explicite name.

So you will have this file tree:

|- .github
    |- workflows
        |- templates
            |- deploy_to_cloud
                |- action.yml
        |- main.yml

Here is our action.yml file:


name: "Deploy to the cloud"
description: "Reusable actions to deploy to the cloud"

inputs:
  name:
    description: 'Name'
    required: true
  deployment-token:
    description: 'Deployment token'
    required: true

runs:
  using: "composite"
  steps: 
    - run: |
        echo Run on env : ${{ inputs.name }}
      shell: bash

    # Other steps here...

    - run: |
        echo Add your actions to deploy to the correct environment using ${{ inputs.deployment-token }}
      shell: bash

As you can see we have two inputs variables, one for the name of the environment and one for a deployment token. Then we define this file as a composite before defining the different steps. Finally, we put the exact same actions but we use the inputs variables so it is totally generic.

Apply the composite

Now, it’s time to apply the composite to the workflow!


deploy_dev:
  runs-on: ubuntu-latest
  name: Deploy to staging
  needs: build_and_package
  steps:
    - name: Checkout the code
      uses: actions/checkout@v2

    - name: Deploy to cloud
      uses: ./.github/workflows/templates/deploy_to_cloud
      with:
        name: 'Dev'
        deployment-token: ${{ secrets.DEV_DEPLOYMENT_TOKEN }}

deploy_staging:
      
  runs-on: ubuntu-latest
  name: Deploy to staging
  needs: deploy_dev
  
  steps:
    - name: Checkout the code
      uses: actions/checkout@v2

    - name: Deploy to cloud
      uses: ./.github/workflows/templates/deploy_to_cloud
      with:
        name: 'Staging'
        deployment-token: ${{ secrets.STAGING_DEPLOYMENT_TOKEN }}

# Same process for production, eluded for clarity

As you can see above, the first important step we do is actions/checkout@v2 to be able to access this composite and then we can use it directly with the specific environment values as parameters.

Final touch

With this technique you can now capitalize on your project and start creating your own bank of templates for all your projects. To see more, checkout my previous tutorial about it.

Happy coding !

Do not hesitate to follow me on to not miss my next tutorial!