Skip to content

Some Interesting Features In GitHub Actions

Posted on:December 27, 2022 at 02:13 AM

Github Actions

Intro

GitHub Actions, in my opinion changed the landscape of Continuous Integration by not only having a CI close to the code (for ones using GitHub) but also allowed very close interactions with the Git Tagging and GitHub Releases which also allow uploading assets that might be related to the release. Also it really helped that they allow free usage of actions for public repositories that help automate many many tasks that free up the really important time of the contributors who already are doing this in addition to their day job.
This coupled with some core actions that take care of basic needs proliferated the usage and creation of multitude of actions that allow doing all kinds of things that you need and almost any new service also keeps the Use Case in mind.

So it’s not like all development there is done and there’s nothing new coming in to GitHub Actions anymore, on the contrary it seems there are features that solve many problems elegantly are being added all the time! This post is about a few such features that I wanted and now do have included in Github Actions.

Also, I have to clarify that (and search on why that is so) the actions are really actions that are performed as a step in a Job and One or more Jobs make a workflow. So You use actions (either created by yourself or others) in steps and write jobs that run (in parallel, sequentially or using different architectures) that are run as part of workflow that is triggered by events.

Table Of Contents

Open Table Of Contents

1. Select Box and Boolean Inputs For Workflow Dispatch (Github Actions Speak for Manual Trigger)

on:
  workflow_dispatch:
    inputs:
      logLevel:
        description: "Log level"
        required: true
        default: "warning"
        type: choice
        options:
          - info
          - warning
          - debug
      tags:
        description: "Test scenario tags"
        required: false
        type: boolean
      environment:
        description: "Environment to run tests against"
        type: environment
        required: true

jobs:
  log-the-inputs:
    runs-on: ubuntu-latest
    steps:
      - run: |
          echo "Log level: $LEVEL"
          echo "Tags: $TAGS"
          echo "Environment: $ENVIRONMENT"
        env:
          LEVEL: ${{ inputs.logLevel }}
          TAGS: ${{ inputs.tags }}
          ENVIRONMENT: ${{ inputs.environment }}

If You now try to run the workflow you should see something like below:

Workflow Dispatch

The Example and The Image are Straight out of the GitHub Actions Documentation.
What is Interesting is
i. The Log Level input is restricted to ‘info’, ‘warning’ and ‘debug’ anything else is just not possible from the webUI and fails to start when started from the cli.
i. The environment is one of ‘production’, ‘staging’ and ‘development’ as per the official documentation. So this is a bit special in that the options are not directly visible and can be created if need be, these are the default ones available.

2. That brings up the next interesting feature, Environments

When we use environments for deployments (it is not really deploying anything without us writing those steps, just creating an additional scope) we can not only use it as a target as seen above but also, have some waits / cool down before the job is run, separate secrets with the same name, so depending upon the environment selected different values are “injected / used” in the workflow and of course combined with protected branches you can also have forced approvals not only before merging to the main branch but also before running a workflow e.g. to production.

Check Reviewing Deployments for more information.

3. Writing Your Own Actions

For Some time now there was a way to write your own action and have anyone (if public) to use it and even have it listed and searchable on the Actions Marketplace and a Dockerfile action and they are great when one wants to do that but are quite involved (at least the JavaScript ones are for me) but what does one do when there are things that one might (or might not) want to publish and might (or might not) want to keep in the same repository using the action. Which is usually the case when working with a company and probably a private repo? That was a long question but has a not quite long answer, composite actions.

Github Composite Action

The full code is here in this repository : composite-actions-example

Here we have a file at .github/actions/setup-asdf/action.yaml that holds multiple steps that we need to run, and this is essentially a composite action. Any Number of workflows can refer to it by simply adding
uses: ./.github/actions/setup-asdf as a step in the workflow. Remember though, this action is not public if the repository is private and then can only be used by workflows within that repository.

We are also not limited to some default values, if need be we can have inputs and outputs.

name: 'Hello Input Output'
description: 'Greet someone'
inputs:
  who-to-greet:  # id of input
    description: 'Who to greet'
    required: true
    default: 'World'
outputs:
  random-number:
    description: "Random number"
    value: ${{ steps.random-number-generator.outputs.random-number }}
runs:
  using: "composite"
  steps:
    - run: echo Hello ${{ inputs.who-to-greet }}.
      shell: bash
    - id: random-number-generator
      run: echo "random-number=$(echo $RANDOM)" >> $GITHUB_OUTPUT
      shell: bash

Here When you use this composite action in a workflow (assuming it is saved in a directory under .github/actions directory named hello-input-output)

name: composite-input-output-test-example
on:
  workflow_dispatch:

jobs:
  composite-input-output-test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - id: capturing-step-id
        uses: ./.github/actions/hello-input-output
        with:
          who-to-greet: 'Santa'
      - name: 'Show The Output'
        run: echo ${{ steps.capturing-step-id.outputs.random-number }}

The Output should be something like below

Composite Input Output Test

There are limitations to Composite Actions, like not being able to have access to secrets that limit the usability in some cases, as any passed in variable is not masked and not subjected to the same strict secret guidelines.

4. Output Masks and Log Grouping

As they say (though this is not a movie) show don’t tell.

The code can be found here. There are other workflow commands as they are called that might also be of interest :)

5. Creating a reusable workflow

At my current place of employment, we are having a monorepo, that means more than one project in one git repository. This brings with it some challenges, it’s difficult to know which directory (which means project) has the changed files? You say easy!!! use the paths filter right?

on:
  push:
    paths:
      - 'sub-project/**'
      - '!sub-project/docs/**'

Wrong! Then you have 2 choices,

  1. List all Sub Projects in the paths and figure out in the workflow somehow the name of the changed project.
    Here You Basically loose out on all the context information just because you want to have a single workflow
    You have to do some shell / git / some other magic to figure out the name (or names!) of changed projects.
  2. Create Separate Workflows for each sub-project that only filters for that specific directory.
    Here You Duplicate all the code just to get the benefit of directly knowing which project changed.
    This works fine if you have only 2 projects, when you add a third one you will have to do any future changes manually to all three, which is not very DRY (Copy Then Refactor for the third time)

So What can we do? well use the workflow_call!

The Basics are

  1. Create a Reusable Workflow that takes inputs and required secrets and looks like the block below
  2. Create one small workflow for each project that triggers on respective project changes and just calls the reusable workflow.
    Let’s See It In Action Shall we? Again the source code is in the same repository

Extro

That’s it for now there are tons of features but these are the lesser known ones or at least ones not easily found.

Meaning Comic Monkey User