在 GitLab CI/CD 里跑 Claude Code:与 GitHub Actions 不通用的那几处

2026-08-18

如果你的代码托管在自建或 SaaS 版 GitLab 上,想让 Claude Code 参与流水线,第一件会撞墙的事是:网上能搜到的绝大多数配置都是 GitHub Actions 的写法,而那套写法在 GitLab 上一行都用不了。

不是语法差异那么简单。GitHub 那边官方文档给的是一个现成的 Action——工作流里写 uses: anthropics/claude-code-action@v1,再用 with:promptclaude_argsanthropic_api_key 这些输入项,剩下的事情由 Action 内部完成。GitLab 这边,官方文档《Claude Code GitLab CI/CD》页给出的形态完全不同:没有可以 uses 的封装,你在 .gitlab-ci.yml 里写一个普通 job,自己把 CLI 装进容器,自己拼命令行。所以两边的”配置字段”根本不是同一批东西——GitHub 侧你填的是 Action 的输入项,GitLab 侧你填的是 GitLab 的 job 关键字加环境变量。

这篇就把 GitLab 侧真正要写的那些字段逐个过一遍。

前置条件:先确认这几件事成立

第一,这是 beta。 官方文档在这一页顶部明确写着 Claude Code for GitLab CI/CD 目前处于 beta,功能与行为可能继续调整。同一段还写明:这个集成由 GitLab 维护,寻求支持的入口是文档里给出的那条 GitLab issue,而不是 Anthropic 的渠道。这一点值得在立项前就跟团队说清楚,它决定了出问题时你去哪里提问。

第二,你要有改 .gitlab-ci.yml 的权限,以及往项目里加 masked CI/CD 变量的权限。 官方文档把加变量这步写成在项目设置里添加 masked 变量,原文给出的位置是 SettingsCI/CDVariables,变量名 ANTHROPIC_API_KEY,并注明可以按需要再设为 protected。

第三,选定 provider。 这一页列出的运行方式有三种:Claude API(SaaS)、Amazon Bedrock(基于 IAM 的访问)、Google Cloud’s Agent Platform(GCP 原生、走 Workload Identity Federation)。后两种在文档里各自列了一段前置条件,核心是要先把 OIDC 身份联合配好,后面单独说。

第四,别指望”评论里 @claude 就会自动跑”是开箱即用的。 官方文档在手动配置一节把它列为可选步骤,并写明做法是:给项目加一个 “Comments (notes)” 的 webhook 指向你自己的事件监听器,由这个监听器在检测到评论包含 @claude 时去调 GitLab 的 pipeline trigger API,并带上 AI_FLOW_INPUTAI_FLOW_CONTEXT 这类变量。也就是说,mention 触发这一环需要你自己搭一个服务,文档里那句 “if you use one”(如果你用了监听器)就是这个意思。

步骤:每个字段在改什么

官方文档的快速配置给的是一个最小 job。下面按它给出的字段逐个说明改的是什么:

stages:
  - ai

