@@ -11,9 +11,9 @@ permissions:
1111
1212jobs :
1313 package :
14- name : Package Windows artifacts
14+ name : Build portable Windows release
1515 runs-on : windows-latest
16- timeout-minutes : 35
16+ timeout-minutes : 40
1717
1818 steps :
1919 - name : Checkout
@@ -33,43 +33,61 @@ jobs:
3333 - name : Protocol smoke validation
3434 run : dotnet run --project .\src\PtpLabClock.Console --configuration Release -- --validate-protocol --domain 0 --export-pcap .\TestResults\ptp-validation.pcap
3535
36- - name : Publish WPF app - framework dependent
37- run : dotnet publish .\src\PtpLabClock.App\PtpLabClock.App.csproj -c Release -r win-x64 --self-contained false -p:PublishSingleFile=true -p:PublishReadyToRun=false -o .\artifacts\PtpLabClock.App.win-x64.framework-dependent
36+ - name : Publish portable single EXE files
37+ shell : pwsh
38+ run : |
39+ $ErrorActionPreference = "Stop"
40+ $publishRoot = Resolve-Path "."
41+ New-Item -ItemType Directory -Path .\artifacts -Force | Out-Null
42+ New-Item -ItemType Directory -Path .\publish\app -Force | Out-Null
43+ New-Item -ItemType Directory -Path .\publish\console -Force | Out-Null
44+
45+ $singleFileProps = @(
46+ "-p:PublishSingleFile=true",
47+ "-p:SelfContained=true",
48+ "-p:IncludeNativeLibrariesForSelfExtract=true",
49+ "-p:EnableCompressionInSingleFile=true",
50+ "-p:PublishReadyToRun=false",
51+ "-p:PublishTrimmed=false",
52+ "-p:DebugType=None",
53+ "-p:DebugSymbols=false"
54+ )
3855
39- - name : Publish WPF app - self contained
40- run : dotnet publish .\src\PtpLabClock.App \PtpLabClock.App .csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:PublishReadyToRun=false - o .\artifacts\PtpLabClock.App.win-x64.self-contained
56+ dotnet publish .\src\PtpLabClock.App\PtpLabClock.App.csproj -c Release -r win-x64 --self- contained true @singleFileProps -o .\publish\app
57+ dotnet publish .\src\PtpLabClock.Console \PtpLabClock.Console .csproj -c Release -r win-x64 --self-contained true @singleFileProps - o .\publish\console
4158
42- - name : Publish console - self contained
43- run : dotnet publish .\src\PtpLabClock.Console \PtpLabClock.Console.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:PublishReadyToRun=false -o .\artifacts\PtpLabClock.Console.win-x64.self-contained
59+ Copy-Item .\publish\app\PtpLabClock.App.exe .\artifacts\PtpLabClock.App.win-x64.portable.exe -Force
60+ Copy-Item .\publish\console \PtpLabClock.Console.exe .\artifacts\PtpLabClock.Console.win-x64.portable.exe -Force
4461
45- - name : Add release notes to packages
46- shell : pwsh
47- run : |
48- $packageDirs = Get-ChildItem .\artifacts -Directory
49- foreach ($dir in $packageDirs) {
50- Copy-Item .\LICENSE $dir.FullName -Force
51- Copy-Item .\NOTICE $dir.FullName -Force
52- Copy-Item .\THIRD-PARTY-NOTICES.md $dir.FullName -Force
53- Copy-Item .\docs\release-readme.md (Join-Path $dir.FullName "README-FIRST.md") -Force
54- }
62+ if (-not (Test-Path .\artifacts\PtpLabClock.App.win-x64.portable.exe)) { throw "App portable EXE was not created." }
63+ if (-not (Test-Path .\artifacts\PtpLabClock.Console.win-x64.portable.exe)) { throw "Console portable EXE was not created." }
5564
56- - name : Create ZIP packages
65+ - name : Create optional ZIP packages with notices
5766 shell : pwsh
5867 run : |
59- $zips = @(
60- "PtpLabClock.App.win-x64.framework-dependent",
61- "PtpLabClock.App.win-x64.self-contained" ,
62- "PtpLabClock.Console.win-x64.self-contained"
68+ $ErrorActionPreference = "Stop"
69+ $packages = @(
70+ @{ Name = "PtpLabClock.App.win-x64.portable"; Exe = ".\artifacts\PtpLabClock.App.win-x64.portable.exe" } ,
71+ @{ Name = "PtpLabClock.Console.win-x64.portable"; Exe = ".\artifacts\PtpLabClock.Console.win-x64.portable.exe" }
6372 )
64- foreach ($name in $zips) {
65- Compress-Archive -Path ".\artifacts\$name\*" -DestinationPath ".\artifacts\$name.zip" -Force
73+
74+ foreach ($package in $packages) {
75+ $dir = Join-Path ".\artifacts" $package.Name
76+ New-Item -ItemType Directory -Path $dir -Force | Out-Null
77+ Copy-Item $package.Exe (Join-Path $dir (Split-Path $package.Exe -Leaf)) -Force
78+ Copy-Item .\LICENSE $dir -Force
79+ Copy-Item .\NOTICE $dir -Force
80+ Copy-Item .\THIRD-PARTY-NOTICES.md $dir -Force
81+ Copy-Item .\docs\release-readme.md (Join-Path $dir "README-FIRST.md") -Force
82+ Compress-Archive -Path (Join-Path $dir "*") -DestinationPath ".\artifacts\$($package.Name).zip" -Force
6683 }
6784
68- - name : Generate checksums and release SBOM manifest
85+ - name : Generate checksums and SPDX-style release manifest
6986 shell : pwsh
7087 run : |
71- $zipFiles = Get-ChildItem .\artifacts -Filter *.zip | Sort-Object Name
72- $checksums = foreach ($file in $zipFiles) {
88+ $ErrorActionPreference = "Stop"
89+ $releaseFiles = Get-ChildItem .\artifacts -File | Where-Object { $_.Extension -in ".exe", ".zip" } | Sort-Object Name
90+ $checksums = foreach ($file in $releaseFiles) {
7391 $hash = Get-FileHash $file.FullName -Algorithm SHA256
7492 "$($hash.Hash.ToLowerInvariant()) $($file.Name)"
7593 }
7997 spdxVersion = "SPDX-2.3"
8098 dataLicense = "CC0-1.0"
8199 SPDXID = "SPDXRef-DOCUMENT"
82- name = "Process Bus Timing Lab release manifest"
100+ name = "Process Bus Timing Lab portable Windows release manifest"
83101 documentNamespace = "https://github.com/masarray/PtpLabClock/releases/$env:GITHUB_RUN_ID"
84102 creationInfo = [ordered]@{
85103 created = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
@@ -89,15 +107,16 @@ jobs:
89107 [ordered]@{ SPDXID = "SPDXRef-Package-PtpLabClock"; name = "PtpLabClock"; downloadLocation = "NOASSERTION"; filesAnalyzed = $false; licenseConcluded = "Apache-2.0"; licenseDeclared = "Apache-2.0"; copyrightText = "Copyright (C) 2026 Ari Sulistiono" },
90108 [ordered]@{ SPDXID = "SPDXRef-Package-SharpPcap"; name = "SharpPcap"; versionInfo = "6.3.1"; downloadLocation = "https://www.nuget.org/packages/SharpPcap/6.3.1"; filesAnalyzed = $false; licenseConcluded = "MIT"; licenseDeclared = "MIT"; copyrightText = "NOASSERTION" }
91109 )
92- releaseArtifacts = @($zipFiles | ForEach-Object { [ordered]@{ name = $_.Name; sha256 = (Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant(); size = $_.Length } })
110+ releaseArtifacts = @($releaseFiles | ForEach-Object { [ordered]@{ name = $_.Name; sha256 = (Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant(); size = $_.Length } })
93111 }
94112 $sbom | ConvertTo-Json -Depth 8 | Set-Content .\artifacts\PtpLabClock.release-sbom.spdx.json -Encoding UTF8
95113
96- - name : Upload artifacts
114+ - name : Upload release build artifact
97115 uses : actions/upload-artifact@v4
98116 with :
99- name : release-packages
117+ name : portable- release-packages
100118 path : |
119+ .\artifacts\*.exe
101120 .\artifacts\*.zip
102121 .\artifacts\checksums.txt
103122 .\artifacts\PtpLabClock.release-sbom.spdx.json
@@ -108,8 +127,20 @@ jobs:
108127 uses : softprops/action-gh-release@v2
109128 with :
110129 files : |
130+ .\artifacts\*.exe
111131 .\artifacts\*.zip
112132 .\artifacts\checksums.txt
113133 .\artifacts\PtpLabClock.release-sbom.spdx.json
114134 .\TestResults\ptp-validation.pcap
115135 generate_release_notes : true
136+ body : |
137+ ## Process Bus Timing Lab portable release
138+
139+ Download the direct portable EXE when you want the fastest Windows start:
140+
141+ - `PtpLabClock.App.win-x64.portable.exe` - WPF dashboard, self-contained single-file app.
142+ - `PtpLabClock.Console.win-x64.portable.exe` - CLI validation, RAW self-test, passive monitor, and scripts.
143+
144+ ZIP packages are also provided for users who want license notices and README files bundled beside the EXE.
145+
146+ RAW NIC mode requires Npcap and may require Administrator privileges. This software is a lab simulator and diagnostic companion, not a certified grandmaster or relay-acceptance timing reference.
0 commit comments