From 46ce16ae97d5ee6d34464d698e61d3099b698944 Mon Sep 17 00:00:00 2001 From: bipproduction Date: Mon, 2 Mar 2026 12:58:42 +0800 Subject: [PATCH 01/10] tambahanya --- .github/workflows/build.yml | 219 ------------------------------------ .github/workflows/test.yml | 55 --------- Dockerfile | 50 ++++++++ 3 files changed, 50 insertions(+), 274 deletions(-) delete mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/test.yml create mode 100644 Dockerfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 4180b9af..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,219 +0,0 @@ -name: Build And Save Log - -on: - workflow_dispatch: - inputs: - environment: - description: "Target environment (e.g., staging, production)" - required: true - default: "staging" - version: - description: "Version to deploy" - required: false - default: "latest" - -env: - APP_NAME: desa-darmasaba-action - WA_PHONE: "6289697338821,6289697338822" - -jobs: - build: - runs-on: ubuntu-latest - services: - postgres: - image: postgres:14 - env: - POSTGRES_USER: ${{ secrets.POSTGRES_USER }} - POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} - POSTGRES_DB: ${{ secrets.POSTGRES_DB }} - ports: - - 5432:5432 - options: >- - --health-cmd="pg_isready" - --health-interval=10s - --health-timeout=5s - --health-retries=5 - - steps: - # Checkout kode sumber - - name: Checkout code - uses: actions/checkout@v3 - - # Setup Bun - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - - # Cache dependencies - - name: Cache dependencies - uses: actions/cache@v3 - with: - path: .bun - key: ${{ runner.os }}-bun-${{ hashFiles('bun.lockb') }} - restore-keys: | - ${{ runner.os }}-bun- - - # Step 1: Set BRANCH_NAME based on event type - - name: Set BRANCH_NAME - run: | - if [[ "${{ github.event_name }}" == "pull_request" ]]; then - echo "BRANCH_NAME=${{ github.head_ref }}" >> $GITHUB_ENV - else - echo "BRANCH_NAME=${{ github.ref_name }}" >> $GITHUB_ENV - fi - - # Step 2: Generate APP_VERSION dynamically - - name: Set APP_VERSION - run: echo "APP_VERSION=${{ github.sha }}---$(date +%Y%m%d%H%M%S)" >> $GITHUB_ENV - - # Step 3: Kirim notifikasi ke API build Start - - name: Notify start build - run: | - IFS=',' read -ra PHONES <<< "${{ env.WA_PHONE }}" - for PHONE in "${PHONES[@]}"; do - ENCODED_TEXT=$(bun -e "console.log(encodeURIComponent('Build:start\nApp:${{ env.APP_NAME }}\nBranch:${{ env.BRANCH_NAME }}\nVersion:${{ env.APP_VERSION }}'))") - curl -X GET "https://wa.wibudev.com/code?text=$ENCODED_TEXT&nom=$PHONE" - done - - # Install dependencies - - name: Install dependencies - run: bun install - - # Konfigurasi environment variable untuk PostgreSQL dan variabel tambahan - - name: Set up environment variables - run: | - echo "DATABASE_URL=postgresql://${{ secrets.POSTGRES_USER }}:${{ secrets.POSTGRES_PASSWORD }}@localhost:5432/${{ secrets.POSTGRES_DB }}?schema=public" >> .env - echo "PORT=3000" >> .env - echo "NEXT_PUBLIC_WIBU_URL=localhost:3000" >> .env - echo "WIBU_UPLOAD_DIR=/uploads" >> .env - - # Create log file - - name: Create log file - run: touch build.txt - - # Migrasi database menggunakan Prisma - - name: Apply Prisma schema to database - run: bun prisma db push >> build.txt 2>&1 - - # Seed database (opsional) - - name: Seed database - run: | - bun prisma db seed >> build.txt 2>&1 || echo "Seed failed or no seed data found. Continuing without seed." >> build.txt - - # Build project - - name: Build project - run: bun run build >> build.txt 2>&1 - - # Ensure project directory exists - - name: Ensure /var/www/projects/${{ env.APP_NAME }} exists - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.VPS_HOST }} - username: ${{ secrets.VPS_USERNAME }} - key: ${{ secrets.VPS_SSH_KEY }} - script: | - mkdir -p /var/www/projects/${{ env.APP_NAME }} - - # Deploy to a new version directory - - name: Deploy to VPS (New Version) - uses: appleboy/scp-action@master - with: - host: ${{ secrets.VPS_HOST }} - username: ${{ secrets.VPS_USERNAME }} - key: ${{ secrets.VPS_SSH_KEY }} - source: "." - target: "/var/www/projects/${{ env.APP_NAME }}/releases/${{ env.APP_VERSION }}" - - # Set up environment variables - - name: Set up environment variables - run: | - rm -r .env - echo "DATABASE_URL=postgresql://${{ secrets.POSTGRES_USER }}:${{ secrets.POSTGRES_PASSWORD }}@localhost:5433/${{ secrets.POSTGRES_DB }}?schema=public" >> .env - echo "NEXT_PUBLIC_WIBU_URL=${{ env.APP_NAME }}" >> .env - echo "WIBU_UPLOAD_DIR=/var/www/projects/${{ env.APP_NAME }}/uploads" >> .env - - # Kirim file .env ke server - - name: Upload .env to server - uses: appleboy/scp-action@master - with: - host: ${{ secrets.VPS_HOST }} - username: ${{ secrets.VPS_USERNAME }} - key: ${{ secrets.VPS_SSH_KEY }} - source: ".env" - target: "/var/www/projects/${{ env.APP_NAME }}/releases/${{ env.APP_VERSION }}/" - - # manage deployment - - name: manage deployment - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.VPS_HOST }} - username: ${{ secrets.VPS_USERNAME }} - key: ${{ secrets.VPS_SSH_KEY }} - script: | - - # Source ~/.bashrc - source ~/.bashrc - - # Find an available port - PORT=$(curl -s -X GET https://wibu-bot.wibudev.com/api/find-port | jq -r '.[0]') - if [ -z "$PORT" ] || ! [[ "$PORT" =~ ^[0-9]+$ ]]; then - echo "Invalid or missing port from API." - exit 1 - fi - - # manage deployment - cd /var/www/projects/${{ env.APP_NAME }}/releases/${{ env.APP_VERSION }} - - # Create uploads directory - mkdir -p /var/www/projects/${{ env.APP_NAME }}/uploads - - # Install dependencies - bun install --production - - # Apply database schema - if ! bun prisma db push; then - echo "Database migration failed." - exit 1 - fi - - # Seed database (optional) - bun prisma db seed || echo "tidak membutuhkan seed" - - # Restart the application - pm2 reload ${{ env.APP_NAME }} || pm2 start "bun run start --port $PORT" --name "${{ env.APP_NAME }}-$PORT" --namespace "${{ env.APP_NAME }}" - - # Step 4: Set BUILD_STATUS based on success or failure - - name: Set BUILD_STATUS - if: success() - run: echo "BUILD_STATUS=success" >> $GITHUB_ENV - - - name: Set BUILD_STATUS on failure - if: failure() - run: echo "BUILD_STATUS=failed" >> $GITHUB_ENV - - # Update status log - - name: Update status log - if: always() - run: | - echo "=====================" >> build.txt - echo "BUILD_STATUS=${{ env.BUILD_STATUS }}" >> build.txt - echo "APP_NAME=${{ env.APP_NAME }}" >> build.txt - echo "APP_VERSION=${{ env.APP_VERSION }}" >> build.txt - echo "=====================" >> build.txt - - # Upload log to 0x0.st - - name: Upload log to 0x0.st - id: upload_log - if: always() - run: | - LOG_URL=$(curl -F "file=@build.txt" https://wibu-bot.wibudev.com/api/file ) - echo "LOG_URL=$LOG_URL" >> $GITHUB_ENV - - # Kirim notifikasi ke API - - name: Notify build success via API - if: always() - run: | - IFS=',' read -ra PHONES <<< "${{ env.WA_PHONE }}" - for PHONE in "${PHONES[@]}"; do - ENCODED_TEXT=$(bun -e "console.log(encodeURIComponent('Build:${{ env.BUILD_STATUS }}\nApp:${{ env.APP_NAME }}\nBranch:${{ env.BRANCH_NAME }}\nVersion:${{ env.APP_VERSION }}\nLog:${{ env.LOG_URL }}'))") - curl -X GET "https://wa.wibudev.com/code?text=$ENCODED_TEXT&nom=$PHONE" - done diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index ce6e2370..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: test workflows - -on: - workflow_dispatch: - inputs: - environment: - description: "Target environment (e.g., staging, production)" - required: true - default: "staging" - version: - description: "Version to deploy" - required: false - default: "latest" - -env: - APP_NAME: desa-darmasaba-action - WA_PHONE: "6289697338821,6289697338822" - -jobs: - build: - runs-on: ubuntu-latest - steps: - # Checkout kode sumber - - name: Checkout code - uses: actions/checkout@v3 - - # Setup Bun - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - - # Create log file - - name: Create log file - run: touch build.txt - - # Step 1: Set BRANCH_NAME based on event type - - name: Set BRANCH_NAME - run: | - if [[ "${{ github.event_name }}" == "pull_request" ]]; then - echo "BRANCH_NAME=${{ github.head_ref }}" >> $GITHUB_ENV - else - echo "BRANCH_NAME=${{ github.ref_name }}" >> $GITHUB_ENV - fi - - # Step 2: Generate APP_VERSION dynamically - - name: Set APP_VERSION - run: echo "APP_VERSION=${{ github.sha }}---$(date +%Y%m%d%H%M%S)" >> $GITHUB_ENV - - # Step 3: Kirim notifikasi ke API build Start - - name: Notify start build - run: | - IFS=',' read -ra PHONES <<< "${{ env.WA_PHONE }}" - for PHONE in "${PHONES[@]}"; do - ENCODED_TEXT=$(bun -e "console.log(encodeURIComponent('Build:start\nApp:${{ env.APP_NAME }}\nenv:${{ inputs.environment }}\nBranch:${{ env.BRANCH_NAME }}\nVersion:${{ env.APP_VERSION }}'))") - curl -X GET "https://wa.wibudev.com/code?text=$ENCODED_TEXT&nom=$PHONE" - done diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..dc2b93db --- /dev/null +++ b/Dockerfile @@ -0,0 +1,50 @@ +FROM debian:bookworm-slim AS base + +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl unzip ca-certificates libssl3 && \ + rm -rf /var/lib/apt/lists/* && \ + curl -fsSL https://bun.sh/install | bash + +ENV PATH="/root/.bun/bin:$PATH" +WORKDIR /app + +# ---- deps ---- +FROM base AS deps + +COPY package.json bun.lock ./ +RUN bun install --frozen-lockfile + +# ---- builder ---- +FROM deps AS builder + +COPY prisma ./prisma +RUN bunx prisma generate + +COPY src ./src +COPY types ./types +COPY tsconfig.json bunfig.toml ./ + + + +# BUN_PUBLIC_* vars are baked into the browser bundle at build time +ARG BUN_PUBLIC_BASE_URL +ENV BUN_PUBLIC_BASE_URL=${BUN_PUBLIC_BASE_URL} +RUN bun run build + +# ---- runner ---- +FROM base AS runner + +COPY package.json bun.lock ./ +RUN bun install --production + +COPY --from=builder /app/generated ./generated +COPY --from=builder /app/dist ./dist +COPY src ./src +COPY prisma ./prisma +COPY types ./types +COPY tsconfig.json bunfig.toml ./ + +ENV NODE_ENV=production +EXPOSE 3000 + +CMD ["bun", "run", "./src/index.tsx"] From 790d6535e569bf76a9a017b762161f3744422eed Mon Sep 17 00:00:00 2001 From: bipproduction Date: Mon, 2 Mar 2026 13:15:54 +0800 Subject: [PATCH 02/10] fix: perbaiki Dockerfile - lockfile, bunfig.toml, dan path build output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ganti bun.lock โ†’ bun.lockb (nama file yang benar) - Hapus bunfig.toml dari COPY (file tidak ada) - Ganti /app/dist โ†’ /app/.next (output Next.js build) Co-Authored-By: Claude Sonnet 4.6 --- Dockerfile | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index dc2b93db..b374aacf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ WORKDIR /app # ---- deps ---- FROM base AS deps -COPY package.json bun.lock ./ +COPY package.json bun.lockb ./ RUN bun install --frozen-lockfile # ---- builder ---- @@ -22,9 +22,7 @@ RUN bunx prisma generate COPY src ./src COPY types ./types -COPY tsconfig.json bunfig.toml ./ - - +COPY tsconfig.json ./ # BUN_PUBLIC_* vars are baked into the browser bundle at build time ARG BUN_PUBLIC_BASE_URL @@ -34,15 +32,15 @@ RUN bun run build # ---- runner ---- FROM base AS runner -COPY package.json bun.lock ./ +COPY package.json bun.lockb ./ RUN bun install --production COPY --from=builder /app/generated ./generated -COPY --from=builder /app/dist ./dist +COPY --from=builder /app/.next ./.next COPY src ./src COPY prisma ./prisma COPY types ./types -COPY tsconfig.json bunfig.toml ./ +COPY tsconfig.json ./ ENV NODE_ENV=production EXPOSE 3000 From 0ba30aa5b200bcebd32d5082fce611ff0c3ad55c Mon Sep 17 00:00:00 2001 From: bipproduction Date: Mon, 2 Mar 2026 13:20:05 +0800 Subject: [PATCH 03/10] tambahan --- .github/workflows/docker-publish.yml | 57 ++++++++++++++++++++++++++++ .gitignore | 1 - 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/docker-publish.yml diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 00000000..6ff1dfe0 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,57 @@ +name: Docker Build & Publish + +on: + push: + tags: + - "v*" + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + BUN_PUBLIC_BASE_URL=${{ vars.BUN_PUBLIC_BASE_URL }} + cache-from: type=gha + cache-to: type=gha,mode=max + + + \ No newline at end of file diff --git a/.gitignore b/.gitignore index ebd64b35..b4e54e0a 100644 --- a/.gitignore +++ b/.gitignore @@ -47,7 +47,6 @@ next-env.d.ts # cache /cache -.github/ .env.* From ed664d5b10535c45ccd28759345a1c8f88c35517 Mon Sep 17 00:00:00 2001 From: bipproduction Date: Mon, 2 Mar 2026 15:09:46 +0800 Subject: [PATCH 04/10] deploy docker ghcr --- .github/workflows/docker-publish.yml | 51 ++++++++-------- Dockerfile | 87 +++++++++++++++++----------- 2 files changed, 79 insertions(+), 59 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 6ff1dfe0..70ac8f87 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -1,57 +1,56 @@ -name: Docker Build & Publish - +name: Publish Docker to GHCR on: push: tags: - "v*" - env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} - jobs: - build-and-push: + publish: + name: Build & Push to GHCR runs-on: ubuntu-latest permissions: contents: read packages: write - steps: - - name: Checkout + - name: Free disk space + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/ghc + sudo rm -rf /opt/hostedtoolcache/CodeQL + sudo docker image prune --all --force + df -h + + - name: Checkout repository uses: actions/checkout@v4 + - name: Extract tag name + id: meta + run: echo "tag=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Log in to GHCR + - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=raw,value=latest - - - name: Build and push + - name: Build and push Docker image uses: docker/build-push-action@v6 with: context: . - platforms: linux/amd64 + file: ./Dockerfile push: true - tags: ${{ steps.meta.outputs.tags }} + platforms: linux/amd64 + tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.tag }} labels: ${{ steps.meta.outputs.labels }} - build-args: | - BUN_PUBLIC_BASE_URL=${{ vars.BUN_PUBLIC_BASE_URL }} cache-from: type=gha cache-to: type=gha,mode=max - - - \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index b374aacf..15c12c01 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,48 +1,69 @@ -FROM debian:bookworm-slim AS base +# ============================== +# Stage 1: Builder (Debian) +# ============================== +FROM node:20-bookworm-slim AS builder -RUN apt-get update && apt-get install -y --no-install-recommends \ - curl unzip ca-certificates libssl3 && \ - rm -rf /var/lib/apt/lists/* && \ - curl -fsSL https://bun.sh/install | bash - -ENV PATH="/root/.bun/bin:$PATH" WORKDIR /app -# ---- deps ---- -FROM base AS deps +# Install dependencies yang sebelumnya pakai apk +RUN apt-get update && apt-get install -y --no-install-recommends \ + libc6 \ + git \ + openssl \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* -COPY package.json bun.lockb ./ -RUN bun install --frozen-lockfile +COPY package.json bun.lockb* ./ -# ---- builder ---- -FROM deps AS builder +ENV ONNXRUNTIME_NODE_INSTALL_CUDA=0 +ENV SHARP_IGNORE_GLOBAL_LIBVIPS=1 -COPY prisma ./prisma -RUN bunx prisma generate +RUN npm install --ignore-scripts -COPY src ./src -COPY types ./types -COPY tsconfig.json ./ +COPY . . -# BUN_PUBLIC_* vars are baked into the browser bundle at build time -ARG BUN_PUBLIC_BASE_URL -ENV BUN_PUBLIC_BASE_URL=${BUN_PUBLIC_BASE_URL} -RUN bun run build +# Skip telemetry & limit memory +ENV NEXT_TELEMETRY_DISABLED=1 +ENV NODE_OPTIONS="--max-old-space-size=4096" -# ---- runner ---- -FROM base AS runner +# Generate prisma client +RUN npx prisma generate -COPY package.json bun.lockb ./ -RUN bun install --production +# Build Next.js +RUN npm run build -COPY --from=builder /app/generated ./generated -COPY --from=builder /app/.next ./.next -COPY src ./src -COPY prisma ./prisma -COPY types ./types -COPY tsconfig.json ./ + +# ============================== +# Stage 2: Runner (Debian) +# ============================== +FROM node:20-bookworm-slim AS runner + +WORKDIR /app ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + openssl \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Buat user non-root (cara Debian) +RUN groupadd --system --gid 1001 nodejs \ + && useradd --system --uid 1001 --gid nodejs nextjs + +COPY --from=builder /app/public ./public +COPY --from=builder /app/.next ./.next +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/package.json ./package.json + +RUN chown -R nextjs:nodejs /app + +USER nextjs + EXPOSE 3000 -CMD ["bun", "run", "./src/index.tsx"] +ENV PORT=3000 +ENV HOSTNAME="0.0.0.0" + +CMD ["npx", "next", "start"] From 7368a367f4f61a0191865aad325886b259a71967 Mon Sep 17 00:00:00 2001 From: bipproduction Date: Mon, 2 Mar 2026 15:38:49 +0800 Subject: [PATCH 05/10] chore: tambah publish.yml workflow_dispatch ke main Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/publish.yml | 60 +++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..12b09769 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,60 @@ +name: Publish Docker to GHCR +on: + workflow_dispatch: + inputs: + tag: + description: "Image tag (e.g. v1.0.0)" + required: true + default: "latest" +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} +jobs: + publish: + name: Build & Push to GHCR + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Free disk space + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/ghc + sudo rm -rf /opt/hostedtoolcache/CodeQL + sudo docker image prune --all --force + df -h + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Extract tag name + id: meta + run: echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + platforms: linux/amd64 + tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.tag }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + From df3f382a971f331cb998b3582e14d1e040366ccf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 9 Mar 2026 07:05:55 +0000 Subject: [PATCH 06/10] chore: sync workflows from base-template --- .github/workflows/publish.yml | 62 +++++++++++++++---- .github/workflows/re-pull.yml | 57 ++++++++++++++++++ .github/workflows/script/notify.sh | 26 ++++++++ .github/workflows/script/re-pull.sh | 93 +++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/re-pull.yml create mode 100644 .github/workflows/script/notify.sh create mode 100644 .github/workflows/script/re-pull.sh diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 12b09769..b8750c6a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,17 +1,30 @@ name: Publish Docker to GHCR + on: workflow_dispatch: inputs: - tag: - description: "Image tag (e.g. v1.0.0)" + stack_env: + description: "stack env" required: true - default: "latest" + type: choice + default: "dev" + options: + - dev + - prod + - stg + tag: + description: "Image tag (e.g. 1.0.0)" + required: true + default: "1.0.0" + + env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} + jobs: publish: - name: Build & Push to GHCR + name: Build & Push to GHCR ${{ github.repository }}:${{ github.event.inputs.stack_env }}-${{ github.event.inputs.tag }} runs-on: ubuntu-latest permissions: contents: read @@ -26,12 +39,10 @@ jobs: sudo docker image prune --all --force df -h - - name: Checkout repository + - name: Checkout branch ${{ github.event.inputs.stack_env }} uses: actions/checkout@v4 - - - name: Extract tag name - id: meta - run: echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT + with: + ref: ${{ github.event.inputs.stack_env }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -46,6 +57,15 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Generate image metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=${{ github.event.inputs.stack_env }}-${{ github.event.inputs.tag }} + type=raw,value=${{ github.event.inputs.stack_env }}-latest + - name: Build and push Docker image uses: docker/build-push-action@v6 with: @@ -53,8 +73,26 @@ jobs: file: ./Dockerfile push: true platforms: linux/amd64 - tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.tag }} + tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max + no-cache: true + - name: Notify success + if: success() + run: bash ./.github/workflows/script/notify.sh + env: + TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }} + TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} + NOTIFY_STATUS: success + NOTIFY_WORKFLOW: "Publish Docker" + NOTIFY_DETAIL: "Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.inputs.stack_env }}-${{ github.event.inputs.tag }}" + + - name: Notify failure + if: failure() + run: bash ./.github/workflows/script/notify.sh + env: + TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }} + TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} + NOTIFY_STATUS: failure + NOTIFY_WORKFLOW: "Publish Docker" + NOTIFY_DETAIL: "Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.inputs.stack_env }}-${{ github.event.inputs.tag }}" diff --git a/.github/workflows/re-pull.yml b/.github/workflows/re-pull.yml new file mode 100644 index 00000000..470a3603 --- /dev/null +++ b/.github/workflows/re-pull.yml @@ -0,0 +1,57 @@ +name: Re-Pull Docker +on: + workflow_dispatch: + inputs: + stack_name: + description: "stack name" + required: true + type: string + stack_env: + description: "stack env" + required: true + type: choice + default: "dev" + options: + - dev + - stg + - prod + +jobs: + publish: + name: Re-Pull Docker ${{ github.event.inputs.stack_name }} + runs-on: ubuntu-latest + environment: ${{ vars.PORTAINER_ENV || 'portainer' }} + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Deploy ke Portainer + run: bash ./.github/workflows/script/re-pull.sh + env: + PORTAINER_USERNAME: ${{ secrets.PORTAINER_USERNAME }} + PORTAINER_PASSWORD: ${{ secrets.PORTAINER_PASSWORD }} + PORTAINER_URL: ${{ secrets.PORTAINER_URL }} + STACK_NAME: ${{ github.event.inputs.stack_name }}-${{ github.event.inputs.stack_env }} + + - name: Notify success + if: success() + run: bash ./.github/workflows/script/notify.sh + env: + TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }} + TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} + NOTIFY_STATUS: success + NOTIFY_WORKFLOW: "Re-Pull Docker" + NOTIFY_DETAIL: "Stack: ${{ github.event.inputs.stack_name }}-${{ github.event.inputs.stack_env }}" + + - name: Notify failure + if: failure() + run: bash ./.github/workflows/script/notify.sh + env: + TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }} + TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} + NOTIFY_STATUS: failure + NOTIFY_WORKFLOW: "Re-Pull Docker" + NOTIFY_DETAIL: "Stack: ${{ github.event.inputs.stack_name }}-${{ github.event.inputs.stack_env }}" diff --git a/.github/workflows/script/notify.sh b/.github/workflows/script/notify.sh new file mode 100644 index 00000000..22944bfd --- /dev/null +++ b/.github/workflows/script/notify.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +: "${TELEGRAM_TOKEN:?TELEGRAM_TOKEN tidak di-set}" +: "${TELEGRAM_CHAT_ID:?TELEGRAM_CHAT_ID tidak di-set}" +: "${NOTIFY_STATUS:?NOTIFY_STATUS tidak di-set}" +: "${NOTIFY_WORKFLOW:?NOTIFY_WORKFLOW tidak di-set}" + +if [ "$NOTIFY_STATUS" = "success" ]; then + ICON="โœ…" + TEXT="${ICON} *${NOTIFY_WORKFLOW}* berhasil!" +else + ICON="โŒ" + TEXT="${ICON} *${NOTIFY_WORKFLOW}* gagal!" +fi + +if [ -n "$NOTIFY_DETAIL" ]; then + TEXT="${TEXT} +${NOTIFY_DETAIL}" +fi + +curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage" \ + -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg chat_id "$TELEGRAM_CHAT_ID" \ + --arg text "$TEXT" \ + '{chat_id: $chat_id, text: $text, parse_mode: "Markdown"}')" diff --git a/.github/workflows/script/re-pull.sh b/.github/workflows/script/re-pull.sh new file mode 100644 index 00000000..8097813e --- /dev/null +++ b/.github/workflows/script/re-pull.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +: "${PORTAINER_URL:?PORTAINER_URL tidak di-set}" +: "${PORTAINER_USERNAME:?PORTAINER_USERNAME tidak di-set}" +: "${PORTAINER_PASSWORD:?PORTAINER_PASSWORD tidak di-set}" +: "${STACK_NAME:?STACK_NAME tidak di-set}" + +echo "๐Ÿ” Autentikasi ke Portainer..." +TOKEN=$(curl -s -X POST https://${PORTAINER_URL}/api/auth \ + -H "Content-Type: application/json" \ + -d "{\"username\": \"${PORTAINER_USERNAME}\", \"password\": \"${PORTAINER_PASSWORD}\"}" \ + | jq -r .jwt) + +if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then + echo "โŒ Autentikasi gagal! Cek PORTAINER_URL, USERNAME, dan PASSWORD." + exit 1 +fi + +echo "๐Ÿ” Mencari stack: $STACK_NAME..." +STACK=$(curl -s -X GET https://${PORTAINER_URL}/api/stacks \ + -H "Authorization: Bearer ${TOKEN}" \ + | jq ".[] | select(.Name == \"$STACK_NAME\")") + +if [ -z "$STACK" ]; then + echo "โŒ Stack '$STACK_NAME' tidak ditemukan di Portainer!" + echo " Pastikan nama stack sudah benar." + exit 1 +fi + +STACK_ID=$(echo "$STACK" | jq -r .Id) +ENDPOINT_ID=$(echo "$STACK" | jq -r .EndpointId) +ENV=$(echo "$STACK" | jq '.Env // []') + +echo "๐Ÿ“„ Mengambil compose file..." +STACK_FILE=$(curl -s -X GET "https://${PORTAINER_URL}/api/stacks/${STACK_ID}/file" \ + -H "Authorization: Bearer ${TOKEN}" \ + | jq -r .StackFileContent) + +PAYLOAD=$(jq -n \ + --arg content "$STACK_FILE" \ + --argjson env "$ENV" \ + '{stackFileContent: $content, env: $env, pullImage: true}') + +echo "๐Ÿš€ Redeploying $STACK_NAME (pull latest image)..." +HTTP_STATUS=$(curl -s -o /tmp/portainer_response.json -w "%{http_code}" \ + -X PUT "https://${PORTAINER_URL}/api/stacks/${STACK_ID}?endpointId=${ENDPOINT_ID}" \ + -H "Authorization: Bearer ${TOKEN}" \ + -H "Content-Type: application/json" \ + -d "$PAYLOAD") + +if [ "$HTTP_STATUS" != "200" ]; then + echo "โŒ Redeploy gagal! HTTP Status: $HTTP_STATUS" + cat /tmp/portainer_response.json | jq . + exit 1 +fi + +echo "โณ Menunggu container running..." + +MAX_RETRY=15 +COUNT=0 + +while [ $COUNT -lt $MAX_RETRY ]; do + sleep 5 + COUNT=$((COUNT + 1)) + + CONTAINERS=$(curl -s -X GET \ + "https://${PORTAINER_URL}/api/endpoints/${ENDPOINT_ID}/docker/containers/json?all=true&filters=%7B%22label%22%3A%5B%22com.docker.compose.project%3D${STACK_NAME}%22%5D%7D" \ + -H "Authorization: Bearer ${TOKEN}") + + TOTAL=$(echo "$CONTAINERS" | jq 'length') + RUNNING=$(echo "$CONTAINERS" | jq '[.[] | select(.State == "running")] | length') + FAILED=$(echo "$CONTAINERS" | jq '[.[] | select(.State == "exited" and (.Status | test("Exited \\(0\\)") | not))] | length') + + echo "๐Ÿ”„ [${COUNT}/${MAX_RETRY}] Running: ${RUNNING} | Failed: ${FAILED} | Total: ${TOTAL}" + echo "$CONTAINERS" | jq -r '.[] | " โ†’ \(.Names[0]) | \(.State) | \(.Status)"' + + if [ "$FAILED" -gt "0" ]; then + echo "" + echo "โŒ Ada container yang crash!" + echo "$CONTAINERS" | jq -r '.[] | select(.State == "exited" and (.Status | test("Exited \\(0\\)") | not)) | " โ†’ \(.Names[0]) | \(.Status)"' + exit 1 + fi + + if [ "$RUNNING" -gt "0" ]; then + echo "" + echo "โœ… Stack $STACK_NAME berhasil di-redeploy dan running!" + exit 0 + fi +done + +echo "" +echo "โŒ Timeout! Stack tidak kunjung running setelah $((MAX_RETRY * 5)) detik." +exit 1 \ No newline at end of file From ba964df32cc1de7574d7e88e09c053d34792b34a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 9 Mar 2026 07:45:35 +0000 Subject: [PATCH 07/10] chore: sync workflows from base-template --- .github/workflows/publish.yml | 11 +++++++++-- .github/workflows/re-pull.yml | 5 ++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b8750c6a..a9608a94 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -44,6 +44,13 @@ jobs: with: ref: ${{ github.event.inputs.stack_env }} + - name: Checkout scripts from main + uses: actions/checkout@v4 + with: + ref: main + path: .ci + sparse-checkout: .github/workflows/script + - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -79,7 +86,7 @@ jobs: - name: Notify success if: success() - run: bash ./.github/workflows/script/notify.sh + run: bash ./.ci/.github/workflows/script/notify.sh env: TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }} TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} @@ -89,7 +96,7 @@ jobs: - name: Notify failure if: failure() - run: bash ./.github/workflows/script/notify.sh + run: bash ./.ci/.github/workflows/script/notify.sh env: TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }} TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} diff --git a/.github/workflows/re-pull.yml b/.github/workflows/re-pull.yml index 470a3603..e2110e79 100644 --- a/.github/workflows/re-pull.yml +++ b/.github/workflows/re-pull.yml @@ -25,8 +25,11 @@ jobs: contents: read packages: write steps: - - name: Checkout repository + - name: Checkout scripts from main uses: actions/checkout@v4 + with: + ref: main + sparse-checkout: .github/workflows/script - name: Deploy ke Portainer run: bash ./.github/workflows/script/re-pull.sh From f5566bca2cea38fe7c1adc2bfb897372b54ffdf7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 9 Mar 2026 07:53:36 +0000 Subject: [PATCH 08/10] chore: sync workflows from base-template --- .github/workflows/publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a9608a94..088f877e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,6 +26,7 @@ jobs: publish: name: Build & Push to GHCR ${{ github.repository }}:${{ github.event.inputs.stack_env }}-${{ github.event.inputs.tag }} runs-on: ubuntu-latest + environment: ${{ vars.PORTAINER_ENV || 'portainer' }} permissions: contents: read packages: write From 6d26ace8ab858f29498afe69a49bbc8617aacc9a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 10 Mar 2026 08:16:42 +0000 Subject: [PATCH 09/10] chore: sync workflows from base-template --- .github/workflows/script/re-pull.sh | 53 +++++++++++++++++++---------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/.github/workflows/script/re-pull.sh b/.github/workflows/script/re-pull.sh index 8097813e..658ec835 100644 --- a/.github/workflows/script/re-pull.sh +++ b/.github/workflows/script/re-pull.sh @@ -5,8 +5,12 @@ : "${PORTAINER_PASSWORD:?PORTAINER_PASSWORD tidak di-set}" : "${STACK_NAME:?STACK_NAME tidak di-set}" +# Timeout total: MAX_RETRY * SLEEP_INTERVAL detik +MAX_RETRY=60 # 60 ร— 10s = 10 menit +SLEEP_INTERVAL=10 + echo "๐Ÿ” Autentikasi ke Portainer..." -TOKEN=$(curl -s -X POST https://${PORTAINER_URL}/api/auth \ +TOKEN=$(curl -s -X POST "https://${PORTAINER_URL}/api/auth" \ -H "Content-Type: application/json" \ -d "{\"username\": \"${PORTAINER_USERNAME}\", \"password\": \"${PORTAINER_PASSWORD}\"}" \ | jq -r .jwt) @@ -17,13 +21,12 @@ if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then fi echo "๐Ÿ” Mencari stack: $STACK_NAME..." -STACK=$(curl -s -X GET https://${PORTAINER_URL}/api/stacks \ +STACK=$(curl -s -X GET "https://${PORTAINER_URL}/api/stacks" \ -H "Authorization: Bearer ${TOKEN}" \ | jq ".[] | select(.Name == \"$STACK_NAME\")") if [ -z "$STACK" ]; then echo "โŒ Stack '$STACK_NAME' tidak ditemukan di Portainer!" - echo " Pastikan nama stack sudah benar." exit 1 fi @@ -31,6 +34,16 @@ STACK_ID=$(echo "$STACK" | jq -r .Id) ENDPOINT_ID=$(echo "$STACK" | jq -r .EndpointId) ENV=$(echo "$STACK" | jq '.Env // []') +# โ”€โ”€ Catat container ID lama sebelum redeploy โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +echo "๐Ÿ“ธ Mencatat container aktif sebelum redeploy..." +CONTAINERS_BEFORE=$(curl -s -X GET \ + "https://${PORTAINER_URL}/api/endpoints/${ENDPOINT_ID}/docker/containers/json?all=true&filters=%7B%22label%22%3A%5B%22com.docker.compose.project%3D${STACK_NAME}%22%5D%7D" \ + -H "Authorization: Bearer ${TOKEN}") + +OLD_IDS=$(echo "$CONTAINERS_BEFORE" | jq -r '[.[] | .Id] | join(",")') +echo " Container lama: $(echo "$CONTAINERS_BEFORE" | jq -r '[.[] | .Names[0]] | join(", ")')" + +# โ”€โ”€ Ambil compose file lalu trigger redeploy โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ echo "๐Ÿ“„ Mengambil compose file..." STACK_FILE=$(curl -s -X GET "https://${PORTAINER_URL}/api/stacks/${STACK_ID}/file" \ -H "Authorization: Bearer ${TOKEN}" \ @@ -41,7 +54,7 @@ PAYLOAD=$(jq -n \ --argjson env "$ENV" \ '{stackFileContent: $content, env: $env, pullImage: true}') -echo "๐Ÿš€ Redeploying $STACK_NAME (pull latest image)..." +echo "๐Ÿš€ Triggering redeploy $STACK_NAME (pull latest image)..." HTTP_STATUS=$(curl -s -o /tmp/portainer_response.json -w "%{http_code}" \ -X PUT "https://${PORTAINER_URL}/api/stacks/${STACK_ID}?endpointId=${ENDPOINT_ID}" \ -H "Authorization: Bearer ${TOKEN}" \ @@ -54,40 +67,44 @@ if [ "$HTTP_STATUS" != "200" ]; then exit 1 fi -echo "โณ Menunggu container running..." +echo "โณ Menunggu image selesai di-pull dan container baru running..." +echo " (Timeout: $((MAX_RETRY * SLEEP_INTERVAL)) detik)" -MAX_RETRY=15 COUNT=0 - while [ $COUNT -lt $MAX_RETRY ]; do - sleep 5 + sleep $SLEEP_INTERVAL COUNT=$((COUNT + 1)) CONTAINERS=$(curl -s -X GET \ "https://${PORTAINER_URL}/api/endpoints/${ENDPOINT_ID}/docker/containers/json?all=true&filters=%7B%22label%22%3A%5B%22com.docker.compose.project%3D${STACK_NAME}%22%5D%7D" \ -H "Authorization: Bearer ${TOKEN}") - TOTAL=$(echo "$CONTAINERS" | jq 'length') - RUNNING=$(echo "$CONTAINERS" | jq '[.[] | select(.State == "running")] | length') - FAILED=$(echo "$CONTAINERS" | jq '[.[] | select(.State == "exited" and (.Status | test("Exited \\(0\\)") | not))] | length') + # Container baru = ID tidak ada di daftar container lama + NEW_RUNNING=$(echo "$CONTAINERS" | jq \ + --arg old "$OLD_IDS" \ + '[.[] | select(.State == "running" and ((.Id) as $id | ($old | split(",") | index($id)) == null))] | length') - echo "๐Ÿ”„ [${COUNT}/${MAX_RETRY}] Running: ${RUNNING} | Failed: ${FAILED} | Total: ${TOTAL}" - echo "$CONTAINERS" | jq -r '.[] | " โ†’ \(.Names[0]) | \(.State) | \(.Status)"' + FAILED=$(echo "$CONTAINERS" | jq \ + '[.[] | select(.State == "exited" and (.Status | test("Exited \\(0\\)") | not) and (.Names[0] | test("seed") | not))] | length') + + echo "๐Ÿ”„ [$((COUNT * SLEEP_INTERVAL))s / $((MAX_RETRY * SLEEP_INTERVAL))s] Container baru running: ${NEW_RUNNING} | Gagal: ${FAILED}" + echo "$CONTAINERS" | jq -r '.[] | " โ†’ \(.Names[0]) | \(.State) | \(.Status) | id: \(.Id[:12])"' if [ "$FAILED" -gt "0" ]; then echo "" echo "โŒ Ada container yang crash!" - echo "$CONTAINERS" | jq -r '.[] | select(.State == "exited" and (.Status | test("Exited \\(0\\)") | not)) | " โ†’ \(.Names[0]) | \(.Status)"' + echo "$CONTAINERS" | jq -r '.[] | select(.State == "exited" and (.Status | test("Exited \\(0\\)") | not) and (.Names[0] | test("seed") | not)) | " โ†’ \(.Names[0]) | \(.Status)"' exit 1 fi - if [ "$RUNNING" -gt "0" ]; then + if [ "$NEW_RUNNING" -gt "0" ]; then echo "" - echo "โœ… Stack $STACK_NAME berhasil di-redeploy dan running!" + echo "โœ… Stack $STACK_NAME berhasil di-redeploy dengan image baru dan running!" exit 0 fi done echo "" -echo "โŒ Timeout! Stack tidak kunjung running setelah $((MAX_RETRY * 5)) detik." -exit 1 \ No newline at end of file +echo "โŒ Timeout $((MAX_RETRY * SLEEP_INTERVAL))s! Container baru tidak kunjung running." +echo " Kemungkinan image masih dalam proses pull atau ada error di server." +exit 1 From 6064ef07599de1dc9d59094afd2f993103309f8e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 12 Mar 2026 06:48:18 +0000 Subject: [PATCH 10/10] chore: sync workflows from base-template --- .github/workflows/script/re-pull.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/script/re-pull.sh b/.github/workflows/script/re-pull.sh index 658ec835..0aa3fa60 100644 --- a/.github/workflows/script/re-pull.sh +++ b/.github/workflows/script/re-pull.sh @@ -97,11 +97,21 @@ while [ $COUNT -lt $MAX_RETRY ]; do exit 1 fi - if [ "$NEW_RUNNING" -gt "0" ]; then + if [ "$NEW_RUNNING" -gt "0" ]; then + # Cleanup dangling images setelah redeploy sukses + echo "๐Ÿงน Membersihkan dangling images..." + curl -s -X POST "https://${PORTAINER_URL}/api/endpoints/${ENDPOINT_ID}/docker/images/prune" \ + -H "Authorization: Bearer ${TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{"filters":{"dangling":["true"]}}' | jq -r '" Reclaimed: \(.SpaceReclaimed // 0 | . / 1073741824 | tostring | .[0:5]) GB"' + + echo "โœ… Cleanup selesai!" echo "" echo "โœ… Stack $STACK_NAME berhasil di-redeploy dengan image baru dan running!" exit 0 fi + + done echo ""