From 9203fdd0c20750b3f600ec679b403f19c78a8210 Mon Sep 17 00:00:00 2001 From: Citrons <909229731@qq.com> Date: Thu, 13 Aug 2026 10:54:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(GOV-006):=20=E5=BB=BA=E7=AB=8B=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E9=85=8D=E7=BD=AE=E4=B8=8E=E5=8F=8C=E9=83=A8=E7=BD=B2?= =?UTF-8?q?=E5=9F=BA=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + AGENTS.md | 2 +- CHANGELOG.md | 7 + README.md | 8 ++ VERSION | 2 +- deploy/compose/development.yml | 23 ++++ deploy/compose/production.yml.example | 30 ++++ deploy/env/development/api.env.example | 31 +++++ deploy/env/development/scheduler.env.example | 18 +++ deploy/env/development/web.env.example | 5 + deploy/env/development/worker.env.example | 34 +++++ deploy/env/production/api.env.example | 28 ++++ deploy/env/production/scheduler.env.example | 16 +++ deploy/env/production/web.env.example | 5 + deploy/env/production/worker.env.example | 34 +++++ deploy/nginx/a-share-analysis.conf.example | 20 +++ deploy/systemd/a-share-api.service.example | 19 +++ .../systemd/a-share-scheduler.service.example | 19 +++ deploy/systemd/a-share-worker.service.example | 19 +++ docs/architecture/system-architecture.md | 9 ++ docs/deployment/docker-compose-production.md | 18 +++ docs/deployment/local-development.md | 21 +++ docs/deployment/ubuntu-source-production.md | 35 +++++ .../gitnexus/after.md | 9 ++ .../gitnexus/comparison.md | 10 ++ .../gitnexus/detected-changes.md | 8 ++ .../gitnexus/impact-plan.md | 7 + .../implementation-plan.md | 129 ++++++++++++++++++ .../manifest.yaml | 17 +++ .../prd.md | 31 +++++ .../qa/test-results.md | 16 +++ .../qa/visual-qa.md | 3 + .../release-notes.md | 8 ++ .../requirements.md | 10 ++ .../review/code-review.md | 27 ++++ .../review/standards-review.md | 21 +++ .../rollback.md | 5 + .../test-plan.md | 8 ++ docs/governance/code-development-standards.md | 23 +++- docs/governance/development-workflow.md | 8 +- docs/index.md | 7 + package.json | 1 + .../governance/environment-baseline.test.mjs | 102 ++++++++++++++ 43 files changed, 846 insertions(+), 11 deletions(-) create mode 100644 deploy/compose/development.yml create mode 100644 deploy/compose/production.yml.example create mode 100644 deploy/env/development/api.env.example create mode 100644 deploy/env/development/scheduler.env.example create mode 100644 deploy/env/development/web.env.example create mode 100644 deploy/env/development/worker.env.example create mode 100644 deploy/env/production/api.env.example create mode 100644 deploy/env/production/scheduler.env.example create mode 100644 deploy/env/production/web.env.example create mode 100644 deploy/env/production/worker.env.example create mode 100644 deploy/nginx/a-share-analysis.conf.example create mode 100644 deploy/systemd/a-share-api.service.example create mode 100644 deploy/systemd/a-share-scheduler.service.example create mode 100644 deploy/systemd/a-share-worker.service.example create mode 100644 docs/deployment/docker-compose-production.md create mode 100644 docs/deployment/local-development.md create mode 100644 docs/deployment/ubuntu-source-production.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/gitnexus/after.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/gitnexus/comparison.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/gitnexus/detected-changes.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/gitnexus/impact-plan.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/implementation-plan.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/manifest.yaml create mode 100644 docs/features/GOV-006-environment-deployment-baseline/prd.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/qa/test-results.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/qa/visual-qa.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/release-notes.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/requirements.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/review/code-review.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/review/standards-review.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/rollback.md create mode 100644 docs/features/GOV-006-environment-deployment-baseline/test-plan.md create mode 100644 tools/governance/environment-baseline.test.mjs diff --git a/.gitignore b/.gitignore index aca2c72..f6fb3b9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,11 @@ coverage/ .env .env.* !.env.example +*.env +*.env.* +!*.env.example *.local +.local/ # 编辑器与操作系统 .DS_Store diff --git a/AGENTS.md b/AGENTS.md index d49ecb6..c55ccb9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,7 +23,7 @@ # GitNexus — Code Intelligence -This project is indexed by GitNexus as **a-share-analysis** (528 symbols, 553 relationships, 0 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. +This project is indexed by GitNexus as **a-share-analysis** (620 symbols, 651 relationships, 0 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. > If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bc0d0d..94b6cc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ 本项目采用语义化版本。功能级变化请从 `docs/features/` 按功能编号追溯。 +## 0.0.5 - 2026-08-13 + +- 建立开发与生产环境配置契约及八份无秘密模板。 +- 增加仅运行 Redis 的本地开发 Compose。 +- 建立 Docker Compose 与 Ubuntu 源码生产部署边界、systemd 和 Nginx 示例。 +- 增加环境配置自动化契约测试。 + ## 0.0.4 - 2026-08-13 - 固定 MySQL 5.7.x、`utf8mb4` 和 `utf8mb4_general_ci` 技术基线。 diff --git a/README.md b/README.md index 73cbcc8..5a8c0ea 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,14 @@ A read-only A-share market overview and analysis tool. The first implementation slice is the market overview screen in `web/`. +## 环境与部署 + +- [本地开发环境](docs/deployment/local-development.md):Compose 只启动 Redis,应用从源码运行。 +- [Docker Compose 生产部署](docs/deployment/docker-compose-production.md):当前为应用脚手架落地前的配置契约骨架。 +- [Ubuntu 源码生产部署](docs/deployment/ubuntu-source-production.md):支持 Ubuntu 22.04/24.04 LTS x86_64。 + +无秘密环境模板位于 `deploy/env/`。生产配置不进入 Git 或发布目录;Node.js 22 LTS、pnpm 10、Nginx 和基础设施由服务器管理员提供。 + ## 开发入口 - [文档索引](docs/index.md) diff --git a/VERSION b/VERSION index 81340c7..bbdeab6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.4 +0.0.5 diff --git a/deploy/compose/development.yml b/deploy/compose/development.yml new file mode 100644 index 0000000..76bd07c --- /dev/null +++ b/deploy/compose/development.yml @@ -0,0 +1,23 @@ +name: a-share-analysis-development + +services: + redis: + image: redis:7.2.5-alpine + container_name: a-share-analysis-dev-redis + command: ["redis-server", "--appendonly", "yes"] + ports: + - "127.0.0.1:6379:6379" + volumes: + - ../../.local/redis:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 10 + restart: unless-stopped + networks: + - a-share-analysis-dev + +networks: + a-share-analysis-dev: + name: a-share-analysis-dev diff --git a/deploy/compose/production.yml.example b/deploy/compose/production.yml.example new file mode 100644 index 0000000..039524c --- /dev/null +++ b/deploy/compose/production.yml.example @@ -0,0 +1,30 @@ +# 应用镜像和启动入口将在 Web/Server 脚手架落地后验证。本文件先固定生产配置边界。 +name: a-share-analysis-production + +services: + web: + image: ${A_SHARE_WEB_IMAGE:?必须提供不可变 Web 镜像引用} + env_file: ${A_SHARE_WEB_ENV_FILE:-/etc/a-share-analysis/web.env} + ports: + - "127.0.0.1:8080:80" + restart: unless-stopped + + api: + image: ${A_SHARE_SERVER_IMAGE:?必须提供不可变 Server 镜像引用} + command: ["node", "dist/apps/api/main.js"] + env_file: ${A_SHARE_API_ENV_FILE:-/etc/a-share-analysis/api.env} + ports: + - "127.0.0.1:3000:3000" + restart: unless-stopped + + scheduler: + image: ${A_SHARE_SERVER_IMAGE:?必须提供不可变 Server 镜像引用} + command: ["node", "dist/apps/scheduler/main.js"] + env_file: ${A_SHARE_SCHEDULER_ENV_FILE:-/etc/a-share-analysis/scheduler.env} + restart: unless-stopped + + worker: + image: ${A_SHARE_SERVER_IMAGE:?必须提供不可变 Server 镜像引用} + command: ["node", "dist/apps/worker/main.js"] + env_file: ${A_SHARE_WORKER_ENV_FILE:-/etc/a-share-analysis/worker.env} + restart: unless-stopped diff --git a/deploy/env/development/api.env.example b/deploy/env/development/api.env.example new file mode 100644 index 0000000..fc391e7 --- /dev/null +++ b/deploy/env/development/api.env.example @@ -0,0 +1,31 @@ +# 运行环境;本地源码运行固定为 development。 +APP_ENV=development +# API 只监听本机,避免开发服务暴露到局域网。 +API_HOST=127.0.0.1 +API_PORT=3000 +LOG_LEVEL=debug + +# 第一阶段实际启用的 Redis。 +REDIS_HOST=127.0.0.1 +REDIS_PORT=6379 +REDIS_USERNAME= +REDIS_PASSWORD= +REDIS_DATABASE=0 +REDIS_TLS=false + +# 第一阶段关闭 MySQL;启用后下列连接变量变为必填。 +MYSQL_ENABLED=false +MYSQL_HOST=127.0.0.1 +MYSQL_PORT=3306 +MYSQL_DATABASE=a_share_analysis_dev +MYSQL_USERNAME= +MYSQL_PASSWORD= +MYSQL_CHARSET=utf8mb4 +MYSQL_COLLATION=utf8mb4_general_ci + +# 第一阶段关闭付费 AI;启用后 BASE_URL、API_KEY 和 MODEL 变为必填。 +AI_ENABLED=false +AI_BASE_URL= +AI_API_KEY= +AI_MODEL= +AI_TIMEOUT_MS=30000 diff --git a/deploy/env/development/scheduler.env.example b/deploy/env/development/scheduler.env.example new file mode 100644 index 0000000..a100a54 --- /dev/null +++ b/deploy/env/development/scheduler.env.example @@ -0,0 +1,18 @@ +# Scheduler 只负责触发时间和调度锁。 +APP_ENV=development +LOG_LEVEL=debug +SCHEDULER_TIMEZONE=Asia/Shanghai +SCHEDULER_ENABLED=true + +REDIS_HOST=127.0.0.1 +REDIS_PORT=6379 +REDIS_USERNAME= +REDIS_PASSWORD= +REDIS_DATABASE=1 +REDIS_TLS=false + +# 第一阶段关闭 RabbitMQ,任务通过服务内 Adapter 调用。 +RABBITMQ_ENABLED=false +RABBITMQ_URL= +RABBITMQ_EXCHANGE=a-share-analysis +RABBITMQ_PREFETCH=10 diff --git a/deploy/env/development/web.env.example b/deploy/env/development/web.env.example new file mode 100644 index 0000000..915e5d7 --- /dev/null +++ b/deploy/env/development/web.env.example @@ -0,0 +1,5 @@ +# VITE 变量会进入浏览器,只允许公开配置。 +VITE_APP_ENV=development +VITE_APP_TITLE=A-Share Analysis +VITE_API_BASE_URL=http://127.0.0.1:3000/api +VITE_ENABLE_DEVTOOLS=true diff --git a/deploy/env/development/worker.env.example b/deploy/env/development/worker.env.example new file mode 100644 index 0000000..7be7477 --- /dev/null +++ b/deploy/env/development/worker.env.example @@ -0,0 +1,34 @@ +# 开发环境使用确定性模拟行情,不访问生产数据源。 +APP_ENV=development +LOG_LEVEL=debug +MARKET_DATA_MODE=mock +MARKET_DATA_PROVIDER=deterministic-fixture +MARKET_DATA_BASE_URL= +MARKET_DATA_API_KEY= + +REDIS_HOST=127.0.0.1 +REDIS_PORT=6379 +REDIS_USERNAME= +REDIS_PASSWORD= +REDIS_DATABASE=2 +REDIS_TLS=false + +MYSQL_ENABLED=false +MYSQL_HOST=127.0.0.1 +MYSQL_PORT=3306 +MYSQL_DATABASE=a_share_analysis_dev +MYSQL_USERNAME= +MYSQL_PASSWORD= +MYSQL_CHARSET=utf8mb4 +MYSQL_COLLATION=utf8mb4_general_ci + +RABBITMQ_ENABLED=false +RABBITMQ_URL= +RABBITMQ_EXCHANGE=a-share-analysis +RABBITMQ_PREFETCH=10 + +AI_ENABLED=false +AI_BASE_URL= +AI_API_KEY= +AI_MODEL= +AI_TIMEOUT_MS=30000 diff --git a/deploy/env/production/api.env.example b/deploy/env/production/api.env.example new file mode 100644 index 0000000..a1b7c72 --- /dev/null +++ b/deploy/env/production/api.env.example @@ -0,0 +1,28 @@ +# 生产进程必须显式使用 production,禁止回退开发默认值。 +APP_ENV=production +API_HOST=127.0.0.1 +API_PORT=3000 +LOG_LEVEL=info + +REDIS_HOST=127.0.0.1 +REDIS_PORT=6379 +REDIS_USERNAME= +REDIS_PASSWORD= +REDIS_DATABASE=0 +REDIS_TLS=false + +# 第一阶段关闭;启用后必须填写真实生产连接,但不得提交本文件副本。 +MYSQL_ENABLED=false +MYSQL_HOST=127.0.0.1 +MYSQL_PORT=3306 +MYSQL_DATABASE=a_share_analysis +MYSQL_USERNAME= +MYSQL_PASSWORD= +MYSQL_CHARSET=utf8mb4 +MYSQL_COLLATION=utf8mb4_general_ci + +AI_ENABLED=false +AI_BASE_URL= +AI_API_KEY= +AI_MODEL= +AI_TIMEOUT_MS=30000 diff --git a/deploy/env/production/scheduler.env.example b/deploy/env/production/scheduler.env.example new file mode 100644 index 0000000..33603f1 --- /dev/null +++ b/deploy/env/production/scheduler.env.example @@ -0,0 +1,16 @@ +APP_ENV=production +LOG_LEVEL=info +SCHEDULER_TIMEZONE=Asia/Shanghai +SCHEDULER_ENABLED=true + +REDIS_HOST=127.0.0.1 +REDIS_PORT=6379 +REDIS_USERNAME= +REDIS_PASSWORD= +REDIS_DATABASE=1 +REDIS_TLS=false + +RABBITMQ_ENABLED=false +RABBITMQ_URL= +RABBITMQ_EXCHANGE=a-share-analysis +RABBITMQ_PREFETCH=10 diff --git a/deploy/env/production/web.env.example b/deploy/env/production/web.env.example new file mode 100644 index 0000000..a5d3d58 --- /dev/null +++ b/deploy/env/production/web.env.example @@ -0,0 +1,5 @@ +# VITE 变量会进入浏览器,只允许公开配置。 +VITE_APP_ENV=production +VITE_APP_TITLE=A-Share Analysis +VITE_API_BASE_URL=/api +VITE_ENABLE_DEVTOOLS=false diff --git a/deploy/env/production/worker.env.example b/deploy/env/production/worker.env.example new file mode 100644 index 0000000..e1baf09 --- /dev/null +++ b/deploy/env/production/worker.env.example @@ -0,0 +1,34 @@ +# 生产环境必须使用 live,启动校验拒绝 mock。 +APP_ENV=production +LOG_LEVEL=info +MARKET_DATA_MODE=live +MARKET_DATA_PROVIDER= +MARKET_DATA_BASE_URL= +MARKET_DATA_API_KEY= + +REDIS_HOST=127.0.0.1 +REDIS_PORT=6379 +REDIS_USERNAME= +REDIS_PASSWORD= +REDIS_DATABASE=2 +REDIS_TLS=false + +MYSQL_ENABLED=false +MYSQL_HOST=127.0.0.1 +MYSQL_PORT=3306 +MYSQL_DATABASE=a_share_analysis +MYSQL_USERNAME= +MYSQL_PASSWORD= +MYSQL_CHARSET=utf8mb4 +MYSQL_COLLATION=utf8mb4_general_ci + +RABBITMQ_ENABLED=false +RABBITMQ_URL= +RABBITMQ_EXCHANGE=a-share-analysis +RABBITMQ_PREFETCH=10 + +AI_ENABLED=false +AI_BASE_URL= +AI_API_KEY= +AI_MODEL= +AI_TIMEOUT_MS=30000 diff --git a/deploy/nginx/a-share-analysis.conf.example b/deploy/nginx/a-share-analysis.conf.example new file mode 100644 index 0000000..d8e775a --- /dev/null +++ b/deploy/nginx/a-share-analysis.conf.example @@ -0,0 +1,20 @@ +server { + listen 80; + server_name _; + + root /opt/a-share-analysis/current/web/dist; + index index.html; + + location /api/ { + proxy_pass http://127.0.0.1:3000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/deploy/systemd/a-share-api.service.example b/deploy/systemd/a-share-api.service.example new file mode 100644 index 0000000..bf231e7 --- /dev/null +++ b/deploy/systemd/a-share-api.service.example @@ -0,0 +1,19 @@ +[Unit] +Description=A Share Analysis API +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=a-share +Group=a-share +WorkingDirectory=/opt/a-share-analysis/current +EnvironmentFile=/etc/a-share-analysis/api.env +ExecStart=/usr/bin/node server/dist/apps/api/main.js +Restart=on-failure +RestartSec=5 +NoNewPrivileges=true +PrivateTmp=true + +[Install] +WantedBy=multi-user.target diff --git a/deploy/systemd/a-share-scheduler.service.example b/deploy/systemd/a-share-scheduler.service.example new file mode 100644 index 0000000..a598284 --- /dev/null +++ b/deploy/systemd/a-share-scheduler.service.example @@ -0,0 +1,19 @@ +[Unit] +Description=A Share Analysis Scheduler +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=a-share +Group=a-share +WorkingDirectory=/opt/a-share-analysis/current +EnvironmentFile=/etc/a-share-analysis/scheduler.env +ExecStart=/usr/bin/node server/dist/apps/scheduler/main.js +Restart=on-failure +RestartSec=5 +NoNewPrivileges=true +PrivateTmp=true + +[Install] +WantedBy=multi-user.target diff --git a/deploy/systemd/a-share-worker.service.example b/deploy/systemd/a-share-worker.service.example new file mode 100644 index 0000000..be07c78 --- /dev/null +++ b/deploy/systemd/a-share-worker.service.example @@ -0,0 +1,19 @@ +[Unit] +Description=A Share Analysis Worker +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=a-share +Group=a-share +WorkingDirectory=/opt/a-share-analysis/current +EnvironmentFile=/etc/a-share-analysis/worker.env +ExecStart=/usr/bin/node server/dist/apps/worker/main.js +Restart=on-failure +RestartSec=5 +NoNewPrivileges=true +PrivateTmp=true + +[Install] +WantedBy=multi-user.target diff --git a/docs/architecture/system-architecture.md b/docs/architecture/system-architecture.md index 25ca6f3..2445ec5 100644 --- a/docs/architecture/system-architecture.md +++ b/docs/architecture/system-architecture.md @@ -47,3 +47,12 @@ tools/ 可重复执行的治理和开发工具 ## 4. 当前实施顺序 第一阶段先实现可交互的市场综合概览和稳定数据契约,再接入正式行情采集链路。用户、邀请、权限、会员和支付后置,不进入当前核心架构。 + +## 5. 环境与部署基线 + +- 应用运行环境只定义 `development` 与 `production`;生产环境禁止模拟数据、热更新、调试端口和开发默认值回退。 +- API、Scheduler、Worker、Web 分别拥有开发和生产模板;同一进程跨环境使用相同变量名,通过启动 Schema 校验条件必填项。 +- 本地开发使用 Docker Compose 只启动 Redis,应用进程从源码运行;第一阶段 MySQL 与 RabbitMQ 保持关闭。 +- 生产同时支持 Docker Compose 和 Ubuntu 22.04/24.04 LTS x86_64 源码部署,两者共用配置契约和进程边界。 +- 源码部署由管理员预装 Node.js 22 LTS、pnpm 10、Nginx 和基础设施;项目只验证,不安装或修改系统软件源。 +- 源码部署使用不可变发布目录、`a-share` 用户和 systemd 独立进程配置;生产 env 存放在 `/etc/a-share-analysis/`,不随代码版本切换覆盖。 diff --git a/docs/deployment/docker-compose-production.md b/docs/deployment/docker-compose-production.md new file mode 100644 index 0000000..7433d71 --- /dev/null +++ b/docs/deployment/docker-compose-production.md @@ -0,0 +1,18 @@ +# Docker Compose 生产部署 + +生产 Docker Compose 使用不可变应用镜像和外部 env 文件。当前 `production.yml.example` 是部署契约骨架,应用镜像、健康检查和实际启动命令必须在 Web/Server 脚手架完成后验证,未验证前不得用于线上部署。 + +## 配置边界 + +- 从 `deploy/env/production/` 准备四份真实配置,存放在服务器 `/etc/a-share-analysis/` 或其他受控目录。 +- Compose 通过外部路径加载,不在 YAML、镜像和 Git 中保存秘密。 +- 镜像引用必须包含版本或内容摘要,禁止浮动 `latest`。 +- 第一阶段只依赖 Redis;MySQL 和 RabbitMQ 功能开关保持关闭。 + +## 发布与回退 + +部署前验证镜像对应已推送提交或标签、环境 Schema、健康检查和数据目录。升级只修改不可变镜像引用;失败时恢复上一完整镜像集合和未改变的外部配置。禁止删除 Redis、MySQL 或 RabbitMQ 数据目录作为应用回退手段。 + +## 验证 + +应用脚手架落地后必须补充 `docker compose config`、服务健康检查、日志读取、优雅停止和回退演练证据。本版本不声称生产 Compose 已可运行。 diff --git a/docs/deployment/local-development.md b/docs/deployment/local-development.md new file mode 100644 index 0000000..f88b22d --- /dev/null +++ b/docs/deployment/local-development.md @@ -0,0 +1,21 @@ +# 本地开发环境 + +本地开发使用 Docker Compose 只启动 Redis,Web、API、Scheduler 和 Worker 从源码运行并使用开发环境模板。第一阶段不启动 MySQL 和 RabbitMQ。 + +## 配置 + +从 `deploy/env/development/` 中对应的 `*.env.example` 创建本机 `*.env.local`。真实文件被 Git 忽略,不得写入生产地址或真实密钥。 + +## Redis + +```bash +docker compose -f deploy/compose/development.yml up -d +docker compose -f deploy/compose/development.yml ps +docker compose -f deploy/compose/development.yml down +``` + +Redis 只绑定 `127.0.0.1:6379`,数据写入仓库忽略的 `.local/redis/`。不得把该 Compose 文件用于生产。 + +## 应用状态 + +Web 和 Server 尚未完成脚手架,因此本版本只交付配置契约和 Redis 开发环境。后续脚手架必须使用 Node.js 22 LTS、pnpm 10 和这些模板,不能在本文档中假装应用已经可启动。 diff --git a/docs/deployment/ubuntu-source-production.md b/docs/deployment/ubuntu-source-production.md new file mode 100644 index 0000000..a72eab4 --- /dev/null +++ b/docs/deployment/ubuntu-source-production.md @@ -0,0 +1,35 @@ +# Ubuntu 源码生产部署 + +正式支持 Ubuntu 22.04 LTS 与 Ubuntu 24.04 LTS 的 x86_64。服务器管理员预先提供 Node.js 22 LTS、pnpm 10、Nginx,以及 MySQL 5.7、Redis、RabbitMQ 或相应外部服务;项目不负责安装或升级这些软件,也不修改系统软件源。 + +## 前置验证 + +```bash +uname -m +node --version +pnpm --version +nginx -v +``` + +架构必须为 `x86_64`,Node.js 必须为 22 LTS,pnpm 必须为 10.x。不满足要求时终止部署。 + +## 目录与配置 + +- 发布目录:`/opt/a-share-analysis/releases/<版本或提交>/`。 +- 当前版本:`/opt/a-share-analysis/current` 符号链接。 +- 生产配置:`/etc/a-share-analysis/api.env`、`scheduler.env`、`worker.env`、`web.env`。 +- 运行用户:非登录系统用户 `a-share`。 + +真实配置从仓库生产模板生成,但不得放入发布目录。API、Scheduler、Worker 的 systemd 单元分别通过 `EnvironmentFile=` 读取自己的配置;包含秘密的文件建议为 `root:a-share 0640`。 + +## 构建与启动边界 + +使用 `pnpm install --frozen-lockfile` 安装锁定依赖,在新发布目录构建 Web 和 Server。Web 静态文件由 Nginx 托管,API、Scheduler、Worker 运行编译后的 JavaScript。禁止 Vite 开发服务器、watch、调试端口和生产运行 TypeScript。 + +当前应用尚未脚手架化,仓库中的 systemd 与 Nginx 文件是路径和权限契约示例,不能冒充已经验证的线上启动文件。后续应用功能必须补充实际构建、启动和健康检查验证。 + +## 升级、检查与回退 + +在新的不可变发布目录完成安装、构建和配置校验后,原子切换 `current`,依次重启 API、Scheduler、Worker 并检查 `systemctl status`、`journalctl` 和 HTTP 健康端点。失败时切回上一完整目录并重启服务;生产配置不随版本切换覆盖。 + +项目部署流程禁止连接或读取生产数据库做开发验证。数据库备份属于独立运维职责,应用回退不得删除或覆盖数据。 diff --git a/docs/features/GOV-006-environment-deployment-baseline/gitnexus/after.md b/docs/features/GOV-006-environment-deployment-baseline/gitnexus/after.md new file mode 100644 index 0000000..60ad875 --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/gitnexus/after.md @@ -0,0 +1,9 @@ +# GitNexus 开发后结果 + +- 执行:`npx gitnexus analyze`。 +- 工作区索引:620 个节点、651 条关系、7 个集群、0 条执行流程。 +- 增量分析:7 个已修改文件、25 个新增文件、0 个删除文件。 +- 当前索引提交:`4ce33d8`;配置和文档变化已进入增量图谱。 +- 状态:up-to-date。 + +实现提交后再次分析并锚定最终提交。 diff --git a/docs/features/GOV-006-environment-deployment-baseline/gitnexus/comparison.md b/docs/features/GOV-006-environment-deployment-baseline/gitnexus/comparison.md new file mode 100644 index 0000000..a70df63 --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/gitnexus/comparison.md @@ -0,0 +1,10 @@ +# GitNexus 前后对比 + +| 项目 | 开发前 | 开发后工作区 | 变化 | +|---|---:|---:|---:| +| 节点 | 452 | 620 | +168 | +| 关系 | 477 | 651 | +174 | +| 集群 | 5 | 7 | +2 | +| 执行流程 | 0 | 0 | 0 | + +新增集群来自环境模板与部署文档边界;没有生产执行流程变化,符合影响预估。 diff --git a/docs/features/GOV-006-environment-deployment-baseline/gitnexus/detected-changes.md b/docs/features/GOV-006-environment-deployment-baseline/gitnexus/detected-changes.md new file mode 100644 index 0000000..b88ed54 --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/gitnexus/detected-changes.md @@ -0,0 +1,8 @@ +# GitNexus 变更检测 + +- 命令:`npx gitnexus detect-changes --repo a-share-analysis --scope staged`。 +- 最终暂存区:43 个文件、77 个符号。 +- 受影响流程:0。 +- 风险等级:低。 + +范围包含配置模板、Compose、systemd、Nginx、部署文档、现行治理入口、契约测试和 GOV-006 档案。 diff --git a/docs/features/GOV-006-environment-deployment-baseline/gitnexus/impact-plan.md b/docs/features/GOV-006-environment-deployment-baseline/gitnexus/impact-plan.md new file mode 100644 index 0000000..7b568db --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/gitnexus/impact-plan.md @@ -0,0 +1,7 @@ +# GitNexus 开发前影响分析 + +- 查询主题:`development production environment templates Docker Compose Ubuntu source deployment systemd`。 +- 关联范围:README、开发流程、开发规范、系统架构、GOV-005 和部署目录。 +- 本次新增配置、文档和静态测试,不修改既有生产函数、类或方法,无生产符号上游影响目标。 +- GitNexus 未识别业务执行流程;预计影响限于配置契约、部署边界和治理测试。 +- 风险控制:不提交真实秘密,不运行应用部署,不连接任何生产服务,明确示例骨架的未验证状态。 diff --git a/docs/features/GOV-006-environment-deployment-baseline/implementation-plan.md b/docs/features/GOV-006-environment-deployment-baseline/implementation-plan.md new file mode 100644 index 0000000..cf71655 --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/implementation-plan.md @@ -0,0 +1,129 @@ +# 开发、生产环境与双部署方式实施计划 + +> **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:** 建立可机器验证的开发/生产环境变量契约、本地 Redis Compose,以及 Docker Compose 与 Ubuntu 源码生产部署规范和配置骨架。 + +**Architecture:** 八份 env 模板按进程和环境分离,但同一进程在 development/production 使用相同变量名。静态契约测试负责检查模板完整性、敏感值为空、Web 仅暴露 `VITE_*`、生产安全默认值和部署文件边界;当前应用尚未实现,不伪造可运行的应用镜像或启动命令。 + +**Tech Stack:** dotenv 模板、Docker Compose、Redis、systemd、Nginx、Node.js 内置测试运行器、Markdown、GitNexus。 + +## Global Constraints + +- 环境仅定义 `development` 与 `production`,生产禁止模拟数据、热更新和开发默认值回退。 +- 仓库提交八份 `*.env.example`,不提交任何真实 `.env` 或凭据。 +- 本地 Compose 只启动 Redis;Web/API/Scheduler/Worker 从源码运行。 +- 生产支持 Docker Compose 和 Ubuntu 22.04/24.04 x86_64 源码部署。 +- Node.js 22 LTS、pnpm 10、Nginx 和基础设施由管理员提供,项目只验证不安装。 +- 源码部署使用 `a-share` 用户和 `/etc/a-share-analysis/*.env`。 +- MySQL 与 RabbitMQ 第一阶段默认关闭;MySQL 条件配置遵守 GOV-005。 + +--- + +### Task 1: 建立失败的环境配置契约测试 + +**Files:** +- Create: `tools/governance/environment-baseline.test.mjs` +- Modify: `package.json` + +**Interfaces:** +- Consumes: `deploy/env/`、`deploy/compose/`、`deploy/systemd/`、`deploy/nginx/` 和 `docs/deployment/`。 +- Produces: `npm run test:environment-baseline`。 + +- [ ] **Step 1: 编写模板和部署边界测试** + +测试八份模板存在、同一进程两环境键集合相同、敏感键为空、Web 仅含 `VITE_*`、生产安全开关正确、本地 Compose 仅定义 Redis,并检查 systemd/Nginx 与两份生产部署文档。 + +- [ ] **Step 2: 验证测试因文件缺失而失败** + +运行:`npm run test:environment-baseline` + +预期:失败明确指出第一份缺少的环境模板或部署文件。 + +### Task 2: 实现统一环境模板 + +**Files:** +- Create: `deploy/env/development/api.env.example` +- Create: `deploy/env/development/scheduler.env.example` +- Create: `deploy/env/development/worker.env.example` +- Create: `deploy/env/development/web.env.example` +- Create: `deploy/env/production/api.env.example` +- Create: `deploy/env/production/scheduler.env.example` +- Create: `deploy/env/production/worker.env.example` +- Create: `deploy/env/production/web.env.example` +- Modify: `.gitignore` + +**Interfaces:** +- Consumes: GOV-006 设计中的变量作用域。 +- Produces: 后续 NestJS Schema 和部署方式共同使用的变量名契约。 + +- [ ] **Step 1: 编写四类进程模板** + +每项变量附中文说明;敏感变量保持空值;MySQL/RabbitMQ/AI 由功能开关控制条件必填;生产值拒绝模拟数据和调试模式。 + +- [ ] **Step 2: 加固忽略规则** + +只允许提交 `*.env.example`,忽略 `*.env.local`、真实 env 和本地运行目录。 + +### Task 3: 实现本地 Redis 与生产部署骨架 + +**Files:** +- Create: `deploy/compose/development.yml` +- Create: `deploy/compose/production.yml.example` +- Create: `deploy/systemd/a-share-api.service.example` +- Create: `deploy/systemd/a-share-scheduler.service.example` +- Create: `deploy/systemd/a-share-worker.service.example` +- Create: `deploy/nginx/a-share-analysis.conf.example` +- Create: `docs/deployment/local-development.md` +- Create: `docs/deployment/docker-compose-production.md` +- Create: `docs/deployment/ubuntu-source-production.md` + +**Interfaces:** +- Consumes: Task 2 的模板键集合。 +- Produces: 本地 Redis 启动入口、生产 Compose 配置边界、systemd EnvironmentFile 和 Nginx 代理示例。 + +- [ ] **Step 1: 配置本地 Redis Compose** + +固定 Redis 镜像补丁版本,只暴露本机端口,使用项目专用容器名、网络、健康检查和仓库外本地数据目录;不得定义其他服务。 + +- [ ] **Step 2: 配置生产部署骨架** + +生产 Compose 只表达应用服务的配置契约和不可变镜像要求;systemd 使用独立 EnvironmentFile,Nginx 托管静态文件并代理 `/api/`。 + +- [ ] **Step 3: 编写开发与双生产部署文档** + +明确当前应用尚未脚手架化,文档只验证配置与部署边界;列出管理员前置条件、版本检查、健康检查、升级和回退规则。 + +- [ ] **Step 4: 验证测试转绿** + +运行:`npm run test:environment-baseline && npm run test:mysql-baseline && npm run test:governance` + +预期:全部通过。Docker 可用时额外运行 `docker compose -f deploy/compose/development.yml config`。 + +### Task 4: 完成现行规范、追溯与发布 + +**Files:** +- Modify: `docs/architecture/system-architecture.md` +- Modify: `docs/governance/code-development-standards.md` +- Modify: `docs/governance/development-workflow.md` +- Modify: `README.md` +- Modify: `docs/index.md` +- Modify: `CHANGELOG.md` +- Modify: `VERSION` +- Create: GOV-006 除现有 `design.md`、`implementation-plan.md`、`gitnexus/before.md` 外的全部必备档案。 + +**Interfaces:** +- Consumes: 通过契约测试的配置与部署文件。 +- Produces: 可审查、可发布、可回退的 GOV-006 完整档案和 `v0.0.5`。 + +- [ ] **Step 1: 更新架构和开发规范** + +记录环境模型、配置校验、两种部署方式、平台范围、管理员职责和生产防误用要求。 + +- [ ] **Step 2: 执行 GitNexus、测试和双 Review** + +检测完整暂存区并运行 `npm run governance:finish -- GOV-006 environment-deployment-baseline`。 + +- [ ] **Step 3: 中文提交、推送与发布** + +提交并推送 `main`;封存后创建带注释 `v0.0.5`,精确推送并核对分支与标签。 diff --git a/docs/features/GOV-006-environment-deployment-baseline/manifest.yaml b/docs/features/GOV-006-environment-deployment-baseline/manifest.yaml new file mode 100644 index 0000000..a5ba136 --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/manifest.yaml @@ -0,0 +1,17 @@ +id: GOV-006 +name: 开发、生产环境与双部署方式 +slug: environment-deployment-baseline +status: verifying +owner: project +created_at: 2026-08-13 +prd: prd.md +requirements: requirements.md +design: design.md +implementation_plan: implementation-plan.md +gitnexus_before: gitnexus/before.md +gitnexus_after: gitnexus/after.md +gitnexus_comparison: gitnexus/comparison.md +code_review: review/code-review.md +standards_review: review/standards-review.md +implementation_commit: pending +release: v0.0.5 diff --git a/docs/features/GOV-006-environment-deployment-baseline/prd.md b/docs/features/GOV-006-environment-deployment-baseline/prd.md new file mode 100644 index 0000000..86e9284 --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/prd.md @@ -0,0 +1,31 @@ +# 开发、生产环境与双部署方式 PRD + +- 功能编号:GOV-006 +- PRD 版本:0.1 +- 状态:approved +- 日期:2026-08-13 + +## 问题 + +项目缺少开发/生产配置契约和部署边界,存在真实配置入库、生产误用模拟数据及两种部署方式行为漂移的风险。 + +## 目标 + +- 提供开发和生产的四类进程无秘密模板。 +- 本地只用 Compose 启动 Redis,应用从源码运行。 +- 同时规范 Docker Compose 与 Ubuntu 源码生产部署。 +- 生产配置严格校验且不随发布版本覆盖。 + +## 非目标 + +- 不安装服务器运行时或基础设施。 +- 不声称尚未实现的应用镜像、进程和健康端点已经可运行。 +- 不启用 MySQL、RabbitMQ、正式数据源或 AI。 + +## 验收标准 + +- 八份模板完整、跨环境键一致且无敏感值。 +- Web 模板只含公开 `VITE_*` 变量。 +- 开发 Compose 仅定义 Redis并可通过配置解析。 +- systemd、Nginx 和三份部署文档满足已确认平台与路径边界。 +- 环境契约测试完成红绿验证,双 Review 和治理门禁通过。 diff --git a/docs/features/GOV-006-environment-deployment-baseline/qa/test-results.md b/docs/features/GOV-006-environment-deployment-baseline/qa/test-results.md new file mode 100644 index 0000000..1391786 --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/qa/test-results.md @@ -0,0 +1,16 @@ +# 测试结果 + +## 测试先行证据 + +新增环境基线测试后首次运行 6 个测试全部失败,分别指出八份模板、开发 Compose、systemd/Nginx 示例和部署文档不存在。 + +补齐最小实现后: + +- 环境基线测试:6 个通过,0 个失败。 +- MySQL 基线测试:2 个通过,0 个失败。 +- 治理测试:5 个通过,0 个失败。 +- `docker compose -f deploy/compose/development.yml config`:通过,仅包含 Redis。 +- `git diff --check`:通过。 +- GitNexus 最终暂存区:43 个文件、77 个符号、0 条流程,风险为低。 + +生产 Compose 和源码部署当前属于配置契约骨架,应用尚未脚手架化,因此没有伪造生产启动或健康检查成功证据。 diff --git a/docs/features/GOV-006-environment-deployment-baseline/qa/visual-qa.md b/docs/features/GOV-006-environment-deployment-baseline/qa/visual-qa.md new file mode 100644 index 0000000..d23409e --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/qa/visual-qa.md @@ -0,0 +1,3 @@ +# 视觉验证 + +本功能没有页面或交互变化,视觉验证不适用。 diff --git a/docs/features/GOV-006-environment-deployment-baseline/release-notes.md b/docs/features/GOV-006-environment-deployment-baseline/release-notes.md new file mode 100644 index 0000000..d67cbc7 --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/release-notes.md @@ -0,0 +1,8 @@ +# 发布说明 + +## v0.0.5 + +- 新增八份开发/生产无秘密环境模板。 +- 新增只启动 Redis 的本地开发 Compose。 +- 新增生产 Compose 契约骨架、systemd/Nginx 示例和 Ubuntu 源码部署规范。 +- 新增环境配置自动化契约测试。 diff --git a/docs/features/GOV-006-environment-deployment-baseline/requirements.md b/docs/features/GOV-006-environment-deployment-baseline/requirements.md new file mode 100644 index 0000000..fc5796b --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/requirements.md @@ -0,0 +1,10 @@ +# 工程需求 + +- 环境只定义 `development` 与 `production`。 +- API、Scheduler、Worker、Web 分别提供开发/生产模板,同进程键集合一致。 +- 敏感变量保持空值,真实配置被 Git 忽略。 +- 生产 Worker 固定 `MARKET_DATA_MODE=live`,第一阶段关闭 MySQL 与 RabbitMQ。 +- 本地 Compose 固定 Redis 补丁版本、仅绑定本机并提供健康检查。 +- Ubuntu 源码部署支持 22.04/24.04 LTS x86_64、Node.js 22 LTS、pnpm 10。 +- systemd 使用 `a-share` 用户及独立 `EnvironmentFile`;Nginx 托管静态文件并代理 API。 +- 生产 Compose 使用不可变镜像和外部 env,不承诺当前骨架可直接上线。 diff --git a/docs/features/GOV-006-environment-deployment-baseline/review/code-review.md b/docs/features/GOV-006-environment-deployment-baseline/review/code-review.md new file mode 100644 index 0000000..7849146 --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/review/code-review.md @@ -0,0 +1,27 @@ +# 配置与文档 Review + +- reviewer_type:self +- reviewer:Codex +- 基线提交:`0328f9e` +- 设计提交:`fa1df71` +- 实现提交:`pending` +- 日期:2026-08-13 + +## 审查结论 + +八份模板保持跨环境键一致,敏感值为空,Web 只含公开变量;开发 Compose 只启动 Redis;生产部署示例明确为未验证骨架。现行架构和规范覆盖启动校验、防误用与管理员职责。 + +## 发现与处置 + +| 严重程度 | 发现 | 处置 | +|---|---|---| +| Important | 两种部署方式可能形成不同变量协议 | 强制复用同一模板键并用自动测试比较 | +| Important | 示例容易被误认为可直接上线 | Compose 和文档显式标记需在应用脚手架后验证 | +| Important | 前端变量可能泄露服务端秘密 | 测试要求 Web 模板每个键都以 `VITE_` 开头且只含公开值 | +| Minor | 本地 Redis 数据可能进入仓库 | 使用 `.local/redis` 并加固忽略规则 | + +- 环境契约测试 6/6、MySQL 基线 2/2、治理测试 5/5 通过。 +- Docker Compose 配置解析成功且只包含 Redis。 +- GitNexus 最终暂存区检测为 43 个文件、77 个符号、0 条流程、低风险。 + +最终结论:passed diff --git a/docs/features/GOV-006-environment-deployment-baseline/review/standards-review.md b/docs/features/GOV-006-environment-deployment-baseline/review/standards-review.md new file mode 100644 index 0000000..da8c0ea --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/review/standards-review.md @@ -0,0 +1,21 @@ +# 开发规范符合性 Review + +- reviewer_type:self +- reviewer:Codex +- 实现提交:`pending` +- 日期:2026-08-13 + +## 检查结果 + +- [x] 设计经用户确认后实施。 +- [x] 环境契约先有 6 个失败测试,再补实现使其通过。 +- [x] 模板无真实秘密,真实 env 与本地数据被忽略。 +- [x] 不连接或探测生产服务,不安装服务器软件。 +- [x] MySQL 变量符合 GOV-005,第一阶段默认关闭。 +- [x] Docker Compose 与源码部署共享变量契约。 +- [x] 未把尚未实现的应用描述为已可部署。 +- [x] 无界面变化,视觉验证合理标记不适用。 + +没有未解决的 Critical 或 Important 不符合项。 + +最终结论:passed diff --git a/docs/features/GOV-006-environment-deployment-baseline/rollback.md b/docs/features/GOV-006-environment-deployment-baseline/rollback.md new file mode 100644 index 0000000..0779819 --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/rollback.md @@ -0,0 +1,5 @@ +# 回退说明 + +本功能没有部署应用或连接生产服务。若配置规范需要修正,使用新的中文修复提交更新模板、文档和测试,不移动共享标签。 + +开发 Redis 数据位于被忽略的 `.local/redis/`,版本回退不得自动删除该目录。生产真实 env 位于仓库外,不随应用版本切换覆盖。 diff --git a/docs/features/GOV-006-environment-deployment-baseline/test-plan.md b/docs/features/GOV-006-environment-deployment-baseline/test-plan.md new file mode 100644 index 0000000..1a50dfe --- /dev/null +++ b/docs/features/GOV-006-environment-deployment-baseline/test-plan.md @@ -0,0 +1,8 @@ +# 测试计划 + +- 先运行环境基线测试,证明六类行为因模板和部署文件缺失而失败。 +- 实现后要求环境测试 6/6、MySQL 基线 2/2、治理测试 5/5 通过。 +- 运行 Docker Compose 配置解析,确认只有 Redis。 +- 运行 `git diff --check`、敏感信息扫描和 GitNexus 最终暂存区检测。 +- 运行 `npm run governance:finish -- GOV-006 environment-deployment-baseline`。 +- 本功能无页面变化,视觉验证不适用。 diff --git a/docs/governance/code-development-standards.md b/docs/governance/code-development-standards.md index 76771dd..f72a6f1 100644 --- a/docs/governance/code-development-standards.md +++ b/docs/governance/code-development-standards.md @@ -82,7 +82,18 @@ count += 1; - 免费数据源失败时保留最后有效快照,并明确标记陈旧,禁止伪装实时数据。 - AI 失败不得影响规则分析和基础行情展示。 -## 8. 前端规范 +## 8. 环境配置规范 + +- 应用只接受 `development` 与 `production`,环境必须显式设置。生产启动不得回退到开发默认值、模拟数据、热更新或调试模式。 +- API、Scheduler、Worker 必须使用各自的配置 Schema,在进程启动时验证类型、格式、范围和条件必填项;配置错误立即退出。 +- 同一进程的开发与生产模板必须保持相同变量名。环境差异通过配置值、依赖注入和 Adapter 收敛,禁止在业务代码中散落 `APP_ENV` 分支。 +- 只有 `*.env.example` 可以提交;真实 `.env`、`*.env.local`、密码、Token 和 API Key 必须被 Git 忽略并由运行环境注入。 +- Web 只能使用公开的 `VITE_*` 变量。数据库、Redis、RabbitMQ、数据源和 AI 凭据禁止进入前端变量或构建产物。 +- MySQL、RabbitMQ、正式数据源与 AI 使用显式功能开关;关闭时连接变量可为空,开启后缺少任何必填值都必须拒绝启动。 +- Docker Compose 与源码部署必须复用同一变量契约,禁止为某种部署方式建立语义不同的别名。 +- 生产配置必须位于发布目录之外,切换应用版本不得覆盖配置;日志和诊断输出必须对配置值脱敏。 + +## 9. 前端规范 - 页面不得直接访问第三方行情源、Redis、RabbitMQ 或 AI 服务。 - 全局服务器状态使用 TanStack Vue Query,本地界面偏好使用 Pinia或轻量组合式函数。 @@ -91,14 +102,14 @@ count += 1; - 图表必须标注单位、时间范围、数据日期和更新时间。 - 所有核心交互覆盖加载、空、部分数据、错误和过期状态。 -## 9. 后端与任务规范 +## 10. 后端与任务规范 - API、Scheduler和Worker作为独立进程维护,不在请求期间临时抓取全市场数据。 - Scheduler只决定何时投递;RabbitMQ负责任务传输;Worker负责执行;MySQL负责事实记录;Redis负责当前快照和短期状态。 - 消费者必须幂等,消息确认只能发生在持久化或可验证处理完成后。 - 免费数据源通过 Adapter 统一为内部模型,上层不得依赖供应商原始字段。 -## 10. MySQL 5.7 兼容规范 +## 11. MySQL 5.7 兼容规范 - 开发、测试、Docker 和生产环境必须使用明确的 MySQL 5.7 补丁版本,禁止使用 `latest`、MySQL 8.x 或其他数据库代替兼容性验证。 - 创建数据库必须显式声明 `DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci`;建表与迁移脚本必须显式保持 `DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci`。 @@ -112,7 +123,7 @@ count += 1; - 每份迁移脚本必须能够从上一版本基线在 MySQL 5.7 正向执行;禁止用直接修改共享数据库表代替迁移脚本。 - 代码 Review 必须检查服务端版本、字符集、排序规则、SQL/迁移兼容性、索引长度和字符串比较语义;任一不符合项按 Important 处理并阻断发布。 -## 11. 测试要求 +## 12. 测试要求 - 功能和缺陷修复遵循测试先行:先看到测试因缺少行为而失败,再实现最小代码。 - 纯规则和指标计算使用单元测试,期望值必须手工推导。 @@ -121,14 +132,14 @@ count += 1; - 关键页面使用 Playwright流程测试和视觉截图对比。 - 合并前必须通过类型检查、测试、构建和适用的视觉验证。 -## 12. 安全要求 +## 13. 安全要求 - 禁止提交 `.env`、API Key、密码、真实账号和生产数据。 - AI 密钥、数据源凭证和支付凭证仅存在服务端密钥管理中。 - SQL必须参数化;外部输入必须在边界处校验。 - 日志和异常报告必须脱敏。 -## 13. 代码 Review 要求 +## 14. 代码 Review 要求 开发者完成自测不等于 Review 完成。审查者必须从功能 PRD、设计和 Git diff 出发,检查: diff --git a/docs/governance/development-workflow.md b/docs/governance/development-workflow.md index e94c44c..8e8f9d1 100644 --- a/docs/governance/development-workflow.md +++ b/docs/governance/development-workflow.md @@ -23,14 +23,15 @@ released → superseded 5. 比较可选方案,记录设计与关键决策。 6. 更新 API、事件和数据结构契约。 7. 编写测试计划和实施计划。 -8. 确保 Git 工作区基线可识别,执行: +8. 明确功能需要的环境变量、功能开关和部署影响;同步更新开发/生产模板,禁止只修改本机真实 env。 +9. 确保 Git 工作区基线可识别,执行: ```bash npm run governance:start -- FEAT-MO-001 market-overview ``` -9. 运行 GitNexus 查询、上下文和影响分析,将结果写入 `gitnexus/impact-plan.md`。 -10. 高风险或关键路径变化必须先向用户说明影响,再进入开发。 +10. 运行 GitNexus 查询、上下文和影响分析,将结果写入 `gitnexus/impact-plan.md`。 +11. 高风险或关键路径变化必须先向用户说明影响,再进入开发。 开发前必须至少回答:修改什么、为什么修改、影响谁、如何验证、失败后如何回退。 @@ -79,6 +80,7 @@ npm run governance:finish -- FEAT-MO-001 market-overview - 发布说明和回退方法可执行。 - 中文提交已关联功能编号。 - 当前分支和本次新建标签已推送到 `origin`,功能档案保存远程核对结果。 +- 配置契约变化已同步开发/生产模板和适用的 Compose、systemd、Nginx及部署文档。 ## 7. 提交流程 diff --git a/docs/index.md b/docs/index.md index e663430..0404495 100644 --- a/docs/index.md +++ b/docs/index.md @@ -15,6 +15,12 @@ - [ADR-0001:单仓库双项目独立部署](adr/ADR-0001-monorepo-independent-deploy.md) - [ADR-0002:GitNexus 双门禁](adr/ADR-0002-gitnexus-double-gate.md) +## 环境与部署 + +- [本地开发环境](deployment/local-development.md) +- [Docker Compose 生产部署](deployment/docker-compose-production.md) +- [Ubuntu 源码生产部署](deployment/ubuntu-source-production.md) + ## GitNexus - [仓库基线](gitnexus/repository-baseline.md) @@ -29,5 +35,6 @@ - [GOV-003:项目专用 Gitea 远程仓库](features/GOV-003-gitea-local-remote/manifest.yaml) - [GOV-004:提交后强制推送 Gitea](features/GOV-004-mandatory-gitea-push/manifest.yaml) - [GOV-005:MySQL 5.7 字符集与兼容性基线](features/GOV-005-mysql57-charset-baseline/manifest.yaml) +- [GOV-006:开发、生产环境与双部署方式](features/GOV-006-environment-deployment-baseline/manifest.yaml) 功能档案是需求、设计、代码、测试、版本和回退之间的唯一正式关联入口。 diff --git a/package.json b/package.json index 7636381..0e6c3c4 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "test:governance": "node --test tools/governance/feature-record.test.mjs", "test:mysql-baseline": "node --test tools/governance/mysql-baseline.test.mjs", + "test:environment-baseline": "node --test tools/governance/environment-baseline.test.mjs", "governance:start": "node tools/governance/start.mjs", "governance:finish": "node tools/governance/finish.mjs" } diff --git a/tools/governance/environment-baseline.test.mjs b/tools/governance/environment-baseline.test.mjs new file mode 100644 index 0000000..70597ac --- /dev/null +++ b/tools/governance/environment-baseline.test.mjs @@ -0,0 +1,102 @@ +import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; +import test from 'node:test'; + +const repoRoot = new URL('../../', import.meta.url); +const processNames = ['api', 'scheduler', 'worker', 'web']; +const sensitiveKeyPattern = /(PASSWORD|API_KEY|TOKEN|SECRET|RABBITMQ_URL)$/; + +async function readRepoFile(relativePath) { + return readFile(new URL(relativePath, repoRoot), 'utf8'); +} + +function parseEnvTemplate(content) { + const entries = new Map(); + + for (const line of content.split(/\r?\n/)) { + const normalizedLine = line.trim(); + if (!normalizedLine || normalizedLine.startsWith('#')) continue; + + const separator = normalizedLine.indexOf('='); + assert.ok(separator > 0, `无效环境变量行:${line}`); + entries.set(normalizedLine.slice(0, separator), normalizedLine.slice(separator + 1)); + } + + return entries; +} + +test('开发和生产环境都提供四类进程模板且键集合一致', async () => { + for (const processName of processNames) { + const development = parseEnvTemplate( + await readRepoFile(`deploy/env/development/${processName}.env.example`), + ); + const production = parseEnvTemplate( + await readRepoFile(`deploy/env/production/${processName}.env.example`), + ); + + assert.deepEqual([...development.keys()].sort(), [...production.keys()].sort()); + } +}); + +test('模板不包含敏感值且 Web 只暴露 VITE 变量', async () => { + for (const environment of ['development', 'production']) { + for (const processName of processNames) { + const entries = parseEnvTemplate( + await readRepoFile(`deploy/env/${environment}/${processName}.env.example`), + ); + + for (const [key, value] of entries) { + if (sensitiveKeyPattern.test(key)) assert.equal(value, '', `${key} 必须保持空值`); + if (processName === 'web') assert.ok(key.startsWith('VITE_'), `Web 禁止暴露 ${key}`); + } + } + } +}); + +test('生产模板禁止模拟数据并保持 MySQL 5.7 配置契约', async () => { + const worker = parseEnvTemplate(await readRepoFile('deploy/env/production/worker.env.example')); + + assert.equal(worker.get('APP_ENV'), 'production'); + assert.equal(worker.get('MARKET_DATA_MODE'), 'live'); + assert.equal(worker.get('MYSQL_ENABLED'), 'false'); + assert.equal(worker.get('MYSQL_CHARSET'), 'utf8mb4'); + assert.equal(worker.get('MYSQL_COLLATION'), 'utf8mb4_general_ci'); + assert.equal(worker.get('RABBITMQ_ENABLED'), 'false'); +}); + +test('本地 Compose 仅启动 Redis 并提供健康检查', async () => { + const compose = await readRepoFile('deploy/compose/development.yml'); + + assert.match(compose, /services:\s*\n\s+redis:/); + assert.match(compose, /healthcheck:/); + assert.match(compose, /127\.0\.0\.1:/); + assert.doesNotMatch(compose, /^\s{2}(mysql|rabbitmq|api|worker|scheduler|web):/m); +}); + +test('生产源码部署使用独立 EnvironmentFile 并提供 Nginx 代理', async () => { + for (const processName of ['api', 'scheduler', 'worker']) { + const unit = await readRepoFile(`deploy/systemd/a-share-${processName}.service.example`); + assert.match(unit, new RegExp(`EnvironmentFile=/etc/a-share-analysis/${processName}\\.env`)); + assert.match(unit, /User=a-share/); + assert.match(unit, /WorkingDirectory=\/opt\/a-share-analysis\/current/); + } + + const nginx = await readRepoFile('deploy/nginx/a-share-analysis.conf.example'); + assert.match(nginx, /location \/api\//); + assert.match(nginx, /proxy_pass http:\/\/127\.0\.0\.1:3000/); + assert.match(nginx, /\/opt\/a-share-analysis\/current\/web\/dist/); +}); + +test('部署文档覆盖本地、Compose 生产和 Ubuntu 源码生产', async () => { + const local = await readRepoFile('docs/deployment/local-development.md'); + const compose = await readRepoFile('docs/deployment/docker-compose-production.md'); + const source = await readRepoFile('docs/deployment/ubuntu-source-production.md'); + + assert.match(local, /Redis/); + assert.match(compose, /Docker Compose/); + assert.match(source, /Ubuntu 22\.04 LTS/); + assert.match(source, /Ubuntu 24\.04 LTS/); + assert.match(source, /Node\.js 22 LTS/); + assert.match(source, /pnpm 10/); + assert.match(source, /不负责安装/); +});