Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ legacy composers. Missing/stale native wheels or unresolved text fonts fail star
Legacy compose functions remain for pixel-reference tools, not service recovery. References to fail-open
below describe the earlier migration and must not be reintroduced into production routes.

Release: `scripts/skia_release_gate.py` runs Linux cold/pure-service and strict warm parity; tag publishing
requires `.github/workflows/renderer-release.yml` and uses its validated wheel. The configured fixture
runner/paths are mandatory, never silently skipped. Private MySekai and uncaptured symbol/stamps are
Release: tag publishing calls `.github/workflows/skia-wheels.yml` on GitHub-hosted runners and uses
the same wheel that passed ABI, capability and native codec smoke checks. Full Linux cold/pure-service
and strict warm parity remain manual acceptance via `scripts/skia_release_gate.py` or the optional
`renderer-release.yml` workflow; only that manual workflow requires a configured fixture runner/paths. Private MySekai and uncaptured symbol/stamps are
user-excluded diagnostic cases, not release blockers. The private real file was explicitly authorized
for migration in this task and remains untracked; preserve it and its implementation.

Expand Down Expand Up @@ -430,8 +431,8 @@ collision between two *different* payloads (there is one payload per endpoint)
read the key material.

Wheels are built by `.github/workflows/skia-wheels.yml` (linux-x86_64 + macos-arm64 artifacts, not published to
an index). Docker requires exactly one matching wheel. Tag releases use the wheel produced by the full
`renderer-release.yml` validation job. Wheels are Python-version-specific: **upgrading Python means
an index). Docker requires exactly one matching wheel. Tag releases use the wheel produced and smoke-tested by the reusable
`skia-wheels.yml` workflow on GitHub-hosted runners. Wheels are Python-version-specific: **upgrading Python means
rebuilding wheels first**; an absent/incompatible wheel is a build failure.

**Traps that have already cost real debugging time:**
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ on:
- "v*"

jobs:
renderer-validation:
uses: ./.github/workflows/renderer-release.yml
renderer-wheels:
uses: ./.github/workflows/skia-wheels.yml
permissions:
contents: read

docker:
needs: renderer-validation
needs: renderer-wheels
runs-on: ubuntu-latest
permissions:
contents: read
Expand All @@ -22,12 +22,12 @@ jobs:
- name: Checkout code
uses: actions/checkout@v7

- name: Download the wheel that passed full renderer validation
- name: Download the wheel that passed native smoke validation
env:
GH_TOKEN: ${{ github.token }}
RELEASE_RUN_ID: ${{ github.run_id }}
run: |
gh run download "$RELEASE_RUN_ID" --name validated-native-wheel --dir docker/skia-wheels
gh run download "$RELEASE_RUN_ID" --name haruki-skia-renderer-linux-x86_64 --dir docker/skia-wheels
test "$(find docker/skia-wheels -maxdepth 1 -name '*.whl' | wc -l)" -eq 1

- name: Log in to GitHub Container Registry
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/renderer-release.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
name: Native Renderer Release Validation
name: Manual Full Renderer Validation

on:
workflow_call:
workflow_dispatch:

permissions:
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/skia-wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ name: Skia Renderer Wheels
# for the platforms we deploy/develop on and uploads them as workflow artifacts.

on:
workflow_call:
push:
branches:
- main
Expand All @@ -16,7 +17,7 @@ permissions:
contents: read

concurrency:
group: skia-wheels-${{ github.ref }}
group: skia-wheels-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
Expand Down
11 changes: 6 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ legacy composers. Missing/stale native wheels or unresolved text fonts fail star
Legacy compose functions remain for pixel-reference tools, not service recovery. References to fail-open
below describe the earlier migration and must not be reintroduced into production routes.

Release: `scripts/skia_release_gate.py` runs Linux cold/pure-service and strict warm parity; tag publishing
requires `.github/workflows/renderer-release.yml` and uses its validated wheel. The configured fixture
runner/paths are mandatory, never silently skipped. Private MySekai and uncaptured symbol/stamps are
Release: tag publishing calls `.github/workflows/skia-wheels.yml` on GitHub-hosted runners and uses
the same wheel that passed ABI, capability and native codec smoke checks. Full Linux cold/pure-service
and strict warm parity remain manual acceptance via `scripts/skia_release_gate.py` or the optional
`renderer-release.yml` workflow; only that manual workflow requires a configured fixture runner/paths. Private MySekai and uncaptured symbol/stamps are
user-excluded diagnostic cases, not release blockers. The private real file was explicitly authorized
for migration in this task and remains untracked; preserve it and its implementation.

Expand Down Expand Up @@ -430,8 +431,8 @@ collision between two *different* payloads (there is one payload per endpoint)
read the key material.

