Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

mini-gitlab-bot

mini-gitlab-bot

新 issue 自动巡检、打中文标签;打上 机器人:修复 后自动修复并开 draft MR。
周期扫描漏掉的 issue、用 commit 引用自动回帖关闭;在 main 打 tag 后自动补 Release 文案。
Codex CLI 做 AI 内核,Python 网关做触发器 / 上下文准备 / 收尾编排。

ci release license python image


它解决什么问题

内网 GitLab 上,一个 issue 从"有人报"到"有人改"经常要排很久。这个机器人把中间那段自动化:

新 issue 自动巡检 → 打类型/严重度/组件标签 → 人工打 机器人:修复 → 拉代码 → 装依赖 → AI 改代码 → 跑测试 → 推分支 → 开 draft MR → 回帖告诉你结果

合并永远由人决定。机器人的产出是 issue 上的巡检结论 + 状态评论,以及(人工确认后)一个 draft MR; 另有两条兜底链路:周期扫描补做漏投的巡检、识别 Closes/Fixes/Resolves #N 自动回帖(open 时关闭;GitLab 已 autoclose 则补标记),Tag Push 后按提交分类补 GitLab Release 说明;以及一个只读 Dashboard 看队列、失败与项目状态。

一次完整的闭环

真实跑出来的结果(GitLab CE 19.5 + 真 codex,41 秒):

issue #8  「parse_labels 在空标签时返回 None」
  └─ alice 新开 issue(自动巡检,无需打标签)
       ├─ 只读 clone → 真跑 pytest 复现 → 定位 app.py
       ├─ 打上 类型:缺陷 / 严重度:一般 / 组件:其他 / 状态:待人工处理
       └─ 回帖:结论 + 复现证据 + 定位文件
  └─ alice 再打上 `机器人:修复` 标签(人工确认)
       ├─ 权限闸门:alice 是 Developer(30) ✓
       ├─ 准备环境:uv sync(网关侧联网)→ 离线自检 ✓
       ├─ Codex 沙箱:无网络、无凭据,读 context.md → 改 app.py
       │    `return None` → `return []`,自己跑 pytest:2 passed
       ├─ 四条件 + diff 校验 ✓ → push 分支 ai-fix/8-…
       ├─ 开 draft MR:「Draft: fix(issue-8): parse_labels 在空标签时返回 None」
       │    描述里有 Closes #8、agent 的 report.json 摘要、验证命令与结果
       └─ issue 上贴状态评论,标签流转 状态:自动修复中 → 状态:待审批

失败也会说话:状态:处理失败 + 一条带失败阶段、原因、日志尾部的评论,而不是静默消失。

周期扫描与自动 Release

两条链路都复用同一个持久队列与状态机,不额外起服务:

  • 补偿巡检(SWEEP_TRIAGE_ENABLED):每 SWEEP_INTERVAL_SEC 扫白名单项目里「open 且没有 状态: 标签」的 issue,按每项目每小时配额补建 triage,专治 webhook 漏投。
  • commit 引用回收(SWEEP_RESOLVE_ENABLED):扫默认分支最近 SWEEP_COMMIT_LOOKBACK_DAYS 天提交,遇到 Closes #12 / Fixes #12, #13 / Resolves #12 时,对仍 open 的 #12 回帖「已由提交修复」、打 状态:已解决 并关闭。评论带 marker,重跑只更新同一条。
  • 自动 Release(RELEASE_ENABLED):Tag Push Hook 产生新建 tag时(忽略分支 push、删 tag、移动/重推 tag),找上一个 tag → compare 提交 → 按 Conventional Commits 分组生成中文 release notes → 创建 GitLab Release。已有 Release 不覆盖。

工作流

flowchart LR
    A["新 issue 自动巡检<br/>或打 机器人:修复 / 评论 /fix"] --> B["Webhook 网关"]
    B --> C{"人工命令过权限闸门<br/>access_level ≥ 30"}
    C -- 拒绝 --> X["忽略并记录"]
    C -- 通过 --> D[("SQLite 持久队列")]
    D --> E["准备环境<br/>uv sync(联网)"]
    E --> F["Codex 沙箱<br/>无网络 / 无凭据"]
    F -. 按需取上下文 .-> M[("只读 MCP<br/>11 个工具")]
    F --> G{"四条件 + diff 校验"}
    G -- 不通过 --> H["失败收尾<br/>评论 + 状态:处理失败"]
    G -- 通过 --> I["push 分支"]
    I --> J["开 draft MR"]
    J --> K["状态评论 + 状态:待审批"]
Loading

为什么不是"把 issue 直接丢给 AI"

