From 310dbba334b4f4c970b1c966d7aba54defc570eb Mon Sep 17 00:00:00 2001 From: leefer Date: Thu, 30 Jul 2026 20:07:52 +0800 Subject: [PATCH] docs(release): pin first Portainer candidate --- next/docs/final/release-candidate.md | 77 ++++++++++++++++++++++++++++ next/docs/portainer-preflight.md | 9 ++-- next/tests/test_deployment_assets.py | 18 +++++++ 3 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 next/docs/final/release-candidate.md diff --git a/next/docs/final/release-candidate.md b/next/docs/final/release-candidate.md new file mode 100644 index 0000000..3d2b228 --- /dev/null +++ b/next/docs/final/release-candidate.md @@ -0,0 +1,77 @@ +# 重建版发布候选清单 + +发布日期:2026-07-30 + +本清单把首次Docker/Portainer预检固定为可重复构建的候选版本。它不包含密码、令牌、 +Fernet密钥、数据库或私有Skill,也不授权停止旧系统或切换正式端口。 + +## 不可变版本 + +| 项目 | 固定值 | +|---|---| +| Git标签 | `xiaobai-next-rc-20260730-1` | +| Portainer引用 | `refs/tags/xiaobai-next-rc-20260730-1` | +| 镜像标签 | `xiaobai-review-next:rc-20260730-1` | +| Compose路径 | `next/compose.preflight.yaml` | +| 预检宿主端口 | `8876` | +| 容器端口 | `8765` | +| 正式旧端口 | `8765`,预检期间不得占用或修改 | + +标签创建后必须在Gitea中记录其完整提交号。Portainer关闭自动更新;重新部署前先确认标签仍指向 +同一提交,镜像不得使用`latest`或只有`preflight`的浮动标签。 + +## 当前真实旧库基线 + +此基线只对应2026-07-30本地冻结副本,用于首次预检核对迁移是否完整,不是未来正式切换时的 +永久固定计数。正式切换必须对停止写入后的最新一致旧库重新计算SHA-256并归档新报告。 + +| 项目 | 预期值 | +|---|---:| +| 旧库SHA-256 | `546c5af1a3010bbf0cf37e5d8b1a440fe6dfbfe80b471cddf430d4363a7b1a4a` | +| SQLite完整性 | `ok` | +| 外键异常 | 0 | +| 未映射旧表 | 0 | +| 未映射快照类型 | 0 | +| 用户 | 3 | +| 会员 | 3 | +| 自选 | 6 | +| 复盘笔记 | 3 | +| 问师消息 | 28 | +| 问天记录 | 31 | +| 选股归档 | 107 | +| 策略跟踪 | 16 | +| 市场摘要 | 22 | +| 市场洞察归档 | 47 | +| 模型 | 3 | +| 系统凭据 | 2 | + +首次迁移后运行中的数据库可能因后台任务、会话和新快照产生合法增量,因此后续重启验收不能要求 +数据库文件哈希保持不变。应核对迁移报告、记录下限、用户归属、SQLite完整性和真实页面行为。 + +## Portainer环境 + +必须人工填写:`APP_ENCRYPTION_KEY`、独立绝对路径`APP_STORAGE_PATH`。固定填写: + +```text +APP_IMAGE=xiaobai-review-next:rc-20260730-1 +APP_PUBLISH_PORT=8876 +APP_LOG_LEVEL=INFO +APP_TIMEZONE=Asia/Shanghai +``` + +`APP_STORAGE_PATH`不得指向旧容器数据目录;目录内旧库副本固定命名为`legacy-review.db`。 +密钥、数据库和私有Skill只进入NAS受控目录及加密备份,不进入Git或镜像层。 + +## 阻断判定 + +只有以下事实全部成立,发布候选才允许进入正式切换申请: + +1. `migration`退出码为0,报告完整性、外键、未映射项及业务计数已核对。 +2. `web`使用非root用户、只读根文件系统、清空Capabilities,健康状态稳定。 +3. `/app/data`是独立可写数据卷;重启后账号、私有记录、系统配置和共享归档不丢失。 +4. 真实管理员会员、会员非管理员和普通用户完成权限及私有数据抽验。 +5. 日间、夜间、1080P、4K和390px抽验完成;4K信息密度由用户确认。 +6. 备份包含数据库、环境密钥和私有Skill,并在另一个独立目录成功恢复。 +7. 固定镜像标签可在不重新构建的情况下完成一次回退启动。 + +任一项失败时保留日志和失败数据卷,不在失败目标库上手工修补;从最新一致旧库副本重新迁移。 diff --git a/next/docs/portainer-preflight.md b/next/docs/portainer-preflight.md index 792cbe6..8982330 100644 --- a/next/docs/portainer-preflight.md +++ b/next/docs/portainer-preflight.md @@ -30,11 +30,13 @@ legacy-review.db |---|---| | Name | `xiaobai-next-preflight` | | Repository URL | `http://192.168.200.36:3200/leefer/xiaobaifupan.git` | -| Repository reference | `refs/heads/main` | +| Repository reference | `refs/tags/xiaobai-next-rc-20260730-1` | | Compose path | `next/compose.preflight.yaml` | 私有 Gitea 仓库使用 Portainer 的仓库认证字段填写用户名和访问令牌,不把凭据拼进 URL。 -关闭自动更新;预检阶段只部署已经记录 Git 提交号的版本。 +关闭自动更新;预检阶段只部署发布候选清单登记的不可变Git标签。禁止改回`refs/heads/main`, +否则重新部署可能静默构建到不同提交。标签、镜像名、迁移基线和核验方式见 +[`final/release-candidate.md`](final/release-candidate.md)。 在 Stack 环境变量中填写: @@ -42,7 +44,7 @@ legacy-review.db |---|---| | `APP_ENCRYPTION_KEY` | 与旧数据库配套的原始 Fernet 密钥 | | `APP_STORAGE_PATH` | 第 1 步创建的 NAS 绝对路径 | -| `APP_IMAGE` | `xiaobai-review-next:preflight-` | +| `APP_IMAGE` | `xiaobai-review-next:rc-20260730-1` | | `APP_PUBLISH_PORT` | `8876` | | `APP_LOG_LEVEL` | `INFO` | | `APP_TIMEZONE` | `Asia/Shanghai` | @@ -81,6 +83,7 @@ http://192.168.200.11:8876/api/health 在 Portainer 的容器详情中确认: - 镜像标签与记录的 Git 提交一致; +- Gitea中的`xiaobai-next-rc-20260730-1`标签仍指向发布候选清单登记的提交; - 容器用户不是 root; - Root filesystem 为只读; - Capabilities 已全部移除; diff --git a/next/tests/test_deployment_assets.py b/next/tests/test_deployment_assets.py index fafb23c..93b2bed 100644 --- a/next/tests/test_deployment_assets.py +++ b/next/tests/test_deployment_assets.py @@ -26,3 +26,21 @@ def test_preflight_migrates_before_web_and_cannot_default_to_formal_storage() -> assert "condition: service_completed_successfully" in compose assert '"${APP_PUBLISH_PORT:-8876}:8765"' in compose assert "8765:8765" not in compose + + +def test_portainer_preflight_is_pinned_to_the_release_candidate() -> None: + guide = ROOT.joinpath("docs", "portainer-preflight.md").read_text(encoding="utf-8") + manifest = ROOT.joinpath("docs", "final", "release-candidate.md").read_text( + encoding="utf-8" + ) + + git_tag = "xiaobai-next-rc-20260730-1" + image_tag = "xiaobai-review-next:rc-20260730-1" + assert f"refs/tags/{git_tag}" in guide + assert f"refs/tags/{git_tag}" in manifest + assert image_tag in guide + assert image_tag in manifest + assert "| Repository reference | `refs/heads/main` |" not in guide + assert "546c5af1a3010bbf0cf37e5d8b1a440fe6dfbfe80b471cddf430d4363a7b1a4a" in manifest + for expected in ("用户 | 3", "自选 | 6", "选股归档 | 107", "策略跟踪 | 16"): + assert expected in manifest