docs: describe Gitea delivery and controlled production updates
Development CI / check-and-package (push) Successful in 4m2s
Development CI / image (push) Failing after 17s
Development CI / deploy-development (push) Skipped

This commit is contained in:
eezd committed 2026-10-27 20:17:30 +08:00
1 parent 0eb1b4696a
commit a137285e35
7 files changed
+130 -24

No files matched your search

+6
View File
@@ -122,3 +122,9 @@ pnpm -C web build
按实际风险补充 Go race、Docker 构建和隔离浏览器验证。只用合成数据测试初始化、导入、恢复与删除,不对生产数据执行迁移或维护;生产部署、重启、数据库迁移执行及 commit/push 均需另行明确授权。
本文件为规范唯一来源,`CLAUDE.md`、`.cursor/rules/`、`.trae/rules/` 仅指回本文件;`.agents/skills/v3-*` 仅提供对应任务的操作入口,不复制第二套规则。
## CI/CD
Gitea 为主仓库和构建执行方;日常 main push 仅构建 Linux amd64,通过隔离验收后发布开发镜像并更新独立开发实例。正式发布只从 Gitea 手动入口选择已通过开发 CI 的提交,构建五平台包及 Linux 双架构镜像,同步同一版本至 GitHub/GHCR;GitHub 不执行项目构建,仅允许 Pages 平台部署预构建 Demo。生产仅手动选择完整发布版本并固定 GHCR 摘要,不能随开发推送更新。
runner 保持容量一,最多两个任务同时运行;当前宿主 Docker 执行器只接收受信代码。临时资源按任务标识清理,不全局 prune,不删除部署容器。CI 行为测试为 `python3 -m unittest discover -s scripts/tests -v`;项目检查与打包入口为 `sh scripts/ci-check.sh development|release`、`sh scripts/ci-package.sh development|release`,仅在 CI 工具链容器执行。正式版本、凭据、上传限制、部署目录与回退步骤见双语开发和部署文档。
+2
View File
@@ -39,6 +39,8 @@ RawMark is built for **single-user, self-hosted use**. It currently does not off
With Docker installed:
Choose an available version from [Releases](https://github.com/eezd/RawMark/releases) first. Previous releases were withdrawn; the command below is an installation example, not an availability guarantee.
```sh
docker run -d --name rawmark --restart unless-stopped \
-p 127.0.0.1:21000:8080 \
+2
View File
@@ -39,6 +39,8 @@ RawMark 面向**单用户、自托管**场景。目前不提供多人协作或
安装 Docker 后运行:
请先从 [Releases](https://github.com/eezd/RawMark/releases) 选择已发布版本。此前版本已撤回,下方命令仅为安装示例,不代表该版本已可拉取。
```sh
docker run -d --name rawmark --restart unless-stopped \
-p 127.0.0.1:21000:8080 \
+25 -2
View File
@@ -54,7 +54,7 @@ Do not point the quick-start container and a Compose instance at the same databa
## Prebuilt images and native programs
Release images are available as `ghcr.io/eezd/rawmark:0.1.1` for Linux amd64 and arm64. Replace `rawmark` in the Docker commands above with that image name to skip source builds. `latest` follows the latest stable release; use a version tag for controlled upgrades. The repository Compose file continues to support source builds.
Formal releases publish `ghcr.io/eezd/rawmark:<version>` for Linux amd64 and arm64. The earlier releases were withdrawn; `VERSION` alone does not mean a version is available. Check the Releases page and package visibility before using the example version below. Replace `rawmark` in the Docker commands above with that image name to skip source builds. `latest` follows the latest stable release; use a version tag for controlled upgrades. The repository Compose file continues to support source builds.
Native archives for Linux amd64/arm64, Windows amd64 and macOS Intel/Apple Silicon, plus `SHA256SUMS`, are available on the [Releases page](https://github.com/eezd/RawMark/releases). Download the archive matching your operating system and architecture and verify its checksum before extracting it:
@@ -146,7 +146,7 @@ In addition to application backups, stop the service before upgrades and back up
1. Check the target version's behavior and data compatibility; retain the current image and deployment configuration.
2. Stop the old service and back up application data and any external vault.
3. Update the source, rebuild, and start the new instance with the same mounts. Do not run old and new services concurrently.
3. Pull the selected prebuilt image digest and start with the same mounts. Source builds are optional contributor workflows, not managed production updates. Do not run old and new services concurrently.
4. Verify container health, sign-in, notes, and attachments. Roll back using the matching previous image and data backup.
The Bearer authentication upgrade requires the frontend and backend to be released together. Old Cookie-based pages require signing in again. Startup creates the resource-ticket table and indexes through the existing SQLite initialization flow; notes, passwords, settings, and existing API tokens are retained. Third-party clients must follow the [current JSON protocol](api.md).
@@ -160,3 +160,26 @@ The Bearer authentication upgrade requires the frontend and backend to be releas
- Resetting settings retains the account, resources, plugin storage, and local tabs.
- Logs live in `logs/`, with three rotated files retained, each up to 10 MiB. Settings can export logs and a version summary; this does not reconstruct historical logs.
- If the interface breaks after an update, first check that the frontend and backend come from the same build, then inspect browser network requests and application logs. See the [user guide](guide.md#plugins-and-interface-recovery) for plugin and appearance recovery entry points.
## Managed development and production
The checked-in `deploy/compose.image.yml` is an image-only template. Copy it to the instance directory as `compose.yml`, with a private mode-600 `.env` beside it. These instances do not depend on a source checkout. Set all required values explicitly:
```dotenv
RAWMARK_CHANNEL=development
RAWMARK_PROJECT=rawmark-dev
RAWMARK_BIND=127.0.0.1
RAWMARK_PORT=21001
RAWMARK_DATA_DIR=/docker/main/rawmark-dev/data
RAWMARK_IMAGE=git.134333.xyz/eezd/rawmark@sha256:<verified-digest>
```
For production, use channel `production`, project `rawmark`, port 21000, `/docker/main/rawmark/data`, and `ghcr.io/eezd/rawmark@sha256:<verified-digest>`. Preserve the current binding during migration. Create only a new development data directory with owner `65532:65532`; never recursively change existing production ownership or mount the repository example template as live storage.
Main-push CI automatically updates development. The **Deploy production manually** Gitea workflow only accepts a version whose Gitea and GitHub releases are formal and contain identical build/release receipts and matching tags. It fixes the GHCR digest, checks current mounts and competing writers, stops only this project's old service, and backs up the whole data directory plus configuration before replacement. Backups are private and include SQLite WAL files; old images and backups are retained.
The job records `deployment.pending.json` before replacement and `deployment.json` after health/frontend checks. If the candidate fails after accessing data, it stops the candidate and retains the backup and pending record for diagnosis; it does not automatically restore private data or run an older program against possibly migrated storage. Recovery requires explicit authorization and compatibility review. If backup fails before replacement, the untouched old service can be restarted safely. No global Docker cleanup is performed.
Preparing the production files does not restart the current service or execute a data migration. Its first transition from the old source-build Compose is an explicitly authorized production update. Do not run both Compose configurations simultaneously. Test upgrades and recovery with synthetic data before that transition.
A new development domain needs DNS, CDN custom-hostname activation and TLS, plus both the origin and regional ingress proxy configuration. Verify public access; a healthy loopback port or DNS record alone is insufficient. Keep resource-ticket paths redacted on every proxy.
+25 -2
View File
@@ -54,7 +54,7 @@ RAWMARK_DATA_DIR=/your/storage/rawmark/data \
## 预构建镜像与原生程序
正式镜像为 `ghcr.io/eezd/rawmark:0.1.1`,支持 Linux amd64 和 arm64。将上述 Docker 命令中的 `rawmark` 镜像名替换为它即可跳过源码构建。`latest` 跟随最新稳定版本;需要可控升级时请固定版本标签。仓库 Compose 文件继续支持源码构建。
正式发布后提供 `ghcr.io/eezd/rawmark:<版本>`,支持 Linux amd64 和 arm64。此前版本已撤回,`VERSION` 不代表版本已可下载;使用下方示例版本前先核对 Releases 和软件包公开状态。将上述 Docker 命令中的 `rawmark` 镜像名替换为它即可跳过源码构建。`latest` 跟随最新稳定版本;需要可控升级时请固定版本标签。仓库 Compose 文件继续支持源码构建。
[Releases 页面](https://github.com/eezd/RawMark/releases) 提供 Linux amd64/arm64、Windows amd64 和 macOS Intel/Apple Silicon 原生压缩包及 `SHA256SUMS`。下载对应系统和架构,先核对校验和再解压:
@@ -146,7 +146,7 @@ docker run -d --name rawmark --restart unless-stopped \
1. 确认目标版本的行为和数据兼容性,保留当前镜像及部署配置。
2. 停止旧服务,备份应用数据与外置笔记库。
3. 更新源码并重新构建,使用同一份挂载启动新实例;不要并行运行新旧服务。
3. 拉取选定的预构建镜像摘要,使用同一份挂载启动新实例;源码构建仅供贡献者选择,不用于托管生产更新。不要并行运行新旧服务。
4. 核对容器健康、登录、笔记与附件。回退时使用配套的旧镜像与数据备份。
Bearer 认证升级需要前后端一起发布。旧 Cookie 页面不能继续使用,需重新登录;启动程序会沿现有 SQLite 初始化流程创建资源票据表和索引,笔记、密码、设置与已有 API 令牌保留。第三方客户端的 JSON 解包方式须遵循[当前协议](api.zh-CN.md)。
@@ -160,3 +160,26 @@ Bearer 认证升级需要前后端一起发布。旧 Cookie 页面不能继续
- 重置设置保留账户、资源、插件存储和本机标签页。
- 日志位于 `logs/`,轮转保留三份,每份至多 10 MiB;可在设置中导出日志及版本摘要。该功能不补录历史日志。
- 更新后界面异常先核对前后端是否来自同一构建,再查看浏览器网络请求和应用日志;插件与外观恢复入口见[使用指南](guide.zh-CN.md#插件与界面恢复)。
## 开发与生产托管
仓库 `deploy/compose.image.yml` 是仅拉镜像的模板,复制至实例目录并命名为 `compose.yml`,旁边放权限 600 的私有 `.env`。实例不依赖源码工作目录,必须明确填写:
```dotenv
RAWMARK_CHANNEL=development
RAWMARK_PROJECT=rawmark-dev
RAWMARK_BIND=127.0.0.1
RAWMARK_PORT=21001
RAWMARK_DATA_DIR=/docker/main/rawmark-dev/data
RAWMARK_IMAGE=git.134333.xyz/eezd/rawmark@sha256:<已验证摘要>
```
生产使用 channel `production`、project `rawmark`、端口 21000、`/docker/main/rawmark/data` 和 `ghcr.io/eezd/rawmark@sha256:<已验证摘要>`;迁移时保留当前监听地址。仅为新的开发目录设置 `65532:65532` 所有者,不递归修改现有生产权限,不将仓库示例模板作为运行数据挂载。
main push CI 自动更新开发。Gitea **Deploy production manually** 工作流只接受两端均正式发布、构建/发布回执相同且标签一致的版本。它固定 GHCR 摘要,检查原挂载与并发写入实例,仅停止本项目旧服务,备份完整数据目录和配置后替换。备份为私有文件并包含 SQLite WAL;旧镜像和备份继续保留。
替换前记录 `deployment.pending.json`,健康和前端检查通过后记录 `deployment.json`。候选版本访问数据后失败则停止候选,保留备份和 pending 记录,不自动恢复私人数据,也不直接用旧程序打开可能迁移后的数据;恢复须明确授权并核对兼容性。备份在替换前失败时,旧数据未被新程序访问,可安全重新启动旧容器。不做全局 Docker 清理。
准备生产文件不等于重启当前服务或执行数据迁移。首次从旧源码构建 Compose 切换须明确授权生产更新,不同时运行两套 Compose。先用合成数据演练更新和恢复。
新开发域名须完成 DNS、CDN 自定义主机名及证书激活,并配置源站和国内入口。必须验证公网访问,不能仅凭回环端口健康或 DNS 有记录认定成功。所有代理都要对资源票据路径脱敏。
+35 -10
View File
@@ -108,22 +108,47 @@ Account language reuses the settings store's serial writes, server settings, and
Backend `internal/i18n/` preserves message IDs and parameters. Errors render at the HTTP boundary according to `Accept-Language`, retaining `errors.Is/As` and original diagnostics; logs keep their existing language. Existing index warnings are reanalyzed from source notes only when needed for translation, refreshing the entry's complete metadata and revision without changing the index format. Stale entries for deleted or moved notes are skipped; other read errors fail explicitly. Add both languages and interpolation parameters for new messages and run the corresponding dictionary-completeness, settings-sync, editor, and API tests.
## Development CI and release promotion
## Gitea CI/CD
Gitea is the development remote (`origin`); GitHub is the publication remote (`github`). Normal `git push origin main` runs `.gitea/workflows/ci.yml`. There is no automatic mirror to GitHub and no release or deployment step in development CI. The private runner executes one job at a time; keep its capacity at one (and never above two for this project).
Gitea is the primary repository and build executor (`origin`). GitHub (`github`) receives explicitly published versions; there is no continuous mirror. All project compilation and checks run in CI toolchain containers, without installing Node or Go on the deployment host. The runner has capacity one. Together with the allowed Pages platform deployment, no more than two jobs run at once.
The Linux runner checks lint, types, frontend tests, production/demo builds, Go vet and tests, filesystem permissions as an unprivileged user, and the Linux amd64 binary lifecycle with disposable data. It cross-compiles and vets Linux amd64/arm64, Windows amd64, and macOS arm64/amd64. Cross-compilation does **not** validate native behavior on Windows, macOS, or Linux arm64; the GitHub release workflow retains native tests. After these checks pass, a dependent Docker job verifies the same run’s Linux amd64 archive checksum and builds it with `deploy/Dockerfile.release`. It tests the non-root image, embedded frontend, health check, authentication, example notes, Unicode paths, edits, moves, recycle bin, persistence across restart and container replacement, and server-side logout. Tests use a fresh internal network and named volume, publish no host ports, and remove the temporary container, volume, network, and image on completion. No image is pushed to a registry.
### Development
Each platform archive is uploaded separately, with checksums and seven-day retention. CI rejects archives of 90,000,000 bytes or more before upload, leaving room below the Cloudflare 100 MB request limit. Do not combine all platforms into one artifact or commit build output. Git's HTTP limit applies to the complete push request too, not just individual files: keep pushes small and do not add large binaries to history. The self-hosted runner connects to Gitea over its configured private Docker network.
A main push runs `.gitea/workflows/ci.yml`: project checks, CI behavior tests, Linux amd64 packaging, and native lifecycle smoke tests. A dependent Docker job verifies that run's archive and checks the non-root image with synthetic data, including restart/replacement persistence and server-side logout. Only then does it publish `git.134333.xyz/eezd/rawmark:dev-<full-SHA>` and deploy its digest to the independent development instance. PRs only run checks and packaging; they receive no publication credentials and do not deploy. The host-Docker runner is for trusted code, not an untrusted-code sandbox; external PRs require maintainer review before execution.
To publish, select a reviewed commit that passed Gitea CI. Update the version and release notes first and validate the resulting commit on Gitea again. After explicit release approval, push that exact commit to GitHub and tag it there; do not publish the moving tip of a newer, unverified branch. GitHub release Actions currently remains manually disabled following the withdrawn releases; enable it only for an explicitly approved release. Production deployment is separately authorized.
Development uses `raw-dev.134333.xyz`, loopback port 21001, and `/docker/main/rawmark-dev/data`. Production retains its own data and port 21000. Before updating development, the deployment lock rechecks main so an old run cannot overwrite a newer deployment.
## Version releases and the public demo
`sh scripts/ci-check.sh development` and `sh scripts/ci-package.sh development` are the CI entry points. The `release` argument enables the Demo and five-platform packaging. Run `python3 -m unittest discover -s scripts/tests -v` for artifact and deployment behavior tests. Do not rebuild these artifacts on the deployment host.
`VERSION` is the release-version source. Docker builds and release binaries inject it into `--version`; Vite uses it for the settings display. The existing content-derived PWA build hash remains independent.
### Formal releases
For a release, update `VERSION`, `.github/release-notes.md`, and the paired installation examples; run the required checks, review and commit the changes, and pass Gitea CI. After release approval, push the selected `main` commit to the `github` remote, then push its matching `vX.Y.Z` tag to `github`. `.github/workflows/release.yml` is the only release workflow: ordinary branch pushes do not run it, and publishing its Release does not start another workflow. Release runs are globally serialized, with at most two native matrix jobs at once. Jobs check the project, test native Linux amd64/arm64, Windows amd64 and macOS amd64/arm64 binaries, package those same binaries into a multi-platform image, deploy the demo, and publish the Release and stable image tags. Rerun failed jobs in the existing run when necessary; do not move published tags or overwrite release assets.
Update `VERSION` and `.github/release-notes.md`, review the commit and pass main-push development CI. In Gitea's **Release** workflow, select main and enter the exact full commit SHA and matching `X.Y.Z` version. Publishing is an explicit action, not a tag-push side effect.
GitHub Pages must use the GitHub Actions publishing source, with the `github-pages` environment allowing release tags. The first GHCR package must be made public once in its package settings; verify anonymous pulls before announcing the release. Runtime production deployment is separate from publishing a version.
The same workflow performs checks and packages Linux amd64/arm64, Windows amd64, and Darwin amd64/arm64. Only Linux amd64 is runtime-tested on the available runner. Cross-builds and vet do not replace native Windows, macOS or ARM64 acceptance; the earlier Windows native test failure has not been reverified. Archives include licenses and `SHA256SUMS`. Linux binaries from these exact archives are packaged into one dual-architecture image and copied unchanged to Gitea Registry and GHCR.
Build the static editor preview with `pnpm -C web build:demo`. Output is `web/dist-demo/`, with `/RawMark/` as its base path. It uses the existing editor with temporary page-only edits; no private API, authentication, persistent workspace, or Service Worker is used. The workflow deploys it to https://eezd.github.io/RawMark/ in the same release run.
The workflow stages a Gitea draft and immutable `build.json` after artifact uploads. `image.json` records the tested candidate; `release.json` connects the build, image digest and Demo commit. Both final releases contain matching files. On interruption, rerun failed jobs or dispatch the same version/commit: existing receipts and original artifacts are reused. If an original artifact has expired, recovery fails explicitly rather than rebuilding different content. Conflicting tags or attachments fail; published versions are not overwritten. GitHub main advances by normal fast-forward only.
GitHub publication and Pages validation complete before the Gitea release becomes formal. Cross-service publication is not atomic: a failed run can leave a GitHub release or candidate images visible while Gitea remains a draft. Such a version is not production-eligible. Stable image tags advance only after artifact and public-image checks.
Each installation archive and Demo artifact must be smaller than 90,000,000 bytes. Upload platforms separately. Check the source Git pack and image blobs too: the Cloudflare 100 MB limit applies to requests, not only individual tracked files. The combined OCI file is local CI scratch space, never a release upload.
### Credentials and runner
Configure these repository Actions Secrets in Gitea:
| Secret | Required access |
| --- | --- |
| `RAWMARK_GITEA_TOKEN` | Gitea repository releases and Packages write access |
| `RAWMARK_GITHUB_TOKEN` | GitHub classic PAT with repository access, `workflow` for workflow-file changes, and `write:packages` |
Gitea rejects Secret names beginning with `GITEA_` or `GITHUB_`. Tokens do not belong in Git or job output. Job Docker credentials use temporary private directories. The manual production job uses a read-only Gitea job token and anonymous public GitHub/GHCR reads, not the publication PAT.
The first GHCR package is private by default. Make it public in its package settings and rerun the failed publication job; anonymous pulling is required before completion. The workflow does not claim a repository token automatically includes Packages access.
The existing runner uses `container.sweep: false` so deployed services survive job completion. Smoke resources carry a run/attempt label and the always-cleanup step removes only those resources. Deployment mounts are allowlisted to the two instance directories. Do not use global Docker prune or delete historical packages. Check cancellation cleanup as well as successful runs.
### Static Demo
`pnpm -C web build:demo` generates `web/dist-demo/` with base `/RawMark/`. It contains page-local example edits, no private API, authentication, persistent workspace or Service Worker. Gitea copies this prebuilt output to GitHub's `gh-pages` branch with `.nojekyll`; Pages uses that branch as its publishing source. GitHub still runs its platform Pages deployment, the only allowed GitHub job. It does not compile RawMark. The old GitHub project release workflow is removed.
Runtime production updates are a separate manual action; see [deployment](deployment.md#managed-development-and-production).
+35 -10
View File
@@ -108,22 +108,47 @@ python3 scripts/prepare-maple-font.py /path/to/MapleMonoNormalNL-NF-CN.zip
后端 `internal/i18n/` 保存消息 ID 与参数,错误在 HTTP 边界根据 `Accept-Language` 渲染,并保留 `errors.Is/As` 及原始诊断;日志遵循原有语言。既有索引警告只在需要时重新分析源笔记生成译文,同时更新该条目的完整元数据和版本,不改索引格式;已删除或移动的过期条目跳过,其他读取错误明确返回。新增文案同时补充中英字典与参数,运行字典完整性、设置同步、编辑器和 API 的相应测试。
## 开发 CI 与发布晋级
## Gitea CI/CD
Gitea 为开发远端(`origin`),GitHub 为发布远端(`github`)。日常 `git push origin main` 触发 `.gitea/workflows/ci.yml`;不配置 GitHub 自动镜像,开发 CI 不发布版本或部署服务。私有 runner 每次执行一个 job,保持容量为一(本项目最多不能超过两个)。
Gitea 是主仓库和构建执行方(`origin`);GitHub(`github`)只接收明确发布的版本,不持续镜像。项目检查和编译全部在 CI 工具链容器执行,不向部署宿主安装 Node 或 Go。runner 容量保持一;加上允许的 Pages 平台部署,同时执行的任务不超过两个。
Linux runner 执行 lint、类型检查、前端测试、正式与 Demo 构建、Go vet 和测试、普通用户文件权限测试,以及使用临时数据的 Linux amd64 程序生命周期测试;交叉编译并 vet Linux amd64/arm64、Windows amd64、macOS arm64/amd64。交叉编译**不代表**已验证 Windows、macOS 或 Linux arm64 的原生行为,GitHub 发布流程仍保留原生测试。 检查通过后,后续 Docker job 核对同一次运行的 Linux amd64 安装包校验和,使用 `deploy/Dockerfile.release` 构建镜像;验证非 root 运行、内嵌前端、健康检查、认证、示例笔记、中文路径、编辑、移动、回收站、重启与重建容器后的持久化和服务端注销。测试使用全新内部网络与命名卷,不发布宿主机端口,结束后移除临时容器、卷、网络及镜像,不推送镜像仓库。
### 开发流程
每个平台独立上传压缩包,附校验和,保留七天。上传前拒绝大于等于 90,000,000 字节的包,为 Cloudflare 的 100 MB 请求限制预留余量;不将五平台合成一个产物,不将构建输出提交到 Git。Git HTTP 限制同样针对整个 push 请求,而非只针对单个文件:保持小批量推送,不将大二进制文件写入历史。自托管 runner 通过已配置的 Docker 私有网络连接 Gitea。
main push 触发 `.gitea/workflows/ci.yml`,执行项目检查、CI 行为测试、Linux amd64 打包和原生生命周期冒烟。后续 Docker job 校验同一运行的包,使用合成数据验证非 root 镜像、重启及重建容器持久化、服务端注销;通过后才发布 `git.134333.xyz/eezd/rawmark:dev-<完整SHA>` 并按摘要更新独立开发实例。PR 仅检查和打包,不注入发布凭据、不部署。宿主 Docker runner 只运行受信代码,并非不可信代码沙箱;外部 PR 运行前须由维护者审查。
发布时选择已 review 且通过 Gitea CI 的 commit。先完成版本与发布说明调整,再在 Gitea 验证最终提交。获得明确发布授权后,将该提交同步至 GitHub,并为该提交推送版本标签,不能直接发布后来移动且未验证的分支顶端。此前版本撤销后,GitHub Release Actions 仍处于手动禁用状态;仅在获准发布时恢复。生产部署另行授权。
开发使用 `raw-dev.134333.xyz`、回环端口 21001 和 `/docker/main/rawmark-dev/data`;生产保留独立数据和 21000 端口。部署锁内再次核对 main,旧运行不能覆盖新部署。
## 版本发布与公开 Demo
CI 入口是 `sh scripts/ci-check.sh development` 和 `sh scripts/ci-package.sh development`;参数换为 `release` 才构建 Demo 和五平台程序。`python3 -m unittest discover -s scripts/tests -v` 验证产物与部署行为。部署宿主不重新编译这些产物。
`VERSION` 是发布版本的唯一来源。Docker 构建和发布二进制将其写入 `--version`,Vite 用于设置页面展示;既有 PWA 内容哈希保持独立。
### 正式发布
发布时更新 `VERSION`、`.github/release-notes.md` 和双语安装示例,完成检查、review、提交并通过 Gitea CI。明确获准发布后,将选定的 `main` 提交推送至 `github` 远端,再将该提交对应的 `vX.Y.Z` 标签推送至 `github`。`.github/workflows/release.yml` 是唯一发布工作流:普通分支推送不触发,创建 Release 也不启动第二个工作流。发布运行全局串行,原生平台矩阵最多同时执行两个 job。同一次运行完成检查、Linux amd64/arm64、Windows amd64 与 macOS amd64/arm64 原生程序测试、以相同程序封装双架构镜像、Demo 部署及 Release 和稳定镜像标签发布。失败时在原 run 中重跑失败 job,不移动已发布标签或覆盖发布附件。
先更新 `VERSION` 和 `.github/release-notes.md`,review 提交并通过 main push 开发 CI。进入 Gitea 的 **Release** 工作流,选择 main,输入完整提交 SHA 和对应的 `X.Y.Z` 版本。手动操作才发布,推标签不会自动启动发布。
GitHub Pages 必须选择 GitHub Actions 作为发布来源,`github-pages` 环境需要允许版本标签部署。首次 GHCR 镜像需在包设置中将可见性改为 Public,公告前验证匿名拉取。生产实例部署与版本发布是不同操作。
同一工作流完成检查并打包 Linux amd64/arm64、Windows amd64、Darwin amd64/arm64。现有 runner 只对 Linux amd64 执行原生运行验收;交叉构建和 vet 不代替 Windows、macOS、ARM64 原生验收,先前 Windows 原生测试失败尚未复验。程序包附许可证和 `SHA256SUMS`。双架构镜像复用这些包中的 Linux 二进制,原样同步到 Gitea Registry 和 GHCR。
执行 `pnpm -C web build:demo` 构建静态编辑器预览,输出至 `web/dist-demo/`,基础路径为 `/RawMark/`。复用现有编辑器,修改仅在当前页面保留;不使用私有 API、认证、持久化工作区或 Service Worker。工作流在同一次发布运行中部署至 https://eezd.github.io/RawMark/。
完成产物上传后,工作流准备 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#开发与生产托管)。