Merge branch 'dev' #1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI/CD | |
| on: | |
| # Roda em PRs para master e em pushes (merge) para master | |
| pull_request: | |
| branches: [ "master" ] | |
| push: | |
| branches: [ "master" ] | |
| workflow_dispatch: | |
| # Permissões mínimas + permissão para publicar pacotes no GHCR | |
| permissions: | |
| contents: read | |
| packages: write | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| tests: | |
| name: Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| # Baixa o código | |
| - name: Check out code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # melhor para versionamento (tags/commits) | |
| # Configura Java 21 (Temurin) para testes | |
| - name: Set up Java 21 (Temurin) | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: '21' | |
| # Configura cache do Gradle automaticamente | |
| - name: Setup Gradle (with caching) | |
| uses: gradle/actions/setup-gradle@v3 | |
| # Garante permissão de execução para o wrapper | |
| - name: Make Gradle wrapper executable | |
| run: chmod +x gradlew | |
| # Executa a suíte de testes | |
| - name: Run unit tests | |
| run: ./gradlew --no-daemon clean test | |
| # Publica relatórios de testes sempre, mesmo em caso de falha | |
| - name: Upload test reports (always) | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-reports | |
| path: | | |
| build/test-results/test | |
| build/reports/tests/test | |
| build_and_publish: | |
| name: Build, Docker and Push | |
| runs-on: ubuntu-latest | |
| needs: tests # Só executa se os testes passarem | |
| if: ${{ needs.tests.result == 'success' }} | |
| env: | |
| # Nome base das imagens (pode ajustar conforme necessidade) | |
| APP_NAME: espacogeek-backend | |
| DB_IMAGE_GHCR: ghcr.io/${{ github.repository_owner }}/espacogeek-db | |
| # Variáveis do .env (use Secrets no GitHub Actions) | |
| SPRING_DATASOURCE_URL: ${{ secrets.SPRING_DATASOURCE_URL }} | |
| SPRING_DATASOURCE_USERNAME: ${{ secrets.SPRING_DATASOURCE_USERNAME }} | |
| SPRING_DATASOURCE_PASSWORD: ${{ secrets.SPRING_DATASOURCE_PASSWORD }} | |
| SPRING_MVC_CORS_ALLOWED_ORIGINS: ${{ secrets.SPRING_MVC_CORS_ALLOWED_ORIGINS }} | |
| SECURITY_JWT_ISSUER: ${{ secrets.SECURITY_JWT_ISSUER }} | |
| SECURITY_JWT_EXPIRATION_MS: ${{ secrets.SECURITY_JWT_EXPIRATION_MS }} | |
| SECURITY_JWT_SECRET: ${{ secrets.SECURITY_JWT_SECRET }} | |
| SAMESITE_WHEN_SAME_SITE: ${{ secrets.SAMESITE_WHEN_SAME_SITE }} | |
| ALLOWED_ORIGINS: ${{ secrets.ALLOWED_ORIGINS }} | |
| MAIL_HOST: ${{ secrets.MAIL_HOST }} | |
| MAIL_PORT: ${{ secrets.MAIL_PORT }} | |
| MAIL_USERNAME: ${{ secrets.MAIL_USERNAME }} | |
| MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }} | |
| APP_NAME_ENV: ${{ secrets.APP_NAME }} | |
| FRONTEND_URL: ${{ secrets.FRONTEND_URL }} | |
| steps: | |
| # Baixa o código (com histórico completo para gerar versões) | |
| - name: Check out code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| # Configura Java 21 (Temurin) para build do WAR | |
| - name: Set up Java 21 (Temurin) | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: '21' | |
| # Configura cache do Gradle para builds | |
| - name: Setup Gradle (with caching) | |
| uses: gradle/actions/setup-gradle@v3 | |
| # Garante permissão de execução para o wrapper (em runners Linux) | |
| - name: Make Gradle wrapper executable | |
| run: chmod +x gradlew | |
| # Lê a versão do projeto definida no build.gradle (project.version) | |
| - name: Read Gradle project version | |
| id: gradle_version | |
| shell: bash | |
| run: | | |
| echo "version=$(./gradlew -q properties | sed -n 's/^version: \(.*\)$/\1/p')" >> "$GITHUB_OUTPUT" | |
| # Gera tags de versão para as imagens Docker e artefatos | |
| # - Em PR: pr-<numero>-<sha-curta>, v<versao>-pr-<numero> | |
| # - Em push para master: latest, sha-<sha-curta>, <yyyyMMdd>, v<versao> | |
| - name: Compute version tags | |
| id: vars | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| SHORT_SHA="$(git rev-parse --short HEAD)" | |
| DATE_TAG="$(date +%Y%m%d)" | |
| VERSION="${{ steps.gradle_version.outputs.version }}" | |
| if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then | |
| PR_NUMBER="${{ github.event.pull_request.number }}" | |
| TAGS="pr-${PR_NUMBER}-${SHORT_SHA},v${VERSION}-pr-${PR_NUMBER}" | |
| SHOULD_PUSH="false" # não faz push em PRs por padrão | |
| else | |
| # push para master | |
| # Inclui tag 'last-release' (Docker não permite espaço em tags) | |
| TAGS="latest,last-release,sha-${SHORT_SHA},${DATE_TAG},v${VERSION}" | |
| SHOULD_PUSH="true" | |
| fi | |
| echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT" | |
| echo "date_tag=${DATE_TAG}" >> "$GITHUB_OUTPUT" | |
| echo "tags_csv=${TAGS}" >> "$GITHUB_OUTPUT" | |
| echo "should_push=${SHOULD_PUSH}" >> "$GITHUB_OUTPUT" | |
| # Prepara Docker Buildx e cache de camadas | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| # Login no GHCR (usa GITHUB_TOKEN) | |
| - name: Login to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| # Normaliza as tags calculadas em formato multilinha para o docker/buildx | |
| - name: Prepare tag list (multiline) | |
| id: tags | |
| shell: bash | |
| run: | | |
| IFS=',' read -ra T <<< "${{ steps.vars.outputs.tags_csv }}" | |
| printf "jvm_tags<<EOF\n" >> "$GITHUB_OUTPUT" | |
| for tag in "${T[@]}"; do | |
| # Usa APP_NAME como nome da imagem JVM | |
| echo "ghcr.io/${{ github.repository_owner }}/${{ env.APP_NAME }}:$tag" >> "$GITHUB_OUTPUT" | |
| done | |
| printf "EOF\n" >> "$GITHUB_OUTPUT" | |
| printf "native_tags<<EOF\n" >> "$GITHUB_OUTPUT" | |
| for tag in "${T[@]}"; do | |
| # Usa sufixo '-native' para diferenciar imagem native (ajuste se necessário) | |
| echo "ghcr.io/${{ github.repository_owner }}/${{ env.APP_NAME }}-native:$tag" >> "$GITHUB_OUTPUT" | |
| done | |
| printf "EOF\n" >> "$GITHUB_OUTPUT" | |
| printf "db_tags<<EOF\n" >> "$GITHUB_OUTPUT" | |
| for tag in "${T[@]}"; do | |
| echo "${{ env.DB_IMAGE_GHCR }}:$tag" >> "$GITHUB_OUTPUT" | |
| done | |
| printf "EOF\n" >> "$GITHUB_OUTPUT" | |
| # Constrói imagem Docker da aplicação rodando na JVM usando Dockerfile existente | |
| - name: Build JVM Docker image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: docker/Dockerfile.jvm | |
| platforms: linux/amd64 | |
| push: ${{ steps.vars.outputs.should_push == 'true' && github.event_name == 'push' }} | |
| tags: ${{ steps.tags.outputs.jvm_tags }} | |
| labels: | | |
| org.opencontainers.image.version=${{ steps.gradle_version.outputs.version }} | |
| org.opencontainers.image.revision=${{ steps.vars.outputs.short_sha }} | |
| org.opencontainers.image.source=https://github.com/${{ github.repository }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # Constrói imagem Docker da aplicação como Native Image (usa Dockerfile existente) | |
| - name: Build Native Docker image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: docker/Dockerfile.native | |
| platforms: linux/amd64 | |
| push: ${{ steps.vars.outputs.should_push == 'true' && github.event_name == 'push' }} | |
| tags: ${{ steps.tags.outputs.native_tags }} | |
| labels: | | |
| org.opencontainers.image.version=${{ steps.gradle_version.outputs.version }} | |
| org.opencontainers.image.revision=${{ steps.vars.outputs.short_sha }} | |
| org.opencontainers.image.source=https://github.com/${{ github.repository }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # Cria um Dockerfile simples para o banco (baseado em MySQL oficial) | |
| - name: Create DB Dockerfile (MySQL base) | |
| run: | | |
| mkdir -p docker | |
| cat > docker/Dockerfile.db << 'EOF' | |
| FROM mysql:8.0 | |
| # Ajustes opcionais de configuração poderiam ser adicionados aqui (volumes, conf, envs, etc.) | |
| EOF | |
| # Constrói imagem do banco de dados a partir do Dockerfile.db | |
| - name: Build DB Docker image (MySQL) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: docker/Dockerfile.db | |
| platforms: linux/amd64 | |
| push: ${{ steps.vars.outputs.should_push == 'true' && github.event_name == 'push' }} | |
| tags: ${{ steps.tags.outputs.db_tags }} | |
| labels: | | |
| org.opencontainers.image.version=${{ steps.gradle_version.outputs.version }} | |
| org.opencontainers.image.revision=${{ steps.vars.outputs.short_sha }} | |
| org.opencontainers.image.source=https://github.com/${{ github.repository }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # Deploy to Hostinger via SSH (expects secrets: HOSTINGER (private key), HOSTINGER_HOST, HOSTINGER_USER, optional HOSTINGER_PORT) | |
| - name: Deploy to Hostinger via SSH | |
| uses: appleboy/ssh-action@v0.1.9 | |
| if: ${{ needs.tests.result == 'success' && steps.vars.outputs.should_push == 'true' && github.event_name == 'push' }} | |
| with: | |
| host: ${{ secrets.HOSTINGER_HOST }} | |
| username: ${{ secrets.HOSTINGER_USER }} | |
| key: ${{ secrets.HOSTINGER }} | |
| port: ${{ secrets.HOSTINGER_PORT }} | |
| script: | | |
| set -e | |
| docker pull | |
| MAIL_USERNAME=${{ secrets.MAIL_USERNAME }} | |
| MAIL_PASSWORD=${{ secrets.MAIL_PASSWORD }} | |
| APP_NAME=${{ secrets.APP_NAME }} | |
| FRONTEND_URL=${{ secrets.FRONTEND_URL }} | |
| EOF | |
| docker run -d --name espacogeek \ | |
| -p 8080:8080 \ | |
| --restart unless-stopped \ | |
| --env-file .env.espacogeek \ | |
| ghcr.io/${{ github.repository_owner }}/espacogeek-backend:latest | |
| # (Opcional) remove arquivo após subir o container (mantém apenas no container) | |
| rm -f .env.espacogeek | |
| # Resumo final com as tags geradas | |
| - name: Summary | |
| if: always() | |
| run: | | |
| echo "Project version: ${{ steps.gradle_version.outputs.version }}" | |
| echo "Tags geradas: ${{ steps.vars.outputs.tags_csv }}" | |
| echo "Push habilitado: ${{ steps.vars.outputs.should_push }}" |