claude:
  stage: ai
  image: node:24-alpine3.21
  # Adjust rules to fit how you want to trigger the job:
  # - manual runs
  # - merge request events
  # - web/API triggers when a comment contains '@claude'
  rules:
    - if: '$CI_PIPELINE_SOURCE == "web"'
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
  variables:
    GIT_STRATEGY: fetch
  before_script:
    - apk update
    - apk add --no-cache git curl bash
    - curl -fsSL https://claude.ai/install.sh | bash
    # The installer places claude in ~/.local/bin, which isn't on PATH in this image
    - export PATH="$HOME/.local/bin:$PATH"
  script:
    # Optional: start a GitLab MCP server if your setup provides one
    - /bin/gitlab-mcp-server || true
    # Use AI_FLOW_* variables when invoking via web/API triggers with context payloads
    - echo "$AI_FLOW_INPUT for $AI_FLOW_CONTEXT on $AI_FLOW_EVENT"
    - >
      claude
      -p "${AI_FLOW_INPUT:-'Review this MR and implement the requested changes'}"
      --permission-mode acceptEdits
      --allowedTools "Bash Read Edit Write mcp__gitlab"
      --debug
  • image:这是你自己指定的运行环境。GitHub 那边的 Action 自带运行逻辑,GitLab 这边容器里有什么全看你写什么。官方示例用的是一个 Node 的 Alpine 镜像。
  • rules:决定这个 job 什么时候进流水线。示例给的两条按 $CI_PIPELINE_SOURCE 判断——"web"(网页/API 触发)和 "merge_request_event"(MR 事件)。注释里还提到手动运行这一路。这里就是”触发条件”的落点:GitHub 侧靠 Action 的 trigger_phrase 之类输入,GitLab 侧靠你写 rules 加外部触发
  • variables: GIT_STRATEGY: fetch:GitLab 的仓库拉取策略关键字,写在 job 级 variables 下。
  • before_script:装依赖和装 CLI。示例先 apk updateapk add --no-cache git curl bash,再 curl -fsSL https://claude.ai/install.sh | bash
  • 紧跟的那行 export PATH="$HOME/.local/bin:$PATH" 是最容易漏的一行。 官方注释直接写明了原因:安装器把 claude 放在 ~/.local/bin,而这个目录在该镜像里不在 PATH 上。漏了这行,后面调 claude 就是找不到命令。
  • script 里的 /bin/gitlab-mcp-server || true:官方注释写的是”可选:如果你的环境提供了 GitLab MCP server 就启动它”。这个二进制从哪来、怎么装,这一页没有说明这一点|| true 是 shell 的写法,作用是这条命令返回非零时整个 script 不因此中断,官方文档没有解释为什么要这样写。
  • AI_FLOW_INPUT / AI_FLOW_CONTEXT / AI_FLOW_EVENT:官方注释写明这组变量用于”通过 web/API 触发并带上下文负载”的场景。它们不是 GitLab 内置变量,是前面那个自建监听器调 trigger API 时塞进来的。示例里 -p "${AI_FLOW_INPUT:-'...'}" 用了 shell 的默认值写法,没有外部输入时退回到一句固定提示词。
  • 命令行部分:-p 传指令、--permission-mode acceptEdits--allowedTools "Bash Read Edit Write mcp__gitlab"--debug。注意 这一页并没有解释 acceptEdits 这个 permission mode 的具体语义,只是把它写在示例里,要弄清它允许什么、拦什么,得去查权限相关的文档页。

变量这一侧,官方文档在手动配置一节还写明了 GitLab API 操作的凭证:默认用 CI_JOB_TOKEN,或者创建一个带 api scope 的 Project Access Token,用 PAT 时以 GITLAB_ACCESS_TOKEN 存成 masked 变量。

走 Bedrock 或 Google Cloud 时多出来的字段

这两条路的共同点是不存长期密钥,靠 GitLab 的 OIDC token 去换云厂商凭证。GitLab 侧新出现的关键字是 id_tokens:

  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.example.com

官方文档写明:GitLab 由这个 id_tokens: 块签发 job 的 OIDC token 并以 GITLAB_OIDC_TOKEN 暴露出来;aud 要填你在云侧那个 OIDC 身份提供方上配置的 audience 值,文档举的例子是你的 GitLab 实例地址。这个值两边必须对得上。

Amazon Bedrock 那条路要的 CI/CD 变量是 AWS_ROLE_TO_ASSUME(IAM role 的 ARN)和 AWS_REGION,job 的 variables 里再置 CLAUDE_CODE_USE_BEDROCK: "1"。示例的 before_script 把 token 写进 /tmp/oidc_token,用 aws sts assume-role-with-web-identity 换出临时凭证,再 jq 解出三个 AWS_* 环境变量。

