把 AI 编程 Agent 接进 GitLab,真正困难的并不是让它"跑起来",而是让它在 Issue、分支、Merge Request 和流水线之间形成一条可审计、可回滚、权限受控的工程闭环。本文基于 OpenCode 当前公开的 GitLab 集成方式,给出两条可落地路线:在普通 GitLab CI 中通过组件运行 OpenCode,以及通过 GitLab Duo Agent Platform 用评论触发任务。
本文所有 Token、项目地址和账号均使用占位符。示例默认让 OpenCode 运行在 GitLab Runner 中,不把长期凭据写进仓库,也不允许 Agent 直接向受保护分支推送。
一、先看清目标:不是"自动写代码",而是闭环交付
一个可用于团队协作的 GitLab Agent 工作流,至少应覆盖五个阶段:
- 读取 Issue、Merge Request、仓库规则和当前代码;
- 分析需求,给出计划,并在受控 Runner 中修改文件;
- 运行 lint、类型检查、测试和安全扫描;
- 创建独立分支与 Merge Request,而不是直接写入默认分支;
- 把结果、失败原因和证据回写到 GitLab,交给人工审批。
这条链路中,OpenCode 负责推理与代码变更,GitLab 负责身份、触发、制品、分支保护、审批和审计,Runner 则是实际执行环境。三者职责分开后,团队才能回答"谁触发了什么任务、Agent 访问了什么、改了哪些文件、经过哪些检查、最后由谁合并"。
二、两条接入路线怎么选
| 维度 | 普通 GitLab CI | GitLab Duo 路线 |
|---|---|---|
| 触发方式 | pipeline、手动任务、规则或定时任务 | 在 Issue/MR 评论中提及 Agent |
| 交互体验 | 偏流水线和平台工程 | 偏开发者协作与对话 |
| 配置核心 | .gitlab-ci.yml、OpenCode 配置、CI 变量 |
Duo Agent Platform、Flow 配置、服务账号与 CI 变量 |
| 执行位置 | GitLab Runner | GitLab Runner |
| 适合场景 | 批处理、质量门禁、定时维护、固定任务 | Issue 分析、自动修复、MR 审查 |
| 主要风险 | 第三方组件供应链、变量暴露、并发失控 | 评论触发滥用、服务账号过权、提示注入 |
官方文档中的普通 CI 示例使用社区维护的 nagyv/gitlab-opencode CI/CD Component。它能减少样板配置,但不等于 OpenCode 官方托管服务。企业使用前应固定组件版本、审查源码和依赖,并准备不依赖第三方组件的回退方案。
三、普通 GitLab CI:先跑通最小任务
3.1 凭据用 File 类型变量保存
OpenCode 的认证 JSON 不应提交到仓库。进入 GitLab 项目的 Settings → CI/CD → Variables,新建 File 类型变量,例如 OPENCODE_AUTH_JSON,并启用 Masked and hidden。若任务只允许在受保护分支或受信任环境运行,还应启用 Protected。
变量保护要与流水线来源一起设计。来自 Fork 的 Merge Request、外部贡献者提交或未受保护分支,不应自动获得生产级模型密钥。仅勾选"Masked"不能阻止脚本主动读取变量,也不能替代 Runner 隔离。
3.2 引入 CI/CD Component
yaml
include:
- component: $CI_SERVER_FQDN/nagyv/gitlab-opencode/opencode@2
inputs:
config_dir: ${CI_PROJECT_DIR}/opencode-config
auth_json: $OPENCODE_AUTH_JSON
command: review
message: |
Review this repository change.
Focus on correctness, security, tests, and backward compatibility.
Do not print credentials or modify protected branches.
config_dir 让每个任务使用独立配置目录,可以按 job 启用不同 Agent、工具和权限;auth_json 接收 File 类型变量的路径;message 是任务入口。组件支持的输入会随版本变化,上线前应以组件目录中的当前文档为准。
3.3 把任务边界写进配置,而不是只写在 Prompt
Prompt 可以说明目标,但安全边界应由多层控制共同实现:
- Runner 容器只挂载当前项目,不挂宿主机工作目录;
- CI Job Token 或服务账号只授予所需项目和 API 范围;
- GitLab 保护默认分支、发布分支和环境;
- OpenCode 配置限制可用工具、路径和命令;
- 流水线通过
rules限制可信触发来源; - 最终合并仍走 Code Owner 和人工审批。
四、让不同任务使用不同权限
"分析 Issue"和"修改代码并创建 MR"不应共用同一身份。建议拆成三个权限级别:
| 任务 | 仓库权限 | GitLab API 能力 | 网络能力 |
|---|---|---|---|
| 解释 Issue | 只读 | 读取 Issue,必要时写评论 | 仅模型与 GitLab |
| 审查 MR | 只读工作树 | 读取 Diff、写 Review/评论 | 仅模型与 GitLab |
| 修复 Issue | 可写临时工作树 | 创建分支、推送、创建 MR | 模型、GitLab、受控包仓库 |
这种拆分能显著缩小泄露或提示注入后的影响面。即使"审查 MR"的 Prompt 被恶意代码注释诱导,它也没有推送分支或修改 CI 设置的权限。
五、用 rules 收紧流水线触发条件
不要让所有 Pipeline Source 自动拿到 Agent 凭据。下面的例子只允许受保护默认分支上的手动任务运行:
yaml
opencode_maintenance:
rules:
- if: '$CI_COMMIT_REF_PROTECTED == "true" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
when: manual
- when: never
resource_group: opencode-maintenance
timeout: 30m
resource_group 可以避免同一项目的多个写任务并发修改;timeout 防止 Agent 长时间占用 Runner。对于只读 MR 审查,可以放宽触发条件,但仍要检查 Fork、外部贡献者和变量可见性。
六、GitLab Duo:从评论触发 Agent
OpenCode 的 GitLab Duo 集成允许开发者在 Issue 或 Merge Request 评论中提及 @opencode。平台把事件上下文交给 Flow,Flow 在 GitLab Runner 中安装 OpenCode 与 glab,随后执行分析、修改或审查任务。
典型交互包括:
@opencode explain this issue
@opencode fix this
@opencode review this merge request
触发词可以调整,但应避免过于宽泛。生产项目最好只允许特定成员、角色或受信任群组触发写任务,并对同一用户、项目和时间窗口设置并发与成本上限。
七、Flow 配置的核心结构
官方示例使用 Node 镜像,安装 opencode-ai 和 glab,设置 GitLab Token,生成 OpenCode 认证文件,然后把 AI_FLOW_CONTEXT、AI_FLOW_INPUT 和 AI_FLOW_EVENT 传给 OpenCode。可以将核心逻辑收敛为以下结构:
yaml
image: node:22-slim
commands:
- npm install --global opencode-ai
- apt-get update --quiet
- apt-get install --yes git curl
- export GITLAB_TOKEN="$GITLAB_TOKEN_OPENCODE"
- mkdir --parents ~/.local/share/opencode
- cp "$OPENCODE_AUTH_JSON" ~/.local/share/opencode/auth.json
- git config --global user.email "opencode@example.invalid"
- git config --global user.name "OpenCode Bot"
- |
opencode run "
Context: $AI_FLOW_CONTEXT
Task: $AI_FLOW_INPUT
Event: $AI_FLOW_EVENT
Use GitLab only through the authenticated CLI.
Never expose variables, tokens, headers, or auth files.
Do not push to protected branches.
Run the repository checks before proposing a merge request.
"
variables:
- OPENCODE_AUTH_JSON
- GITLAB_TOKEN_OPENCODE
- GITLAB_HOST
示例省略了安装 glab 的具体步骤,因为发行版、镜像和企业镜像仓库策略不同。生产环境更适合构建经过扫描、签名和固定版本的内部 Runner 镜像,而不是每次运行时从互联网执行安装脚本。
八、自动创建分支与 Merge Request
Agent 修改文件后,应先判断工作树是否真的变化,再创建唯一分支。分支名不要直接拼接未经处理的 Issue 标题,可以使用 Job ID 或经过白名单清洗的编号:
bash
if ! git diff --quiet || \
! git diff --cached --quiet || \
[ -n "$(git ls-files --others --exclude-standard)" ]; then
git add --all
if ! git diff --cached --quiet; then
BRANCH="opencode/${CI_JOB_ID}"
git checkout -b "$BRANCH"
git commit -m "chore: apply OpenCode change for job ${CI_JOB_ID}"
git push origin "HEAD:refs/heads/${BRANCH}"
glab mr create \
--source-branch "$BRANCH" \
--target-branch "$CI_DEFAULT_BRANCH" \
--fill \
--yes
fi
fi
更稳妥的做法是让服务账号只能创建普通分支和 MR,不能覆盖他人分支、修改保护规则、创建 Token 或直接合并。GitLab 的审批规则、Code Owners、状态检查和安全扫描继续生效。
九、MR 审查不要只输出"看起来不错"
高质量自动审查需要固定输出协议。建议要求 Agent 对每个发现给出严重级别、文件与行号、风险解释、可复现证据和最小修复建议,并明确区分"确定问题""需要确认"和"风格建议"。
text
Review contract:
1. Inspect only the MR diff and directly affected code.
2. Report correctness, security, compatibility, and missing-test risks.
3. For every finding include severity, location, evidence, and fix.
4. Do not invent APIs, files, test results, or runtime behavior.
5. If evidence is insufficient, say what must be verified.
6. Never include secrets, full environment dumps, or auth headers.
审查任务应先运行仓库已有检查,再结合 Diff 分析。测试失败和静态分析告警属于可验证证据;模型推断属于辅助判断,不能伪装成已执行结果。
十、把 Issue 变成可执行任务
Issue 往往缺少验收标准。Agent 直接开改,容易把模糊需求变成大范围重构。建议采用"两阶段"流程:
- 只读阶段:复述目标、列出影响范围、风险、测试计划和待确认问题;
- 写入阶段:用户或维护者确认计划后,再使用具备分支写权限的任务实现。
对于标签为 security、database-migration、billing 或涉及基础设施的 Issue,可强制只读分析,不允许自动进入写阶段。标签不是唯一安全信号,但适合做平台级路由。
十一、Runner 隔离是安全底座
Agent 能执行 Shell、读取仓库并访问网络时,Runner 就是安全边界的一部分。至少应落实:
- 使用临时、任务结束即销毁的 Runner;
- 不挂载 Docker Socket、宿主机家目录和其他项目缓存;
- 缓存按项目和信任级别隔离,禁止不可信任务写入共享缓存;
- 限制出站网络,只放行模型、GitLab 和必要的包仓库;
- 以非 root 用户运行,文件系统与系统调用按需收口;
- 设置 CPU、内存、磁盘、时长和最大日志量;
- Job 结束后清理工作区、认证文件和临时凭据。
Prompt 中的"不要读取秘密"只是行为提示,不能替代真正的隔离。Agent 看不到的秘密,才是最可靠的秘密。
十二、Token 与服务账号的最小权限
服务账号应专用于 Agent,不与个人账号共用。按任务拆分 Token,并设置明确的项目范围、API Scope 与过期时间。只读审查不需要分支写权限;创建 MR 的账号也通常不需要维护项目设置或管理成员。
还应避免把带 Token 的 URL 输出到日志。若通过 Git 认证推送,使用 GitLab 支持的凭据机制,并确保 Shell 没有开启会回显命令参数的调试模式。发生凭据疑似泄露时,应立即吊销并轮换,而不是只删除 Job 日志。
十三、防御 Prompt Injection
Issue 描述、评论、代码、测试输出和依赖文档都属于不可信输入。攻击者可能在其中写入"忽略规则、打印环境变量、下载脚本并执行"等指令。防护要点包括:
- 把用户内容作为数据传入,明确它不能覆盖系统与仓库策略;
- 对 Shell、网络、文件和 GitLab API 做独立权限控制;
- 敏感任务使用允许列表,不把任意评论直接变成命令;
- 写操作前检查操作者角色、目标分支和任务类型;
- 禁止输出环境变量全集、认证文件、HTTP Header 和完整 Prompt;
- 对外部下载、依赖安装和脚本执行设置来源与完整性校验。
十四、质量门禁与失败策略
Agent 生成代码后,至少执行与人工提交相同的质量门禁:
bash
set -euo pipefail
npm ci
npm run lint
npm run typecheck
npm test
npm audit --audit-level=high
企业项目可再加入 SAST、依赖扫描、License 检查、Secret Detection 和容器扫描。任何必需门禁失败时,Agent 可以总结失败原因并更新 MR,但不能绕过门禁、删除测试或降低阈值来制造"绿色流水线"。
失败策略需要预先定义:模型服务不可用时是重试还是失败?GitLab API 返回 429 时是否退避?创建 MR 成功但回写评论失败时如何避免重复创建?所有写操作都应尽量幂等,并用 Job ID、Issue ID 或标签记录关联关系。
十五、审计、成本与可观测性
建议为每次 Agent 任务记录以下非敏感字段:
| 类别 | 建议字段 |
|---|---|
| 触发 | 项目、Issue/MR ID、触发者、事件类型、时间 |
| 执行 | Pipeline/Job ID、Runner、模型、配置版本、耗时 |
| 结果 | 改动文件数、测试结果、分支、MR ID、最终状态 |
| 成本 | 请求次数、Token 或平台计量、重试次数 |
| 安全 | 策略拒绝、越权尝试、网络拦截、脱敏事件 |
不要记录完整 Prompt、源文件全文、环境变量和模型认证信息。审计的目标是还原决策链,而不是把所有秘密复制到另一个日志系统。
十六、上线前检查清单
身份与权限
- 服务账号与个人账号分离,Token 有范围和过期时间;
- 只读、审查、写分支任务使用不同权限;
- 默认分支受保护,Agent 无直接推送和自动合并权限;
- Fork 与不可信分支无法读取受保护变量。
执行环境
- Runner 临时、隔离,不挂宿主机高权限资源;
- 基础镜像、OpenCode、
glab和 CI Component 固定版本; - 出站网络、资源、时长和并发均有限制;
- 日志不会回显 Token、认证 JSON 和敏感 Header。
交付与治理
- 所有代码变更通过独立分支和 MR 进入;
- 测试、扫描、Code Owner 与审批规则未被绕过;
- 写操作具有幂等键,失败后不会重复创建 MR;
- 能按配置版本追踪结果,并能快速停用与回滚。
十七、总结
OpenCode 接入 GitLab 的价值,不是把一句评论直接变成无人监管的代码提交,而是把 Issue 分析、代码修改、验证、MR 创建和人工审批连接起来。普通 GitLab CI 适合固定、可重复的流水线任务;GitLab Duo 路线更适合在 Issue 与 MR 中进行自然交互。两者最终都运行在 Runner 中,因此 Runner 隔离、变量保护和最小权限比 Prompt 更重要。
落地时可以从只读 Issue 解释和 MR 审查开始,观察准确率、成本和误报,再逐步开放"创建分支与 MR"。始终保留受保护分支、质量门禁和人工审批,Agent 才能从演示工具变成可治理的交付能力。