diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 825bb43..4fd4047 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,6 +109,18 @@ jobs: shell: pwsh run: .\build\pack.ps1 + - name: Smoke-test the packed package (Windows) + shell: pwsh + run: .\build\test-package.ps1 + + - name: Upload win-x64 Native Artifacts + uses: actions/upload-artifact@v4 + if: always() + with: + name: native-win-x64 + path: artifacts/native/win-x64/* + if-no-files-found: warn + - name: Upload NuGet Packages uses: actions/upload-artifact@v4 if: always() @@ -124,3 +136,85 @@ jobs: name: symbol-packages path: artifacts/nuget/*.snupkg if-no-files-found: warn + + # --------------------------------------------------------------------------- + # Linux: prove the *cross-platform* package (packed on Windows above) actually + # works on Linux. Downloads the finished 'nuget-packages' artifact and consumes + # it as a real NuGet package via build/test-package.sh: restore -> the bundled + # Linux idlc runs code generation -> DllImport resolves libddsc.so -> pub/sub + # round-trip, then the ddsmonitor tool is installed and served. No native build. + # --------------------------------------------------------------------------- + linux-verify: + runs-on: ubuntu-latest + needs: [ build ] + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # Required for Nerdbank.GitVersioning (applies to the smoke-test project) + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: | + 8.0.x + 10.0.x + + - name: Download cross-platform packages + uses: actions/download-artifact@v4 + with: + name: nuget-packages + path: artifacts/nuget/ + + - name: Smoke-test the cross-platform package (Linux) + shell: bash + run: build/test-package.sh + + # --------------------------------------------------------------------------- + # Publish to NuGet.org — ONLY on a version tag (vX.Y.Z), and ONLY after the + # cross-platform package has been verified on both Windows (build) and Linux + # (linux-verify). Pushes the packages produced by the build job (both + # CycloneDDS.NET and CycloneDDS.NET.DdsMonitor) plus their symbol packages. + # + # Requires a repository secret named NUGET_API_KEY: + # Repo -> Settings -> Secrets and variables -> Actions -> New repository secret. + # To release: create a GitHub Release with a tag matching the NBGV version of + # the target commit (e.g. v0.3.2 — check with `nbgv get-version` first). + # --------------------------------------------------------------------------- + publish: + runs-on: ubuntu-latest + needs: [ build, linux-verify ] + if: startsWith(github.ref, 'refs/tags/v') + + steps: + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 10.0.x + + - name: Download packages + uses: actions/download-artifact@v4 + with: + name: nuget-packages + path: artifacts/nuget/ + + - name: Download symbol packages + uses: actions/download-artifact@v4 + with: + name: symbol-packages + path: artifacts/nuget/ + + - name: Push to NuGet.org + shell: bash + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} + run: | + if [ -z "$NUGET_API_KEY" ]; then + echo "::error::NUGET_API_KEY secret is not set — cannot publish." >&2 + exit 1 + fi + # dotnet expands the wildcard and auto-pushes the matching .snupkg for each .nupkg. + dotnet nuget push "artifacts/nuget/*.nupkg" \ + --api-key "$NUGET_API_KEY" \ + --source https://api.nuget.org/v3/index.json \ + --skip-duplicate diff --git a/CHANGELOG.md b/CHANGELOG.md index 73f4470..79de05a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## unreleased nothing yet +## 0.3.2 + +### Fixed +- **SourceLink symbols for the whole library.** `CycloneDDS.Core` and + `CycloneDDS.Schema` now ship their SourceLink-enabled PDBs in `lib/net8.0` + (previously only `CycloneDDS.Runtime` did), so consumers can step into all three + assemblies straight from the package. + +### Added +- **Automatic NuGet publish on a version tag.** Pushing a `vX.Y.Z` tag (e.g. via a + GitHub Release) builds and verifies the cross-platform package on Windows and + Linux, then publishes it — and the `ddsmonitor` tool — to NuGet.org. Requires the + `NUGET_API_KEY` repository secret. +- **CI now verifies the packaged output on both platforms** before publishing (a + Windows smoke test + a Linux job that consumes the finished package), and uploads + the `win-x64` native as a workflow artifact. +- **`examples/PackageSmokeTest`** and **`build/test-package.{sh,ps1}`** for + smoke-testing the package (and the `ddsmonitor` tool) locally on either OS. + +### Changed +- Packing now succeeds on Linux too (Windows-only pack entries are `Exists`-guarded), + so a per-platform package can be built locally on either OS. +- CI builds set `ContinuousIntegrationBuild` for deterministic PDBs with normalized + SourceLink source paths. + ## 0.3.1 ### Added diff --git a/Directory.Build.props b/Directory.Build.props index c5f4167..6c3f1ce 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -10,6 +10,9 @@ snupkg true true + + true $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb false diff --git a/RELEASE-GUIDE.md b/RELEASE-GUIDE.md index 6b9be94..6041cba 100644 --- a/RELEASE-GUIDE.md +++ b/RELEASE-GUIDE.md @@ -9,10 +9,97 @@ Before you begin, ensure you have: 1. **Maintainer Access** to the GitHub repository 2. **NuGet API Key** with push permissions for the CycloneDDS.NET package 3. **Development Environment** with: - - .NET 8.0 SDK or later - - PowerShell + - **.NET 10 SDK** — the code uses C# 13 language features, so building requires it; the produced binaries still target `net8.0`. (The .NET 8 runtime is also handy for running the `net8.0` tests.) + - **CMake 3.16+** + - **Windows:** PowerShell and Visual Studio 2022 (*C++ Desktop Development* workload) for native compilation. + - **Linux:** a C toolchain and `patchelf` — `sudo apt-get install -y cmake build-essential patchelf`. - Git command-line tools +## Building & Testing a Multi-Platform Package Locally + +The published `CycloneDDS.NET` and `CycloneDDS.NET.DdsMonitor` packages ship native +assets for **both** `win-x64` and `linux-x64`. Every green CI run already produces +these as artifacts, so **you rarely need to build them yourself** — the options +below are ordered easiest first. + +> **How the cross-platform package is assembled.** The pack step includes whatever +> native artifacts are present under `artifacts/native//` (Windows-only entries +> are `Exists`-guarded), so a cross-platform package needs *both* platforms' natives +> on one pack host. Because the packages bundle the Windows apphost `.exe` launchers +> for the build-time tools, the final cross-platform pack runs on **Windows**. +> Packing on Linux is fully supported but yields a `linux-x64`-only package. + +CI publishes three artifacts per run: **`nuget-packages`** (the finished +cross-platform `.nupkg` + `.snupkg`), **`native-win-x64`**, and +**`native-linux-x64`**. Download them with the GitHub CLI (`gh`, recommended — +`winget install GitHub.cli` on Windows) or from the Actions run page. + +### Option A — Download the finished package from CI (easiest, no build) + +```bash +# Grab the cross-platform packages from the latest green run on main: +gh run download --repo pjanec/CycloneDds.NET -n nuget-packages -D artifacts/nuget +build/test-package.sh # smoke-test them on this OS (PowerShell: .\build\test-package.ps1) +``` + +Then publish `artifacts/nuget/*.nupkg` manually (see *Publishing* below). Run +`test-package` on both a Windows and a Linux box against the same package before +publishing. + +### Option B — Assemble the package locally without a C++ toolchain + +Download **both** natives from CI and pack — no Visual Studio / CMake needed, just +the .NET 10 SDK: + +```powershell +gh run download --repo pjanec/CycloneDds.NET -n native-win-x64 -D artifacts\native\win-x64 +gh run download --repo pjanec/CycloneDds.NET -n native-linux-x64 -D artifacts\native\linux-x64 +dotnet build CycloneDDS.NET.Core.slnf -c Release +dotnet pack src\CycloneDDS.Runtime\CycloneDDS.Runtime.csproj -c Release --no-build -o artifacts\nuget +dotnet pack tools\DdsMonitor\DdsMonitor.Blazor\DdsMonitor.csproj -c Release --no-build -o artifacts\nuget +.\build\test-package.ps1 +``` + +### Option C — Full local build from source + +```powershell +# Windows (needs VS 2022 C++ workload + CMake): builds win native, downloads linux native, packs both +.\build\native-win.ps1 +gh run download --repo pjanec/CycloneDds.NET -n native-linux-x64 -D artifacts\native\linux-x64 +.\build\pack.ps1 +.\build\test-package.ps1 +``` + +```bash +# Linux (needs cmake/gcc/patchelf): produces a linux-x64-only package, enough to validate the Linux side +build/native-linux.sh Release +dotnet build CycloneDDS.NET.Core.slnf -c Release +dotnet pack src/CycloneDDS.Runtime/CycloneDDS.Runtime.csproj -c Release --no-build -o artifacts/nuget +dotnet pack tools/DdsMonitor/DdsMonitor.Blazor/DdsMonitor.csproj -c Release --no-build -o artifacts/nuget +build/test-package.sh +``` + +### The smoke test (`build/test-package.{sh,ps1}`) + +Both scripts pick the **newest** package in the feed by write-time (so a cluttered +`artifacts/nuget` with old builds is fine — do **not** name-sort), then: + +1. Consume `CycloneDDS.NET` as a real NuGet **package** via `examples/PackageSmokeTest` + — which declares a `[DdsTopic]` (so the packaged code generator + `idlc` run at + build time) and does a publish/subscribe round-trip. Expect + `[smoke] PASS: round-trip Id=42 ...`. +2. Install the `ddsmonitor` global tool, confirm it serves HTTP 200 (native loaded), + and uninstall it. + +``` +build/test-package.sh [FEED_DIR] # default artifacts/nuget; NO_DDSMON=1 to skip the tool +.\build\test-package.ps1 [-FeedDir ] [-NoDdsMon] +``` + +Run the smoke test on **both** a Windows and a Linux box before publishing. Both +were validated during development — Linux locally + in CI, Windows in the CI +`build` job. + ## Version Management This project uses **Nerdbank.GitVersioning (NBGV)** for automatic semantic versioning. @@ -133,8 +220,20 @@ Expand-Archive artifacts/nuget/CycloneDDS.NET.1.0.0.nupkg -DestinationPath temp_ # - ThirdPartyNotices.txt is present # - Native binaries are in runtimes/ folder # - Build tools are in build/ or buildTransitive/ folder +# - lib/net8.0/ has *.dll AND matching *.pdb for Runtime, Core and Schema +# (SourceLink debugging), and a .snupkg was produced alongside the .nupkg ``` +**Debugging support (SourceLink).** The package embeds SourceLink so consumers can +step into the library sources straight from GitHub. Symbols ship two ways: the +`lib/net8.0/*.pdb` travel inside the `.nupkg` (Visual Studio loads them directly), +and a `.snupkg` is published to the NuGet symbol server. Verify with the +[`sourcelink`](https://www.nuget.org/packages/sourcelink) tool, e.g. +`sourcelink print-urls lib\net8.0\CycloneDDS.Runtime.pdb` should list +`https://raw.githubusercontent.com/pjanec/CycloneDds.NET//...` URLs. +Publish **both** the `.nupkg` and the `.snupkg` (see below). SourceLink URLs only +resolve for commits pushed to GitHub, so publish from a CI build on `main`/a tag. + **Key Things to Check:** - [ ] Package version is correct (no `-alpha` suffix for stable) - [ ] `icon.png` is at package root @@ -146,13 +245,42 @@ Expand-Archive artifacts/nuget/CycloneDDS.NET.1.0.0.nupkg -DestinationPath temp_ ## Publishing to NuGet.org -### Option A: Automatic Publishing (CI/CD) +### Option A: Automatic Publishing (CI/CD) — recommended + +CI publishes to NuGet.org automatically when you push a **version tag** (`vX.Y.Z`), +but only after the cross-platform package has passed the Windows and Linux +verification jobs. The `publish` job pushes both `CycloneDDS.NET` and +`CycloneDDS.NET.DdsMonitor` (plus their `.snupkg` symbols). -If CI is configured to auto-publish on tags: +**One-time setup — store the NuGet API key as a repository secret:** -1. **Wait for CI to complete** after pushing the tag -2. **Verify on NuGet.org** that the new version appears -3. **Create a GitHub Release** with release notes +1. Create an API key at with **"Push new + packages and package versions"**. Scope the *Package glob* to `CycloneDDS.NET*` + so it covers both packages. (For the very first push of a brand-new package id, + a key scoped to *all* packages / your account may be required until the id + exists.) +2. In GitHub: **Repo → Settings → Secrets and variables → Actions → New repository + secret**. Name it exactly **`NUGET_API_KEY`** and paste the key. GitHub encrypts + it and masks it in logs; the workflow reads it as `secrets.NUGET_API_KEY`. You + never commit the key. + +**Releasing:** + +1. Make sure `main` is green and at the commit you want to ship. +2. Check the version NBGV will produce for that commit: + ```bash + nbgv get-version # e.g. 0.3.2 (dotnet tool install -g nbgv) + ``` + The published version is `version.json` base + git height — the tag name does + **not** set it, so name the tag to match (e.g. `v0.3.2`). +3. **Create a GitHub Release** with tag `vX.Y.Z` targeting that commit (or + `git tag v0.3.2 && git push origin v0.3.2`). The tag push runs CI; on success + the `publish` job pushes to NuGet.org. +4. **Verify on NuGet.org** that the new version (and the DdsMonitor tool) appear. + +> The tag is added to `publicReleaseRefSpec` in `version.json`, so a tag build gets +> a clean version (no `-gSHA` prerelease suffix). SourceLink URLs resolve because +> the tagged commit is on GitHub. ### Option B: Manual Publishing diff --git a/build/test-package.ps1 b/build/test-package.ps1 new file mode 100644 index 0000000..64a67bc --- /dev/null +++ b/build/test-package.ps1 @@ -0,0 +1,101 @@ +<# +.SYNOPSIS + Smoke-test the CycloneDDS.NET NuGet package (and the DdsMonitor global tool) + from a local feed — whether built locally (build\pack.ps1) or downloaded from a + CI run (the 'nuget-packages' artifact). + +.DESCRIPTION + Picks the NEWEST matching package by LastWriteTime (so a cluttered + artifacts\nuget with many historical builds is fine), then consumes it as a real + PackageReference via examples\PackageSmokeTest: restore -> run the bundled code + generator (idlc) -> publish/subscribe round-trip. Optionally installs the + ddsmonitor tool, confirms it serves HTTP, and uninstalls it. + +.EXAMPLE + .\build\test-package.ps1 + .\build\test-package.ps1 -FeedDir C:\downloads\nuget-packages -NoDdsMon +#> +[CmdletBinding()] +param( + [string]$FeedDir = "artifacts/nuget", + [switch]$NoDdsMon +) + +$ErrorActionPreference = "Stop" +$RepoRoot = Split-Path -Parent $PSScriptRoot +$Feed = (Resolve-Path $FeedDir).Path + +function Get-Newest($pattern) { + Get-ChildItem -Path (Join-Path $Feed $pattern) -ErrorAction SilentlyContinue | + Sort-Object LastWriteTime -Descending | Select-Object -First 1 +} + +$rt = Get-Newest 'CycloneDDS.NET.[0-9]*.nupkg' +if (-not $rt) { throw "No CycloneDDS.NET..nupkg found in $Feed" } +$rtVer = $rt.Name -replace '^CycloneDDS\.NET\.(.+)\.nupkg$', '$1' + +Write-Host "============================================================" +Write-Host " Package smoke test" +Write-Host " feed: $Feed" +Write-Host " package: $($rt.Name) (version $rtVer)" +Write-Host "============================================================" + +Remove-Item -Recurse -Force ` + "$env:USERPROFILE\.nuget\packages\cyclonedds.net", ` + "$env:USERPROFILE\.nuget\packages\cyclonedds.net.ddsmonitor" -ErrorAction SilentlyContinue +Remove-Item -Recurse -Force ` + "$RepoRoot\examples\PackageSmokeTest\bin", ` + "$RepoRoot\examples\PackageSmokeTest\obj" -ErrorAction SilentlyContinue + +Write-Host "`n[1/2] Running examples/PackageSmokeTest against $rtVer ..." +dotnet run --project "$RepoRoot\examples\PackageSmokeTest" -c Release ` + -p:SmokePkgVersion=$rtVer -p:RestoreAdditionalSources=$Feed +if ($LASTEXITCODE -ne 0) { throw "runtime package smoke test FAILED" } +Write-Host " [+] runtime package smoke test PASSED" + +if ($NoDdsMon) { Write-Host "`nSkipping DdsMonitor tool check (-NoDdsMon)."; Write-Host "All good."; exit 0 } + +$mon = Get-Newest 'CycloneDDS.NET.DdsMonitor.*.nupkg' +if (-not $mon) { Write-Host "`nNo DdsMonitor package in feed — skipping tool check."; Write-Host "All good."; exit 0 } +$monVer = $mon.Name -replace '^CycloneDDS\.NET\.DdsMonitor\.(.+)\.nupkg$', '$1' + +Write-Host "`n[2/2] Testing the ddsmonitor global tool $monVer ..." +dotnet tool uninstall --global CycloneDDS.NET.DdsMonitor 2>$null | Out-Null +dotnet tool install --global --add-source $Feed --version $monVer CycloneDDS.NET.DdsMonitor | Out-Null + +# Invoke by absolute path: the global-tools dir may not be on PATH in this session +# (e.g. right after `dotnet tool install --global` in CI). +$toolExe = Join-Path $env:USERPROFILE ".dotnet\tools\ddsmonitor.exe" +if (-not (Test-Path $toolExe)) { $toolExe = "ddsmonitor" } + +$out = [System.IO.Path]::GetTempFileName() +$err = [System.IO.Path]::GetTempFileName() +$proc = Start-Process -FilePath $toolExe -ArgumentList "--NoBrowser true" ` + -RedirectStandardOutput $out -RedirectStandardError $err -PassThru -NoNewWindow + +$ok = $false; $port = $null +for ($i = 0; $i -lt 30; $i++) { + $txt = (Get-Content $out, $err -Raw -ErrorAction SilentlyContinue) -join "`n" + if ($txt -match 'Now listening on: http://127\.0\.0\.1:(\d+)') { $port = $Matches[1] } + if ($txt -match 'Application started') { $ok = $true; break } + if ($proc.HasExited) { break } + Start-Sleep -Milliseconds 500 +} + +$code = $null +if ($port) { + try { $code = (Invoke-WebRequest -Uri "http://127.0.0.1:$port/" -UseBasicParsing -TimeoutSec 5).StatusCode } catch {} +} + +if (-not $proc.HasExited) { $proc.Kill(); $proc.WaitForExit() } +dotnet tool uninstall --global CycloneDDS.NET.DdsMonitor 2>$null | Out-Null + +if ($ok -and $code -eq 200) { + Write-Host " [+] ddsmonitor started (port $port) and served HTTP 200 — native loaded OK" + Write-Host "`nAll good." + exit 0 +} + +Write-Host " [-] ddsmonitor check FAILED (started=$ok, http=$code). Log tail:" -ForegroundColor Red +Get-Content $out, $err -Tail 20 -ErrorAction SilentlyContinue +exit 1 diff --git a/build/test-package.sh b/build/test-package.sh new file mode 100755 index 0000000..d88f1bc --- /dev/null +++ b/build/test-package.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +set -euo pipefail + +# =================================================================================== +# build/test-package.sh +# +# PURPOSE: +# Smoke-test the CycloneDDS.NET NuGet package (and, optionally, the DdsMonitor +# global tool) from a local feed — whether the packages were built locally +# (build/pack.ps1 or dotnet pack) or downloaded from a CI run (the +# 'nuget-packages' artifact). It always picks the NEWEST matching package by +# modification time, so a cluttered artifacts/nuget with many historical builds +# is fine. +# +# It consumes the package as a real PackageReference via examples/PackageSmokeTest: +# restore -> run the bundled code generator (idlc) -> publish/subscribe round-trip. +# +# USAGE: +# build/test-package.sh [FEED_DIR] # default FEED_DIR = artifacts/nuget +# NO_DDSMON=1 build/test-package.sh # skip the DdsMonitor tool check +# =================================================================================== + +FEED="${1:-artifacts/nuget}" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(dirname "$SCRIPT_DIR")" + +if [ ! -d "$FEED" ]; then + echo "ERROR: feed directory not found: $FEED" >&2 + exit 1 +fi +FEED_ABS="$(cd "$FEED" && pwd)" + +# Newest file matching a glob (by mtime), or empty. +newest() { ls -t "$FEED_ABS"/$1 2>/dev/null | head -n1 || true; } + +RT_PKG="$(newest 'CycloneDDS.NET.[0-9]*.nupkg')" +if [ -z "$RT_PKG" ]; then + echo "ERROR: no CycloneDDS.NET..nupkg found in $FEED_ABS" >&2 + exit 1 +fi +RT_VER="$(basename "$RT_PKG" | sed -E 's/^CycloneDDS\.NET\.(.+)\.nupkg$/\1/')" + +echo "============================================================" +echo " Package smoke test" +echo " feed: $FEED_ABS" +echo " package: $(basename "$RT_PKG") (version $RT_VER)" +echo "============================================================" + +# Force a fresh restore of exactly this build. +rm -rf "$HOME/.nuget/packages/cyclonedds.net" "$HOME/.nuget/packages/cyclonedds.net.ddsmonitor" 2>/dev/null || true +rm -rf "$REPO_ROOT/examples/PackageSmokeTest/bin" "$REPO_ROOT/examples/PackageSmokeTest/obj" + +echo "" +echo "[1/2] Running examples/PackageSmokeTest against $RT_VER ..." +dotnet run --project "$REPO_ROOT/examples/PackageSmokeTest" -c Release \ + -p:SmokePkgVersion="$RT_VER" \ + -p:RestoreAdditionalSources="$FEED_ABS" +echo " [+] runtime package smoke test PASSED" + +if [ "${NO_DDSMON:-0}" = "1" ]; then + echo "" + echo "Skipping DdsMonitor tool check (NO_DDSMON=1)." + echo "All good." + exit 0 +fi + +MON_PKG="$(newest 'CycloneDDS.NET.DdsMonitor.*.nupkg')" +if [ -z "$MON_PKG" ]; then + echo "" + echo "No CycloneDDS.NET.DdsMonitor package in feed — skipping tool check." + echo "All good." + exit 0 +fi +MON_VER="$(basename "$MON_PKG" | sed -E 's/^CycloneDDS\.NET\.DdsMonitor\.(.+)\.nupkg$/\1/')" + +echo "" +echo "[2/2] Testing the ddsmonitor global tool $MON_VER ..." +dotnet tool uninstall --global CycloneDDS.NET.DdsMonitor >/dev/null 2>&1 || true +dotnet tool install --global --add-source "$FEED_ABS" --version "$MON_VER" CycloneDDS.NET.DdsMonitor >/dev/null + +TOOL="$HOME/.dotnet/tools/ddsmonitor" +LOG="$(mktemp)" +"$TOOL" --NoBrowser true >"$LOG" 2>&1 & +TOOL_PID=$! + +ok=0 +for _ in $(seq 1 30); do + if grep -q "Application started" "$LOG" 2>/dev/null; then ok=1; break; fi + if ! kill -0 "$TOOL_PID" 2>/dev/null; then break; fi + sleep 0.5 +done + +PORT="$(grep -oE 'Now listening on: http://127.0.0.1:[0-9]+' "$LOG" | grep -oE '[0-9]+$' | head -n1 || true)" +code="" +if [ -n "$PORT" ]; then + code="$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:$PORT/" 2>/dev/null || true)" +fi + +kill "$TOOL_PID" 2>/dev/null || true +wait "$TOOL_PID" 2>/dev/null || true +dotnet tool uninstall --global CycloneDDS.NET.DdsMonitor >/dev/null 2>&1 || true + +if [ "$ok" = "1" ] && [ "$code" = "200" ]; then + echo " [+] ddsmonitor started (port $PORT) and served HTTP 200 — native loaded OK" + echo "" + echo "All good." + exit 0 +fi + +echo " [-] ddsmonitor check FAILED (started=$ok, http=$code). Log:" >&2 +tail -n 20 "$LOG" >&2 +exit 1 diff --git a/examples/PackageSmokeTest/PackageSmokeTest.csproj b/examples/PackageSmokeTest/PackageSmokeTest.csproj new file mode 100644 index 0000000..19ec19f --- /dev/null +++ b/examples/PackageSmokeTest/PackageSmokeTest.csproj @@ -0,0 +1,39 @@ + + + + + Exe + net8.0 + enable + disable + + true + false + + false + + *-* + + + + + + + diff --git a/examples/PackageSmokeTest/Program.cs b/examples/PackageSmokeTest/Program.cs new file mode 100644 index 0000000..3f290c0 --- /dev/null +++ b/examples/PackageSmokeTest/Program.cs @@ -0,0 +1,90 @@ +using System; +using System.Threading; +using System.Runtime.InteropServices; +using CycloneDDS.Runtime; +using CycloneDDS.Schema; + +namespace PackageSmokeTest +{ + // Declaring a [DdsTopic] forces the package's code generator (idlc) to run at + // build time — so a successful build already proves the packaged tooling works + // on this OS. Main() then proves the runtime native (libddsc.so / ddsc.dll) + // works by doing a real publish -> subscribe round-trip. + [DdsTopic("PackageSmokeTest_SmokeSample")] + public partial struct SmokeSample + { + [DdsKey] public int Id; + public double Value; + public FixedString32 Label; + } + + public static class Program + { + public static int Main() + { + Console.WriteLine($"[smoke] {RuntimeInformation.OSDescription} / {RuntimeInformation.ProcessArchitecture}"); + try + { + using var participant = new DdsParticipant(); + using var writer = new DdsWriter(participant); + using var reader = new DdsReader(participant); + + // Wait for the reader/writer to discover each other so the first + // sample isn't dropped. This also exercises the matched-status + // listener callback (the ABI fix that was broken on Linux). + if (!writer.WaitForReaderAsync(TimeSpan.FromSeconds(5)).GetAwaiter().GetResult()) + { + Console.Error.WriteLine("[smoke] FAIL: reader/writer did not discover each other"); + return 1; + } + + var sent = new SmokeSample + { + Id = 42, + Value = 3.14159, + Label = new FixedString32("hello-cyclonedds") + }; + writer.Write(sent); + + SmokeSample? received = null; + for (int attempt = 0; attempt < 50 && received is null; attempt++) + { + using (var loan = reader.Take(maxSamples: 10)) + { + foreach (var sample in loan) + { + if (sample.IsValid) { received = sample.Data; break; } + } + } + if (received is null) Thread.Sleep(100); + } + + if (received is null) + { + Console.Error.WriteLine("[smoke] FAIL: no sample received within timeout"); + return 1; + } + + var got = received.Value; + bool ok = got.Id == sent.Id + && Math.Abs(got.Value - sent.Value) < 1e-9 + && got.Label.ToString() == sent.Label.ToString(); + + if (!ok) + { + Console.Error.WriteLine( + $"[smoke] FAIL: round-trip mismatch: Id={got.Id} Value={got.Value} Label={got.Label}"); + return 1; + } + + Console.WriteLine($"[smoke] PASS: round-trip Id={got.Id} Value={got.Value} Label={got.Label}"); + return 0; + } + catch (Exception ex) + { + Console.Error.WriteLine($"[smoke] FAIL: {ex}"); + return 1; + } + } + } +} diff --git a/examples/PackageSmokeTest/nuget.config b/examples/PackageSmokeTest/nuget.config new file mode 100644 index 0000000..ca4635b --- /dev/null +++ b/examples/PackageSmokeTest/nuget.config @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/src/CycloneDDS.Runtime/CycloneDDS.Runtime.csproj b/src/CycloneDDS.Runtime/CycloneDDS.Runtime.csproj index ca1a998..7c8cce5 100644 --- a/src/CycloneDDS.Runtime/CycloneDDS.Runtime.csproj +++ b/src/CycloneDDS.Runtime/CycloneDDS.Runtime.csproj @@ -13,7 +13,7 @@ - + - - - - - + + + + + - + @@ -63,7 +63,7 @@ - + @@ -83,6 +83,14 @@ + + + diff --git a/tools/DdsMonitor/version.json b/tools/DdsMonitor/version.json index 8564c5f..cb201cd 100644 --- a/tools/DdsMonitor/version.json +++ b/tools/DdsMonitor/version.json @@ -4,7 +4,8 @@ "publicReleaseRefSpec": [ "^refs/heads/main$", "^refs/heads/master$", - "^refs/heads/v\\d+(?:\\.\\d+)?$" + "^refs/heads/v\\d+(?:\\.\\d+)?$", + "^refs/tags/v\\d+\\.\\d+" ], "cloudBuild": { "buildNumber": { diff --git a/version.json b/version.json index db804ce..c75a435 100644 --- a/version.json +++ b/version.json @@ -4,7 +4,8 @@ "publicReleaseRefSpec": [ "^refs/heads/main$", "^refs/heads/master$", - "^refs/heads/v\\d+(?:\\.\\d+)?$" + "^refs/heads/v\\d+(?:\\.\\d+)?$", + "^refs/tags/v\\d+\\.\\d+" ], "cloudBuild": { "buildNumber": {