Skip to content

CI/CD Code Generation with OpenAPI Generator — Automate API Code in Pipelines

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about CI/CD Code Generation with OpenAPI Generator. We cover key concepts, practical examples, and best practices to help you master this topic.

CI/CD code generation with OpenAPI Generator automates server stub, client SDK, and documentation generation whenever the OpenAPI specification changes, ensuring every deployment reflects the latest API contract.

What You'll Learn

How to integrate OpenAPI Generator into CI/CD pipelines: GitHub Actions workflows, GitLab CI configuration, Jenkins pipeline integration, Docker-based generation, multi-platform SDK generation, and automated publishing to package registries.

Why It Matters

Manual code generation is error-prone and inconsistent. Automated CI/CD generation ensures every team uses the same spec version, generated code is always up to date, and deployment is blocked if generation fails. DodaTech generates 15 SDKs and deploys updated docs within 5 minutes of spec changes.

Real-World Use

A developer merges a spec change to main. GitHub Actions detects the change, runs OpenAPI Generator for 8 target platforms, builds each SDK, runs integration tests, publishes updated packages to npm, PyPI, and Maven Central, and deploys new API docs to the developer portal.

flowchart LR
    A["Push Spec\nChange"] --> B["CI Trigger"]
    B --> C["Validate\nSpec"]
    C --> D{"Valid?"}
    D -->|"No"| E["Fail Build"]
    D -->|"Yes"| F["Generate\nAll SDKs"]
    F --> G["Build &\nTest"]
    G --> H{"Tests\nPass?"}
    H -->|"No"| I["Report\nError"]
    H -->|"Yes"| J["Publish\nPackages"]
    J --> K["Deploy\nDocs"]
    style F fill:#bbf7d0,stroke:#16a34a
    style K fill:#6cb4ee,color:#fff

GitHub Actions Workflow

# .github/workflows/codegen.yml
name: API Code Generation
on:
  push:
    branches: [main]
    paths:
      - 'specs/openapi.yaml'
  workflow_dispatch:

jobs:
  generate:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        generator:
          - python-fastapi
          - typescript-fetch
          - swift5
          - kotlin

    steps:
      - uses: actions/checkout@v4

      - name: Setup Java
        uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'

      - name: Generate ${{ matrix.generator }}
        uses: openapi-generators/openapitools-generator-action@v1
        with:
          generator: ${{ matrix.generator }}
          openapi-file: specs/openapi.yaml
          config-file: codegen/${{ matrix.generator }}.yaml

      - name: Upload SDK artifact
        uses: actions/upload-artifact@v4
        with:
          name: sdk-${{ matrix.generator }}
          path: generated/

      - name: Build generated code
        run: |
          cd generated
          if [ -f package.json ]; then npm install && npm run build; fi
          if [ -f pom.xml ]; then mvn compile; fi
          if [ -f Package.swift ]; then swift build; fi

# Expected behavior:
# - Triggers on spec changes to main
# - Generates 4 SDKs in parallel
# - Uploads each as a build artifact
# - Verifies generated code compiles

GitLab CI Configuration

# .gitlab-ci.yml
stages:
  - validate
  - generate
  - test
  - publish

variables:
  SPEC_FILE: specs/openapi.yaml
  JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64

validate-spec:
  stage: validate
  script:
    - openapi-generator validate -i $SPEC_FILE
  only:
    changes:
      - specs/openapi.yaml

generate-python:
  stage: generate
  script:
    - openapi-generator generate -i $SPEC_FILE -g python-fastapi -o gen/python/
  artifacts:
    paths:
      - gen/python/

generate-typescript:
  stage: generate
  script:
    - openapi-generator generate -i $SPEC_FILE -g typescript-fetch -o gen/typescript/
  artifacts:
    paths:
      - gen/typescript/

test-generated:
  stage: test
  script:
    - cd gen/python && pip install -r requirements.txt && python -m pytest
    - cd gen/typescript && npm install && npm test
  needs:
    - generate-python
    - generate-typescript

