Test and deploy an OpenAPI with Github Actions
It is time to go full circle with the search-to-rent OpenAPI demo by adding a Github Actions workflow for CI / CD.
March 02, 2024
You may want to get up to speed by taking a peek at the previous two episodes of this series Real estate API for a search-to-rent application and Clerk authentication, authorization, Upstash limiter, Axiom logging.
What is covered
Code repository
Online resources
- Github Actions - Automate your workflow from idea to production
- Bun ships with a fast, built-in, Jest-compatible test runner
- Testing is important. In actuality, it is easy to test Hono's applications
- Postman - Build, Test, Document, Debug, Monitor, Publish APIs together
- Fly.io - Launch Apps Near Users - Fly.io transforms containers into micro-VMs that run on our hardware in 30+ regions on six continents
A short recap of repository testers
I am using a combination of recommended tests by Bun and Hono Docs and a custom test runner as a Bun CLI that spawns children's processes and streams their stdout
to sniff out if everything is all right.
- /src/routes/__tests__/featureToPropertyRoutes.test.ts a sample of classic Bun tests of Hono's endpoints
- /src/routes/__tests__/startAppAndRunPostmanTests.ts custom test runner for Postman CLI child process in charge of endpoints' collection contract tests
- /src/services/taskWorkers/__tests__/startAppAndReadStdout.ts custom test runner for the behind-the-scenes workers keeping the Algolia index in sync
Adding secrets and variables to the Github repo
Github offers an elegant and safe way to store secrets and variables (environment vars that are not secrets) required by any actions workflow. You may either use the Github web application or the multi-tallented Github Actions VS Code extension.
Test job
The Github Actions workflow /.github/workflows/fly-test-deploy.yml is triggered on each push
to the main
branch at any of the paths defined here:
name: Fly Test and Deploy
on:
push:
branches:
- main
paths:
- 'src/**'
- 'package.json'
- 'bun.lockb'
- 'fly.toml'
- 'Dockerfile'
The test
job uses a Linux ephemeral instance to load all the environment variables, secrets, or otherwise required by the three test runners. After checking out the repo and setting up Bun, it installs all project dependencies. It runs all tests in sequence, overriding the BUN_ENV
environment variable value when it is needed.
jobs:
test:
name: Run Tests
runs-on: ubuntu-latest
env:
ALGOLIA_ADMIN_API_KEY: ${{ secrets.ALGOLIA_ADMIN_API_KEY }}
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
AXIOM_API_TOKEN: ${{ secrets.AXIOM_API_TOKEN }}
CLERK_PUBLISHABLE_KEY: ${{ secrets.CLERK_PUBLISHABLE_KEY }}
CLERK_SECRET_KEY: ${{ secrets.CLERK_SECRET_KEY }}
CLOUDAMQP_URL: ${{ secrets.CLOUDAMQP_URL }}
DATABASE_NAME: ${{ vars.DATABASE_NAME }}
DATABASE_HOST: ${{ vars.DATABASE_HOST }}
DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
DATABASE_USERNAME: ${{ secrets.DATABASE_USERNAME }}
GCLOUD_API_KEY: ${{ secrets.GCLOUD_API_KEY }}
UPSTASH_REDIS_REST_TOKEN: ${{ secrets.UPSTASH_REDIS_REST_TOKEN }}
UPSTASH_REDIS_REST_URL: ${{ secrets.UPSTASH_REDIS_REST_URL }}
SERVER_TIMEZONE: ${{ vars.SERVER_TIMEZONE }}
DATABASE_SEED_BLOCKED: ${{ vars.DATABASE_SEED_BLOCKED }}
AXIOM_DATASET: ${{ vars.AXIOM_DATASET }}
POSTMAN_API_KEY: ${{ secrets.POSTMAN_API_KEY }}
CLERK_JWT_TEST: ${{ secrets.CLERK_JWT_TEST }}
BUN_ENV: dev
concurrency: deploy-group
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
with:
bun-version: '1.0.29'
- run: bun install
- run: bun run postman:test:cleanup:actions
- run: bun run test:bun:actions
env:
BUN_ENV: test
- run: bun run postman:test:cleanup:actions
- run: bun run workers:test:utils:actions
env:
BUN_ENV: algolia
CICD: true
- name: Install Postman CLI
run: |
curl -o- "https://dl-cli.pstmn.io/install/linux64.sh" | sh
- run: bun run postman:test:cleanup:actions
- run: bun run test:postman:actions
env:
BUN_ENV: postman
CICD: true
Deploy job
The deploy
job starts anew with another Linux instance to make sure none of the previous secrets spill into the build process. Of course, it needs the test
job to be successful; otherwise, it is aborted. The previous steps are common, and they are cached. Another set of variables is required; none of them are secret. Deploying to Fly.io first installs superfly
CLI tools to be able to load the build and push the Docker image.
deploy:
name: Deploy to Fly
runs-on: ubuntu-latest
needs: test
concurrency: deploy-group
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
with:
bun-version: '1.0.29'
- run: bun install
- run: bun run build:actions
env:
BUN_ENV: production
DATABASE_NAME: ${{ vars.DATABASE_NAME }}
DATABASE_HOST: ${{ vars.DATABASE_HOST }}
SERVER_TIMEZONE: ${{ vars.SERVER_TIMEZONE }}
DATABASE_SEED_BLOCKED: ${{ vars.DATABASE_SEED_BLOCKED }}
AXIOM_DATASET: ${{ vars.AXIOM_DATASET }}
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
Accessing Github actions jobs output
First, if you don't succeed, try again. Of course, I initially had two failed attempts to run the workflow. The first was due to wrong permissions, and the second was because I forgot to install Postman CLI before the related step. The good thing is that both the Github web application and the VS Code extension provide you with detailed workflow output.