Module 3: GitLab CI/CD and Headless SDK

Module 3: GitLab CI/CD and Headless SDK

Module 3: GitLab CI/CD and Headless SDK

Overview

Modules 1-2 gave you Claude Code operating in GitHub Actions: working workflows, automatic code review with inline comments. But there's a detail: everything you built is tied to GitHub. If your team migrates to GitLab, if you work with a client that uses Bitbucket, if you need to run the same analysis in Jenkins — you'd have to rewrite everything.

This module solves that problem by showing an abstraction layer: the Claude Code headless SDK. The lesson isn't "how to use GitLab" — it's portability. You write the logic once in Python or TypeScript using the SDK, and you run it on any CI/CD platform: GitHub Actions, GitLab CI/CD, Jenkins, CircleCI, whatever comes next.

GitLab CI/CD is the case study because it has significant adoption (~25% of the market, dominant in enterprise and self-hosted) and because its stages/jobs model is different enough from GitHub Actions that the portability is genuine, not a copy-paste with syntax changes.

By the end of the 5 capsules, you'll have working GitLab CI/CD pipelines with Claude Code integrated, you'll understand why the headless SDK is the right abstraction, and you'll be able to port your logic to any future platform.


Where We Are in the Guide

Phase 1: Pre-Merge Automation (Modules 1-2)
├── Module 1: Claude Code in GitHub Actions ✅
└── Module 2: Automated Code Review on PRs ✅

Phase 2: Cross-Platform and Deployment (Modules 3-4)
├── Module 3: GitLab CI/CD and Headless SDK ← YOU ARE HERE
│   → Portability, GitLab pipelines, SDK as abstraction
└── Module 4: Deployment Automation

Phase 3: Resilience and Project (Modules 5-6)
├── Module 5: Security Scanning and Rollback
└── Module 6: Integrative Project — Complete Pipeline

This is Module 3 of 6 — the transition from "Claude Code in GitHub" to "Claude Code on any platform". Modules 4-5 go back to using GitHub Actions as the main example, but the techniques you learn here apply universally.


The Real Point: Portability

There's a useful analogy for understanding why this module matters beyond GitLab:

WITHOUT THE HEADLESS SDK:
→ Your code review logic is in GitHub Actions YAML
→ If you migrate to GitLab: rewrite everything in GitLab CI/CD YAML
→ If you later use Jenkins: rewrite everything in Groovy
→ The business logic (what to analyze, how) gets mixed with
  the platform logic (how to orchestrate)

WITH THE HEADLESS SDK (this module):
→ You write the logic once: review.py or review.ts
→ Each platform's YAML only orchestrates: "run this
  script in this step"
→ Changing platform = changing the YAML, not the logic
→ Clear separation: the script has the intelligence, the YAML
  only triggers it

The headless SDK is to Claude Code what a cloud provider's SDK is to infrastructure. It doesn't tie you to a platform — it abstracts you above it.


A Real Decision: Enterprise Client

To anchor the module, consider a typical case for a freelance developer or consultant:

Situation: You work with two clients. Client A uses GitHub Actions, Client B uses GitLab self-hosted (enterprise). Both want the same code review bot you built in Module 2.

Approach A: Rewrite everything

For Client A (GitHub):
- Complete workflow YAML in .github/workflows/
- Diff extraction logic embedded in YAML
- Calls to the GitHub API embedded in bash
- ~150 lines of YAML

For Client B (GitLab):
- Complete pipeline YAML in .gitlab-ci.yml
- Diff extraction logic REWRITTEN (CI_MERGE_REQUEST_*
  variables are different from GITHUB_*)
- Calls to the GitLab API REWRITTEN (different structure)
- ~150 lines of YAML

Total time: 2× — you wrote everything twice
Maintenance: 2× — any improvement goes to both sides
Specific bugs: show up in one and not the other

Approach B: Headless SDK (this module)

