Files
RawMark/docs/development.zh-CN.md
T
eezd 9f1ca095d0
Development CI / check-and-package (push) Successful in 3m30s
Development CI / image (push) Successful in 18s
Development CI / deploy-development (push) Successful in 12s
ci: relocate development deployments under /docker/dev
2026-10-28 00:03:42 +08:00

155 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 开发指南
[English](development.md) · **简体中文**
[返回项目首页](../README.zh-CN.md) · [API 与认证](api.zh-CN.md) · [项目约束](../AGENTS.md)
## 工具链
- Go 1.27.2 或更高版本,具体要求见 [go.mod](../go.mod)。
- Node.js 24+、pnpm **12.11.2**;前端依赖以 [pnpm-lock.yaml](../web/pnpm-lock.yaml) 为准。
- Docker 仅在验证容器构建或部署时需要,本地开发不强制使用。
前端采用 Vue 3、TypeScript、Vite、Router、Pinia、Element Plus、Axios、SCSS 与 UnoCSS;编辑器使用 CodeMirror 6。服务端使用 Go 标准库 HTTP、SQLite 与文件系统;Markdown 文件是真源,搜索索引可以重建。
## 启动本地开发
```sh
corepack enable
corepack prepare [email protected] --activate
scripts/dev.sh
```
脚本使用锁文件安装前端依赖,同时启动 Go 开发服务和 Vite 热更新。打开 http://localhost:5173,首次访问初始化密码;Ctrl+C 同时退出。
```sh
scripts/dev.sh --host # Allow LAN devices to access the frontend
scripts/dev.sh --help
```
`RAWMARK_DATA` 默认指向仓库的 `.dev-data/`,`API_PORT` 默认 8080,`WEB_PORT` 默认 5173。可通过环境变量覆盖,例如 `API_PORT=18080 WEB_PORT=15173 scripts/dev.sh`。不要将开发服务指向生产数据;初始化、恢复、导入和删除测试只使用合成数据。
开发模式从源码统计可选字体,日志版本显示为 `dev`,不读取旧 `web/dist`。生产模式使用嵌入的前端构建、字体清单和构建版本。
## 目录职责
```text
cmd/rawmark/ 服务入口
internal/
api/ HTTP 路由、授权、协议及 OpenAPI
auth/ 密码、会话与 API 凭据
store/ 应用 SQLite
vault/ 真源笔记、附件与文件操作
search/ 可重建的派生索引
web/
src/
pages/login/ 初始化与验证码登录
pages/workspace/ 笔记、目录、编辑器与图片管理
pages/settings/ 设置、账户与维护
common/apis/ 全部资源 API
common/components/ 跨模块公共组件
common/ 共享外观、Markdown、插件 SDK 与工具
http/ Axios、认证头、统一响应与错误
router/ 路由与守卫
pinia/ 实例及共享认证、权限、设置状态
types/auto/ 自动生成的导入声明
deploy/ 容器、代理配置及嵌入的公开示例笔记库
scripts/ 开发、资源生成及历史迁移脚本
```
页面私有组件、类型、状态、组合函数和资源跟随 `pages/<module>/`,确实跨模块复用的能力才进入 `common`;公共代码不得依赖页面私有实现。模块内优先相对导入,跨职责用 `@`(`web/src`)与 `@@`(`web/src/common`)。
全部资源接口位于 `common/apis/<resource>/index.ts` 和 `type.ts`。函数直接调用 `request<ResponseData>({ url, method, data, params })`,返回完整 `{ code, data, message }`,调用方显式读取 `.data`。原文/下载设置 `returnResponse: true` 保留 Axios 响应与响应头,204 返回 void,事件连接也归资源 API。不增加页面私有请求入口或多格式 fallback。
公共组件放在 `common/components/<PascalCaseName>/index.vue`。SFC 使用 Composition API 与 TypeScript,其他文件使用 kebab-case。自动声明应修改生成配置后重新构建,不手工编辑。更完整的执行约束以根 [AGENTS.md](../AGENTS.md) 为准,不另维护一套规范。
## 验证
```sh
pnpm -C web install --frozen-lockfile
pnpm -C web lint:check
pnpm -C web typecheck
pnpm -C web test
pnpm -C web build
go vet -tags dev ./...
go test ./internal/...
go test -tags dev ./web
```
前端使用 Vitest,测试随模块存放;Go 使用标准 `testing`。生产构建需先生成 `web/dist`,Go 再将其嵌入二进制。`-tags dev` 保留无需嵌入产物的开发和对应测试边界。
按改动风险补充 Go race、Docker 构建及真实浏览器验收。认证与请求变更应覆盖验证码成功/失败/过期、401 与 403、服务端注销以及旧会话响应隔离;编辑器与文件操作应核对原文保存、版本冲突、附件及移动/导入/导出。不用空数据或模拟成功掩盖失败。
## 主题、字体与插件
内置默认、白色与黑色主题,保留独立明暗模式和 CSS 片段。自定义主题位于数据目录 `themes/<id>/`,包含 `manifest.json` 与 `theme.css`;清单包含 `id`、`name`、`version`,ID 与目录一致,不能覆盖保留的 `default`、`white`、`black`。在外观设置刷新列表后选择。
默认字体资源已入库,常规构建无需下载或转换。重新生成时,在独立 Python 环境安装 `fonttools[woff]`,下载 [Maple v7.9 指定字体包](https://github.com/subframe7536/maple-font/releases/download/v7.9/MapleMonoNormalNL-NF-CN.zip),执行:
```sh
python3 scripts/prepare-maple-font.py /path/to/MapleMonoNormalNL-NF-CN.zip
```
脚本生成 WOFF2 和 `maple-font.css`,保留上游许可。代码块使用 highlight.js 的固定 Atom One Dark,不能在更新主题时顺带恢复旧高亮实现或配色。
插件 SDK 类型见 [common/plugin-api/index.d.ts](../web/src/common/plugin-api/index.d.ts),加载与生命周期实现位于 `common/plugins/`。插件仅在前端运行,安装者必须信任其代码;命名空间、停用及资源版本管理不等于权限沙箱。编辑器扩展故障时保留正文并停用当前页相关扩展。
## 提交改动
先阅读 [AGENTS.md](../AGENTS.md),聚焦实际需求,保留必要契约,不机械照搬模板示例。前端目录以 [vue3-starter 固定基线](https://github.com/eezd/vue3-starter/tree/fd41d62ce3f1c8338a478e0be3e2f31b53b7ea5e) 为参考,RawMark 的编辑器、主题和专用交互按业务保留。
提交前运行适用检查,说明实际验证与未验证范围。Commit 使用清晰英文,按功能拆分;不要提交真实笔记、账户数据、密钥或本地交接文件。
## 多语言
使用 Vue I18n Composition API,支持 `en` 与 `zh-CN`,默认英文。公共字典位于 `web/src/common/i18n/`,模块私有字典位于 `pages/<module>/locales/`,应用 `src/i18n.ts` 装配字典;不要用中文原文作为运行时查找键,也不要从公共模块导入页面字典。模板及命令、设置搜索等长期对象在使用时翻译;日期、数字和排序读取当前语言。
账户语言沿用 settings store 的串行写入、服务器设置与事件同步;登录页仅更新本机启动缓存,认证后读取服务器值。CodeMirror 使用 locale Compartment 更新控件,不替换文档、选区或撤销历史。用户正文、现有路径和插件提供的文字保持原样。
后端 `internal/i18n/` 保存消息 ID 与参数,错误在 HTTP 边界根据 `Accept-Language` 渲染,并保留 `errors.Is/As` 及原始诊断;日志遵循原有语言。既有索引警告只在需要时重新分析源笔记生成译文,同时更新该条目的完整元数据和版本,不改索引格式;已删除或移动的过期条目跳过,其他读取错误明确返回。新增文案同时补充中英字典与参数,运行字典完整性、设置同步、编辑器和 API 的相应测试。
## Gitea CI/CD
Gitea 是主仓库和构建执行方(`origin`);GitHub(`github`)只接收明确发布的版本,不持续镜像。项目检查和编译全部在 CI 工具链容器执行,不向部署宿主安装 Node 或 Go。runner 容量保持一;加上允许的 Pages 平台部署,同时执行的任务不超过两个。
### 开发流程
main push 触发 `.gitea/workflows/ci.yml`,执行项目检查、CI 行为测试、Linux amd64 打包和原生生命周期冒烟。后续 Docker job 校验同一运行的包,使用合成数据验证非 root 镜像、重启及重建容器持久化、服务端注销;通过后才发布 `git.134333.xyz/eezd/rawmark:dev-<完整SHA>` 并按摘要更新独立开发实例。PR 仅检查和打包,不注入发布凭据、不部署。宿主 Docker runner 只运行受信代码,并非不可信代码沙箱;外部 PR 运行前须由维护者审查。
开发使用 `raw-dev.134333.xyz`、回环端口 21001 和 `/docker/dev/rawmark/data`;生产保留 `/docker/main/rawmark` 下的独立数据和 21000 端口。部署锁内再次核对 main,旧运行不能覆盖新部署。
CI 入口是 `sh scripts/ci-check.sh development` 和 `sh scripts/ci-package.sh development`;参数换为 `release` 才构建 Demo 和五平台程序。`python3 -m unittest discover -s scripts/tests -v` 验证产物与部署行为。部署宿主不重新编译这些产物。
### 正式发布
先更新 `VERSION` 和 `.github/release-notes.md`,review 提交并通过 main push 开发 CI。进入 Gitea 的 **Release** 工作流,选择 main,输入完整提交 SHA 和对应的 `X.Y.Z` 版本。手动操作才发布,推标签不会自动启动发布。
同一工作流完成检查并打包 Linux amd64/arm64、Windows amd64、Darwin amd64/arm64。现有 runner 只对 Linux amd64 执行原生运行验收;交叉构建和 vet 不代替 Windows、macOS、ARM64 原生验收,先前 Windows 原生测试失败尚未复验。程序包附许可证和 `SHA256SUMS`。双架构镜像复用这些包中的 Linux 二进制,原样同步到 Gitea Registry 和 GHCR。
完成产物上传后,工作流准备 Gitea 草稿和不可变的 `build.json`;`image.json` 记录已验证候选镜像,`release.json` 关联构建、镜像摘要和 Demo 提交。两端最终发布文件一致。中断后重跑失败 job 或重新选择相同版本和提交,复用已有回执与原始产物;原产物过期则明确失败,不重新构建另一份内容。标签或附件冲突报错,已发布版本不覆盖。GitHub main 只正常快进。
GitHub 发布和 Pages 验证完成后,Gitea 才成为正式 Release。跨服务发布不具原子性:失败时可能已有 GitHub Release 或候选镜像,但 Gitea 仍为草稿,该版本不能进入生产。稳定镜像标签只在产物和公开镜像验证后更新。
每个安装包及 Demo 产物小于 90,000,000 字节,按平台分别上传。同时核对源码 Git pack 和镜像 blob:Cloudflare 100 MB 限制针对请求,而非只针对单个入库文件。合并 OCI 文件只留在 CI 临时空间,不作为发布附件上传。
### 凭据与 runner
在 Gitea 仓库 Actions Secrets 中配置:
| Secret | 所需权限 |
| --- | --- |
| `RAWMARK_GITEA_TOKEN` | Gitea 仓库 Release 与 Packages 写入 |
| `RAWMARK_GITHUB_TOKEN` | GitHub classic PAT,仓库权限、修改工作流文件所需的 `workflow`、`write:packages` |
Gitea 禁止 Secret 名以 `GITEA_` 或 `GITHUB_` 开头。令牌不进入 Git 或日志;job 的 Docker 凭据使用私有临时目录。手动生产 job 使用只读 Gitea job token 和匿名 GitHub/GHCR 读取,不获取发布 PAT。
首次创建的 GHCR 包默认私有,需要在包设置中设为公开后重跑失败的发布 job;匿名拉取通过后才能完成发布。不假定仓库凭据天然有 Packages 权限。
现有 runner 设置 `container.sweep: false`,避免任务结束后清除已部署服务。冒烟资源带 run/attempt 标签,由 always 清理步骤仅回收本次资源;部署挂载仅允许两个实例目录。不执行全局 Docker prune,不删除历史软件包;成功、失败和取消后均核对清理结果。
### 静态 Demo
`pnpm -C web build:demo` 生成 `web/dist-demo/`,基础路径为 `/RawMark/`。示例编辑仅在当前页面有效,不使用私有 API、身份、持久化工作区或 Service Worker。Gitea 将预构建产物和 `.nojekyll` 同步至 GitHub `gh-pages`,Pages 从此分支发布。GitHub 仍会执行平台 Pages 部署,这是唯一允许的 GitHub job,不编译 RawMark;旧 GitHub 项目发布工作流已移除。
生产更新为独立手动操作,见[部署文档](deployment.zh-CN.md#开发与生产托管)。