fix(GOV-009): 完成仅源码部署最终复审

This commit is contained in:
2026-08-13 21:33:38 +08:00
parent 47d0e0d248
commit c75665446b
13 changed files with 191 additions and 50 deletions
+1 -1
View File
@@ -24,7 +24,7 @@
<!-- gitnexus:start --> <!-- gitnexus:start -->
# GitNexus — Code Intelligence # GitNexus — Code Intelligence
This project is indexed by GitNexus as **a-share-analysis** (1589 symbols, 2211 relationships, 47 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** (1606 symbols, 2253 relationships, 50 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. > If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
+11 -7
View File
@@ -23,16 +23,20 @@ pnpm verify:release
生产配置放在 `/etc/a-share-analysis/api.env`、`scheduler.env`、`worker.env`,建议权限 `root:a-share 0640`;Web 的 `VITE_` 公开变量在构建前注入。三个 systemd 单元通过各自的 `EnvironmentFile=` 只读取本进程需要的键,运行用户为非登录系统用户 `a-share`。 生产配置放在 `/etc/a-share-analysis/api.env`、`scheduler.env`、`worker.env`,建议权限 `root:a-share 0640`;Web 的 `VITE_` 公开变量在构建前注入。三个 systemd 单元通过各自的 `EnvironmentFile=` 只读取本进程需要的键,运行用户为非登录系统用户 `a-share`。
```bash ```bash
sudo install -m 0644 deploy/systemd/a-share-*.service.example /etc/systemd/system/ sudo install -m 0644 deploy/systemd/a-share-api.service.example /etc/systemd/system/a-share-api.service
sudo install -m 0644 deploy/systemd/a-share-scheduler.service.example /etc/systemd/system/a-share-scheduler.service
sudo install -m 0644 deploy/systemd/a-share-worker.service.example /etc/systemd/system/a-share-worker.service
sudo install -m 0644 deploy/nginx/a-share-analysis.conf.example /etc/nginx/conf.d/a-share-analysis.conf sudo install -m 0644 deploy/nginx/a-share-analysis.conf.example /etc/nginx/conf.d/a-share-analysis.conf
sudo systemd-analyze verify /etc/systemd/system/a-share-api.service.example sudo systemd-analyze verify /etc/systemd/system/a-share-api.service
sudo systemd-analyze verify /etc/systemd/system/a-share-scheduler.service.example sudo systemd-analyze verify /etc/systemd/system/a-share-scheduler.service
sudo systemd-analyze verify /etc/systemd/system/a-share-worker.service.example sudo systemd-analyze verify /etc/systemd/system/a-share-worker.service
sudo nginx -t sudo nginx -t
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl restart a-share-api.service.example sudo systemctl restart a-share-api.service
sudo systemctl restart a-share-scheduler.service.example sudo systemctl restart a-share-scheduler.service
sudo systemctl restart a-share-worker.service.example sudo systemctl restart a-share-worker.service
sudo systemctl status a-share-api.service a-share-scheduler.service a-share-worker.service
sudo journalctl -u a-share-api.service -u a-share-scheduler.service -u a-share-worker.service --since '5 minutes ago'
curl --fail http://127.0.0.1:3000/api/v1/health curl --fail http://127.0.0.1:3000/api/v1/health
curl --fail http://127.0.0.1/ curl --fail http://127.0.0.1/
``` ```
@@ -2,18 +2,18 @@
- 记录时间:2026-08-13 GMT+8 - 记录时间:2026-08-13 GMT+8
- 分支:`main` - 分支:`main`
- 实现提交:`95b8611bd9bbc282193591e472e2e19c617e5035` - 仅源码实现提交:`47d0e0d248eb9b21cbd12f1625d3bd0e9c266dc9`
- 远端:`origin/main` 已核对为同一 SHA - 远端:`origin/main` 已核对为同一 SHA;最终 Review 证据提交将在验证后同步
```text ```text
Repository: a-share-analysis Repository: a-share-analysis
Indexed commit: 95b8611 Indexed commit: 47d0e0d
Current commit: 95b8611 Current commit: 47d0e0d
Status: up-to-date Status: up-to-date
Nodes: 1568 Nodes: 1606
Relationships: 2190 Relationships: 2253
Clusters: 46 Clusters: 48
Execution flows: 47 Execution flows: 50
``` ```
图谱已包含 Web、API、Scheduler、Worker、配置加载、健康检查、OpenAPI、消息信封、Schema、Docker 验证和治理门禁。GitNexus 本地索引未提交仓库。 图谱已包含 Web、API、Scheduler、Worker、配置加载、健康检查、OpenAPI、消息信封、Schema、外部 Redis 预检、源码产物/systemd/Nginx 发布验证和治理门禁。旧 Docker 验证符号已删除,GitNexus 本地索引未提交仓库。
@@ -16,3 +16,16 @@
- Schema 空库、MySQL 5.7.42、契约漂移和 `.vue` 工程边界均有自动门禁。 - Schema 空库、MySQL 5.7.42、契约漂移和 `.vue` 工程边界均有自动门禁。
新增关系数量符合“从占位目录建立应用基线”的预期。后续市场概览功能应在这些边界内增加业务模块,不应把 Web 与 Server 重新耦合。 新增关系数量符合“从占位目录建立应用基线”的预期。后续市场概览功能应在这些边界内增加业务模块,不应把 Web 与 Server 重新耦合。
## 仅源码部署调整对比
| 项目 | 调整设计提交 `cfcd7f1` | 实现与 Review 后 | 变化 |
| --- | ---: | ---: | ---: |
| 符号 | 1589 | 1606 | +17 |
| 关系 | 2211 | 2253 | +42 |
| 执行流程 | 47 | 50 | +3 |
- 删除 Docker 构建、镜像引用和容器生命周期校验符号,新增源码发布校验流程。
- 新增 `checkLocalRedis` 与 `runTestApi`,Playwright 只依赖现有本机 Redis,不拥有基础设施生命周期。
- `verifySourceDeployment` 校验 Web、API、Scheduler、Worker 产物以及 systemd/Nginx;递归阻断任意活动 Dockerfile、`.dockerignore` 和 Compose YAML。
- GitNexus compare 检测风险为 MEDIUM,98 个变更符号、3 个受影响符号,影响集中在部署门禁与 E2E 启动链;未影响业务 API、市场模块、生产数据库或 RocketMQ 消息发送。
@@ -13,3 +13,14 @@ Review 整改前对具体符号分别执行上游影响分析:三进程 bootst
最终代码复审补充发现无引号 Vue 外部 `script src` 可绕过早期正则门禁;影响分析为 LOW,只有 1 个直接调用者和 1 条边界检查流程。`95b8611` 改用 Vue 3.5.41 官方 SFC 解析器,并以单双引号、无引号和属性换序用例验证,未扩大应用运行影响面。 最终代码复审补充发现无引号 Vue 外部 `script src` 可绕过早期正则门禁;影响分析为 LOW,只有 1 个直接调用者和 1 条边界检查流程。`95b8611` 改用 Vue 3.5.41 官方 SFC 解析器,并以单双引号、无引号和属性换序用例验证,未扩大应用运行影响面。
检测范围与开发前影响计划一致,未出现用户体系、生产数据库、外部消息写入或其他项目流程。 检测范围与开发前影响计划一致,未出现用户体系、生产数据库、外部消息写入或其他项目流程。
## 仅源码部署调整
- 对比基线:`cfcd7f13d9a6a35173d01bd8ca1458911190e70f`
- 实现提交:`47d0e0d248eb9b21cbd12f1625d3bd0e9c266dc9`
- 变更范围:27 个已索引文件、98 个变更符号、3 个受影响符号
- 风险:MEDIUM
开发前对 `assertImmutableImageReferences`、旧 Docker 辅助函数和 `forwardSignal` 的上游影响均为 LOW,0 条业务执行流受影响。最终 compare 的 MEDIUM 来自部署门禁整体替换:旧 Docker 验证删除,源码发布与 Redis 预检新增,并非应用业务依赖扩散。
Review 中 `verifySourceDeployment` 和 `assertDockerArtifactsAbsent` 的影响分析均为 LOW;后者只影响 1 条源码部署校验流程。递归活动资产扫描与 systemd 正式目标名测试完成后,部署 6/6、环境 10/10 和完整 `verify:release` 通过。
@@ -57,7 +57,7 @@
### GREEN ### GREEN
- 源码部署夹具:5/5;覆盖完整发布、Docker 活动文件、Worker 产物缺失、systemd root 用户和 Nginx 错误配置。 - 源码部署夹具:初始 5/5;最终 Review 加入任意新 Dockerfile/Compose 路径回归后为 6/6。
- 环境与部署契约:10/10;活动 Docker 入口不存在,本地外部 Redis和唯一 Ubuntu 源码部署规则通过。 - 环境与部署契约:10/10;活动 Docker 入口不存在,本地外部 Redis和唯一 Ubuntu 源码部署规则通过。
- Redis 预检:2/2;OS 分配的本机 TCP 端口分别验证可达和不可达,不安装或启动外部服务。 - Redis 预检:2/2;OS 分配的本机 TCP 端口分别验证可达和不可达,不安装或启动外部服务。
- 本机 `redis-cli -h 127.0.0.1 -p 6379 ping` 返回 `PONG`。 - 本机 `redis-cli -h 127.0.0.1 -p 6379 ping` 返回 `PONG`。
@@ -65,3 +65,18 @@
- Playwright 桌面与移动视口共 4/4,通过真实本机 Redis、API 健康接口和 Web 同源代理链路。 - Playwright 桌面与移动视口共 4/4,通过真实本机 Redis、API 健康接口和 Web 同源代理链路。
前述 `pnpm verify:docker` 及镜像验证内容是调整前的真实历史证据。2026-08-13 用户确认仅源码部署后,这些能力已被取代并从活动仓库删除,不再执行或作为发布依据。 前述 `pnpm verify:docker` 及镜像验证内容是调整前的真实历史证据。2026-08-13 用户确认仅源码部署后,这些能力已被取代并从活动仓库删除,不再执行或作为发布依据。
### 最终源码发布验证
`pnpm verify:release`:passed。
- Node.js 22.11.0、pnpm 10.25.0 运行时检查通过。
- 环境 10/10、Schema 28/28、契约 3/3、工程边界 8/8、源码部署 6/6。
- Web 8/8;Server Vitest 30/30,Redis 预检 Node 测试 2/2。
- Web、API、Scheduler、Worker 生产构建通过。
- Playwright 桌面/移动共 4/4,通过现有本机 Redis 和真实 API 健康链路。
- `verify:source` 确认四类产物、三份 systemd 单元与 Nginx 配置一致;未调用 Docker。
应用内浏览器补充验证 1440×900 与 390×844:两种视口页面均非空,页面宽度等于视口宽度,无横向溢出、文字重叠或控制台 warning/error;DOM 中导航与“应用就绪”区域完整。
最终 Review 发现并修复两项 Important:systemd 模板曾以 `.service.example` 目标名安装,任意新路径的 Dockerfile/Compose 文件曾可绕过固定清单。两项均先增加失败测试,再修复至 GREEN。
@@ -22,3 +22,25 @@
Critical、Important、Minor 均无未关闭问题。 Critical、Important、Minor 均无未关闭问题。
最终结论:passed 最终结论:passed
## 仅源码部署调整复审
- 审查范围:`cfcd7f13d9a6a35173d01bd8ca1458911190e70f..47d0e0d248eb9b21cbd12f1625d3bd0e9c266dc9` 及其后 Review 修复
- 审查重点:发布门禁绕过、子进程与信号、Redis 失败语义、systemd/Nginx 真实命令、历史证据和测试缺口
### 发现与处置
1. **Important:systemd 模板目标名不可执行。** 初版源码手册把 `*.service.example` 原样安装并以该名称重启,真实 systemd 单元名不正确。新增环境契约测试先 RED,随后改为分别安装成 `a-share-api.service`、`a-share-scheduler.service`、`a-share-worker.service`,并统一 verify/restart/status/journalctl 命令;环境 10/10 GREEN。
2. **Important:Docker 禁止清单可被新路径绕过。** 初版只阻断已删除的固定旧路径,`ops/Dockerfile` 或新 Compose YAML 会被放行。新增未列举路径测试先 RED,随后用递归活动资产扫描阻断任意 Dockerfile、`.dockerignore`、`docker-compose*.yml` 和 `compose/` YAML;部署测试 6/6 GREEN。
### 最终证据
- `pnpm verify:release` 完整通过,包含静态检查、类型、全部测试、四类构建、Playwright 4/4 和源码发布校验。
- Redis 不可达时只返回脱敏的固定本机服务提示;无安装、启动、停止或删除基础设施的分支。
- API 子进程只接收公开禁用态测试夹具,SIGINT/SIGTERM 只转发给该子进程。
- 源码门禁要求 Web 与三个 Server 入口、三份最小权限 systemd 单元和 Nginx Web/API 配置完整。
- 当前跟踪文件没有 Dockerfile、Compose、`.dockerignore` 或 Docker 专用脚本;历史文档事实仍可追溯。
Critical、Important、Minor 均无未关闭问题。
最终结论:passed
@@ -19,3 +19,17 @@
Critical、Important、Minor 均无未关闭问题。 Critical、Important、Minor 均无未关闭问题。
最终结论:passed 最终结论:passed
## 仅源码部署调整复审
- 实施计划、TDD RED/GREEN、GitNexus 前置影响、开发后 compare 和回退说明均已写入 GOV-009 档案。
- 新增关键函数包含外部基础设施所有权和安全边界中文注释,文件按“源码发布门禁”和“E2E 启动器”稳定职责组织,未过度拆分。
- MySQL 仍固定 5.7.42、`utf8mb4`、`utf8mb4_general_ci`、`+08:00`;本次没有连接、探测或读取生产数据库,也没有创建迁移或业务表。
- 本机 Redis 只执行 `PING` 与 TCP 可达性检查;RocketMQ 保持禁用,没有创建 Topic、生产或消费消息。
- 现行架构、开发规范、流程规范和部署手册只宣称 Ubuntu 源码部署;GOV-006 与早期 GOV-009 Docker 记录保留为历史并明确被取代。
- `47d0e0d` 为带 GOV-009 的中文 Conventional Commit,已推送项目专用 Gitea;最终 Review 修复和证据将在验证后以独立中文提交推送。
- manifest 保持 `reviewing/unreleased`,本地与远端均不存在 `v0.0.8`,等待用户完成总体架构确认。
Critical、Important、Minor 均无未关闭问题。
最终结论:passed
@@ -2,7 +2,7 @@
- 功能编号:GOV-009 - 功能编号:GOV-009
- 决策日期:2026-08-13 - 决策日期:2026-08-13
- 状态:设计已确认,待实施 - 状态:已实施并通过 Review,等待总体架构确认
- 取代范围:GOV-006、GOV-009 中仍有效的 Docker 部署约定 - 取代范围:GOV-006、GOV-009 中仍有效的 Docker 部署约定
## 结论 ## 结论
@@ -54,7 +54,7 @@
- Consumes: repository paths and the existing `deploy/systemd/`, `deploy/nginx/`, `web/dist/`, and `server/dist/` conventions. - Consumes: repository paths and the existing `deploy/systemd/`, `deploy/nginx/`, `web/dist/`, and `server/dist/` conventions.
- Produces: root scripts `test:deployment` and `verify:source`; failing tests that define the source-only repository contract before implementation. - Produces: root scripts `test:deployment` and `verify:source`; failing tests that define the source-only repository contract before implementation.
- [ ] **Step 1: Add failing fixture tests for the source verifier** - [x] **Step 1: Add failing fixture tests for the source verifier**
Create tests that import the not-yet-existing module and define these exact behaviors: Create tests that import the not-yet-existing module and define these exact behaviors:
@@ -96,7 +96,7 @@ test('Nginx 未代理 API 或未指向 Web dist 时会阻断源码发布', async
The local `createSourceReleaseFixture` helper must create only the minimum paths needed by the verifier: four build outputs, three unit files with distinct `EnvironmentFile`, and one Nginx file. It must accept `omit`, `apiUser`, and `invalidNginx` options so each negative test changes one condition. The local `createSourceReleaseFixture` helper must create only the minimum paths needed by the verifier: four build outputs, three unit files with distinct `EnvironmentFile`, and one Nginx file. It must accept `omit`, `apiUser`, and `invalidNginx` options so each negative test changes one condition.
- [ ] **Step 2: Replace Docker assertions in the environment baseline with source-only assertions** - [x] **Step 2: Replace Docker assertions in the environment baseline with source-only assertions**
Keep env-template and RocketMQ tests unchanged. Replace Compose/Docker tests with explicit active-path assertions: Keep env-template and RocketMQ tests unchanged. Replace Compose/Docker tests with explicit active-path assertions:
@@ -123,7 +123,7 @@ test('活动仓库不再包含 Docker 运行构建或部署入口', async () =>
Add focused checks that current docs describe external local Redis, Ubuntu 22.04/24.04, Node 22.x, pnpm 10.25.0, immutable releases, health checks, and rollback. Do not scan historical feature archives for the word Docker because those files are intentional evidence. Add focused checks that current docs describe external local Redis, Ubuntu 22.04/24.04, Node 22.x, pnpm 10.25.0, immutable releases, health checks, and rollback. Do not scan historical feature archives for the word Docker because those files are intentional evidence.
- [ ] **Step 3: Point root deployment scripts at the not-yet-existing source verifier** - [x] **Step 3: Point root deployment scripts at the not-yet-existing source verifier**
Change root scripts to the following contract: Change root scripts to the following contract:
@@ -135,7 +135,7 @@ Change root scripts to the following contract:
Remove `check:images` and `verify:docker`. Remove `check:images` and `verify:docker`.
- [ ] **Step 4: Run RED tests and record expected failures** - [x] **Step 4: Run RED tests and record expected failures**
Run: Run:
@@ -167,11 +167,11 @@ Expected: the deployment test fails because `verify-source-deployment.mjs` does
- Consumes: `verifySourceDeployment(repositoryRoot?: string | URL): Promise<void>` inputs and the existing build/deployment paths. - Consumes: `verifySourceDeployment(repositoryRoot?: string | URL): Promise<void>` inputs and the existing build/deployment paths.
- Produces: an importable verifier plus the CLI command `pnpm verify:source`, which exits non-zero on incomplete or unsafe source releases. - Produces: an importable verifier plus the CLI command `pnpm verify:source`, which exits non-zero on incomplete or unsafe source releases.
- [ ] **Step 1: Run GitNexus impact before modifying or deleting deployment symbols** - [x] **Step 1: Run GitNexus impact before modifying or deleting deployment symbols**
Run upstream impact for `assertImmutableImageReferences`, `docker`, and `verifyLongRunningProcess`, including tests. Report direct callers, affected processes, and risk to the user. If risk is HIGH or CRITICAL, stop before editing and ask for confirmation as required by `AGENTS.md`. Run upstream impact for `assertImmutableImageReferences`, `docker`, and `verifyLongRunningProcess`, including tests. Report direct callers, affected processes, and risk to the user. If risk is HIGH or CRITICAL, stop before editing and ask for confirmation as required by `AGENTS.md`.
- [ ] **Step 2: Implement the minimal source verifier** - [x] **Step 2: Implement the minimal source verifier**
Implement these exports in one readable file: Implement these exports in one readable file:
@@ -206,11 +206,11 @@ The implementation must:
- produce errors that identify the missing or invalid file without printing env values; - produce errors that identify the missing or invalid file without printing env values;
- print `源码发布产物与 systemd/Nginx 配置验证通过` only in direct CLI mode. - print `源码发布产物与 systemd/Nginx 配置验证通过` only in direct CLI mode.
- [ ] **Step 3: Delete the obsolete Docker artifacts** - [x] **Step 3: Delete the obsolete Docker artifacts**
Delete only the paths listed in this task. Do not delete env templates, systemd units, the production Nginx template, or historical GOV-006/GOV-009 evidence. Delete only the paths listed in this task. Do not delete env templates, systemd units, the production Nginx template, or historical GOV-006/GOV-009 evidence.
- [ ] **Step 4: Run GREEN deployment tests after creating build outputs** - [x] **Step 4: Run GREEN deployment tests after creating build outputs**
Run: Run:
@@ -223,7 +223,7 @@ pnpm verify:source
Expected: all commands pass; `verify:source` confirms all four build outputs and the deployment templates. Expected: all commands pass; `verify:source` confirms all four build outputs and the deployment templates.
- [ ] **Step 5: Detect scope, commit, and push the executable deployment change** - [x] **Step 5: Detect scope, commit, and push the executable deployment change**
Run GitNexus `detect_changes(scope: "all")`. Review that only deployment validation and environment-policy flows are affected, then run: Run GitNexus `detect_changes(scope: "all")`. Review that only deployment validation and environment-policy flows are affected, then run:
@@ -246,11 +246,11 @@ git push origin "$(git branch --show-current)"
- Consumes: TCP endpoint `127.0.0.1:6379` and the existing public production API fixture. - Consumes: TCP endpoint `127.0.0.1:6379` and the existing public production API fixture.
- Produces: `checkLocalRedis({ host, port, timeoutMs }): Promise<void>` and `runTestApi(): Promise<void>`; direct execution launches only the API process after Redis succeeds. - Produces: `checkLocalRedis({ host, port, timeoutMs }): Promise<void>` and `runTestApi(): Promise<void>`; direct execution launches only the API process after Redis succeeds.
- [ ] **Step 1: Run GitNexus upstream impact for launcher symbols** - [x] **Step 1: Run GitNexus upstream impact for launcher symbols**
Run impact for `forwardSignal` and the `run-test-api.mjs` file, including tests. Report the Playwright web-server dependency and any API health flows before editing. Stop for confirmation only if the graph reports HIGH or CRITICAL risk. Run impact for `forwardSignal` and the `run-test-api.mjs` file, including tests. Report the Playwright web-server dependency and any API health flows before editing. Stop for confirmation only if the graph reports HIGH or CRITICAL risk.
- [ ] **Step 2: Write failing launcher tests** - [x] **Step 2: Write failing launcher tests**
Refactor-safe tests must verify behavior rather than depend on Docker availability: Refactor-safe tests must verify behavior rather than depend on Docker availability:
@@ -275,7 +275,7 @@ test('API 测试启动器不包含 Docker 子进程路径', async () => {
Use an OS-assigned loopback port for deterministic tests. The user-facing failure message may state the fixed required Redis endpoint even when the injected test port differs. Use an OS-assigned loopback port for deterministic tests. The user-facing failure message may state the fixed required Redis endpoint even when the injected test port differs.
- [ ] **Step 3: Run the launcher RED test** - [x] **Step 3: Run the launcher RED test**
Run: Run:
@@ -285,7 +285,7 @@ node --test server/tools/run-test-api.test.mjs
Expected: FAIL because `checkLocalRedis` and guarded direct-execution behavior do not yet exist. Expected: FAIL because `checkLocalRedis` and guarded direct-execution behavior do not yet exist.
- [ ] **Step 4: Refactor the launcher without infrastructure fallback** - [x] **Step 4: Refactor the launcher without infrastructure fallback**
Remove `execFileSync`, container naming, dynamic Redis port publication, ownership flags, and cleanup. Export and use: Remove `execFileSync`, container naming, dynamic Redis port publication, ownership flags, and cleanup. Export and use:
@@ -305,7 +305,7 @@ export async function runTestApi() {
Guard execution with the existing `process.argv[1]` and `fileURLToPath(import.meta.url)` pattern so tests can import the functions without starting API. Keep `REDIS_PORT: '6379'`; never derive or override it from a container. Guard execution with the existing `process.argv[1]` and `fileURLToPath(import.meta.url)` pattern so tests can import the functions without starting API. Keep `REDIS_PORT: '6379'`; never derive or override it from a container.
- [ ] **Step 5: Include the new test in the Server test command** - [x] **Step 5: Include the new test in the Server test command**
Change the Server test script to run both Vitest and the Node launcher test: Change the Server test script to run both Vitest and the Node launcher test:
@@ -313,7 +313,7 @@ Change the Server test script to run both Vitest and the Node launcher test:
"test": "vitest run && node --test tools/run-test-api.test.mjs" "test": "vitest run && node --test tools/run-test-api.test.mjs"
``` ```
- [ ] **Step 6: Run GREEN and real local Redis checks** - [x] **Step 6: Run GREEN and real local Redis checks**
Run: Run:
@@ -326,7 +326,7 @@ pnpm verify:e2e
Expected: unit tests pass, Redis prints `PONG`, and both Playwright viewports pass using the already-running local Redis. If Redis is unavailable, stop with the new explicit error; do not start or install it. Expected: unit tests pass, Redis prints `PONG`, and both Playwright viewports pass using the already-running local Redis. If Redis is unavailable, stop with the new explicit error; do not start or install it.
- [ ] **Step 7: Detect scope, commit, and push the launcher change** - [x] **Step 7: Detect scope, commit, and push the launcher change**
Run GitNexus `detect_changes(scope: "all")`, confirm the impact is limited to Playwright/API startup, then run: Run GitNexus `detect_changes(scope: "all")`, confirm the impact is limited to Playwright/API startup, then run:
@@ -364,7 +364,7 @@ git push origin "$(git branch --show-current)"
- Consumes: the confirmed `source-only-deployment-design.md` decision and actual Task 2/3 behavior. - Consumes: the confirmed `source-only-deployment-design.md` decision and actual Task 2/3 behavior.
- Produces: one unambiguous current deployment policy with intact historical evidence and a traceable GOV-009 record. - Produces: one unambiguous current deployment policy with intact historical evidence and a traceable GOV-009 record.
- [ ] **Step 1: Update current public docs and architecture** - [x] **Step 1: Update current public docs and architecture**
Make these statements exact and consistent: Make these statements exact and consistent:
@@ -377,7 +377,7 @@ Make these statements exact and consistent:
Remove the active Docker deployment link from `README.md` and `docs/index.md`. Remove the active Docker deployment link from `README.md` and `docs/index.md`.
- [ ] **Step 2: Update mandatory engineering and workflow rules** - [x] **Step 2: Update mandatory engineering and workflow rules**
In code standards, replace the dual-deployment variable rule with: In code standards, replace the dual-deployment variable rule with:
@@ -387,7 +387,7 @@ In code standards, replace the dual-deployment variable rule with:
Replace `开发、测试、Docker 和生产环境` with `开发、测试和生产环境`. In workflow rules, require env/systemd/Nginx synchronization and immutable release-directory rollback; remove Compose/image language. State that `verify:release` is the source-release gate and that unavailable external infrastructure is a failed prerequisite, never a container fallback. Replace `开发、测试、Docker 和生产环境` with `开发、测试和生产环境`. In workflow rules, require env/systemd/Nginx synchronization and immutable release-directory rollback; remove Compose/image language. State that `verify:release` is the source-release gate and that unavailable external infrastructure is a failed prerequisite, never a container fallback.
- [ ] **Step 3: Update GOV-009 current records without falsifying history** - [x] **Step 3: Update GOV-009 current records without falsifying history**
Revise PRD scope, R7, design configuration/deployment sections, automated test plan, current release notes, rollback, and architecture review to match actual source-only implementation. Add `source_only_deployment_implementation_plan` to `manifest.yaml`. Keep `status: reviewing`, `release: unreleased`, and the architecture confirmation completion gate. Revise PRD scope, R7, design configuration/deployment sections, automated test plan, current release notes, rollback, and architecture review to match actual source-only implementation. Add `source_only_deployment_implementation_plan` to `manifest.yaml`. Keep `status: reviewing`, `release: unreleased`, and the architecture confirmation completion gate.
@@ -395,11 +395,11 @@ In `qa/test-results.md`, preserve dated Docker validation as historical fact and
If GOV-006's manifest presents its Docker approach as current, add only a dated `superseded_by` pointer to the GOV-009 source-only decision; do not rewrite its original PRD/design/test evidence. If GOV-006's manifest presents its Docker approach as current, add only a dated `superseded_by` pointer to the GOV-009 source-only decision; do not rewrite its original PRD/design/test evidence.
- [ ] **Step 4: Add the unreleased changelog decision** - [x] **Step 4: Add the unreleased changelog decision**
At the top current section, record that the project removed Docker/Compose/image validation and now supports only Ubuntu source deployment with external infrastructure. Leave older entries about when Docker was added unchanged below it. At the top current section, record that the project removed Docker/Compose/image validation and now supports only Ubuntu source deployment with external infrastructure. Leave older entries about when Docker was added unchanged below it.
- [ ] **Step 5: Run active-document policy tests and scan for contradictions** - [x] **Step 5: Run active-document policy tests and scan for contradictions**
Run: Run:
@@ -410,7 +410,7 @@ rg -n '双部署|同时支持 Docker|Compose 只|Docker Compose 生产|verify:do
Expected: tests pass. Search results may remain only in explicitly dated historical results or supersession explanations; no current instruction may expose a Docker command or claim Docker support. Expected: tests pass. Search results may remain only in explicitly dated historical results or supersession explanations; no current instruction may expose a Docker command or claim Docker support.
- [ ] **Step 6: Detect scope, commit, and push documentation alignment** - [x] **Step 6: Detect scope, commit, and push documentation alignment**
Run GitNexus `detect_changes(scope: "all")`, then: Run GitNexus `detect_changes(scope: "all")`, then:
@@ -437,7 +437,7 @@ git push origin "$(git branch --show-current)"
- Consumes: the completed implementation, root verification scripts, GitNexus graph, and Review standards. - Consumes: the completed implementation, root verification scripts, GitNexus graph, and Review standards.
- Produces: final evidence for the deployment-policy adjustment while GOV-009 remains `reviewing/unreleased` pending user architecture confirmation. - Produces: final evidence for the deployment-policy adjustment while GOV-009 remains `reviewing/unreleased` pending user architecture confirmation.
- [ ] **Step 1: Run focused tests and full source release verification** - [x] **Step 1: Run focused tests and full source release verification**
Run without Docker: Run without Docker:
@@ -450,11 +450,11 @@ pnpm verify:release
Expected: all pass. `verify:release` must execute the full root verification, desktop/mobile Playwright checks, builds, and `verify:source`; it must not invoke Docker. Expected: all pass. `verify:release` must execute the full root verification, desktop/mobile Playwright checks, builds, and `verify:source`; it must not invoke Docker.
- [ ] **Step 2: Perform visual verification at both configured viewports** - [x] **Step 2: Perform visual verification at both configured viewports**
Use Playwright/browser screenshots at 1440x900 and 390x844. Confirm the page is nonblank, API health data renders, no content overlaps, and browser console/network contains no blocking error. Save screenshots under the existing ignored/output convention and record results, not binary artifacts, in `qa/test-results.md`. Use Playwright/browser screenshots at 1440x900 and 390x844. Confirm the page is nonblank, API health data renders, no content overlaps, and browser console/network contains no blocking error. Save screenshots under the existing ignored/output convention and record results, not binary artifacts, in `qa/test-results.md`.
- [ ] **Step 3: Refresh GitNexus and record before/after evidence** - [x] **Step 3: Refresh GitNexus and record before/after evidence**
Run: Run:
@@ -466,7 +466,7 @@ npm run governance:finish -- GOV-009 application-scaffold-baseline
Then use GitNexus `detect_changes(scope: "compare", base_ref: "cfcd7f13d9a6a35173d01bd8ca1458911190e70f")`. Record node/edge/flow counts, removed Docker verifier symbols, added source verifier symbols, and affected processes in the three GitNexus records. Do not commit `.gitnexus/`. Then use GitNexus `detect_changes(scope: "compare", base_ref: "cfcd7f13d9a6a35173d01bd8ca1458911190e70f")`. Record node/edge/flow counts, removed Docker verifier symbols, added source verifier symbols, and affected processes in the three GitNexus records. Do not commit `.gitnexus/`.
- [ ] **Step 4: Conduct code Review** - [x] **Step 4: Conduct code Review**
Review for functional regressions, release-gate bypasses, unsafe process spawning, Redis failure semantics, missing build artifacts, and test gaps. Resolve every blocking finding, rerun affected checks, and finish `review/code-review.md` with the exact line: Review for functional regressions, release-gate bypasses, unsafe process spawning, Redis failure semantics, missing build artifacts, and test gaps. Resolve every blocking finding, rerun affected checks, and finish `review/code-review.md` with the exact line:
@@ -474,7 +474,7 @@ Review for functional regressions, release-gate bypasses, unsafe process spawnin
最终结论:passed 最终结论:passed
``` ```
- [ ] **Step 5: Conduct development-standards Review** - [x] **Step 5: Conduct development-standards Review**
Review source-only policy consistency, Chinese comments for non-obvious logic, file responsibility, no secrets, MySQL 5.7 constraints, no production access, historical traceability, Chinese commits, Gitea push state, and the absence of a `v0.0.8` tag. Finish `review/standards-review.md` with the exact line: Review source-only policy consistency, Chinese comments for non-obvious logic, file responsibility, no secrets, MySQL 5.7 constraints, no production access, historical traceability, Chinese commits, Gitea push state, and the absence of a `v0.0.8` tag. Finish `review/standards-review.md` with the exact line:
@@ -482,7 +482,7 @@ Review source-only policy consistency, Chinese comments for non-obvious logic, f
最终结论:passed 最终结论:passed
``` ```
- [ ] **Step 6: Re-run final verification after Review edits** - [x] **Step 6: Re-run final verification after Review edits**
Run: Run:
@@ -494,7 +494,7 @@ git status --short
Expected: formatting and release verification pass. Only intended Review/evidence files remain uncommitted. Expected: formatting and release verification pass. Only intended Review/evidence files remain uncommitted.
- [ ] **Step 7: Detect final scope, commit, and push evidence** - [x] **Step 7: Detect final scope, commit, and push evidence**
Run GitNexus `detect_changes(scope: "all")`, verify no unexpected application flow changed, then: Run GitNexus `detect_changes(scope: "all")`, verify no unexpected application flow changed, then:
@@ -504,7 +504,7 @@ git commit -m "docs(GOV-009): 封存仅源码部署复核证据"
git push origin "$(git branch --show-current)" git push origin "$(git branch --show-current)"
``` ```
- [ ] **Step 8: Confirm remote consistency without releasing** - [x] **Step 8: Confirm remote consistency without releasing**
Run: Run:
+39 -1
View File
@@ -1,4 +1,4 @@
import { access, readFile } from 'node:fs/promises'; import { access, readFile, readdir } from 'node:fs/promises';
import path from 'node:path'; import path from 'node:path';
import process from 'node:process'; import process from 'node:process';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
@@ -32,6 +32,16 @@ const serverProcesses = Object.freeze([
['worker', 'Worker'], ['worker', 'Worker'],
]); ]);
const ignoredScanDirectories = new Set([
'.git',
'.gitnexus',
'.local',
'dist',
'node_modules',
'playwright-report',
'test-results',
]);
function resolveRoot(repositoryRoot) { function resolveRoot(repositoryRoot) {
return repositoryRoot instanceof URL return repositoryRoot instanceof URL
? fileURLToPath(repositoryRoot) ? fileURLToPath(repositoryRoot)
@@ -54,6 +64,34 @@ async function assertDockerArtifactsAbsent(repositoryRoot) {
throw new Error(`禁止存在 Docker 活动文件:${relativePath}`); throw new Error(`禁止存在 Docker 活动文件:${relativePath}`);
} }
} }
const pendingDirectories = [repositoryRoot];
while (pendingDirectories.length > 0) {
const currentDirectory = pendingDirectories.pop();
const entries = await readdir(currentDirectory, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory() && ignoredScanDirectories.has(entry.name)) continue;
const absolutePath = path.join(currentDirectory, entry.name);
if (entry.isDirectory()) {
pendingDirectories.push(absolutePath);
continue;
}
if (!entry.isFile()) continue;
const relativePath = path.relative(repositoryRoot, absolutePath).split(path.sep).join('/');
const segments = relativePath.split('/');
const fileName = segments.at(-1);
const isDockerfile = /^Dockerfile(?:\..+)?$/i.test(fileName);
const isDockerIgnore = fileName === '.dockerignore';
const isDockerCompose = /^docker-compose.*\.ya?ml$/i.test(fileName);
const isComposeYaml = segments.includes('compose') && /\.ya?ml$/i.test(fileName);
if (isDockerfile || isDockerIgnore || isDockerCompose || isComposeYaml) {
throw new Error(`禁止存在 Docker 活动文件:${relativePath}`);
}
}
}
} }
async function assertBuildOutputsPresent(repositoryRoot) { async function assertBuildOutputsPresent(repositoryRoot) {
@@ -81,6 +81,20 @@ test('任一 Docker 活动文件会阻断源码发布', async () => {
await assert.rejects(() => verifySourceDeployment(root), /禁止存在 Docker 活动文件.*Dockerfile/); await assert.rejects(() => verifySourceDeployment(root), /禁止存在 Docker 活动文件.*Dockerfile/);
}); });
test('未预先列举的 Dockerfile 和 Compose 文件仍会阻断源码发布', async () => {
for (const relativePath of ['ops/Dockerfile', 'deploy/compose/extra.yml']) {
const root = await createSourceReleaseFixture();
const absolutePath = path.join(root, relativePath);
await mkdir(path.dirname(absolutePath), { recursive: true });
await writeFile(absolutePath, 'services: {}\n');
await assert.rejects(
() => verifySourceDeployment(root),
new RegExp(`禁止存在 Docker 活动文件.*${relativePath.replace('/', '\\/')}`),
);
}
});
test('Server 构建入口缺失会阻断源码发布', async () => { test('Server 构建入口缺失会阻断源码发布', async () => {
const root = await createSourceReleaseFixture({ omit: 'server/dist/apps/worker/main.js' }); const root = await createSourceReleaseFixture({ omit: 'server/dist/apps/worker/main.js' });
@@ -169,6 +169,16 @@ test('部署文档要求本机外部 Redis 和唯一 Ubuntu 源码生产路径',
assert.match(source, /pnpm verify:release/); assert.match(source, /pnpm verify:release/);
assert.match(source, /\/opt\/a-share-analysis\/releases\//); assert.match(source, /\/opt\/a-share-analysis\/releases\//);
assert.match(source, /\/opt\/a-share-analysis\/current/); assert.match(source, /\/opt\/a-share-analysis\/current/);
for (const processName of ['api', 'scheduler', 'worker']) {
assert.match(
source,
new RegExp(
`install -m 0644 deploy/systemd/a-share-${processName}\\.service\\.example /etc/systemd/system/a-share-${processName}\\.service`,
),
);
assert.match(source, new RegExp(`systemctl restart a-share-${processName}\\.service`));
}
assert.doesNotMatch(source, /systemctl (?:restart|status) a-share-\w+\.service\.example/);
assert.match(source, /不负责安装/); assert.match(source, /不负责安装/);
for (const document of [local, source]) { for (const document of [local, source]) {
assert.match(document, /MySQL 5\.7\.42/); assert.match(document, /MySQL 5\.7\.42/);