From c1748d68193f597d2d646d3d507911d28b6032aa Mon Sep 17 00:00:00 2001 From: sursini Date: Tue, 10 Mar 2026 17:48:59 -0300 Subject: [PATCH] feat: add reusable nextjs-deploy and nextjs-nginx-conf workflows --- .gitea/templates/maintenance.html | 62 ++++++++++++ .../templates/nginx-nextjs-site.conf.template | 37 +++++++ .gitea/workflows/nextjs-deploy.yaml | 74 ++++++++++++++ .gitea/workflows/nextjs-nginx-conf.yaml | 98 +++++++++++++++++++ 4 files changed, 271 insertions(+) create mode 100644 .gitea/templates/maintenance.html create mode 100644 .gitea/templates/nginx-nextjs-site.conf.template create mode 100644 .gitea/workflows/nextjs-deploy.yaml create mode 100644 .gitea/workflows/nextjs-nginx-conf.yaml diff --git a/.gitea/templates/maintenance.html b/.gitea/templates/maintenance.html new file mode 100644 index 0000000..01fe3a3 --- /dev/null +++ b/.gitea/templates/maintenance.html @@ -0,0 +1,62 @@ + + + + + + Kalpasys — Maintenance + + + + + +

Under maintenance

+

We're working on something. Back shortly.

