Skip to content

clinic: add deploy job to the github workflow #46

clinic: add deploy job to the github workflow

clinic: add deploy job to the github workflow #46

Workflow file for this run

name: Clinic
on:
push:
branches:
- main
tags:
- "**"
paths:
- clinic/**
- .github/workflows/clinic.yaml
pull_request:
branches:
- main
env:
JDK_DISTRIBUTION: temurin
JAVA_VERSION: 21
NODE_VERSION: 20
jobs:
tests:
name: Tests
runs-on: ubuntu-latest
timeout-minutes: 10
services:
hapi-fhir:
image: hapiproject/hapi:latest
env:
server.port: 8090
hapi.fhir.tester.home.server_address: http://localhost:8090/fhir
hapi.fhir.allow_multiple_delete: true
ports:
- 8090:8090
defaults:
run:
working-directory: ./clinic
steps:
- name: Checkout Source
uses: actions/checkout@v4
- name: Setup JDK
uses: actions/setup-java@v3
with:
distribution: ${{ env.JDK_DISTRIBUTION }}
java-version: ${{ env.JAVA_VERSION }}
- name: Setup Lein Dependency Cache
uses: actions/cache@v3
with:
path: ~/.m2/repository
key: ${{ runner.os }}-lein-${{ hashFiles('clinic/project.clj') }}
# can't use `services.[id].options` to add Docker health checks because
# HAPI has a scratch docker image without a shell installation.
- name: HAPI FHIR Health Check
run: while ! curl -sSI "http://localhost:8090"; do sleep 5; done
- name: Run Tests
run: lein test
build:
name: Build
runs-on: ubuntu-latest
timeout-minutes: 10
if: github.event_name == 'push' # skip for pull requests.
needs:
- tests
permissions:
contents: read
packages: write
defaults:
run:
working-directory: ./clinic
steps:
- name: Checkout Source
uses: actions/checkout@v4
- name: Setup JDK
uses: actions/setup-java@v3
with:
distribution: ${{ env.JDK_DISTRIBUTION }}
java-version: ${{ env.JAVA_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
cache-dependency-path: clinic/package-lock.json
- name: Setup Lein Dependency Cache
uses: actions/cache@v3
with:
path: ~/.m2/repository
key: ${{ runner.os }}-lein-${{ hashFiles('clinic/project.clj') }}
- name: Build CLJS
run: |
npm install
npx shadow-cljs release app
- name: Build Uberjar
run: lein uberjar
# buildx uses QEMU. Docker Buildx is needed for multiplatform build.
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Generate Docker Image Tags
id: docker-image-tags
run: |
image_name=ghcr.io/nilenso/ashutosh-onboarding/clinic
image_tags="${image_name}:latest"
if [[ "$GITHUB_REF" =~ refs/tags/* ]]; then
image_tags="${image_tags},${image_name}:${{ github.ref_name }}"
fi
echo "tags=$image_tags" >> "$GITHUB_OUTPUT"
- name: Build and Push Latest Docker Image
uses: docker/build-push-action@v5
with:
context: clinic
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.docker-image-tags.outputs.tags }}
- name: Delete Untagged Images from GHCR
uses: vlaurin/[email protected]
with:
container: ashutosh-onboarding%2Fclinic # is a path param
token: ${{ github.token }}
user: ${{ github.actor }}
keep-last: 3
prune-untagged: true
deploy:
name: Deploy
runs-on: ubuntu-latest
timeout-minutes: 10
if: github.event_name == 'push' # skip for pull requests.
needs:
- build
# GitHub won't allow using an env var to specify environment.
environment: ${{ startsWith(github.ref, 'refs/tags/') && 'production' || 'staging' }}
env:
DEPLOYMENT_ID: ${{ startsWith(github.ref, 'refs/tags/') && 'production' || 'staging' }}
SCP_DEST_PATH: /tmp/${{ github.sha }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.DEPLOY_BOT_ACCESS_KEY }}
defaults:
run:
working-directory: ./clinic
steps:
- name: Checkout Source
uses: actions/checkout@v4
- name: Install prerequisites
run: |
sudo apt-get -qq update -y
sudo apt-get -qq install -y awscli
- name: Get Workflow Runner's Public IP
id: workflow-runner-ip
run: echo "ipv4=$(curl -s 'https://api.ipify.org')" >> "$GITHUB_OUTPUT"
- name: Authorize runner's SSH access to EC2 host
run: |
aws ec2 authorize-security-group-ingress \
--group-id "$AWS_EC2_SG_ID" \
--protocol tcp \
--port 22 \
--cidr ${{ steps.workflow-runner-ip.outputs.ipv4 }}/32
env:
AWS_SECRET_ACCESS_KEY: ${{ secrets.DEPLOY_BOT_SECRET_KEY }}
AWS_EC2_SG_ID: ${{ secrets.AWS_EC2_SG_ID }}
- name: Upload Systemd Service and Logrotate Config
uses: appleboy/[email protected]
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
port: ${{ secrets.SSH_PORT }}
key: ${{ secrets.SSH_KEY }}
source: clinic/deploy/systemd,clinic/deploy/logrotate
target: ${{ env.SCP_DEST_PATH }}
strip_components: 2 # remove `clinic/deploy/` path component at target.
rm: true # remove target directory before uploading data
- name: Configure Deployment
uses: appleboy/[email protected]
env:
APP_ENV: ${{ secrets.APP_ENV }}
GHCR_USER: ${{ github.actor }}
GHCR_TOKEN: ${{ github.token }}
GHCR_IMAGE: ghcr.io/nilenso/ashutosh-onboarding/clinic:${{ env.DEPLOYMENT_ID == 'staging' && 'latest' || github.ref_name }}
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
port: ${{ secrets.SSH_PORT }}
key: ${{ secrets.SSH_KEY }}
# only pass sensitive env vars from here. for others, use GitHub's env
# context, which is a little less error-prone.
envs: APP_ENV,GHCR_TOKEN
script_stop: true # stop script after first failure.
script: |
# stop existing deployment
service_name="clinic@${{ env.DEPLOYMENT_ID }}.service"
sudo systemctl stop "$service_name" || true
# pull and tag the new docker image
echo "$GHCR_TOKEN" | sudo docker login ghcr.io -u ${{ env.GHCR_USER }} --password-stdin
sudo docker pull ${{ env.GHCR_IMAGE }}
sudo docker tag ${{ env.GHCR_IMAGE }} clinic:${{ env.DEPLOYMENT_ID }}
sudo docker logout ghcr.io
# write environment configuration
sudo mkdir -p /etc/clinic
echo "$APP_ENV" | sudo tee "/etc/clinic/${{ env.DEPLOYMENT_ID }}.env" > /dev/null
# update systemd unit
sudo chown root:root ${{ env.SCP_DEST_PATH }}/systemd/*
sudo chmod 644 ${{ env.SCP_DEST_PATH }}/systemd/*
sudo mv ${{ env.SCP_DEST_PATH }}/systemd/* /etc/systemd/system/
# reload systemd daemon and restart the service
sudo systemctl daemon-reload
sudo systemctl reenable "$service_name"
sudo systemctl restart "$service_name"
# deploy logrotate config
sudo chown root:root ${{ env.SCP_DEST_PATH }}/logrotate/*
sudo chmod 644 ${{ env.SCP_DEST_PATH }}/logrotate/*
sudo mv ${{ env.SCP_DEST_PATH }}/logrotate/* /etc/logrotate.d/
# clean-up
sudo docker image prune -f
sudo rm -rf ${{ env.SCP_DEST_PATH }}
- name: Revoke runner's SSH access from EC2 host
if: ${{ always() }}
run: |
aws ec2 revoke-security-group-ingress \
--group-id "$AWS_EC2_SG_ID" \
--protocol tcp \
--port 22 \
--cidr ${{ steps.workflow-runner-ip.outputs.ipv4 }}/32
env:
AWS_SECRET_ACCESS_KEY: ${{ secrets.DEPLOY_BOT_SECRET_KEY }}
AWS_EC2_SG_ID: ${{ secrets.AWS_EC2_SG_ID }}