问题 这里的做法
AI 可能拿到凭据并外泄 agent 零凭据:GitLab token 只存在于网关进程;git 凭据只经 GIT_ASKPASS 进,绝不进 argv
AI 可能联网乱跑 agent 零网络:--sandbox workspace-write + 显式关闭 network;装依赖在网关的联网阶段做完
AI 可能改 CI 配置 / 密钥 diff 校验:禁改路径 + 文件数/行数上限,命中就在 push 之前拦下
AI 可能"假装完成" 四条件判定:exit 0 且出现 turn.completed 且 -o 文件本次新写入 且 diff 非空
AI 可能直接合代码 永远只开 draft MR,合并必须人工审批
出问题查不到 每个任务都有状态机流水、失败阶段/原因、codex 原始 JSONL 日志(14 天)

快速开始

1. 起服务(不需要 clone、不需要 .env)

docker run -d --name mini-gitlab-bot \
  --restart unless-stopped --init \
  -p 8080:8080 \
  -v mini-gitlab-bot-data:/data \
  -v mini-gitlab-bot-work:/work \
  --security-opt seccomp=unconfined --security-opt apparmor=unconfined \
  --cap-drop ALL \
  ghcr.io/flyinghenanman/mini-gitlab-bot:latest

打开 http://<host>:8080/setup,按页面填 GitLab、模型与管理员账号,保存后重启容器, 再打开 /login 登录。配置写入 /data/config.json(0600),不需要先复制 .env。

或者用 compose(同样不用 clone,下载一个文件即可):

curl -O https://raw.githubusercontent.com/FlyingHeNanman/mini-gitlab-bot/main/docker-compose.yml
docker compose up -d
# 固定版本:MGB_VERSION=0.4.0 docker compose up -d

v0.4.0 起的镜像包含初始化页和登录页;从源码构建用 docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build。

容器挂两个卷:/data(配置 + SQLite + 日志)、/work(job 目录 + uv 缓存)。

给容器 seccomp=unconfined(compose 里已配好):Codex 的 Linux 沙箱用 bubblewrap 创建 非特权 user namespace,Docker 默认 seccomp 会拒绝,表现为 agent 一条命令都执行不了。

2. 初始化

  1. 打开 /setup,填基础变量:GitLab 地址、写 token、只读 token(可选)、Webhook Secret、机器人用户名、项目白名单、模型端点/模型名/API Key、Dashboard 管理员与密码;
  2. 高级变量(并发、配额、Sweep、Release、保留策略、diff 上限)在折叠区,有默认值;
  3. 保存后点“重启服务”(或执行 docker restart mini-gitlab-bot),配置才进入运行时;
  4. 打开 /login 登录 Dashboard;之后随时可在 /settings 修改基础/高级配置。

初始化完成后 /setup 自动关闭;Dashboard 密码为空时整个观测台 fail closed。 /setup 在初始化前是开放的,首次启动请只把端口暴露在可信网络(初始化完成后它会自动关闭)。

3. GitLab 侧准备

步骤 说明
机器人账号 建一个专用账号(如 gitlab-bot),角色 Developer,不要给 Maintainer/Owner
两个 Token 主 token:api;只读 token:read_api(给 MCP 用,物理上不能写)
标签 不需要手工建:机器人第一次干活时自动创建 32 个中文标签(类型/严重度/组件/状态/证据/风险/命令),其中 状态:已解决 用于 commit 引用自动关闭
Webhook Settings → Webhooks → http://<bot 容器>:8080/webhook/gitlab,Secret 填初始化页生成的那一串,勾 Issues events、Comments、Merge request events、Tag push events

4. 触发

  • 自动巡检:新开 / 重开 issue 即触发(TRIAGE_ENABLED=true,默认开);
  • 修复:在 issue 上打 机器人:修复 标签,或评论 /fix;
  • 重跑巡检:打 机器人:巡检 标签,或评论 /triage(人工命令需 Developer=30);
  • 跳过:打 机器人:跳过 标签,机器人不再自动巡检该 issue;
  • 自动 Release:在仓库里新建 tag(通常打在默认分支)即触发,机器人补 Release 说明;
  • 周期扫描:SWEEP_ENABLED=true 时默认每 30 分钟跑一轮,无需手工触发。

旧标签(ai-fix*)已硬切;从旧版本升级时跑一次:

export GITLAB_URL=https://gitlab.example.com GITLAB_TOKEN=glpat-xxx
python scripts/migrate_labels.py --project group/project-a            # 预演
python scripts/migrate_labels.py --project group/project-a --apply    # 执行

之后去 issue 看巡检/状态评论,或去看 draft MR。

可观测性

