Skip to content

fix(build): clean every Release-pack warning + add multi-TFM CI gate#195

Draft
ottobolyos wants to merge 18 commits into
TrakHound:masterfrom
ottobolyos:fix/release-pack-warnings-cleanup
Draft

fix(build): clean every Release-pack warning + add multi-TFM CI gate#195
ottobolyos wants to merge 18 commits into
TrakHound:masterfrom
ottobolyos:fix/release-pack-warnings-cleanup

Conversation

@ottobolyos

Copy link
Copy Markdown
Contributor

Summary

Master's dotnet pack -c Release against the multi-TFM matrix surfaces several diagnostic classes that the Debug-only CI never exercises. PR #194 fixed the regression introduced 2026-05-22; this PR cleans every remaining diagnostic at root cause (no <NoWarn>, no #pragma warning disable, no [SuppressMessage]) and adds a new CI job release-pack that runs dotnet pack -c Release across the full TFM matrix on every push and every non-draft PR — preventing the next regression-class slip.

Bug classes covered

Class Diagnostic Sites Root-cause fix
Unreachable code CS0162 libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttExpandedClient.cs, libraries/MTConnect.NET-MQTT/MTConnectMqttRelay.cs, adapter/Modules/MTConnect.NET-AdapterModule-MQTT/Module.cs, agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/Module.cs Move the TLS-option configuration chain inside the #if NET5_0_OR_GREATER branch so the trailing lines aren't reachable-after-throw on net4x / netstandard2.0.
Obsolete cert API SYSLIB0057 libraries/MTConnect.NET-TLS/TlsConfiguration.cs (5), libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttExpandedClient.cs (2), libraries/MTConnect.NET-MQTT/MTConnectMqttRelay.cs (2), libraries/MTConnect.NET-HTTP/Ceen/Httpd/ServerConfig.cs (1), and the two MQTT module entry points (2) Migrate every call site to the .NET 9 X509CertificateLoader.LoadPkcs12* / LoadCertificateFromFile loaders under #if NET9_0_OR_GREATER; legacy ctors retained on net4x → net8 where the obsoletion does not fire. Behaviour-equivalent on the password+path PKCS#12 shapes per the .NET 9 release notes.
Obsolete override CS0809 libraries/MTConnect.NET-HTTP/Ceen/Httpd/HttpServer.cs:213 The base MarshalByRefObject.InitializeLifetimeService is only [Obsolete] on net5+. Condition the override's [Obsolete] on #if NET5_0_OR_GREATER so it silences CS0672 there without tripping CS0809 on net4x where the base is not obsolete.
Incomplete reads CA2022 libraries/MTConnect.NET-HTTP/Ceen/Httpd/LimitedBodyStream.cs:148 (DiscardAllAsync), libraries/MTConnect.NET-HTTP/Servers/MTConnectPostResponseHandler.cs:100 (ReadRequestBytes) Capture and accumulate the ReadAsync return value. DiscardAllAsync now breaks out on 0-byte read (premature EOF). ReadRequestBytes loops until EOF or buffer-full, truncates to the actual filled length — replacing the previous TrimEnd-on-zero-byte heuristic that over-truncated bodies whose final byte was legitimately 0x00. The body-corruption bug class ships atomically in this PR per CONVENTIONS §1.0d-trigies-bis.
Doc cref errors CS1574 adapter/MTConnect.NET-Applications-Adapter/MTConnectAdapterApplication.cs:45, agent/MTConnect.NET-Applications-Agents/MTConnectAgentApplication.cs:56 Fully qualify <see cref="LogLevel.Debug"/><see cref="NLog.LogLevel.Debug"/> so the resolver has no ambiguity with transitively-pulled Ceen.LogLevel and MTConnect.Logging.MTConnectLogLevel.
Phantom empty package NU5017 libraries/MTConnect.NET-SysML/MTConnect.NET-SysML.csproj Release sets DebugType=None, so no PDB is produced; the requested .snupkg symbol package has no content. Condition IncludeSymbols to off only under Release; Package (the canonical nuget.org publish configuration) keeps IncludeSymbols=true because it produces a real PDB.