Google Cloud’s Agent Platform 那条路要的是 GCP_WORKLOAD_IDENTITY_PROVIDER(文档特别注明:填不带 //iam.googleapis.com/ 前缀的 provider 资源名)、GCP_SERVICE_ACCOUNTGCP_PROJECT_IDCLOUD_ML_REGION,job variables 里置 CLAUDE_CODE_USE_VERTEX: "1"ANTHROPIC_VERTEX_PROJECT_ID。示例把一份 type: external_account 的凭证配置写到 /tmp/cred.json,其中 credential_source.file 指向前面那个 token 文件,再用 GOOGLE_APPLICATION_CREDENTIALS 指向它,让凭证通过 Application Default Credentials 被读到。

以上均为官方文档中给出的示例片段,我们没有做过实测;参数语义以官方文档与 claude --help 的实际输出为准。

边界:这一页没有覆盖的地方

Windows。 官方这一页给的三个 job 示例,两个跑在 Alpine 容器里(apk),一个跑在 Google Cloud CLI 的镜像里(apt-get),装 CLI 用的都是同一条 shell 安装脚本。在 Windows runner 上怎么装、怎么跑,官方文档没有说明这一点。 如果你的 GitLab runner 是 Windows executor,照抄这两段不成立,得先确认能不能改用 Linux runner 或容器。另外,本机在 Windows 上编辑 .gitlab-ci.yml 与 runner 是什么系统是两回事——编辑器的换行符与编码设置属于通用工程注意事项,非本页官方内容,这里只提醒一句。

订阅制凭证。 GitHub Actions 那一页写明了两种认证方式,除 API key 外还有 CLAUDE_CODE_OAUTH_TOKEN(用订阅认证的 OAuth token,由 claude setup-token 生成)。GitLab 这一页只写了 ANTHROPIC_API_KEY,我们在其中没有找到订阅 token 的对应说明。

谁能触发。 GitHub 那一页专门有一节讲触发者校验:写权限检查、机器人检查、allowed_botsallowed_non_write_usersGitLab 这一页没有写对应机制,它讲的安全边界是另一套表述:文档自述每次交互跑在有网络与文件系统规则约束的容器里、Claude Code 施加工作区范围的写入权限、所有改动经由 MR 让评审者看到 diff 且审批流程照常生效。这是文档的说法,不等于你可以省掉自己的评审——同一页的安全建议里也写着”像对待任何其他贡献者一样评审 Claude 的 MR”,以及限制 job 权限与网络出口。具体权限边界请以权限文档能核到的部分为准。

Microsoft Foundry。 GitHub Actions 那一页列了它,GitLab 这一页的 provider 里没有出现,我们没有找到对应说明。

一处口径不一致,值得单独记一笔。 GitHub Actions 页把 --allowedTools 描述为”逗号分隔的工具列表”,而 GitLab 页的三个示例里写的都是 --allowedTools "Bash Read Edit Write mcp__gitlab",空格分隔。两处白纸黑字就是这样写的,这里只把它们并排指出来。真要落地时,以 claude --help 在你那个版本上的实际输出为准——GitLab 页自己也有一条注记说明:确切的 flag 与参数可能随版本变化,建议在 job 里跑 claude --help 看支持哪些选项。

控量的字段官方也点了名:--max-turns 限制来回轮次,GitLab 的 job 级 timeout 关键字限制总执行时长(文档举的写法是 timeout: 30m),再配合并发限制控制并行运行数。

怎么验证配对了

官方文档给的第一步验证很直接:加完 job 和变量后,从流水线页面手动跑一次这个 job(原文写的位置是 CI/CDPipelines),或者从一个 MR 触发它。示例里已经带了 --debug,日志能给你更多线索。

排障时按官方 troubleshooting 的三组对照着看:

现象官方文档给的检查点
@claude 没反应确认流水线确实被触发了(手动、MR 事件,或经由 note 事件监听器/webhook);确认 ANTHROPIC_API_KEY 或云厂商变量存在;确认评论里是 @claude 而不是 /claude,且 mention 触发已配置
job 不能发评论或开 MR确认 CI_JOB_TOKEN 在该项目上权限足够,或改用带 api scope 的 Project Access Token;确认 --allowedTools 里启用了 mcp__gitlab;确认 job 跑在 MR 上下文里,或通过 AI_FLOW_* 变量拿到了足够上下文
认证报错Claude API:确认 ANTHROPIC_API_KEY 有效未过期。Bedrock / Google Cloud’s Agent Platform:核对 OIDC/WIF 配置、role 与服务账号模拟、变量名,以及区域与模型可用性

第二条那个”确认 --allowedTools 里有 mcp__gitlab”值得和前面那行 /bin/gitlab-mcp-server || true 并排读:官方文档把”job 不能发评论或开 MR”这个现象下的检查点写成了三条并列——token 权限、--allowedTools 里有没有 mcp__gitlab、job 有没有拿到 MR 上下文。也就是说,同一个现象在文档口径里对应三处不同的配置,排查时三处都要过一遍,不能只看到第一条就收工。至于每一处配错时日志会打印什么,这一页没有说明这一点。

最后一句实话:这一页目前是 beta,且由 GitLab 维护。把它放进关键流水线之前,先按上面的字段清单把每一项在你自己的项目里核一遍,而不是把示例整段贴进去等它自己跑通。


本文依据 Claude Code 官方文档(code.claude.com/docs)于 2026-08-17 的公开内容整理。 该产品闭源,本文只复述官方文档写明的机制,不推断其内部实现我们没有对文中涉及的功能做过实测,因此不涉及界面外观、操作手感与运行速度的任何描述。 该产品迭代频繁,文中涉及的命令、配置项与默认值随版本变动,请以官方文档最新内容为准。 本文不涉及价格、额度与限流的具体数值,相关信息请以官方定价与用量说明页为准。

安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。