name: Build production Docker image on: push: branches: - main workflow_dispatch: jobs: build-backend-image: runs-on: wsl2-docker-host env: IMAGE_NAME: eryao-backend IMAGE_SIZE_LIMIT_BYTES: 500000000 RUNNER_REPO_CACHE: /home/zl/Code/eryao steps: - name: Check out repository run: | set -euo pipefail git -C "${RUNNER_REPO_CACHE}" fetch --no-tags origin "${GITHUB_SHA}" git init . git remote add origin "${RUNNER_REPO_CACHE}/.git" git fetch --no-tags --depth=1 origin "${GITHUB_SHA}" git checkout --detach FETCH_HEAD - name: Validate Volcengine registry configuration env: VOLCENGINE_REGISTRY_USERNAME: ${{ secrets.VOLCENGINE_REGISTRY_USERNAME }} VOLCENGINE_REGISTRY_PASSWORD: ${{ secrets.VOLCENGINE_REGISTRY_PASSWORD }} run: | set -euo pipefail test -n "${VOLCENGINE_REGISTRY_USERNAME}" test -n "${VOLCENGINE_REGISTRY_PASSWORD}" - name: Build backend production image run: | set -euo pipefail docker buildx build \ --provenance=false \ --load \ --file backend/Dockerfile \ --tag ${IMAGE_NAME}:prod-${GITHUB_SHA} \ . - name: Check image size budget run: | set -euo pipefail image_size_bytes="$(docker image inspect ${IMAGE_NAME}:prod-${GITHUB_SHA} --format '{{.Size}}')" echo "Image size: ${image_size_bytes} bytes" if [ "${image_size_bytes}" -gt "${IMAGE_SIZE_LIMIT_BYTES}" ]; then echo "Image exceeds ${IMAGE_SIZE_LIMIT_BYTES} bytes" >&2 exit 1 fi - name: Smoke test backend image run: | set -euo pipefail docker run --rm \ -e ERYAO_RUNTIME__ENVIRONMENT=prod \ -e ERYAO_SUPABASE__PUBLIC_URL=http://localhost:8001 \ -e ERYAO_POINTS_POLICY__REGISTER_BONUS_HMAC_KEY=ci-smoke-test-key \ --entrypoint python \ ${IMAGE_NAME}:prod-${GITHUB_SHA} \ -c "import app; print(app.app.title)" - name: Push backend image to Volcengine registry env: VOLCENGINE_REGISTRY_USERNAME: ${{ secrets.VOLCENGINE_REGISTRY_USERNAME }} VOLCENGINE_REGISTRY_PASSWORD: ${{ secrets.VOLCENGINE_REGISTRY_PASSWORD }} run: | set -euo pipefail export HTTP_PROXY= HTTPS_PROXY= ALL_PROXY= http_proxy= https_proxy= all_proxy= volcengine_registry="linksy-docker-cn-beijing.cr.volces.com" volcengine_image="${volcengine_registry}/eryao/${IMAGE_NAME}" retry() { for attempt in 1 2 3; do if "$@"; then return 0 fi if [ "${attempt}" -eq 3 ]; then return 1 fi sleep "$((attempt * 5))" done } volcengine_login() { printf '%s' "${VOLCENGINE_REGISTRY_PASSWORD}" \ | docker login --username "${VOLCENGINE_REGISTRY_USERNAME}" --password-stdin "${volcengine_registry}" } retry volcengine_login docker tag "${IMAGE_NAME}:prod-${GITHUB_SHA}" "${volcengine_image}:${GITHUB_SHA}" docker tag "${IMAGE_NAME}:prod-${GITHUB_SHA}" "${volcengine_image}:latest" retry docker push "${volcengine_image}:${GITHUB_SHA}" retry docker push "${volcengine_image}:latest" deploy-production: needs: build-backend-image runs-on: wsl2-docker-host steps: - name: Validate deploy configuration env: DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }} DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} DEPLOY_USER: ${{ secrets.DEPLOY_USER }} VOLCENGINE_REGISTRY_USERNAME: ${{ secrets.VOLCENGINE_REGISTRY_USERNAME }} VOLCENGINE_REGISTRY_PASSWORD: ${{ secrets.VOLCENGINE_REGISTRY_PASSWORD }} run: | set -euo pipefail test -n "${DEPLOY_SSH_KEY}" test -n "${DEPLOY_HOST}" test -n "${DEPLOY_USER}" test -n "${VOLCENGINE_REGISTRY_USERNAME}" test -n "${VOLCENGINE_REGISTRY_PASSWORD}" - name: Deploy production server env: DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} DEPLOY_USER: ${{ secrets.DEPLOY_USER }} DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }} VOLCENGINE_REGISTRY_USERNAME: ${{ secrets.VOLCENGINE_REGISTRY_USERNAME }} VOLCENGINE_REGISTRY_PASSWORD: ${{ secrets.VOLCENGINE_REGISTRY_PASSWORD }} run: | set -euo pipefail install -m 700 -d ~/.ssh printf '%s\n' "${DEPLOY_SSH_KEY}" > ~/.ssh/eryao_deploy_key chmod 600 ~/.ssh/eryao_deploy_key ssh-keyscan -H "${DEPLOY_HOST}" >> ~/.ssh/known_hosts remote_registry_username="$(printf '%q' "${VOLCENGINE_REGISTRY_USERNAME}")" remote_registry_password="$(printf '%q' "${VOLCENGINE_REGISTRY_PASSWORD}")" ssh -i ~/.ssh/eryao_deploy_key \ -o IdentitiesOnly=yes \ "${DEPLOY_USER}@${DEPLOY_HOST}" \ "VOLCENGINE_REGISTRY_USERNAME=${remote_registry_username} VOLCENGINE_REGISTRY_PASSWORD=${remote_registry_password} bash -se" <<'REMOTE' set -euo pipefail cd ~/deploy test -f ./.env for required_key in ERYAO_DATABASE__HOST ERYAO_DATABASE__PORT ERYAO_DATABASE__NAME ERYAO_DATABASE__USER ERYAO_DATABASE__PASSWORD; do grep -q "^${required_key}=" ./.env done sed -i \ -e 's#^ERYAO_DATABASE__HOST=.*#ERYAO_DATABASE__HOST=pgm-j6ckocm243gd2997.pg.cnhk.rds.aliyuncs.com#' \ -e 's#^ERYAO_DATABASE__PORT=.*#ERYAO_DATABASE__PORT=5432#' \ -e 's#^ERYAO_DATABASE__NAME=.*#ERYAO_DATABASE__NAME=supabase_db#' \ -e 's#^ERYAO_DATABASE__USER=.*#ERYAO_DATABASE__USER=postgres#' \ ./.env set -a . ./.env set +a volcengine_registry="linksy-docker-cn-beijing.cr.volces.com" printf '%s' "${VOLCENGINE_REGISTRY_PASSWORD}" \ | sudo docker login --username "${VOLCENGINE_REGISTRY_USERNAME}" --password-stdin "${volcengine_registry}" sudo docker compose --env-file ./.env -f docker-compose.prod.yml --profile workers pull sudo docker compose --env-file ./.env -f docker-compose.prod.yml --profile workers up -d --remove-orphans for attempt in $(seq 1 12); do if curl -fsS "http://127.0.0.1:${ERYAO_WEB__PORT:-5775}/health"; then break fi if [ "${attempt}" -eq 12 ]; then sudo docker compose --env-file ./.env -f docker-compose.prod.yml --profile workers ps sudo docker logs --tail 200 eryao-prod-backend || true exit 1 fi sleep 5 done sudo docker image prune -af --filter "until=168h" REMOTE