diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d8d4ec8..7bd061a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -7,8 +7,14 @@ on: - dev paths: - 'server/**' + - 'client/**' - 'k8s/**' - '.github/workflows/**' + workflow_dispatch: # Allow manual trigger from GitHub UI + +concurrency: + group: deploy-${{ github.ref }} + cancel-in-progress: true env: DOCKER_IMAGE: tharusha69/apexpos-backend @@ -19,7 +25,7 @@ env: jobs: # ───────────────────────────────────────────── - # Job 1: Lint & Build client (optional validation) + # Job 1a: Lint & Build client (runs in PARALLEL) # ───────────────────────────────────────────── lint-client: name: 🔍 Lint & Build Frontend @@ -47,12 +53,46 @@ jobs: working-directory: client # ───────────────────────────────────────────── - # Job 2: Build & Push Docker image + # Job 1b: Validate backend (runs in PARALLEL) + # ───────────────────────────────────────────── + test-backend: + name: 🧪 Validate Backend + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: server/package-lock.json + + - name: Install dependencies + run: npm ci + working-directory: server + + - name: Syntax check all JS files + run: | + echo "🔍 Checking JavaScript syntax..." + find . -name '*.js' -not -path './node_modules/*' | while read f; do + node --check "$f" || exit 1 + done + echo "✅ All files pass syntax check" + working-directory: server + + - name: Run tests + run: npm test --if-present || echo "⚠️ No tests configured yet" + working-directory: server + continue-on-error: true + + # ───────────────────────────────────────────── + # Job 2: Build & Push Docker image (waits for BOTH) # ───────────────────────────────────────────── build-push: name: 🐳 Build & Push Docker Image runs-on: ubuntu-latest - needs: lint-client + needs: [lint-client, test-backend] # Parallel gate — both must pass outputs: image_tag: ${{ steps.meta.outputs.version }} steps: @@ -98,7 +138,7 @@ jobs: name: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} steps: - name: Deploy to EC2 via SSH - uses: appleboy/ssh-action@v1.0.3 + uses: appleboy/ssh-action@v1.2.0 with: host: ${{ env.EC2_HOST }} username: ${{ env.EC2_USER }} diff --git a/Jenkinsfile b/Jenkinsfile index 643467a..320ec64 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -7,11 +7,14 @@ pipeline { DEPLOYMENT_FILE = 'k8s/backend/deployment.yml' NAMESPACE = 'apexpos' DEPLOYMENT = 'apexpos-backend' + // Limit Node memory consumption to 256MB to prevent OOM + NODE_OPTIONS = '--max-old-space-size=256' } options { - buildDiscarder(logRotator(numToKeepStr: '5')) + buildDiscarder(logRotator(numToKeepStr: '3')) timeout(time: 20, unit: 'MINUTES') + disableConcurrentBuilds() } stages { @@ -21,23 +24,25 @@ pipeline { } } - stage('Install & Lint') { - parallel { - stage('Backend Deps') { - steps { - dir('server') { - sh 'npm ci --omit=dev' - } - } + // Run sequentially (NOT in parallel) to conserve memory on t3.micro + stage('Backend Dependencies') { + steps { + dir('server') { + echo '📦 Installing Backend Dependencies...' + sh 'npm ci --omit=dev --no-audit --no-fund' } - stage('Frontend Lint & Build') { - steps { - dir('client') { - sh 'npm ci' - sh 'npm run lint' - sh 'npm run build' - } - } + } + } + + stage('Frontend Dependencies & Build') { + steps { + dir('client') { + echo '📦 Installing Frontend Dependencies...' + sh 'npm ci --no-audit --no-fund' + echo '🔍 Linting Frontend...' + sh 'npm run lint' + echo '🚀 Building Frontend Production Bundle...' + sh 'npm run build' } } } @@ -51,6 +56,7 @@ pipeline { } steps { dir('server') { + echo '🐳 Building Docker Image...' sh "docker build -t ${DOCKER_IMAGE}:${BUILD_NUMBER} ." sh "docker tag ${DOCKER_IMAGE}:${BUILD_NUMBER} ${DOCKER_IMAGE}:latest" } @@ -65,6 +71,7 @@ pipeline { } } steps { + echo '📤 Pushing Image to Docker Hub...' sh """ echo ${DOCKER_HUB_CREDS_PSW} | docker login -u ${DOCKER_HUB_CREDS_USR} --password-stdin docker push ${DOCKER_IMAGE}:${BUILD_NUMBER} @@ -81,6 +88,7 @@ pipeline { } } steps { + echo '🔄 Restarting Kubernetes Deployment...' sh """ export KUBECONFIG=/root/.kube/config kubectl rollout restart deployment/${DEPLOYMENT} -n ${NAMESPACE} @@ -97,6 +105,7 @@ pipeline { } } steps { + echo '🩺 Running API health check...' sh """ sleep 10 curl -f http://localhost:30500/ || exit 1 diff --git a/k8s/backend/configmap.yml b/k8s/backend/configmap.yml index fd797e3..c021bc5 100644 --- a/k8s/backend/configmap.yml +++ b/k8s/backend/configmap.yml @@ -7,4 +7,4 @@ data: PORT: "5000" NODE_ENV: "production" # Allow local dev + EC2 NodePort access + Vercel frontend - ALLOWED_ORIGINS: "http://localhost,http://127.0.0.1,http://YOUR_EC2_IP,http://YOUR_EC2_IP:30500,https://apexpos.vercel.app" # Update YOUR_EC2_IP before deploy + ALLOWED_ORIGINS: "http://localhost,http://127.0.0.1,http://13.206.37.231,http://13.206.37.231:30500,https://client-jade-phi.vercel.app,https://client-75ejziams-tharusha-bashithas-projects.vercel.app" diff --git a/k8s/backend/deployment.yml b/k8s/backend/deployment.yml index 8a6f77e..1c1bcf6 100644 --- a/k8s/backend/deployment.yml +++ b/k8s/backend/deployment.yml @@ -23,7 +23,7 @@ spec: containers: - name: backend image: tharusha69/apexpos-backend:latest - imagePullPolicy: IfNotPresent + imagePullPolicy: Always ports: - containerPort: 5000 envFrom: diff --git a/scripts/install-jenkins.sh b/scripts/install-jenkins.sh index 5a6fefd..4721abb 100755 --- a/scripts/install-jenkins.sh +++ b/scripts/install-jenkins.sh @@ -23,12 +23,11 @@ if docker ps -a --format '{{.Names}}' | grep -q '^jenkins$'; then fi fi -echo "Starting Jenkins with memory limits (400MB max)..." +echo "Starting Jenkins with memory limits (700MB max)..." docker run -d --name jenkins \ --restart unless-stopped \ - --memory="400m" \ - --memory-swap="600m" \ + --memory="700m" \ --cpus="0.8" \ -p 8080:8080 -p 50000:50000 \ -e JAVA_OPTS="-Xmx256m -Xms128m -XX:MaxMetaspaceSize=128m -Dhudson.model.DirectoryBrowserSupport.CSP=" \ @@ -39,7 +38,7 @@ docker run -d --name jenkins \ jenkins/jenkins:lts-jdk17 echo "" -echo "✅ Jenkins starting on port 8080 (memory limited to 400MB)" +echo "✅ Jenkins starting on port 8080 (memory limited to 700MB)" echo "⏳ Wait ~60 seconds for Jenkins to fully start..." echo "" echo "Initial admin password (run after 60 seconds):"