Wheels are built by `.github/workflows/skia-wheels.yml` (linux-x86_64 + macos-arm64 artifacts, not published to
an index). Docker requires exactly one matching wheel. Tag releases use the wheel produced by the full
`renderer-release.yml` validation job. Wheels are Python-version-specific: **upgrading Python means
an index). Docker requires exactly one matching wheel. Tag releases use the wheel produced and smoke-tested by the reusable
`skia-wheels.yml` workflow on GitHub-hosted runners. Wheels are Python-version-specific: **upgrading Python means
rebuilding wheels first**; an absent/incompatible wheel is a build failure.

**Traps that have already cost real debugging time:**
Expand Down
11 changes: 6 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ legacy composers. Missing/stale native wheels or unresolved text fonts fail star
Legacy compose functions remain for pixel-reference tools, not service recovery. References to fail-open
below describe the earlier migration and must not be reintroduced into production routes.

Release: `scripts/skia_release_gate.py` runs Linux cold/pure-service and strict warm parity; tag publishing
requires `.github/workflows/renderer-release.yml` and uses its validated wheel. The configured fixture
runner/paths are mandatory, never silently skipped. Private MySekai and uncaptured symbol/stamps are
Release: tag publishing calls `.github/workflows/skia-wheels.yml` on GitHub-hosted runners and uses
the same wheel that passed ABI, capability and native codec smoke checks. Full Linux cold/pure-service
and strict warm parity remain manual acceptance via `scripts/skia_release_gate.py` or the optional
`renderer-release.yml` workflow; only that manual workflow requires a configured fixture runner/paths. Private MySekai and uncaptured symbol/stamps are
user-excluded diagnostic cases, not release blockers. The private real file was explicitly authorized
for migration in this task and remains untracked; preserve it and its implementation.

Expand Down Expand Up @@ -430,8 +431,8 @@ collision between two *different* payloads (there is one payload per endpoint)
read the key material.

Wheels are built by `.github/workflows/skia-wheels.yml` (linux-x86_64 + macos-arm64 artifacts, not published to
an index). Docker requires exactly one matching wheel. Tag releases use the wheel produced by the full
`renderer-release.yml` validation job. Wheels are Python-version-specific: **upgrading Python means
an index). Docker requires exactly one matching wheel. Tag releases use the wheel produced and smoke-tested by the reusable
`skia-wheels.yml` workflow on GitHub-hosted runners. Wheels are Python-version-specific: **upgrading Python means
rebuilding wheels first**; an absent/incompatible wheel is a build failure.

**Traps that have already cost real debugging time:**
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Haruki Drawing API 是 Team Haruki 的 Project Sekai 图片生成服务。它接收 JSON 请求并输出 PNG/JPG,覆盖玩家资料、卡牌、活动、歌曲、谱面、招募、成绩和 MySekai 等页面。

当前版本为 `3.0.0-rc2`。生产绘图必须使用 Rust + Skia 后端;缺失或过旧的原生扩展会阻止构建/启动,渲染失败不再调用 Pillow。Pillow 仅作为开发对照环境的依赖,继续消费共享 widget 树来验证像素。
当前版本为 `3.1.0`。生产绘图必须使用 Rust + Skia 后端;缺失或过旧的原生扩展会阻止构建/启动,渲染失败不再调用 Pillow。Pillow 仅作为开发对照环境的依赖,继续消费共享 widget 树来验证像素。

## 运行要求

