mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
Penyesuaian flow repo
This commit is contained in:
+134
-170
@@ -1,185 +1,149 @@
|
|||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
|
- migrate
|
||||||
- deploy
|
- deploy
|
||||||
|
- seed
|
||||||
|
|
||||||
# ==========================================================
|
|
||||||
# ✅ Global defaults
|
|
||||||
# ==========================================================
|
|
||||||
default:
|
default:
|
||||||
tags:
|
tags:
|
||||||
- server-development-biznet
|
- self-hosted-stg
|
||||||
interruptible: true
|
|
||||||
|
|
||||||
# ==========================================================
|
workflow:
|
||||||
# 🏗️ Build Template
|
rules:
|
||||||
# ==========================================================
|
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "staging"'
|
||||||
.build_template: &build_template
|
when: always
|
||||||
|
- when: never
|
||||||
|
|
||||||
|
variables:
|
||||||
|
DOCKER_BUILDKIT: "1"
|
||||||
|
IMAGE_TAG: "staging_${CI_COMMIT_SHORT_SHA}"
|
||||||
|
IMAGE_NAME: "${CI_REGISTRY_IMAGE}:${IMAGE_TAG}"
|
||||||
|
IMAGE_LATEST: "${CI_REGISTRY_IMAGE}:staging_latest"
|
||||||
|
DEPLOY_DIR: "/opt/deploy/stg-lti-api"
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# BUILD (AUTO)
|
||||||
|
# =========================
|
||||||
|
build_staging:
|
||||||
stage: build
|
stage: build
|
||||||
image: node:20-alpine
|
rules:
|
||||||
cache:
|
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "staging"'
|
||||||
key: npm-cache
|
script: |
|
||||||
paths:
|
set -e
|
||||||
- node_modules/
|
docker info
|
||||||
variables:
|
echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
|
||||||
NPM_CONFIG_PRODUCTION: 'false'
|
|
||||||
NODE_ENV: ''
|
|
||||||
script:
|
|
||||||
- echo "Installing dependencies..."
|
|
||||||
- npm ci --no-audit --no-fund
|
|
||||||
- echo "Build env used:"
|
|
||||||
- echo "NEXT_PUBLIC_LTI_URL=$NEXT_PUBLIC_LTI_URL"
|
|
||||||
- echo "NEXT_PUBLIC_SSO_LOGIN_URL=$NEXT_PUBLIC_SSO_LOGIN_URL"
|
|
||||||
- echo "NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL"
|
|
||||||
- echo "Building Next.js static export..."
|
|
||||||
- npx next build
|
|
||||||
- |
|
|
||||||
mkdir -p out
|
|
||||||
cat <<EOF > out/build-info.json
|
|
||||||
{
|
|
||||||
"commit": "$CI_COMMIT_SHORT_SHA",
|
|
||||||
"pipeline": "$CI_PIPELINE_ID",
|
|
||||||
"built_at": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
|
|
||||||
"NEXT_PUBLIC_LTI_URL": "$NEXT_PUBLIC_LTI_URL",
|
|
||||||
"NEXT_PUBLIC_SSO_LOGIN_URL": "$NEXT_PUBLIC_SSO_LOGIN_URL",
|
|
||||||
"NEXT_PUBLIC_API_BASE_URL": "$NEXT_PUBLIC_API_BASE_URL"
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
artifacts:
|
|
||||||
name: 'out-$CI_COMMIT_SHORT_SHA'
|
|
||||||
paths:
|
|
||||||
- out/
|
|
||||||
expire_in: 1 week
|
|
||||||
|
|
||||||
# ==========================================================
|
echo "✅ Build image: $IMAGE_NAME"
|
||||||
# 🚀 Deploy Template
|
docker build -t "$IMAGE_NAME" -f Dockerfile .
|
||||||
# ==========================================================
|
|
||||||
.deploy_template: &deploy_template
|
echo "✅ Push image: $IMAGE_NAME"
|
||||||
|
docker push "$IMAGE_NAME"
|
||||||
|
|
||||||
|
echo "✅ Tag latest: $IMAGE_LATEST"
|
||||||
|
docker tag "$IMAGE_NAME" "$IMAGE_LATEST"
|
||||||
|
docker push "$IMAGE_LATEST"
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# MIGRATE (AUTO) - migrations diambil dari repo GitLab
|
||||||
|
# =========================
|
||||||
|
migrate_staging:
|
||||||
|
stage: migrate
|
||||||
|
rules:
|
||||||
|
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "staging"'
|
||||||
|
needs:
|
||||||
|
- job: build_staging
|
||||||
|
artifacts: false
|
||||||
|
script: |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# ✅ Load env dari server (.env hanya ada di server)
|
||||||
|
cd "$DEPLOY_DIR"
|
||||||
|
test -f .env || (echo "❌ .env not found in $DEPLOY_DIR" && exit 1)
|
||||||
|
set -a
|
||||||
|
. ./.env
|
||||||
|
set +a
|
||||||
|
|
||||||
|
# ✅ Generate DATABASE_URL dari DB_*
|
||||||
|
test -n "$DB_HOST" || (echo "❌ DB_HOST empty" && exit 1)
|
||||||
|
test -n "$DB_PORT" || (echo "❌ DB_PORT empty" && exit 1)
|
||||||
|
test -n "$DB_USER" || (echo "❌ DB_USER empty" && exit 1)
|
||||||
|
test -n "$DB_PASSWORD" || (echo "❌ DB_PASSWORD empty" && exit 1)
|
||||||
|
test -n "$DB_NAME" || (echo "❌ DB_NAME empty" && exit 1)
|
||||||
|
|
||||||
|
export DATABASE_URL="postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=${DB_SSLMODE:-disable}"
|
||||||
|
echo "✅ DATABASE_URL ready"
|
||||||
|
|
||||||
|
# ✅ migrations dari repo
|
||||||
|
echo "✅ Checking migrations from repo..."
|
||||||
|
ls -lah "$CI_PROJECT_DIR/internal/database/migrations"
|
||||||
|
|
||||||
|
echo "✅ Running migrations via migrate/migrate container"
|
||||||
|
set +e
|
||||||
|
docker run --rm \
|
||||||
|
-v "$CI_PROJECT_DIR/internal/database/migrations:/migrations:ro" \
|
||||||
|
migrate/migrate:v4.15.2 \
|
||||||
|
-path=/migrations -database "$DATABASE_URL" up
|
||||||
|
code=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ $code -eq 0 ]; then
|
||||||
|
echo "✅ Migration applied successfully"
|
||||||
|
elif [ $code -eq 1 ]; then
|
||||||
|
echo "✅ No change (already up to date)"
|
||||||
|
else
|
||||||
|
echo "❌ Migration failed with exit code $code"
|
||||||
|
exit $code
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# DEPLOY (AUTO)
|
||||||
|
# =========================
|
||||||
|
deploy_staging:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
image:
|
|
||||||
name: amazon/aws-cli:latest
|
|
||||||
entrypoint: ['/bin/sh', '-c']
|
|
||||||
script:
|
|
||||||
- set -e
|
|
||||||
- aws --version
|
|
||||||
- echo "Cleaning up newline characters in AWS credentials..."
|
|
||||||
- export AWS_ACCESS_KEY_ID=$(echo $AWS_ACCESS_KEY_ID | tr -d '\r\n')
|
|
||||||
- export AWS_SECRET_ACCESS_KEY=$(echo $AWS_SECRET_ACCESS_KEY | tr -d '\r\n')
|
|
||||||
- echo "Deploying to s3://$S3_BUCKET in region $AWS_REGION"
|
|
||||||
- aws s3api head-bucket --bucket "$S3_BUCKET" --region "$AWS_REGION" || aws s3api create-bucket --bucket "$S3_BUCKET" --region "$AWS_REGION" --create-bucket-configuration LocationConstraint="$AWS_REGION"
|
|
||||||
- aws s3 sync ./out "s3://$S3_BUCKET" --delete --region "$AWS_REGION" --endpoint-url "https://s3.ap-southeast-3.amazonaws.com"
|
|
||||||
|
|
||||||
# CloudFront invalidation
|
|
||||||
- |
|
|
||||||
STATUS="success"
|
|
||||||
if [ -n "$CLOUDFRONT_DISTRIBUTION_ID" ]; then
|
|
||||||
echo "Invalidating CloudFront cache..."
|
|
||||||
if ! aws cloudfront create-invalidation --distribution-id "$CLOUDFRONT_DISTRIBUTION_ID" --paths "/*"; then
|
|
||||||
echo "CloudFront invalidation failed."
|
|
||||||
STATUS="failed"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "No CloudFront distribution specified — skipping invalidation"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Notifikasi Discord
|
|
||||||
- |
|
|
||||||
RUN_URL="${CI_PROJECT_URL}/-/pipelines/${CI_PIPELINE_ID}"
|
|
||||||
|
|
||||||
if [ "$CI_COMMIT_BRANCH" = "development" ]; then
|
|
||||||
ENVIRONMENT_NAME="WEB-LTI-DEV"
|
|
||||||
elif [ "$CI_COMMIT_BRANCH" = "staging" ]; then
|
|
||||||
ENVIRONMENT_NAME="WEB-LTI-STAGING"
|
|
||||||
else
|
|
||||||
ENVIRONMENT_NAME="UNKNOWN"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$STATUS" = "success" ]; then
|
|
||||||
COLOR=3066993
|
|
||||||
TITLE="✅ Deployment ${ENVIRONMENT_NAME} Succeeded"
|
|
||||||
DESC="Deployment job on branch \${CI_COMMIT_REF_NAME}\ completed successfully."
|
|
||||||
else
|
|
||||||
COLOR=15158332
|
|
||||||
TITLE="❌ Deployment ${ENVIRONMENT_NAME} Failed"
|
|
||||||
DESC="Deployment job on branch \${CI_COMMIT_REF_NAME}\ encountered issues."
|
|
||||||
fi
|
|
||||||
|
|
||||||
jq -n \
|
|
||||||
--arg title "$TITLE" \
|
|
||||||
--arg desc "$DESC" \
|
|
||||||
--arg color "$COLOR" \
|
|
||||||
--arg repo "$CI_PROJECT_PATH" \
|
|
||||||
--arg actor "$GITLAB_USER_LOGIN" \
|
|
||||||
--arg commit "$CI_COMMIT_SHA" \
|
|
||||||
--arg run_url "$RUN_URL" \
|
|
||||||
'{
|
|
||||||
username: "CI Bot - LTI WEB",
|
|
||||||
embeds: [{
|
|
||||||
title: $title,
|
|
||||||
description: $desc,
|
|
||||||
color: ($color|tonumber),
|
|
||||||
fields: [
|
|
||||||
{name: "Repository", value: $repo, inline: true},
|
|
||||||
{name: "Actor", value: $actor, inline: true},
|
|
||||||
{name: "Commit", value: $commit, inline: false},
|
|
||||||
{name: "Pipeline", value: ("[Open run](" + $run_url + ")"), inline: false}
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
}' > payload.json
|
|
||||||
|
|
||||||
curl -sS -H "Content-Type: application/json" -d @payload.json "$DISCORD_WEBHOOK_URL"
|
|
||||||
|
|
||||||
# ==========================================================
|
|
||||||
# ==== DEVELOPMENT (Branch development) ======
|
|
||||||
# ==========================================================
|
|
||||||
build:dev:
|
|
||||||
<<: *build_template
|
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_COMMIT_BRANCH == "development"'
|
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "staging"'
|
||||||
environment:
|
needs:
|
||||||
name: development
|
- job: migrate_staging
|
||||||
variables:
|
artifacts: false
|
||||||
NEXT_PUBLIC_LTI_URL: 'https://dev-lti-erp.mbugroup.id'
|
- job: build_staging
|
||||||
NEXT_PUBLIC_SSO_LOGIN_URL: 'https://dev-auth-erp.mbugroup.id'
|
artifacts: false
|
||||||
NEXT_PUBLIC_API_BASE_URL: 'https://dev-api-lti.mbugroup.id/api'
|
script: |
|
||||||
NEXT_PUBLIC_CLIENT_ID: 'Lumbung-Telur-Indonesia'
|
set -e
|
||||||
|
|
||||||
deploy:dev:
|
docker info
|
||||||
<<: *deploy_template
|
echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
|
||||||
needs: ['build:dev']
|
|
||||||
rules:
|
|
||||||
- if: '$CI_COMMIT_BRANCH == "development"'
|
|
||||||
variables:
|
|
||||||
S3_BUCKET: 'dev-lti-erp.mbugroup.id'
|
|
||||||
AWS_REGION: 'ap-southeast-3'
|
|
||||||
CLOUDFRONT_DISTRIBUTION_ID: 'E1Z8XTA8XF1GIV'
|
|
||||||
environment:
|
|
||||||
name: development
|
|
||||||
url: https://dev-lti-erp.mbugroup.id
|
|
||||||
|
|
||||||
# ==========================================================
|
cd "$DEPLOY_DIR"
|
||||||
# ====== STAGING (Branch staging) ======
|
test -f docker-compose.yaml || (echo "❌ docker-compose.yaml not found in $DEPLOY_DIR" && exit 1)
|
||||||
# ==========================================================
|
test -f .env || (echo "❌ .env not found in $DEPLOY_DIR" && exit 1)
|
||||||
build:staging:
|
|
||||||
<<: *build_template
|
|
||||||
rules:
|
|
||||||
- if: '$CI_COMMIT_BRANCH == "staging"'
|
|
||||||
environment:
|
|
||||||
name: staging
|
|
||||||
variables:
|
|
||||||
NEXT_PUBLIC_LTI_URL: 'https://stg-lti-erp.mbugroup.id'
|
|
||||||
NEXT_PUBLIC_SSO_LOGIN_URL: 'https://stg-auth-erp.mbugroup.id'
|
|
||||||
NEXT_PUBLIC_API_BASE_URL: 'https://stg-api-lti.mbugroup.id/api'
|
|
||||||
NEXT_PUBLIC_CLIENT_ID: 'Lumbung-Telur-Indonesia'
|
|
||||||
|
|
||||||
deploy:staging:
|
docker compose pull
|
||||||
<<: *deploy_template
|
docker compose up -d --force-recreate
|
||||||
needs: ['build:staging']
|
docker image prune -f
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# SEED (MANUAL)
|
||||||
|
# =========================
|
||||||
|
seed_staging:
|
||||||
|
stage: seed
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_COMMIT_BRANCH == "staging"'
|
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "staging"'
|
||||||
variables:
|
needs:
|
||||||
S3_BUCKET: 'stg-lti-erp.mbugroup.id'
|
- job: deploy_staging
|
||||||
AWS_REGION: 'ap-southeast-3'
|
artifacts: false
|
||||||
CLOUDFRONT_DISTRIBUTION_ID: 'E2V6PPO1AUIU7H'
|
when: manual
|
||||||
environment:
|
allow_failure: false
|
||||||
name: staging
|
script: |
|
||||||
url: https://stg-lti-erp.mbugroup.id
|
set -e
|
||||||
|
|
||||||
|
cd "$DEPLOY_DIR"
|
||||||
|
test -f docker-compose.yaml || (echo "❌ docker-compose.yaml not found in $DEPLOY_DIR" && exit 1)
|
||||||
|
test -f .env || (echo "❌ .env not found in $DEPLOY_DIR" && exit 1)
|
||||||
|
|
||||||
|
echo "✅ Pull latest seed image"
|
||||||
|
docker compose pull seed || true
|
||||||
|
|
||||||
|
echo "🌱 Running seeder..."
|
||||||
|
docker compose run --rm seed
|
||||||
|
|
||||||
|
echo "✅ Seed completed"
|
||||||
|
|||||||
+28
-15
@@ -1,25 +1,38 @@
|
|||||||
FROM node:20-alpine
|
# =========================
|
||||||
|
# Builder stage
|
||||||
RUN apk add --no-cache git bash build-base curl
|
# =========================
|
||||||
|
FROM golang:1.23-alpine AS builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache git ca-certificates tzdata
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package*.json ./
|
COPY go.mod go.sum ./
|
||||||
RUN npm ci
|
RUN go mod download
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Buat config agar Next tahu output: export
|
# Build API binary
|
||||||
RUN echo "const config = { output: 'export', images: { unoptimized: true } }; export default config;" > next.config.mjs
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
||||||
|
go build -trimpath -ldflags="-s -w" -o lti-api ./cmd/api
|
||||||
|
|
||||||
# Build project (Next.js 15 otomatis static export)
|
# Build SEED binary (pastikan cmd/seed ada)
|
||||||
RUN NEXT_DISABLE_TURBOPACK=1 npx next build
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
||||||
|
go build -trimpath -ldflags="-s -w" -o lti-seed ./cmd/seed
|
||||||
|
|
||||||
# Copy static assets dan hasil build agar bisa diakses
|
# =========================
|
||||||
RUN mkdir -p .next/server/app/_next && \
|
# Runtime stage
|
||||||
cp -r .next/static .next/server/app/_next/static && \
|
# =========================
|
||||||
cp -r public/* .next/server/app/
|
FROM alpine:3.20
|
||||||
|
|
||||||
EXPOSE 3000
|
RUN apk add --no-cache ca-certificates tzdata curl bash postgresql-client \
|
||||||
|
&& adduser -D -H -u 10001 appuser
|
||||||
|
|
||||||
CMD ["npx", "serve", ".next/server/app", "-l", "3000"]
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /app/lti-api /app/lti-api
|
||||||
|
COPY --from=builder /app/lti-seed /app/lti-seed
|
||||||
|
|
||||||
|
USER appuser
|
||||||
|
EXPOSE 8081
|
||||||
|
|
||||||
|
CMD ["/app/lti-api"]
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
version: '3.9'
|
|
||||||
|
|
||||||
services:
|
|
||||||
dev-web-lti:
|
|
||||||
container_name: dev-web-lti
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
ports:
|
|
||||||
- '3002:3000'
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
environment:
|
|
||||||
NODE_ENV: production
|
|
||||||
APP_ENV: production
|
|
||||||
networks:
|
|
||||||
- dev-lti-network
|
|
||||||
restart: always
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpus: '3.0'
|
|
||||||
memory: 3G
|
|
||||||
reservations:
|
|
||||||
cpus: '1.0'
|
|
||||||
memory: 512M
|
|
||||||
extra_hosts:
|
|
||||||
- 'host.docker.internal:host-gateway'
|
|
||||||
# Optional: aktifkan healthcheck jika punya endpoint
|
|
||||||
# healthcheck:
|
|
||||||
# test: ["CMD-SHELL", "curl -fsS http://localhost:3000/api/healthz || exit 1"]
|
|
||||||
# interval: 10s
|
|
||||||
# timeout: 3s
|
|
||||||
# retries: 10
|
|
||||||
# start_period: 15s
|
|
||||||
|
|
||||||
networks:
|
|
||||||
dev-lti-network:
|
|
||||||
external: true
|
|
||||||
Reference in New Issue
Block a user