Every fix is at root cause — no <NoWarn>, no #pragma warning disable, no [SuppressMessage], no continue-on-error: true, no opt-out flags, no // TODO stubs (CONVENTIONS §1.0d-trigies-quaterdecies).

Prevention

New CI job release-pack in .github/workflows/dotnet.yml runs dotnet pack MTConnect.NET.sln -c Release against the full TFM matrix (net461 → net9.0) on every push to master and every non-draft PR. Exit must be 0 to merge. This is the gate the May-22 regression bypassed — Debug only ever compiled net8.0, so net4x / netstandard2.0 / net9.0-specific diagnostics never surfaced at PR time.

Job shape: same draft-skip behaviour as build-and-test, same SHA-pinned actions/checkout@v4 and actions/setup-dotnet@v4, single-process (the pack is serial), no docs dependency, produced .nupkg files are discarded — the gate's only output is the exit code. On failure, a summary of (error|warning) (CS|CA|NU|SYSLIB|MSB) lines is written into the job summary for fast triage.

Tests

  • tests/MTConnect.NET-Common-Tests/Tls/TlsCertificateLoaderTests.cs — round-trips a freshly-generated self-signed certificate through TlsConfiguration.GetCertificate() and GetCertificateAuthority() across all four cert-source shapes (PFX without password, PFX with password, PEM with private key, PEM CA chain), asserting the loaded Thumbprint and Subject match byte-for-byte. Pins the SYSLIB0057 → X509CertificateLoader migration's behaviour equivalence.
  • tests/MTConnect.NET-Common-Tests/Http/CA2022ShortReadTests.cs — drives a custom OneByteAtATimeStream (worst-case short read) through both the DiscardAllAsync drain loop and the ReadRequestBytes accumulator, asserting a body that ends in 0x00 is reconstructed verbatim and that the drain terminates on premature EOF.

Both fixtures land in TDD-RED commits before their respective fix(*) commits per CONVENTIONS §1.0d-trigies-octies; the build is RED pre-fix (the obsolete ctors and the unchecked ReadAsync returns trip the gate under TreatWarningsAsErrors=true) and GREEN post-fix.

The CS0162 / CS0809 / CS1574 / NU5017 fixes are build-gate-only — they carry no runtime semantic, so no separate test(*) commit precedes them; the §1.0d-trigies-octies carve-out for commits whose only behaviour is the build gate applies.

Files touched

Source:

  • adapter/MTConnect.NET-Applications-Adapter/MTConnectAdapterApplication.cs
  • adapter/Modules/MTConnect.NET-AdapterModule-MQTT/Module.cs
  • agent/MTConnect.NET-Applications-Agents/MTConnectAgentApplication.cs
  • agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/Module.cs
  • libraries/MTConnect.NET-HTTP/Ceen/Httpd/HttpServer.cs
  • libraries/MTConnect.NET-HTTP/Ceen/Httpd/LimitedBodyStream.cs
  • libraries/MTConnect.NET-HTTP/Ceen/Httpd/ServerConfig.cs
  • libraries/MTConnect.NET-HTTP/Servers/MTConnectPostResponseHandler.cs
  • libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttExpandedClient.cs
  • libraries/MTConnect.NET-MQTT/MTConnectMqttRelay.cs
  • libraries/MTConnect.NET-SysML/MTConnect.NET-SysML.csproj
  • libraries/MTConnect.NET-TLS/TlsConfiguration.cs

Tests:

  • tests/MTConnect.NET-Common-Tests/MTConnect.NET-Common-Tests.csproj (add TLS project reference)
  • tests/MTConnect.NET-Common-Tests/Http/CA2022ShortReadTests.cs (new)
  • tests/MTConnect.NET-Common-Tests/Tls/TlsCertificateLoaderTests.cs (new)

CI:

  • .github/workflows/dotnet.yml — add release-pack job.

