Skip to content

fix: add missing slog import and remove unused imports #67

fix: add missing slog import and remove unused imports

fix: add missing slog import and remove unused imports #67

Workflow file for this run

name: CI/CD Focus Project
on:
push:
branches:
- dev
- stage
- prod
workflow_dispatch:
permissions:
contents: read
packages: write
concurrency:
group: focus-${{ github.ref }}
cancel-in-progress: true
jobs:
compose-validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create placeholder env files
run: |
touch .env
mkdir -p backend
touch backend/.env
- name: Validate local Docker Compose
run: docker compose config
- name: Validate server Docker Compose
env:
BACKEND_IMAGE: ghcr.io/example/focus/backend:test
DATABASE_URL: postgres://user:pass@db:5432/focus?sslmode=disable
JWT_SECRET: test-secret
run: docker compose -f deploy/docker-compose.server.yml config
build-and-push-images:
needs: compose-validate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push backend
uses: docker/build-push-action@v6
with:
context: ./backend
push: true
tags: |
ghcr.io/${{ github.repository }}/backend:${{ github.sha }}
ghcr.io/${{ github.repository }}/backend:latest
cache-from: type=gha
cache-to: type=gha,mode=max
deploy-to-server:
needs: build-and-push-images
if: github.ref == 'refs/heads/prod'
runs-on: ubuntu-latest
environment:
name: production
env:
SERVER_HOST: ${{ secrets.SERVER_IP }}
SERVER_USER: ${{ secrets.SERVER_USER }}
SERVER_PORT: ${{ secrets.SERVER_PORT }}
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }}
BACKEND_IMAGE: ghcr.io/${{ github.repository }}/backend:${{ github.sha }}
GITHUB_ACTOR: ${{ github.actor }}
steps:
- uses: actions/checkout@v4
- name: Normalize deploy settings
run: |
server_host="$(printf '%s' "${SERVER_HOST}" | tr -d '\r' | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')"
server_user="$(printf '%s' "${SERVER_USER}" | tr -d '\r' | sed -E 's/[[:space:]]+$//')"
server_port="$(printf '%s' "${SERVER_PORT}" | tr -d '\r' | sed -E 's/[[:space:]]+$//')"
deploy_path="$(printf '%s' "${DEPLOY_PATH}" | tr -d '\r' | sed -E 's/[[:space:]]+$//')"
server_host="${server_host#http://}"
server_host="${server_host#https://}"
server_host="${server_host%%/*}"
if [[ "$server_host" == *"@"* ]]; then
[ -z "$server_user" ] && server_user="${server_host%@*}"
server_host="${server_host#*@}"
fi
if printf '%s' "$server_host" | grep -Eq '^[^:/]+:[0-9]+$'; then
[ -z "$server_port" ] && server_port="${server_host##*:}"
server_host="${server_host%:*}"
fi
: "${server_user:=devops}"
: "${server_port:=22}"
: "${deploy_path:=/opt/focus}"
[ -z "$server_host" ] && { echo "SERVER_IP secret is empty or invalid" >&2; exit 1; }
[ -z "$SSH_PRIVATE_KEY" ] && { echo "SSH_PRIVATE_KEY secret is empty" >&2; exit 1; }
[ -z "$GHCR_TOKEN" ] && { echo "GHCR_TOKEN secret is empty" >&2; exit 1; }
{
echo "SERVER_HOST=$server_host"
echo "SERVER_USER=$server_user"
echo "SERVER_PORT=$server_port"
echo "DEPLOY_PATH=$deploy_path"
} >> "$GITHUB_ENV"
- name: Prepare SSH
run: |
mkdir -p ~/.ssh
printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -T 10 -p "$SERVER_PORT" -H "$SERVER_HOST" >> ~/.ssh/known_hosts || {
echo "Could not read SSH host key from $SERVER_HOST:$SERVER_PORT" >&2
echo "Check that SERVER_IP and SERVER_PORT point to the SSH service, not a web panel port." >&2
exit 1
}
- name: Upload deployment files
run: |
ssh -p "$SERVER_PORT" "$SERVER_USER@$SERVER_HOST" "mkdir -p '$DEPLOY_PATH'"
scp -P "$SERVER_PORT" deploy/docker-compose.server.yml "$SERVER_USER@$SERVER_HOST:$DEPLOY_PATH/docker-compose.yml"
- name: Render and upload env file
run: |
cat > deploy.env <<EOF
BACKEND_IMAGE=${BACKEND_IMAGE}
DOCKER_NETWORK=${{ secrets.DOCKER_NETWORK }}
BACKEND_PORT=${{ secrets.BACKEND_PORT }}
PORT=${{ secrets.PORT }}
DATABASE_URL=${{ secrets.DATABASE_URL }}
CORS_ORIGIN=${{ secrets.CORS_ORIGIN }}
JWT_SECRET=${{ secrets.JWT_SECRET }}
RESEND_API_KEY=${{ secrets.RESEND_API_KEY }}
RESEND_FROM_EMAIL=${{ secrets.RESEND_FROM_EMAIL }}
APP_URL=${{ secrets.APP_URL }}
BACKEND_URL=${{ secrets.BACKEND_URL }}
EOF
scp -P "$SERVER_PORT" deploy.env "$SERVER_USER@$SERVER_HOST:$DEPLOY_PATH/.env"
- name: Deploy on server
run: |
ssh -p "$SERVER_PORT" "$SERVER_USER@$SERVER_HOST" bash -s <<EOF
set -euo pipefail
cd "$DEPLOY_PATH"
echo "$GHCR_TOKEN" | docker login ghcr.io -u "$GITHUB_ACTOR" --password-stdin
docker compose --env-file .env pull backend migrate
docker compose --env-file .env run --rm migrate
docker compose --env-file .env up -d backend
docker image prune -f
EOF