端点 用途
GET /healthz 存活探针(不含任何任务信息)
GET /readyz 就绪:数据库可读 / worker 在跑 / 没有卡死任务
GET /metrics Prometheus 文本:tasks_total{state}、task_duration_seconds、queue_depth、worker_inflight、sweep_*、release_*…
GET /tasks 只读任务页(token 保护):状态、失败阶段、原因、耗时、MR 链接,点进去看日志尾部
GET /tasks.json 同上,给脚本用
GET /dashboard 只读观测台(登录页 + session):健康、7 日趋势、失败中心;/dashboard/recent、/dashboard/projects 为独立切换页

/tasks 默认开启但必须带 token:没配 ADMIN_TOKEN 时首次启动自动生成,持久化到 <DB_PATH 同目录>/admin_token(0600),cat /data/admin_token 取即可。

/dashboard 默认关闭(fail closed):初始化时设置 DASHBOARD_PASSWORD 后才启用,走 /login 的签名 HttpOnly session(12 小时过期),不再弹浏览器 Basic 框。只读,手动刷新; 当前实例只看自己的 SQLite。初始化写 /data/config.json(0600),/setup 在未初始化时开放, /settings 登录后可改基础/高级变量,保存后重启生效。设计见 docs/design/dashboard.md。 日志是结构化 JSON,每行自带 task_id;保留策略:日志 14 天、终态任务 90 天。

安全模型

┌───────────────────────── 网关进程(有网、有凭据)─────────────────────────┐
│  Webhook → 权限闸门 → 队列 → clone/commit/push → 开 MR → 评论/标签          │
│                                     │                                      │
│                          ┌──────────▼──────────┐                           │
│                          │  Codex 沙箱(子进程)│  无网络 · 无 GitLab 凭据   │
│                          │  只能写 job 目录     │  读 context.md / 改代码    │
│                          └──────────┬──────────┘                           │
│                                     │ 只读 MCP(read_api token,项目绑定)  │
└─────────────────────────────────────┼──────────────────────────────────────┘
                                      ▼
                             GitLab API(只读 11 个工具)
  • 安全关键配置全部用 -c 下发(优先级高于仓库自带配置),启动前删除 workdir 里的 .codex/
  • 只读 MCP 工具 schema 里没有 project 参数,项目由网关经环境变量绑定 —— agent 物理上换不了项目
  • 每个 job 独立 TMPDIR;任务本地产物(.mini-gitlab-bot/、.venv/…)绝不进提交

配置

配置页把变量分成两层,第一次部署只要求基础变量;其余都有默认值,可在高级区改。

基础变量(/setup 必填或建议填)
变量 说明 默认
GITLAB_URL / GITLAB_TOKEN 实例地址 / 主 token(api) —
GITLAB_READONLY_TOKEN 只读 token(read_api),给 MCP;可留空 —
WEBHOOK_SECRET Webhook secret;初始化页自动生成 —
REPO_WHITELIST 允许处理的项目,逗号分隔;空 = 全部拒绝 空
BOT_USERNAME 机器人账号,用于忽略自己产生的事件 —
MODEL_BASE_URL / MODEL_NAME 内网模型端点(需支持 Responses API)/ 模型名 —
LLM_API_KEY 模型 key;写入 0600 配置文件,只进 agent 子进程 —
DASHBOARD_USERNAME / DASHBOARD_PASSWORD Dashboard 登录凭据;密码空 = 关闭 admin / 空
AGENT_CONCURRENCY 并发任务数 2
高级变量(有默认值,按需覆盖)
变量 说明 默认
MIN_ACCESS_LEVEL 触发者角色下限(Developer = 30) 30
TRIAGE_ENABLED 新 issue 自动巡检 true
TRIAGE_QUOTA_PER_HOUR 每项目每小时巡检上限 20
SWEEP_ENABLED / SWEEP_INTERVAL_SEC 周期扫描总开关 / 间隔秒数 true / 1800
SWEEP_RESOLVE_ENABLED commit 引用自动回帖/关闭 true
RELEASE_ENABLED / RELEASE_LOOKBACK_DAYS Tag Push 自动 Release / 首个 tag 回溯天数 true / 365
AGENT_TIMEOUT_SEC 单次 codex 会话超时 1800
DIFF_MAX_FILES / DIFF_MAX_LINES 改动规模上限 50 / 2000
FORBIDDEN_PATHS 禁改路径 CI 配置、密钥…
LOG_RETENTION_DAYS / TASK_RETENTION_DAYS 日志 / 终态任务保留天数 14 / 90
ADMIN_TOKEN /tasks 的 token;空 = 自动生成 空
DASHBOARD_ENABLED Dashboard 总开关 true
CONFIG_FILE 配置文件路径 /data/config.json

