clinic: add deploy job to the github workflow #29
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Clinic | |
on: | |
push: | |
branches: | |
- main | |
tags: | |
- "**" | |
paths-ignore: | |
- "**.md" | |
pull_request: | |
branches: | |
- main | |
env: | |
JDK_DISTRIBUTION: temurin | |
JAVA_VERSION: 21 | |
NODE_VERSION: 21 | |
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 | |
- name: Login to GitHub Container Registry | |
uses: docker/login-action@v3 | |
with: | |
registry: ghcr.io | |
username: ${{ github.actor }} | |
password: ${{ secrets.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 | |
tags: ${{ steps.docker-image-tags.outputs.tags }} | |
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: Install prerequisites | |
run: | | |
sudo apt-get -qq update -y | |
sudo apt-get -qq install -y awscli | |
- name: Checkout Source | |
uses: actions/checkout@v4 | |
- 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: deploy/systemd,deploy/logrotate | |
target: ${{ env.SCP_DEST_PATH }} | |
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 }} | |
envs: APP_ENV,GHCR_USER,GHCR_TOKEN,GHCR_IMAGE | |
script_stop: true # stop script after first failure. | |
script: | | |
# stop existing deployment | |
service_name="clinic@${DEPLOYMENT_ID}.service" | |
sudo systemctl stop "$service_name" || true | |
# pull and tag the new docker image | |
echo "$GHCR_TOKEN" | docker login ghcr.io -u "$GHCR_USER" --password-stdin | |
docker pull "$GHCR_IMAGE" | |
docker tag "$GHCR_IMAGE" "clinic:${DEPLOYMENT_ID}" | |
docker logout ghcr.io | |
# write environment configuration | |
sudo mkdir -p /etc/clinic | |
echo "$APP_ENV" | sudo tee "/etc/clinic/${DEPLOYMENT_ID}.env" > /dev/null | |
# update systemd unit | |
sudo chown root:root ${{ env.SCP_DEST_PATH }}/deploy/systemd/* | |
sudo chmod 644 ${{ env.SCP_DEST_PATH }}/deploy/systemd/* | |
sudo mv ${{ env.SCP_DEST_PATH }}/deploy/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 }}/deploy/logrotate/* | |
sudo chmod 644 ${{ env.SCP_DEST_PATH }}/deploy/logrotate/* | |
sudo mv ${{ env.SCP_DEST_PATH }}/deploy/logrotate/* /etc/logrotate/ | |
# clean-up | |
sudo docker image prune -af | |
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 }} |