Skip to content

Commit

Permalink
feat: 修改 render.jinja 相关内容。
Browse files Browse the repository at this point in the history
  • Loading branch information
BigOrangeQWQ committed Sep 8, 2024
1 parent ab1a467 commit 4872ca2
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 81 deletions.
32 changes: 25 additions & 7 deletions docker/docker_plugin_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,10 @@ async def run(self):
# 创建插件测试项目
await self.create_poetry_project()
if self._create:
await self.show_package_info()
await self.show_plugin_dependencies()
await asyncio.gather(
self.show_package_info(),
self.show_plugin_dependencies(),
)
await self.run_poetry_project()

metadata = {}
Expand All @@ -278,8 +280,14 @@ async def run(self):
return self._run, self._lines_output

async def command(self, cmd: str, timeout: int = 300) -> tuple[bool, str, str]:
"""
执行命令
"""执行命令
Args:
cmd (str): 命令
timeout (int, optional): 超时限制. Defaults to 300.
Returns:
tuple[bool, str, str]: 命令执行返回值,标准输出,标准错误
"""
try:
proc = await create_subprocess_shell(
Expand All @@ -299,6 +307,7 @@ async def command(self, cmd: str, timeout: int = 300) -> tuple[bool, str, str]:
return not code, stdout.decode(), stderr.decode()

async def create_poetry_project(self):
"""创建 poetry 项目用来测试插件"""
if not self.path.exists():
self.path.mkdir()

Expand All @@ -321,6 +330,7 @@ async def create_poetry_project(self):
self._create = True

async def show_package_info(self) -> None:
"""获取插件的版本与插件信息"""
if self.path.exists():
code, stdout, stderr = await self.command(
f"poetry show {self.project_link}"
Expand All @@ -337,6 +347,7 @@ async def show_package_info(self) -> None:
self._log_output(f"插件 {self.project_link} 信息获取失败。")

async def run_poetry_project(self) -> None:
"""运行插件"""
if self.path.exists():
# 默认使用 fake 驱动
with open(self.path / ".env", "w", encoding="utf8") as f:
Expand Down Expand Up @@ -377,6 +388,7 @@ async def run_poetry_project(self) -> None:
self._log_output(f" {i}")

async def show_plugin_dependencies(self) -> None:
"""获取插件的依赖"""
if self.path.exists():
code, stdout, stderr = await self.command("poetry export --without-hashes")

Expand Down Expand Up @@ -414,9 +426,15 @@ def _get_plugin_module_name(self, require: str) -> str | None:


def main():
plugin = PluginTest(
os.environ.get("PLUGIN_INFO", ""), os.environ.get("PLUGIN_CONFIG", None)
)
"""
根据传入的环境变量 PLUGIN_INFO 和 PLUGIN_CONFIG 进行测试
PLUGIN_INFO 即为该插件的键
"""

plugin_info = os.environ.get("PLUGIN_INFO", "")
plugin_config = os.environ.get("PLUGIN_CONFIG", None)
plugin = PluginTest(plugin_info, plugin_config)

asyncio.run(plugin.run())

Expand Down
6 changes: 3 additions & 3 deletions src/plugins/publish/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,13 @@
from .utils import (
process_pr_and_issue_title,
comment_issue,
commit_and_push,
create_pull_request,
ensure_issue_content,
ensure_issue_test_button,
resolve_conflict_pull_requests,
run_shell_command,
should_skip_plugin_publish,
should_skip_plugin_test,
trigger_registry_update,
update_file,
)
from .validation import (
validate_adapter_info_from_issue,
Expand Down Expand Up @@ -206,6 +203,7 @@ async def handle_publish_plugin_check(
# 分支命名示例 publish/issue123
branch_name = f"{BRANCH_NAME_PREFIX}{issue_number}"

# 验证之后创建拉取请求和修改议题的标题
await process_pr_and_issue_title(
bot, repo_info, result, branch_name, issue_number, title, issue
)
Expand Down Expand Up @@ -251,6 +249,7 @@ async def handle_adapter_publish_check(
# 分支命名示例 publish/issue123
branch_name = f"{BRANCH_NAME_PREFIX}{issue_number}"

# 验证之后创建拉取请求和修改议题的标题
await process_pr_and_issue_title(
bot, repo_info, result, branch_name, issue_number, title, issue
)
Expand Down Expand Up @@ -292,6 +291,7 @@ async def handle_bot_publish_check(
# 分支命名示例 publish/issue123
branch_name = f"{BRANCH_NAME_PREFIX}{issue_number}"

# 验证之后创建拉取请求和修改议题的标题
await process_pr_and_issue_title(
bot, repo_info, result, branch_name, issue_number, title, issue
)
Expand Down
7 changes: 2 additions & 5 deletions src/plugins/publish/render.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
from pathlib import Path
from typing import TYPE_CHECKING

import jinja2

from src.utils.validation.models import PublishType

from .config import plugin_config
from .constants import LOC_NAME_MAP

if TYPE_CHECKING:
from src.utils.validation import ValidationDict
from src.utils.validation import ValidationDict


def tags_to_str(tags: list[dict[str, str]]) -> str:
Expand Down Expand Up @@ -50,7 +47,7 @@ def loc_to_name(loc: list[str | int]) -> str:
env.filters["loc_to_name"] = loc_to_name


async def render_comment(result: "ValidationDict", reuse: bool = False) -> str:
async def render_comment(result: ValidationDict, reuse: bool = False) -> str:
"""将验证结果转换为评论内容"""
title = f"{result.type}: {result.name}"

Expand Down
24 changes: 12 additions & 12 deletions src/plugins/publish/templates/render_data.md.jinja
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
{% macro render_data(key, value, skip_plugin_test) %}
{% if key == "homepage" %}
项目 <a href="{{ value }}">主页</a> 返回状态码 200。
项目 <a href="{{ value }}">主页</a> 返回状态码 200。
{%- elif key == "tags" and value %}
标签: {{ value|tags_to_str }}。
标签: {{ value|tags_to_str }}。
{%- elif key == "project_link" %}
项目 <a href="https://pypi.org/project/{{ value }}/">{{ value }}</a> 已发布至 PyPI。
项目 <a href="https://pypi.org/project/{{ value }}/">{{ value }}</a> 已发布至 PyPI。
{%- elif key == "type" %}
插件类型: {{ value }}。
插件类型: {{ value }}。
{%- elif key == "load" %}
插件加载测试: {{ value }}。
插件加载测试: {{ value }}。
{%- elif key == "supported_adapters" %}
插件支持的适配器: {{ value|supported_adapters_to_str }}。
插件支持的适配器: {{ value|supported_adapters_to_str }}。
{%- elif key == "action_url" %}
{% if skip_plugin_test %}
插件 <a href="{{ value }}">加载测试</a> 已跳过。
{%- else %}
插件 <a href="{{ value }}">加载测试</a> 通过。
{%- endif %}
{% if skip_plugin_test %}
插件 <a href="{{ value }}">加载测试</a> 已跳过。
{%- else %}
插件 <a href="{{ value }}">加载测试</a> 通过。
{%- endif %}
{% else %}
{{ key }}: {{ value }}。
{{ key }}: {{ value }}。
{%- endif %}
{% endmacro %}
103 changes: 60 additions & 43 deletions src/plugins/publish/templates/render_error.md.jinja
Original file line number Diff line number Diff line change
@@ -1,47 +1,64 @@
{% macro render_error(error) %}

{% set loc = error.loc %}
{% set type = error.type %}
{%- if loc|length == 3 and loc[0] == "tags" and type == "missing" %}
第 {{ loc[1] + 1 }} 个标签缺少 {{ loc[2] }} 字段。<dt>请确保标签字段完整。</dt>
{% elif loc|length == 3 and loc[0] == "tags" and type == "string_too_long" %}
第 {{ loc[1] + 1 }} 个标签名称过长<dt>请确保标签名称不超过 10 个字符。</dt>
{%- elif loc|length == 3 and loc[0] == "tags" and type == "color_error" %}
第 {{ loc[1] + 1 }} 个标签颜色错误<dt>请确保标签颜色符合十六进制颜色码规则。</dt>
{%- elif loc|length == 2 and loc[0] == "tags" and type == "model_type" %}
第 {{ loc[1] + 1 }} 个标签格式错误。<dt>请确保标签为字典。</dt>
{%- elif type == "homepage" and error.ctx.status_code != -1 %}
项目 <a href="{{ error.input }}">主页</a> 返回状态码 {{ error.ctx.status_code }}。<dt>请确保你的项目主页可访问。</dt>
{%- elif type == "homepage" and error.ctx.status_code == -1 %}
项目 <a href="{{ error.input }}">主页</a> 访问出错。<details><summary>错误信息</summary>{{ error.ctx.msg }}</details>
{%- elif type == "project_link.not_found" %}
项目 <a href="https://pypi.org/project/{{ error.input }}/">{{ error.input }}</a> 未发布至 PyPI。<dt>请将你的项目发布至 PyPI。</dt>
{%- elif type == "project_link.name" %}
PyPI 项目名 {{ error.input }} 不符合规范。<dt>请确保项目名正确。</dt>
{%- elif type == "module_name" %}
包名 {{ error.input }} 不符合规范。<dt>请确保包名正确。</dt>
{%- elif type == "duplication" %}
{{ error.msg }}<dt>请确保没有重复发布。</dt>
{%- elif type == "plugin_test" %}
插件加载测试未通过。<details><summary>测试输出</summary>{{ error.ctx.output }}</details>
{%- elif type == "metadata" %}
无法获取到插件元数据。<dt>{{ "请填写插件元数据" if error.ctx.plugin_test_result else "请确保插件正常加载" }}。</dt>
{%- elif type == "plugin.type" %}
插件类型 {{ error.input }} 不符合规范。<dt>请确保插件类型正确,当前仅支持 application 与 library。</dt>
{%- elif type == "supported_adapters.missing" %}
适配器 {{ ', '.join(error.ctx.missing_adapters) }} 不存在。<dt>请确保适配器模块名称正确。</dt>
{%- elif type == "missing" %}
{{ loc|loc_to_name }}: 无法匹配到数据。<dt>请确保填写该项目。</dt>
{%- elif type == "model_type" %}
{{ loc|loc_to_name }}: 格式错误。<dt>请确保其为字典。</dt>
{%- elif type == "list_type" %}
{{ loc|loc_to_name }}: 格式错误。<dt>请确保其为列表。</dt>
{%- elif type == "set_type" %}
{{ loc|loc_to_name }}: 格式错误。<dt>请确保其为集合。</dt>
{%- elif type == "json_type" %}
{{ loc|loc_to_name }}: 解码失败。<dt>请确保其为 JSON 格式。</dt>
{%- elif type == "string_too_long" %}
{{ loc|loc_to_name }}: 字符过多。<dt>请确保其不超过 {{ error.ctx.max_length }} 个字符。</dt>
{%- else %}
{{ error.loc|loc_to_name }}: {{ error.msg }}
{%- endif %}
{% set ctx = error.ctx %}
{% set name = loc|loc_to_name %}
{% set input = error.input %}

{% macro render_tag_error(index, message, dt_message) %}
第 {{ index + 1 }} 个标签{{ message }}。<dt>请确保标签{{ dt_message }}。</dt>
{% endmacro %}

{% macro detail_message(summary, detail) %}
<details><summary>{{ summary }}</summary>{{ detail }}</details>
{% endmacro %}

{% if loc|length == 3 and loc[0] == "tags" %}
{% if type == "missing" %}
{{ render_tag_error(loc[1], "缺少第" ~ loc[2] ~ " 字段", "请确保标签字段完整。") }}
{% elif type == "string_too_long" %}
{{ render_tag_error(loc[1], "名称过长", "请确保标签名称不超过 10 个字符。") }}
{% elif type == "color_error" %}
{{ render_tag_error(loc[1], "颜色错误", "请确保标签颜色符合十六进制颜色码规则。") }}
{% endif %}
{% elif loc|length == 2 and loc[0] == "tags" and type == "model_type" %}
{{ render_tag_error(loc[1], "格式错误", "请确保标签为字典。") }}
{% elif type == "homepage" %}
{% if ctx.status_code != -1 %}
项目 <a href="{{ input }}">主页</a> 返回状态码 {{ ctx.status_code }}。<dt>请确保你的项目主页可访问。</dt>
{% else %}
项目 <a href="{{ input }}">主页</a> 访问出错。{{ detail_message("错误信息", ctx.error) }}
{% endif %}
{% elif type == "project_link.not_found" %}
项目 <a href="https://pypi.org/project/{{ input }}/">{{ input }}</a> 未发布至 PyPI。<dt>请将你的项目发布至 PyPI。</dt>
{% elif type == "project_link.name" %}
PyPI 项目名 {{ input }} 不符合规范。<dt>请确保项目名正确。</dt>
{% elif type == "module_name" %}
包名 {{ input }} 不符合规范。<dt>请确保包名正确。</dt>
{% elif type == "duplication" %}
{{ error.msg }}<dt>请确保没有重复发布。</dt>
{% elif type == "plugin.test" %}
插件加载测试未通过。{{ detail_message("测试输出", ctx.output) }}
{% elif type == "metadata" %}
无法获取到插件元数据。<dt>{{ "请填写插件元数据" if ctx.plugin_test_result else "请确保插件正常加载" }}。</dt>
{% elif type == "plugin.type" %}
插件类型 {{ input }} 不符合规范。<dt>请确保插件类型正确,当前仅支持 application 与 library。</dt>
{% elif type == "supported_adapters.missing" %}
适配器 {{ ', '.join(ctx.missing_adapters) }} 不存在。<dt>请确保适配器模块名称正确。</dt>
{% elif type == "missing" %}
{{ name }}: 无法匹配到数据。<dt>请确保填写该项目。</dt>
{% elif type == "model_type" %}
{{ name }}: 格式错误。<dt>请确保其为字典。</dt>
{% elif type == "list_type" %}
{{ name }}: 格式错误。<dt>请确保其为列表。</dt>
{% elif type == "set_type" %}
{{ name }}: 格式错误。<dt>请确保其为集合。</dt>
{% elif type == "json_type" %}
{{ name }}: 解码失败。<dt>请确保其为 JSON 格式。</dt>
{% elif type == "string_too_long" %}
{{ name }}: 字符过多。<dt>请确保其不超过 {{ ctx.max_length }} 个字符。</dt>
{% else %}
{{ error.name }}: {{ error.msg }}
{% endif %}
{% endmacro %}
2 changes: 1 addition & 1 deletion src/plugins/publish/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ async def trigger_registry_update(

client_payload = {
"type": publish_type.value,
"key": f"{result.data['project_link']}:{result.data['module_name']}",
"key": f"{result.data["project_link"]}:{result.data["module_name"]}",
"config": config.group(1) if config else "",
"data": json.dumps(result.data),
}
Expand Down
20 changes: 12 additions & 8 deletions src/plugins/publish/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ def strip_ansi(text: str | None) -> str:


async def validate_plugin_info_from_issue(issue: "Issue") -> ValidationDict:
"""从议题中提取插件信息"""
"""从议题中获取插件信息,并且运行插件测试加载且获取插件元信息后进行验证"""
body = issue.body if issue.body else ""
author = issue.user.id if issue.user else ""
author_id = issue.user.id if issue.user else None

# 从议题里提取插件所需信息
raw_data: dict[str, Any] = extract_publish_info_from_issue(
{
"module_name": PLUGIN_MODULE_NAME_PATTERN,
Expand All @@ -67,6 +68,10 @@ async def validate_plugin_info_from_issue(issue: "Issue") -> ValidationDict:
with plugin_config.input_config.plugin_path.open("r", encoding="utf-8") as f:
previous_data: list[dict[str, str]] = json.load(f)

raw_data["author"] = author
raw_data["author_id"] = author_id

# 运行插件测试,获取插件测试输出与元信息
plugin_test_result: DockerTestResult = await DockerPluginTest(
DOCKER_IMAGES, project_link, module_name, test_config
).run("3.10")
Expand All @@ -82,11 +87,10 @@ async def validate_plugin_info_from_issue(issue: "Issue") -> ValidationDict:
"output": plugin_test_output,
"metadata": plugin_test_metadata,
"previous_data": previous_data,
"author": author,
"author_id": author_id,
}
)
# 如果插件测试被跳过,则从议题中获取信息

# 如果插件被跳过,则从议题获取插件信息
if plugin_config.skip_plugin_test:
plugin_info = extract_publish_info_from_issue(
{
Expand All @@ -100,23 +104,23 @@ async def validate_plugin_info_from_issue(issue: "Issue") -> ValidationDict:
)
raw_data.update(plugin_info)
elif plugin_test_metadata:
raw_data.update(plugin_test_metadata)
raw_data["desc"] = raw_data.get("description")
# 发布信息更新插件元数据
raw_data.update(plugin_test_metadata.model_dump())
else:
# 插件缺少元数据
# 可能为插件测试未通过,或者插件未按规范编写
raw_data["name"] = project_link

# 如果升级至 pydantic 2 后,可以使用 validation-context
# 传入的验证插件信息的上下文
validation_context = {
"previous_data": raw_data.get("previous_data"),
"skip_plugin_test": raw_data.get("skip_plugin_test"),
"plugin_test_output": raw_data.get("plugin_test_output"),
}

# 验证插件相关信息
validate_data = validate_info(PublishType.PLUGIN, raw_data, validation_context)

# 如果是插件,还需要额外验证插件加载测试结果
if (
validate_data.data.get("metadata") is None
and not plugin_config.skip_plugin_test
Expand Down
1 change: 1 addition & 0 deletions src/utils/store_test/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ async def merge_data(
dump_json(DRIVERS_PATH, self._store_drivers)
dump_json(PLUGINS_PATH, list(plugins.values()))
dump_json(RESULTS_PATH, results)
dump_json(PLUGIN_CONFIG_PATH, self._plugin_configs)

async def run(self, limit: int, force: bool = False):
"""运行商店测试
Expand Down
2 changes: 1 addition & 1 deletion src/utils/validation/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import re
from typing_extensions import LiteralString
from typing import LiteralString

# PyPI 格式
PYPI_PACKAGE_NAME_PATTERN = re.compile(
Expand Down
3 changes: 2 additions & 1 deletion src/utils/validation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,8 @@ def plugin_test_metadata_validator(
context = info.context
if context is None:
raise PydanticCustomError("validation_context", "未获取到验证上下文")

if v is None:
raise PydanticCustomError("plugin.metadata", "插件缺少元数据")
if context.get("skip_plugin_test"):
return None
return v
Expand Down

0 comments on commit 4872ca2

Please sign in to comment.