Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 96 additions & 73 deletions .github/workflows/ci-build.yml
Original file line number Diff line number Diff line change
@@ -1,103 +1,126 @@
name: CI Build

# Autobuild that runs on every push and pull request, plus manual triggers.
# Complements release.yml (which only runs when a GitHub Release is published).
# Produces a runnable x64 package as a downloadable build artifact.

on:
push:
branches: [ "**" ]
tags-ignore: [ "**" ] # tag pushes are handled by release.yml
branches: ["**"]
tags-ignore: ["**"]
pull_request:
branches: [ "**" ]
branches: ["**"]
workflow_dispatch:

permissions:
contents: read

jobs:
test:
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
env:
DOTNET_NOLOGO: "1"
DOTNET_CLI_TELEMETRY_OPTOUT: "1"

- name: Restore dependencies
run: dotnet restore

- name: Run tests (x64)
# NOTE: Three tests are excluded below. They are exact-string XML round-trip
# "snapshot" tests whose hardcoded expected XML predates several fields the
# current serializer emits (ProcessPriority, ProfileChangedNotification,
# UseMoonlight, DebouncingMs, LeftStickDriftXAxis, ...) and whose fixture
# source files use LF while the serializer writes CRLF. They fail on a clean
# checkout of upstream, independently of any feature change, so they are not
# used to gate the build. All other tests run and must pass.
# Remove the --filter once the upstream snapshot fixtures are regenerated.
run: dotnet test .\DS4WindowsTests\DS4WindowsTests.csproj -c Release -p:Platform=x64 --verbosity normal --filter "Name!=CheckSettingsSave&Name!=CheckWriteProfile&Name!=CheckJaysProfileRead"

build:
jobs:
verify:
runs-on: windows-2022
needs: test
strategy:
fail-fast: false
matrix:
platform: [ x64 ]
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- name: Checkout exact source
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false

- name: Setup .NET
uses: actions/setup-dotnet@v4
- name: Setup .NET 8
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
with:
dotnet-version: 8.0.x

- name: Set up Python 3.10
uses: actions/setup-python@v5
- name: Setup Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.10"

- name: Restore dependencies
run: dotnet restore
python-version: "3.12"

- name: Compute version
id: ver
shell: bash
- name: Restore managed projects
shell: pwsh
run: |
VERSION="$(date +%Y.%m.%d)-ci-${GITHUB_SHA::7}"
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
dotnet restore .\DS4WindowsTests\DS4WindowsTests.csproj
if ($LASTEXITCODE -ne 0) { throw "Test restore failed." }
dotnet restore .\installer\DS4Windows.SetupActions\DS4Windows.SetupActions.csproj
if ($LASTEXITCODE -ne 0) { throw "SetupActions restore failed." }
dotnet restore .\installer\DS4Windows.Bootstrapper\DS4Windows.Bootstrapper.csproj
if ($LASTEXITCODE -ne 0) { throw "Bootstrapper restore failed." }

- name: Validate native package contract (Windows PowerShell 5.1)
shell: powershell
run: .\extras\Test-ViiperNativePackageContract.ps1

- name: Validate native package contract (PowerShell 7)
shell: pwsh
run: .\extras\Test-ViiperNativePackageContract.ps1

- name: Validate Windows 11 bundle contract (Windows PowerShell 5.1)
shell: powershell
run: .\extras\validation\Test-ViiperWin11ValidationContract.ps1

- name: Validate Windows 11 bundle contract (PowerShell 7)
shell: pwsh
run: .\extras\validation\Test-ViiperWin11ValidationContract.ps1

- name: Build ${{ matrix.platform }}
run: dotnet publish .\DS4Windows\DS4WinWPF.csproj -c Release /p:platform=${{ matrix.platform }} -o .\bin\${{ matrix.platform }}\Release\output
- name: Validate installer security contracts (Windows PowerShell 5.1)
shell: powershell
run: .\installer\Test-InstallerSecurityContracts.ps1