publish:
  stage: publish
  script:
    - npm publish gen/typescript/
    - twine upload gen/python/dist/*
  only:
    - main
  needs:
    - test-generated

Docker-Based Generation

# Dockerfile.codegen
FROM openapitools/openapi-generator-cli:v7.5.0

WORKDIR /workspace

COPY specs/ specs/
COPY codegen/ codegen/

# Generate all targets
RUN for gen in python-fastapi typescript-fetch swift5 kotlin; do \
      openapi-generator generate \
        -i specs/openapi.yaml \
        -g $gen \
        -o /output/$gen \
        -c codegen/$gen.yaml; \
    done

CMD ["echo", "Generation complete"]
# Build and run the Docker codegen image:
docker build -t api-codegen -f Dockerfile.codegen .
docker run --rm -v $(pwd)/output:/output api-codegen

# List generated SDKs:
ls output/
# Expected output:
# python-fastapi/
# typescript-fetch/
# swift5/
# kotlin/

Multi-Platform Publishing

# publish_sdks.py - Automate SDK publishing after generation
import os
import subprocess
import json

def publish_sdk(language, output_dir, registry_config):
    """Publish generated SDK to appropriate package registry."""
    print(f"Publishing {language} SDK from {output_dir}")

    if language == "python":
        # Build and publish to PyPI
        subprocess.run(["python", "setup.py", "sdist", "bdist_wheel"],
                      cwd=output_dir, check=True)
        subprocess.run(["twine", "upload", "dist/*"], cwd=output_dir, check=True)
        print(f"Published to PyPI: {output_dir}")

    elif language == "typescript":
        # Publish to npm
        subprocess.run(["npm", "publish"], cwd=output_dir, check=True)
        print(f"Published to npm: {output_dir}")

    elif language == "swift":
        # Publish Swift package
        tag = f"sdk-{language}-{os.environ.get('VERSION', 'latest')}"
        subprocess.run(["git", "tag", tag], check=True)
        subprocess.run(["git", "push", "origin", tag], check=True)
        print(f"Tagged for SPM: {tag}")

    elif language == "kotlin":
        # Publish to Maven Central
        subprocess.run(["./gradlew", "publish"], cwd=output_dir, check=True)
        print(f"Published to Maven Central: {output_dir}")

# Run in CI pipeline:
# python publish_sdks.py
print("SDK publishing script ready")

Common Mistakes

1. Not Pinning Generator Version

Using latest as the generator version produces non-reproducible builds. Always pin to a specific version: openapitools/openapi-generator-cli:v7.5.0.

2. Ignoring Spec Validation in CI

Generating from an invalid spec produces broken code. Validate the spec as the first step in the pipeline. Fail Fast before spending time on generation.

3. Not Separating Generated from Custom Code

Generated SDKs often include custom business logic. Keep generated files in a gen/ directory, custom code in src/, and add gen/ to .gitignore. Custom changes are not lost on regeneration.

4. Skipping Generated Code Tests

Generated code may not compile. Always run the test suite on generated output. A failed test means the pipeline should not publish the SDK.

5. Publishing Without Version Bump

Every spec change should increment the API version. Use semantic versioning: breaking changes bump major, new features bump minor, fixes bump patch. CI should enforce this.

Practice Questions

  1. Why should you validate the spec before generating code in CI?
  2. How do you generate multiple SDKs in parallel?
  3. What is the benefit of Docker-based generation?
  4. How do you prevent custom code from being overwritten on regeneration?

Answers:

  1. Spec validation catches errors early before time is spent generating code. An invalid spec produces broken output, wasting compute resources and requiring re-runs.
  2. Use CI matrix strategies (GitHub Actions matrix, GitLab parallel jobs) to run multiple generators simultaneously. Each job generates one SDK independently.
  3. Docker-based generation ensures the same generator version and environment across all machines, eliminates dependency installation, and provides reproducible builds.
  4. Separate generated code into a gen/ directory and custom code into src/. Add gen/ to .gitignore. Use separate source directories and import generated modules from custom code.

Challenge: Set up a complete CI/CD pipeline that validates the spec, generates Python FastAPI server, TypeScript Fetch client, and Swift 5 client, tests each generated output, publishes to the appropriate package registry, and blocks deployment if any step fails.

FAQ

How long does CI code generation take?

Generation itself takes 5-30 seconds per generator. Total pipeline time depends on the number of generators, build steps, and tests. A typical pipeline with 5 generators runs in 2-5 minutes.

Can I regenerate code only when the spec changes?

Yes. Use CI path filters to trigger the pipeline only when spec files change. GitHub Actions uses paths, GitLab uses only/except changes.

How do I handle multiple spec files for multiple APIs?

Create separate pipeline jobs for each API spec, or use a monorepo approach where each API has its own directory with spec and generator config.

What if generated code has compilation errors?

The pipeline should fail. Run compile/test steps after generation. File a bug against the generator or fix your spec. Never publish broken generated code.

Should I commit generated code to the repository?

No. Generated code should be treated as build artifacts. Commit only the spec and generator config. Generated code is rebuilt during CI and published to package registries.

Mini Project

Create a GitHub Actions workflow for a sample API that generates Python FastAPI server, TypeScript Fetch client, and HTML docs, runs validation and testing on each generated output, publishes the client SDK to npm, and deploys the docs to GitHub Pages.

What's Next

Version Management — manage API specification versions across releases.

Complete Project — build a full code generation system with CI/CD.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro