修复部署复检链路(05-31-deploy-unified-list-detail-reimbursement-public-check)

这个提交包含在:
2026-05-31 13:29:53 +08:00
父节点 efd6d285e5
当前提交 ec367ed3ae
共修改 9 个文件,包含 276 行新增和 10 行删除
+94 -1
查看文件
@@ -168,5 +168,98 @@ await expect(
page.getByRole("link", { name: "工作台", exact: true })
.or(page.getByRole("link", { name: "Portal", exact: true }))
.first(),
).toBeVisible();
).toBeVisible();
```
## Scenario: Trial Run Runtime Navigation Baseline
### 1. Scope / Trigger
- Trigger: changing sidebar grouping, workspace module visibility defaults,
`server/src/services/workspaceSettings.js`, or
`server/src/scripts/smoke-trialrun-runtime.js`.
- Purpose: keep deployment smoke aligned with the current small-team navigation
model instead of blocking deployment on retired menu assumptions.
### 2. Signatures
- Runtime smoke command inside deployment:
`docker compose exec -T server node src/scripts/smoke-trialrun-runtime.js`
- Source of truth for sidebar grouping:
`server/src/services/workspaceSettings.js`
- Runtime smoke assertion file:
`server/src/scripts/smoke-trialrun-runtime.js`
### 3. Contracts
- Runtime smoke must distinguish primary sidebar entries from collapsed
`更多` entries.
- Core day-to-day ledgers such as `报销`, `采购申请`, `合同台账`, `项目` and
`资产` are expected in `main` when the active role has access.
- Low-frequency entries such as `周报` and `公告` may be enabled but should be
checked in `backup` / `更多`, not forced into `main`.
- `待办` follows the current workspace and role-aware navigation baseline; do
not move it between `main`, `backup` or hidden as part of a deployment-smoke
fix unless a product task explicitly changes that rule.
- Management pages should remain represented by the single `管理` entry unless
a current task explicitly changes the management navigation model.
### 4. Validation & Error Matrix
- Core ledger missing from `main` -> runtime smoke fails with
`系统导航 main 缺少 <key> 入口`.
- `周报` or `公告` missing from `backup` while enabled -> runtime smoke fails
with `系统导航 backup 缺少 <key> 入口`.
- Management children are hidden behind `管理`, but smoke still expects
`workspace`, `people`, `featureFlags` or `auditLogs` in `admin` -> update the
smoke baseline; do not re-expand the sidebar for the smoke.
- `systemSettings` missing from `admin` -> runtime smoke fails because the
management entry is no longer reachable.
### 5. Good/Base/Bad Cases
- Good: `main` includes `portal`, `reimbursements`, `purchaseRequests`,
`contractsRegistry`, `projects` and `assets`; `backup` includes
`weeklyReports` and `announcements`; `admin` includes `systemSettings`.
- Base: `待办` is in `main` for a configured admin/owner trial account; smoke
accepts it there because it follows the current workspace baseline.
- Bad: smoke expects `weeklyReports` in `main` after the sidebar rule moved
low-frequency modules to `更多`.
- Bad: smoke expects every management child as a sidebar row after the product
rule consolidated management into one `管理` entry.
### 6. Tests Required
- Run `node --check server/src/scripts/smoke-trialrun-runtime.js` after editing
runtime smoke.
- Run `SYNC_ENV=0 bash deploy/trialrun/deploy.sh` for public deployment so the
remote runtime smoke validates the actual workspace and feature-flag state.
### 7. Wrong vs Correct
#### Wrong
```js
const REQUIRED_MAIN_NAV_KEYS = [
"portal",
"reimbursements",
"weeklyReports",
"announcements",
];
const REQUIRED_ADMIN_NAV_KEYS = ["workspace", "people", "featureFlags"];
```
#### Correct
```js
const REQUIRED_MAIN_NAV_KEYS = [
"portal",
"reimbursements",
"purchaseRequests",
"contractsRegistry",
"projects",
"assets",
];
const REQUIRED_BACKUP_NAV_KEYS = ["weeklyReports", "announcements"];
const REQUIRED_ADMIN_NAV_KEYS = ["systemSettings"];
```
@@ -0,0 +1,5 @@
{"file": ".trellis/spec/hyb-oa/index.md", "reason": "hyb-oa spec entry and task governance read order"}
{"file": ".trellis/spec/hyb-oa/project-governance.md", "reason": "Trellis task, approval, and commit rules for public deployment"}
{"file": ".trellis/spec/hyb-oa/deployment-trialrun.md", "reason": "public deployment command, env contract, smoke, and reporting requirements"}
{"file": ".trellis/spec/hyb-oa/docs-and-evidence.md", "reason": "evidence recording and documentation hygiene for public checks"}
{"file": ".trellis/spec/hyb-oa/security-privacy.md", "reason": "secret and sensitive-data handling during deployment evidence collection"}
@@ -0,0 +1,4 @@
{"file": ".trellis/spec/hyb-oa/index.md", "reason": "hyb-oa spec entry and task governance read order"}
{"file": ".trellis/spec/hyb-oa/project-governance.md", "reason": "Trellis task, approval, and commit rules for public deployment"}
{"file": ".trellis/spec/hyb-oa/deployment-trialrun.md", "reason": "public deployment command, env contract, smoke, and reporting requirements"}
{"file": ".trellis/spec/hyb-oa/security-privacy.md", "reason": "secret and sensitive-data handling during deployment evidence collection"}
@@ -0,0 +1,18 @@
# Implementation Notes
## Plan
1. Run local deployment preflight checks and confirm the reimbursement page source no longer contains the old top task-card entry markers.
2. Deploy with `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`, preserving the remote `.env`.
3. If deploy verification is blocked by deployment tooling or smoke assertions that are inconsistent with current specs, fix only those deployment/checking assets.
4. Rerun deployment until the remote env contract, `/ready`, external deps smoke, runtime smoke and public bundle scan pass.
5. Record evidence and residual limitations in this task.
## Implemented Adjustments
- `deploy/trialrun/deploy.sh`: added explicit `/dev/null` stdin redirection to remote `docker compose exec -T` checks so the SSH here-doc continues through `/ready`, external deps smoke, runtime smoke and service-status steps.
- `server/src/scripts/smoke-trialrun-runtime.js`: updated navigation smoke expectations to match the current sidebar model:
- core business ledgers in `main`;
- `周报` and `公告` in `backup` / `更多`;
- one `systemSettings` management entry instead of many sidebar management children.
- `.trellis/spec/hyb-oa/deployment-trialrun.md`: recorded the runtime navigation smoke baseline so future deploys do not regress to old menu assumptions.
@@ -0,0 +1,61 @@
# 部署统一列表详情改造并复检报销页
## Goal
将当前 `main` 的统一列表、详情页操作区和报销页改造部署到公网试运行环境,复检用户反馈的“报销页面顶部仍看到三个圆形按钮”是否已由最新版本修正。
## What I Already Know
- 用户已明确批准公网部署。
- 本地 `main` 最新提交为 `efd6d28`,工作区干净。
- 本地 `client/src/pages/ReimbursementsPage.tsx` 已不再保留旧的三张顶部快捷任务卡/圆形入口,当前顶部应为常规 `WorkspaceHeader`、`新建报销`、状态筛选和按权限显隐的 `台账工具`。
- 公网试运行环境属于准生产环境,部署必须保留远端 `.env`,使用 `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`。
## Requirements
- 部署当前 `main` 到 `https://oa.hyinda.com` 对应公网试运行环境。
- 不覆盖远端环境变量,不执行真实付款、真实业务写入或数据库重置。
- 部署后验证 `/ready` 可用。
- 部署后复检公网前端资源已更新到当前提交对应版本。
- 尽量复检报销页视觉状态:报销页顶部不应再出现旧的三个圆形按钮或三张任务入口。
- 如果本地缺少 `LIVE_SMOKE_*` 登录凭据,必须明确标注完整登录后页面检查的限制,并使用可用的公开 readiness/bundle 检查补足证据。
## Scope
- 公网试运行部署脚本、部署后 smoke、runtime smoke 基线和本任务证据记录。
- 允许修复阻塞部署验证完成的部署脚本或 smoke 基线问题。
- 不改变报销、列表、详情等业务页面实现。
## Acceptance Criteria
- [x] 本地部署前检查通过。
- [x] `SYNC_ENV=0 bash deploy/trialrun/deploy.sh` 成功完成。
- [x] 公网 `/ready` 返回健康状态。
- [x] 公网 bundle 检查通过或给出明确失败原因。
- [x] 报告部署提交 hash、ready 结果、smoke/复检结果,以及用户是否可以测试。
## Out Of Scope
- 不新增或修改业务代码。
- 不修改数据库结构和远端 `.env`。
- 不执行真实付款、银行、ERP 或其他外部写入。
- 不处理与本次部署无关的 UI 新问题。
## Test Plan
- `node deploy/trialrun/validate-env-contract.js`
- `cd client && npm run build`
- `node --check server/src/scripts/smoke-trialrun-runtime.js`
- `bash -n deploy/trialrun/deploy.sh`
- `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`
- `curl -fsS https://oa.hyinda.com/ready`
- `LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh`
- `node scripts/qa/check-public-bundle-copy.mjs https://oa.hyinda.com`
- Public reimbursement bundle scan for absence of old task-card markers.
## Technical Notes
- 部署规范:`.trellis/spec/hyb-oa/deployment-trialrun.md`
- 部署命令:`SYNC_ENV=0 bash deploy/trialrun/deploy.sh`
- 环境契约检查:`node deploy/trialrun/validate-env-contract.js`
- 公网目标:`https://oa.hyinda.com`
@@ -0,0 +1,49 @@
# Deployment Evidence
## Deployment
- Date: 2026-05-31
- Target: `https://oa.hyinda.com`
- Command: `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`
- App baseline before deployment: `efd6d28`
- Scope note: deployment included the current task's deployment-script/runtime-smoke fixes so the deploy command could complete the intended post-deploy checks.
## Findings During Deploy
- First full rerun showed that `docker compose exec -T` could consume the SSH here-doc and skip later deployment steps. Fixed by redirecting the affected remote `exec` commands from `/dev/null`.
- Runtime smoke still assumed the old sidebar model. Updated it to match the current mature Ant Design-style navigation baseline:
- core business ledgers in `main`;
- `周报` and `公告` in `backup` / `更多`;
- management represented by one `管理` entry.
## Verification
- `node deploy/trialrun/validate-env-contract.js`: PASS.
- `cd client && npm run build`: PASS before deployment.
- `node --check server/src/scripts/smoke-trialrun-runtime.js`: PASS.
- `bash -n deploy/trialrun/deploy.sh`: PASS.
- `git diff --check`: PASS.
- Final deploy script run:
- env contract: PASS;
- Prisma migration status: up to date;
- `/ready`: PASS;
- external deps smoke: PASS;
- runtime smoke: PASS;
- public bundle copy scan: `scanned=93 focused=2 findings=0`.
- Independent `/ready`: `{"ok":true}`.
- `LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh`: PASS with live browser/OCR checks skipped because local `LIVE_SMOKE_*` credentials and OCR sample were not configured.
## Reimbursement Page Check
- Public asset `https://oa.hyinda.com/assets/ReimbursementsPage-DgJ0EYZV.js` is available.
- The public reimbursement bundle contains the new header/action shape: `我的报销`, `新建报销`, status tabs and optional `台账工具`.
- The public reimbursement bundle does not contain old task-card markers:
- `reimbursements-task-card`
- `被驳回待补正`
- `草稿待完善`
- `审批中进度`
## Residual Limits
- Full logged-in live smoke and logged-in visual inspection were skipped because this local environment does not have complete `LIVE_SMOKE_*` credentials.
- Public readiness, deployment-in-container smoke, and public bundle checks passed, so the deployed app is available for user testing.
@@ -0,0 +1,30 @@
{
"id": "deploy-unified-list-detail-reimbursement-public-check",
"name": "deploy-unified-list-detail-reimbursement-public-check",
"title": "部署统一列表详情改造并复检报销页",
"description": "Deploy the unified business list/detail UI changes to the public trial run environment and verify the reimbursement page no longer serves the old top task-card buttons.",
"status": "in_progress",
"dev_type": null,
"scope": "deployment-trialrun",
"package": null,
"priority": "P2",
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-05-31",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [
"deploy/trialrun/deploy.sh",
"server/src/scripts/smoke-trialrun-runtime.js",
".trellis/spec/hyb-oa/deployment-trialrun.md"
],
"notes": "",
"meta": {}
}
+3 -3
查看文件
@@ -141,7 +141,7 @@ echo ">>> 4.4 执行 init"
"${compose_cmd[@]}" run --rm -T --entrypoint sh init -lc 'cd /app/server && npm run prisma:migrate:deploy && npm run seed:config && npm run seed:trialrun' </dev/null
echo ">>> 4.5 校验 Prisma migration status"
if ! "${compose_cmd[@]}" exec -T server sh -lc 'cd /app/server && npx prisma migrate status'; then
if ! "${compose_cmd[@]}" exec -T server sh -lc 'cd /app/server && npx prisma migrate status' </dev/null; then
echo "[ERROR] Prisma migration status 未通过,停止部署" >&2
"${compose_cmd[@]}" logs --tail=200 server || true
exit 1
@@ -164,14 +164,14 @@ if [[ "$ready_ok" != "1" ]]; then
fi
echo ">>> 4.7 执行 external deps smoke"
if ! "${compose_cmd[@]}" exec -T server node src/scripts/smoke-external-deps.js; then
if ! "${compose_cmd[@]}" exec -T server node src/scripts/smoke-external-deps.js </dev/null; then
echo "[ERROR] external deps smoke 失败,输出最近 server 日志" >&2
"${compose_cmd[@]}" logs --tail=200 server || true
exit 1
fi
echo ">>> 4.8 执行 runtime smoke"
if ! "${compose_cmd[@]}" exec -T server node src/scripts/smoke-trialrun-runtime.js; then
if ! "${compose_cmd[@]}" exec -T server node src/scripts/smoke-trialrun-runtime.js </dev/null; then
echo "[ERROR] runtime smoke 失败,输出最近 server 日志" >&2
"${compose_cmd[@]}" logs --tail=200 server || true
exit 1
+12 -6
查看文件
@@ -43,17 +43,19 @@ const REQUIRED_MAIN_NAV_KEYS = [
'leaveRequests',
'reimbursements',
'assets',
'weeklyReports',
'contractsRegistry',
'purchaseRequests',
'projects',
'todos',
];
const REQUIRED_BACKUP_NAV_KEYS = [
'weeklyReports',
'announcements',
];
const REQUIRED_ADMIN_NAV_KEYS = [
'workspace',
'people',
'systemSettings',
'featureFlags',
'auditLogs',
];
const REQUIRED_WORKSPACE_VISIBILITY = {
@@ -137,14 +139,18 @@ function assertNavigation(navigation) {
const mainKeys = new Set((navigation.sections.main ?? []).map((item) => item.key));
const adminKeys = new Set((navigation.sections.admin ?? []).map((item) => item.key));
const backupKeys = new Set((navigation.sections.backup ?? []).map((item) => item.key));
for (const key of REQUIRED_MAIN_NAV_KEYS) {
assertCondition(mainKeys.has(key), `系统导航 main 缺少 ${key} 入口`);
}
for (const key of REQUIRED_BACKUP_NAV_KEYS) {
assertCondition(backupKeys.has(key), `系统导航 backup 缺少 ${key} 入口`);
}
for (const key of REQUIRED_ADMIN_NAV_KEYS) {
assertCondition(adminKeys.has(key), `系统导航 admin 缺少 ${key} 入口`);
}
console.log(`[PASS] 导航构建成功,main=${navigation.sections.main.length} admin=${navigation.sections.admin.length}`);
console.log(`[PASS] 导航构建成功,main=${navigation.sections.main.length} backup=${navigation.sections.backup.length} admin=${navigation.sections.admin.length}`);
}
async function main() {