diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c2392f1a..9b13bb02 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,95 +1,44 @@ stages: - build - - cleanup - - deploy variables: + # ๐Ÿ”ง Aktifkan Docker BuildKit (build lebih cepat & caching layer) + DOCKER_BUILDKIT: "1" + COMPOSE_DOCKER_CLI_BUILD: "1" DOCKER_DRIVER: overlay2 - IMAGE_NAME: "${CI_REGISTRY_IMAGE}/web-lti" - DEPLOY_ENV: development - KEEP_IMAGES: 3 - BUILD_MODE: static -before_script: - - echo "๐Ÿ” Logging in to GitLab Container Registry..." - - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY" + # ๐Ÿง  Nama image (pakai commit short SHA) + IMAGE_NAME: "$CI_REGISTRY_IMAGE/web-lti:development_${CI_COMMIT_SHORT_SHA}" + + # Cache npm (disimpan antar pipeline) + NPM_CACHE_DIR: "$CI_PROJECT_DIR/.npm" + +cache: + key: npm-cache + paths: + - .npm/ -# ===================================================== -# ๐Ÿงฑ BUILD IMAGE -# ===================================================== build-image: stage: build - image: docker:27.0.2 + image: docker:27.0.3 services: - docker:dind - variables: - DOCKER_TLS_CERTDIR: "" - script: | - echo "๐Ÿš€ Building Docker image for ${DEPLOY_ENV} branch..." - export TAG="${DEPLOY_ENV}_${CI_COMMIT_SHORT_SHA}" - echo "๐Ÿงฑ Tagging image as: $IMAGE_NAME:$TAG" - docker build \ - --build-arg NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL \ - --build-arg NEXT_PUBLIC_SSO_LOGIN_URL=$NEXT_PUBLIC_SSO_LOGIN_URL \ - --build-arg BUILD_MODE=$BUILD_MODE \ - -t "$IMAGE_NAME:$TAG" \ - -t "$IMAGE_NAME:$DEPLOY_ENV" . - - echo "๐Ÿ“ฆ Pushing images to registry..." - docker push "$IMAGE_NAME:$TAG" - docker push "$IMAGE_NAME:$DEPLOY_ENV" - only: - - development - -# ===================================================== -# ๐Ÿงน CLEANUP OLD IMAGES (KEEP 3) -# ===================================================== -cleanup-registry: - stage: cleanup - image: alpine:3.20 - script: | - apk add --no-cache curl jq - echo "๐Ÿงน Cleaning up old images (keeping ${KEEP_IMAGES})..." - - TOKEN=$(curl --silent --request POST --header "Content-Type: application/json" \ - --data "{\"login\": \"$GITLAB_USER\", \"password\": \"$GITLAB_TOKEN\"}" \ - "${CI_REGISTRY}/jwt/auth" | jq -r '.token') - - ALL_TAGS=$(curl --silent --header "Authorization: Bearer $TOKEN" \ - "${CI_REGISTRY}/v2/${CI_PROJECT_PATH}/web-lti/tags/list" \ - | jq -r ".tags | sort | reverse | .[${KEEP_IMAGES}:]" | jq -r '.[]') - - for tag in $ALL_TAGS; do - echo "๐Ÿ—‘๏ธ Deleting old image tag: $tag" - DIGEST=$(curl --silent -H "Authorization: Bearer $TOKEN" \ - "${CI_REGISTRY}/v2/${CI_PROJECT_PATH}/web-lti/manifests/$tag" | jq -r '.config.digest') - curl --silent -X DELETE -H "Authorization: Bearer $TOKEN" \ - "${CI_REGISTRY}/v2/${CI_PROJECT_PATH}/web-lti/manifests/${DIGEST}" || true - done - only: - - development - when: always - -# ===================================================== -# ๐Ÿš€ DEPLOY TO SERVER (VIA SSH) -# ===================================================== -deploy: - stage: deploy - image: alpine:3.20 before_script: - - apk add --no-cache openssh - - mkdir -p ~/.ssh - - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa - - chmod 600 ~/.ssh/id_rsa - - ssh-keyscan -H "$SERVER_IP" >> ~/.ssh/known_hosts - script: | - echo "๐Ÿš€ Deploying $IMAGE_NAME:$DEPLOY_ENV to $SERVER_USER@$SERVER_IP" - ssh $SERVER_USER@$SERVER_IP " - docker login -u '$GITLAB_USER' -p '$GITLAB_TOKEN' $CI_REGISTRY && - docker pull $IMAGE_NAME:$DEPLOY_ENV && - docker compose -f /home/devops/docker/deployment/development/compose/docker-compose.web-lti.yaml up -d dev-web-lti && - docker image prune -f - " - only: - - development \ No newline at end of file + - echo "๐Ÿ” Logging in to GitLab Container Registry..." + - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY" + + script: + - echo "๐Ÿšง Building optimized Docker image..." + - docker build --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $CI_REGISTRY_IMAGE/web-lti:latest -t "$IMAGE_NAME" . + - docker push "$IMAGE_NAME" + + # ๐Ÿงน Keep only last 3 images (hapus yang lama) + - echo "๐Ÿงน Cleaning old images..." + - docker image prune -af --filter "until=72h" + + after_script: + - echo "โœ… Build complete: $IMAGE_NAME" + + rules: + - if: '$CI_COMMIT_BRANCH == "development"' \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a7273724..f625ce73 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,56 +1,52 @@ # ============================================================ -# ๐Ÿ—๏ธ Stage 1 โ€” Builder +# ๐Ÿ—๏ธ Stage 1 โ€” Builder # ============================================================ FROM node:20-alpine AS builder -# Install hanya yang diperlukan untuk build +# Install dependensi dasar RUN apk add --no-cache git bash build-base curl WORKDIR /app -# Copy dependency list terlebih dahulu agar cache efektif +# Copy dependencies terlebih dahulu agar cache efisien COPY package*.json ./ -# Gunakan npm ci (lebih cepat, konsisten) -RUN npm ci --omit=dev +# Pastikan npm up to date agar mendukung flag terbaru +RUN npm install -g npm@11 && npm --version -# Copy source code terakhir +# Install dependency tanpa devDependencies (aman di semua npm versi) +RUN npm ci --only=production + +# Copy seluruh source COPY . . -# Buat config agar Next tahu mode static export +# Buat konfigurasi output Next.js RUN echo "const config = { output: 'export', images: { unoptimized: true } }; export default config;" > next.config.mjs -# Build Next.js tanpa Turbopack, lalu hapus cache npm +# Build project (disable Turbopack agar tidak makan RAM) ENV NEXT_DISABLE_TURBOPACK=1 RUN npx next build && npm cache clean --force -# Tambahkan cache folder _next agar bisa dilayani oleh server +# Siapkan folder static untuk serve RUN mkdir -p .next/server/app/_next && \ cp -r .next/static .next/server/app/_next/static && \ cp -r public/assets .next/server/app/ || true # ============================================================ -# ๐Ÿงฑ Stage 2 โ€” Runtime (super ringan) +# ๐Ÿงฑ Stage 2 โ€” Runtime # ============================================================ FROM node:20-alpine AS runtime -# Install hanya 1 dependency ringan untuk serving static file -RUN npm install -g serve && apk add --no-cache tini +RUN apk add --no-cache tini && npm install -g serve WORKDIR /app -# Copy hasil build dari stage sebelumnya COPY --from=builder /app/.next/server/app ./server -COPY --from=builder /app/.next/server/app/_next ./server/_next COPY --from=builder /app/public ./public -# Set environment minimal ENV NODE_ENV=production ENV PORT=3000 EXPOSE 3000 - -# Jalankan lewat tini untuk handle signal & memory leak ENTRYPOINT ["/sbin/tini", "--"] - CMD ["serve", "-s", "server", "-l", "3000"] \ No newline at end of file