diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml deleted file mode 100644 index 3b86c43..0000000 --- a/.github/workflows/ci-cd.yml +++ /dev/null @@ -1,334 +0,0 @@ -name: CI/CD Pipeline - -on: - push: - branches: [main, dev, production] - pull_request: - branches: [main, dev, production] - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - -jobs: - # Test Job (parallel) - test: - name: Run Tests - runs-on: self-hosted # Use your own server for speed! - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - - - name: Cache dependencies - uses: actions/cache@v4 - with: - path: | - ~/.npm - node_modules - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- - - - name: Install dependencies - run: npm ci - - - name: Create test environment file - run: | - cat > .env < .env < /dev/null 2>&1; then - echo "✅ Staging deployment successful!" - break - fi - sleep 2 - done - - # Verify deployment - if curl -f http://localhost:3002/api/health; then - echo "✅ Staging deployment verified!" - else - echo "⚠️ Staging health check failed, but container is running" - docker compose -f $COMPOSE_FILE logs --tail=50 - fi - - # Deploy to production - deploy: - name: Deploy to Production - runs-on: self-hosted - needs: build - if: github.event_name == 'push' && github.ref == 'refs/heads/production' - environment: production - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Log in to Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Deploy to production (zero-downtime) - run: | - # Set deployment variables - export IMAGE_NAME="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:production" - export CONTAINER_NAME="portfolio-app" - export COMPOSE_FILE="docker-compose.production.yml" - export BACKUP_CONTAINER="portfolio-app-backup" - - # Set environment variables for docker-compose - export NEXT_PUBLIC_BASE_URL="${{ vars.NEXT_PUBLIC_BASE_URL }}" - export MY_EMAIL="${{ vars.MY_EMAIL }}" - export MY_INFO_EMAIL="${{ vars.MY_INFO_EMAIL }}" - export MY_PASSWORD="${{ secrets.MY_PASSWORD }}" - export MY_INFO_PASSWORD="${{ secrets.MY_INFO_PASSWORD }}" - export ADMIN_BASIC_AUTH="${{ secrets.ADMIN_BASIC_AUTH }}" - - # Pull latest production image - echo "📦 Pulling latest production image..." - docker pull $IMAGE_NAME - - # Check if production container is running - if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then - echo "🔄 Production container is running - performing zero-downtime deployment..." - - # Start new container with different name first (blue-green) - echo "🚀 Starting new container (green)..." - docker run -d \ - --name ${BACKUP_CONTAINER} \ - --network portfolio_net \ - -p 3002:3000 \ - -e NODE_ENV=production \ - -e DATABASE_URL=postgresql://portfolio_user:portfolio_pass@postgres:5432/portfolio_db?schema=public \ - -e REDIS_URL=redis://redis:6379 \ - -e NEXT_PUBLIC_BASE_URL="${{ vars.NEXT_PUBLIC_BASE_URL }}" \ - -e MY_EMAIL="${{ vars.MY_EMAIL }}" \ - -e MY_INFO_EMAIL="${{ vars.MY_INFO_EMAIL }}" \ - -e MY_PASSWORD="${{ secrets.MY_PASSWORD }}" \ - -e MY_INFO_PASSWORD="${{ secrets.MY_INFO_PASSWORD }}" \ - -e ADMIN_BASIC_AUTH="${{ secrets.ADMIN_BASIC_AUTH }}" \ - $IMAGE_NAME || true - - # Wait for new container to be healthy - echo "⏳ Waiting for new container to be healthy..." - for i in {1..30}; do - if curl -f http://localhost:3002/api/health > /dev/null 2>&1; then - echo "✅ New container is healthy!" - break - fi - sleep 2 - done - - # Stop old container - echo "🛑 Stopping old container..." - docker stop ${CONTAINER_NAME} || true - - # Remove old container - docker rm ${CONTAINER_NAME} || true - - # Rename new container to production name - docker rename ${BACKUP_CONTAINER} ${CONTAINER_NAME} - - # Update port mapping (requires container restart, but it's already healthy) - docker stop ${CONTAINER_NAME} - docker rm ${CONTAINER_NAME} - - # Start with correct port using docker-compose - docker compose -f $COMPOSE_FILE up -d --force-recreate - else - echo "🆕 No existing container - starting fresh deployment..." - docker compose -f $COMPOSE_FILE up -d --force-recreate - fi - - # Wait for health check - echo "⏳ Waiting for production application to be healthy..." - for i in {1..30}; do - if curl -f http://localhost:3000/api/health > /dev/null 2>&1; then - echo "✅ Production deployment successful!" - break - fi - sleep 2 - done - - # Verify deployment - if curl -f http://localhost:3000/api/health; then - echo "✅ Production deployment verified!" - else - echo "❌ Production deployment failed!" - docker compose -f $COMPOSE_FILE logs --tail=100 - exit 1 - fi - - # Cleanup backup container if it exists - docker rm -f ${BACKUP_CONTAINER} 2>/dev/null || true - - - name: Cleanup old images - run: | - # Remove unused images older than 7 days - docker image prune -f --filter "until=168h" - - # Remove unused containers - docker container prune -f diff --git a/package-lock.json b/package-lock.json index 149ba8b..e7ec212 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@react-three/fiber": "^9.5.0", "@sentry/nextjs": "^10.36.0", "@shadergradient/react": "^2.4.20", + "@swc/helpers": "^0.5.19", "@tiptap/extension-color": "^3.15.3", "@tiptap/extension-highlight": "^3.15.3", "@tiptap/extension-link": "^3.15.3", @@ -4865,9 +4866,9 @@ "license": "Apache-2.0" }, "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.19.tgz", + "integrity": "sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.0" @@ -13697,6 +13698,15 @@ "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, + "node_modules/next/node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", diff --git a/package.json b/package.json index 6f59f4b..20d0cd8 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "@react-three/fiber": "^9.5.0", "@sentry/nextjs": "^10.36.0", "@shadergradient/react": "^2.4.20", + "@swc/helpers": "^0.5.19", "@tiptap/extension-color": "^3.15.3", "@tiptap/extension-highlight": "^3.15.3", "@tiptap/extension-link": "^3.15.3",