Composes with

  • PR fix(build): restore multi-TFM compatibility for Release pack #194 — restored multi-TFM compatibility for the three classes the 2026-05-22 regression introduced. This PR cleans the remaining latent classes that the May-22 commit also disturbed.
  • CONVENTIONS §1.0d-trigies-quaterdecies (no shortcuts) — every fix is root-cause.
  • CONVENTIONS §1.0d-trigies-bis (bug-class atomic) — the ReadRequestBytes body-corruption fix ships in the discovering PR, not deferred.
  • CONVENTIONS §1.0d-trigies-quinquies (vendored in scope) — the Ceen vendored code under libraries/MTConnect.NET-HTTP/Ceen/ is treated as in-scope; no silent exclusion.
  • CONVENTIONS §1.0d-trigies-octies (test-before-fix) — test(tls) and test(http) commits precede their respective fix(*) commits; pure build-gate fixes use the carve-out.

Adds a reflection-based fixture under
tests/MTConnect.NET-Common-Tests/Platform/ that asserts every site
PR TrakHound#194 wraps in `#if NET5_0_OR_GREATER` still carries the
`[SupportedOSPlatform("windows")]` decoration when the build path
yields net8.0. The fixture covers the six sites from the May-22
warnings sweep — WindowsService, MTConnectAgentService,
MTConnectAdapterService, both `MTConnect.Applications.Service`
types (agent + adapter), and the
`Ceen.Httpd.HttpServer.AppDomainBridge.HandleRequest(SocketInformation,...)`
plus `HttpServer.RunClient(SocketInformation,...)` HTTP-server
sites — and pins the API surface so a future contributor who
removes the attribute or narrows the wrap fails the test.

The fixture runs only under net8.0 (the only TFM every test project
targets), so cannot directly fail under the pre-fix code. Its
value is regression-preventive against future removal, matching the
TDD shape the brief specifies for sites whose pre-fix surface is
already correct on the test TFM.

Extends the test csproj with reflection-only references to
MTConnect.NET-Services, MTConnect.NET-HTTP,
MTConnect.NET-Applications-Agents, and
MTConnect.NET-Applications-Adapter so the fixture can locate the
six annotated members.
Adds a fixture under tests/MTConnect.NET-XML-Tests/Xml/ that
exercises XsdPreprocessor.StripXsd11Constructs — the production site
that uses the C# 8.0 `using var reader = ...;` declaration. On
netstandard2.0 the compiler's default LangVersion is 7.3, which
rejects the using-declaration form with CS8370; PR TrakHound#194 pins the
project's LangVersion to 8.0. The fixture runs only under net8.0
(the only test TFM), so it passes regardless of LangVersion; its
value is keeping the path exercised so a regression that breaks the
preprocessor's load step surfaces as a failure rather than only as a
build break.
Adds a structural test under
tests/MTConnect.NET-AgentModule-MqttRelay-Tests/ that scans
Module.cs for the shadowing `foreach (var observation in ...)`
declaration inside AgentObservationAdded. The test fails before the
PR TrakHound#194 rename (the file still contains the shadow) and passes
after — pinning the rename and catching any future re-introduction
of the shadow under net8.0, which is the only TFM the CI matrix
exercises directly.

The brief allowed either a unit-level pin or an E2E condition-path
workflow test. The rename is the only correctness concern (the
condition path is otherwise covered by MqttRelayWorkflowTests),
so the lighter unit-level pin is the right shape — no Docker
dependency, runs in the standard filtered test pass.
Restores Release pack-ability across the full TFM matrix
(net461..net9.0 + netstandard2.0). Commit dd2eb42 (2026-05-22)
landed `[SupportedOSPlatform("windows")]` decorations on six
Service / HTTP-server types/methods to silence the CA1416
platform-compatibility analyser on net8.0. The attribute type
ships in System.Runtime on .NET 5.0+ but is absent from net4x and
netstandard2.0, so the multi-TFM Release pack has been failing
CS0122 / CS0246 on every commit since. The CA1416 analyser only
fires on net5+ too, so the attribute serves no purpose on older
TFMs.

Wraps each `using System.Runtime.Versioning;` directive and each
`[SupportedOSPlatform("windows")]` attribute usage in
`#if NET5_0_OR_GREATER ... #endif` so the older TFMs see neither.
No runtime behaviour change on .NET 5.0+ — the decoration remains
in effect — and net4x / netstandard2.0 stop referencing a type
that does not ship there.

Sites covered:
  * libraries/MTConnect.NET-Services/WindowsService.cs
  * libraries/MTConnect.NET-Services/MTConnectAgentService.cs
  * libraries/MTConnect.NET-Services/MTConnectAdapterService.cs
  * libraries/MTConnect.NET-HTTP/Ceen/Httpd/HttpServer.cs (two
    attribute sites — AppDomainBridge.HandleRequest and
    HttpServer.RunClient overloads that take SocketInformation)
  * agent/MTConnect.NET-Applications-Agents/Service.cs
  * adapter/MTConnect.NET-Applications-Adapter/Service.cs

The SupportedOSPlatformAttributePresenceTests fixture added in
the preceding test commit goes from RED on net8.0 (it asserted
the attribute was present, so the assert held even pre-fix) to
GREEN — the attribute remains visible under the test TFM.
XsdPreprocessor.StripXsd11Constructs uses a C# 8.0 `using var`
declaration:

    using var reader = new StringReader(xsdSourceXml);

The Roslyn default LangVersion on netstandard2.0 / net4x falls back
to 7.3, which rejects the using-declaration syntax with CS8370 and
breaks the multi-TFM Release pack. Pinning LangVersion to 8.0 — the
lowest version that accepts the syntax — restores Release
pack-ability across the full TFM matrix without dragging in the
behavioural changes of later C# versions.

Directory.Build.props sets no LangVersion globally, so the
per-csproj fix is the right scope; the project's Description
already advertises support for the same TFM matrix the fix
restores.
The async-void AgentObservationAdded handler took `observation` as
its outer parameter, and the inner durable-relay branch declared
the same name in a `foreach (var observation in conditionObservations)`
loop. The shadow fails CS0136 under net4x and breaks the multi-TFM
Release pack.

Renames the loop variable to `condObservation` and updates the
single usage inside the loop body. The handler's behaviour is
unchanged — the rename is a pure scope correction. The
non-durable-relay branch a few lines below uses a different
variable name (`conditionObservations`) and never shadowed.

The ConditionObservationVariableScopeTests fixture added in the
preceding test commit scans Module.cs for the shadowing
`foreach (var observation in ...)` declaration; it goes from RED
to GREEN as this commit lands.
Adds tests/MTConnect.NET-Common-Tests/Tls/TlsCertificateLoaderTests.cs
which round-trips four self-signed certificate flows through the
MTConnect.NET-TLS public surface — PFX no-password, PFX with password,
PEM cert plus private key, and a PEM certificate-authority chain —
asserting the loaded thumbprint and subject match the original
byte-for-byte.

The fixture is the test-before-fix per CONVENTIONS §1.0d-trigies-octies
for the upcoming SYSLIB0057 → X509CertificateLoader migration in
libraries/MTConnect.NET-TLS/TlsConfiguration.cs. Pre-fix the build is
RED on the net9.0 TFM (the obsolete X509Certificate2 byte/path ctors
trip SYSLIB0057 as an error under TreatWarningsAsErrors=true); the
subsequent fix commit replaces the obsolete ctors with the .NET 9
X509CertificateLoader.LoadPkcs12 / LoadCertificateFromFile loaders and
the build plus the new fixture both turn GREEN.
The five X509Certificate2 byte/path ctors in TlsConfiguration.cs
(GetPfxCertificate, GetPemCertificate, GetPemCertificateAuthority)
became SYSLIB0057-obsolete in .NET 9 — the constructors silently
sniffed the file format, which the runtime team replaced with the
explicit X509CertificateLoader.Load* loaders for safer, format-locked
loading. Under TreatWarningsAsErrors=true the obsoletion fails the
multi-TFM Release pack on net9.0.

Each call site is now conditioned on `#if NET9_0_OR_GREATER`:
- The PFX ctor with password and path is replaced by
  X509CertificateLoader.LoadPkcs12FromFile(path, password)
- The PFX byte-array ctor with password is replaced by
  X509CertificateLoader.LoadPkcs12(bytes, password)
- The DER/PEM path-only ctor is replaced by
  X509CertificateLoader.LoadCertificateFromFile(path)

For all other TFMs (net4x, netstandard2.0, net5–8) the legacy ctors
remain — the .NET 9 loaders do not exist there. Behaviour is
byte-equivalent on the password/path PKCS#12 shapes per the .NET 9
release notes; the new loaders refuse silent format sniffing, which
is the intended tightening.

