Enhancement/在应用层而非镜像构建层下载 Typst 构建产物 #60

Open
Passthem wants to merge 5 commits from enhancement/typst-binary into master
Owner

PR Type

Enhancement


Description

  • 将 Typst 二进制下载从 Docker 构建层移至应用层

  • ensure_artifact 返回 bool 表示是否执行了下载

  • 渲染结果为空时抛出 FileNotFoundError

  • 代码格式化:长日志字符串换行


Diagram Walkthrough

flowchart LR
  A["Dockerfile"] -- "移除 COPY artifacts" --> B["运行时容器"]
  C["ensure_artifact"] -- "应用启动时下载" --> B
  B -- "提供二进制" --> D["typst 插件"]

File Walkthrough

Relevant files
Enhancement
artifact.py
ensure_artifact 增加布尔返回值并格式化日志                                                       

konabot/common/artifact.py

  • ensure_artifact 返回类型改为 bool,指示是否进行了下载操作
  • 不匹配平台时返回 False,下载或重新下载后返回 True,已存在且哈希匹配返回 False
  • 长日志格式化为多行字符串
+41/-10 
Error handling
__init__.py
渲染结果为空时抛出异常                                                                                           

konabot/plugins/typst/init.py

  • 渲染结果为 None 时主动抛出 FileNotFoundError
+100/-5 
Configuration changes
Dockerfile
移除 Dockerfile 中的产物复制步骤                                                                     

Dockerfile

  • 移除从 artifacts 阶段复制二进制到 /usr/local/bin/COPY 指令
+0/-14   

### **PR Type** Enhancement ___ ### **Description** - 将 Typst 二进制下载从 Docker 构建层移至应用层 - `ensure_artifact` 返回 `bool` 表示是否执行了下载 - 渲染结果为空时抛出 `FileNotFoundError` - 代码格式化:长日志字符串换行 ___ ### Diagram Walkthrough ```mermaid flowchart LR A["Dockerfile"] -- "移除 COPY artifacts" --> B["运行时容器"] C["ensure_artifact"] -- "应用启动时下载" --> B B -- "提供二进制" --> D["typst 插件"] ``` <details> <summary><h3> File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table> <tr> <td> <details> <summary><strong>artifact.py</strong><dd><code>ensure_artifact 增加布尔返回值并格式化日志</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> konabot/common/artifact.py <ul><li><code>ensure_artifact</code> 返回类型改为 <code>bool</code>,指示是否进行了下载操作<br> <li> 不匹配平台时返回 <code>False</code>,下载或重新下载后返回 <code>True</code>,已存在且哈希匹配返回 <code>False</code><br> <li> 长日志格式化为多行字符串</ul> </details> </td> <td><a href="https://gitea.service.jazzwhom.top/mttu-developers/konabot/src/branch/enhancement/typst-binary/konabot/common/artifact.py">+41/-10</a>&nbsp; </td> </tr> </table></td></tr><tr><td><strong>Error handling</strong></td><td><table> <tr> <td> <details> <summary><strong>__init__.py</strong><dd><code>渲染结果为空时抛出异常</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> konabot/plugins/typst/__init__.py - 渲染结果为 `None` 时主动抛出 `FileNotFoundError` </details> </td> <td><a href="https://gitea.service.jazzwhom.top/mttu-developers/konabot/src/branch/enhancement/typst-binary/konabot/plugins/typst/__init__.py">+100/-5</a>&nbsp; </td> </tr> </table></td></tr><tr><td><strong>Configuration changes</strong></td><td><table> <tr> <td> <details> <summary><strong>Dockerfile</strong><dd><code>移除 Dockerfile 中的产物复制步骤</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> Dockerfile - 移除从 `artifacts` 阶段复制二进制到 `/usr/local/bin/` 的 `COPY` 指令 </details> </td> <td><a href="https://gitea.service.jazzwhom.top/mttu-developers/konabot/src/branch/enhancement/typst-binary/Dockerfile">+0/-14</a>&nbsp; &nbsp; </td> </tr> </table></td></tr></tr></tbody></table> </details> ___
Passthem added 2 commits 2026-03-18 17:30:51 +08:00
Collaborator

Failed to generate code suggestions for PR

Failed to generate code suggestions for PR
Author
Owner

/describe
/review
/improve

/describe /review /improve
Collaborator

PR Description updated to latest commit (9064b31fe9)

