Table of Contents

CI/CD Integration

JD.Efcpt.Build is designed to work seamlessly in continuous integration and deployment pipelines. This guide covers integration with popular CI/CD platforms.

Overview

The package requires no special configuration for CI/CD. Models are generated deterministically from your database project or connection, ensuring consistent results across environments.

Prerequisites

Ensure your CI/CD environment has:

  • .NET SDK 8.0 or later
  • EF Core Power Tools CLI (not required for .NET 10+)
  • For DACPAC mode: SQL Server Data Tools components

GitHub Actions

No tool installation required - the CLI is executed via dnx:

name: Build

on: [push, pull_request]

jobs:
  build:
    runs-on: windows-latest

    steps:
    - uses: actions/checkout@v4

    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: '10.0.x'

    - name: Restore dependencies
      run: dotnet restore

    - name: Build
      run: dotnet build --configuration Release --no-restore

    - name: Test
      run: dotnet test --configuration Release --no-build

.NET 8-9

Requires tool installation:

name: Build

on: [push, pull_request]

jobs:
  build:
    runs-on: windows-latest

    steps:
    - uses: actions/checkout@v4

    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: '8.0.x'

    - name: Restore tools
      run: dotnet tool restore

    - name: Restore dependencies
      run: dotnet restore

    - name: Build
      run: dotnet build --configuration Release --no-restore

    - name: Test
      run: dotnet test --configuration Release --no-build

With Caching

Speed up builds by caching the efcpt intermediate directory:

name: Build with Cache

on: [push, pull_request]

jobs:
  build:
    runs-on: windows-latest

    steps:
    - uses: actions/checkout@v4

    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: '10.0.x'

    - name: Cache efcpt outputs
      uses: actions/cache@v4
      with:
        path: |
          **/obj/efcpt/
        key: efcpt-${{ runner.os }}-${{ hashFiles('**/*.sqlproj', '**/efcpt-config.json') }}
        restore-keys: |
          efcpt-${{ runner.os }}-

    - name: Restore dependencies
      run: dotnet restore

    - name: Build
      run: dotnet build --configuration Release --no-restore

    - name: Test
      run: dotnet test --configuration Release --no-build

Azure DevOps

Basic Pipeline

trigger:
  - main

pool:
  vmImage: 'windows-latest'

steps:
- task: UseDotNet@2
  displayName: 'Setup .NET SDK'
  inputs:
    version: '10.0.x'

- task: DotNetCoreCLI@2
  displayName: 'Restore'
  inputs:
    command: 'restore'

- task: DotNetCoreCLI@2
  displayName: 'Build'
  inputs:
    command: 'build'
    arguments: '--configuration Release --no-restore'

- task: DotNetCoreCLI@2
  displayName: 'Test'
  inputs:
    command: 'test'
    arguments: '--configuration Release --no-build'

With Tool Manifest (.NET 8-9)

trigger:
  - main

pool:
  vmImage: 'windows-latest'

steps:
- task: UseDotNet@2
  displayName: 'Setup .NET SDK'
  inputs:
    version: '8.0.x'

- task: DotNetCoreCLI@2
  displayName: 'Restore tools'
  inputs:
    command: 'custom'
    custom: 'tool'
    arguments: 'restore'

- task: DotNetCoreCLI@2
  displayName: 'Restore'
  inputs:
    command: 'restore'

- task: DotNetCoreCLI@2
  displayName: 'Build'
  inputs:
    command: 'build'
    arguments: '--configuration Release --no-restore'

- task: DotNetCoreCLI@2
  displayName: 'Test'
  inputs:
    command: 'test'
    arguments: '--configuration Release --no-build'

With Caching

trigger:
  - main

pool:
  vmImage: 'windows-latest'

variables:
  NUGET_PACKAGES: $(Pipeline.Workspace)/.nuget/packages

steps:
- task: Cache@2
  displayName: 'Cache NuGet packages'
  inputs:
    key: 'nuget | "$(Agent.OS)" | **/packages.lock.json'
    restoreKeys: |
      nuget | "$(Agent.OS)"
    path: $(NUGET_PACKAGES)

- task: Cache@2
  displayName: 'Cache efcpt outputs'
  inputs:
    key: 'efcpt | "$(Agent.OS)" | **/*.sqlproj | **/efcpt-config.json'
    restoreKeys: |
      efcpt | "$(Agent.OS)"
    path: '**/obj/efcpt'

- task: UseDotNet@2
  inputs:
    version: '10.0.x'

- task: DotNetCoreCLI@2
  displayName: 'Restore'
  inputs:
    command: 'restore'

- task: DotNetCoreCLI@2
  displayName: 'Build'
  inputs:
    command: 'build'
    arguments: '--configuration Release --no-restore'

Docker

Multi-Stage Dockerfile

# Build stage
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src

# Copy solution and project files
COPY *.sln .
COPY src/**/*.csproj ./src/
COPY database/**/*.sqlproj ./database/

# Restore dependencies
RUN dotnet restore

# Copy everything else
COPY . .

# Build
RUN dotnet build --configuration Release --no-restore

# Publish
RUN dotnet publish src/MyApp/MyApp.csproj --configuration Release --no-build -o /app/publish

# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]

With Tool Manifest (.NET 8-9)

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# Copy tool manifest and restore tools
COPY .config/dotnet-tools.json .config/
RUN dotnet tool restore

# Copy and restore
COPY *.sln .
COPY src/**/*.csproj ./src/
COPY database/**/*.sqlproj ./database/
RUN dotnet restore

# Copy everything and build
COPY . .
RUN dotnet build --configuration Release --no-restore

