Skip to content

Commit

Permalink
Merge pull request #1 from ieeeuoft/setting-up-workflow-files
Browse files Browse the repository at this point in the history
Pushing new workflow + init.py file changes
  • Loading branch information
carmen-chau authored Nov 10, 2024
2 parents a7fe106 + 44c6f4f commit 13fdb91
Show file tree
Hide file tree
Showing 9 changed files with 397 additions and 20 deletions.
8 changes: 2 additions & 6 deletions .github/workflows/main.yml → .github/workflows/checks.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CI/CD
name: Checks

on:
pull_request_target:
Expand Down Expand Up @@ -31,7 +31,7 @@ jobs:
black --check .
- name: Tests
env:
SECRET_KEY: ${{ secrets.DJANGO_SECRET_KEY }}
SECRET_KEY: ${{ secrets.CI_SECRET_KEY }}
DEBUG: 0
run: python manage.py test --settings=hackathon_site.settings.ci

Expand All @@ -52,8 +52,6 @@ jobs:
node-version: '16.x'
- name: Install dependencies
run: yarn install
- name: Formatting check
run: yarn run prettier-check

dashboard-checks:
runs-on: ubuntu-latest
Expand All @@ -78,5 +76,3 @@ jobs:
run: yarn run tsc
- name: Tests
run: yarn test --watchAll=false
- name: Build frontend
run: yarn run build
144 changes: 144 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
name: Deploy

on:
push:
branches:
- 'develop'

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
STACK_NAME: ${{ github.repository }}

jobs:
build:
runs-on: ubuntu-latest
outputs:
GITHUB_SHA_SHORT: ${{ steps.sha7.outputs.GITHUB_SHA_SHORT }}

steps:
- uses: actions/checkout@v2
- name: Get short SHA
id: sha7
run: |
GITHUB_SHA_SHORT=$(echo ${{ github.sha }} | cut -c1-7)
echo "GITHUB_SHA_SHORT=${GITHUB_SHA_SHORT}" >> $GITHUB_ENV
echo "::set-output name=GITHUB_SHA_SHORT::${GITHUB_SHA_SHORT}"
- name: Build image
run: docker compose -f deployment/docker-compose.ci.yml build
- name: Docker login
uses: docker/login-action@v1.10.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push image
run: docker compose -f deployment/docker-compose.ci.yml push

