Build Automated CI/CD Workflows with Github Actions : Part-2

Atharva Paliwal
Coinmonks
Published in
11 min readApr 15, 2023

--

This is the Part-2 of the series that is going on for learning Github Actions. If you have not yet read Part-1 then do give it a read as it covers the concepts that are needed to get started with this article. However if you already know the basics of Github Actions and its components and you want to know how it works in the practical world, then your search stopped at the right article.

In this article we are going to build workflows, run jobs parallelly and sequentially, setup steps inside jobs so as to automate all this steps so that we don’t need to run them manually in future.

To get started you can clone this repository on your local machine or if you want, you can also use your own custom template to follow along with this article.

Setup

  1. After cloning the repository, you can now connect that repository to your remote repository on github so that we will be able to execute workflows on github. Not sure how to do this, Check steps here !!
  2. On your local, install the dependencies using command -npm install

Getting Started

So in this tutorial we will be setting up two workflows —

  1. First workflow will be the workflow that tests and lints the project and simulates the deployment of project whenever a new commit is pushed.

The repo contains lint, test and build scripts that you need to execute. For this either this can be done in a single job or 3 different jobs, we will look at both the scenarios.

2. Second workflow will be the workflow where we will listen to an Issues event and output the event details in the shell via echo command.

Building our first Workflow

1. Workflow with single job

Now our code is on github but we don’t have a github actions workflows yet. So, in the previous article we learnt that our workflows reside in a folder called .github/workflows and in this folder we can add our workflows YAML files.

NOTE : For every workflow you should have a different YAML file

Inside the workflows folder, let’s add a file name Deployment1.yaml in which we will write our first workflow.

As of now, my folder structure looks like this -

Current Folder structure

Inside Deployment1.yaml file , we need to start with the definition of our workflow i.e., by giving our workflow a name.

#define workflow
name : "Deployment using a single Job"

Now, we want our workflow to trigger or start when a push event happens i.e., our workflow should trigger whenever someone who has the access to the repository pushes a commit. We want to trigger test, lint and deploy steps whenever someone pushes the code to the repository. Events are specified using keyword on.

#define workflow
name : Deployment using a single Job
#event that triggers workflow
on: push

NOTE : You can have any event that can act as a trigger for your workflow. For ex. If you want to manually trigger your workflows, you can use an event workflow_dispatch.

You can find more events here.

After defining the event, let’s define our jobs, now as we want to define test, lint and deploy in a single job so we won’t have multiple jobs here that we will see in later part of the article. Jobs are defined using jobs keyword.

Now as I said, there can be multiple jobs in a single workflow, so we need to give a name to our job, In our case let’s give it a name deploy.

NOTE : Make sure you keep the indentation in mind when you are giving a name to your job

#define workflow
name : Deployment using a single Job
#event that triggers workflow
on: push
#jobs
jobs :
# name of job
deploy :

As discussed in the previous article, jobs are some set of steps that execute on a runner where runner is your server that will run your workflow.

So let’s define a runner for our job deploy. Runners are specified using the keyword runs-on.

#define workflow
name: Deployment using a single Job
#event that triggers workflow
on: push
#jobs
jobs:
# name of job
deploy:
#specify runner for the job
runs-on: ubuntu-latest

I have chosen ubuntu-latest as my runner as it is most commonly used, you can chose any of the runner. You can find more runners here.

We defined our job, we setup a runner and now jobs also contains multiple steps inside them, in our case this steps are test, lint and deploy so let’s define them. Steps are specified using keyword steps.

Similar to jobs, steps can also be multiple so we need to give a name to each step.

#define workflow
name: Deployment using a single Job
#event that triggers workflow
on: push
#jobs
jobs:
# name of job
deploy:
#specify runner for the job
runs-on: ubuntu-latest
#Steps
steps:
#STEP-1
- name: Get Code

Now, you must be wondering why have I given the name as Get Code and why not test, lint or deploy.

So, let’s get back to runners, we have a separate runner for every job in our case it is ubuntu-latest but how will that runner get to know which code it wants to work upon. So this is the reason, we will need to have the first step to bring the codebase in that runner. But how do we do that? There are some third party packages that are provided to us via the marketplace by the github team. So we can directly use them in our YAML files.

#define workflow
name: Deployment using a single Job
#event that triggers workflow
on: push
#jobs
jobs:
# name of job
deploy:
#specify runner for the job
runs-on: ubuntu-latest
#Steps
steps:
#STEP-1
- name: Get Code
uses: actions/checkout@v3

NOTE : There is a marketplace by Github actions which contains all this actions. You can find those here!!

uses keyword tells about which action we want to use from the marketplace. actions/checkout@v3 is the action that is available in the marketplace which checkouts the repository in the runner.

After getting the code, we need to install the dependencies too, so let’s do that using a command npm ci.

NOTE : We can install NodeJS too if you want to work with a particular node version. However our runner machine comes with a default NodeJS version of 18.

You can check what ubuntu-latest comes with which preinstalled packages here.

If you want to install NodeJS too you can do it using a marketplace action actions/setup-node@v3 and specify your version of NodeJS using with command.

NodeJS Action
#define workflow
name: Deployment using a single Job
#event that triggers workflow
on: push
#jobs
jobs:
# name of job
deploy:
#specify runner for the job
runs-on: ubuntu-latest
#Steps
steps:
#STEP-1
- name: Get Code
uses: actions/checkout@v3
#STEP-2
- name: Install Dependencies
run: npm ci

Now, we can run our concerned steps i.e. lint, test and a dummy deploy message.

#define workflow
name: Deployment using a single Job
#event that triggers workflow
on: push
#jobs
jobs:
# name of job
deploy:
#specify runner for the job
runs-on: ubuntu-latest
#Steps
steps:
#STEP-1
- name: Get Code
uses: actions/checkout@v3
#STEP-2
- name: Install Dependencies
run: npm ci
#STEP-3
- name: Lint
run: npm run lint
#STEP-4
- name: Test
run: npm run test
#STEP-5
- name: Build Code
run: npm run build
#STEP-6
- name: Deploy Code
run: echo "Deploying..."

For the last step, I just ran a echo command as for deploying we would need to setup a hosting service etc. So for the simplicity of this article for that step let’s just echo it.

So with the all this steps, I think we are ready with out first workflow, let’s now commit and push our changes and see what we have next!!

Head towards Actions tab onto your github, and you must be seeing something like this-

Workflow status after pushing code onto repository
  1. added deployment workflows resembles my commit message and the blue tick resembles that it is successfully executed.
  2. Deployment using a single Job resembles our workflow name.

Going into the details we see our job deploy.

View Job

Let’s see what steps were executed in our Job -

Steps in Job

Hurrayyy !! We successfully ran our first workflow!!!!! Excited to do this with multiple Jobs?? Let’s do it.

2. Workflow with jobs running parallelly

The basics remain the same, it’s just now we have different jobs for each of our process of lint, test and deploy.

#define workflow
name: Deployment using multiple jobs
#event that triggers workflow
on: push
#jobs
jobs:
# name of JOB-1
lint:
#specify runner for the job
runs-on: ubuntu-latest
#Steps
steps:
#STEP-1
- name: Get Code
uses: actions/checkout@v3
#STEP-2
- name: Install Dependencies
run: npm ci
#STEP-3
- name: Lint
run: npm run lint
# name of JOB-2
test:
#specify runner for the job
runs-on: ubuntu-latest
#Steps
steps:
#STEP-1
- name: Get Code
uses: actions/checkout@v3
#STEP-2
- name: Install Dependencies
run: npm ci
#STEP-3
- name: Test
run: npm run test
# name of JOB-3
deploy:
#specify runner for the job
runs-on: ubuntu-latest
#Steps
steps:
#STEP-1
- name: Get Code
uses: actions/checkout@v3
#STEP-2
- name: Install Dependencies
run: npm ci
#STEP-3
- name: Deploy Code
run: echo "Deploying..."

