Skip to content

Add Rust-based LQL Language Server and VSCode extension overhaul #75

Add Rust-based LQL Language Server and VSCode extension overhaul

Add Rust-based LQL Language Server and VSCode extension overhaul #75

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'
- 'Lql/lql-lsp-rust/**'
- 'Lql/LqlExtension/**'
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'
- 'Lql/lql-lsp-rust/**'
- 'Lql/LqlExtension/**'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
DOTNET_VERSION: '10.0.x'
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs:
# Lint + format check (runs on every PR/push, no path filter)
lint:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- 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', '**/*.fsproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Cache Cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
Lql/lql-lsp-rust/target
key: ${{ runner.os }}-cargo-lint-${{ hashFiles('Lql/lql-lsp-rust/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-lint-
${{ runner.os }}-cargo-
- name: Restore .NET tools
run: dotnet tool restore
- name: Install Node dependencies (LQL Extension)
run: cd Lql/LqlExtension && npm install --no-audit --no-fund
- name: Format check
run: make fmt-check
- name: Lint
run: make lint
# Detect which areas changed to conditionally run tests
changes:
name: Detect Changes
runs-on: ubuntu-latest
timeout-minutes: 10
outputs:
dotnet: ${{ steps.filter.outputs.dotnet }}
postgres: ${{ steps.filter.outputs.postgres }}
icd10: ${{ steps.filter.outputs.icd10 }}
dashboard: ${{ steps.filter.outputs.dashboard }}
lql-rust: ${{ steps.filter.outputs.lql-rust }}
lql-extension: ${{ steps.filter.outputs.lql-extension }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
dotnet:
- 'DataProvider/**'
- 'Lql/Lql/**'
- 'Lql/Lql.Tests/**'
- 'Lql/LqlCli.SQLite.Tests/**'
- 'Lql/Lql.TypeProvider.FSharp.Tests/**'
- 'Migration/**'
- 'Sync/Sync/**'
- 'Sync/Sync.Tests/**'
- 'Sync/Sync.SQLite/**'
- 'Sync/Sync.SQLite.Tests/**'
- 'Other/Selecta/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
postgres:
- 'Gatekeeper/**'
- 'Samples/Clinical/**'
- 'Samples/Scheduling/**'
- 'Sync/**'
- 'DataProvider/**'
- 'Migration/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
icd10:
- 'Samples/ICD10/**'
- 'DataProvider/**'
- 'Migration/**'
- 'Lql/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
dashboard:
- 'Samples/Dashboard/**'
- 'Samples/Clinical/**'
- 'Samples/Scheduling/**'
- 'DataProvider/**'
- 'Sync/**'
- 'Migration/**'
- 'Gatekeeper/**'
- 'Directory.Build.props'
- 'Directory.Packages.props'
lql-rust:
- 'Lql/lql-lsp-rust/**'
lql-extension:
- 'Lql/LqlExtension/**'
# All .NET tests that don't need external services
dotnet-tests:
name: .NET Tests
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [lint, changes]
if: needs.changes.outputs.dotnet == '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: Build CLI tools (needed by F# Type Provider MSBuild targets)
run: |
dotnet build Migration/Migration.Cli -c Debug
dotnet build DataProvider/DataProvider.SQLite.Cli -c Debug
- name: Test DataProvider
run: |
dotnet test DataProvider/DataProvider.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
dotnet test DataProvider/DataProvider.Example.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
- name: Test LQL
run: |
dotnet test Lql/Lql.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
dotnet test Lql/LqlCli.SQLite.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
- name: Test LQL F# Type Provider
run: dotnet test Lql/Lql.TypeProvider.FSharp.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
- name: Test Migration
run: dotnet test Migration/Migration.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
- name: Test Sync (SQLite)
run: |
dotnet test Sync/Sync.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
dotnet test Sync/Sync.SQLite.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-dotnet
path: '**/TestResults/*.trx'
# All tests needing a Postgres service
postgres-tests:
name: Postgres Tests
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [lint, changes]
if: needs.changes.outputs.postgres == '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: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Test Gatekeeper
run: dotnet test Gatekeeper/Gatekeeper.Api.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
env:
TEST_POSTGRES_CONNECTION: "Host=localhost;Database=postgres;Username=postgres;Password=changeme"
- name: Test Sample APIs
run: |
dotnet test Samples/Clinical/Clinical.Api.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
dotnet test Samples/Scheduling/Scheduling.Api.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
env:
TEST_POSTGRES_CONNECTION: "Host=localhost;Database=postgres;Username=postgres;Password=changeme"
- name: Test Sync (Postgres)
run: |
dotnet test Sync/Sync.Postgres.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
dotnet test Sync/Sync.Integration.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
dotnet test Sync/Sync.Http.Tests --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-postgres
path: '**/TestResults/*.trx'
# ICD10 tests (need pgvector + embedding service)
icd10-tests:
name: ICD10 Tests
runs-on: ubuntu-latest
# TIMEOUT EXCEPTION: ICD10 builds Docker image for embedding service + waits up to 90s for model load
timeout-minutes: 15
needs: [lint, 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: Test ICD10
run: |
dotnet test Samples/ICD10/ICD10.Api.Tests --verbosity normal --logger "trx;LogFileName=test-results.trx"
dotnet test Samples/ICD10/ICD10.Cli.Tests --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'
# Dashboard E2E tests (Playwright)
e2e-tests:
name: Dashboard E2E Tests
runs-on: ubuntu-latest
# TIMEOUT EXCEPTION: E2E tests build multiple .NET projects + install Playwright browsers
timeout-minutes: 15
needs: [lint, 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: Build all dependencies and integration tests
run: |
dotnet build Samples/Dashboard/Dashboard.Web -c Release
dotnet build Samples/Clinical/Clinical.Sync -c Release
dotnet build Samples/Scheduling/Scheduling.Sync -c Release
dotnet build Samples/ICD10/ICD10.Api/ICD10.Api.csproj -c Release
dotnet build Migration/Migration.Cli -c Release
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: 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/**'
# LQL LSP Rust tests + coverage (per-crate, 85% minimum)
lql-rust-tests:
name: LQL Rust Tests (${{ matrix.crate }})
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [lint, changes]
if: needs.changes.outputs.lql-rust == 'true'
strategy:
fail-fast: false
matrix:
crate: [lql-parser, lql-analyzer, lql-lsp]
defaults:
run:
working-directory: Lql/lql-lsp-rust
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache Cargo registry + build
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
Lql/lql-lsp-rust/target
key: ${{ runner.os }}-cargo-${{ matrix.crate }}-${{ hashFiles('Lql/lql-lsp-rust/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-${{ matrix.crate }}-
${{ runner.os }}-cargo-
- name: Run tests
run: cargo test --package ${{ matrix.crate }}
- name: Check formatting
run: cargo fmt --package ${{ matrix.crate }} -- --check
- name: Clippy
run: cargo clippy --package ${{ matrix.crate }} -- -D warnings
- name: Install cargo-tarpaulin
run: cargo install cargo-tarpaulin
- name: Coverage (90% minimum)
run: |
cargo tarpaulin \
--packages ${{ matrix.crate }} \
--skip-clean \
--timeout 120 \
--engine llvm \
--exclude-files "*/generated/*" \
--fail-under 90 \
--out stdout
# LQL VS Code Extension CI (lint, compile, package)
lql-extension-ci:
name: LQL Extension CI
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [lint, changes]
if: needs.changes.outputs.lql-extension == 'true'
defaults:
run:
working-directory: Lql/LqlExtension
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm install --no-audit --no-fund
- name: Lint
run: npm run lint
- name: Compile
run: npm run compile
- name: Package VSIX (dry run)
run: npx vsce package --no-git-tag-version --no-update-package-json