diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a574926 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,24 @@ +.git +.cache +.claude +.superpowers +.playwright-cli +.playwright-mcp +node_modules +.pnpm-store +coverage +dist +*.log +*.tmp +*.tar.gz +.env +.env.* +apps/*/dist +apps/*/out +apps/*/release +apps/*/node_modules +apps/*/.output +apps/*/.wxt +apps/*/chrome-mv* +apps/*/firefox-mv* +ops-api diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..50c64fa --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,111 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +jobs: + backend: + name: Backend + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: server/go.mod + cache-dependency-path: server/go.sum + + - name: Add Go bin to PATH + run: echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" + + - name: Install sqlc + run: go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.30.0 + + - name: Generate sqlc code + working-directory: server + run: make sqlc-generate + + - name: Verify generated code is committed + run: git diff --exit-code -- server/internal/tenant/repository/generated + + - name: Check tenant SQL scope guard + working-directory: server + run: make tenant-scope-guard + + - name: Run tests + working-directory: server + run: make test + + - name: Build Go commands + working-directory: server + run: go build ./cmd/... + + frontend: + name: Frontend + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Setup pnpm + run: | + corepack enable + corepack prepare pnpm@10.33.0 --activate + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Typecheck admin web + run: pnpm typecheck:admin + + - name: Typecheck ops web + run: pnpm --filter ops-web typecheck + + - name: Typecheck browser extension + run: pnpm typecheck:extension + + - name: Build admin web + run: pnpm build:admin + + - name: Build ops web + run: pnpm --filter ops-web build + + deploy-config: + name: Deployment Config + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Docker CLI + run: | + if command -v docker >/dev/null 2>&1; then + docker --version + exit 0 + fi + apt-get update + apt-get install -y docker.io docker-compose-plugin + + - name: Validate compose files + run: | + docker compose -f deploy/docker-compose.yaml config >/tmp/compose.yaml + docker compose -f deploy/docker-compose.yaml -f deploy/docker-compose.offline.yaml config >/tmp/compose-offline.yaml + + - name: Validate shell scripts + run: bash -n deploy/scripts/package.sh deploy/scripts/load-and-start.sh diff --git a/.gitea/workflows/offline-package.yml b/.gitea/workflows/offline-package.yml new file mode 100644 index 0000000..9d7bb50 --- /dev/null +++ b/.gitea/workflows/offline-package.yml @@ -0,0 +1,93 @@ +name: Offline Package + +on: + workflow_dispatch: + inputs: + image_tag: + description: "Docker image tag. Defaults to the current tag name or short commit SHA." + required: false + default: "" + platform: + description: "Target platform for the offline package." + required: false + default: "linux/amd64" + push: + tags: + - "v*" + +permissions: + contents: read + +jobs: + package: + name: Build Offline Package + runs-on: ubuntu-latest + timeout-minutes: 180 + env: + DOCKER_BUILDKIT: "1" + REQUESTED_IMAGE_TAG: ${{ inputs.image_tag }} + REQUESTED_PLATFORM: ${{ inputs.platform }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Docker CLI and buildx + run: | + if command -v docker >/dev/null 2>&1 && docker buildx version >/dev/null 2>&1; then + docker version + docker buildx version + exit 0 + fi + + apt-get update + apt-get install -y ca-certificates curl gnupg + install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc + chmod a+r /etc/apt/keyrings/docker.asc + . /etc/os-release + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list + apt-get update + apt-get install -y docker-ce-cli docker-buildx-plugin docker-compose-plugin + docker version + docker buildx version + + - name: Check Docker daemon + run: docker info + + - name: Resolve package metadata + run: | + image_tag="${REQUESTED_IMAGE_TAG}" + if [ -z "${image_tag}" ]; then + case "${GITHUB_REF:-}" in + refs/tags/*) + image_tag="${GITHUB_REF#refs/tags/}" + ;; + *) + image_tag="$(git rev-parse --short=12 HEAD)" + ;; + esac + fi + + platform="${REQUESTED_PLATFORM:-linux/amd64}" + printf '%s\n' "${image_tag}" > .ci-image-tag + printf '%s\n' "${platform}" > .ci-platform + echo "Image tag: ${image_tag}" + echo "Platform: ${platform}" + + - name: Build offline deployment package + run: | + image_tag="$(cat .ci-image-tag)" + platform="$(cat .ci-platform)" + mkdir -p artifacts + PACKAGE_OUTPUT_DIR="${PWD}/artifacts" bash deploy/scripts/package.sh "${image_tag}" "${platform}" + ls -lh artifacts + + - name: Upload offline package + uses: actions/upload-artifact@v3 + with: + name: geo-rankly-offline-package + path: | + artifacts/*.tar.gz + artifacts/*.sha256 + if-no-files-found: error + retention-days: 14 diff --git a/Dockerfile.frontend b/Dockerfile.frontend index 0de1292..44446f9 100644 --- a/Dockerfile.frontend +++ b/Dockerfile.frontend @@ -4,7 +4,7 @@ # ─── Stage 1: Build Vue app ──────────────────────────────────────────────────── FROM node:20-alpine AS builder -RUN npm install -g pnpm@9 +RUN npm install -g pnpm@10.33.0 WORKDIR /app diff --git a/Dockerfile.ops-web b/Dockerfile.ops-web index 6dd64ef..61ef5bd 100644 --- a/Dockerfile.ops-web +++ b/Dockerfile.ops-web @@ -3,7 +3,7 @@ FROM node:20-alpine AS builder -RUN npm install -g pnpm@9 +RUN npm install -g pnpm@10.33.0 WORKDIR /app diff --git a/deploy/.env.example b/deploy/.env.example index 5cd6c76..5250363 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -27,8 +27,10 @@ MINIO_ROOT_USER=minioadmin MINIO_ROOT_PASSWORD=minioadmin # ─── Optional: Exposed ports ───────────────────────────────────────────────── -FRONTEND_PORT=80 -API_PORT=8080 -OPS_API_PORT=8090 -RABBITMQ_MGMT_PORT=15672 -MINIO_CONSOLE_PORT=9001 +# Defaults avoid common NAS ports such as 80, 8080, and 8090. +FRONTEND_PORT=18080 +API_PORT=18081 +OPS_WEB_PORT=18082 +OPS_API_PORT=18090 +RABBITMQ_MGMT_PORT=15673 +MINIO_CONSOLE_PORT=19001 diff --git a/deploy/OFFLINE_PACKAGE.md b/deploy/OFFLINE_PACKAGE.md new file mode 100644 index 0000000..b7779c3 --- /dev/null +++ b/deploy/OFFLINE_PACKAGE.md @@ -0,0 +1,40 @@ +# geo-rankly offline package + +This directory is self-contained. It deploys containers, not host binaries. + +On a Docker host with Docker Compose v2: + +```bash +bash deploy.sh compose +``` + +`compose` is the default, so this is equivalent: + +```bash +bash deploy.sh +``` + +`deploy.sh compose` loads `images.tar`, creates `.env` from `.env.example` when missing, and starts the stack with: + +```bash +docker compose -f docker-compose.yaml -f docker-compose.offline.yaml --env-file .env up -d +``` + +On a k3s host: + +```bash +bash deploy.sh k3s +``` + +This imports `images.tar` into k3s containerd and applies `k3s/` with Kustomize. + +Default ports are NAS-friendly: + +- tenant web: `18080` +- tenant API: `18081` +- ops web: `18082` +- ops API: `18090` +- RabbitMQ management: `15673` +- MinIO console: `19001` + +Edit `.env` before rerunning `bash deploy.sh` if you need real API keys, stronger secrets, or different ports. diff --git a/deploy/docker-compose.offline.yaml b/deploy/docker-compose.offline.yaml index 6f9c0f0..ff771de 100644 --- a/deploy/docker-compose.offline.yaml +++ b/deploy/docker-compose.offline.yaml @@ -23,6 +23,11 @@ services: pull_policy: never build: !reset null + kol-assist-worker: + image: geo-rankly/kol-assist-worker:${IMAGE_TAG:-latest} + pull_policy: never + build: !reset null + scheduler: image: geo-rankly/scheduler:${IMAGE_TAG:-latest} pull_policy: never @@ -38,6 +43,11 @@ services: pull_policy: never build: !reset null + ops-web: + image: geo-rankly/ops-web:${IMAGE_TAG:-latest} + pull_policy: never + build: !reset null + postgres: pull_policy: never diff --git a/deploy/docker-compose.yaml b/deploy/docker-compose.yaml index 5d95232..fb8eb89 100644 --- a/deploy/docker-compose.yaml +++ b/deploy/docker-compose.yaml @@ -11,8 +11,8 @@ name: geo-rankly x-app-env: &app-env CONFIG_PATH: /app/configs/config.yaml - LLM_API_KEY: ${LLM_API_KEY} - SILICONFLOW_API_KEY: ${SILICONFLOW_API_KEY} + LLM_API_KEY: ${LLM_API_KEY:-} + SILICONFLOW_API_KEY: ${SILICONFLOW_API_KEY:-} JWT_SECRET: ${JWT_SECRET:-change-me-in-production} JWT_ACCESS_TTL: ${JWT_ACCESS_TTL:-15m} JWT_REFRESH_TTL: ${JWT_REFRESH_TTL:-720h} @@ -87,7 +87,7 @@ services: volumes: - rabbitmq_data:/var/lib/rabbitmq ports: - - "${RABBITMQ_MGMT_PORT:-15672}:15672" + - "${RABBITMQ_MGMT_PORT:-15673}:15672" healthcheck: test: ["CMD", "rabbitmq-diagnostics", "ping"] interval: 10s @@ -120,7 +120,7 @@ services: volumes: - minio_data:/data ports: - - "${MINIO_CONSOLE_PORT:-9001}:9001" + - "${MINIO_CONSOLE_PORT:-19001}:9001" # ─── MinIO bucket initializer (one-shot) ──────────────────────────────────── minio-init: @@ -181,9 +181,9 @@ services: volumes: *app-volumes depends_on: *app-depends ports: - - "${API_PORT:-8080}:8080" + - "${API_PORT:-18081}:8080" healthcheck: - test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"] + test: ["CMD-SHELL", "wget -qO- http://localhost:8080/api/health/live || exit 1"] interval: 10s timeout: 5s retries: 5 @@ -204,6 +204,21 @@ services: volumes: *app-volumes depends_on: *app-depends + kol-assist-worker: + image: ${IMAGE_REGISTRY:-ghcr.io/your-org/geo-rankly}/kol-assist-worker:${IMAGE_TAG:-latest} + container_name: geo-kol-assist-worker + build: + context: ../server + dockerfile: Dockerfile + target: runtime + args: + SERVICE: kol-assist-worker + restart: unless-stopped + environment: + <<: *app-env + volumes: *app-volumes + depends_on: *app-depends + scheduler: image: ${IMAGE_REGISTRY:-ghcr.io/your-org/geo-rankly}/scheduler:${IMAGE_TAG:-latest} container_name: geo-scheduler @@ -246,7 +261,7 @@ services: migrate: condition: service_completed_successfully ports: - - "${OPS_API_PORT:-8090}:8090" + - "${OPS_API_PORT:-18090}:8090" healthcheck: test: ["CMD-SHELL", "wget -qO- http://localhost:8090/api/health/live || exit 1"] interval: 10s @@ -267,7 +282,20 @@ services: tenant-api: condition: service_healthy ports: - - "${FRONTEND_PORT:-80}:80" + - "${FRONTEND_PORT:-18080}:80" + + ops-web: + image: ${IMAGE_REGISTRY:-ghcr.io/your-org/geo-rankly}/ops-web:${IMAGE_TAG:-latest} + container_name: geo-ops-web + build: + context: .. + dockerfile: Dockerfile.ops-web + restart: unless-stopped + depends_on: + ops-api: + condition: service_healthy + ports: + - "${OPS_WEB_PORT:-18082}:80" volumes: pgdata: diff --git a/deploy/scripts/load-and-start.sh b/deploy/scripts/load-and-start.sh index e0bf03a..c021848 100755 --- a/deploy/scripts/load-and-start.sh +++ b/deploy/scripts/load-and-start.sh @@ -1,13 +1,15 @@ #!/usr/bin/env bash # load-and-start.sh — 在【离线目标服务器】上运行 -# 加载镜像并启动所有服务 +# 加载镜像并使用 Docker Compose 或 k3s 启动服务 # # 用法(解压部署包后): -# bash deploy.sh +# bash deploy.sh compose # 默认,Docker Compose 部署 +# bash deploy.sh k3s # k3s / Kubernetes 部署 set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +MODE="${1:-compose}" # 读取打包时记录的元数据 if [[ -f "${SCRIPT_DIR}/.package-meta" ]]; then @@ -18,39 +20,93 @@ LOCAL_PREFIX="${LOCAL_PREFIX:-geo-rankly}" # ─── 检查 .env ──────────────────────────────────────────────────────────────── if [[ ! -f "${SCRIPT_DIR}/.env" ]]; then - echo "❌ 未找到 .env 文件,请先执行:" - echo " cp .env.example .env && vi .env" - exit 1 + echo "==> 未找到 .env,已从 .env.example 自动创建。" + cp "${SCRIPT_DIR}/.env.example" "${SCRIPT_DIR}/.env" + echo " 如需填写真实 API Key 或修改端口,可编辑 ${SCRIPT_DIR}/.env 后再次执行 bash deploy.sh" fi -# ─── Step 1: 加载镜像 ───────────────────────────────────────────────────────── -echo "==> [1/3] 加载 Docker 镜像(可能需要几分钟)..." -docker load -i "${SCRIPT_DIR}/images.tar" -echo " 镜像加载完成" -docker images | grep -E "geo-rankly|postgres|rabbitmq|redis|qdrant|minio" | head -20 +env_value() { + local key="$1" default_value="$2" value + value="$(grep -E "^${key}=" "${SCRIPT_DIR}/.env" | tail -1 | cut -d= -f2- || true)" + value="${value%\"}" + value="${value#\"}" + printf '%s' "${value:-${default_value}}" +} -# ─── Step 2: 设置 IMAGE_REGISTRY 为本地前缀 ────────────────────────────────── -echo "==> [2/3] 配置离线镜像引用..." -export IMAGE_REGISTRY="${LOCAL_PREFIX}" -export IMAGE_TAG="${IMAGE_TAG}" +deploy_compose() { + # ─── Step 1: 加载镜像 ─────────────────────────────────────────────────────── + echo "==> [1/3] 加载 Docker 镜像(可能需要几分钟)..." + docker load -i "${SCRIPT_DIR}/images.tar" + echo " 镜像加载完成" + docker images | grep -E "geo-rankly|postgres|rabbitmq|redis|qdrant|minio" | head -20 -# ─── Step 3: 启动服务 ───────────────────────────────────────────────────────── -echo "==> [3/3] 启动所有服务..." -cd "${SCRIPT_DIR}" -docker compose \ - -f docker-compose.yaml \ - -f docker-compose.offline.yaml \ - --env-file .env \ - up -d + # ─── Step 2: 设置 IMAGE_REGISTRY 为本地前缀 ──────────────────────────────── + echo "==> [2/3] 配置离线镜像引用..." + export IMAGE_REGISTRY="${LOCAL_PREFIX}" + export IMAGE_TAG="${IMAGE_TAG}" -echo "" -echo "✅ 部署完成!" -echo "" -echo "服务状态:" -docker compose -f docker-compose.yaml -f docker-compose.offline.yaml ps -echo "" -echo "查看日志: docker compose -f docker-compose.yaml logs -f tenant-api" -echo "停止服务: docker compose -f docker-compose.yaml down" -echo "管理界面:" -echo " RabbitMQ: http://:15672 (geo / geo_dev)" -echo " MinIO: http://:9001 (minioadmin / minioadmin)" + # ─── Step 3: 启动服务 ─────────────────────────────────────────────────────── + echo "==> [3/3] 启动所有服务..." + cd "${SCRIPT_DIR}" + docker compose \ + -f docker-compose.yaml \ + -f docker-compose.offline.yaml \ + --env-file .env \ + up -d + + echo "" + echo "✅ Docker Compose 部署完成!" + echo "" + echo "服务状态:" + docker compose -f docker-compose.yaml -f docker-compose.offline.yaml ps + echo "" + echo "查看日志: docker compose -f docker-compose.yaml logs -f tenant-api" + echo "停止服务: docker compose -f docker-compose.yaml down" + echo "管理界面:" + echo " Tenant Web: http://:$(env_value FRONTEND_PORT 18080)" + echo " Ops Web: http://:$(env_value OPS_WEB_PORT 18082)" + echo " RabbitMQ: http://:$(env_value RABBITMQ_MGMT_PORT 15673) (geo / geo_dev)" + echo " MinIO: http://:$(env_value MINIO_CONSOLE_PORT 19001) ($(env_value MINIO_ROOT_USER minioadmin) / $(env_value MINIO_ROOT_PASSWORD minioadmin))" +} + +deploy_k3s() { + local kubectl_cmd="kubectl" + + if command -v k3s >/dev/null 2>&1; then + echo "==> [1/3] 导入镜像到 k3s containerd..." + sudo k3s ctr images import "${SCRIPT_DIR}/images.tar" + kubectl_cmd="sudo k3s kubectl" + elif command -v kubectl >/dev/null 2>&1 && command -v ctr >/dev/null 2>&1; then + echo "==> [1/3] 导入镜像到 containerd..." + sudo ctr -n k8s.io images import "${SCRIPT_DIR}/images.tar" + else + echo "❌ 未找到 k3s/kubectl/ctr。请先安装 k3s,或改用:bash deploy.sh compose" + exit 1 + fi + + echo "==> [2/3] 应用 k3s 清单..." + ${kubectl_cmd} apply -k "${SCRIPT_DIR}/k3s" + + echo "==> [3/3] 查看服务状态..." + ${kubectl_cmd} -n geo-rankly get pods,svc + + echo "" + echo "✅ k3s 部署命令已执行。等待 Pod 全部 Running:" + echo " ${kubectl_cmd} -n geo-rankly get pods -w" + echo "" + echo "查看日志:" + echo " ${kubectl_cmd} -n geo-rankly logs deploy/tenant-api -f" +} + +case "${MODE}" in + compose|docker-compose|docker) + deploy_compose + ;; + k3s|kubernetes) + deploy_k3s + ;; + *) + echo "用法:bash deploy.sh [compose|k3s]" + exit 1 + ;; +esac diff --git a/deploy/scripts/package.sh b/deploy/scripts/package.sh index 2c59cab..99cc116 100755 --- a/deploy/scripts/package.sh +++ b/deploy/scripts/package.sh @@ -11,7 +11,7 @@ # bash package.sh latest linux/amd64,linux/arm64 # 暂不支持(save 不支持 multi-arch) # # 产物: -# geo-rankly-deploy--.tar.gz +# geo-rankly-deploy---.tar.gz set -euo pipefail @@ -19,14 +19,18 @@ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" DEPLOY_DIR="${REPO_ROOT}/deploy" IMAGE_TAG="${1:-latest}" PLATFORM="${2:-linux/amd64}" -LOCAL_PREFIX="geo-rankly" +LOCAL_PREFIX="${LOCAL_PREFIX:-geo-rankly}" +OUTPUT_DIR="${PACKAGE_OUTPUT_DIR:-${REPO_ROOT}}" DATE_TAG="$(date +%Y%m%d)" # 将 linux/amd64 → amd64,用于包名 ARCH_LABEL="${PLATFORM##*/}" -PACKAGE_NAME="geo-rankly-deploy-${DATE_TAG}-${ARCH_LABEL}" +SAFE_IMAGE_TAG="$(printf '%s' "${IMAGE_TAG}" | tr '/:@' '---' | tr -c 'A-Za-z0-9_.-' '-')" +PACKAGE_NAME="geo-rankly-deploy-${SAFE_IMAGE_TAG}-${DATE_TAG}-${ARCH_LABEL}" WORK_DIR="/tmp/${PACKAGE_NAME}" +PACKAGE_PATH="${OUTPUT_DIR}/${PACKAGE_NAME}.tar.gz" echo "==> 目标平台: ${PLATFORM} | 镜像 tag: ${IMAGE_TAG}" +echo "==> 输出目录: ${OUTPUT_DIR}" echo "" # ─── 确保 buildx builder 支持目标平台 ──────────────────────────────────────── @@ -147,6 +151,16 @@ cp "${DEPLOY_DIR}/prompts.yml" "${WORK_DIR}/" cp "${DEPLOY_DIR}/.env.example" "${WORK_DIR}/" cp "${DEPLOY_DIR}/scripts/load-and-start.sh" "${WORK_DIR}/deploy.sh" cp -R "${DEPLOY_DIR}/k3s" "${WORK_DIR}/k3s" +if [[ -f "${DEPLOY_DIR}/OFFLINE_PACKAGE.md" ]]; then + cp "${DEPLOY_DIR}/OFFLINE_PACKAGE.md" "${WORK_DIR}/README.md" +fi +if [[ -f "${WORK_DIR}/k3s/kustomization.yaml" ]]; then + awk -v tag="${IMAGE_TAG}" ' + $1 == "newTag:" && $2 == "latest" { print " newTag: " tag; next } + { print } + ' "${WORK_DIR}/k3s/kustomization.yaml" > "${WORK_DIR}/k3s/kustomization.yaml.tmp" + mv "${WORK_DIR}/k3s/kustomization.yaml.tmp" "${WORK_DIR}/k3s/kustomization.yaml" +fi chmod +x "${WORK_DIR}/deploy.sh" # 写入元数据,供 load 脚本读取 @@ -158,18 +172,43 @@ BUILT_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ") BUILT_ON=$(uname -m) EOF +cat > "${WORK_DIR}/MANIFEST.txt" < [5/5] 压缩打包" +mkdir -p "${OUTPUT_DIR}" cd /tmp -tar -czf "${REPO_ROOT}/${PACKAGE_NAME}.tar.gz" "${PACKAGE_NAME}/" +tar -czf "${PACKAGE_PATH}" "${PACKAGE_NAME}/" +if command -v sha256sum >/dev/null 2>&1; then + sha256sum "${PACKAGE_PATH}" > "${PACKAGE_PATH}.sha256" +else + shasum -a 256 "${PACKAGE_PATH}" > "${PACKAGE_PATH}.sha256" +fi rm -rf "${WORK_DIR}" echo "" -echo "✅ 打包完成: ${REPO_ROOT}/${PACKAGE_NAME}.tar.gz" -echo " 平台: ${PLATFORM} | 大小: $(du -sh "${REPO_ROOT}/${PACKAGE_NAME}.tar.gz" | cut -f1)" +echo "✅ 打包完成: ${PACKAGE_PATH}" +echo " 校验文件: ${PACKAGE_PATH}.sha256" +echo " 平台: ${PLATFORM} | 大小: $(du -sh "${PACKAGE_PATH}" | cut -f1)" echo "" echo "传输到目标服务器后执行:" echo " tar xzf ${PACKAGE_NAME}.tar.gz" echo " cd ${PACKAGE_NAME}" -echo " cp .env.example .env && vi .env" echo " bash deploy.sh" diff --git a/server/.dockerignore b/server/.dockerignore new file mode 100644 index 0000000..4fae13f --- /dev/null +++ b/server/.dockerignore @@ -0,0 +1,9 @@ +.git +vendor +*.exe +*.test +*.out +coverage +.env +.env.* +.superpowers diff --git a/server/Dockerfile b/server/Dockerfile index 13db063..8a223d4 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,9 +1,11 @@ # syntax=docker/dockerfile:1 # ─── Stage 1: Build Go binaries ─────────────────────────────────────────────── -FROM golang:1.22-alpine AS builder +FROM --platform=$BUILDPLATFORM golang:1.23-alpine AS builder ARG SERVICE=tenant-api +ARG TARGETOS=linux +ARG TARGETARCH=amd64 RUN apk --no-cache add ca-certificates git @@ -14,12 +16,12 @@ RUN go mod download COPY . . -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ go build -trimpath -ldflags="-s -w" \ -o /bin/service ./cmd/${SERVICE} # ─── Stage 2: Migration image (bundles SQL files + migrate binary) ───────────── -FROM migrate/migrate:v4.18.1 AS migrate-tool +FROM migrate/migrate:v4.19.1 AS migrate-tool FROM alpine:3.19 AS migrate RUN apk --no-cache add ca-certificates