From 48bb30b0096b4df60e94b563376825f9cc2ca921 Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Fri, 10 Apr 2026 20:03:19 +0700 Subject: [PATCH] feat(cicd): switch CI/CD from GitHub Actions to Gitea Actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add .gitea/workflows/deploy.yaml (detect changes → docker build → Harbor push → kubectl deploy) - Add gitea-sync-cronjob.yaml (GitHub → Gitea mirror sync every 5 min) - Add act-runner-rbac.yaml (RBAC for act_runner to deploy to staging namespace) - Add setup-secrets.sh (one-time cluster secret setup script) - Disable GitHub Actions deploy-staging.yml (CI/CD now via Gitea) Flow: GitHub push → Gitea sync (5min) → Gitea Actions → Docker build → Harbor → K8s Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitea/workflows/deploy.yaml | 223 ++++++++++++++++++ .github/workflows/deploy-staging.yml | 22 +- .../staging/kubernetes/act-runner-rbac.yaml | 46 ++++ .../kubernetes/gitea-sync-cronjob.yaml | 69 ++++++ .../staging/kubernetes/setup-secrets.sh | 82 +++++++ 5 files changed, 436 insertions(+), 6 deletions(-) create mode 100644 .gitea/workflows/deploy.yaml create mode 100644 deployments/staging/kubernetes/act-runner-rbac.yaml create mode 100644 deployments/staging/kubernetes/gitea-sync-cronjob.yaml create mode 100755 deployments/staging/kubernetes/setup-secrets.sh diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml new file mode 100644 index 00000000..6e59c188 --- /dev/null +++ b/.gitea/workflows/deploy.yaml @@ -0,0 +1,223 @@ +# EN: Build & Deploy GoodGo Platform to K8s staging via Gitea Actions +# VI: Build & Deploy GoodGo Platform len K8s staging qua Gitea Actions +# +# Flow: Push → detect changed services → docker build → push Harbor → kubectl deploy +# Runner: act_runner (host mode, in-cluster kubectl) + +name: Build & Deploy to K8s + +on: + push: + branches: [master, main] + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - name: Clone source from GitHub + run: | + git clone --depth 2 --branch ${{ github.ref_name }} \ + https://admin:Velik%402026@gitea.techbi.org/admin/pos-system.git . + + - name: Detect changed services + run: | + IMAGE_TAG=$(echo "${{ github.sha }}" | cut -c1-7) + echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV + + CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "ALL") + + SERVICES="" + + check_change() { + local path="$1" + local name="$2" + if echo "$CHANGED" | grep -q "^${path}/" || echo "$CHANGED" | grep -q "ALL"; then + SERVICES="${SERVICES} ${name}" + fi + } + + # Core services + check_change "services/iam-service-net" "iam-service" + check_change "services/merchant-service-net" "merchant-service" + check_change "services/order-service-net" "order-service" + check_change "services/fnb-engine-net" "fnb-engine" + check_change "services/catalog-service-net" "catalog-service" + check_change "services/inventory-service-net" "inventory-service" + check_change "services/wallet-service-net" "wallet-service" + check_change "services/storage-service-net" "storage-service" + check_change "services/booking-service-net" "booking-service" + check_change "services/chat-service-net" "chat-service" + check_change "services/social-service-net" "social-service" + check_change "services/promotion-service-net" "promotion-service" + check_change "services/membership-service-net" "membership-service" + check_change "services/mining-service-net" "mining-service" + check_change "services/mission-service-net" "mission-service" + + # Ads services + check_change "services/ads-manager-service-net" "ads-manager-service" + check_change "services/ads-serving-service-net" "ads-serving-service" + check_change "services/ads-billing-service-net" "ads-billing-service" + check_change "services/ads-tracking-service-net" "ads-tracking-service" + check_change "services/ads-analytics-service-net" "ads-analytics-service" + + # Marketing services + check_change "services/mkt-facebook-service-net" "mkt-facebook-service" + check_change "services/mkt-whatsapp-service-net" "mkt-whatsapp-service" + check_change "services/mkt-x-service-net" "mkt-x-service" + check_change "services/mkt-zalo-service-net" "mkt-zalo-service" + + # Frontend + check_change "apps/web-client-tpos-net" "pos-web" + + # If K8s configs changed, flag for config deploy + if echo "$CHANGED" | grep -q "^deployments/staging/"; then + SERVICES="${SERVICES} __k8s_config__" + fi + + echo "CHANGED_SERVICES=${SERVICES}" >> $GITHUB_ENV + echo "Changed services: ${SERVICES:-none}" + + - name: Build and push changed services + run: | + if [ -z "$CHANGED_SERVICES" ]; then + echo "No services changed, skipping build" + exit 0 + fi + + HARBOR="harbor.techbi.org" + PROJECT="goodgo" + + echo "${{ secrets.HARBOR_PASSWORD }}" | docker login "${HARBOR}" -u "${{ secrets.HARBOR_USERNAME }}" --password-stdin + + build_service() { + local name="$1" + local context="$2" + local image="${HARBOR}/${PROJECT}/${name}-net" + + echo "=== Building ${name} ===" + docker build -t "${image}:${IMAGE_TAG}" -t "${image}:latest" "${context}" + docker push "${image}:${IMAGE_TAG}" + docker push "${image}:latest" + echo "=== ${name} pushed ===" + } + + for svc in $CHANGED_SERVICES; do + case "$svc" in + iam-service) build_service "iam-service" "./services/iam-service-net" ;; + merchant-service) build_service "merchant-service" "./services/merchant-service-net" ;; + order-service) build_service "order-service" "./services/order-service-net" ;; + fnb-engine) build_service "fnb-engine" "./services/fnb-engine-net" ;; + catalog-service) build_service "catalog-service" "./services/catalog-service-net" ;; + inventory-service) build_service "inventory-service" "./services/inventory-service-net" ;; + wallet-service) build_service "wallet-service" "./services/wallet-service-net" ;; + storage-service) build_service "storage-service" "./services/storage-service-net" ;; + booking-service) build_service "booking-service" "./services/booking-service-net" ;; + chat-service) build_service "chat-service" "./services/chat-service-net" ;; + social-service) build_service "social-service" "./services/social-service-net" ;; + promotion-service) build_service "promotion-service" "./services/promotion-service-net" ;; + membership-service) build_service "membership-service" "./services/membership-service-net" ;; + mining-service) build_service "mining-service" "./services/mining-service-net" ;; + mission-service) build_service "mission-service" "./services/mission-service-net" ;; + ads-manager-service) build_service "ads-manager-service" "./services/ads-manager-service-net" ;; + ads-serving-service) build_service "ads-serving-service" "./services/ads-serving-service-net" ;; + ads-billing-service) build_service "ads-billing-service" "./services/ads-billing-service-net" ;; + ads-tracking-service) build_service "ads-tracking-service" "./services/ads-tracking-service-net" ;; + ads-analytics-service) build_service "ads-analytics-service" "./services/ads-analytics-service-net" ;; + mkt-facebook-service) build_service "mkt-facebook-service" "./services/mkt-facebook-service-net" ;; + mkt-whatsapp-service) build_service "mkt-whatsapp-service" "./services/mkt-whatsapp-service-net" ;; + mkt-x-service) build_service "mkt-x-service" "./services/mkt-x-service-net" ;; + mkt-zalo-service) build_service "mkt-zalo-service" "./services/mkt-zalo-service-net" ;; + pos-web) build_service "web-client-tpos" "./apps/web-client-tpos-net" ;; + __k8s_config__) echo "K8s config changed, will apply manifests" ;; + esac + done + + - name: Deploy to K8s + run: | + if [ -z "$CHANGED_SERVICES" ]; then + echo "No services changed, skipping deploy" + exit 0 + fi + + HARBOR="harbor.techbi.org" + PROJECT="goodgo" + NS="staging" + + # Apply K8s configs if changed + if echo "$CHANGED_SERVICES" | grep -q "__k8s_config__"; then + echo "=== Applying K8s configs ===" + kubectl apply -f deployments/staging/kubernetes/namespace.yaml + kubectl apply -f deployments/staging/kubernetes/configmap.yaml + kubectl apply -f deployments/staging/kubernetes/redis.yaml + kubectl apply -f deployments/staging/kubernetes/rabbitmq.yaml + kubectl apply -f deployments/staging/kubernetes/minio.yaml + kubectl apply -f deployments/staging/kubernetes/ingress.yaml + kubectl apply -f deployments/staging/kubernetes/network-policy.yaml + fi + + deploy_service() { + local name="$1" + local image_name="$2" + local manifest="$3" + local image="${HARBOR}/${PROJECT}/${image_name}:${IMAGE_TAG}" + + echo "=== Deploying ${name} ===" + kubectl apply -f "deployments/staging/kubernetes/${manifest}" + kubectl set image "deployment/${name}" "${name}=${image}" -n "${NS}" + kubectl patch deployment "${name}" -n "${NS}" \ + -p '{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"harbor-pull-secret"}],"containers":[{"name":"'"${name}"'","imagePullPolicy":"Always"}]}}}}' + fi + + for svc in $CHANGED_SERVICES; do + case "$svc" in + iam-service) deploy_service "iam-service" "iam-service-net" "iam-service.yaml" ;; + merchant-service) deploy_service "merchant-service" "merchant-service-net" "merchant-service.yaml" ;; + order-service) deploy_service "order-service" "order-service-net" "order-service.yaml" ;; + fnb-engine) deploy_service "fnb-engine" "fnb-engine-net" "fnb-engine.yaml" ;; + catalog-service) deploy_service "catalog-service" "catalog-service-net" "catalog-service.yaml" ;; + inventory-service) deploy_service "inventory-service" "inventory-service-net" "inventory-service.yaml" ;; + wallet-service) deploy_service "wallet-service" "wallet-service-net" "wallet-service.yaml" ;; + storage-service) deploy_service "storage-service" "storage-service-net" "storage-service.yaml" ;; + booking-service) deploy_service "booking-service" "booking-service-net" "booking-service.yaml" ;; + chat-service) deploy_service "chat-service" "chat-service-net" "chat-service.yaml" ;; + social-service) deploy_service "social-service" "social-service-net" "social-service.yaml" ;; + promotion-service) deploy_service "promotion-service" "promotion-service-net" "promotion-service.yaml" ;; + membership-service) deploy_service "membership-service" "membership-service-net" "membership-service.yaml" ;; + mining-service) deploy_service "mining-service" "mining-service-net" "mining-service.yaml" ;; + mission-service) deploy_service "mission-service" "mission-service-net" "mission-service.yaml" ;; + ads-manager-service) deploy_service "ads-manager-service" "ads-manager-service-net" "ads-manager-service.yaml" ;; + ads-serving-service) deploy_service "ads-serving-service" "ads-serving-service-net" "ads-serving-service.yaml" ;; + ads-billing-service) deploy_service "ads-billing-service" "ads-billing-service-net" "ads-billing-service.yaml" ;; + ads-tracking-service) deploy_service "ads-tracking-service" "ads-tracking-service-net" "ads-tracking-service.yaml" ;; + ads-analytics-service) deploy_service "ads-analytics-service" "ads-analytics-service-net" "ads-analytics-service.yaml" ;; + mkt-facebook-service) deploy_service "mkt-facebook-service" "mkt-facebook-service-net" "mkt-facebook-service.yaml" ;; + mkt-whatsapp-service) deploy_service "mkt-whatsapp-service" "mkt-whatsapp-service-net" "mkt-whatsapp-service.yaml" ;; + mkt-x-service) deploy_service "mkt-x-service" "mkt-x-service-net" "mkt-x-service.yaml" ;; + mkt-zalo-service) deploy_service "mkt-zalo-service" "mkt-zalo-service-net" "mkt-zalo-service.yaml" ;; + pos-web) deploy_service "pos-web" "web-client-tpos-net" "pos-web.yaml" ;; + esac + done + + # Wait for rollouts + FAILED=0 + for svc in $CHANGED_SERVICES; do + if [ "$svc" = "__k8s_config__" ]; then continue; fi + SVC_NAME="$svc" + if [ "$svc" = "pos-web" ]; then SVC_NAME="pos-web"; fi + echo "Waiting for ${SVC_NAME}..." + if ! kubectl rollout status "deployment/${SVC_NAME}" -n "${NS}" --timeout=180s 2>/dev/null; then + echo "WARNING: ${SVC_NAME} rollout did not complete" + FAILED=$((FAILED + 1)) + fi + done + + echo "" + echo "=== Deployment Summary ===" + kubectl get pods -n "${NS}" -o wide + echo "" + kubectl get svc -n "${NS}" + + if [ $FAILED -gt 0 ]; then + echo "WARNING: ${FAILED} service(s) did not complete rollout" + exit 1 + fi diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index a2026edc..f811a42d 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -1,11 +1,21 @@ -# EN: Deploy GoodGo Platform MVP services to Kubernetes staging -# VI: Trien khai cac service MVP cua GoodGo Platform len K8s staging -name: Deploy to Staging +# EN: DISABLED — CI/CD moved to Gitea Actions (gitea.techbi.org) +# VI: DA TAT — CI/CD chuyen sang Gitea Actions (gitea.techbi.org) +# See: .gitea/workflows/deploy.yaml +name: Deploy to Staging (DISABLED) on: - push: - branches: - - master + workflow_dispatch: + inputs: + confirm: + description: 'This workflow is disabled. CI/CD runs via Gitea Actions.' + required: true + default: 'I understand' + +# Original trigger (disabled): +# on: +# push: +# branches: +# - master paths: - 'services/iam-service-net/**' - 'services/merchant-service-net/**' diff --git a/deployments/staging/kubernetes/act-runner-rbac.yaml b/deployments/staging/kubernetes/act-runner-rbac.yaml new file mode 100644 index 00000000..5ea4ad56 --- /dev/null +++ b/deployments/staging/kubernetes/act-runner-rbac.yaml @@ -0,0 +1,46 @@ +# EN: RBAC for Gitea act_runner to deploy to staging namespace +# VI: RBAC cho Gitea act_runner deploy vao namespace staging +# +# The act_runner ServiceAccount (in gitea namespace) needs permissions to: +# - Apply manifests (deployments, services, configmaps, secrets, ingress, hpa, networkpolicies, pvc) +# - Patch deployments (set image, rollout restart) +# - Read pods/logs (rollout status) +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: act-runner-staging + namespace: staging +rules: + - apiGroups: ["apps"] + resources: ["deployments"] + verbs: ["get", "list", "watch", "create", "update", "patch"] + - apiGroups: [""] + resources: ["services", "configmaps", "secrets", "persistentvolumeclaims"] + verbs: ["get", "list", "watch", "create", "update", "patch"] + - apiGroups: [""] + resources: ["pods", "pods/log"] + verbs: ["get", "list", "watch"] + - apiGroups: ["networking.k8s.io"] + resources: ["ingresses", "networkpolicies"] + verbs: ["get", "list", "watch", "create", "update", "patch"] + - apiGroups: ["autoscaling"] + resources: ["horizontalpodautoscalers"] + verbs: ["get", "list", "watch", "create", "update", "patch"] + - apiGroups: [""] + resources: ["namespaces"] + verbs: ["get", "list", "create"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: act-runner-staging-binding + namespace: staging +subjects: + - kind: ServiceAccount + name: act-runner + namespace: gitea +roleRef: + kind: Role + name: act-runner-staging + apiGroup: rbac.authorization.k8s.io diff --git a/deployments/staging/kubernetes/gitea-sync-cronjob.yaml b/deployments/staging/kubernetes/gitea-sync-cronjob.yaml new file mode 100644 index 00000000..c0b4813e --- /dev/null +++ b/deployments/staging/kubernetes/gitea-sync-cronjob.yaml @@ -0,0 +1,69 @@ +# EN: CronJob to sync GitHub → Gitea every 5 minutes +# VI: CronJob dong bo GitHub → Gitea moi 5 phut +# +# Flow: GitHub (hongochai10/Microservices-Development) +# → git clone --bare via SSH +# → git push --mirror to Gitea (admin/pos-system) +# +# Prerequisites: +# 1. Create Gitea repo: admin/pos-system on gitea.techbi.org +# 2. Create secrets: +# kubectl create secret generic pos-gitea-push-creds -n gitea \ +# --from-literal=url=https://admin:Velik%402026@gitea.techbi.org/admin/pos-system.git +# (git-ssh-key secret already exists in gitea namespace from neon-ui setup) +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: github-gitea-sync-pos + namespace: gitea + labels: + app: pos-system + platform: goodgo +spec: + schedule: "*/5 * * * *" + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 2 + jobTemplate: + spec: + backoffLimit: 1 + ttlSecondsAfterFinished: 120 + template: + spec: + containers: + - name: sync + image: alpine/git:latest + command: ["sh", "-c"] + args: + - | + mkdir -p /root/.ssh + cp /ssh/ssh-privatekey /root/.ssh/id_ed25519 + cp /ssh/known_hosts /root/.ssh/known_hosts + chmod 600 /root/.ssh/id_ed25519 + git clone --bare git@github.com:hongochai10/Microservices-Development.git /tmp/repo + cd /tmp/repo + git push --mirror "${GITEA_PUSH_URL}" + env: + - name: GITEA_PUSH_URL + valueFrom: + secretKeyRef: + name: pos-gitea-push-creds + key: url + volumeMounts: + - name: git-ssh + mountPath: /ssh + readOnly: true + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 200m + memory: 256Mi + restartPolicy: Never + volumes: + - name: git-ssh + secret: + secretName: git-ssh-key + defaultMode: 0600 diff --git a/deployments/staging/kubernetes/setup-secrets.sh b/deployments/staging/kubernetes/setup-secrets.sh new file mode 100755 index 00000000..3a43544d --- /dev/null +++ b/deployments/staging/kubernetes/setup-secrets.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# EN: Setup K8s secrets for GoodGo staging deployment +# VI: Thiet lap K8s secrets cho GoodGo staging deployment +# +# Prerequisites: +# - kubectl configured with cluster access +# - Gitea repo admin/pos-system created on gitea.techbi.org +# - Harbor project "goodgo" created on harbor.techbi.org +# +# Usage: ./setup-secrets.sh + +set -e + +NS="staging" + +echo "=== Creating namespace ===" +kubectl create namespace "$NS" --dry-run=client -o yaml | kubectl apply -f - + +echo "=== Creating Gitea push credentials (for CronJob sync) ===" +kubectl create secret generic pos-gitea-push-creds -n gitea \ + --from-literal=url='https://admin:Velik%402026@gitea.techbi.org/admin/pos-system.git' \ + --dry-run=client -o yaml | kubectl apply -f - + +echo "=== Creating Harbor pull secret (for K8s to pull images) ===" +kubectl create secret docker-registry harbor-pull-secret -n "$NS" \ + --docker-server=harbor.techbi.org \ + --docker-username=admin \ + --docker-password='Velik@2026' \ + --docker-email=admin@techbi.org \ + --dry-run=client -o yaml | kubectl apply -f - + +echo "=== Applying RBAC for act_runner ===" +kubectl apply -f act-runner-rbac.yaml + +echo "=== Applying K8s secrets (DB, JWT, Redis, etc.) ===" +# Create goodgo-secrets with real values +kubectl create secret generic goodgo-secrets -n "$NS" \ + --from-literal=Jwt__Secret='GoodGo-Local-Dev-JWT-Secret-2024-Min32Chars!!' \ + --from-literal=Jwt__RefreshSecret='GoodGo-Local-Dev-Refresh-Secret-2024-32Ch!!' \ + --from-literal=IdentityServer__IssuerUri='https://api.staging.goodgo.vn' \ + --from-literal=Redis__Password='goodgo-redis-local' \ + --from-literal=ConnectionStrings__Redis='redis:6379,password=goodgo-redis-local' \ + --from-literal=Storage__MinIO__AccessKey='minioadmin' \ + --from-literal=Storage__MinIO__SecretKey='minioadmin123' \ + --from-literal=Storage__MinIO__Endpoint='minio.staging.svc.cluster.local:9000' \ + --from-literal=RabbitMQ__Host='rabbitmq' \ + --from-literal=RabbitMQ__Username='goodgo' \ + --from-literal=RabbitMQ__Password='goodgo-rabbitmq-local' \ + --from-literal=IAM_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=iam_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=MERCHANT_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=merchant_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=ORDER_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=order_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=FNB_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=fnb_engine;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=CATALOG_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=catalog_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=INVENTORY_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=inventory_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=WALLET_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=wallet_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=STORAGE_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=storage_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=BOOKING_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=booking_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=CHAT_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=chat_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=SOCIAL_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=social_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=PROMOTION_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=promotion_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=MEMBERSHIP_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=membership_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=MINING_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=mining_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=MISSION_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=mission_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=ADS_MANAGER_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=ads_manager_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=ADS_SERVING_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=ads_serving_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=ADS_BILLING_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=ads_billing_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=ADS_TRACKING_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=ads_tracking_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=ADS_ANALYTICS_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=ads_analytics_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=MKT_FACEBOOK_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=mkt_facebook_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=MKT_WHATSAPP_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=mkt_whatsapp_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=MKT_X_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=mkt_x_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --from-literal=MKT_ZALO_DATABASE_URL='Host=212.28.186.239;Port=30992;Database=mkt_zalo_service;Username=cloud_admin;Password=XbnKQ2ONe6pMxxCh;SSL Mode=Prefer' \ + --dry-run=client -o yaml | kubectl apply -f - + +echo "" +echo "=== Setup complete ===" +echo "Next steps:" +echo " 1. Create Harbor project 'goodgo' at https://harbor.techbi.org" +echo " 2. Create Gitea repo 'admin/pos-system' at https://gitea.techbi.org" +echo " 3. Set Gitea repo secrets: HARBOR_USERNAME=admin, HARBOR_PASSWORD=Velik@2026" +echo " 4. Apply CronJob: kubectl apply -f gitea-sync-cronjob.yaml" +echo " 5. Apply manifests: kubectl apply -f deployments/staging/kubernetes/"