Skip to content

Commit d2f4942

Browse files
authored
👷 expanded CI with multi-target smoke tests (.NET 5–9, 10 preview), Windows .NET Framework smoke, and a compile-only .NET Core 3.1 check (#12)
1 parent 2f61405 commit d2f4942

File tree

2 files changed

+291
-10
lines changed

2 files changed

+291
-10
lines changed

.github/workflows/test.yml

Lines changed: 275 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
pull_request:
77
branches: [ main ]
88
workflow_call:
9-
9+
1010
permissions:
1111
contents: read
1212

@@ -40,23 +40,46 @@ jobs:
4040
run: dotnet format --verify-no-changes --verbosity minimal
4141
- name: Build (warnings -> errors)
4242
run: dotnet build --configuration Release -p:TreatWarningsAsErrors=true --no-restore
43+
- name: Pack NuGet (artifact for smoke_netcore31)
44+
run: |
45+
dotnet pack QsNet/QsNet.csproj -c Release -p:ContinuousIntegrationBuild=true -p:IncludeSymbols=false -p:Version=0.0.0-ci -o $RUNNER_TEMP/nupkg
46+
- name: Upload nupkg artifact
47+
uses: actions/upload-artifact@v4
48+
with:
49+
name: qsnet-nupkg
50+
path: ${{ runner.temp }}/nupkg/*.nupkg
51+
retention-days: 1
4352
test:
4453
name: Test ${{ matrix.dotnet }} on ${{ matrix.os }}
4554
needs: lint
4655
runs-on: ${{ matrix.os }}
56+
continue-on-error: ${{ matrix.experimental }}
4757
strategy:
4858
fail-fast: false
4959
matrix:
50-
os: [ ubuntu-latest ]
51-
dotnet:
52-
- '8.0.x'
53-
- '9.0.x'
60+
include:
61+
- os: ubuntu-latest
62+
dotnet: '8.0.x'
63+
experimental: false
64+
- os: ubuntu-latest
65+
dotnet: '9.0.x'
66+
experimental: false
67+
- os: ubuntu-latest
68+
dotnet: '10.0.x'
69+
experimental: true
5470
steps:
5571
- uses: actions/checkout@v5
56-
- name: Setup .NET SDK
72+
- name: Setup .NET SDK (stable)
73+
if: ${{ matrix.dotnet != '10.0.x' }}
5774
uses: actions/setup-dotnet@v4
5875
with:
5976
dotnet-version: ${{ matrix.dotnet }}
77+
- name: Setup .NET SDK (10 preview)
78+
if: ${{ matrix.dotnet == '10.0.x' }}
79+
uses: actions/setup-dotnet@v4
80+
with:
81+
dotnet-version: ${{ matrix.dotnet }}
82+
dotnet-quality: preview
6083
- name: Cache NuGet
6184
uses: actions/cache@v4
6285
with:
@@ -91,7 +114,6 @@ jobs:
91114
name: Ensure compatibility with qs (JS vs C#)
92115
needs: lint
93116
runs-on: ubuntu-latest
94-
95117
steps:
96118
- name: Checkout
97119
uses: actions/checkout@v5
@@ -130,12 +152,257 @@ jobs:
130152
node.out
131153
cs.out
132154
diff.out
155+
smoke:
156+
name: Consumer smoke (net5.0; net6.0; net7.0; net8.0; net9.0)
157+
needs: lint
158+
runs-on: ubuntu-latest
159+
steps:
160+
- uses: actions/checkout@v5
161+
- name: Setup .NET SDKs (5, 6, 7, 8, 9)
162+
uses: actions/setup-dotnet@v4
163+
with:
164+
dotnet-version: |
165+
5.0.x
166+
6.0.x
167+
7.0.x
168+
8.0.x
169+
9.0.x
170+
- name: Setup .NET 10 preview SDK (non-blocking)
171+
continue-on-error: true
172+
uses: actions/setup-dotnet@v4
173+
with:
174+
dotnet-version: 10.0.x
175+
dotnet-quality: preview
176+
- name: Create consumer project (multi-target)
177+
run: |
178+
set -euo pipefail
179+
mkdir -p consumer-smoke
180+
cat > consumer-smoke/consumer-smoke.csproj <<'XML'
181+
<Project Sdk="Microsoft.NET.Sdk">
182+
<PropertyGroup>
183+
<OutputType>Exe</OutputType>
184+
<TargetFrameworks>net5.0;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
185+
<Nullable>enable</Nullable>
186+
<ImplicitUsings>false</ImplicitUsings>
187+
</PropertyGroup>
188+
<ItemGroup>
189+
<ProjectReference Include="../QsNet/QsNet.csproj" />
190+
</ItemGroup>
191+
</Project>
192+
XML
193+
cat > consumer-smoke/Program.cs <<'CS'
194+
using System;
195+
using System.Collections.Generic;
196+
using QsNet;
197+
class P
198+
{
199+
static int Main()
200+
{
201+
try
202+
{
203+
// Decode should not throw and should return a non-null object graph.
204+
var decoded = Qs.Decode("foo[bar]=baz&foo[list][]=a&foo[list][]=b");
205+
// Encode should produce a non-empty query string.
206+
var qs = Qs.Encode(new Dictionary<string, object?> {
207+
["foo"] = new Dictionary<string, object?> { ["bar"] = "baz" }
208+
});
209+
if (string.IsNullOrEmpty(qs))
210+
throw new Exception("Encode returned empty string");
211+
Console.WriteLine("SMOKE OK");
212+
return 0;
213+
}
214+
catch (Exception ex)
215+
{
216+
Console.Error.WriteLine(ex);
217+
return 1;
218+
}
219+
}
220+
}
221+
CS
222+
- name: Restore consumer
223+
run: dotnet restore consumer-smoke/consumer-smoke.csproj
224+
- name: Build consumer (net6.0; net7.0; net8.0; net9.0)
225+
run: |
226+
set -euo pipefail
227+
for f in net6.0 net7.0 net8.0 net9.0; do
228+
echo "Building $f"
229+
dotnet build -c Release -f "$f" consumer-smoke/consumer-smoke.csproj --no-restore
230+
done
231+
- name: Build consumer (net5.0 — non-blocking)
232+
continue-on-error: true
233+
run: |
234+
set -euo pipefail
235+
dotnet build -c Release -f net5.0 consumer-smoke/consumer-smoke.csproj --no-restore
236+
- name: Build consumer (net10.0 preview — non-blocking)
237+
continue-on-error: true
238+
run: |
239+
set -euo pipefail
240+
dotnet build -c Release -f net10.0 consumer-smoke/consumer-smoke.csproj --no-restore
241+
- name: Run smoke (net5.0 — non-blocking)
242+
continue-on-error: true
243+
run: |
244+
set -euo pipefail
245+
dotnet run -c Release --framework net5.0 --project consumer-smoke/consumer-smoke.csproj --no-build
246+
- name: Run smoke (net6.0; net7.0; net8.0; net9.0)
247+
run: |
248+
set -euo pipefail
249+
for f in net6.0 net7.0 net8.0 net9.0; do
250+
echo "Running smoke for $f"
251+
dotnet run -c Release --framework "$f" --project consumer-smoke/consumer-smoke.csproj --no-build
252+
done
253+
- name: Run smoke (net10.0 preview — non-blocking)
254+
continue-on-error: true
255+
run: |
256+
set -euo pipefail
257+
dotnet run -c Release --framework net10.0 --project consumer-smoke/consumer-smoke.csproj --no-build
258+
smoke_netcore31:
259+
name: Consumer smoke (netcoreapp3.1 compile-only)
260+
needs: lint
261+
runs-on: ubuntu-latest
262+
container:
263+
image: mcr.microsoft.com/dotnet/sdk:3.1
264+
continue-on-error: true
265+
steps:
266+
- uses: actions/checkout@v5
267+
- name: Download nupkg
268+
uses: actions/download-artifact@v4
269+
with:
270+
name: qsnet-nupkg
271+
path: ./nupkg
272+
- name: Verify local nupkg
273+
run: |
274+
set -eu
275+
echo "Listing $GITHUB_WORKSPACE/nupkg";
276+
ls -la "$GITHUB_WORKSPACE/nupkg" || { echo "nupkg directory missing"; exit 1; }
277+
- name: Ensure git in container
278+
run: |
279+
set -eu
280+
if ! command -v git >/dev/null 2>&1; then
281+
apt-get update
282+
apt-get install -y git ca-certificates
283+
rm -rf /var/lib/apt/lists/*
284+
fi
285+
- name: Create consumer project (netcoreapp3.1)
286+
run: |
287+
set -eu
288+
mkdir -p consumer-core31
289+
cat > consumer-core31/consumer-core31.csproj <<'XML'
290+
<Project Sdk="Microsoft.NET.Sdk">
291+
<PropertyGroup>
292+
<OutputType>Exe</OutputType>
293+
<TargetFramework>netcoreapp3.1</TargetFramework>
294+
<Nullable>enable</Nullable>
295+
<ImplicitUsings>false</ImplicitUsings>
296+
<LangVersion>8.0</LangVersion>
297+
</PropertyGroup>
298+
<ItemGroup>
299+
<PackageReference Include="QsNet" Version="0.0.0-ci" />
300+
</ItemGroup>
301+
</Project>
302+
XML
303+
cat > consumer-core31/Program.cs <<'CS'
304+
using System;
305+
using System.Collections.Generic;
306+
using QsNet;
307+
class P
308+
{
309+
static int Main()
310+
{
311+
// compile-only smoke; not executed in CI
312+
var decoded = Qs.Decode("a[b]=c&x[]=1&x[]=2");
313+
var qs = Qs.Encode(new Dictionary<string, object?> {
314+
["a"] = new Dictionary<string, object?> { ["b"] = "c" }
315+
});
316+
return string.IsNullOrEmpty(qs) ? 1 : 0;
317+
}
318+
}
319+
CS
320+
- name: Restore consumer
321+
run: dotnet restore consumer-core31/consumer-core31.csproj --source "$GITHUB_WORKSPACE/nupkg" --source https://api.nuget.org/v3/index.json
322+
- name: Build consumer (compile only)
323+
run: dotnet build -c Release consumer-core31/consumer-core31.csproj --no-restore
324+
smoke_netfx:
325+
name: Consumer smoke (.NET Framework 4.6.1 & 4.8.1 on Windows)
326+
needs: lint
327+
runs-on: windows-latest
328+
steps:
329+
- uses: actions/checkout@v5
330+
- name: Setup .NET SDK
331+
uses: actions/setup-dotnet@v4
332+
with:
333+
dotnet-version: |
334+
8.0.x
335+
- name: Create consumer project (net461 & net481)
336+
shell: bash
337+
run: |
338+
set -euo pipefail
339+
mkdir -p consumer-netfx
340+
cat > consumer-netfx/consumer-netfx.csproj <<'XML'
341+
<Project Sdk="Microsoft.NET.Sdk">
342+
<PropertyGroup>
343+
<OutputType>Exe</OutputType>
344+
<TargetFrameworks>net461;net481</TargetFrameworks>
345+
<Nullable>enable</Nullable>
346+
<ImplicitUsings>false</ImplicitUsings>
347+
<LangVersion>8.0</LangVersion>
348+
</PropertyGroup>
349+
<ItemGroup>
350+
<ProjectReference Include="../QsNet/QsNet.csproj" />
351+
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
352+
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
353+
</ItemGroup>
354+
</Project>
355+
XML
356+
cat > consumer-netfx/Program.cs <<'CS'
357+
using System;
358+
using System.Collections.Generic;
359+
using QsNet;
360+
class P
361+
{
362+
static int Main()
363+
{
364+
try
365+
{
366+
var decoded = Qs.Decode("foo[bar]=baz&foo[list][]=a&foo[list][]=b");
367+
var qs = Qs.Encode(new Dictionary<string, object?> {
368+
["foo"] = new Dictionary<string, object?> { ["bar"] = "baz" }
369+
});
370+
if (string.IsNullOrEmpty(qs))
371+
throw new Exception("Encode returned empty string");
372+
#if NET461
373+
Console.WriteLine("SMOKE net461 OK");
374+
#elif NET481
375+
Console.WriteLine("SMOKE net481 OK");
376+
#else
377+
Console.WriteLine("SMOKE NETFX OK");
378+
#endif
379+
return 0;
380+
}
381+
catch (Exception ex)
382+
{
383+
Console.Error.WriteLine(ex);
384+
return 1;
385+
}
386+
}
387+
}
388+
CS
389+
- name: Restore consumer
390+
run: dotnet restore consumer-netfx/consumer-netfx.csproj
391+
- name: Build consumer
392+
run: dotnet build -c Release consumer-netfx/consumer-netfx.csproj --no-restore
393+
- name: Run smoke (net461)
394+
run: dotnet run -c Release --framework net461 --project consumer-netfx/consumer-netfx.csproj --no-build
395+
- name: Run smoke (net481)
396+
run: dotnet run -c Release --framework net481 --project consumer-netfx/consumer-netfx.csproj --no-build
133397
coverage:
134398
name: Coverage (merged)
135399
runs-on: ubuntu-latest
136-
needs:
400+
needs:
137401
- test
138402
- ensure_compatibility
403+
- smoke
404+
- smoke_netcore31
405+
- smoke_netfx
139406
steps:
140407
- uses: actions/checkout@v5
141408
- name: Setup .NET 8 SDK

README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ A query string encoding and decoding library for C#/.NET.
88

99
Ported from [qs](https://www.npmjs.com/package/qs) for JavaScript.
1010

11-
[![Targets](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Ftechouse%2Fqs-net%2Fmain%2FQsNet%2FQsNet.csproj&query=string(//TargetFrameworks|//TargetFramework)&label=targets&logo=dotnet&color=512BD4&labelColor=222222&logoColor=white&style=flat-square&link=https%3A%2F%2Fwww.nuget.org%2Fpackages%2FQsNet)]((https://www.nuget.org/packages/QsNet))
11+
[![Targets](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Ftechouse%2Fqs-net%2Fmain%2FQsNet%2FQsNet.csproj&query=string(//TargetFrameworks%7C//TargetFramework)&label=targets&logo=dotnet&color=512BD4&labelColor=222222&logoColor=white&style=flat-square)](https://www.nuget.org/packages/QsNet)
1212
[![DocFX Docs](https://img.shields.io/badge/DocFX-docs-512BD4?logo=dotnet&logoColor=white&labelColor=222222&style=flat-square)](https://techouse.github.io/qs-net/)
1313
[![NuGet Version](https://img.shields.io/nuget/v/QsNet)](https://www.nuget.org/packages/QsNet)
1414
[![NuGet Downloads](https://img.shields.io/nuget/dt/QsNet)](https://www.nuget.org/packages/QsNet)
@@ -57,7 +57,21 @@ dotnet add package QsNet
5757

5858
## Requirements
5959

60-
- **Targets:** `net8.0`, `netstandard2.0`
60+
- **Target frameworks (TFMs):** `net8.0`, `netstandard2.0`
61+
- **Supported runtimes (via the target frameworks above):**
62+
63+
| Runtime | Version | CI Coverage | Status |
64+
|----------------|--------------|--------------------------------------|-----------|
65+
| .NET | 10 (preview) | Full CI (experimental; non-blocking) | Preview |
66+
| .NET | 9 (STS) | Full CI | Supported |
67+
| .NET | 8 (LTS) | Full CI | Supported |
68+
| .NET | 7 | Consumer smoke test | Supported |
69+
| .NET | 6 (LTS) | Consumer smoke test | Supported |
70+
| .NET | 5 | Optional smoke (non-blocking) | EOL |
71+
| .NET Core | 3.1 | Compile-only smoke | EOL |
72+
| .NET Framework | 4.6.1+ | Smoke test (4.6.1, 4.8.1) | Supported |
73+
74+
- **Platforms:** Windows, Linux, macOS (cross-platform, no native dependencies)
6175

6276
---
6377

0 commit comments

Comments
 (0)