From aba7a4c8fc135fadda8981f526b4fc3d8b9b3d4e Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Mon, 9 Mar 2026 10:35:37 +0800 Subject: [PATCH 1/3] update workflow --- .github/workflows/publish.yml | 22 +++---- .github/workflows/re-pull.yml | 37 ++++++++++++ .github/workflows/script/re-pull.sh | 93 +++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/re-pull.yml create mode 100644 .github/workflows/script/re-pull.sh diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 190e112..2774e8d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,18 +3,20 @@ name: Publish Docker to GHCR on: workflow_dispatch: inputs: - environment: - description: "Target environment" + stack_env: + description: "stack env" required: true type: choice - default: "development" + default: "dev" options: - - development - - production - - staging + - dev + - prod + - stg tag: - description: "Image tag (e.g. v1.0.0)" + description: "Image tag (e.g. 1.0.0)" required: true + default: "1.0.0" + env: REGISTRY: ghcr.io @@ -22,7 +24,7 @@ env: jobs: publish: - name: Build & Push to GHCR (${{ github.event.inputs.environment }}) + name: Build & Push to GHCR ${{ github.repository }}:${{ github.event.inputs.stack_env }}-${{ github.event.inputs.tag }} runs-on: ubuntu-latest permissions: contents: read @@ -59,8 +61,8 @@ jobs: with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | - type=raw,value=${{ github.event.inputs.environment }}-${{ github.event.inputs.tag }} - type=raw,value=${{ github.event.inputs.environment }}-latest + 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 diff --git a/.github/workflows/re-pull.yml b/.github/workflows/re-pull.yml new file mode 100644 index 0000000..3ddf162 --- /dev/null +++ b/.github/workflows/re-pull.yml @@ -0,0 +1,37 @@ +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 }} \ No newline at end of file diff --git a/.github/workflows/script/re-pull.sh b/.github/workflows/script/re-pull.sh new file mode 100644 index 0000000..8097813 --- /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 93e7f33f7c811fca1707b3cf88a1f652f61f8243 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Mon, 9 Mar 2026 10:36:31 +0800 Subject: [PATCH 2/3] update version --- src/app/api/version-app/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index 2ae3ab5..e8528e1 100644 --- a/src/app/api/version-app/route.ts +++ b/src/app/api/version-app/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from "next/server"; export async function GET(request: Request) { try { - return NextResponse.json({ success: true, version: "2.1.3", tahap: "beta", update: "-revisi api mobile pengumuman, diskusi umum dan diskusi divisi; -ditambah kan file " }, { status: 200 }); + return NextResponse.json({ success: true, version: "2.1.4", tahap: "beta", update: "-revisi api mobile pengumuman, diskusi umum dan diskusi divisi; -ditambah kan file " }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); From 079395654d282e7444ba52a2c3af58d85e1e1f14 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Mon, 9 Mar 2026 11:34:13 +0800 Subject: [PATCH 3/3] update version dan data seeder --- src/app/api/version-app/route.ts | 2 +- src/module/seeder/data/user.json | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index e8528e1..3b5f624 100644 --- a/src/app/api/version-app/route.ts +++ b/src/app/api/version-app/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from "next/server"; export async function GET(request: Request) { try { - return NextResponse.json({ success: true, version: "2.1.4", tahap: "beta", update: "-revisi api mobile pengumuman, diskusi umum dan diskusi divisi; -ditambah kan file " }, { status: 200 }); + return NextResponse.json({ success: true, version: "2.1.6", tahap: "beta", update: "-revisi api mobile pengumuman, diskusi umum dan diskusi divisi; -ditambah kan file " }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); diff --git a/src/module/seeder/data/user.json b/src/module/seeder/data/user.json index 2be17b5..7f44b07 100644 --- a/src/module/seeder/data/user.json +++ b/src/module/seeder/data/user.json @@ -19,7 +19,7 @@ "idPosition": "pos_ketua_rt01", "nik": "3201010101010001", "name": "Juli Ningrum", - "phone": "081234567890", + "phone": "6281234567890", "email": "juliningrum@gmail.com", "gender": "F" }, @@ -31,7 +31,7 @@ "idPosition": "pos_sekretaris_rt01", "nik": "3201010101010002", "name": "Salwa Kusmawati", - "phone": "081234567891", + "phone": "6281234567891", "email": "salwakusmawati@gmail.com", "gender": "F" }, @@ -43,7 +43,7 @@ "idPosition": "pos_staff_rt01", "nik": "3201010101010005", "name": "Bakidin Wibowo", - "phone": "081234567894", + "phone": "6281234567894", "email": "bakidinwibowo@gmail.com", "gender": "M" }, @@ -55,7 +55,7 @@ "idPosition": "pos_staff_rt01", "nik": "3201010101010006", "name": "Jais Kurniawan", - "phone": "081234567895", + "phone": "6281234567895", "email": "jaiskurniawan@gmail.com", "gender": "M" }, @@ -67,7 +67,7 @@ "idPosition": "pos_staff_rt01", "nik": "3201010101010007", "name": "Safira Oktaviani S.I.Kom", - "phone": "081234567896", + "phone": "6281234567896", "email": "safiraoktaviani@gmail.com", "gender": "F" }, @@ -79,7 +79,7 @@ "idPosition": "pos_staff_rt01", "nik": "3201010101010008", "name": "Agus Setiawan", - "phone": "081234567897", + "phone": "6281234567897", "email": "agussetiawannn@gmail.com", "gender": "M" }