- name: Package ${{ matrix.platform }}
run: python .\utils\post-build.py .\bin\${{ matrix.platform }}\Release\output . ${{ env.VERSION }}
- name: Validate installer security contracts (PowerShell 7)
shell: pwsh
run: .\installer\Test-InstallerSecurityContracts.ps1

- name: Stage ${{ matrix.platform }} artifact folder
- name: Validate installer source and state machine
shell: pwsh
run: |
$artifactRoot = ".\bin\${{ matrix.platform }}\Release\artifact"
New-Item -ItemType Directory -Path $artifactRoot -Force | Out-Null
Copy-Item -Path ".\bin\${{ matrix.platform }}\Release\DS4Windows" -Destination $artifactRoot -Recurse -Force
python .\utils\test-installer-state-machine.py
if ($LASTEXITCODE -ne 0) { throw "Installer state-machine tests failed." }
python .\utils\validate-installer.py --source-only --bundle-source .\installer\DS4Windows.Bundle\Bundle.wxs --setup-actions-source .\installer\DS4Windows.SetupActions\Program.cs --bootstrapper-source .\installer\DS4Windows.Bootstrapper\InstallerApplication.cs
if ($LASTEXITCODE -ne 0) { throw "Installer source validation failed." }

- name: Upload ${{ matrix.platform }} artifact
id: upload
uses: actions/upload-artifact@v4
with:
name: DS4Windows_${{ env.VERSION }}_${{ matrix.platform }}
path: .\bin\${{ matrix.platform }}\Release\artifact
if-no-files-found: error
- name: Prove production build fails without signing credentials
shell: pwsh
run: |
foreach ($name in @("DS4W_SIGN_CERT_PATH", "DS4W_SIGN_CERT_PASSWORD", "DS4W_SIGN_CERTIFICATE_SHA256", "DS4W_SIGN_TIMESTAMP_URL")) {
Remove-Item -LiteralPath ("Env:\" + $name) -ErrorAction SilentlyContinue
}
try {
& .\installer\build-installer.ps1 -PublishRoot (Join-Path $env:RUNNER_TEMP "must-not-publish")
throw "Production build unexpectedly accepted missing signing credentials."
} catch {
if ($_.Exception.Message -notmatch "requires environment variable") { throw }
}

- name: Compile installer executables
shell: pwsh
run: |
dotnet build .\installer\DS4Windows.SetupActions\DS4Windows.SetupActions.csproj -c Release -p:Platform=x64 --no-restore
if ($LASTEXITCODE -ne 0) { throw "SetupActions build failed." }
dotnet build .\installer\DS4Windows.Bootstrapper\DS4Windows.Bootstrapper.csproj -c Release -p:Platform=x64 --no-restore
if ($LASTEXITCODE -ne 0) { throw "Bootstrapper build failed." }

- name: Publish artifact link to run summary
shell: bash
- name: Compile disposable MSI and Burn composition
shell: pwsh
run: |
$publishRoot = Join-Path $env:RUNNER_TEMP "ds4windows-installer-composition-publish"
dotnet publish .\DS4Windows\DS4WinWPF.csproj -c Release -p:Platform=x64 -o $publishRoot
if ($LASTEXITCODE -ne 0) { throw "Composition publish failed." }
& .\installer\Test-InstallerComposition.ps1 -PublishRoot $publishRoot

- name: Run DS4Windows tests (x64)
shell: pwsh
run: |
echo "### DS4Windows ${{ matrix.platform }} build" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "Version: \`${{ env.VERSION }}\`" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "[Download DS4Windows_${{ env.VERSION }}_${{ matrix.platform }} (contains DS4Windows folder)](${{ steps.upload.outputs.artifact-url }})" >> "$GITHUB_STEP_SUMMARY"
dotnet test .\DS4WindowsTests\DS4WindowsTests.csproj -c Release -p:Platform=x64 --no-restore --verbosity normal
if ($LASTEXITCODE -ne 0) { throw "DS4Windows tests failed." }

- name: Verify source checkout remains unchanged
shell: pwsh
run: |
$changes = @(git status --short)
if ($changes.Count -ne 0) {
$changes | Write-Host
throw "CI validation changed tracked or untracked source."
}
Loading
Loading