feat(history, stats): surface wind aggregates from history backend #936
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [master] | |
| pull_request: | |
| branches: [master] | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| global-json-file: global.json | |
| - name: Install workloads | |
| run: dotnet workload install wasm-tools | |
| - name: Restore | |
| run: dotnet restore OnaPlotter.slnx | |
| - name: Build | |
| run: dotnet build OnaPlotter.slnx --no-restore --configuration Release | |
| # JS lint runs on the build job (not e2e) so a TDZ-class bug | |
| # like the editModeDeps ReferenceError fails CI as soon as the | |
| # PR opens, not 10+ minutes later when Playwright trips over a | |
| # broken /map page. Setup-node happens here too because the | |
| # build job didn't previously need Node; the e2e job sets up | |
| # its own Node further down. | |
| - name: Setup Node (for JS lint) | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| - name: Install JS lint deps | |
| run: npm ci | |
| - name: Lint (JS) | |
| run: npm run lint | |
| # `dotnet test` on .NET 10 SDK dispatches through VSTest by default, | |
| # which Microsoft.Testing.Platform refuses. TUnit is MTP-native and | |
| # runs as an executable, so invoking the test project directly is | |
| # the cleanest route. `--no-build` reuses the Release artefacts from | |
| # the step above. A non-zero exit propagates a red CI. | |
| - name: Test (C# unit + bUnit component) | |
| run: dotnet run --project OnaPlotter.Tests --configuration Release --no-build | |
| # All JS unit tests: geoMath, chartOverzoom, markerLayer. Pure Node, | |
| # no server dependency. Drops the single-quoted glob that bash | |
| # couldn't expand (previous CI was red with "Could not find | |
| # '.../OnaPlotter/wwwroot/js/*.test.js'"); bash's default globbing | |
| # hands Node a concrete file list so new *.test.js files still | |
| # pick up automatically. | |
| - name: Test (JS unit) | |
| shell: bash | |
| run: node --test OnaPlotter/wwwroot/js/*.test.js | |
| # End-to-end job: runs OnaPlotter + the dev SignalK stack from | |
| # docker/docker-compose.dev.yml and drives the Map page via Playwright. | |
| # Separate job so a pure-unit failure still reports quickly, and so | |
| # docker setup doesn't slow down the C# build's feedback loop. | |
| e2e: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| global-json-file: global.json | |
| - name: Install workloads | |
| run: dotnet workload install wasm-tools | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| # Start the dev SignalK server + fake-data pump. --wait makes | |
| # compose block until the sk healthcheck passes (curl /signalk). | |
| # fake-data is a TCP server; signalk-server connects to it via | |
| # a pipedProvider configured in docker/signalk-config/settings.json. | |
| - name: Start dev SignalK stack | |
| run: docker compose -f docker/docker-compose.dev.yml up -d --build --wait | |
| # Give the pipedProvider a few ticks to fan deltas onto the bus | |
| # before Playwright starts querying for vessels. 6 = own + 5 AIS. | |
| - name: Wait for SignalK deltas | |
| run: | | |
| for i in $(seq 1 30); do | |
| COUNT=$(curl -sS http://localhost:3000/signalk/v1/api/vessels 2>/dev/null \ | |
| | python3 -c 'import json,sys; d=json.loads(sys.stdin.read()); print(len(d))' 2>/dev/null || echo 0) | |
| if [ "$COUNT" -ge "6" ]; then | |
| echo "SignalK has $COUNT vessels; ready." | |
| exit 0 | |
| fi | |
| sleep 2 | |
| done | |
| echo "Timeout waiting for SignalK vessels" | |
| docker compose -f docker/docker-compose.dev.yml logs | |
| exit 1 | |
| - name: Build OnaPlotter (Release) | |
| run: dotnet build OnaPlotter/OnaPlotter.csproj --configuration Release | |
| # Override the SignalK target to the compose'd server on localhost. | |
| - name: Point OnaPlotter at dev SK | |
| run: | | |
| cat > OnaPlotter/bin/Release/net10.0/wwwroot/appsettings.json <<EOF | |
| { "SignalK": { "ServerUrl": "http://localhost:3000" } } | |
| EOF | |
| - name: Start OnaPlotter | |
| run: | | |
| nohup dotnet run --project OnaPlotter --configuration Release --no-build \ | |
| > onaplotter.log 2>&1 & | |
| echo $! > onaplotter.pid | |
| for i in $(seq 1 30); do | |
| if curl -sf http://localhost:5282/ > /dev/null 2>&1; then | |
| echo "OnaPlotter ready." | |
| exit 0 | |
| fi | |
| sleep 2 | |
| done | |
| echo "Timeout waiting for OnaPlotter" | |
| cat onaplotter.log | |
| exit 1 | |
| - name: Install Playwright deps | |
| working-directory: OnaPlotter.UiTests | |
| run: | | |
| npm ci | |
| npx playwright install --with-deps chromium | |
| # Smoke is required. Fuzz uses a 200-click budget which fills | |
| # the test's 3-min cap (avg ~0.4 s per click on a healthy CI | |
| # runner - ~80 s real-time, plenty of headroom). Higher click | |
| # counts catch "banner path crashes after N random clicks" | |
| # regressions the local 10-min fuzz exercises. Seed is fixed | |
| # so a failure reproduces on a dev machine with the same input. | |
| - name: Playwright smoke + actions + short fuzz | |
| working-directory: OnaPlotter.UiTests | |
| env: | |
| BASE_URL: http://localhost:5282/ | |
| FUZZ_CLICKS: "200" | |
| FUZZ_SEED: "20260419" | |
| # Explicit spec list keeps the diagnose-*.spec.js files (manual | |
| # debug helpers that only log) out of the CI run. Ordering: | |
| # smoke first for the quickest failure signal, fuzz last since | |
| # it's the slowest of the three. | |
| run: npx playwright test smoke.spec.js actions.spec.js fuzz.spec.js --reporter=list | |
| - name: Upload Playwright report on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-report | |
| path: OnaPlotter.UiTests/playwright-report/ | |
| retention-days: 7 | |
| - name: Dump logs on failure | |
| if: failure() | |
| run: | | |
| echo "=== OnaPlotter log ===" | |
| tail -200 onaplotter.log || true | |
| echo "=== docker compose logs ===" | |
| docker compose -f docker/docker-compose.dev.yml logs --tail=200 || true | |
| - name: Stop OnaPlotter | |
| if: always() | |
| run: | | |
| if [ -f onaplotter.pid ]; then | |
| kill $(cat onaplotter.pid) 2>/dev/null || true | |
| fi | |
| - name: Stop dev SignalK stack | |
| if: always() | |
| run: docker compose -f docker/docker-compose.dev.yml down | |
| # Packages the Release build as the SignalK-webapp NPM shape and | |
| # uploads it as an artifact so a maintainer can download, sanity- | |
| # check, and then publish manually. Mirrors deploy/deploy.ps1's | |
| # staging so the shape the kiosk gets is the shape CI builds. | |
| # Actual publish lives in publish.yml and is only wired on a tag. | |
| package: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| global-json-file: global.json | |
| - name: Install workloads | |
| run: dotnet workload install wasm-tools | |
| - name: Publish OnaPlotter (Release + AOT) | |
| run: dotnet publish OnaPlotter/OnaPlotter.csproj -c Release -o publish | |
| # SignalK webapps expect <base href="/{webapp-name}/"> so the | |
| # Blazor router resolves correctly when the app is served from | |
| # /signalk/apps/<webapp-name>/ on the SK server. See deploy.ps1 | |
| # for the matching local patch. | |
| - name: Patch index.html base href | |
| run: | | |
| sed -i 's|<base href="/" />|<base href="/signalk-onaplotter/" />|' publish/wwwroot/index.html | |
| grep '<base href' publish/wwwroot/index.html | |
| # Runtime ServerUrl = "auto" tells SignalkClient to use the page | |
| # origin, which IS the SignalK host when the app is served as a | |
| # webapp. Dev / local builds override this locally. | |
| - name: Set appsettings ServerUrl to auto | |
| run: | | |
| cat > publish/wwwroot/appsettings.json <<'EOF' | |
| { "SignalK": { "ServerUrl": "auto" } } | |
| EOF | |
| # Stage the SignalK-webapp layout: | |
| # signalk-onaplotter/ | |
| # package.json (from deploy/package.json.template) | |
| # public/ (Blazor wwwroot) | |
| # Version = 0.0.0-ci.<run> so artifacts from different CI runs | |
| # don't collide. Real publish rewrites with the tag version. | |
| - name: Stage SignalK webapp bundle | |
| run: | | |
| set -euo pipefail | |
| stage=staging/signalk-onaplotter | |
| mkdir -p "$stage" | |
| version="0.0.0-ci.${{ github.run_number }}" | |
| sed "s|__VERSION__|$version|" deploy/package.json.template > "$stage/package.json" | |
| cp -r publish/wwwroot "$stage/public" | |
| echo "--- package.json ---" | |
| cat "$stage/package.json" | |
| echo "--- staging tree ---" | |
| find "$stage" -maxdepth 2 -type d | sort | |
| # Pre-flight: make sure the staged folder would actually pack | |
| # cleanly. --dry-run doesn't touch the registry but surfaces | |
| # any files npm would refuse (tarball-too-big, missing license, | |
| # manifest typos, etc). | |
| - name: npm pack dry-run | |
| working-directory: staging/signalk-onaplotter | |
| run: npm pack --dry-run | |
| # Real tarball for the artifact. Named signalk-onaplotter-*.tgz | |
| # so a maintainer can `npm publish <tarball>` if they want. | |
| - name: npm pack | |
| working-directory: staging/signalk-onaplotter | |
| run: npm pack | |
| - name: Upload webapp bundle | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: signalk-onaplotter-${{ github.run_number }} | |
| path: staging/signalk-onaplotter/signalk-onaplotter-*.tgz | |
| if-no-files-found: error | |
| retention-days: 30 |