From 337be0f332fefaf858c2092b69947889ada21445 Mon Sep 17 00:00:00 2001 From: ISMAIL MASSERAN Date: Sun, 25 Oct 2026 11:59:58 +0800 Subject: [PATCH] DONE: separate repo for be and fe, update cicd --- .dockerignore | 2 + .gitea/workflows/build.yml | 42 ++++++ .gitea/workflows/deploy-prod.yml | 130 ++++++++++++++++++ docker/common/unified/Dockerfile | 16 +-- .../production/docker-compose.production.yml | 25 +--- docker/staging/docker-compose.staging.yml | 4 +- 6 files changed, 187 insertions(+), 32 deletions(-) create mode 100644 .gitea/workflows/build.yml create mode 100644 .gitea/workflows/deploy-prod.yml diff --git a/.dockerignore b/.dockerignore index bd7e2f9..9ed5b5e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,6 +5,8 @@ /docker-images /.vscode .env +.env.* +.gitea NOTE.md /node_modules /vendor diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..66abbe5 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,42 @@ +name: Build Docker Image + +on: + push: + tags: + - "v*" + +jobs: + build-backend: + runs-on: docker + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Login to Docker registry + run: | + echo "${{ secrets.REGISTRY_PASSWORD }}" | \ + docker login git.koppkb.com \ + -u "${{ secrets.REGISTRY_USERNAME }}" \ + --password-stdin + + - name: Build & push backend image + run: | + set -e + + IMAGE="git.koppkb.com/kopkb/my-kopkb-be" + TAGS="-t ${IMAGE}:${{ gitea.sha }}" + + if [ "${{ gitea.ref_type }}" = "tag" ] && echo "${{ gitea.ref_name }}" | grep -q '^v'; then + TAGS="${TAGS} -t ${IMAGE}:${{ gitea.ref_name }}" + fi + + echo "Building and pushing backend: ${TAGS}" + docker buildx build \ + --target production \ + --platform linux/amd64 \ + --cache-from type=registry,ref=${IMAGE}:buildcache,ignore-error=true \ + --cache-to type=registry,ref=${IMAGE}:buildcache,mode=max \ + -f docker/common/unified/Dockerfile \ + ${TAGS} \ + --push . diff --git a/.gitea/workflows/deploy-prod.yml b/.gitea/workflows/deploy-prod.yml new file mode 100644 index 0000000..0e21d54 --- /dev/null +++ b/.gitea/workflows/deploy-prod.yml @@ -0,0 +1,130 @@ +name: Deploy to Production + +# Manual production deploy — pulls pre-built images from the registry +on: + workflow_dispatch: + inputs: + image_tag: + description: "Docker image tag to deploy (commit SHA or version tag, e.g. v1.0.0)" + required: true + default: "latest" + type: string + +env: + BACKEND_IMAGE: git.koppkb.com/kopkb/my-kopkb-be + DEPLOY_DIR: /home/arrahn/Project + +jobs: + deploy: + runs-on: host + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ inputs.image_tag }} + + - name: Login to Docker registry + run: | + echo "${{ secrets.REGISTRY_PASSWORD }}" | \ + docker login git.koppkb.com \ + -u "${{ secrets.REGISTRY_USERNAME }}" \ + --password-stdin + + - name: Verify images exist in registry + run: | + set -e + IMAGE_TAG="${{ inputs.image_tag }}" + + IMAGE="${BACKEND_IMAGE}:${IMAGE_TAG}" + echo "Checking if image exists: ${IMAGE}" + if ! docker manifest inspect "${IMAGE}" > /dev/null 2>&1; then + echo "ERROR: Image ${IMAGE} does not exist in registry!" + exit 1 + fi + echo "✓ ${IMAGE} found" + + - name: Pull Docker images + run: | + set -e + IMAGE_TAG="${{ inputs.image_tag }}" + docker pull "${BACKEND_IMAGE}:${IMAGE_TAG}" + echo "✓ Image pulled successfully" + + - name: Sync compose file and deploy + run: | + set -e + IMAGE_TAG="${{ inputs.image_tag }}" + + mkdir -p "${DEPLOY_DIR}" + cp docker/production/docker-compose.production.yml "${DEPLOY_DIR}/docker-compose.yml" + + ENV_FILE=".env.production" + if [ ! -f "${ENV_FILE}" ]; then + echo "ERROR: ${ENV_FILE} is missing from the repository checkout" + exit 1 + fi + cp "${ENV_FILE}" "${DEPLOY_DIR}/.env" + chmod 600 "${DEPLOY_DIR}/.env" + + NGINX_CONF="$(pwd)/docker/production/nginx/nginx.conf" + if [ ! -f "${NGINX_CONF}" ]; then + echo "ERROR: nginx config not found at ${NGINX_CONF}" + exit 1 + fi + + cd "${DEPLOY_DIR}" + + export IMAGE_TAG + export NGINX_CONF + + docker rm -f mykopkb-be-production || true + docker compose -f docker-compose.yml pull backend + docker compose -f docker-compose.yml up -d backend + + echo "✓ Deployed backend IMAGE_TAG=${IMAGE_TAG}" + + - name: Verify deployment + run: | + set -e + cd "${DEPLOY_DIR}" + + echo "Waiting for services to start..." + sleep 10 + + STATUS=$(docker compose -f docker-compose.yml ps --status running --format '{{.Name}}' backend 2>/dev/null || true) + if [ -z "$STATUS" ]; then + echo "ERROR: backend is not running" + docker compose -f docker-compose.yml ps + docker compose -f docker-compose.yml logs --tail=50 backend || true + exit 1 + fi + echo "✓ backend is running (${STATUS})" + + BE_CONTAINER=$(docker compose -f docker-compose.yml ps -q backend) + if docker exec "$BE_CONTAINER" test -f /var/www/.env; then + echo "✓ Backend .env file exists" + else + echo "WARNING: Backend .env file not found" + fi + + API_PORT=$(grep -E '^API_PORT=' .env 2>/dev/null | cut -d= -f2- | tr -d '"' || true) + API_PORT=${API_PORT:-8080} + + check_health() { + NAME=$1 + URL=$2 + for i in 1 2 3 4 5 6; do + if curl -sf "$URL" > /dev/null; then + echo "✓ ${NAME} health check passed (${URL})" + return 0 + fi + echo "Waiting for ${NAME}... attempt ${i}/6" + sleep 5 + done + echo "ERROR: ${NAME} health check failed (${URL})" + docker compose -f docker-compose.yml logs --tail=50 "${NAME}" || true + return 1 + } + + check_health "backend" "http://127.0.0.1:${API_PORT}/health" diff --git a/docker/common/unified/Dockerfile b/docker/common/unified/Dockerfile index e8baac1..76b1255 100644 --- a/docker/common/unified/Dockerfile +++ b/docker/common/unified/Dockerfile @@ -37,7 +37,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ WORKDIR /var/www # Copy the entire Laravel application code into the container -COPY be/ /var/www +COPY . /var/www # Copy Composer from official image (avoids flaky getcomposer.org in CI) COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer @@ -56,7 +56,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ unzip \ && apt-get clean && rm -rf /var/lib/apt/lists/* -COPY be/package.json be/package-lock.json ./ +COPY package.json package-lock.json ./ ENV PUPPETEER_SKIP_DOWNLOAD=true \ PUPPETEER_CACHE_DIR=/var/www/.cache/puppeteer @@ -135,11 +135,11 @@ RUN curl -o /usr/local/bin/php-fpm-healthcheck \ && chmod +x /usr/local/bin/php-fpm-healthcheck # Copy the initialization script -COPY be/docker/common/unified/entrypoint.sh /usr/local/bin/entrypoint.sh +COPY docker/common/unified/entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh # Copy supervisor configuration files -COPY be/docker/common/unified/supervisor/supervisord.conf /etc/supervisor/supervisord.conf +COPY docker/common/unified/supervisor/supervisord.conf /etc/supervisor/supervisord.conf # Create supervisor conf.d directory RUN mkdir -p /etc/supervisor/conf.d @@ -149,11 +149,11 @@ RUN mkdir -p /etc/supervisor/conf.d # by mounting files into: # - /etc/nginx/nginx.conf # - /etc/supervisor/conf.d/ -COPY be/docker/common/unified/supervisor/unified.conf /etc/supervisor/conf.d/unified.conf -COPY be/docker/common/unified/nginx/nginx.conf /etc/nginx/nginx.conf +COPY docker/common/unified/supervisor/unified.conf /etc/supervisor/conf.d/unified.conf +COPY docker/common/unified/nginx/nginx.conf /etc/nginx/nginx.conf # Copy the initial storage structure -COPY be/storage /var/www/storage-init +COPY storage /var/www/storage-init # Copy PHP extensions and libraries from the builder stage COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/ @@ -164,7 +164,7 @@ COPY --from=builder /usr/local/bin/docker-php-ext-* /usr/local/bin/ RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" # Custom PHP overrides (upload limits, etc.) — official image loads conf.d/*.ini -COPY be/docker/common/unified/php/uploads.ini /usr/local/etc/php/conf.d/uploads.ini +COPY docker/common/unified/php/uploads.ini /usr/local/etc/php/conf.d/uploads.ini # Enable PHP-FPM status page via separate config file (avoid modifying zz-docker.conf) RUN echo '[www]' > /usr/local/etc/php-fpm.d/zzz-status.conf && \ diff --git a/docker/production/docker-compose.production.yml b/docker/production/docker-compose.production.yml index eff6107..9c729b1 100644 --- a/docker/production/docker-compose.production.yml +++ b/docker/production/docker-compose.production.yml @@ -17,7 +17,7 @@ services: - app-storage-production:/var/www/storage - app-logs-production:/var/www/storage/logs - ./.env:/var/www/.env:ro - - ${NGINX_CONF:-./be/docker/production/nginx/nginx.conf}:/etc/nginx/nginx.conf:ro + - ${NGINX_CONF:-./docker/production/nginx/nginx.conf}:/etc/nginx/nginx.conf:ro networks: - mykopkb-production-network depends_on: @@ -35,26 +35,6 @@ services: max-size: "10m" max-file: "3" - frontend: - image: git.koppkb.com/kopkb/my-kopkb-fe:${IMAGE_TAG:-latest} - container_name: mykopkb-fe-production - restart: unless-stopped - ports: - - "${FE_PORT:-8081}:80" - networks: - - mykopkb-production-network - healthcheck: - test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 10s - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" - db: image: postgres:17 container_name: mykopkb-postgres-production @@ -67,7 +47,7 @@ services: - POSTGRES_DB=${DB_DATABASE} volumes: - db-data-production:/var/lib/postgresql/data - - ./be/docker/postgres/conf.d:/etc/postgres/conf.d:ro + - ./docker/postgres/conf.d:/etc/postgres/conf.d:ro networks: - mykopkb-production-network healthcheck: @@ -110,6 +90,7 @@ services: networks: mykopkb-production-network: + name: mykopkb-production-network driver: bridge volumes: diff --git a/docker/staging/docker-compose.staging.yml b/docker/staging/docker-compose.staging.yml index 4ae6487..0e7126b 100644 --- a/docker/staging/docker-compose.staging.yml +++ b/docker/staging/docker-compose.staging.yml @@ -2,7 +2,7 @@ services: app: build: context: . - dockerfile: ./be/docker/common/unified/Dockerfile + dockerfile: ./docker/common/unified/Dockerfile args: ENV: staging platforms: @@ -87,7 +87,7 @@ services: - POSTGRES_PASSWORD=${DB_PASSWORD} volumes: - postgres-data-staging:/var/lib/postgres - - ./be/docker/postgres/conf.d:/etc/postgres/conf.d:ro + - ./docker/postgres/conf.d:/etc/postgres/conf.d:ro networks: - sutera-staging-network healthcheck: