From 99c55f1145efae2d7f480a2e24f342fc4d5bf7ac Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 13 Oct 2026 00:53:31 +0800 Subject: [PATCH] deploy: prepare olons server test rollout --- .dockerignore | 11 ++ .gitea/workflows/deploy.yml | 10 +- ...2026-10-13-olons-server-test-deployment.md | 130 ++++++++++++++++++ tests/test_deployment_contract.py | 38 +++++ 4 files changed, 184 insertions(+), 5 deletions(-) create mode 100644 .dockerignore create mode 100644 docs/superpowers/plans/2026-10-13-olons-server-test-deployment.md create mode 100644 tests/test_deployment_contract.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..019ebcb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.git +.gitea +.idea +.vscode +.DS_Store +*.log +tests +docs +web/node_modules +web/dist +coverage diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 5a85fe2..216d8b5 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -1,4 +1,4 @@ -name: Deploy im-workbench (k3s test cluster) +name: Deploy olons-server (k3s test cluster) on: workflow_dispatch: @@ -25,13 +25,13 @@ jobs: - name: 构建并推送镜像 run: | - docker build -t git.118im.com/im_team/im-workbench:${{ steps.vars.outputs.tag }} . - docker push git.118im.com/im_team/im-workbench:${{ steps.vars.outputs.tag }} + docker build -t git.118im.com/im_team/olons-server:${{ steps.vars.outputs.tag }} . + docker push git.118im.com/im_team/olons-server:${{ steps.vars.outputs.tag }} - name: 部署到k3s测试集群 run: | mkdir -p ~/.kube echo "${{ secrets.KUBECONFIG }}" > ~/.kube/config chmod 600 ~/.kube/config - kubectl rollout restart deployment/im-workbench -n workbench-system - kubectl rollout status deployment/im-workbench -n workbench-system --timeout=120s + kubectl rollout restart deployment/olons-server -n olons-server-system + kubectl rollout status deployment/olons-server -n olons-server-system --timeout=120s diff --git a/docs/superpowers/plans/2026-10-13-olons-server-test-deployment.md b/docs/superpowers/plans/2026-10-13-olons-server-test-deployment.md new file mode 100644 index 0000000..7e1e19d --- /dev/null +++ b/docs/superpowers/plans/2026-10-13-olons-server-test-deployment.md @@ -0,0 +1,130 @@ +# Olons Server 测试集群部署 Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 将 `olons_server` 的 `dev` 分支以独立前后端一体服务部署到现有 k3s 测试集群,并通过 `https://olns-web.118im.com` 验收。 + +**Architecture:** 保留仓库现有 Vue 构建产物嵌入 Go 服务的单镜像结构。新增独立 GitOps 应用 `olons-server-system/olons-server`,复用现有 PXC、Redis、Traefik、证书和镜像仓库,通过 Secret 注入运行时配置,不改动现有 `im-workbench`。 + +**Tech Stack:** Go 1.26、Vue 3/Vite、Docker、Kubernetes/Kustomize、Argo CD、Traefik、PXC、Redis、Python unittest/PyYAML。 + +--- + +### Task 1: 固定应用仓库部署契约 + +**Files:** +- Create: `tests/test_deployment_contract.py` +- Create: `.dockerignore` +- Modify: `.gitea/workflows/deploy.yml` + +- [ ] **Step 1: Write the failing test** + +新增测试,断言工作流名称、镜像名、Namespace、Deployment、Docker 前后端一体构建以及 `.dockerignore` 的必要条目。 + +- [ ] **Step 2: Run test to verify it fails** + +Run: `python3 -m unittest tests/test_deployment_contract.py -v` + +Expected: FAIL,旧工作流仍引用 `im-workbench` 和 `workbench-system`,且 `.dockerignore` 不存在。 + +- [ ] **Step 3: Write minimal implementation** + +将工作流镜像改为 `git.118im.com/im_team/olons-server:`,滚动目标改为 `olons-server-system/olons-server`;新增仅排除本地和构建垃圾的 `.dockerignore`。 + +- [ ] **Step 4: Run test to verify it passes** + +Run: `python3 -m unittest tests/test_deployment_contract.py -v` + +Expected: PASS。 + +- [ ] **Step 5: Verify application builds** + +Run: `docker run --rm -v "$PWD":/src -w /src golang:1.26 go test ./...` + +Run: `docker build --platform linux/amd64 -t local/olons-server:test .` + +Expected: 两条命令均退出 0。 + +### Task 2: 新增独立 GitOps 应用与公网路由 + +**Files:** +- Create: `/Users/aa/workspace/imcn/im-deploy-config/tests/test_olons_server.py` +- Create: `/Users/aa/workspace/imcn/im-deploy-config/apps/olons-server/base/deployment.yaml` +- Create: `/Users/aa/workspace/imcn/im-deploy-config/apps/olons-server/base/service.yaml` +- Create: `/Users/aa/workspace/imcn/im-deploy-config/apps/olons-server/base/kustomization.yaml` +- Create: `/Users/aa/workspace/imcn/im-deploy-config/apps/olons-server/overlays/test/kustomization.yaml` +- Modify: `/Users/aa/workspace/imcn/im-deploy-config/platform/ingress/routes.yaml` +- Modify: `/Users/aa/workspace/imcn/im-deploy-config/platform/ingress/certificate.yaml` +- Modify: `/Users/aa/workspace/imcn/im-deploy-config/platform/ci/rbac.yaml` + +- [ ] **Step 1: Write the failing test** + +新增测试,断言一副本、小资源、6061 探针、Secret 注入、无 PVC、测试镜像、专用 Host 路由、根路径重定向、证书域名及 CI RoleBinding。 + +- [ ] **Step 2: Run test to verify it fails** + +Run: `python3 -m unittest tests/test_olons_server.py -v` + +Expected: FAIL,目标 Kustomize 目录和专用路由尚不存在。 + +- [ ] **Step 3: Write minimal implementation** + +新增 Deployment、Service 与 test overlay;在现有 Traefik IngressRoute 中加入优先级 20 的 `olns-web.118im.com` 路由和根路径重定向中间件;补充证书 DNS 名称及 CI RoleBinding。 + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `python3 -m unittest discover -s tests -v` + +Expected: 全部 PASS。 + +- [ ] **Step 5: Render and inspect manifests** + +Run: `kubectl kustomize apps/olons-server/overlays/test` + +Expected: 只渲染 `olons-server` Service 与 Deployment,不含 Secret 明文和 PVC。 + +### Task 3: 推送、构建镜像与创建运行时 Secret + +**Files:** +- No repository file changes. + +- [ ] **Step 1: Re-fetch both remotes** + +确认远端无新提交后再推送,避免覆盖开发更新。 + +- [ ] **Step 2: Commit and push application and GitOps changes** + +分别推送 `olons_server/dev` 与 `im-deploy-config/main`,不强推。 + +- [ ] **Step 3: Build and push amd64 image** + +构建并推送 `git.118im.com/im_team/olons-server:test`,记录远端 digest。 + +- [ ] **Step 4: Create scoped runtime secrets** + +在 `olons-server-system` 创建 `gitea-registry` 与 `olons-server-secrets`。从现有集群配置安全取得 PXC/Redis 参数,将后台 `security.dataKey` 按当前算法派生为应用 AES/HMAC key,生成独立 API Key 和 Redis 会话前缀;不得输出任何值。 + +### Task 4: 同步并完成集群和公网验收 + +**Files:** +- Create: `/Users/aa/.codex/memories/extensions/ad_hoc/notes/-olons-server-test-deployment.md` + +- [ ] **Step 1: Wait for Argo CD synchronization** + +确认 `olons-server-test`、`platform-ingress`、`platform-ci` 为 `Synced/Healthy`。 + +- [ ] **Step 2: Verify Kubernetes runtime** + +确认 Deployment 1/1 Available,Pod Ready、零异常重启,Service/Endpoint 正常,容器使用本次镜像 digest。 + +- [ ] **Step 3: Verify middleware isolation** + +确认 PXC、Redis、现有 `im-workbench` 和 `olons-admin` 持续健康且未被重启。 + +- [ ] **Step 4: Verify public behavior** + +确认 `/` 跳转 `/admin/`、`/healthz` 200、TLS 有效;在浏览器检查前端无控制台错误,并以真实 API 完成注册、登录和一个会话只读接口。 + +- [ ] **Step 5: Run final verification and write memory note** + +重新运行两个仓库测试、源码测试/构建及公网检查;将实际提交、镜像 digest、Argo/Pod/API 结果和未覆盖项写入记忆扩展文件,且不得记录 Secret。 diff --git a/tests/test_deployment_contract.py b/tests/test_deployment_contract.py new file mode 100644 index 0000000..bfae227 --- /dev/null +++ b/tests/test_deployment_contract.py @@ -0,0 +1,38 @@ +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + + +class DeploymentContractTest(unittest.TestCase): + def test_workflow_targets_only_olons_server(self): + workflow = (ROOT / ".gitea/workflows/deploy.yml").read_text() + + self.assertIn("Deploy olons-server (k3s test cluster)", workflow) + self.assertIn("git.118im.com/im_team/olons-server:", workflow) + self.assertIn("deployment/olons-server -n olons-server-system", workflow) + self.assertNotIn("im-workbench", workflow) + self.assertNotIn("workbench-system", workflow) + + def test_dockerfile_builds_frontend_into_backend_image(self): + dockerfile = (ROOT / "Dockerfile").read_text() + + self.assertIn("RUN pnpm run build", dockerfile) + self.assertIn("COPY --from=web-builder /web/dist ./web/dist", dockerfile) + self.assertIn("COPY --from=web-builder /web/dist /app/web/dist", dockerfile) + self.assertIn("EXPOSE 6061", dockerfile) + + def test_docker_context_excludes_local_artifacts(self): + entries = { + line.strip() + for line in (ROOT / ".dockerignore").read_text().splitlines() + if line.strip() and not line.lstrip().startswith("#") + } + + for required in {".git", "web/node_modules", "web/dist", "*.log"}: + self.assertIn(required, entries) + + +if __name__ == "__main__": + unittest.main()