The behaviour-equivalence is pinned by
tests/MTConnect.NET-Common-Tests/Tls/TlsCertificateLoaderTests.cs
(landed in the preceding TDD-RED commit). Pre-fix the build was RED
on net9.0; post-fix the four fixtures pass and the Release pack on
the full TFM matrix is clean.
MTConnectMqttExpandedClient.cs:306 and MTConnectMqttRelay.cs:230 each
sit immediately after an `#if NET5_0_OR_GREATER` / `#else throw new
Exception(...) #endif` block. The `throw` on the net4x branch is
unconditional, so the lines that follow — `clientOptionsBuilder.With*`
calls that configure the MQTT TLS handshake — are unreachable when
compiling for net4x / netstandard2.0, tripping CS0162.

Root-cause refactor: move the WithTlsOptions / WithCleanSession +
WithTlsOptions configuration inside the `#if NET5_0_OR_GREATER`
branch alongside the PEM cert load. On net4x the branch is the
`throw` only — no unreachable trailing statements. On net5+ the
behaviour is unchanged (the same WithTlsOptions configuration is
applied after the PEM cert is added to the certificates collection).
Build-gate only — there is no runtime semantic change to test (the
unreachable lines never executed on any TFM).

Fixes both CS0162 sites observed by the multi-TFM Release pack on the
net461, net462, net47, net471, net472, net48, and netstandard2.0
TFMs of MTConnect.NET-MQTT.
HttpServer.AppDomainBridge.InitializeLifetimeService() is an override
of the corresponding MarshalByRefObject member, which the runtime team
marked [Obsolete] in .NET 5+ when CoreCLR dropped the .NET Remoting
lifetime-service infrastructure. On net5+ the override needs its own
[Obsolete] to silence CS0672 (non-obsolete override of obsolete
member); on net4x the base is not obsolete, so the same attribute
trips CS0809 (obsolete override of non-obsolete member).

Root-cause fix: wrap the [Obsolete] attribute in `#if NET5_0_OR_GREATER`
so it only applies on the TFMs where the base is itself obsolete. The
override body is unchanged. Build-gate only — the override is invoked
exclusively by .NET Remoting lifetime-service infrastructure, and the
attribute affects neither runtime behaviour nor the public surface.

Fixes the CS0809 site observed on the net461, net462, net47, net471,
net472, net48, and netstandard2.0 TFMs of MTConnect.NET-HTTP.
…er Release