**[PR Description](https://gitea.service.jazzwhom.top/mttu-developers/konabot/pulls/60)** updated to latest commit (https://gitea.service.jazzwhom.top/mttu-developers/konabot/commit/9064b31fe9ab116df24f3fc2ba1944c175039a05)
Author
Owner

/review

/review
Collaborator

PR Reviewer Guide 🔍

(Review updated until commit 31d19b7ec0)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵
🧪 No relevant tests
🔒 No security concerns identified
 Recommended focus areas for review

非200继续处理

当 HTTP 状态码不是 200 时,代码仅记录了一条 warning,但仍然继续读取响应体并写入文件。这意味着错误页面(如 404 HTML)会被当作二进制文件保存到磁盘,后续的 sha256 校验虽然会发现不匹配并发出警告,但不会阻止写入损坏的文件。应该在非 200 时抛出异常或提前返回。

        logger.warning(
            f"已经下载了二进制,但是注意服务器没有返回 200! URL={artifact.url} TARGET={artifact.target} CODE={result.status}"
        )
    data = await result.read()
artifact.target.write_bytes(data)
哈希不匹配仅警告

download_artifact 中 sha256 校验失败时只记录了 warning,没有抛出异常或删除文件。这会导致一个哈希不匹配的二进制文件留在磁盘上并可能被后续流程使用,存在完整性风险。而 ensure_artifact 中的哈希不匹配逻辑会触发重新下载,但如果重新下载后哈希仍然不匹配,就会陷入静默失败的状态。

if m.hexdigest().lower() != artifact.sha256.lower():
    logger.warning(
        f"下载到的二进制的 sha256 与需求不同 TARGET={artifact.target} REQUESTED={artifact.sha256} ACTUAL={m.hexdigest()}"
    )
异常语义

使用 FileNotFoundError 来表示"渲染结果为空"在语义上不太准确,因为并不是文件找不到,而是渲染没有产出内容。考虑使用更贴切的异常类型(如 RuntimeError 或自定义异常),或者直接在此处处理 None 的情况而不是抛出异常再立即捕获。

if res is None:
    raise FileNotFoundError("没有渲染出来内容")
## PR Reviewer Guide 🔍 #### (Review updated until commit https://gitea.service.jazzwhom.top/mttu-developers/konabot/commit/31d19b7ec0140f674ed445f04554489b7328c35d) Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 2 🔵🔵⚪⚪⚪</td></tr> <tr><td>🧪&nbsp;<strong>No relevant tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> <tr><td>⚡&nbsp;<strong>Recommended focus areas for review</strong><br><br> <details><summary><a href='https://gitea.service.jazzwhom.top/mttu-developers/konabot/src/branch/enhancement/typst-binary/konabot/common/artifact.py#L104-L108'><strong>非200继续处理</strong></a> 当 HTTP 状态码不是 200 时,代码仅记录了一条 warning,但仍然继续读取响应体并写入文件。这意味着错误页面(如 404 HTML)会被当作二进制文件保存到磁盘,后续的 sha256 校验虽然会发现不匹配并发出警告,但不会阻止写入损坏的文件。应该在非 200 时抛出异常或提前返回。 </summary> ```python logger.warning( f"已经下载了二进制,但是注意服务器没有返回 200! URL={artifact.url} TARGET={artifact.target} CODE={result.status}" ) data = await result.read() artifact.target.write_bytes(data) ``` </details> <details><summary><a href='https://gitea.service.jazzwhom.top/mttu-developers/konabot/src/branch/enhancement/typst-binary/konabot/common/artifact.py#L114-L117'><strong>哈希不匹配仅警告</strong></a> `download_artifact` 中 sha256 校验失败时只记录了 warning,没有抛出异常或删除文件。这会导致一个哈希不匹配的二进制文件留在磁盘上并可能被后续流程使用,存在完整性风险。而 `ensure_artifact` 中的哈希不匹配逻辑会触发重新下载,但如果重新下载后哈希仍然不匹配,就会陷入静默失败的状态。 </summary> ```python if m.hexdigest().lower() != artifact.sha256.lower(): logger.warning( f"下载到的二进制的 sha256 与需求不同 TARGET={artifact.target} REQUESTED={artifact.sha256} ACTUAL={m.hexdigest()}" ) ``` </details> <details><summary><a href='https://gitea.service.jazzwhom.top/mttu-developers/konabot/src/branch/enhancement/typst-binary/konabot/plugins/typst/__init__.py#L188-L189'><strong>异常语义</strong></a> 使用 `FileNotFoundError` 来表示"渲染结果为空"在语义上不太准确,因为并不是文件找不到,而是渲染没有产出内容。考虑使用更贴切的异常类型(如 `RuntimeError` 或自定义异常),或者直接在此处处理 `None` 的情况而不是抛出异常再立即捕获。 </summary> ```python if res is None: raise FileNotFoundError("没有渲染出来内容") ``` </details> </td></tr> </table>
Author
Owner

/improve

/improve
Collaborator

PR Code Suggestions

Latest suggestions up to 31d19b7
Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
异常类型语义不匹配

使用 FileNotFoundError 来表示"渲染结果为空"在语义上不准确。FileNotFoundError
通常表示文件系统中找不到文件,而这里的情况是渲染没有产出内容,应该使用更合适的异常类型(如 RuntimeError),否则会与下方 except
FileNotFoundError 捕获的真正文件未找到错误混淆,导致错误信息误导用户。

konabot/plugins/typst/init.py [188-189]

 if res is None:
-    raise FileNotFoundError("没有渲染出来内容")
+    raise RuntimeError("没有渲染出来内容")
Suggestion importance[1-10]: 4

__

Why: The suggestion correctly identifies that FileNotFoundError is semantically misleading for a "no render output" scenario. However, looking at the code context, the raised FileNotFoundError is intentionally caught by the except FileNotFoundError block on line 190, which sends a user-facing error message. Changing to RuntimeError would cause it to bypass that handler entirely, breaking the intended error flow. The current code is a deliberate design choice to reuse the existing exception handler, so while the semantics are imprecise, the suggestion would break functionality without also updating the except clause.

Low

Previous suggestions

Suggestions up to commit 9064b31
CategorySuggestion                                                                                                                                    Impact
Possible issue
非200响应时应中止下载

当服务器返回非 200 状态码(如 404、500)时,代码仅记录了一条 warning 日志,但仍然继续读取响应体并写入文件。这会导致将错误页面内容(如 HTML
错误页)写入为二进制文件,后续虽然 sha256 校验会发现不匹配,但文件已经被错误内容覆盖。应在非 200 时抛出异常,避免写入无效数据。

konabot/common/artifact.py [102-108]

     result = await client.get(artifact.url)
     if result.status != 200:
-        logger.warning(
-            f"已经下载了二进制,但是注意服务器没有返回 200! URL={artifact.url} TARGET={artifact.target} CODE={result.status}"
+        raise RuntimeError(
+            f"下载失败,服务器返回非 200 状态码 URL={artifact.url} TARGET={artifact.target} CODE={result.status}"
         )
     data = await result.read()
 artifact.target.write_bytes(data)
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that on non-200 responses, the code logs a warning but still writes potentially invalid data (like an HTML error page) to the target file. Raising an exception to abort is a reasonable improvement. However, the original code intentionally changed this from the old behavior (which also just logged a warning) and the downstream sha256 check would catch mismatches, so the impact is moderate rather than critical.

Medium
General
使用更准确的异常类型

当渲染结果为 None 时,手动抛出 FileNotFoundError 在语义上不准确——这并不是文件未找到的问题,而是渲染未产生输出。使用 RuntimeError
或自定义异常更合适,否则会与外层 except FileNotFoundError 混淆,向用户展示"内部错误"而掩盖真实原因。

konabot/plugins/typst/init.py [180-181]

     if res is None:
-        raise FileNotFoundError("没有渲染出来内容")
+        raise RuntimeError("没有渲染出来内容")
Suggestion importance[1-10]: 4

__

Why: The suggestion has a valid point about semantic accuracy — FileNotFoundError is misleading when the issue is that rendering produced no output. However, looking at the code context, the raise FileNotFoundError is deliberately caught by the except FileNotFoundError block on line 182, which sends a user-facing "内部错误" message. Changing to RuntimeError would bypass that handler entirely, breaking the intended error handling flow. The suggestion would need to also update the except clause, so as-is it's partially incorrect in its proposed fix.

Low
## PR Code Suggestions ✨ <!-- 31d19b7 --> Latest suggestions up to 31d19b7 Explore these optional code suggestions: <table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=1>Possible issue</td> <td> <details><summary>异常类型语义不匹配</summary> ___ **使用 <code>FileNotFoundError</code> 来表示"渲染结果为空"在语义上不准确。<code>FileNotFoundError</code> <br>通常表示文件系统中找不到文件,而这里的情况是渲染没有产出内容,应该使用更合适的异常类型(如 <code>RuntimeError</code>),否则会与下方 <code>except </code><br><code>FileNotFoundError</code> 捕获的真正文件未找到错误混淆,导致错误信息误导用户。** [konabot/plugins/typst/__init__.py [188-189]](https://gitea.service.jazzwhom.top/mttu-developers/konabot/src/branch/enhancement/typst-binary/konabot/plugins/typst/__init__.py#L188-L189) ```diff if res is None: - raise FileNotFoundError("没有渲染出来内容") + raise RuntimeError("没有渲染出来内容") ``` <details><summary>Suggestion importance[1-10]: 4</summary> __ Why: The suggestion correctly identifies that `FileNotFoundError` is semantically misleading for a "no render output" scenario. However, looking at the code context, the raised `FileNotFoundError` is intentionally caught by the `except FileNotFoundError` block on line 190, which sends a user-facing error message. Changing to `RuntimeError` would cause it to bypass that handler entirely, breaking the intended error flow. The current code is a deliberate design choice to reuse the existing exception handler, so while the semantics are imprecise, the suggestion would break functionality without also updating the except clause. </details></details></td><td align=center>Low </td></tr></tr></tbody></table> ___ #### Previous suggestions <details><summary>Suggestions up to commit 9064b31</summary> <br><table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=1>Possible issue</td> <td> <details><summary>非200响应时应中止下载</summary> ___ **当服务器返回非 200 状态码(如 404、500)时,代码仅记录了一条 warning 日志,但仍然继续读取响应体并写入文件。这会导致将错误页面内容(如 HTML <br>错误页)写入为二进制文件,后续虽然 sha256 校验会发现不匹配,但文件已经被错误内容覆盖。应在非 200 时抛出异常,避免写入无效数据。** [konabot/common/artifact.py [102-108]](https://gitea.service.jazzwhom.top/mttu-developers/konabot/src/branch/enhancement/typst-binary/konabot/common/artifact.py#L102-L108) ```diff result = await client.get(artifact.url) if result.status != 200: - logger.warning( - f"已经下载了二进制,但是注意服务器没有返回 200! URL={artifact.url} TARGET={artifact.target} CODE={result.status}" + raise RuntimeError( + f"下载失败,服务器返回非 200 状态码 URL={artifact.url} TARGET={artifact.target} CODE={result.status}" ) data = await result.read() artifact.target.write_bytes(data) ``` <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly identifies that on non-200 responses, the code logs a warning but still writes potentially invalid data (like an HTML error page) to the target file. Raising an exception to abort is a reasonable improvement. However, the original code intentionally changed this from the old behavior (which also just logged a warning) and the downstream sha256 check would catch mismatches, so the impact is moderate rather than critical. </details></details></td><td align=center>Medium </td></tr><tr><td rowspan=1>General</td> <td> <details><summary>使用更准确的异常类型</summary> ___ **当渲染结果为 <code>None</code> 时,手动抛出 <code>FileNotFoundError</code> 在语义上不准确——这并不是文件未找到的问题,而是渲染未产生输出。使用 <code>RuntimeError</code> <br>或自定义异常更合适,否则会与外层 <code>except FileNotFoundError</code> 混淆,向用户展示"内部错误"而掩盖真实原因。** [konabot/plugins/typst/__init__.py [180-181]](https://gitea.service.jazzwhom.top/mttu-developers/konabot/src/branch/enhancement/typst-binary/konabot/plugins/typst/__init__.py#L180-L181) ```diff if res is None: - raise FileNotFoundError("没有渲染出来内容") + raise RuntimeError("没有渲染出来内容") ``` <details><summary>Suggestion importance[1-10]: 4</summary> __ Why: The suggestion has a valid point about semantic accuracy — `FileNotFoundError` is misleading when the issue is that rendering produced no output. However, looking at the code context, the `raise FileNotFoundError` is deliberately caught by the `except FileNotFoundError` block on line 182, which sends a user-facing "内部错误" message. Changing to `RuntimeError` would bypass that handler entirely, breaking the intended error handling flow. The suggestion would need to also update the except clause, so as-is it's partially incorrect in its proposed fix. </details></details></td><td align=center>Low </td></tr></tr></tbody></table> </details>
Passthem added 2 commits 2026-03-19 00:08:56 +08:00
Collaborator

Persistent review updated to latest commit 31d19b7ec0

**[Persistent review](https://gitea.service.jazzwhom.top/mttu-developers/konabot/pulls/60#issuecomment-353)** updated to latest commit https://gitea.service.jazzwhom.top/mttu-developers/konabot/commit/31d19b7ec0140f674ed445f04554489b7328c35d
Passthem added 1 commit 2026-03-21 20:13:36 +08:00
Collaborator

Failed to generate code suggestions for PR

Failed to generate code suggestions for PR
Checking for merge conflicts ...
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin enhancement/typst-binary:enhancement/typst-binary
git checkout enhancement/typst-binary
Sign in to join this conversation.
No description provided.