Expand Down Expand Up @@ -46,7 +46,7 @@ Compose 默认把 `./data` 挂载到容器内 `/pjskdata/Data`,并把 `configs

公开仓库中的 `src/sekai/mysekai/drawer.py` 只是接口占位文件。生产环境必须将真实实现 bind-mount 到同一路径;不要把 `drawer.real.py` 复制进镜像或提交到仓库。

Docker 构建前必须在 `docker/skia-wheels/` 放入且只放入一个匹配目标平台的 wheel。构建检查原生能力、实际编解码及生产依赖树,Pillow、Matplotlib、Pilmoji 均不得存在。标签发布使用完整渲染校验通过的同一个 wheel。
Docker 构建前必须在 `docker/skia-wheels/` 放入且只放入一个匹配目标平台的 wheel。构建检查原生能力、实际编解码及生产依赖树,Pillow、Matplotlib、Pilmoji 均不得存在。标签发布使用 GitHub 托管 runner 构建并通过 ABI、能力握手和原生编解码检查的同一个 wheel。

## 运维端点

Expand Down Expand Up @@ -80,7 +80,9 @@ uv run python -X gil=0 scripts/skia_release_gate.py --out-dir out/release-gate

该命令要求完整资产和 `out/parity-payloads/`,串联冷像素、禁止 Pillow 的绘图入口/完整服务、双后端热缓存检查。私有 MySekai 与尚未捕获的 symbol/stamps 按约定仅作诊断,不阻塞发布。

标签工作流依赖 `.github/workflows/renderer-release.yml`。配置仓库变量 `RENDER_VALIDATION_RUNNER`(可信 Linux x86_64 runner 标签)、`RENDER_ASSETS_DIR`、`RENDER_PAYLOAD_DIR`、`RENDER_CONFIG_PATH`。后三项必须是 runner 上 checkout 之外的绝对路径;配置文件中的字体等路径必须适用于该 runner。缺少配置会明确失败,不能跳过校验发布。校验通过后才上传 wheel,镜像作业下载该 wheel 构建并推送;请求样本与图片不上传为工作流诊断产物。
标签工作流复用 `.github/workflows/skia-wheels.yml`,在 GitHub 托管 runner 上构建并检查 wheel;镜像作业下载同一次工作流中通过检查的 Linux wheel,保留生产依赖无 Pillow 检查和实际编解码自检。发布不需要自建 runner 或私有素材。

完整素材对拍保留为手动验收:渲染或缓存逻辑变化时,在具备资产和样本的 Linux 环境运行上述命令。也可手动触发 `.github/workflows/renderer-release.yml`;只有该可选工作流需要配置 `RENDER_VALIDATION_RUNNER`、`RENDER_ASSETS_DIR`、`RENDER_PAYLOAD_DIR`、`RENDER_CONFIG_PATH`。路径必须位于 runner checkout 之外;请求样本与图片不上传为工作流诊断产物。

生产依赖安装使用 `uv sync --frozen --no-dev`,随后安装匹配的 wheel;默认开发组包含 `legacy-renderer`,因此本地对拍与测试仍可使用 Pillow。

Expand Down
17 changes: 9 additions & 8 deletions docs/pillow-retirement-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -547,17 +547,18 @@ fontTools 仍被 TMP 轮廓与备用度量使用,已从旧 Matplotlib 间接
悄悄切换字形算法。

Docker 要求恰好一个匹配平台的 wheel,调用 `load_native_renderer()` 检查当前能力,实际执行
codec smoke,并断言三个旧后端包不可导入。标签工作流依赖新增 `renderer-release.yml`;该作业
构建并安装 wheel,执行 `skia_release_gate.py`,通过后上传 wheel。镜像作业下载这个已验证的
wheel,构建成功后才推送。没有通过重新编译另一个 wheel 绕过已验证产物
codec smoke,并断言三个旧后端包不可导入。标签工作流复用 `skia-wheels.yml`,在 GitHub
托管 runner 构建并安装 wheel,完成 ABI、能力握手与 codec smoke 后上传。镜像作业下载
同一次运行中已验证的 Linux wheel,构建成功后才推送。

发布校验要求可信 Linux x86_64 runner 和 checkout 外的素材、payload、配置文件路径。
2026-09-09 调整:完整素材对拍保留为手动验收,不再要求每次标签发布重复执行。
仅可选的 `renderer-release.yml` 手动工作流要求可信 Linux x86_64 runner 和 checkout 外的素材、payload、配置文件路径。
仓库变量为 `RENDER_VALIDATION_RUNNER`、`RENDER_ASSETS_DIR`、`RENDER_PAYLOAD_DIR`、
`RENDER_CONFIG_PATH`;配置内字体路径应适用于该 runner。未设置这些输入会在 preflight 失败,
不会默许发布。工作流代码已接入;本任务未配置远端 runner,也未推送或部署
该手动验收不会静默跳过;普通标签发布不依赖这些变量或自建 runner。

`skia_release_gate.py` 只接受全新输出目录,依次运行严格冷对拍/无 Pillow 完整服务和严格双后端
热缓存。超时、缺报告或任何阶段失败都阻止发布。严格热缓存检查拒绝缺样本、无渲染路径、缺少
热缓存。超时、缺报告或任何阶段失败都令该手动验收失败。严格热缓存检查拒绝缺样本、无渲染路径、缺少
哈希、未知结果、重复结果及无法解释的漂移;只有 event_planner 的已知实时倒计时可在两个成功
冷渲染确实不同的前提下记录为 nondeterministic。返回 None 不能冒充时间变化。
严格冷门槛还核对实际 OpenAPI 绘图路由,防止新增路由通过“不登记 Case”逃过检查。
Expand Down Expand Up @@ -594,8 +595,8 @@ wheel,构建成功后才推送。没有通过重新编译另一个 wheel 绕
Pillow 恢复调用,生产依赖不含旧后端。开发用 Pillow composer 和惰性适配器仍保留供参考检查,
不构成生产渲染恢复能力。未采集的 symbol/stamps 按用户要求留待实际问题修复。

部署交接仍需在 GitHub 配置上述可信 runner 和外部素材变量后运行标签发布;本地完成不等同于
远端 CI 已运行。此前 stage30 压测中的 readiness 阈值触发仍是独立容量发现,未将该压测称为
标签发布使用 GitHub 托管 runner,不再需要上述自建 runner 和素材变量;完整素材验收应在
渲染或缓存逻辑变化时手动执行,本地完成不等同于远端 CI 已运行。此前 stage30 压测中的 readiness 阈值触发仍是独立容量发现,未将该压测称为
全通过,也未擅自调整阈值;所有图像响应成功及 Pillow 退役结果不依赖容量门槛放宽。


Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "Haruki-Drawing-API"
version = "3.0.5"
version = "3.1.0"
description = "FastAPI image rendering service for Project Sekai"
readme = "README.md"
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.