Skip to content

Rename projects to Nimblesite.* namespace, remove Gatekeeper and Samples #93

Rename projects to Nimblesite.* namespace, remove Gatekeeper and Samples

Rename projects to Nimblesite.* namespace, remove Gatekeeper and Samples #93

Workflow file for this run

name: CI
on:
pull_request:
branches: [main]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
DOTNET_VERSION: '9.0.x'
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs:
# Track 1: Format Check All -> Lint All -> DataProvider Tests
lint-and-dataprovider:
name: Lint + DataProvider Tests
runs-on: ubuntu-latest
timeout-minutes: 15
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
- name: Build CLI tools (needed by F# Type Provider MSBuild targets)
run: |
dotnet build Migration/Nimblesite.DataProvider.Migration.Cli -c Debug
dotnet build DataProvider/Nimblesite.DataProvider.SQLite.Cli -c Debug
- name: Test DataProvider (with coverage enforcement)
run: |
for proj in DataProvider/Nimblesite.DataProvider.Tests DataProvider/Nimblesite.DataProvider.Example.Tests; do
THRESHOLD=$(jq -r ".projects[\"$proj\"].threshold // .default_threshold" coverage-thresholds.json)
INCLUDE=$(jq -r ".projects[\"$proj\"].include // empty" coverage-thresholds.json)
echo "==> Testing $proj (threshold: ${THRESHOLD}%)"
rm -rf "$proj/TestResults"
if [ -n "$INCLUDE" ]; then
dotnet test "$proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$proj/TestResults" \
--verbosity normal \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Include="$INCLUDE"
else
dotnet test "$proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$proj/TestResults" \
--verbosity normal
fi
COBERTURA=$(find "$proj/TestResults" -name "coverage.cobertura.xml" -type f | head -1)
if [ -z "$COBERTURA" ]; then echo "FAIL: No coverage file for $proj"; exit 1; fi
LINE_RATE=$(sed -n 's/.*line-rate="\([0-9.]*\)".*/\1/p' "$COBERTURA" | head -1)
COVERAGE=$(echo "$LINE_RATE * 100" | bc -l)
COVERAGE_FMT=$(printf "%.2f" $COVERAGE)
echo " Coverage: ${COVERAGE_FMT}% | Threshold: ${THRESHOLD}%"
BELOW=$(echo "$COVERAGE < $THRESHOLD" | bc -l)
if [ "$BELOW" = "1" ]; then
echo " FAIL: ${COVERAGE_FMT}% is BELOW threshold ${THRESHOLD}%"
exit 1
fi
echo " PASS"
done
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-dataprovider
path: '**/TestResults/*.trx'
# Track 2: Build All -> LQL Tests -> Migration Tests -> Sync Tests
build-and-test:
name: Build + LQL / Migration / Sync Tests
runs-on: ubuntu-latest
timeout-minutes: 15
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
env:
DOTNET_CLI_DO_NOT_USE_MSBUILD_SERVER: 1
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
- name: Install cargo-tarpaulin
run: cargo install cargo-tarpaulin
- 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 + build
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
Lql/lql-lsp-rust/target
key: ${{ runner.os }}-cargo-build-${{ hashFiles('Lql/lql-lsp-rust/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-build-
${{ runner.os }}-cargo-
- name: Build .NET
run: dotnet build DataProvider.sln -c Debug
- name: Build Rust
run: cd Lql/lql-lsp-rust && cargo build
- name: Test LQL .NET (with coverage enforcement)
run: |
for proj in Lql/Nimblesite.Lql.Tests; do
THRESHOLD=$(jq -r ".projects[\"$proj\"].threshold // .default_threshold" coverage-thresholds.json)
INCLUDE=$(jq -r ".projects[\"$proj\"].include // empty" coverage-thresholds.json)
echo "==> Testing $proj (threshold: ${THRESHOLD}%)"
rm -rf "$proj/TestResults"
if [ -n "$INCLUDE" ]; then
dotnet test "$proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$proj/TestResults" \
--verbosity normal \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Include="$INCLUDE"
else
dotnet test "$proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$proj/TestResults" \
--verbosity normal
fi
COBERTURA=$(find "$proj/TestResults" -name "coverage.cobertura.xml" -type f | head -1)
if [ -z "$COBERTURA" ]; then echo "FAIL: No coverage file for $proj"; exit 1; fi
LINE_RATE=$(sed -n 's/.*line-rate="\([0-9.]*\)".*/\1/p' "$COBERTURA" | head -1)
COVERAGE=$(echo "$LINE_RATE * 100" | bc -l)
COVERAGE_FMT=$(printf "%.2f" $COVERAGE)
echo " Coverage: ${COVERAGE_FMT}% | Threshold: ${THRESHOLD}%"
BELOW=$(echo "$COVERAGE < $THRESHOLD" | bc -l)
if [ "$BELOW" = "1" ]; then
echo " FAIL: ${COVERAGE_FMT}% is BELOW threshold ${THRESHOLD}%"
exit 1
fi
echo " PASS"
done
- name: Test LQL F# Type Provider (with coverage enforcement)
run: |
proj="Lql/Nimblesite.Lql.TypeProvider.FSharp.Tests"
THRESHOLD=$(jq -r ".projects[\"$proj\"].threshold // .default_threshold" coverage-thresholds.json)
INCLUDE=$(jq -r ".projects[\"$proj\"].include // empty" coverage-thresholds.json)
echo "==> Testing $proj (threshold: ${THRESHOLD}%)"
rm -rf "$proj/TestResults"
if [ -n "$INCLUDE" ]; then
dotnet test "$proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$proj/TestResults" \
--verbosity normal \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Include="$INCLUDE"
else
dotnet test "$proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$proj/TestResults" \
--verbosity normal
fi
COBERTURA=$(find "$proj/TestResults" -name "coverage.cobertura.xml" -type f | head -1)
if [ -z "$COBERTURA" ]; then echo "FAIL: No coverage file for $proj"; exit 1; fi
LINE_RATE=$(sed -n 's/.*line-rate="\([0-9.]*\)".*/\1/p' "$COBERTURA" | head -1)
COVERAGE=$(echo "$LINE_RATE * 100" | bc -l)
COVERAGE_FMT=$(printf "%.2f" $COVERAGE)
echo " Coverage: ${COVERAGE_FMT}% | Threshold: ${THRESHOLD}%"
BELOW=$(echo "$COVERAGE < $THRESHOLD" | bc -l)
if [ "$BELOW" = "1" ]; then
echo " FAIL: ${COVERAGE_FMT}% is BELOW threshold ${THRESHOLD}%"
exit 1
fi
echo " PASS"
- name: Test LQL Rust (with coverage enforcement)
run: |
THRESHOLD=$(jq -r '.projects["Lql/lql-lsp-rust"].threshold // .default_threshold' coverage-thresholds.json)
echo "==> Testing Lql/lql-lsp-rust (threshold: ${THRESHOLD}%)"
cd Lql/lql-lsp-rust && cargo tarpaulin --workspace --skip-clean 2>&1 | tee /tmp/_dp_tarpaulin_out.txt
COVERAGE=$(grep -oE '[0-9]+\.[0-9]+% coverage' /tmp/_dp_tarpaulin_out.txt | tail -1 | grep -oE '[0-9]+\.[0-9]+')
if [ -z "$COVERAGE" ]; then echo "FAIL: Could not parse tarpaulin coverage"; exit 1; fi
echo " Coverage: ${COVERAGE}% | Threshold: ${THRESHOLD}%"
BELOW=$(echo "$COVERAGE < $THRESHOLD" | bc -l)
if [ "$BELOW" = "1" ]; then
echo " FAIL: ${COVERAGE}% is BELOW threshold ${THRESHOLD}%"
exit 1
fi
echo " PASS"
- name: Test Migration (with coverage enforcement)
run: |
proj="Migration/Nimblesite.DataProvider.Migration.Tests"
THRESHOLD=$(jq -r ".projects[\"$proj\"].threshold // .default_threshold" coverage-thresholds.json)
INCLUDE=$(jq -r ".projects[\"$proj\"].include // empty" coverage-thresholds.json)
echo "==> Testing $proj (threshold: ${THRESHOLD}%)"
rm -rf "$proj/TestResults"
if [ -n "$INCLUDE" ]; then
dotnet test "$proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$proj/TestResults" \
--verbosity normal \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Include="$INCLUDE"
else
dotnet test "$proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$proj/TestResults" \
--verbosity normal
fi
COBERTURA=$(find "$proj/TestResults" -name "coverage.cobertura.xml" -type f | head -1)
if [ -z "$COBERTURA" ]; then echo "FAIL: No coverage file for $proj"; exit 1; fi
LINE_RATE=$(sed -n 's/.*line-rate="\([0-9.]*\)".*/\1/p' "$COBERTURA" | head -1)
COVERAGE=$(echo "$LINE_RATE * 100" | bc -l)
COVERAGE_FMT=$(printf "%.2f" $COVERAGE)
echo " Coverage: ${COVERAGE_FMT}% | Threshold: ${THRESHOLD}%"
BELOW=$(echo "$COVERAGE < $THRESHOLD" | bc -l)
if [ "$BELOW" = "1" ]; then
echo " FAIL: ${COVERAGE_FMT}% is BELOW threshold ${THRESHOLD}%"
exit 1
fi
echo " PASS"
- name: Test Sync SQLite (with coverage enforcement)
run: |
for proj in Sync/Nimblesite.Sync.Tests Sync/Nimblesite.Sync.SQLite.Tests; do
THRESHOLD=$(jq -r ".projects[\"$proj\"].threshold // .default_threshold" coverage-thresholds.json)
INCLUDE=$(jq -r ".projects[\"$proj\"].include // empty" coverage-thresholds.json)
echo "==> Testing $proj (threshold: ${THRESHOLD}%)"
rm -rf "$proj/TestResults"
if [ -n "$INCLUDE" ]; then
dotnet test "$proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$proj/TestResults" \
--verbosity normal \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Include="$INCLUDE"
else
dotnet test "$proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$proj/TestResults" \
--verbosity normal
fi
COBERTURA=$(find "$proj/TestResults" -name "coverage.cobertura.xml" -type f | head -1)
if [ -z "$COBERTURA" ]; then echo "FAIL: No coverage file for $proj"; exit 1; fi
LINE_RATE=$(sed -n 's/.*line-rate="\([0-9.]*\)".*/\1/p' "$COBERTURA" | head -1)
COVERAGE=$(echo "$LINE_RATE * 100" | bc -l)
COVERAGE_FMT=$(printf "%.2f" $COVERAGE)
echo " Coverage: ${COVERAGE_FMT}% | Threshold: ${THRESHOLD}%"
BELOW=$(echo "$COVERAGE < $THRESHOLD" | bc -l)
if [ "$BELOW" = "1" ]; then
echo " FAIL: ${COVERAGE_FMT}% is BELOW threshold ${THRESHOLD}%"
exit 1
fi
echo " PASS"
done
- name: Test Sync Postgres (with coverage enforcement)
run: |
for proj in Sync/Nimblesite.Sync.Postgres.Tests Sync/Nimblesite.Sync.Integration.Tests Sync/Nimblesite.Sync.Http.Tests; do
THRESHOLD=$(jq -r ".projects[\"$proj\"].threshold // .default_threshold" coverage-thresholds.json)
INCLUDE=$(jq -r ".projects[\"$proj\"].include // empty" coverage-thresholds.json)
echo "==> Testing $proj (threshold: ${THRESHOLD}%)"
rm -rf "$proj/TestResults"
if [ -n "$INCLUDE" ]; then
dotnet test "$proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$proj/TestResults" \
--verbosity normal \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Include="$INCLUDE"
else
dotnet test "$proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$proj/TestResults" \
--verbosity normal
fi
COBERTURA=$(find "$proj/TestResults" -name "coverage.cobertura.xml" -type f | head -1)
if [ -z "$COBERTURA" ]; then echo "FAIL: No coverage file for $proj"; exit 1; fi
LINE_RATE=$(sed -n 's/.*line-rate="\([0-9.]*\)".*/\1/p' "$COBERTURA" | head -1)
COVERAGE=$(echo "$LINE_RATE * 100" | bc -l)
COVERAGE_FMT=$(printf "%.2f" $COVERAGE)
echo " Coverage: ${COVERAGE_FMT}% | Threshold: ${THRESHOLD}%"
BELOW=$(echo "$COVERAGE < $THRESHOLD" | bc -l)
if [ "$BELOW" = "1" ]; then
echo " FAIL: ${COVERAGE_FMT}% is BELOW threshold ${THRESHOLD}%"
exit 1
fi
echo " PASS"
done
env:
TESTCONTAINERS_RYUK_DISABLED: false
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-build
path: '**/TestResults/*.trx'
# Track 3: Build All -> LQL Extension Tests
extension-tests:
name: LQL Extension Tests
runs-on: ubuntu-latest
timeout-minutes: 10
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: Run tests with coverage
run: |
npm run compile
rm -rf out-cov && npx nyc instrument out out-cov && rm -rf out && mv out-cov out
xvfb-run -a node ./out/test/runTest.js
npx nyc report --reporter=json-summary --reporter=text
- name: Enforce coverage threshold
working-directory: .
run: |
THRESHOLD=$(jq -r '.projects["Lql/LqlExtension"].threshold // .default_threshold' coverage-thresholds.json)
SUMMARY="Lql/LqlExtension/coverage/coverage-summary.json"
if [ ! -f "$SUMMARY" ]; then
SUMMARY="Lql/LqlExtension/.nyc_output/coverage-summary.json"
fi
if [ ! -f "$SUMMARY" ]; then
echo "FAIL: No coverage summary produced for Lql/LqlExtension"
exit 1
fi
COVERAGE=$(jq -r '.total.lines.pct' "$SUMMARY")
echo " Coverage: ${COVERAGE}% | Threshold: ${THRESHOLD}%"
BELOW=$(echo "$COVERAGE < $THRESHOLD" | bc -l)
if [ "$BELOW" = "1" ]; then
echo " FAIL: ${COVERAGE}% is BELOW threshold ${THRESHOLD}%"
exit 1
fi
echo " PASS"
- name: Package VSIX (dry run)
run: npm run compile && npx vsce package --no-git-tag-version --no-update-package-json