Shared logic (written once):
- review.py: ~80 lines that use the Claude Code SDK
- Takes the diff as an argument (doesn't assume the platform)
- Takes the repo URL as an argument
- Returns a JSON structure with the findings

For Client A (GitHub):
- Workflow YAML of ~25 lines that:
  → checkout
  → installs the SDK
  → runs `python review.py`
  → publishes results via the GitHub API

For Client B (GitLab):
- Pipeline YAML of ~25 lines that:
  → runs the same `python review.py`
  → publishes results via the GitLab API

Total time: 1× logic + 2× thin orchestration
Maintenance: improvements to the logic benefit both clients
Bugs: if it works on one, it works on the other

Same capability. Same agent. Difference: the business logic lives in a Python script, not in platform YAML.

This principle scales beyond GitHub vs GitLab — if a new CI/CD platform appears in 2 years, your logic keeps working.


Prerequisites

Required knowledge:

  • ✅ Modules 1-2 completed (Claude Code in GitHub Actions with code review)
  • ✅ Python or TypeScript at an intermediate level
  • ✅ Familiarity with Docker (GitLab jobs run in containers)

Recommended:

  • ✅ Access to GitLab.com (free account) or GitLab self-hosted
  • ✅ Experience with virtual environments (Python) or package managers (npm/pnpm)

NOT required:

  • ❌ You don't need to have configured a GitLab pipeline before
  • ❌ You don't need to know Jenkins or other platforms (the mentions are referential)

Module Roadmap

Capsule 01 — Module introduction (this capsule)

Why portability matters. The two-clients scenario.

Capsule 02 — Headless SDK: Python and TypeScript

Anatomy of the Claude Code SDK. How it's invoked from a script. Differences between the Python SDK and the TypeScript one. When to choose each.

Capsule 03 — GitLab CI/CD: stages, jobs, artifacts

The key differences between GitHub Actions (workflows + jobs + steps) and GitLab CI/CD (pipelines + stages + jobs). Specific environment variables. Artifacts. Docker executor.

Capsule 04 — GitLab pipeline with the headless SDK

Your first working GitLab CI/CD pipeline running the SDK script. Configuring the Docker executor with dependencies. Handling secrets in GitLab.

Capsule 05 — Project: Porting the Module 2 bot to GitLab

Take the code review bot from Module 2 (which lives in GitHub Actions YAML), refactor the logic to the headless SDK, and demonstrate that the same script runs on both platforms.

Progression map

Capsule 01 (this)  → Why portability
Capsule 02         → Headless SDK explained
Capsule 03         → GitLab CI/CD vs GitHub Actions
Capsule 04         → Working GitLab pipeline
Capsule 05         → Project: ported bot

Difficulty: ⭐⭐⭐ ──────────▶ ⭐⭐⭐⭐

What You'll Achieve in This Module

By completing the 5 capsules, you'll be able to:

  1. Write scripts in Python or TypeScript that invoke the Claude Code headless SDK
  2. Distinguish between GitHub Actions and GitLab CI/CD at the architectural level (not just syntactic)
  3. Configure GitLab CI/CD pipelines with stages appropriate for Claude Code
  4. Handle secrets and variables in GitLab (CI/CD variables, masked, protected)
  5. Demonstrate portability by running the same script on both platforms
  6. Make architectural decisions about where to put the logic (script vs YAML)

The before and after

BEFORE the module:
→ "My Claude Code logic is in YAML"
→ "Changing platform means rewriting everything"
→ "GitLab and GitHub are basically the same"

AFTER the module:
→ Logic in the SDK script, orchestration in YAML
→ Changing platform = changing 25 lines of YAML
→ The architectural differences between GitHub and GitLab
  are clear, and I know how to abstract them

Pitfalls to Avoid While Taking This Module

Five predictable misunderstandings. Anticipate them before you start.

1. "I'll copy the GitHub Actions YAML and change the syntax"

It doesn't work. The models are different: GitHub uses workflow → job → step, GitLab uses pipeline → stage → job. The environment variables are different (GITHUB_* vs CI_*). Artifacts and caches operate differently. Translating line by line gives fragile pipelines. Capsule 03 explains the models, not just the syntax.

2. "The headless SDK is only for advanced automation"

No. The headless SDK is the right way to use Claude Code in any script — not just in CI/CD. It's the API that gives you programmatic control: you pass the context, specify the prompt, receive the response as structured data. The interactive mode (terminal) is for humans; the SDK is for automation. Capsule 02 develops this.

3. "I'll put all the logic in each platform's YAML"

The opposite of the module. All business logic in the script, all orchestration logic in the YAML. If your YAML has more than 50 lines, it's probably doing too much. Capsule 04 shows the right balance: thin YAML that only invokes rich scripts.

4. "GitLab isn't important because I use GitHub"

That's exactly the pitfall the module avoids. It's not about GitLab — it's about your logic not being tied to a platform. Even if you use GitHub forever, writing the logic with the SDK gives you: easier testing (you can run the script locally), easier debugging (you don't depend on the runner), and future-proofing.

5. "The Python SDK and the TypeScript SDK are interchangeable"

