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

Next Steps