diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..88cf3cf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# Multi-stage build for Next.js +FROM node:20-slim AS builder + +WORKDIR /app + +# Copy package files +COPY package*.json ./ +RUN npm ci --prefer-offline --no-audit + +# Copy source and build +COPY . . +RUN npm run build + +# Production stage +FROM node:20-slim AS runner + +WORKDIR /app + +ENV NODE_ENV=production +ENV PORT=3000 +ENV HOSTNAME=0.0.0.0 + +# Copy necessary files from builder +COPY --from=builder /app/public ./public +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static + +EXPOSE 3000 + +CMD ["node", "server.js"] diff --git a/k8s/deployment-nodejs.yaml b/k8s/deployment-nodejs.yaml deleted file mode 100644 index 267d50b..0000000 --- a/k8s/deployment-nodejs.yaml +++ /dev/null @@ -1,181 +0,0 @@ -# Note: Secret 'woocommerce-credentials' must be created manually with real credentials: -# kubectl create secret generic woocommerce-credentials -n manoonoils \ -# --from-literal=WOOCOMMERCE_URL="https://manoonoils.com" \ -# --from-literal=WOOCOMMERCE_CONSUMER_KEY="ck_6a62a2ac8fa8d50e4757bf3b35c9d052dbbcf09f" \ -# --from-literal=WOOCOMMERCE_CONSUMER_SECRET="cs_0ea41d2c8fc232d1e609e559ea8561d02c4406ee" ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: storefront - namespace: manoonoils -spec: - replicas: 1 - selector: - matchLabels: - app: storefront - template: - metadata: - labels: - app: storefront - annotations: - # Git revision annotation - changes with each commit to force pod restart - gitRevision: ${GIT_REVISION} - spec: - initContainers: - - name: clone - image: alpine/git:latest - command: - - sh - - -c - - | - set -e - apk add --no-cache git - git clone --depth 1 --branch master \ - http://gitea.gitea.svc.cluster.local:3000/unchained/manoon-headless.git \ - /workspace - echo "Clone complete." - volumeMounts: - - name: workspace - mountPath: /workspace - securityContext: - runAsUser: 0 - resources: - limits: - cpu: 500m - memory: 256Mi - - name: install - image: node:20-slim - workingDir: /workspace - command: - - sh - - -c - - | - set -e - echo "Installing dependencies..." - npm install --prefer-offline --no-audit 2>&1 - echo "Dependencies installed." - volumeMounts: - - name: workspace - mountPath: /workspace - securityContext: - runAsUser: 0 - resources: - limits: - cpu: 2000m - memory: 3Gi - requests: - cpu: 100m - memory: 1Gi - - name: build - image: node:20-slim - workingDir: /workspace - command: - - sh - - -c - - | - set -e - echo "Building Next.js app..." - npm run build - echo "Build complete!" - env: - - name: NODE_ENV - value: "production" - - name: NEXT_PUBLIC_WOOCOMMERCE_URL - valueFrom: - secretKeyRef: - name: woocommerce-credentials - key: WOOCOMMERCE_URL - - name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_KEY - valueFrom: - secretKeyRef: - name: woocommerce-credentials - key: WOOCOMMERCE_CONSUMER_KEY - - name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_SECRET - valueFrom: - secretKeyRef: - name: woocommerce-credentials - key: WOOCOMMERCE_CONSUMER_SECRET - - name: NEXT_PUBLIC_SITE_URL - value: "https://dev.manoonoils.com" - volumeMounts: - - name: workspace - mountPath: /workspace - securityContext: - runAsUser: 0 - resources: - limits: - cpu: 2000m - memory: 2Gi - requests: - cpu: 100m - memory: 512Mi - containers: - - name: storefront - image: node:20-slim - workingDir: /workspace - command: - - npm - - start - ports: - - containerPort: 3000 - envFrom: - - configMapRef: - name: deployment-metadata - optional: true - env: - - name: NODE_ENV - value: "production" - - name: PORT - value: "3000" - - name: HOSTNAME - value: "0.0.0.0" - - name: NEXT_PUBLIC_WOOCOMMERCE_URL - valueFrom: - secretKeyRef: - name: woocommerce-credentials - key: WOOCOMMERCE_URL - - name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_KEY - valueFrom: - secretKeyRef: - name: woocommerce-credentials - key: WOOCOMMERCE_CONSUMER_KEY - - name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_SECRET - valueFrom: - secretKeyRef: - name: woocommerce-credentials - key: WOOCOMMERCE_CONSUMER_SECRET - - name: NEXT_PUBLIC_SITE_URL - value: "https://dev.manoonoils.com" - resources: - limits: - cpu: 500m - memory: 512Mi - requests: - cpu: 50m - memory: 128Mi - volumeMounts: - - name: workspace - mountPath: /workspace - startupProbe: - httpGet: - path: /favicon.ico - port: 3000 - periodSeconds: 10 - failureThreshold: 30 - livenessProbe: - httpGet: - path: /favicon.ico - port: 3000 - periodSeconds: 30 - failureThreshold: 3 - readinessProbe: - httpGet: - path: /favicon.ico - port: 3000 - periodSeconds: 5 - failureThreshold: 3 - volumes: - - name: workspace - emptyDir: - sizeLimit: 2Gi diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml new file mode 100644 index 0000000..361edb9 --- /dev/null +++ b/k8s/deployment.yaml @@ -0,0 +1,70 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: storefront + namespace: manoonoils +spec: + replicas: 1 + selector: + matchLabels: + app: storefront + template: + metadata: + labels: + app: storefront + spec: + containers: + - name: storefront + image: ghcr.io/unchainedio/manoon-headless:latest + imagePullPolicy: Always + ports: + - containerPort: 3000 + env: + - name: NODE_ENV + value: "production" + - name: PORT + value: "3000" + - name: HOSTNAME + value: "0.0.0.0" + - name: NEXT_PUBLIC_WOOCOMMERCE_URL + valueFrom: + secretKeyRef: + name: woocommerce-credentials + key: WOOCOMMERCE_URL + - name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_KEY + valueFrom: + secretKeyRef: + name: woocommerce-credentials + key: WOOCOMMERCE_CONSUMER_KEY + - name: NEXT_PUBLIC_WOOCOMMERCE_CONSUMER_SECRET + valueFrom: + secretKeyRef: + name: woocommerce-credentials + key: WOOCOMMERCE_CONSUMER_SECRET + - name: NEXT_PUBLIC_SITE_URL + value: "https://dev.manoonoils.com" + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 50m + memory: 128Mi + startupProbe: + httpGet: + path: /favicon.ico + port: 3000 + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: + path: /favicon.ico + port: 3000 + periodSeconds: 30 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /favicon.ico + port: 3000 + periodSeconds: 5 + failureThreshold: 3 diff --git a/k8s/kustomization.yaml b/k8s/kustomization.yaml index 20719b0..21cb583 100644 --- a/k8s/kustomization.yaml +++ b/k8s/kustomization.yaml @@ -1,6 +1,11 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - - deployment-nodejs.yaml + - deployment.yaml - service.yaml - ingress.yaml + +# Images field allows Flux ImageUpdateAutomation to update the tag +images: + - name: ghcr.io/unchainedio/manoon-headless + newTag: 467b513 # This will be updated by CI/CD diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100755 index 0000000..2235e88 --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +# CI/CD Deploy Script +# Usage: ./scripts/deploy.sh + +COMMIT_SHA=$(git rev-parse --short HEAD) +IMAGE_NAME="ghcr.io/unchainedio/manoon-headless" +IMAGE_TAG="$COMMIT_SHA" + +echo "=== Building Docker image ===" +echo "Commit: $COMMIT_SHA" +echo "Image: $IMAGE_NAME:$IMAGE_TAG" + +# Build Docker image +docker build -t "$IMAGE_NAME:$IMAGE_TAG" -t "$IMAGE_NAME:latest" . + +# Push to registry (requires GHCR auth) +echo "" +echo "=== Pushing to GHCR ===" +docker push "$IMAGE_NAME:$IMAGE_TAG" +docker push "$IMAGE_NAME:latest" + +# Update kustomization with new tag +echo "" +echo "=== Updating kustomization.yaml ===" +sed -i "s/newTag: .*/newTag: $COMMIT_SHA # Updated by CI\/CD/" k8s/kustomization.yaml + +# Commit and push the change (this triggers Flux deployment) +echo "" +echo "=== Committing and pushing ===" +git add k8s/kustomization.yaml +git commit -m "deploy: update image to $COMMIT_SHA [ci skip]" +git push origin master + +echo "" +echo "=== Deploy complete! ===" +echo "Image: $IMAGE_NAME:$IMAGE_TAG" +echo "Flux will auto-deploy within 30 seconds"