开发

协作约束见 AGENTS.md;任何 UI 设计与实现先读 DESIGN.md,以其中的 token 和版面规范为准。

export UV_CACHE_DIR=$PWD/.uv-cache     # 沙箱里必须可写
uv sync
uv run ruff check . && uv run ruff format --check .
uv run pytest                          # 677 项,无需 GitLab / 无需 LLM

预览 Dashboard(临时 SQLite + 样例任务,不接真 GitLab):

uv run python scripts/preview_dashboard.py
# http://127.0.0.1:8099/login;用户名 admin / 密码 preview2026

测试分层:

  • 单元:状态机、策略、渲染、配置、脱敏、MCP 协议
  • 集成(假件):假 SCM/VCS/agent 把整条流水线(含失败、恢复、崩溃重启)跑穿
  • 集成(真件):真 git 仓库、真 uv sync、真 HTTP 打桩 GitLab、真子进程的 codex / MCP

想在真 GitLab 上跑完整闭环(含越权用例):

./scripts/e2e/run.sh --reuse --keep          # 桩 agent
# 或接真 codex:
E2E_STUB_AGENT=0 MODEL_BASE_URL=… MODEL_NAME=… LLM_API_KEY=… ./scripts/e2e/run.sh --reuse

项目结构

端口 / 适配器分层
core/      状态机 · 领域模型 · diff 策略 · context.md 渲染 · 编排器 · worker
           · 周期扫描(sweep)· Release 说明/编排 · MCP 工具契约 · 日志
           · 指标 · 任务页渲染                              ← 纯逻辑,零 IO
ports/     store · scm · vcs · agent · workspace · env · notifier · janitor
adapters/  sqlite_store · gitlab_client · git_cli · codex_cli · uv_env
           · local_workspace · janitor · proc(子进程+脱敏)· fakes · unavailable
gateway/   app(HTTP) · config · webhook · permissions · runtime(装配) · mcp_readonly
tests/     unit / integration / stub_gitlab(可编程的 GitLab API 桩)
scripts/   e2e(真 GitLab 端到端)
docs/      PLAN 事实核查 · 文案模板 · 发版流程

依赖方向单向:gateway → adapters → ports → core,core 不 import 任何 IO。

已验证 / 未验证

项 状态
单元 + 集成测试 ✅ 677 项全绿(CI 每次 push/PR 都跑)
自动巡检(真件) ✅ GitLab CE 19.5.3 + 真 codex 0.166 + Ark:新 issue → 只读检出 → 复现 → 中文标签/评论,重开幂等
自动修(真件) ✅ 巡检判定 eligible → 内部派生 fix → 真 codex 修复 → draft MR(父任务链、draft、Closes 均验证)
重复/指派 ✅ 高置信重复关联并关闭;CODEOWNERS 命中即指派;GitLab assign/link/close API 已真机验证
合并解释(真件) ✅ 真 GitLab MR merge → 不 clone、只看 diff → 真 codex 解释评论 + 状态:已合并
周期扫描 / 自动关闭 ✅ 真 GitLab CE 19.5.3:open issue → 回帖/状态:已解决/关闭;GitLab autoclose 先关闭时,近期 closed issue 也会补回帖/标签,marker 幂等
自动 Release ✅ 真 GitLab CE 19.5.3:Tag Push → Release、previous tag compare、修复/新功能分组;已有 Release 跳过由假件覆盖
Dashboard ✅ 只读观测台:登录页 + session、7 日趋势/失败中心/项目/最近任务/任务详情;首次初始化为 /setup,配置页 /settings;当前仅单实例 SQLite
真 GitLab 闭环 ✅ GitLab CE 19.5:打标签 → draft MR → 单条评论 → 标签流转,15/15 断言
真 codex 修复 ✅ codex 0.157.1 + 内网模型端点:自主改代码、跑 pytest、写报告,41 秒
只读 MCP ✅ 11 个工具在真实 GitLab 数据上逐个验证(含真实 CI 日志)
镜像 ✅ 多阶段构建(1.2 GB),容器内非 root 跑通完整闭环
非 Python 项目 ❌ 未支持(首期只做 uv 管理的 Python 项目)
非 GitLab 平台 ❌ 未支持(GitHub/Gitea 只是端口层面的可能性)
其他 GitLab 版本 ⚠️ 只验证过 19.5;16/17/18 未逐一测试
GitLab 默认自动关闭 ✅ 已兼容:autoclose_referenced_issues=true 时 GitLab 先关闭,sweep 扫近期 closed issue 补回帖/标签,不重复处理

文档

许可

Apache License 2.0

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages