Refactoring GitHub Actions Workflow
Tags: azure, azurestaticwebapps, githubactions, refactoring
Published: 2021-12-03 00:00:26

---
title: Refactoring GitHub Actions Workflow
published: true
description: In this post, I'm going to discuss how to refactor the auto-generated GitHub Actions workflow for ASWA, using the 'reusable workflow' technique.
tags: azure, azurestaticwebapps, githubactions, refactoring
canonical_url: https://devkimchi.com/2021/12/01/refactoring-aswa-github-actions-workflow/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5gkj2ylkbztjh6pn73t.png
series: 

---
A while ago, I wrote a blog post about [Azure DevOps Pipelines refactoring technics][post azdo refactoring]. [GitHub Actions][gh actions] is also suitable for building CI/CD pipelines. But, compared to [Azure DevOps][az do], there are many spaces to achieve the same efficiency level as Azure DevOps. GitHub Actions has recently released a new feature called ["Reusable Workflows"][gh actions reusable], which you can reduce the refactoring concerns. Throughout this post, I'm going to refactor the existing [Azure Static Web Apps][az swa] CI/CD pipeline workflows, using the "reusable workflows" feature of GitHub Actions.


## Workflow for Azure Static Web Apps ##

While provisioning an [Azure Static Web Apps (ASWA)][az swa] instance, GitHub Actions workflow is automatically generated by default. Here's a sample workflow. I'm pretty sure it's not that different from yours.

{% gist https://gist.github.com/justinyoo/3f8de0ebaff5bdd7e41c961ed37b5b53 file=01-original-workflow.yaml %}

The problem is that once you provision the ASWA instance, you can't change the name of the auto-generated workflow file. Therefore, you are only allowed to modify the file.

Let's imagine a situation. You've got a codebase that deploys to multiple Azure Static Web Apps instances &ndash; DEV, TEST and PROD, which is pretty common. In that case, you will have as many GitHub Actions workflow files as the number of ASWA instances. But all the workflow files are virtually the same as each other except their filenames. So if you refactor those workflows, the overall process would be more simplified.

For the refactoring practice, you would use the [`workflow_dispatch`][gh actions workflow_dispath] event, together with the [webhook event][gh api workflow_dispatch], to call the refactored workflow. Once it's set up, unless the access token gets invalidated, you'll be able to use the workflow. But what if the access token is expired or compromised? You MUST reissue the token, which is less ideal. How can we work out this situation?


## Reusable Workflows (or Called Workflow) ##

The reusable workflows use the newly introduced event called [`workflow_call`][gh actions workflow_call]. It reuses the workflows at the job level. Let's refactor the workflow above. Copy both `build_and_deploy_job` and `close_pull_request_job` jobs and paste both into a new YAML file, which is called either "reusable workflow" or "called workflow".

{% gist https://gist.github.com/justinyoo/3f8de0ebaff5bdd7e41c961ed37b5b53 file=02-reusable-workflow-1.yaml %}

According to the workflow above, there are input variables and secret variables and other variables:

* Event Object
  * Event Name: `github.event_name`
  * Event Action: `github.event.action`
* Secrets
  * Azure Static Web Apps API Token: `secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_XXXX_XXXX_XXXX`
  * GitHub Token: `secrets.GITHUB_TOKEN`
* App Locations
  * Web App: `app_location`
  * API App: `api_location`
  * Web App Artifact: `output_location`

The "called" workflow can't directly access those values defined in the "caller" workflow. Therefore, they MUST be propagated from the "caller" workflow to the "called" workflow. Let's update the variable parts in the "called" workflow like below *(line #7-8, 20-21, 23-24, 30-31, 33-34, 36-37, 43-44, 54-55)*.

{% gist https://gist.github.com/justinyoo/3f8de0ebaff5bdd7e41c961ed37b5b53 file=03-reusable-workflow-2.yaml %}

All values have become parameterised. Now, you need to define them in the "called" workflow, under the `workflow_call` event. All non-secret variables go under the `inputs` attribute *(line #6-21)*, and all the secret variables go under the `secrets` attribute *(line #23-27)*.

{% gist https://gist.github.com/justinyoo/3f8de0ebaff5bdd7e41c961ed37b5b53 file=04-reusable-workflow-3.yaml %}

Now, the refactoring has been completed!


## Caller Workflow ##

Let's update the existing ASWA workflow. As both jobs defined under the `jobs` node are no longer necessary, delete them. And define a new "reusable" workflow like below:

* Called Workflow *(line #14-15)*: `<org_name>/<repo_name>/.github/workflows/<reusable_workflow_filename>@<branch_or_tag>`
* Input Variables *(line #17-22)*: Under `with`
* Secret Variables *(line #24-26)*: Under `secrets`

{% gist https://gist.github.com/justinyoo/3f8de0ebaff5bdd7e41c961ed37b5b53 file=05-caller-workflow.yaml %}

Now, regardless of the number of "caller" workflows, update them like above. You only need to update the "called" workflow then all ASWA pipelines will get the updated workflow applied, which is really convenient!

---

So far, I've shown how to refactor the [Azure Static Web Apps][az swa] workflow with the [reusable workflows][gh actions reusable] feature of [GitHub Actions][gh actions]. I'm sure that it's not just for ASWA but also can be utilised in many different scenarios.


[post azdo refactoring]: https://devkimchi.com/2019/09/04/azure-devops-pipelines-refactoring-technics/

[az do]: https://docs.microsoft.com/azure/devops/user-guide/what-is-azure-devops?view=azure-devops&WT.mc_id=dotnet-51099-juyooo&ocid=AID3035186

[az swa]: https://docs.microsoft.com/azure/static-web-apps/overview?WT.mc_id=dotnet-51099-juyooo&ocid=AID3035186

[gh actions]: https://docs.github.com/en/actions
[gh actions reusable]: https://docs.github.com/en/actions/learn-github-actions/reusing-workflows
[gh actions workflow_call]: https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#workflow_call
[gh actions workflow_dispath]: https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#workflow_dispatch
[gh api workflow_dispatch]: https://docs.github.com/en/rest/reference/actions#create-a-workflow-dispatch-event

[monorepo]: https://en.wikipedia.org/wiki/Monorepo