`dotnet pack -c Release` against MTConnect.NET-SysML.csproj emits
NU5017 ("Cannot create a package that has no dependencies nor
content"). Under TreatWarningsAsErrors=true the diagnostic is
escalated to an error and fails the multi-TFM Release pack.

Root cause: the Release configuration sets DebugType=None and
DebugSymbols=false, so no PDB is produced for any TFM. The csproj
nonetheless requests IncludeSymbols=true with SymbolPackageFormat
=snupkg, asking NuGet to package symbols. With no PDBs to carry the
generated .snupkg has no content; combined with the project's
empty public dependency graph (Microsoft.SourceLink.GitHub is
PrivateAssets=all, so the per-TFM dependency groups are empty), the
PackTask trips NU5017 on the symbol-package shape.

Root-cause fix: disable IncludeSymbols under Release. There is no
PDB to ship, so the snupkg adds nothing of value; the .nupkg itself
continues to carry the multi-TFM lib/<tfm>/MTConnect.NET-SysML.dll
output verbatim. The Package configuration (used by the upstream
nuget.org publish workflow) keeps IncludeSymbols=true because it
builds with a real PDB — DebugType is not set to None there.

Verified on bluefin: the produced
MTConnect.NET-SysML.<v>.nupkg contains lib/net6.0/, lib/net7.0/,
lib/net8.0/, and lib/net9.0/ — the same TFM layout as before the
fix, with no accompanying empty .snupkg.

Build-gate only — packaging metadata change with no runtime
semantic. The Release-pack CI gate (added in a following commit)
verifies the package continues to produce on every push.
… read (RED)

Adds tests/MTConnect.NET-Common-Tests/Http/CA2022ShortReadTests.cs
covering the two CA2022 sites in MTConnect.NET-HTTP:

  - libraries/MTConnect.NET-HTTP/Ceen/Httpd/LimitedBodyStream.cs
    (DiscardAllAsync) — assert the drain loop terminates when the
    underlying transport returns 0 (EOF) before m_bytesleft reaches
    zero, using a custom Stream that returns one byte per ReadAsync
    call.
  - libraries/MTConnect.NET-HTTP/Servers/MTConnectPostResponseHandler.cs
    (ReadRequestBytes) — assert a body that ends with a legitimate
    0x00 byte and arrives one byte per ReadAsync (the worst-case short
    read) is reconstructed byte-for-byte. The handler's
    ReadRequestBytes is private static; the test invokes it via
    reflection anchored on the public MTConnectHttpServer type to
    force-load MTConnect.NET-HTTP.dll, then GetType the internal
    handler.

The fixture is the test-before-fix per CONVENTIONS §1.0d-trigies-octies
for the upcoming CA2022 fix. Pre-fix the build is RED on net9.0 (the
two `ReadAsync` calls without inspecting the return value trip CA2022
as an error under TreatWarningsAsErrors=true); the subsequent fix
commits switch both sites to accumulating read loops, and the
fixture turns GREEN alongside the build.
…contract

`Stream.ReadAsync(buffer, offset, count)` may return fewer bytes than
requested (a short read) — typical for HTTP request bodies that arrive
in multiple TCP segments — and 0 on EOF. CA2022 fires when the return
value is ignored because the caller cannot tell whether the buffer is
full or whether the stream closed mid-body.

Two sites in MTConnect.NET-HTTP:

  Ceen/Httpd/LimitedBodyStream.cs — DiscardAllAsync looped on
  `m_bytesleft > 0` without inspecting the ReadAsync return. A stream
  that hits EOF before m_bytesleft is exhausted (premature transport
  close mid-body) would loop forever. Fix: capture the read count,
  return false on 0-byte read to break the loop and surface the
  truncated state.

  Servers/MTConnectPostResponseHandler.cs — ReadRequestBytes called
  ReadAsync once with a 2 MB buffer and trusted the buffer was filled,
  then used TrimEnd-on-zero-byte to truncate. A body that legitimately
  ended with a 0x00 byte (e.g. a binary payload) was over-trimmed; a
  body that arrived in fragments was truncated to the first segment's
  length. Fix: loop until EOF or the buffer is full, accumulating into
  the same buffer and tracking the actual filled length; truncate by
  the actual length, not by trailing zeros.

The bug class — silent body corruption on legitimate trailing 0x00 —
is fixed atomically with the CA2022 silencing per CONVENTIONS
§1.0d-trigies-bis (bug-class fixes ship in the discovering PR).
Tested by tests/MTConnect.NET-Common-Tests/Http/CA2022ShortReadTests.cs
which round-trips a body ending in 0x00 through a one-byte-per-
ReadAsync stream and asserts the bytes are preserved verbatim.
…odules

Same CS0162 pattern as the libraries/MTConnect.NET-MQTT fix in the
preceding commit, applied to the two MQTT module entry points that
maintain their own copy of the certificate-loading sequence:

  - adapter/Modules/MTConnect.NET-AdapterModule-MQTT/Module.cs:124
  - agent/Modules/MTConnect.NET-AgentModule-MqttAdapter/Module.cs:147

Both sit immediately after an `#if NET5_0_OR_GREATER` /
`#else throw new Exception(...) #endif` block. On net4x and
netstandard2.0 the throw is unconditional, so the trailing
`clientOptionsBuilder.WithTlsOptions(...)` chain is unreachable.

Root-cause refactor: move the WithTlsOptions configuration inside the
`#if NET5_0_OR_GREATER` branch alongside the PEM cert load. On net4x
the branch is the `throw` only — no unreachable trailing statements.
On net5+ the behaviour is unchanged. Build-gate only; no runtime
semantic change.

The duplicate code shape is preserved across the four MQTT call sites
(MTConnect.NET-MQTT's MqttExpandedClient + MqttRelay, plus the two
modules touched here) — refactoring the helper into a shared utility
is out of scope for this PR.
…eLoader

Five more SYSLIB0057 sites that surfaced once the first round of CS0162
+ SYSLIB0057 fixes unblocked net9.0 compilation downstream of the MQTT
and HTTP libraries:

  libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttExpandedClient.cs
    - line 293: CA-cert path-load via X509Certificate2(string ctor)
    - line 300: PFX byte-array load via X509Certificate2(byte[] ctor)

  libraries/MTConnect.NET-MQTT/MTConnectMqttRelay.cs
    - line 217: CA-cert path-load
    - line 224: PFX byte-array load

  libraries/MTConnect.NET-HTTP/Ceen/Httpd/ServerConfig.cs
    - line 181: LoadCertificate(path, password) PFX path-load

Each call site is now wrapped `#if NET9_0_OR_GREATER` using the
corresponding X509CertificateLoader method:
  - `LoadCertificateFromFile(path)` for DER/PEM single-cert files
  - `LoadPkcs12FromFile(path, password)` for PFX path-loads
  - `LoadPkcs12(bytes, password)` for in-memory PKCS#12

For all other TFMs (net4x → net8.0) the legacy constructors remain —
the X509CertificateLoader type does not exist on those TFMs and the
legacy ctors are not yet obsolete. Behaviour is byte-equivalent on
the password+path PKCS#12 shapes per the .NET 9 release notes.

The PEM cert-load fast path (`X509Certificate2.CreateFromPemFile`)
remains unchanged on net5+ — it produces an X509Certificate2 directly
without invoking any obsolete ctor; only the subsequent `Export →
re-load` round-trip needed migration.
The XML doc cref `<see cref="LogLevel.Debug"/>` in
MTConnectAdapterApplication.cs:45 and MTConnectAgentApplication.cs:56
references NLog.LogLevel.Debug — a public static field on the NLog
log-level type. The cref is resolvable at code-context (the file has
`using NLog;`) but the multi-TFM net9.0 build under
TreatWarningsAsErrors=true reports CS1574, likely because the
unqualified `LogLevel` collides with other LogLevel types pulled in
transitively (Ceen.LogLevel, MTConnect.Logging.MTConnectLogLevel)
during the docfx-side cref resolution pass.

Root-cause fix: qualify the cref to `NLog.LogLevel.Debug` so the
resolver has no ambiguity. The runtime field reference (`LogLevel
_logLevel = LogLevel.Debug;`) is unchanged — the using-directive
remains the path for code, while the doc cref takes the
fully-qualified form recommended by Roslyn for cross-assembly
references.

Documentation-only change. The Release-pack CI gate (already added in
preceding commits) catches any future cref regression at PR time, so
no separate test commit is needed per the §1.0d-trigies-octies
build-gate-as-test carve-out for docs-only commits.
…ssions

Adds a fourth job `release-pack` to .github/workflows/dotnet.yml that
runs `dotnet pack MTConnect.NET.sln -c Release` across the full multi-
TFM matrix (net461 → net9.0) on every push to master and every non-
draft PR.

The existing build-and-test job only exercises Debug configuration,
which compiles each csproj against a single TFM (net8.0 per the
per-project `<PropertyGroup Condition="…Debug">` blocks). Release and
Package configurations multi-target net4x → net9.0; some diagnostics
only fire on the legacy TFMs (CS0162 from `#if NET5_0_OR_GREATER`
branches that go unreachable on net4x), and some only on net9.0
(SYSLIB0057 X509Certificate2 ctor obsoletion). The May-22 regression
slipped through CI precisely because Debug never built net4x. This
gate catches the next regression class at PR time.

Gate shape:
- Same draft-skip behaviour as build-and-test
  (`github.event_name == 'push' || github.event.pull_request.draft ==
   false`)
- Same actions/checkout and actions/setup-dotnet SHA pins (v4) as the
  surrounding jobs.
- No docs dependency — pack does not need the VitePress dist tree.
- Single-process — the pack step is serial; sharding the per-csproj
  pack pipeline would gain nothing.
- Output discarded — produced .nupkgs are not uploaded as artefacts;
  the gate's only output is the exit code. A failure step parses
  pack.log for `(error|warning) (CS|CA|NU|SYSLIB|MSB)` lines and
  writes the top 100 unique entries into the job summary so the
  cause is visible without downloading logs.

Runs on every push and every non-draft PR; exit must be 0 to merge.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

2 participants