They have feature parity, but there are differences. Python is more common in data/ML/scripts; TypeScript is more natural in JS/TS projects. The types are more expressive in TypeScript; the ecosystem is richer in Python. Capsule 02 helps you choose based on your context and stack.


Diagnosis: What's Your Starting Point?

Five questions to calibrate before you start.

Question 1: Have you used the Claude Code headless SDK before (Guide 9)?

If yes: you have the foundation. This module applies the SDK to the specific context of CI/CD.

If no: review Guide 9 Module 6 first — the SDK is a technical prerequisite of this module.

Question 2: Have you configured a GitLab CI/CD pipeline before?

If yes: capsule 03 gives you a comparative view with GitHub Actions.

If no: capsule 03 teaches you from scratch, focusing on what differs from GitHub.

Question 3: If you had to run the same script in GitHub Actions and GitLab CI/CD, what would you change?

If you said "the YAML, not the script": you have the right mental model.

If you said "the script's logic": pitfall #3 applies to you. Capsule 04 develops the separation.

Question 4: Do you know what a Docker executor is in GitLab CI/CD?

If yes: capsule 04 shows you how to configure it for the SDK.

If no: GitLab jobs run in Docker containers. Capsule 04 teaches you how the container brings in the dependencies.

Question 5: Why would it matter to write logic in the SDK instead of YAML, even if you only use one platform?

If you have reasons: capsules 02 and 04 reinforce them.

If not: the main reasons — local testing, debugging, future-proofing, architectural clarity. Capsule 02 develops each one.

If you hesitated on 3 or more: this module fills important gaps for the next ones. If you answered all of them confidently, use it focused on capsule 05 (the project), which is where portability gets internalized.


Connection with the Final Project

In Module 6 (Integrative Project), the complete pipeline is built primarily in GitHub Actions, but the portability demonstrated here allows adapting it to GitLab or any other platform. The module's lesson is that your technical investment isn't tied to a specific platform.


How to Work Through This Module

  1. Capsule 02 is the technical foundation. Without understanding the SDK, capsules 04-05 can't be executed.
  2. Capsule 03 is strategic. It gives you the mental model to distinguish GitHub Actions from GitLab beyond the syntax.
  3. Capsule 04 is the concrete practice. Read it with a GitLab account open.
  4. Capsule 05 internalizes the concept. Porting the bot to GitLab is where portability is felt.

Estimated time:

Capsule 01 (this)  →  10 min reading
Capsule 02         →  20 min + practice with the SDK
Capsule 03         →  15 min comparative reading
Capsule 04         →  25 min + GitLab setup
Capsule 05         →  30 min + porting the bot

Total: ~1.5-2 hours

Evidence of Success

Before advancing to Module 4 (Deployment Automation), you should be able to:

  • ✅ Write a script that uses the Claude Code headless SDK for a specific task
  • ✅ Compare the architectures of GitHub Actions and GitLab CI/CD (not just syntax)
  • ✅ Configure a GitLab CI/CD pipeline with a Docker executor and the SDK
  • ✅ Handle variables and secrets in GitLab securely
  • ✅ Run the same script in GitHub Actions and GitLab CI/CD with minimal changes to the YAML
  • ✅ Consciously decide where to put each piece: logic in the script, orchestration in the YAML

If any of these isn't met by the end, go back to the corresponding capsule. Module 4 (deployment) assumes you already understand the logic-script / orchestration-YAML separation.


Summary

  • This module gives you cross-platform portability via the headless SDK
  • GitLab CI/CD is the case study, not the destination — the principle applies universally
  • SDK = logic, YAML = orchestration is the right architectural separation
  • The Docker executor is the enabler in GitLab — the container brings all the dependencies
  • Your Claude Code logic shouldn't be tied to a specific platform
  • The difference between "rewrite everything twice" and "logic once, thin orchestration" in the opening scenario is exactly this module

Next capsule: 02 — Headless SDK: Python and TypeScript. We start by understanding the SDK as a programmatic interface to Claude Code — the technical foundation on which we build portability. Without this, capsules 04-05 are execution without understanding what's happening underneath.


Additional Resources

  1. Claude Code SDK Documentation — Official SDK reference
  2. GitLab CI/CD Documentation — Complete official documentation
  3. GitLab CI/CD Variables — How variables and secrets are handled
  4. GitLab Docker Executor — Executor configuration
  5. GitHub Actions vs GitLab CI/CD — GitLab's official migration guide
  6. Anthropic Python SDK — Official Python SDK repository
  7. Anthropic TypeScript SDK — Official TypeScript SDK repository