发布阶段 A

issue #97 的阶段 A 负责把发布链路的基础设施落地:tag 白名单、本地发布命令、.env.production、外置硬盘存储根、7131 下载服务、7132 docs 静态服务,以及统一的 release metadata。当前默认发布路径是在当前电脑本地执行命令,不依赖 GitLab CI、pipeline、runner 或 GitLab Release。

Tag 白名单

阶段 A 只接受以下 tag 前缀:
发布单元tag 格式版本来源
workflow-code-coreworkflow-code-core/vX.Y.Z根目录 package.json
workflow-code-cliworkflow-code-cli/vX.Y.Zserver/cli/package.json
serverserver/vX.Y.Zserver/package.json
server-webserver-web/vX.Y.Zserver/web/package.json
docsdocs/vX.Y.Ztag 本身
appapp/vX.Y.Zapp/package.json
除了 docs,其余发布单元都会通过本地 node scripts/release.mjs assert-tag --tag <unit>/vX.Y.Z 校验 tag 版本和对应 package.json.version 一致。不一致时本地发布命令直接失败。

生产 env 与外置硬盘

发布态必须使用 .env.production。真实生产配置默认只保存在当前电脑,由 loadProductionEnv()server/.env.production、仓库根目录 .env.productionserver/.env、仓库根目录 .env 的顺序补充缺失变量;不需要 GitLab File Variable 参与。shell 中已有的变量优先级最高,.env 只作为缺省回退,不能覆盖生产值。 生产态新增的关键变量:
变量用途
WORKFLOW_RELEASE_STORAGE_ROOT发布态外置硬盘根目录,阶段 A 的 server / download / release metadata 全都从这里派生。
WORKFLOW_SERVER_DATA_DIR可选;显式覆盖 server 数据目录。未设置时,生产态会自动落到 ${WORKFLOW_RELEASE_STORAGE_ROOT}/server-data
WORKFLOW_DOWNLOAD_ROOT可选;显式覆盖下载根目录。未设置时,默认使用 ${WORKFLOW_RELEASE_STORAGE_ROOT}/downloads/workflow-code
WORKFLOW_DOWNLOAD_PORT下载服务端口,默认 7131
WORKFLOW_DOWNLOAD_HOST下载服务监听地址,默认 127.0.0.1
WORKFLOW_DOCS_ROOT可选;显式覆盖 docs 静态目录。未设置时,默认使用 ${WORKFLOW_RELEASE_STORAGE_ROOT}/published/docs/stable
WORKFLOW_DOCS_PORTdocs 静态服务端口,默认 7132
WORKFLOW_DOCS_HOSTdocs 静态服务监听地址,默认 127.0.0.1
WORKFLOW_PUBLIC_BASE_URL发行版 workflow 管理端公开入口,默认 https://wf.yuhe.space
WORKFLOW_API_PUBLIC_BASE_URL发行版 API 公开入口,默认 https://wfapi.yuhe.space
WORKFLOW_HOOKS_PUBLIC_BASE_URL发行版 webhook 公开入口,默认 https://wfhooks.yuhe.space
WORKFLOW_EMBED_PUBLIC_BASE_URL发行版 embed 公开入口,默认 https://wfembed.yuhe.space
WORKFLOW_DOWNLOAD_PUBLIC_BASE_URL手动下载公开入口,默认 https://wfdownload.yuhe.space
WORKFLOW_UPDATES_PUBLIC_BASE_URL自动更新 feed 公开入口,默认 https://wfupdates.yuhe.space
WORKFLOW_DOCS_PUBLIC_BASE_URLdocs 站点公开入口,默认 https://wfdocs.yuhe.space
WORKFLOW_DOCS_API_PUBLIC_BASE_URLdocs 发行版 OpenAPI playground 的公开 API 根地址,默认 https://wfapi.yuhe.space;开发预览仍使用源码 OpenAPI 里的 http://localhost:7125
阶段 A 明确要求:WORKFLOW_RELEASE_STORAGE_ROOT 缺失、未挂载或不可写时,生产服务、下载服务和 docs 服务都必须启动失败,不能回退到仓库里的 server/.data 或系统盘临时目录。

