Why Create a Custom Action ?

  • Boost Efficiency: One of the key advantages is increased efficiency. Consider the scenario where you need to modify deployment workflows or add a new feature to multiple workflows, such as sending notifications after deployment. By creating your own GitHub Action, you only need to make the changes in one place, update the action version, and you’re done.
    fdsfasd
  • Version Control: Custom actions allow you to ensure that critical workflows run on a stable version, while also providing flexibility to experiment with new features without disrupting existing workflows.

Types of Custom Actions

There are several types of custom actions you can create:

  • Docker: Action code packaged in a Docker container image.
  • JavaScript: Action code packaged in a JavaScript file.
  • Composite: Packages a series of actions into a single reusable unit.

On this course we will focus on Composite actions, as Docker and JavaScript actions are mostly used for more complex and specific tasks. Also, I want to highlight that we can substitute big workflows with a single composite action, simplifying your workflows and enhancing readability.

Creating a Composite Action

The composite action is defined in a YAML file named action.yaml in the root directory of your repository. The file must have the following structure:

  • name: Name of the action.
  • description: Description of the action.
  • inputs: Defines the action’s inputs, which can be required, optional, or have default values.
  • outputs: Defines the action’s outputs, which can be utilized in subsequent steps of the workflow using the action.
  • steps: Contains a sequence of steps of the action.
    • uses Allows you to reference an existing action.
    • shell: Specifies the script type (bash, pwsh, python, etc.).
    • run Defines the command to execute.

Example:

name: Basic Composite Action
description: 'A basic composite action'

inputs:
  name:
    description: 'Your name'
    required: true
    default: 'Joan'

outputs:
  greeting:
    description: 'The generated greeting'
    value: ${{ steps.generate-greeting.outputs.greeting }}

runs:
  using: 'composite'
  steps:
    - name: Step 1
      id: generate-greeting
      shell: bash
      run: |
        echo "Hello, ${{ inputs.name }}!"
        echo "::set-output name=greeting::Hello, ${{ inputs.name }}!"        

Versioning

Versioning plays a critical role in effectively managing the development and deployment of GitHub Actions. By utilizing version numbers, you can clearly track changes made to your actions and ensure compatibility with existing workflows.

GitHub Actions employ a combination of major and minor version numbers. Major versions (e.g., v1, v2, v3) are typically used for stable releases in production environments. Minor versions (e.g., v1.1, v1.2) are employed during active development for testing and iteration, without disrupting production workflows.

Procedure

  1. After committing changes, retrieve the latest tag using the following commands:
git fetch
# Search for the last minor version using the pattern "*.*"
git describe --tags --match "*.*" `git rev-list --tags --max-count=1`
  1. Increment the last minor version to create a new tag. For example, if the last minor version is v1.9, execute:
git tag v1.10 -m "Bump minor version"
  1. If the new version is stable and ready for production, create or force update the major tag to point to the latest commit:
git tag -fa v1 -m "Update major tag"
  1. Push the new tags, ensuring the major version tag is force-pushed to overwrite any existing tags:
git push origin v1.10
git push -f origin v1

Hands-On

In this section, we will guide you through the process of creating a new GitHub Action and versioning it.

STEP 1 - Create a New Action

1. Create a new repository on GitHub.

2. Inside the repository, create a file named action.yaml with the following content:

name: Basic Composite Action
description: 'A basic composite action'

inputs:
  name:
    description: 'Your name'
    required: true
    default: 'Joan'

outputs:
  greeting:
    description: 'The generated greeting'
    value: ${{ steps.generate-greeting.outputs.greeting }}

runs:
  using: 'composite'
  steps:
    - name: Step 1
      id: generate-greeting
      shell: bash
      run: |
        echo "Hello, ${{ inputs.name }}!"
        echo "::set-output name=greeting::Hello, ${{ inputs.name }}!"        

3. Commit and tag it as version v0.1:

git add .
git commit -m "feat: First version of the greeting action."
git tag v0.1
git push origin main v0.1

STEP 2 - Create a Workflow Using Minor Version v0.1 of the Action

1. Inside your repository, create a file named test-action-v0.1.yaml inside .github/workflows/ folder with the following content:

name: Test Action minor version v0.1

on:
  push:
    branches:
      - main

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      
      - name: Test v0.1 minor version - Greeting
        id: greeting
        uses: joanplaja/action-template@v0.1
        with:
          name: "Pau Lozano"

      - name: Show greeting
        run: echo ${{steps.greeting.outputs.greeting}}

2. Commit and push the changes to the repository.

git add .
git commit -m "feat: Adding a workflow to test action version v0.1"
git push origin main

3. Check the results on the Actions tab of your repository.

STEP 3 - Update the Action and Align the Major Version

1. Update the action.yaml contents with the following:

name: Dummy deploy
description: 'This is not real deploy'

inputs:
  app_name:
    description: 'Application name'
    required: true
  environment:
    description: 'Target environment for deployment'
    required: true

runs:
  using: 'composite'
  steps:
    - name: Build the application
      id: build
      shell: bash
      run: echo "Building application ${{ inputs.app_name }}"

    - name: Deploy application
      id: deploy
      shell: bash
      run: |
        echo "Deploying application to ${{ inputs.targetEnvironment }}"
        echo "::set-output name=deployStatus::success"        

    - name: Notify success
      id: notify_success
      if: steps.deploy.outputs.deployStatus == 'success'
      shell: bash
      run: echo "Notify the new deployment"
    
    - name: Notify error
      id: notify_error
      if: steps.deploy.outputs.deployStatus != 'success'
      shell: bash
      run: echo "Notify the team about the error"

2. Commit and tag it to the v0.2 version:

git add .
git commit -m "feat: New version of the action, now it deploys a dummy environment."
git tag v0.2
git push origin v0.2
git tag -fa v0 -m "Update major tag"
git push -f origin v0

STEP 4 - Create a Workflow Using Major Version v0 of the Action

Inside your repository, create a file named test-action-v0.yaml inside .github/workflows/ folder with the following content:

name: Test Action major version v0

on:
  push:
    branches:
      - main

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      
      - name: Test v0 major version - Dummy deploy
        uses: joanplaja/action-template@v0
        with:
          app_name: Backend
          environment: test
  1. Commit and push the changes to the repository.
git add .
git commit -m "feat: Adding a workflow to test action version v0"
git push origin main
  1. Check the results on the Actions tab of your repository.

Template repository

You can find all the hands-on project, which can be used as a template for creating any custom action, on the following repository.

Image by storyset on Freepik