Let’s now see how our workflow looks with jobs running in parallel-

Workflow-2 with jobs in parallel

Our new workflow is added to the list and it is successfully executed!!

Jobs running parallelly

You must notice that the jobs are listed one below the other with no connection in between them, this is because they are parallelly running and not dependent on anyone.

Let’s also run this jobs sequentially.

3. Workflow with jobs running sequentially

Since we want to run this jobs sequentially, we need to add a keyword needs in every job that we want to get executed only after certain job.

So, in our case we want that deployment should only happen after the successful execution of lint and test jobs, so we will add needs keyword in deploy job.

#define workflow
name: Deployment using multiple jobs
#event that triggers workflow
on: push
#jobs
jobs:
# name of JOB-1
lint:
#specify runner for the job
runs-on: ubuntu-latest
#Steps
steps:
#STEP-1
- name: Get Code
uses: actions/checkout@v3
#STEP-2
- name: Install Dependencies
run: npm ci
#STEP-3
- name: Lint
run: npm run lint
# name of JOB-2
test:
#needs keyword to execute jobs sequentially
needs: lint
#specify runner for the job
runs-on: ubuntu-latest
#Steps
steps:
#STEP-1
- name: Get Code
uses: actions/checkout@v3
#STEP-2
- name: Install Dependencies
run: npm ci
#STEP-3
- name: Test
run: npm run test
# name of JOB-3
deploy:
needs: [lint, test]
#specify runner for the job
runs-on: ubuntu-latest
#Steps
steps:
#STEP-1
- name: Get Code
uses: actions/checkout@v3
#STEP-2
- name: Install Dependencies
run: npm ci
#STEP-3
- name: Deploy Code
run: echo "Deploying..."

needs:[lint,test] means that we want lint and test jobs to be executed successfully before running deploy job.

Let’s see how running jobs sequentially looks like-

we have added our commit run jobs sequentially and it is successfully executed. Let’s check how the flow of our jobs looks like-

Noticed Something? This time the jobs are connected with each other which means if lint job fails to execute successfully, test job will not execute. The jobs are getting executed sequentially.

Soo if you have followed it till here, I really appreciate your efforts as you have learnt now how to build you first workflow along with a single job and with multiple jobs running parallelly and sequentially.

Building our second workflow

In this part, we will now add a new workflow that is not triggered when we push, instead get’s triggered when someone raises a Issue onto our github repository.

In this part, we will just output the details about Issues event in the shell of our runner on which the job of workflow executes.

name: Handle issues
on: issues
jobs:
output-info:
runs-on: ubuntu-latest
steps:
- name: Output event details
run: echo "${{ toJSON(github.event) }}"

${{}} this is a special expression syntax where you have a dollar sign, and then two opening and closing curly braces, between those opening and closing curly braces you can run special commands and access special data objects that are provided by GitHub in that Workflow.

${{github}} is the context object of which we want to access the information. Since we want information specific to event that’s why we have ${{github.event}}.

NOTE : You can read about Github context here !!

So, now let’s commit and push our code and let’s see what happens!!

We got the Handle issues workflow, but there is no job running in it? Why ?

This is because the workflow will only be triggered on the event that we specified and in our case it is issues event.

So, let’s create a Issue from Issue tab in our repository.

I created a issue named Issue-1 and we have a job running now-

It’s executed successfully!! Let’s check it’s output-

If you will notice, it outputs the event details that we wanted to display!!

That’s it for this article, In this article we saw how to build our first workflow with a single job, multiple jobs running parallelly and sequentially and after that another workflow with a Issues event where we displayed the event details whenever a new issue was created.

If you enjoyed reading this article, please do leave a clap or a comment and you can also connect with me on LinkedIn and Twitter. You can follow me on Medium as I keep writing about new things that I learn and share that with you guys!!

--

--

Atharva Paliwal
Coinmonks

Blockchain Developer (R&D) at Persistent Systems | Writes about new and less known things that you may want to add to your skills portfolio ✍️💫