Skip to content

Merge pull request #326 from DevKor-github/feature/jwt-auth-query-cou… #32

Merge pull request #326 from DevKor-github/feature/jwt-auth-query-cou…

Merge pull request #326 from DevKor-github/feature/jwt-auth-query-cou… #32

Workflow file for this run

name: Deploy Dev
on:
workflow_dispatch:
push:
branches:
- dev
permissions:
contents: read
packages: write
concurrency:
group: deploy-development
cancel-in-progress: true
env:
REGISTRY: ghcr.io
IMAGE_NAME: devkor-github/ontime-back
IMAGE_TAG: dev-${{ github.sha }}
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v6
with:
context: ./ontime-back
file: ./ontime-back/Dockerfile
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev-latest
cache-from: type=gha
cache-to: type=gha,mode=max
deploy-to-remote-pc:
needs: build-and-push
runs-on: ubuntu-latest
environment: development
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Prepare dev deploy directory
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.REMOTE_SSH_KEY }}
script: |
set -eu
[ "${{ secrets.DEPLOY_ENVIRONMENT }}" = "development" ] || {
echo "Invalid development deployment secret scope." >&2
exit 1
}
mkdir -p "${{ secrets.DEPLOY_DIR }}"
- name: Upload compose files to remote PC
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.REMOTE_SSH_KEY }}
source: "ontime-back/docker-compose.yml,ontime-back/docker-compose.dev.yml"
target: ${{ secrets.DEPLOY_DIR }}
strip_components: 1
- name: Pull image and restart dev containers
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.REMOTE_SSH_KEY }}
script: |
set -eu
DEPLOY_DIR="${{ secrets.DEPLOY_DIR }}"
CONTAINER_NAME="ontime-dev-container"
mkdir -p "$DEPLOY_DIR"
cd "$DEPLOY_DIR"
umask 077
cat > .env <<'EOF'
IMAGE_TAG=${{ env.IMAGE_TAG }}
BACKEND_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
BACKEND_CONTAINER_NAME=ontime-dev-container
BACKEND_HTTP_PORT=${{ secrets.BACKEND_HTTP_PORT }}
BACKEND_MEMORY_LIMIT=${{ secrets.BACKEND_MEMORY_LIMIT }}
BACKEND_CPU_LIMIT=${{ secrets.BACKEND_CPU_LIMIT }}
SERVER_PORT=8080
SPRING_PROFILES_ACTIVE=dev
JAVA_TOOL_OPTIONS=-XX:InitialRAMPercentage=50.0 -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/./urandom
MYSQL_DATABASE=${{ secrets.MYSQL_DATABASE }}
MYSQL_USER=${{ secrets.MYSQL_USER }}
MYSQL_PASSWORD=${{ secrets.MYSQL_PASSWORD }}
MYSQL_ROOT_PASSWORD=${{ secrets.MYSQL_ROOT_PASSWORD }}
SPRING_APPLICATION_NAME=${{ secrets.SPRING_APPLICATION_NAME }}
SPRING_DATASOURCE_URL=${{ secrets.SPRING_DATASOURCE_URL }}
SPRING_DATASOURCE_USERNAME=${{ secrets.SPRING_DATASOURCE_USERNAME }}
SPRING_DATASOURCE_PASSWORD=${{ secrets.SPRING_DATASOURCE_PASSWORD }}
SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver
SPRING_JPA_HIBERNATE_DDL_AUTO=validate
SPRING_FLYWAY_ENABLED=true
SPRING_FLYWAY_BASELINE_ON_MIGRATE=false
JWT_SECRET_KEY=${{ secrets.JWT_SECRETKEY }}
JWT_ACCESS_EXPIRATION=${{ secrets.JWT_ACCESS_EXPIRATION }}
JWT_REFRESH_EXPIRATION=${{ secrets.JWT_REFRESH_EXPIRATION }}
JWT_ACCESS_HEADER=${{ secrets.JWT_ACCESS_HEADER }}
JWT_REFRESH_HEADER=${{ secrets.JWT_REFRESH_HEADER }}
GOOGLE_WEB_CLIENT_ID=${{ secrets.GOOGLE_WEB_CLIENT_ID }}
GOOGLE_APP_CLIENT_ID=${{ secrets.GOOGLE_APP_CLIENT_ID }}
APPLE_CLIENT_ID=${{ secrets.APPLE_CLIENT_ID }}
APPLE_TEAM_ID=${{ secrets.APPLE_TEAM_ID }}
APPLE_LOGIN_KEY=${{ secrets.APPLE_LOGIN_KEY }}
APPLE_PRIVATE_KEY_BASE64=${{ secrets.APPLE_PRIVATE_KEY_BASE64 }}
FEATURE_APPLE_LOGIN_ENABLED=${{ secrets.FEATURE_APPLE_LOGIN_ENABLED }}
FIREBASE_CREDENTIALS_BASE64=${{ secrets.FIREBASE_CREDENTIALS_BASE64 }}
EOF
fail_deploy() {
echo "Unsafe development configuration: $1" >&2
exit 1
}
get_env_value() {
grep -E "^$1=" .env | tail -n 1 | cut -d= -f2-
}
DB_URL="$(get_env_value SPRING_DATASOURCE_URL)"
BACKEND_HTTP_BIND="$(get_env_value BACKEND_HTTP_PORT)"
DB_URL_NO_PREFIX="${DB_URL#jdbc:mysql://}"
[ "$DB_URL_NO_PREFIX" != "$DB_URL" ] || fail_deploy "SPRING_DATASOURCE_URL must start with jdbc:mysql://."
DB_ADDRESS="${DB_URL_NO_PREFIX%%/*}"
DB_HOST="${DB_ADDRESS%%:*}"
DB_NAME_AND_QUERY="${DB_URL_NO_PREFIX#*/}"
DB_NAME="$(printf '%s' "$DB_NAME_AND_QUERY" | sed 's/[?;].*$//')"
[ "${{ secrets.DEPLOY_ENVIRONMENT }}" = "development" ] || fail_deploy "DEPLOY_ENVIRONMENT must be development."
[ -n "$DB_URL" ] || fail_deploy "SPRING_DATASOURCE_URL is required."
[ "$DB_HOST" = "mysql" ] || fail_deploy "SPRING_DATASOURCE_URL must point to the Compose mysql service."
[ "$DB_NAME" = "ontime_dev" ] || fail_deploy "SPRING_DATASOURCE_URL must use the ontime_dev database."
[ "$BACKEND_HTTP_BIND" != "127.0.0.1:8080" ] || fail_deploy "BACKEND_HTTP_PORT looks like the production bind."
echo "${{ secrets.GITHUB_TOKEN }}" | sudo docker login ghcr.io -u "${{ github.actor }}" --password-stdin
if sudo docker compose version >/dev/null 2>&1; then
COMPOSE="sudo docker compose"
else
COMPOSE="sudo docker-compose"
fi
$COMPOSE -f docker-compose.yml -f docker-compose.dev.yml pull
$COMPOSE -f docker-compose.yml -f docker-compose.dev.yml up -d mysql
DB_ROOT_PASSWORD="$(get_env_value MYSQL_ROOT_PASSWORD)"
DB_NAME="$(get_env_value MYSQL_DATABASE)"
for attempt in $(seq 1 30); do
MYSQL_STATUS="$(sudo docker inspect -f '{{.State.Health.Status}}' ontime-dev-mysql 2>/dev/null || true)"
if [ "$MYSQL_STATUS" = "healthy" ]; then
echo "MySQL container is healthy."
break
fi
echo "Waiting for healthy MySQL status; current status: ${MYSQL_STATUS:-unknown}"
sleep 5
done
MYSQL_STATUS="$(sudo docker inspect -f '{{.State.Health.Status}}' ontime-dev-mysql 2>/dev/null || true)"
[ "$MYSQL_STATUS" = "healthy" ] || fail_deploy "MySQL container did not become healthy."
FAILED_V17_COUNT="$(
sudo docker exec ontime-dev-mysql \
mysql -u root --password="$DB_ROOT_PASSWORD" "$DB_NAME" \
-Nse "SELECT COUNT(*) FROM flyway_schema_history WHERE version = '17' AND success = 0;" 2>/dev/null || echo 0
)"
if [ "$FAILED_V17_COUNT" != "0" ]; then
echo "Repairing failed development Flyway migration V17 before backend startup."
sudo docker exec ontime-dev-mysql \
mysql -u root --password="$DB_ROOT_PASSWORD" "$DB_NAME" \
-e "DROP TABLE IF EXISTS user_refresh_token; DELETE FROM flyway_schema_history WHERE version = '17' AND success = 0;"
fi
$COMPOSE -f docker-compose.yml -f docker-compose.dev.yml up -d --remove-orphans
HEALTHY=false
for attempt in $(seq 1 30); do
STATUS="$(sudo docker inspect -f '{{.State.Health.Status}}' "$CONTAINER_NAME" 2>/dev/null || true)"
if [ "$STATUS" = "healthy" ]; then
echo "Container is healthy."
HEALTHY=true
break
fi
echo "Waiting for healthy container status; current status: ${STATUS:-unknown}"
sleep 5
done
if [ "$HEALTHY" != "true" ]; then
sudo docker logs --tail=200 "$CONTAINER_NAME" || true
exit 1
fi
curl -fsS "http://127.0.0.1:${{ secrets.BACKEND_HTTP_PORT }}/actuator/health/readiness"