name: Deploy to Production run-name: 🚀 Deploy ${{ github.event.repository.name }} ${{ github.ref_name }} on: workflow_call: inputs: app_dir: description: "Relative path to the Next.js app within the repository" type: string default: "." workflow_dispatch: inputs: app_dir: description: "Relative path to the Next.js app within the repository" type: string default: "." jobs: deploy: runs-on: ubuntu-latest env: VAR_APP_NAME: ${{ vars.VAR_APP_NAME }} APP_DIR: ${{ inputs.app_dir || '.' }} 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: ${{ env.APP_DIR }}/package-lock.json - name: 📦 Install dependencies working-directory: ${{ env.APP_DIR }} 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: "${{ env.APP_DIR }}/.env" - name: 🏗️ Build Next.js (Standalone) working-directory: ${{ env.APP_DIR }} 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 "${APP_DIR}/.next/standalone/." "$RELEASE_DIR/" mkdir -p "$RELEASE_DIR/.next/static" cp -r "${APP_DIR}/.next/static/." "$RELEASE_DIR/.next/static/" if [ -d "${APP_DIR}/public" ]; then cp -r "${APP_DIR}/public" "$RELEASE_DIR/" fi cp "${APP_DIR}/.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