# Publish
RUN dotnet publish src/MyApp/MyApp.csproj --configuration Release --no-build -o /app/publish

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]

GitLab CI

stages:
  - build
  - test

variables:
  DOTNET_VERSION: "10.0"

build:
  stage: build
  image: mcr.microsoft.com/dotnet/sdk:10.0
  script:
    - dotnet restore
    - dotnet build --configuration Release --no-restore
  artifacts:
    paths:
      - "**/bin/"
      - "**/obj/"
    expire_in: 1 hour

test:
  stage: test
  image: mcr.microsoft.com/dotnet/sdk:10.0
  dependencies:
    - build
  script:
    - dotnet test --configuration Release --no-build

Jenkins

Jenkinsfile (Declarative)

pipeline {
    agent {
        docker {
            image 'mcr.microsoft.com/dotnet/sdk:10.0'
        }
    }

    stages {
        stage('Restore') {
            steps {
                sh 'dotnet restore'
            }
        }

        stage('Build') {
            steps {
                sh 'dotnet build --configuration Release --no-restore'
            }
        }

        stage('Test') {
            steps {
                sh 'dotnet test --configuration Release --no-build'
            }
        }
    }
}

Connection String Mode in CI/CD

When using connection string mode, you'll need a database available during build.

Using Environment Variables

# GitHub Actions
env:
  DB_CONNECTION_STRING: ${{ secrets.DB_CONNECTION_STRING }}

steps:
- name: Build
  run: dotnet build --configuration Release
<!-- .csproj -->
<PropertyGroup>
  <EfcptConnectionString>$(DB_CONNECTION_STRING)</EfcptConnectionString>
</PropertyGroup>

Using a Container Database

# GitHub Actions with SQL Server container
services:
  sqlserver:
    image: mcr.microsoft.com/mssql/server:2022-latest
    env:
      ACCEPT_EULA: Y
      SA_PASSWORD: YourStrong!Passw0rd
    ports:
      - 1433:1433
    options: >-
      --health-cmd "/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P YourStrong!Passw0rd -Q 'SELECT 1'"
      --health-interval 10s
      --health-timeout 5s
      --health-retries 5

steps:
- name: Setup database
  run: |
    sqlcmd -S localhost -U sa -P YourStrong!Passw0rd -i scripts/setup.sql

- name: Build
  env:
    EfcptConnectionString: "Server=localhost;Database=MyDb;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;"
  run: dotnet build --configuration Release

Windows vs Linux Agents

DACPAC Mode Requirements

Modern SDK-style SQL Projects (Microsoft.Build.Sql or MSBuild.Sdk.SqlProj) build cross-platform:

# GitHub Actions - Linux works with modern SQL SDKs
jobs:
  build:
    runs-on: ubuntu-latest  # Works with Microsoft.Build.Sql and MSBuild.Sdk.SqlProj

Traditional SQL Projects (legacy .sqlproj format) require Windows with SQL Server Data Tools:

# GitHub Actions - Windows required for traditional .sqlproj
jobs:
  build:
    runs-on: windows-latest

Connection String Mode

Connection string mode works on both Windows and Linux:

# GitHub Actions - Any platform works
jobs:
  build:
    runs-on: ubuntu-latest

Troubleshooting CI/CD

Build fails with "efcpt not found"

For .NET 8-9, ensure tool restore runs before build:

- name: Restore tools
  run: dotnet tool restore

DACPAC build fails

For traditional SQL Projects, use Windows with SQL Server Data Tools:

pool:
  vmImage: 'windows-latest'

For modern SDK-style projects (Microsoft.Build.Sql or MSBuild.Sdk.SqlProj), Linux works fine - verify your project SDK is configured correctly.

Inconsistent generated code

Clear the cache to force regeneration:

- name: Clear efcpt cache
  run: rm -rf **/obj/efcpt

Slow builds

Enable caching for the efcpt intermediate directory to skip regeneration when schema hasn't changed.

Best Practices

  1. Use .NET 10+ when possible to eliminate tool installation steps
  2. Use local tool manifests (.NET 8-9) for version consistency
  3. Cache intermediate directories to speed up incremental builds
  4. Use modern SQL SDKs (Microsoft.Build.Sql or MSBuild.Sdk.SqlProj) for cross-platform DACPAC builds
  5. Use environment variables for connection strings
  6. Never commit credentials to source control

efcpt Compatibility

The Provider Compatibility workflow runs the task unit suite on .NET 8, 9, and 10 on Windows, Linux, and macOS. Each of its nine jobs checks passing connection and schema-reader tests for SQL Server, PostgreSQL, MySQL, Oracle, Snowflake, Firebird, and SQLite, covering 63 provider/runtime/OS combinations. TRX reports are uploaded as artifacts; missing or skipped provider cases fail verification.

This matrix exercises driver loading, connection construction, and schema-reader logic. Live database tests remain in the Linux integration suite; Snowflake live tests require an account. The separate upstream-tool workflow below tests actual efcpt execution.

To reproduce one runtime locally:

dotnet test tests/JD.Efcpt.Build.Tests/JD.Efcpt.Build.Tests.csproj -c Release -p:EfcptTestFramework=net8.0 --filter "Category!=Integration"

Install the .NET 8/9/10 SDKs first. EfcptTestFramework selects the task test host and its fixture; the default remains net10.0.

The efcpt CLI is an external tool whose release lines track EF Core versions, so an upstream release can change scaffolding behavior. A scheduled workflow guards against this by weekly rebuilding a sample against the latest efcpt per supported EF Core line. See efcpt Compatibility for the supported version matrix and how the guard works.

Next Steps