Skip to content

ICD-10 microservice, Docker stack, and CI improvements #61

ICD-10 microservice, Docker stack, and CI improvements

ICD-10 microservice, Docker stack, and CI improvements #61

Workflow file for this run

name: CI
on:
push:
branches: [main]
paths:
- '**/*.cs'
- '**/*.csproj'
- '**/*.sln'
- '**/*.py'
- '**/requirements.txt'
- '**/Directory.Build.props'
- '**/Directory.Packages.props'
- '.github/workflows/ci.yml'
- '.config/dotnet-tools.json'
- 'Samples/docker/**'
- '**/Dockerfile*'
pull_request:
branches: [main]
paths:
- '**/*.cs'
- '**/*.csproj'
- '**/*.sln'
- '**/*.py'
- '**/requirements.txt'
- '**/Directory.Build.props'
- '**/Directory.Packages.props'
- '.github/workflows/ci.yml'
- '.config/dotnet-tools.json'
- 'Samples/docker/**'
- '**/Dockerfile*'
workflow_dispatch:
env:
DOTNET_VERSION: '10.0.x'
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs:
# Detect which areas changed to conditionally run tests
changes:
name: Detect Changes
runs-on: ubuntu-latest
outputs:
dataprovider: ${{ steps.filter.outputs.dataprovider }}
lql: ${{ steps.filter.outputs.lql }}
migration: ${{ steps.filter.outputs.migration }}
sync: ${{ steps.filter.outputs.sync }}
gatekeeper: ${{ steps.filter.outputs.gatekeeper }}
samples: ${{ steps.filter.outputs.samples }}
dashboard: ${{ steps.filter.outputs.dashboard }}
icd10: ${{ steps.filter.outputs.icd10 }}
docker: ${{ steps.filter.outputs.docker }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
dataprovider:
- 'DataProvider/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
lql:
- 'Lql/**'
- 'DataProvider/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
migration:
- 'Migration/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
sync:
- 'Sync/**'
- 'Migration/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
gatekeeper:
- 'Gatekeeper/**'
- 'DataProvider/**'
- 'Migration/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
samples:
- 'Samples/**'
- 'DataProvider/**'
- 'Sync/**'
- 'Migration/**'
- 'Gatekeeper/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
dashboard:
- 'Samples/Dashboard/**'
- 'Samples/Clinical/**'
- 'Samples/Scheduling/**'
- 'DataProvider/**'
- 'Sync/**'
- 'Migration/**'
- 'Gatekeeper/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
icd10:
- 'Samples/ICD10/**'
- 'DataProvider/**'
- 'Migration/**'
- 'Lql/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
docker:
- 'Samples/docker/**'
- '**/Dockerfile*'
- 'Samples/Clinical/**'
- 'Samples/Scheduling/**'
- 'Samples/ICD10/**'
- 'Gatekeeper/**'
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
${{ env.DOTNET_VERSION }}
- name: Restore .NET tools
run: |
dotnet tool restore
# Verify h5 tool is available
dotnet tool run h5 --version || echo "h5 tool check (may fail if no version flag)"
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Build core projects
run: |
# Build core libraries (excludes Dashboard which requires h5 tool setup)
dotnet build DataProvider/DataProvider/DataProvider.csproj -c Release
dotnet build DataProvider/DataProvider.SQLite/DataProvider.SQLite.csproj -c Release
dotnet build DataProvider/DataProvider.Postgres.Cli/DataProvider.Postgres.Cli.csproj -c Release
dotnet build DataProvider/DataProvider.SQLite.Cli/DataProvider.SQLite.Cli.csproj -c Release
dotnet build Migration/Migration/Migration.csproj -c Release
dotnet build Migration/Migration.SQLite/Migration.SQLite.csproj -c Release
dotnet build Migration/Migration.Postgres/Migration.Postgres.csproj -c Release
dotnet build Migration/Migration.Cli/Migration.Cli.csproj -c Release
dotnet build Other/Selecta/Selecta.csproj -c Release
dotnet build Lql/Lql/Lql.csproj -c Release
dotnet build Sync/Sync/Sync.csproj -c Release
dotnet build Sync/Sync.SQLite/Sync.SQLite.csproj -c Release
dotnet build Sync/Sync.Postgres/Sync.Postgres.csproj -c Release
dotnet build Gatekeeper/Gatekeeper.Api/Gatekeeper.Api.csproj -c Release
# Build Samples that don't need h5
dotnet build Samples/Clinical/Clinical.Api/Clinical.Api.csproj -c Release
dotnet build Samples/Scheduling/Scheduling.Api/Scheduling.Api.csproj -c Release
dotnet build Samples/ICD10/ICD10.Api/ICD10.Api.csproj -c Release
# DataProvider tests
dataprovider-tests:
name: DataProvider Tests
runs-on: ubuntu-latest
needs: [build, changes]
if: needs.changes.outputs.dataprovider == 'true'
strategy:
fail-fast: false
matrix:
project:
- DataProvider/DataProvider.Tests
- DataProvider/DataProvider.Example.Tests
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore
run: dotnet restore ${{ matrix.project }}
- name: Test
run: dotnet test ${{ matrix.project }} --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx"
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-dataprovider-${{ strategy.job-index }}
path: '**/TestResults/*.trx'
# LQL tests
lql-tests:
name: LQL Tests
runs-on: ubuntu-latest
needs: [build, changes]
if: needs.changes.outputs.lql == 'true'
strategy:
fail-fast: false
matrix:
project:
- Lql/Lql.Tests
- Lql/LqlCli.SQLite.Tests
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore
run: dotnet restore ${{ matrix.project }}
- name: Test
run: dotnet test ${{ matrix.project }} --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx"
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-lql-${{ strategy.job-index }}
path: '**/TestResults/*.trx'
# LQL F# Type Provider tests
lql-fsharp-typeprovider-tests:
name: LQL F# Type Provider Tests
runs-on: ubuntu-latest
needs: [build, changes]
if: needs.changes.outputs.lql == 'true'
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.fsproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore
run: dotnet restore Lql/Lql.TypeProvider.FSharp.Tests
- name: Build CLI tools (needed by MSBuild targets)
run: |
dotnet build Migration/Migration.Cli -c Debug
dotnet build DataProvider/DataProvider.SQLite.Cli -c Debug
- name: Test
run: dotnet test Lql/Lql.TypeProvider.FSharp.Tests --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx"
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-lql-fsharp-typeprovider
path: '**/TestResults/*.trx'
# Migration tests
migration-tests:
name: Migration Tests
runs-on: ubuntu-latest
needs: [build, changes]
if: needs.changes.outputs.migration == 'true'
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore
run: dotnet restore Migration/Migration.Tests
- name: Test
run: dotnet test Migration/Migration.Tests --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx"
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-migration
path: '**/TestResults/*.trx'
# Sync SQLite tests (no Docker)
sync-sqlite-tests:
name: Sync SQLite Tests
runs-on: ubuntu-latest
needs: [build, changes]
if: needs.changes.outputs.sync == 'true'
strategy:
fail-fast: false
matrix:
project:
- Sync/Sync.Tests
- Sync/Sync.SQLite.Tests
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore
run: dotnet restore ${{ matrix.project }}
- name: Test
run: dotnet test ${{ matrix.project }} --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx"
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-sync-sqlite-${{ strategy.job-index }}
path: '**/TestResults/*.trx'
# Sync Postgres tests (need Docker)
sync-postgres-tests:
name: Sync Postgres Tests
runs-on: ubuntu-latest
needs: [build, changes]
if: needs.changes.outputs.sync == 'true'
strategy:
fail-fast: false
matrix:
project:
- Sync/Sync.Postgres.Tests
- Sync/Sync.Integration.Tests
- Sync/Sync.Http.Tests
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore
run: dotnet restore ${{ matrix.project }}
- name: Test
run: dotnet test ${{ matrix.project }} --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx"
env:
TESTCONTAINERS_RYUK_DISABLED: false
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-sync-postgres-${{ strategy.job-index }}
path: '**/TestResults/*.trx'
# Gatekeeper API tests
gatekeeper-tests:
name: Gatekeeper Tests
runs-on: ubuntu-latest
needs: [build, changes]
if: needs.changes.outputs.gatekeeper == 'true'
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: changeme
POSTGRES_DB: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore
run: dotnet restore Gatekeeper/Gatekeeper.Api.Tests
- name: Test
run: dotnet test Gatekeeper/Gatekeeper.Api.Tests --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx"
env:
TEST_POSTGRES_CONNECTION: "Host=localhost;Database=postgres;Username=postgres;Password=changeme"
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-gatekeeper
path: '**/TestResults/*.trx'
# Sample API tests
sample-api-tests:
name: Sample API Tests
runs-on: ubuntu-latest
needs: [build, changes]
if: needs.changes.outputs.samples == 'true'
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: changeme
POSTGRES_DB: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
strategy:
fail-fast: false
matrix:
project:
- Samples/Clinical/Clinical.Api.Tests
- Samples/Scheduling/Scheduling.Api.Tests
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
${{ env.DOTNET_VERSION }}
- name: Restore .NET tools
run: dotnet tool restore
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore
run: dotnet restore ${{ matrix.project }}
- name: Test
run: dotnet test ${{ matrix.project }} --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx"
env:
TEST_POSTGRES_CONNECTION: "Host=localhost;Database=postgres;Username=postgres;Password=changeme"
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-sample-api-${{ strategy.job-index }}
path: '**/TestResults/*.trx'
# ICD10 API + CLI tests (need pgvector PostgreSQL + embedding service)
icd10-tests:
name: ICD10 Tests
runs-on: ubuntu-latest
needs: [build, changes]
if: needs.changes.outputs.icd10 == 'true'
services:
postgres:
image: pgvector/pgvector:pg16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: changeme
POSTGRES_DB: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build embedding service
uses: docker/build-push-action@v5
with:
context: Samples/ICD10/embedding-service
load: true
tags: medembed-service:latest
cache-from: type=gha,scope=embedding-service
cache-to: type=gha,mode=max,scope=embedding-service
- name: Start embedding service
run: |
docker run -d --name embedding-service -p 8000:8000 medembed-service:latest
echo "Waiting for embedding service to load model..."
for i in $(seq 1 90); do
if curl -sf http://localhost:8000/health > /dev/null 2>&1; then
echo "Embedding service is healthy!"
break
fi
if [ $i -eq 90 ]; then
echo "Embedding service failed to start within timeout"
docker logs embedding-service
exit 1
fi
echo "Attempt $i/90 - waiting..."
sleep 5
done
- name: Restore
run: |
dotnet restore Samples/ICD10/ICD10.Api.Tests
dotnet restore Samples/ICD10/ICD10.Cli.Tests
- name: Test ICD10.Api.Tests
run: dotnet test Samples/ICD10/ICD10.Api.Tests --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx"
env:
ICD10_TEST_CONNECTION_STRING: "Host=localhost;Database=postgres;Username=postgres;Password=changeme"
- name: Test ICD10.Cli.Tests
run: dotnet test Samples/ICD10/ICD10.Cli.Tests --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx"
env:
ICD10_TEST_CONNECTION_STRING: "Host=localhost;Database=postgres;Username=postgres;Password=changeme"
- name: Embedding service logs
if: failure()
run: docker logs embedding-service || true
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-icd10
path: '**/TestResults/*.trx'
# Docker build validation
docker-build:
name: Docker Build Validation
runs-on: ubuntu-latest
needs: [build, changes]
if: needs.changes.outputs.docker == 'true'
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
${{ env.DOTNET_VERSION }}
- name: Restore .NET tools
run: dotnet tool restore
- name: Build Dashboard.Web for Docker
run: |
dotnet restore Samples/Dashboard/Dashboard.Web
dotnet publish Samples/Dashboard/Dashboard.Web -c Release -o Samples/docker/dashboard-build
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build app container
uses: docker/build-push-action@v5
with:
context: .
file: Samples/docker/Dockerfile.app
load: true
tags: dataprovider-app:test
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build dashboard container
uses: docker/build-push-action@v5
with:
context: .
file: Samples/docker/Dockerfile.dashboard
load: true
tags: dataprovider-dashboard:test
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Verify containers built
run: |
docker images | grep dataprovider
# Dashboard E2E tests (need Playwright browser)
e2e-tests:
name: Dashboard E2E Tests
runs-on: ubuntu-latest
needs: [build, changes]
if: needs.changes.outputs.dashboard == 'true'
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: changeme
POSTGRES_DB: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
${{ env.DOTNET_VERSION }}
- name: Restore .NET tools
run: dotnet tool restore
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore Dashboard.Web
run: dotnet restore Samples/Dashboard/Dashboard.Web
- name: Build Dashboard.Web (downloads React vendor files)
run: dotnet build Samples/Dashboard/Dashboard.Web -c Release --no-restore
- name: Build Sync projects (required for Sync E2E tests)
run: |
dotnet build Samples/Clinical/Clinical.Sync -c Release
dotnet build Samples/Scheduling/Scheduling.Sync -c Release
- name: Build Integration Tests (includes wwwroot copy)
run: dotnet build Samples/Dashboard/Dashboard.Integration.Tests -c Release
- name: Install Playwright browsers
run: dotnet tool install --global Microsoft.Playwright.CLI && playwright install --with-deps chromium
- name: Verify wwwroot files
run: |
echo "=== Dashboard.Web wwwroot (source) ==="
ls -la Samples/Dashboard/Dashboard.Web/wwwroot/js/ || echo "Dashboard.Web js folder not found"
ls -la Samples/Dashboard/Dashboard.Web/wwwroot/js/vendor/ || echo "Dashboard.Web vendor folder not found"
echo "=== Integration Tests wwwroot (output) ==="
ls -la Samples/Dashboard/Dashboard.Integration.Tests/bin/Release/net10.0/wwwroot/js/ || echo "Integration Tests js folder not found"
ls -la Samples/Dashboard/Dashboard.Integration.Tests/bin/Release/net10.0/wwwroot/js/vendor/ || echo "Integration Tests vendor folder not found"
- name: Test
run: dotnet test Samples/Dashboard/Dashboard.Integration.Tests -c Release --no-build --verbosity normal --logger "trx;LogFileName=test-results.trx"
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-e2e
path: '**/TestResults/*.trx'
- name: Upload Playwright traces
uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-traces
path: '**/playwright-traces/**'