Supabase 发布存储

开发态仍可用 pnpm run deploy:supabase 启动 docker/supabase/docker-compose.yml 和 Docker named volumes。发布态必须改用 pnpm run deploy:supabase:release 或完整 pnpm deploy,它会先加载 .env.production,确认 WORKFLOW_RELEASE_STORAGE_ROOT 可写,并创建以下 bind mount 目录:
  • ${WORKFLOW_RELEASE_STORAGE_ROOT}/supabase/postgres-data
  • ${WORKFLOW_RELEASE_STORAGE_ROOT}/supabase/db-config
  • ${WORKFLOW_RELEASE_STORAGE_ROOT}/supabase/storage
  • ${WORKFLOW_RELEASE_STORAGE_ROOT}/supabase/deno-cache
docker/supabase/docker-compose.release.yml 会把 Postgres、Storage、imgproxy 和 Edge Functions cache 的持久数据固定到这些外置硬盘目录;如果缺少 WORKFLOW_RELEASE_STORAGE_ROOT,Docker Compose 会拒绝解析发布 override,避免生产数据落回系统盘 named volume。

下载服务

7131 下载服务负责托管 app 安装包、latest-mac.yml 和 blockmap 等 updater 静态文件。当前有两种启动方式,静态根目录都指向 WORKFLOW_DOWNLOAD_ROOT,通常是 ${WORKFLOW_RELEASE_STORAGE_ROOT}/downloads/workflow-code 长期或可复现部署优先使用 docker/download
cp docker/download/.env.example docker/download/.env
docker compose --env-file docker/download/.env -f docker/download/docker-compose.yml up -d
docker compose --env-file docker/download/.env -f docker/download/docker-compose.yml ps
curl -i http://127.0.0.1:7131/health
该配置使用只读 bind mount 暴露下载目录,容器内由 Nginx 提供静态文件、HTTP Range 和 /health 探活;它不会生成、复制或修改发布产物。停止服务:
docker compose --env-file docker/download/.env -f docker/download/docker-compose.yml down
如果只是本机临时托管,也可以使用 server/scripts/download-server.mjs 启动 Node/Express 后台服务,默认监听 127.0.0.1:7131
pnpm -C server download:start
pnpm -C server download:status
pnpm -C server download:stop
它会把 WORKFLOW_DOWNLOAD_ROOT 当作静态目录,提供:
  • /health
  • /app/v<ver>/*
  • /app/stable/*
macOS 开机自启由 server/scripts/download-launchd.mjs 生成 launchd plist:
pnpm -C server download:launchd:print
pnpm -C server download:launchd:install

Docs 服务

docs/vX.Y.Z 发布会先把 Mintlify 导出的静态文件 stage 到 ${WORKFLOW_RELEASE_STORAGE_ROOT}/published/docs/v<ver>stable/,然后通过 server/scripts/docs-server.mjs 启动 127.0.0.1:7132 静态服务:
pnpm release:generate-docs
pnpm release:prepare-docs-source
cd .release/docs-source
mint export
发行版必须从 .release/docs-source 执行 Mintlify 导出。这个 staging 目录会复制仓库内 docs/,并把 docs/openapi/workflow-code.openapi.yaml 的默认 server 改成 WORKFLOW_DOCS_API_PUBLIC_BASE_URL。源码 docs/ 不会被改动,所以 pnpm doc:dev 仍默认连 http://localhost:7125。由于 OpenAPI paths 本身包含 /api/...,默认 https://wfapi.yuhe.space 会在 API 页面拼成 https://wfapi.yuhe.space/api/...

发行域名

当前发行默认域名使用 yuhe.space 子域,旧的 hookcode.win 域名只作为兼容入口继续指向同一组服务:
用途默认域名本机服务兼容域名
Workflow 管理端wf.yuhe.space127.0.0.1:7130workflow.hookcode.win
Workflow APIwfapi.yuhe.space127.0.0.1:7130api.hookcode.win
Webhookswfhooks.yuhe.space127.0.0.1:7130hooks.hookcode.win
Embedwfembed.yuhe.space127.0.0.1:7130embed.hookcode.win
App 手动下载wfdownload.yuhe.space127.0.0.1:7131download.hookcode.win
App 自动更新wfupdates.yuhe.space127.0.0.1:7131updates.hookcode.win
Docswfdocs.yuhe.space127.0.0.1:7132docs.hookcode.win
pnpm -C server docs:start
pnpm -C server docs:status
pnpm -C server docs:stop
它会把 WORKFLOW_DOCS_ROOT 当作静态目录,提供:
  • /health
  • /* 静态站点内容
macOS 开机自启由 server/scripts/docs-launchd.mjs 生成 launchd plist:
pnpm -C server docs:launchd:print
pnpm -C server docs:launchd:install

Release Metadata

阶段 A 引入统一的结构化发布元数据,默认 schema 版本为 1。核心字段包括:
  • unit
  • tag
  • version
  • packageName
  • releaseTarget
  • channel
  • publishedAt
  • commitSha
  • artifacts[]
生成脚本位于 scripts/release.mjs
node scripts/release.mjs assert-tag --tag server/v0.1.0
node scripts/release.mjs write-metadata \
  --tag server/v0.1.0 \
  --artifacts-dir server/dist \
  --output /tmp/server-release.json \
  --index /tmp/releases-index.json
write-metadata 会写单次发布的 release.jsonpublish-metadata 会把它持久化到 ${WORKFLOW_RELEASE_STORAGE_ROOT}/releases:单次 metadata 落在 <unit>/v<ver>/release.json<unit>/stable/release.json,聚合索引落在 releases/index.json。阶段 B 的 docs 版本页、下载页和发布记录页会继续消费这份聚合数据,并在 docs 发布前通过 scripts/generate-release-docs.mjs 写成静态 MDX 页面;同时 workflow server 的 /health 也会对外补充当前上线中的 server / server-web 部署版本与发布时间,避免 docs 把“稳定版”误写成“已部署版本”。

本地发布命令

阶段 A/C 的发布动作通过本地命令完成。常用路径如下:
  • workflow-code-core/vX.Y.Z:本地执行 pnpm build:core、必要测试和 npm publish,再用 release.mjs write-metadata / publish-metadata 写入发布记录。
  • workflow-code-cli/vX.Y.Z:本地执行 pnpm -C server/cli build、必要测试和 npm publish,再写入发布记录。
  • server/vX.Y.Z:本地构建 server 和 server-web,把产物 stage 到 ${WORKFLOW_RELEASE_STORAGE_ROOT}/published/server/v<ver>,再 promote-stable 并启动或重启 7130 服务。
  • docs/vX.Y.Z:本地执行 node scripts/generate-release-docs.mjspnpm release:prepare-docs-source 与 Mintlify 静态导出,把 .release/docs-source 的导出结果 stage 到 ${WORKFLOW_RELEASE_STORAGE_ROOT}/published/docs/v<ver>,再 promote-stable 并启动或重启 7132 docs 服务。
  • app/vX.Y.Z:本地执行 pnpm -C app build:mac,再运行 pnpm release:finalize-app --version X.Y.Z --source-dir app/release。finalize 会校验 latest-mac.yml、macOS zip 和同名 *.blockmap 齐全,且 latest-mac.ymlfiles[].url 必须同时引用 arm64x64 zip。通过后切换到 downloads/workflow-code/app/stable/;手动下载和自动更新默认使用同一组 stable zip feed。公开 HTTPS 入口的反代证书还必须覆盖 wfdownload.yuhe.space / wfupdates.yuhe.space
当前阶段只要求本地命令、目录落盘和 metadata 聚合完成;docs 展示层已经由阶段 B 生成静态页面,app 内自动更新 UI 继续放在阶段 D。