name: Deploy on: push: branches: [main, master] workflow_dispatch: inputs: environment: description: 'Environment to deploy to' required: true default: 'staging' type: choice options: - staging - production env: NODE_VERSION: '20' jobs: # Run CI first ci: uses: ./.github/workflows/ci.yml # Deploy Backend deploy-backend: name: Deploy Backend runs-on: ubuntu-latest needs: ci if: success() environment: name: ${{ github.event.inputs.environment || 'staging' }} url: ${{ vars.API_URL }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Download backend artifacts uses: actions/download-artifact@v4 with: name: backend-dist path: apps/backend/dist - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} # Docker deployment option - name: Build Docker image if: vars.DEPLOY_METHOD == 'docker' working-directory: apps/backend run: | docker build -t ${{ vars.DOCKER_REGISTRY }}/template-saas-backend:${{ github.sha }} . docker tag ${{ vars.DOCKER_REGISTRY }}/template-saas-backend:${{ github.sha }} ${{ vars.DOCKER_REGISTRY }}/template-saas-backend:latest - name: Push Docker image if: vars.DEPLOY_METHOD == 'docker' run: | echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin ${{ vars.DOCKER_REGISTRY }} docker push ${{ vars.DOCKER_REGISTRY }}/template-saas-backend:${{ github.sha }} docker push ${{ vars.DOCKER_REGISTRY }}/template-saas-backend:latest # Alternative: SSH deployment - name: Deploy via SSH if: vars.DEPLOY_METHOD == 'ssh' uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} script: | cd ${{ vars.DEPLOY_PATH }}/backend git pull origin ${{ github.ref_name }} npm ci --production npm run build pm2 restart template-saas-api || pm2 start dist/main.js --name template-saas-api - name: Notify deployment success if: success() run: echo "Backend deployed successfully to ${{ github.event.inputs.environment || 'staging' }}" # Deploy Frontend deploy-frontend: name: Deploy Frontend runs-on: ubuntu-latest needs: ci if: success() environment: name: ${{ github.event.inputs.environment || 'staging' }} url: ${{ vars.APP_URL }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Download frontend artifacts uses: actions/download-artifact@v4 with: name: frontend-dist path: apps/frontend/dist # Vercel deployment option - name: Deploy to Vercel if: vars.DEPLOY_METHOD == 'vercel' uses: amondnet/vercel-action@v25 with: vercel-token: ${{ secrets.VERCEL_TOKEN }} vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} working-directory: apps/frontend vercel-args: ${{ github.event.inputs.environment == 'production' && '--prod' || '' }} # S3/CloudFront deployment option - name: Deploy to S3 if: vars.DEPLOY_METHOD == 's3' uses: jakejarvis/s3-sync-action@v0.5.1 with: args: --acl public-read --follow-symlinks --delete env: AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ vars.AWS_REGION }} SOURCE_DIR: apps/frontend/dist - name: Invalidate CloudFront if: vars.DEPLOY_METHOD == 's3' uses: chetan/invalidate-cloudfront-action@v2 env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ vars.AWS_REGION }} DISTRIBUTION: ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} PATHS: '/*' - name: Notify deployment success if: success() run: echo "Frontend deployed successfully to ${{ github.event.inputs.environment || 'staging' }}" # Database migrations run-migrations: name: Run Database Migrations runs-on: ubuntu-latest needs: [deploy-backend] if: success() environment: name: ${{ github.event.inputs.environment || 'staging' }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Run migrations via SSH if: vars.DEPLOY_METHOD == 'ssh' uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} script: | cd ${{ vars.DEPLOY_PATH }}/database ./scripts/run-migrations.sh - name: Notify migration success run: echo "Migrations completed successfully" # Deployment summary deploy-summary: name: Deployment Summary runs-on: ubuntu-latest needs: [deploy-backend, deploy-frontend, run-migrations] if: always() steps: - name: Deployment Summary run: | echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Component | Status |" >> $GITHUB_STEP_SUMMARY echo "|-----------|--------|" >> $GITHUB_STEP_SUMMARY echo "| Backend | ${{ needs.deploy-backend.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Frontend | ${{ needs.deploy-frontend.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Migrations | ${{ needs.run-migrations.result }} |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "Environment: ${{ github.event.inputs.environment || 'staging' }}" >> $GITHUB_STEP_SUMMARY echo "Commit: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY