DevOps
With all the pieces of the application at hand we are ready to lay out CI/CD pipeline architecture.
Hosting
For the simplicity of usage and zero payment service, we are using github static hosting. According to the service, we need to specify a branch that will hold compiled application. The rest is taking care of for us (excluding custom domain, more on this later). Since master
branch is already used by the application source code itself, we can use a second option - gh-pages
branch.
Deployment
Manually deployment process looks simple.
- Data files are downloaded from the public data source
- Data files are saved into the
public
folder - The application is built into the
build
folder that is excluded from source control tracking - Application source code is cloned into a separate folder from remote (
gh-pages
branch only) public
folder content from original application source folder is copied to the new clone- Commit and push of the
gh-page
branch to remote is executed
Unfortunately this process would work only once. Next time, when we execute the same sequence of steps, previously saved data source would be overridden. At the second step, when we save downloaded data to the public
folder, we are not taking care of previously saved information.
So the new/fixed process looks like this:
- Download data files
- Download previously saved data files (from github using
raw.githubusercontent.com
server) - Merge new data files into old data files
- Save merged data into the
public
folder - Build the application
- Clone
gh-pages
branch from remote - Copy
public
folder contents intogh-pages
clone - Commit and push
Actions
Besides the hosting, github will help us automating the deployment process as well. It provides a feature that does exactly what we are looking for - automate the workflow described by deployment steps. This feature is called actions and is turned on automatically when repository source code contains specific execution script with all the steps.
It is possible to automate all the steps we need as is, but for the gh-pages
branch deployment we are going to use an existing step built by somebody else (there is no need to reinvent the wheel). Different actions could be imported into the workflow from different repositories for the purpose of reusability. We we are taking advantage of this options as well.
Here is how our actions look in the workflow:
Checkout sources code
Checking out source code is a built-in action, we just need to use it
- uses: actions/checkout@v2
Setup nodejs environment
Nodejs step is also built-in but requires extra config to specify exact version of node to be used
strategy:
matrix:
node-version: [14.x]
...
- name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
Load data
Next one is a custom action and represents JavaScript file that is executed for a particular step. In our case, it's a file that is part of the source control. It loads, merges and saves data.
- uses: ./.github/actions/load-data
Install dependencies
After data is saved, we have to prepare the source code for the compilation by installing necessary 3rd party dependencies.
- name: Install
run: npm ci
Build
While building the application we need to provide all the environment variables required. Variables that should not be exposed to public are taken from github secrets.
- name: Build
run: npm run build --if-present
env:
REACT_APP_MAP_TOKEN: ${{ secrets.MAPBOX_KEY }}
REACT_APP_DATA_URL: .
REACT_APP_GITHUB_URL: https://github.com/maxgherman/nsw-coronavirus
Deploy
Deployment itself represents an invocation of the action we exported into the workflow before
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
deploy_key: ${{ secrets.GH_PAGES_DEPLOY_KEY }}
publish_dir: ./build
publish_branch: gh-pages
cname: nsw-coronavirus.js.org
Custom domain
By default, github hosting provides publishing under github.io domain. Final URL includes username and repository name
Github histing URL for user repositories: http(s)://
user
.github.io/repository
Specifying a custom domain requires a few more details. As you might noticed, previous step has one of the parameters called cname
. This parameter instructs the step action to create a file called CNAME at the root of the repo with one single line nsw-coronavirus.js.org
. Essentially, this file represents a CNAME record that links one DNS entry to another in a way that requests to nsw-coronavirus.js.org are resolved by http(s):// user
.github.io/repository
. Finally, we need to instruct js.org DNS servers of the linking between js.org and github.io. There is a project that does just that for free. The only thing we need is to create a pull request following provided instructions.