Skip to content

Release

Release #1

Workflow file for this run

name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
permissions:
contents: write
jobs:
package:
name: Build portable Windows release
runs-on: windows-latest
timeout-minutes: 40
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore
run: dotnet restore .\PtpLabClock.sln
- name: Test
run: dotnet test .\PtpLabClock.sln -c Release --no-restore --logger trx --results-directory TestResults
- name: Protocol smoke validation
run: dotnet run --project .\src\PtpLabClock.Console --configuration Release -- --validate-protocol --domain 0 --export-pcap .\TestResults\ptp-validation.pcap
- name: Publish portable single EXE files
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$publishRoot = Resolve-Path "."
New-Item -ItemType Directory -Path .\artifacts -Force | Out-Null
New-Item -ItemType Directory -Path .\publish\app -Force | Out-Null
New-Item -ItemType Directory -Path .\publish\console -Force | Out-Null
$singleFileProps = @(
"-p:PublishSingleFile=true",
"-p:SelfContained=true",
"-p:IncludeNativeLibrariesForSelfExtract=true",
"-p:EnableCompressionInSingleFile=true",
"-p:PublishReadyToRun=false",
"-p:PublishTrimmed=false",
"-p:DebugType=None",
"-p:DebugSymbols=false"
)
dotnet publish .\src\PtpLabClock.App\PtpLabClock.App.csproj -c Release -r win-x64 --self-contained true @singleFileProps -o .\publish\app
dotnet publish .\src\PtpLabClock.Console\PtpLabClock.Console.csproj -c Release -r win-x64 --self-contained true @singleFileProps -o .\publish\console
Copy-Item .\publish\app\PtpLabClock.App.exe .\artifacts\PtpLabClock.App.win-x64.portable.exe -Force
Copy-Item .\publish\console\PtpLabClock.Console.exe .\artifacts\PtpLabClock.Console.win-x64.portable.exe -Force
if (-not (Test-Path .\artifacts\PtpLabClock.App.win-x64.portable.exe)) { throw "App portable EXE was not created." }
if (-not (Test-Path .\artifacts\PtpLabClock.Console.win-x64.portable.exe)) { throw "Console portable EXE was not created." }
- name: Create optional ZIP packages with notices
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$packages = @(
@{ Name = "PtpLabClock.App.win-x64.portable"; Exe = ".\artifacts\PtpLabClock.App.win-x64.portable.exe" },
@{ Name = "PtpLabClock.Console.win-x64.portable"; Exe = ".\artifacts\PtpLabClock.Console.win-x64.portable.exe" }
)
foreach ($package in $packages) {
$dir = Join-Path ".\artifacts" $package.Name
New-Item -ItemType Directory -Path $dir -Force | Out-Null
Copy-Item $package.Exe (Join-Path $dir (Split-Path $package.Exe -Leaf)) -Force
Copy-Item .\LICENSE $dir -Force
Copy-Item .\NOTICE $dir -Force
Copy-Item .\THIRD-PARTY-NOTICES.md $dir -Force
Copy-Item .\docs\release-readme.md (Join-Path $dir "README-FIRST.md") -Force
Compress-Archive -Path (Join-Path $dir "*") -DestinationPath ".\artifacts\$($package.Name).zip" -Force
}
- name: Generate checksums and SPDX-style release manifest
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$releaseFiles = Get-ChildItem .\artifacts -File | Where-Object { $_.Extension -in ".exe", ".zip" } | Sort-Object Name
$checksums = foreach ($file in $releaseFiles) {
$hash = Get-FileHash $file.FullName -Algorithm SHA256
"$($hash.Hash.ToLowerInvariant()) $($file.Name)"
}
$checksums | Set-Content .\artifacts\checksums.txt -Encoding UTF8
$sbom = [ordered]@{
spdxVersion = "SPDX-2.3"
dataLicense = "CC0-1.0"
SPDXID = "SPDXRef-DOCUMENT"
name = "Process Bus Timing Lab portable Windows release manifest"
documentNamespace = "https://github.com/masarray/PtpLabClock/releases/$env:GITHUB_RUN_ID"
creationInfo = [ordered]@{
created = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
creators = @("Tool: GitHub Actions", "Organization: Process Bus Timing Lab")
}
packages = @(
[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" },
[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" }
)
releaseArtifacts = @($releaseFiles | ForEach-Object { [ordered]@{ name = $_.Name; sha256 = (Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant(); size = $_.Length } })
}
$sbom | ConvertTo-Json -Depth 8 | Set-Content .\artifacts\PtpLabClock.release-sbom.spdx.json -Encoding UTF8
- name: Upload release build artifact
uses: actions/upload-artifact@v4
with:
name: portable-release-packages
path: |
.\artifacts\*.exe
.\artifacts\*.zip
.\artifacts\checksums.txt
.\artifacts\PtpLabClock.release-sbom.spdx.json
.\TestResults\ptp-validation.pcap
- name: Publish GitHub Release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2
with:
files: |
.\artifacts\*.exe
.\artifacts\*.zip
.\artifacts\checksums.txt
.\artifacts\PtpLabClock.release-sbom.spdx.json
.\TestResults\ptp-validation.pcap
generate_release_notes: true
body: |
## Process Bus Timing Lab portable release
Download the direct portable EXE when you want the fastest Windows start:
- `PtpLabClock.App.win-x64.portable.exe` - WPF dashboard, self-contained single-file app.
- `PtpLabClock.Console.win-x64.portable.exe` - CLI validation, RAW self-test, passive monitor, and scripts.
ZIP packages are also provided for users who want license notices and README files bundled beside the EXE.
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.