+ + diff --git a/.gitea/templates/nginx-nextjs-site.conf.template b/.gitea/templates/nginx-nextjs-site.conf.template new file mode 100644 index 0000000..b89a94b --- /dev/null +++ b/.gitea/templates/nginx-nextjs-site.conf.template @@ -0,0 +1,37 @@ +# Kalpasys – generated by nextjs-nginx-conf.yaml +server { + listen 80; + server_name {{DOMAIN}}; + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl; + server_name {{DOMAIN}}; + + ssl_certificate /etc/letsencrypt/live/{{DOMAIN}}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{DOMAIN}}/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + error_page 502 503 504 /maintenance.html; + + location = /maintenance.html { + root /var/www/errors; + internal; + } + + location / { + proxy_intercept_errors on; + proxy_pass http://127.0.0.1:{{PORT}}; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_cache_bypass $http_upgrade; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/.gitea/workflows/nextjs-deploy.yaml b/.gitea/workflows/nextjs-deploy.yaml new file mode 100644 index 0000000..16ea6f5 --- /dev/null +++ b/.gitea/workflows/nextjs-deploy.yaml @@ -0,0 +1,74 @@ +name: Deploy to Production +run-name: 🚀 Deploy ${{ github.event.repository.name }} ${{ github.ref_name }} + +on: + workflow_call: + push: + tags: + - "v*" + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + + env: + VAR_APP_NAME: ${{ vars.VAR_APP_NAME }} + + steps: + - name: 📥 Checkout + uses: actions/checkout@v4 + + - name: 🟢 Setup Node 20 + Cache + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "npm" + cache-dependency-path: web/package-lock.json + + - name: 📦 Install dependencies + working-directory: web + run: npm ci --include=dev + + - name: Create Env + uses: jakubcieslik99/secrets-vars-to-env-file-action@main + with: + secrets: ${{ toJSON(secrets) }} + vars: ${{ toJSON(vars) }} + generate-file: "web/.env" + + - name: 🏗️ Build Next.js (Standalone) + working-directory: web + run: | + npm run build + test -d .next/standalone || (echo "Standalone build missing" && exit 1) + test -d .next/static || (echo "Static assets missing" && exit 1) + + - name: 🚀 Deploy (Atomic Release) + run: | + set -e + DEPLOY_BASE="/var/www/${VAR_APP_NAME}" + RELEASE_DIR="$DEPLOY_BASE/releases/$(date +%Y%m%d%H%M%S)" + CURRENT_LINK="$DEPLOY_BASE/current" + + mkdir -p "$RELEASE_DIR" + cp -r web/.next/standalone/. "$RELEASE_DIR/" + mkdir -p "$RELEASE_DIR/.next/static" + cp -r web/.next/static/. "$RELEASE_DIR/.next/static/" + + if [ -d "web/public" ]; then + cp -r web/public "$RELEASE_DIR/" + fi + cp web/.env "$RELEASE_DIR/" + + SHARED_LOGS="$DEPLOY_BASE/shared/logs" + mkdir -p "$SHARED_LOGS" + + ln -sfn "$RELEASE_DIR" "$CURRENT_LINK" + + - name: 🔄 Reload PM2 + run: | + set -e + echo "Restarting $VAR_APP_NAME" + sudo -u ansible pm2 restart "$VAR_APP_NAME" + sudo -u ansible pm2 save diff --git a/.gitea/workflows/nextjs-nginx-conf.yaml b/.gitea/workflows/nextjs-nginx-conf.yaml new file mode 100644 index 0000000..999af35 --- /dev/null +++ b/.gitea/workflows/nextjs-nginx-conf.yaml @@ -0,0 +1,98 @@ +name: Generate Nginx Config +run-name: 🔧 Generate Nginx config for ${{ vars.VAR_APP_NAME }} + +on: + workflow_call: + workflow_dispatch: + push: + paths: + - '.gitea/workflows/nextjs-nginx-conf.yaml' + - '.gitea/templates/nginx-nextjs-site.conf.template' + branches: + - main + +jobs: + generate-config: + runs-on: ubuntu-latest + + env: + APP_NAME: ${{ vars.VAR_APP_NAME }} + NEXTJS_PORT: ${{ vars.NEXTJS_PORT }} + WEBSITE_URL: ${{ vars.WEBSITE_URL }} + ADMIN_EMAIL: ${{ vars.ADMIN_EMAIL }} + + steps: + - name: 📥 Checkout devops + uses: actions/checkout@v3 + with: + repository: Infrastructure/gitea-workflows + ref: main + fetch-depth: 1 + + - name: 📋 Infos de génération + run: | + echo "APP_NAME : ${{ env.APP_NAME }}" + echo "NEXTJS_PORT : ${{ env.NEXTJS_PORT }}" + echo "WEBSITE_URL : ${{ env.WEBSITE_URL }}" + if [ -z "${{ env.APP_NAME }}" ]; then echo "❌ VAR_APP_NAME manquant" && exit 1; fi + if [ -z "${{ env.NEXTJS_PORT }}" ]; then echo "❌ NEXTJS_PORT manquant" && exit 1; fi + if [ -z "${{ env.WEBSITE_URL }}" ]; then echo "❌ WEBSITE_URL manquant" && exit 1; fi + + - name: 🔧 Génération du fichier de configuration + run: | + DOMAIN=$(echo "${{ env.WEBSITE_URL }}" | sed -E 's#^https?://##' | sed -E 's#/.*$##') + CONFIG_FILENAME="${DOMAIN}.conf" + LOCAL_TEMP="/tmp/$CONFIG_FILENAME" + cp .gitea/templates/nginx-nextjs-site.conf.template "$LOCAL_TEMP" + sed -i "s/{{DOMAIN}}/$DOMAIN/g" "$LOCAL_TEMP" + sed -i "s/{{PORT}}/${{ env.NEXTJS_PORT }}/g" "$LOCAL_TEMP" + cat "$LOCAL_TEMP" + + - name: 🛠️ Déploiement page maintenance + run: | + sudo mkdir -p /var/www/errors + sudo cp .gitea/templates/maintenance.html /var/www/errors/maintenance.html + sudo chmod 644 /var/www/errors/maintenance.html + + - name: 🔒 Certificat SSL (certbot --standalone si absent) + run: | + DOMAIN=$(echo "${{ env.WEBSITE_URL }}" | sed -E 's#^https?://##' | sed -E 's#/.*$##') + CERT_PATH="/etc/letsencrypt/live/$DOMAIN/fullchain.pem" + + # Installer certbot si absent + if ! command -v certbot &>/dev/null; then + echo "📦 Installation de certbot..." + sudo apt-get update -qq + sudo apt-get install -y -qq certbot + fi + + if [ -f "$CERT_PATH" ]; then + echo "✅ Certificat existant trouvé — aucune action requise" + else + echo "🔐 Certificat absent — obtention via certbot --standalone" + # Arrêt de nginx pour libérer le port 80 (standalone en a besoin) + sudo systemctl stop nginx || true + sudo certbot certonly \ + --standalone \ + --non-interactive \ + --agree-tos \ + --email "${{ env.ADMIN_EMAIL }}" \ + -d "$DOMAIN" + echo "✅ Certificat obtenu pour $DOMAIN" + fi + + - name: 🚀 Installation NGINX + run: | + DOMAIN=$(echo "${{ env.WEBSITE_URL }}" | sed -E 's#^https?://##' | sed -E 's#/.*$##') + CONFIG_FILENAME="${DOMAIN}.conf" + LOCAL_TEMP="/tmp/$CONFIG_FILENAME" + TARGET_AVAILABLE="/etc/nginx/sites-available/$CONFIG_FILENAME" + TARGET_ENABLED="/etc/nginx/sites-enabled/$CONFIG_FILENAME" + sudo mv "$LOCAL_TEMP" "$TARGET_AVAILABLE" + if [ ! -f "$TARGET_ENABLED" ]; then sudo ln -s "$TARGET_AVAILABLE" "$TARGET_ENABLED"; fi + sudo nginx -t + if sudo systemctl is-active --quiet nginx; then + sudo systemctl reload nginx + else + sudo systemctl start nginx + fi