From fbd7377e3d2ed5d25815dfd27c3c40d356f0b290 Mon Sep 17 00:00:00 2001 From: uy/sun Date: Sun, 18 Jan 2026 11:41:34 +0800 Subject: [PATCH 1/3] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=20AGENTS.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..deb7a26a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,114 @@ +# NoneFlow + +> NoneBot 商店工作流自动化机器人,基于 GitHub Actions + NoneBot2 构建 + +## 项目概览 + +- 运行环境:GitHub Actions 中的 NoneBot2 机器人,使用 `nonebot-adapter-github` 处理 webhook 事件 +- 核心目标:自动处理 NoneBot 商店的插件/适配器/机器人 发布、修改、删除议题,生成 PR 并执行测试验证 +- 主要组件: + - `src/plugins/github/`:事件处理、GitHub/Git 操作 + - `src/providers/`:商店数据模型、校验逻辑、插件测试运行器 + +## 快速开始(本地) + +- 安装依赖:`uv sync` +- 运行测试(并行 + 覆盖率):`uv run poe test` +- 快照测试: + - 创建快照:`uv run poe snapshot-create` + - 更新快照:`uv run poe snapshot-fix` +- 本地运行 providers CLI: + - 商店插件测试:`uv run poe store-test` + - Docker 插件测试:`uv run poe docker-test` + +## GitHub Actions 执行流程 + +1. `action.yml` 定义 Docker action +2. `docker/noneflow.dockerfile` 构建运行环境 +3. `bot.py` 读取环境变量(`GITHUB_EVENT_NAME`、`GITHUB_EVENT_PATH`、`GITHUB_RUN_ID`) +4. 将 webhook payload 转换为 NoneBot Event 并分发到插件 + +## 代码结构与关键约定 + +### 插件加载机制 + +- `bot.py` 加载 `src/plugins/` 下所有插件 +- `src/plugins/github/__init__.py` 动态加载子插件(`src/plugins/github/plugins/publish`、`remove`、`resolve` 等) +- 每个子插件通过 NoneBot matcher 监听特定 GitHub 事件(如 `issues.opened`、`pull_request.closed`) + +### 配置模型(Pydantic) + +- 主配置:`src/plugins/github/config.py` + - `Config.model_config = coerce_numbers_to_str=True`:Actions 环境变量会被 NoneBot 强制转换 + - `input_config: PublishConfig`:嵌套配置,包含 `plugin_path`、`registry_repository`、`store_repository`、`artifact_path` + +### GitHub/Git 操作规范 + +- Handler 类层次(`src/plugins/github/handlers/`): + - `GitHandler`:通过 `run_shell_command` 执行 git 命令(同步) + - `GithubHandler`:封装 `githubkit` REST/GraphQL 调用(异步,方法前缀 `async_*`) + - `IssueHandler`:继承 `GithubHandler`,维护 `issue.title`/`issue.body` 缓存,避免重复更新 +- 关键约定: + - 评论复用:通过 `NONEFLOW_MARKER = ""` 标记已有评论并更新,避免重复发布 + - 跳过测试:在议题评论中使用 `/skip`(仅 OWNER/MEMBER 权限生效) + - 分支命名:`BRANCH_NAME_PREFIX = "publish/issue"` + issue 编号 + +### 议题解析 + +- 使用正则表达式从议题 body 提取结构化信息(`src/plugins/github/utils.py` 的 `extract_issue_info_from_issue`) +- 模板:`### {字段名}\n\n{内容}`,见 `src/plugins/github/constants.py` 的 `ISSUE_PATTERN` + +### 商店数据(store/registry) + +- 数据模型:`src/providers/models.py` + - `StorePlugin` / `StoreAdapter` 等:NoneBot 仓库存储格式 + - `RegistryPlugin` 等:商店 API 返回格式 +- 校验逻辑:`src/providers/validation/`(主页可访问性、PyPI 版本、模块可加载性) +- JSON5 格式化约定:必须使用 `dump_json5`(`src/providers/utils.py`)写入文件,自动添加尾随逗号和换行 + +## 测试与质量 + +### 测试框架配置 + +- 使用 `nonebug` 测试 NoneBot 插件 +- `pytest-asyncio`:所有异步测试自动标记为 `loop_scope="session"`(见 `tests/conftest.py`) +- `respx`:模拟所有外部 HTTP 调用(PyPI、GitHub API),禁止真实网络访问 + +### Fixture 说明 + +- `app` fixture(`tests/conftest.py`): + - 自动复制 `tests/store/` 下的 JSON5 文件到临时目录 + - Mock `plugin_config` 路径指向临时文件 + - 注意:`src/providers/` 不初始化 NoneBot,直接读取环境变量(如 `GITHUB_STEP_SUMMARY`) + +### 时间 Mock + +- 使用 `mock_datetime` fixture 固定时间为 `2023-08-23 09:22:14`(UTC+8),确保快照测试稳定 + +## 修改代码时的硬性规则 + +- 异步边界:GitHub API 调用必须 `async`/`await`;Git/文件操作必须走 `run_shell_command`(同步) +- 不要在同步上下文调用异步方法 +- 议题解析修改需复用 `src/plugins/github/utils.py` 的正则辅助函数;常量集中在 `src/plugins/github/constants.py` +- 修改议题解析后必须更新相关测试快照 +- JSON5 序列化必须使用 `dump_json5`,保持尾随逗号格式,避免无意义差异 + +## 提交与作者信息 + +- 提交信息通过 `commit_message` 辅助函数生成(`src/plugins/github/utils.py`) +- 格式:`{prefix} {message} (#{issue_number})` +- Git author 配置为议题提交者(`author@users.noreply.github.com`) + +## 环境变量 + +### Actions 运行时必需 + +- `APP_ID` / `PRIVATE_KEY`:GitHub App 认证 +- `GITHUB_EVENT_NAME` / `GITHUB_EVENT_PATH` / `GITHUB_RUN_ID`:Actions 上下文 +- `GITHUB_STEP_SUMMARY`:输出作业摘要路径 + +### Providers CLI 使用 + +- `REGISTRY_UPDATE_PAYLOAD`:商店更新数据(JSON) +- `PROJECT_LINK` / `MODULE_NAME` / `PLUGIN_CONFIG`:Docker 测试参数 +- `PYTHON_VERSION`:插件测试的 Python 版本 From 617a0ce0ff95109a79bcb0807715218440bddf13 Mon Sep 17 00:00:00 2001 From: uy/sun Date: Mon, 23 Feb 2026 23:51:19 +0800 Subject: [PATCH 2/3] =?UTF-8?q?docs:=20=E5=88=A9=E7=94=A8=20kimi=20?= =?UTF-8?q?=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 323 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 243 insertions(+), 80 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index deb7a26a..093831fb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,114 +1,277 @@ -# NoneFlow +# NoneFlow 项目指南 + +NoneFlow 是一个基于 NoneBot2 框架开发的 GitHub Actions 工作流管理机器人,用于自动化处理 NoneBot 生态中的插件、适配器和机器人发布流程。 + +## 项目概述 + +NoneFlow 主要功能包括: + +- 自动处理商店发布议题(Plugin/Adapter/Bot),根据议题内容创建拉取请求 +- 自动修改已创建的拉取请求当议题内容变化时 +- 拉取请求关闭时自动关闭对应议题并删除分支 +- 自动解决已创建拉取请求的冲突 +- 自动检查发布要求(主页可访问、已发布至 PyPI、插件可正常加载) +- 审查通过后自动合并 + +## 技术栈 + +- **Python**: 3.14.3+ +- **框架**: NoneBot2 (异步机器人框架) +- **适配器**: nonebot-adapter-github +- **验证**: Pydantic v2 +- **包管理**: uv +- **容器**: Docker +- **GitHub API**: githubkit + +## 项目结构 + +```text +. +├── bot.py # 主入口,初始化 NoneBot 并处理 GitHub Actions 事件 +├── pyproject.toml # 项目配置、依赖和工具设置 +├── action.yml # GitHub Action 定义 +├── src/ +│ ├── plugins/ +│ │ └── github/ # GitHub 事件处理插件 +│ │ ├── plugins/ +│ │ │ ├── publish/ # 发布处理(Plugin/Adapter/Bot) +│ │ │ ├── remove/ # 移除处理 +│ │ │ ├── resolve/ # 冲突解决 +│ │ │ └── config/ # 配置处理 +│ │ ├── handlers/ # GitHub API 处理器 +│ │ ├── depends/ # 依赖注入函数 +│ │ └── config.py # 插件配置 +│ └── providers/ +│ ├── validation/ # 数据验证模块 +│ ├── docker_test/ # Docker 插件测试 +│ ├── store_test/ # 商店测试 +│ └── models.py # 数据模型定义 +├── tests/ # 测试代码 +├── docker/ # Dockerfile 定义 +│ ├── noneflow.dockerfile # NoneFlow 主镜像 +│ └── nonetest.dockerfile # 插件测试镜像 +└── examples/ # GitHub Actions 工作流示例 +``` -> NoneBot 商店工作流自动化机器人,基于 GitHub Actions + NoneBot2 构建 +## 构建与运行 -## 项目概览 +### 本地开发环境 -- 运行环境:GitHub Actions 中的 NoneBot2 机器人,使用 `nonebot-adapter-github` 处理 webhook 事件 -- 核心目标:自动处理 NoneBot 商店的插件/适配器/机器人 发布、修改、删除议题,生成 PR 并执行测试验证 -- 主要组件: - - `src/plugins/github/`:事件处理、GitHub/Git 操作 - - `src/providers/`:商店数据模型、校验逻辑、插件测试运行器 +项目使用 `uv` 作为包管理器: -## 快速开始(本地) +```bash +# 安装依赖 +uv sync -- 安装依赖:`uv sync` -- 运行测试(并行 + 覆盖率):`uv run poe test` -- 快照测试: - - 创建快照:`uv run poe snapshot-create` - - 更新快照:`uv run poe snapshot-fix` -- 本地运行 providers CLI: - - 商店插件测试:`uv run poe store-test` - - Docker 插件测试:`uv run poe docker-test` +# 运行测试 +uv run poe test -## GitHub Actions 执行流程 +# 创建快照 +uv run poe snapshot-create -1. `action.yml` 定义 Docker action -2. `docker/noneflow.dockerfile` 构建运行环境 -3. `bot.py` 读取环境变量(`GITHUB_EVENT_NAME`、`GITHUB_EVENT_PATH`、`GITHUB_RUN_ID`) -4. 将 webhook payload 转换为 NoneBot Event 并分发到插件 +# 修复快照 +uv run poe snapshot-fix +``` -## 代码结构与关键约定 +### Docker 构建 -### 插件加载机制 +```bash +# 构建 NoneFlow 镜像 +docker build -f docker/noneflow.dockerfile -t noneflow . -- `bot.py` 加载 `src/plugins/` 下所有插件 -- `src/plugins/github/__init__.py` 动态加载子插件(`src/plugins/github/plugins/publish`、`remove`、`resolve` 等) -- 每个子插件通过 NoneBot matcher 监听特定 GitHub 事件(如 `issues.opened`、`pull_request.closed`) +# 构建 NoneTest 镜像(用于插件测试) +docker build -f docker/nonetest.dockerfile -t nonetest . +``` -### 配置模型(Pydantic) +### GitHub Actions 运行 -- 主配置:`src/plugins/github/config.py` - - `Config.model_config = coerce_numbers_to_str=True`:Actions 环境变量会被 NoneBot 强制转换 - - `input_config: PublishConfig`:嵌套配置,包含 `plugin_path`、`registry_repository`、`store_repository`、`artifact_path` +项目作为 GitHub Action 运行,配置示例见 `examples/noneflow.yml`: -### GitHub/Git 操作规范 +```yaml +- name: NoneFlow + uses: docker://ghcr.io/nonebot/noneflow:latest + with: + config: > + { + "base": "master", + "plugin_path": "assets/plugins.json5", + "bot_path": "assets/bots.json5", + "adapter_path": "assets/adapters.json5", + "registry_repository": "nonebot/registry" + } + env: + APP_ID: ${{ secrets.APP_ID }} + PRIVATE_KEY: ${{ secrets.APP_KEY }} +``` -- Handler 类层次(`src/plugins/github/handlers/`): - - `GitHandler`:通过 `run_shell_command` 执行 git 命令(同步) - - `GithubHandler`:封装 `githubkit` REST/GraphQL 调用(异步,方法前缀 `async_*`) - - `IssueHandler`:继承 `GithubHandler`,维护 `issue.title`/`issue.body` 缓存,避免重复更新 -- 关键约定: - - 评论复用:通过 `NONEFLOW_MARKER = ""` 标记已有评论并更新,避免重复发布 - - 跳过测试:在议题评论中使用 `/skip`(仅 OWNER/MEMBER 权限生效) - - 分支命名:`BRANCH_NAME_PREFIX = "publish/issue"` + issue 编号 +## 测试 -### 议题解析 +测试使用 pytest 框架,配置如下: -- 使用正则表达式从议题 body 提取结构化信息(`src/plugins/github/utils.py` 的 `extract_issue_info_from_issue`) -- 模板:`### {字段名}\n\n{内容}`,见 `src/plugins/github/constants.py` 的 `ISSUE_PATTERN` +```bash +# 运行所有测试(并行) +uv run poe test -### 商店数据(store/registry) +# 等同于 +pytest --cov=src --cov-report xml --junitxml=./junit.xml -n auto +``` -- 数据模型:`src/providers/models.py` - - `StorePlugin` / `StoreAdapter` 等:NoneBot 仓库存储格式 - - `RegistryPlugin` 等:商店 API 返回格式 -- 校验逻辑:`src/providers/validation/`(主页可访问性、PyPI 版本、模块可加载性) -- JSON5 格式化约定:必须使用 `dump_json5`(`src/providers/utils.py`)写入文件,自动添加尾随逗号和换行 +### 测试结构 -## 测试与质量 +- `tests/conftest.py`: 全局测试配置和 fixtures +- `tests/plugins/github/`: GitHub 插件测试 +- `tests/providers/`: Provider 模块测试 +- `tests/constant.py`: 测试常量 -### 测试框架配置 +### 关键测试 Fixtures -- 使用 `nonebug` 测试 NoneBot 插件 -- `pytest-asyncio`:所有异步测试自动标记为 `loop_scope="session"`(见 `tests/conftest.py`) -- `respx`:模拟所有外部 HTTP 调用(PyPI、GitHub API),禁止真实网络访问 +- `app`: NoneBot 应用实例 +- `mocked_api`: 使用 respx 模拟的 HTTP API(PyPI、GitHub、商店数据) +- `mock_datetime`: 固定时间戳,确保测试一致性 -### Fixture 说明 +## 代码风格 -- `app` fixture(`tests/conftest.py`): - - 自动复制 `tests/store/` 下的 JSON5 文件到临时目录 - - Mock `plugin_config` 路径指向临时文件 - - 注意:`src/providers/` 不初始化 NoneBot,直接读取环境变量(如 `GITHUB_STEP_SUMMARY`) +项目使用 Ruff 进行代码格式化和 lint: -### 时间 Mock +- **行长度**: 88 字符 +- **目标 Python 版本**: 3.13+ +- **导入排序**: isort 规则启用 +- **类型检查**: Pyright (standard 模式) -- 使用 `mock_datetime` fixture 固定时间为 `2023-08-23 09:22:14`(UTC+8),确保快照测试稳定 +### 主要规则 -## 修改代码时的硬性规则 +- `F`, `W`, `E`: Pyflakes 和 pycodestyle +- `UP`: pyupgrade +- `ASYNC`: flake8-async +- `I`: isort 导入排序 +- `RUF`: Ruff 特定规则 -- 异步边界:GitHub API 调用必须 `async`/`await`;Git/文件操作必须走 `run_shell_command`(同步) -- 不要在同步上下文调用异步方法 -- 议题解析修改需复用 `src/plugins/github/utils.py` 的正则辅助函数;常量集中在 `src/plugins/github/constants.py` -- 修改议题解析后必须更新相关测试快照 -- JSON5 序列化必须使用 `dump_json5`,保持尾随逗号格式,避免无意义差异 +## 主要模块说明 -## 提交与作者信息 +### 1. GitHub 插件 (`src/plugins/github/`) -- 提交信息通过 `commit_message` 辅助函数生成(`src/plugins/github/utils.py`) -- 格式:`{prefix} {message} (#{issue_number})` -- Git author 配置为议题提交者(`author@users.noreply.github.com`) +#### 发布处理 (`plugins/publish/`) -## 环境变量 +处理 Plugin/Adapter/Bot 的发布流程: -### Actions 运行时必需 +- `__init__.py`: 事件处理器(issue 打开/编辑/评论,PR 关闭/审查) +- `validation.py`: 议题内容验证逻辑 +- `render.py`: 评论渲染 +- `utils.py`: 工具函数 -- `APP_ID` / `PRIVATE_KEY`:GitHub App 认证 -- `GITHUB_EVENT_NAME` / `GITHUB_EVENT_PATH` / `GITHUB_RUN_ID`:Actions 上下文 -- `GITHUB_STEP_SUMMARY`:输出作业摘要路径 +#### 移除处理 (`plugins/remove/`) -### Providers CLI 使用 +处理商店项目的移除请求。 -- `REGISTRY_UPDATE_PAYLOAD`:商店更新数据(JSON) -- `PROJECT_LINK` / `MODULE_NAME` / `PLUGIN_CONFIG`:Docker 测试参数 -- `PYTHON_VERSION`:插件测试的 Python 版本 +#### 冲突解决 (`plugins/resolve/`) + +自动解决多个发布 PR 之间的冲突。 + +#### 配置处理 (`plugins/config/`) + +处理商店配置的修改。 + +### 2. 数据验证 (`src/providers/validation/`) + +使用 Pydantic 模型验证发布数据: + +- `PluginPublishInfo`: 插件发布信息 +- `AdapterPublishInfo`: 适配器发布信息 +- `BotPublishInfo`: 机器人发布信息 +- `DriverPublishInfo`: 驱动器发布信息 + +### 3. Docker 测试 (`src/providers/docker_test/`) + +在隔离的 Docker 环境中测试插件加载: + +- 拉取插件代码 +- 安装依赖 +- 验证插件可正常加载 +- 提取插件元数据 + +### 4. 数据模型 (`src/providers/models.py`) + +核心数据模型: + +- `StorePlugin`/`StoreAdapter`/`StoreBot`: 商店数据格式 +- `RegistryPlugin`/`RegistryAdapter`/`RegistryBot`: 注册表数据格式 +- `RegistryArtifactData`: GitHub Artifact 数据传递 + +## 配置说明 + +### 环境变量 + +- `APP_ID`: GitHub App ID +- `PRIVATE_KEY`: GitHub App 私钥 +- `GITHUB_REPOSITORY`: 当前仓库 +- `GITHUB_RUN_ID`: 工作流运行 ID +- `GITHUB_EVENT_NAME`: 事件名称 +- `GITHUB_EVENT_PATH`: 事件文件路径 +- `INPUT_CONFIG`: JSON 格式的配置 + +### 输入配置 (INPUT_CONFIG) + +```json +{ + "base": "master", // 基础分支 + "plugin_path": "assets/plugins.json5", // 插件数据文件路径 + "bot_path": "assets/bots.json5", // 机器人数据文件路径 + "adapter_path": "assets/adapters.json5", // 适配器数据文件路径 + "registry_repository": "nonebot/registry", // 注册表仓库 + "store_repository": "nonebot/nonebot2", // 商店仓库 + "artifact_path": "artifact" // Artifact 存储路径 +} +``` + +## 发布流程 + +1. 用户在仓库创建带 `Plugin`/`Adapter`/`Bot` 标签的议题 +2. NoneFlow 自动检测议题并提取信息 +3. 验证信息完整性(名称、主页、PyPI 项目等) +4. 如果是插件,在 Docker 中测试加载 +5. 创建拉取请求修改商店数据文件 +6. 审查通过后自动合并 +7. 触发注册表更新 + +## 版本管理 + +使用 `bump-my-version` 管理版本号: + +```bash +# 显示当前版本和可升级版本 +uv run poe show-bump + +# 升级版本(自动修改 pyproject.toml、CHANGELOG.md、uv.lock) +uv run poe bump +``` + +## CI/CD + +### GitHub Actions 工作流 + +1. **CI** (`.github/workflows/main.yml`): + + - 运行测试 + - 上传覆盖率到 Codecov + - 构建并推送 Docker 镜像 + +2. **Release Draft** (`.github/workflows/release-draft.yml`): + - 自动生成发布草稿 + +### Docker 镜像 + +- `ghcr.io/nonebot/noneflow`: NoneFlow 主镜像 +- `ghcr.io/nonebot/nonetest`: 插件测试镜像 + +## 开发注意事项 + +1. **GitHub App 权限**: 需要 `contents:write`、`issues:write`、`pull_requests:write` 等权限 +2. **Docker 测试**: 插件测试需要 Docker 环境,测试时会拉取插件代码并在隔离环境中运行 +3. **并发控制**: 使用 `concurrency` 配置避免同一议题的并发处理 +4. **Artifact 传递**: 通过 GitHub Actions Artifact 在不同工作流间传递数据 + +## 相关仓库 + +- [nonebot2](https://github.com/nonebot/nonebot2): NoneBot 主仓库 +- [registry](https://github.com/nonebot/registry): 商店注册表 +- [noneflow-test](https://github.com/nonebot/noneflow-test): 测试仓库 From f4d40b931cbd7a87e899a616239ebeeb21bb191a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 15:57:32 +0000 Subject: [PATCH 3/3] style: auto fix by pre-commit hooks --- AGENTS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 093831fb..ff2a57e7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -250,7 +250,6 @@ uv run poe bump ### GitHub Actions 工作流 1. **CI** (`.github/workflows/main.yml`): - - 运行测试 - 上传覆盖率到 Codecov - 构建并推送 Docker 镜像