deploy:
runs-on: ubuntu-latest
needs: [ build ]
environment:
name: production
url: https://hackstudentlife.ca
defaults:
run:
working-directory: deployment
env:
GITHUB_SHA_SHORT: ${{ needs.build.outputs.GITHUB_SHA_SHORT }}

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Install python dependencies
working-directory: hackathon_site
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Use Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: '16.x'
- name: Install nodejs dependencies
working-directory: hackathon_site/event
run: yarn install
- name: Compile scss
working-directory: hackathon_site/event
run: yarn scss
- name: Collect static
working-directory: hackathon_site
env:
SECRET_KEY: ${{ secrets.CI_SECRET_KEY }}
run: python manage.py collectstatic
- name: Build frontend
working-directory: hackathon_site/dashboard/frontend
run: |
yarn install
yarn run build
- name: Set environment variables in .env
run: |
echo 'DEBUG=0' >> .env
echo 'SECRET_KEY=${{ secrets.CI_SECRET_KEY }}' >> .env
echo 'DB_NAME=${{ secrets.DB_NAME }}' >> .env
echo 'DB_USER=${{ secrets.DB_USER }}' >> .env
echo 'DB_PASSWORD=${{ secrets.DB_PASSWORD }}' >> .env
echo 'DB_HOST=${{ secrets.DB_HOST }}' >> .env
echo 'DB_PORT=${{ secrets.DB_PORT }}' >> .env
echo 'EMAIL_HOST=${{ secrets.EMAIL_HOST }}' >> .env
echo 'EMAIL_PORT=${{ secrets.EMAIL_PORT }}' >> .env
echo 'EMAIL_HOST_USER=${{ secrets.EMAIL_HOST_USER }}' >> .env
echo 'EMAIL_HOST_PASSWORD=${{ secrets.EMAIL_HOST_PASSWORD }}' >> .env
echo 'EMAIL_FROM_ADDRESS=${{ secrets.EMAIL_FROM_ADDRESS }}' >> .env
echo 'REDIS_URI=${{ secrets.REDIS_URI }}' >> .env
echo 'RECAPTCHA_PUBLIC_KEY=${{ secrets.RECAPTCHA_PUBLIC_KEY }}' >> .env
echo 'RECAPTCHA_PRIVATE_KEY=${{ secrets.RECAPTCHA_PRIVATE_KEY }}' >> .env
- name: Transfer static files to the Swarm manager
uses: appleboy/scp-action@v0.1.1
with:
host: ${{ secrets.SWARM_MANAGER_IP }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
overwrite: true
# scp-action doesn't obey working-directory, runs at repo root
source: "hackathon_site/static/"
target: "/usr/src/${{ env.IMAGE_NAME }}"
strip_components: 1
- name: Set up SSH
run: |
mkdir -p ~/.ssh
ssh-keyscan -t ed25519 ${{ secrets.SWARM_MANAGER_IP }} >> ~/.ssh/known_hosts
echo "${{ secrets.SSH_PRIVATE_KEY }}" >> ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
- name: Transfer frontend files to server
uses: appleboy/scp-action@v0.1.1
with:
host: ${{ secrets.SWARM_MANAGER_IP }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
overwrite: true
# scp-action doesn't obey working-directory, runs at repo root
source: "hackathon_site/dashboard/frontend/build/"
target: "/usr/src/${{ env.IMAGE_NAME }}"
strip_components: 3
- name: Bring up deployment
env:
DOCKER_HOST: ssh://${{ secrets.SSH_USER }}@${{ secrets.SWARM_MANAGER_IP }}
run: |
echo "Logging in to GitHub packages..."
echo ${{ secrets.GITHUB_TOKEN }} | docker login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin
echo "Bringing up deployment..."
docker stack deploy --prune --with-registry-auth -c docker-compose.prod.yml ${{ env.STACK_NAME }}
echo "Waiting for deployment..."
sleep 30
chmod 777 docker-stack-wait.sh
./docker-stack-wait.sh -t 600 ${{ env.STACK_NAME }}
echo "Running migrations..."
# TODO: It would be better to use docker-compose against the django service,
# but there is currently a bug in docker-compose preventing running services
# over an SSH host.
IMAGE=${REGISTRY}/${IMAGE_NAME}/django:${GITHUB_SHA_SHORT}
docker run --rm --env-file .env ${IMAGE} python manage.py migrate
echo "Deployment complete"
4 changes: 4 additions & 0 deletions deployment/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ WORKDIR /usr/src/hackathon_site
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# Set time zone
RUN cp /usr/share/zoneinfo/Canada/Eastern /etc/localtime \
&& echo "America/Toronto" > /etc/timezone

# Install dependencies
RUN apt-get update && apt-get install -y postgresql-client

Expand Down
8 changes: 8 additions & 0 deletions deployment/docker-compose.ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: "3.8"

services:
django:
build:
context: ..
dockerfile: ./deployment/Dockerfile
image: ${REGISTRY}/${IMAGE_NAME}/django:${GITHUB_SHA_SHORT}
37 changes: 37 additions & 0 deletions deployment/docker-compose.prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
version: "3.8"

services:
django:
image: ${REGISTRY}/${IMAGE_NAME}/django:${GITHUB_SHA_SHORT}
command: gunicorn hackathon_site.wsgi:application --bind 0.0.0.0:8000 --workers 5 --capture-output --access-logfile - --error-logfile -
ports:
- "8000:8000"
env_file: .env
volumes:
- /var/www/${IMAGE_NAME}/media/:/var/www/media/
deploy:
replicas: 1
update_config:
failure_action: rollback
order: start-first
restart_policy:
condition: on-failure
networks:
- aws-hacks-2024
redis:
image: redis:6-alpine
ports:
- "6379:6379"
deploy:
replicas: 1
update_config:
failure_action: rollback
order: start-first
restart_policy:
condition: on-failure
networks:
- aws-hacks-2024

networks:
aws-hacks-2024:
driver: overlay
148 changes: 148 additions & 0 deletions deployment/docker-stack-wait.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#!/bin/sh

# By: Brandon Mitchell <public@bmitch.net>
# License: MIT
# Source repo: https://github.com/sudo-bmitch/docker-stack-wait

set -e
trap "{ exit 1; }" TERM INT
opt_h=0
opt_r=0
opt_s=5
opt_t=3600
start_epoc=$(date +%s)

usage() {
echo "$(basename $0) [opts] stack_name"
echo " -f filter: only wait for services matching filter, may be passed multiple"
echo " times, see docker stack services for the filter syntax"
echo " -h: this help message"
echo " -n name: only wait for specific service names, overrides any filters,"
echo " may be passed multiple times, do not include the stack name prefix"
echo " -r: treat a rollback as successful"
echo " -s sec: frequency to poll service state (default $opt_s sec)"
echo " -t sec: timeout to stop waiting"
[ "$opt_h" = "1" ] && exit 0 || exit 1
}
check_timeout() {
# timeout when a timeout is defined and we will exceed the timeout after the
# next sleep completes
if [ "$opt_t" -gt 0 ]; then
cur_epoc=$(date +%s)
cutoff_epoc=$(expr ${start_epoc} + $opt_t - $opt_s)
if [ "$cur_epoc" -gt "$cutoff_epoc" ]; then
echo "Error: Timeout exceeded"
exit 1
fi
fi
}
get_service_ids() {
if [ -n "$opt_n" ]; then
service_list=""
for name in $opt_n; do
service_list="${service_list:+${service_list} }${stack_name}_${name}"
done
docker service inspect --format '{{.ID}}' ${service_list}
else
docker stack services ${opt_f} -q "${stack_name}"
fi
}
service_state() {
# output the state when it changes from the last state for the service
service=$1
# strip any invalid chars from service name for caching state
service_safe=$(echo "$service" | sed 's/[^A-Za-z0-9_]/_/g')
state=$2
if eval [ \"\$cache_${service_safe}\" != \"\$state\" ]; then
echo "Service $service state: $state"
eval cache_${service_safe}=\"\$state\"
fi
}

while getopts 'f:hn:rs:t:' opt; do
case $opt in
f) opt_f="${opt_f:+${opt_f} }-f $OPTARG";;
h) opt_h=1;;
n) opt_n="${opt_n:+${opt_n} } $OPTARG";;
r) opt_r=1;;
s) opt_s="$OPTARG";;
t) opt_t="$OPTARG";;
esac
done
shift $(expr $OPTIND - 1)

