sass-imobiliaria/.forgejo/workflows/deploy.yml
MatheusAlves96 385a2f78e8
Some checks failed
CI/CD → Deploy via SSH / Build & Push Docker Images (push) Successful in 27s
CI/CD → Deploy via SSH / Deploy via SSH (push) Successful in 1m0s
CI/CD → Deploy via SSH / Validate HTTPS & Endpoints (push) Failing after 46s
fix: create deploy dir before scp
2026-04-21 01:18:42 -03:00

195 lines
8 KiB
YAML

name: CI/CD → Deploy via SSH
on:
push:
branches:
- main
env:
REGISTRY: ${{ vars.REGISTRY }}
BACKEND_IMAGE: saas-imobiliaria-backend
FRONTEND_IMAGE: saas-imobiliaria-frontend
jobs:
# ────────────────────────────────────────────────────────────────────────────
# 1. BUILD & PUSH IMAGES (Docker-in-Docker)
# ────────────────────────────────────────────────────────────────────────────
build:
name: Build & Push Docker Images
runs-on: docker
container:
image: node:20-alpine
options: --privileged
steps:
- name: Install git
run: apk add --no-cache git docker-cli
- name: Checkout
uses: actions/checkout@v4
- name: Set image tag
id: tag
run: echo "IMAGE_TAG=$(echo $GITHUB_SHA | cut -c1-8)" >> $GITHUB_OUTPUT
- name: Log in to registry
run: |
echo "${{ secrets.REGISTRY_PASSWORD }}" | \
docker login ${{ vars.REGISTRY }} -u ${{ secrets.REGISTRY_USER }} --password-stdin
- name: Build & push backend
run: |
TAG=${{ steps.tag.outputs.IMAGE_TAG }}
docker build \
-f backend/Dockerfile.prod \
-t ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE }}:${TAG} \
-t ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE }}:latest \
./backend
docker push ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE }}:${TAG}
docker push ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE }}:latest
- name: Build & push frontend
run: |
TAG=${{ steps.tag.outputs.IMAGE_TAG }}
docker build \
-f frontend/Dockerfile.prod \
--build-arg VITE_API_URL=https://${{ vars.DOMAIN }}/api \
-t ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE }}:${TAG} \
-t ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE }}:latest \
./frontend
docker push ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE }}:${TAG}
docker push ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE }}:latest
outputs:
image_tag: ${{ steps.tag.outputs.IMAGE_TAG }}
# ────────────────────────────────────────────────────────────────────────────
# 2. DEPLOY VIA SSH
# ────────────────────────────────────────────────────────────────────────────
deploy:
name: Deploy via SSH
runs-on: docker
needs: build
container:
image: node:20-alpine
steps:
- name: Install SSH tools
run: apk add --no-cache openssh-client
- name: Checkout
uses: actions/checkout@v4
- name: Setup SSH key
run: |
mkdir -p ~/.ssh
printf '%s\n' "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan -p ${{ vars.SSH_PORT }} ${{ vars.SSH_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
- name: Copy compose file to server
run: |
ssh -i ~/.ssh/deploy_key -p ${{ vars.SSH_PORT }} \
${{ vars.SSH_USER }}@${{ vars.SSH_HOST }} \
"mkdir -p /opt/saas-imobiliaria"
scp -i ~/.ssh/deploy_key -P ${{ vars.SSH_PORT }} \
docker-compose.prod.yml \
${{ vars.SSH_USER }}@${{ vars.SSH_HOST }}:/opt/saas-imobiliaria/docker-compose.prod.yml
- name: Deploy Swarm stack on server
env:
IMAGE_TAG: ${{ needs.build.outputs.image_tag }}
run: |
ssh -i ~/.ssh/deploy_key -p ${{ vars.SSH_PORT }} \
${{ vars.SSH_USER }}@${{ vars.SSH_HOST }} \
"set -e
mkdir -p /opt/saas-imobiliaria
cd /opt/saas-imobiliaria
echo '${{ secrets.REGISTRY_PASSWORD }}' | \
docker login ${{ vars.REGISTRY }} -u ${{ secrets.REGISTRY_USER }} --password-stdin
docker pull ${{ vars.REGISTRY }}/saas-imobiliaria-backend:${IMAGE_TAG}
docker pull ${{ vars.REGISTRY }}/saas-imobiliaria-frontend:${IMAGE_TAG}
IMAGE_TAG=${IMAGE_TAG} \
REGISTRY=${{ vars.REGISTRY }} \
DOMAIN=${{ vars.DOMAIN }} \
POSTGRES_DB=${{ secrets.POSTGRES_DB }} \
POSTGRES_USER=${{ secrets.POSTGRES_USER }} \
POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} \
SECRET_KEY=${{ secrets.SECRET_KEY }} \
JWT_SECRET_KEY=${{ secrets.JWT_SECRET_KEY }} \
docker stack deploy \
--compose-file docker-compose.prod.yml \
--with-registry-auth \
--prune \
saas-imobiliaria
sleep 15
docker stack services saas-imobiliaria"
# ────────────────────────────────────────────────────────────────────────────
# 3. HEALTH CHECK
# ────────────────────────────────────────────────────────────────────────────
healthcheck:
name: Validate HTTPS & Endpoints
runs-on: docker
needs: [build, deploy]
container:
image: node:20-alpine
steps:
- name: Install tools
run: apk add --no-cache curl openssl
- name: Wait for stack to stabilize
run: sleep 40
- name: Frontend HTTPS
run: |
S=$(curl -s -o /dev/null -w "%{http_code}" --max-time 15 "https://${{ vars.DOMAIN }}")
echo "Frontend: $S"
[ "$S" = "200" ] || (echo "❌ Frontend falhou ($S)" && exit 1)
- name: HTTP → HTTPS redirect
run: |
L=$(curl -s -o /dev/null -w "%{redirect_url}" --max-time 10 "http://${{ vars.DOMAIN }}")
echo "Redirect: $L"
echo "$L" | grep -q "https://" || (echo "❌ Redirect ausente" && exit 1)
- name: Backend /api/health
run: |
R=$(curl -s --max-time 15 "https://${{ vars.DOMAIN }}/api/health")
S=$(curl -s -o /dev/null -w "%{http_code}" --max-time 15 "https://${{ vars.DOMAIN }}/api/health")
echo "Health: $S → $R"
[ "$S" = "200" ] || (echo "❌ Health falhou ($S)" && exit 1)
echo "$R" | grep -q '"db": "ok"' || (echo "❌ DB não conectado" && exit 1)
- name: TLS certificate validity
run: |
EXPIRY=$(echo | openssl s_client -connect ${{ vars.DOMAIN }}:443 \
-servername ${{ vars.DOMAIN }} 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
echo "Expira: $EXPIRY"
DAYS=$(( ($(date -d "$EXPIRY" +%s) - $(date +%s)) / 86400 ))
echo "Dias restantes: $DAYS"
[ "$DAYS" -gt 7 ] || (echo "❌ Cert expira em $DAYS dias" && exit 1)
- name: GET /api/properties
run: |
S=$(curl -s -o /dev/null -w "%{http_code}" --max-time 15 "https://${{ vars.DOMAIN }}/api/properties?limit=1")
echo "Properties: $S"
[ "$S" = "200" ] || (echo "❌ Properties falhou ($S)" && exit 1)
- name: Version tag matches
run: |
R=$(curl -s --max-time 10 "https://${{ vars.DOMAIN }}/api/version")
echo "Version: $R"
echo "$R" | grep -q "${{ needs.build.outputs.image_tag }}" \
|| (echo "❌ Tag não confere — esperado ${{ needs.build.outputs.image_tag }}" && exit 1)
- name: Summary
run: |
echo "✅ Todos os checks passaram!"
echo " → https://${{ vars.DOMAIN }} está no ar com tag ${{ needs.build.outputs.image_tag }}"