What is a Github Action ?

Simple, is a set of instructions that specify what actions should be performed when certain events occur in your GitHub repository.

Here’s an example of events and actions:

Event Actions
Branch push Deploy code
  New pull_request  Run tests
New release Build and package the release and notify users  
schedule Perform code backups
New issue Assign an issue to a developer

Workflow File

A workflow file defines the events and instructios for your automation, is a YAML file that resides in the .github/workflows directory of your repository.

Structure

The minimum structure of a workflow file follows these components:

  • name: Defines the name of the workflow.
  • on: Specifies the event(s) that trigger the workflow.
  • jobs: Defines one or more jobs to be executed within the workflow. Jobs are independent units of work that can run in parallel or sequentially.
  • steps: Contains a sequence of steps within a job.
    • runs-on: Allows you to specify the type of machine to run the job on.
    • uses: Allows you to reference an action or a composite action defined in your repository or from a public repository.
    • run: Allows you to run a command or a script directly within the step.

name: 'First workflow'

on:
  push:
    branches:
      - main

jobs:
  first-job:
    runs-on: ubuntu-latest
    steps:
      - name: STEP 1 - Checkout main branch
        uses: actions/checkout@v3
      - name: STEP 2 - List repository files
        run: ls -la

Flow control

GitHub Actions provides several flow options to control the execution behavior of jobs within a workflow. In this section, we will cover the basic options.

Parallel

Jobs defined under the jobs are executed in parallel by default.

Example: Let’s consider a scenario where you want to deploy your app to DEMO and PRODUCTION environments only when you push to the main branch. You can run the deploy-demo and deploy-prod jobs in parallel. The following figure illustrates this:

fdsfasd

And here’s the corresponding workflow file:

name: Parallel Example

on:
  push:
    branches:
      - main

jobs:
  deploy-demo:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy DEMO
        run: echo "Deploying DEMO"

  deploy-prod:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy PROD
        run: echo "Deploying PROD"
Needs

The needs keyword in GitHub Actions specifies dependencies between jobs. It allows you to define which jobs should complete successfully before an other job starts.

Example: In the previous example, let’s say you need to run tests before initiating any deployment process. To achieve this, you can create a tests job and establish dependencies with the deploy-demo and deploy-prod jobs. The diagram below illustrates this scenario:

fdsfasd

And here’s the corresponding workflow file:

name: Needs Example

on:
  push:
    branches:
      - main

jobs:

  tests:
    runs-on: ubuntu-latest
    steps:
      - name: Run tests
        run: echo "Running tests"

  deploy-demo:
    runs-on: ubuntu-latest
    needs: tests
    steps:
      - name: Deploy DEMO
        run: echo "Deploying DEMO"

  deploy-prod:
    runs-on: ubuntu-latest
    needs: tests
    steps:
      - name: Deploy PROD
        run: echo "Deploying PROD"
Strategy

The strategy keyword allows you to create multiple jobs with different configurations.

Example: Let’s imagine that you have a variety of environments where you want to deploy your application. Instead of manually defining separate jobs for each environment, you can leverage the power of the strategy keyword. The following workflow file demonstrates this approach:

name: Strategy Example

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        environment: [development, staging, demo, production]
    steps:

      - name: Deploy Application to ${{ matrix.environment }}
        run: echo "Deploying to ${{ matrix.environment }}"
If

The if condition in GitHub Actions provides the ability to execute steps or jobs conditionally based on specific conditions. It allows you to control the flow of your workflow by defining when a particular step or job should run based on certain criteria, such as the values of environment variables.

Here’s an example of using the if condition to conditionally execute a step within a job:

- name: Notification
  if: matrix.environment == 'production'
  run: echo "Notification: Deployment to production completed."

In this example, the Notification step will only execute if the value of the matrix.environment variable is set to production.

Outputs

Outputs allow you to pass data between jobs within a workflow, enabling communication and sharing of information.

Here’s a simplified example showcasing the usage of outputs in a workflow:

name: Outputs Example

on:
  push:
    branches:
      - main

jobs:
  generate_message:
    runs-on: ubuntu-latest
    outputs:
      message: ${{ steps.message.outputs.message }}

    steps:

      - name: Joan
        id: joan
        run: echo "name=JOAN" >> $GITHUB_OUTPUT
      # Here's an example of how to use an output from a previous step
      - name: Generate Message
        id: message
        run: echo "message=Hi ${{ steps.joan.outputs.name }}" >> $GITHUB_OUTPUT

  display_message:
    runs-on: ubuntu-latest
    needs: generate_message
    steps:
      # This step uses the output from the generate_message job
      - name: Display Message
        run: echo "${{ needs.generate_message.outputs.message }}"
Repository with this examples

On the following repository you can find all the examples explained in this post: Github Actions course examples.

Using existing actions

Using existing GitHub Actions is a powerful practice that can significantly enhance your workflows. By leveraging pre-built actions created and shared by the GitHub community, you can save time and effort.

Some useful links to find existing GitHub Actions:

Image by storyset on Freepik