if [ $# -ne 1 -o "$opt_h" = "1" -o "$opt_s" -le "0" ]; then
usage
fi

stack_name=$1

# 0 = running, 1 = success, 2 = error
stack_done=0
while [ "$stack_done" != "1" ]; do
stack_done=1
# run get_service_ids outside of the for loop to catch errors
service_ids=$(get_service_ids)
for service_id in ${service_ids}; do
service_done=1
service=$(docker service inspect --format '{{.Spec.Name}}' "$service_id")

# hardcode a "new" state when UpdateStatus is not defined
state=$(docker service inspect -f '{{if .UpdateStatus}}{{.UpdateStatus.State}}{{else}}new{{end}}' "$service_id")

# check for failed update states
case "$state" in
paused|rollback_paused)
service_done=2
;;
rollback_*)
if [ "$opt_r" = "0" ]; then
service_done=2
fi
;;
esac

# identify/report current state
if [ "$service_done" != "2" ]; then
replicas=$(docker service ls --format '{{.Replicas}}' --filter "id=$service_id" | cut -d' ' -f1)
current=$(echo "$replicas" | cut -d/ -f1)
target=$(echo "$replicas" | cut -d/ -f2)
if [ "$current" != "$target" ]; then
# actively replicating service
service_done=0
state="replicating $replicas"
fi
fi
service_state "$service" "$state"

# check for states that indicate an update is done
if [ "$service_done" = "1" ]; then
case "$state" in
new|completed|rollback_completed)
service_done=1
;;
*)
# any other state is unknown, not necessarily finished
service_done=0
;;
esac
fi

# update stack done state
if [ "$service_done" = "2" ]; then
# error condition
stack_done=2
elif [ "$service_done" = "0" -a "$stack_done" = "1" ]; then
# only go to an updating state if not in an error state
stack_done=0
fi
done
if [ "$stack_done" = "2" ]; then
echo "Error: This deployment will not complete"
exit 1
fi
if [ "$stack_done" != "1" ]; then
check_timeout
sleep "${opt_s}"
fi
done
Loading

0 comments on commit 13fdb91

Please sign in to comment.