From 546c70dc98c4b125e6c2662747cbc525d3e9f7bb Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 13 Oct 2026 19:29:48 +0000
Subject: [PATCH 1/5] Initial plan
From 69dfc85764c29f62bafe9920d8ed43160aaf7107 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 13 Oct 2026 19:38:41 +0000
Subject: [PATCH 2/5] Sign packaged DLLs with Authenticode
Co-authored-by: TheCodeTraveler <13558917+TheCodeTraveler@users.noreply.github.com>
---
.github/workflows/dotnet-build.yml | 99 +++++++++++++++++++++++++++++-
Directory.Build.targets | 9 +++
2 files changed, 106 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/dotnet-build.yml b/.github/workflows/dotnet-build.yml
index 16da23e..444c2d7 100644
--- a/.github/workflows/dotnet-build.yml
+++ b/.github/workflows/dotnet-build.yml
@@ -98,6 +98,8 @@ jobs:
name: Build + Test Library
runs-on: windows-latest
timeout-minutes: 60
+ env:
+ EnableAuthenticodeSigning: 'true'
steps:
- name: Checkout
@@ -176,11 +178,104 @@ jobs:
name: code-coverage-report
path: ${{ runner.temp }}/CodeCoverage
+ - name: Prepare Authenticode Signing Certificate
+ env:
+ CODE_SIGNING_CERTIFICATE_PFX_BASE64: ${{ github.event_name != 'pull_request' && secrets.CODE_SIGNING_CERTIFICATE_PFX_BASE64 || '' }}
+ CODE_SIGNING_CERTIFICATE_PASSWORD: ${{ github.event_name != 'pull_request' && secrets.CODE_SIGNING_CERTIFICATE_PASSWORD || '' }}
+ run: |
+ $signTool = Get-ChildItem -Path "${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x64\signtool.exe" |
+ Sort-Object { [version]$_.Directory.Parent.Name } -Descending |
+ Select-Object -First 1
+ if ($null -eq $signTool) {
+ throw "Windows SDK signtool.exe was not found."
+ }
+
+ if ($env:GITHUB_EVENT_NAME -eq 'pull_request') {
+ $certificate = New-SelfSignedCertificate -Subject 'CN=AsyncAwaitBestPractices CI' -Type CodeSigningCert -CertStoreLocation 'Cert:\CurrentUser\My' -NotAfter (Get-Date).AddDays(1)
+ $store = [System.Security.Cryptography.X509Certificates.X509Store]::new('My', 'CurrentUser')
+ $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
+ try {
+ $store.Add($certificate)
+ }
+ finally {
+ $store.Close()
+ }
+
+ $rootStore = [System.Security.Cryptography.X509Certificates.X509Store]::new('Root', 'CurrentUser')
+ $rootStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
+ try {
+ $rootStore.Add($certificate)
+ }
+ finally {
+ $rootStore.Close()
+ }
+ }
+ else {
+ if ([string]::IsNullOrWhiteSpace($env:CODE_SIGNING_CERTIFICATE_PFX_BASE64) -or [string]::IsNullOrWhiteSpace($env:CODE_SIGNING_CERTIFICATE_PASSWORD)) {
+ throw "Set the CODE_SIGNING_CERTIFICATE_PFX_BASE64 and CODE_SIGNING_CERTIFICATE_PASSWORD repository secrets to sign release DLLs."
+ }
+
+ $certificatePath = Join-Path $env:RUNNER_TEMP 'code-signing-certificate.pfx'
+ [System.IO.File]::WriteAllBytes($certificatePath, [Convert]::FromBase64String($env:CODE_SIGNING_CERTIFICATE_PFX_BASE64))
+ try {
+ $password = ConvertTo-SecureString $env:CODE_SIGNING_CERTIFICATE_PASSWORD -AsPlainText -Force
+ $certificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new(
+ $certificatePath,
+ $password,
+ [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet
+ )
+ $store = [System.Security.Cryptography.X509Certificates.X509Store]::new('My', 'CurrentUser')
+ $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
+ try {
+ $store.Add($certificate)
+ }
+ finally {
+ $store.Close()
+ }
+ }
+ finally {
+ Remove-Item -LiteralPath $certificatePath -Force
+ }
+ }
+
+ if ($null -eq $certificate -or -not $certificate.HasPrivateKey) {
+ throw "The code-signing certificate does not contain a private key."
+ }
+
+ "SignToolPath=$($signTool.FullName)" >> $env:GITHUB_ENV
+ "CodeSigningCertificateThumbprint=$($certificate.Thumbprint)" >> $env:GITHUB_ENV
+
- name: Pack AsyncAwaitBestPractices NuGet
- run: dotnet pack $env:PathToAsyncAwaitBestPracticesCsproj -c Release "-p:PackageVersion=$env:NugetPackageVersion"
+ run: dotnet pack $env:PathToAsyncAwaitBestPracticesCsproj -c Release "-p:PackageVersion=$env:NugetPackageVersion" "-p:EnableAuthenticodeSigning=$env:EnableAuthenticodeSigning" "-p:SignToolPath=$env:SignToolPath" "-p:CodeSigningCertificateThumbprint=$env:CodeSigningCertificateThumbprint"
- name: Pack AsyncAwaitBestPractices.MVVM NuGet
- run: dotnet pack $env:PathToAsyncAwaitBestPracticesMVVMCsproj -c Release "-p:PackageVersion=$env:NugetPackageVersion"
+ run: dotnet pack $env:PathToAsyncAwaitBestPracticesMVVMCsproj -c Release "-p:PackageVersion=$env:NugetPackageVersion" "-p:EnableAuthenticodeSigning=$env:EnableAuthenticodeSigning" "-p:SignToolPath=$env:SignToolPath" "-p:CodeSigningCertificateThumbprint=$env:CodeSigningCertificateThumbprint"
+
+ - name: Verify Authenticode Signatures in NuGet Packages
+ run: |
+ Add-Type -AssemblyName System.IO.Compression.FileSystem
+ $packages = @(Get-ChildItem -Path ./src -Recurse -Filter *.nupkg | Where-Object { $_.FullName -match '[\\/]bin[\\/]Release[\\/]' })
+ if ($packages.Count -eq 0) {
+ throw "No NuGet packages were produced."
+ }
+
+ foreach ($package in $packages) {
+ $extractPath = Join-Path $env:RUNNER_TEMP ([System.IO.Path]::GetRandomFileName())
+ [System.IO.Compression.ZipFile]::ExtractToDirectory($package.FullName, $extractPath)
+ $assemblies = @(Get-ChildItem -Path (Join-Path $extractPath 'lib') -Recurse -Filter *.dll -ErrorAction SilentlyContinue)
+ if ($assemblies.Count -eq 0) {
+ throw "No library DLLs were found in $($package.Name)."
+ }
+
+ foreach ($assembly in $assemblies) {
+ & $env:SignToolPath verify /pa /all /v $assembly.FullName
+ if ($LASTEXITCODE -ne 0) {
+ throw "Authenticode signature verification failed for $($assembly.FullName)."
+ }
+ }
+
+ Remove-Item -Path $extractPath -Recurse -Force
+ }
- name: Check AsyncAwaitBestPractices Dependencies Vulnerabilities
if: ${{ !cancelled() }}
diff --git a/Directory.Build.targets b/Directory.Build.targets
index 01b7a10..fb73137 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -42,4 +42,13 @@
+
+
+
+
+
+
+
\ No newline at end of file
From 680a757a4a01b0ffb19301988a8b8accb7b5573a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 15 Oct 2026 03:15:16 +0000
Subject: [PATCH 3/5] Scope EnableAuthenticodeSigning to the pack steps
Co-authored-by: TheCodeTraveler <13558917+TheCodeTraveler@users.noreply.github.com>
---
.github/workflows/dotnet-build.yml | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/dotnet-build.yml b/.github/workflows/dotnet-build.yml
index 444c2d7..a843237 100644
--- a/.github/workflows/dotnet-build.yml
+++ b/.github/workflows/dotnet-build.yml
@@ -98,8 +98,6 @@ jobs:
name: Build + Test Library
runs-on: windows-latest
timeout-minutes: 60
- env:
- EnableAuthenticodeSigning: 'true'
steps:
- name: Checkout
@@ -246,10 +244,10 @@ jobs:
"CodeSigningCertificateThumbprint=$($certificate.Thumbprint)" >> $env:GITHUB_ENV
- name: Pack AsyncAwaitBestPractices NuGet
- run: dotnet pack $env:PathToAsyncAwaitBestPracticesCsproj -c Release "-p:PackageVersion=$env:NugetPackageVersion" "-p:EnableAuthenticodeSigning=$env:EnableAuthenticodeSigning" "-p:SignToolPath=$env:SignToolPath" "-p:CodeSigningCertificateThumbprint=$env:CodeSigningCertificateThumbprint"
+ run: dotnet pack $env:PathToAsyncAwaitBestPracticesCsproj -c Release "-p:PackageVersion=$env:NugetPackageVersion" -p:EnableAuthenticodeSigning=true "-p:SignToolPath=$env:SignToolPath" "-p:CodeSigningCertificateThumbprint=$env:CodeSigningCertificateThumbprint"
- name: Pack AsyncAwaitBestPractices.MVVM NuGet
- run: dotnet pack $env:PathToAsyncAwaitBestPracticesMVVMCsproj -c Release "-p:PackageVersion=$env:NugetPackageVersion" "-p:EnableAuthenticodeSigning=$env:EnableAuthenticodeSigning" "-p:SignToolPath=$env:SignToolPath" "-p:CodeSigningCertificateThumbprint=$env:CodeSigningCertificateThumbprint"
+ run: dotnet pack $env:PathToAsyncAwaitBestPracticesMVVMCsproj -c Release "-p:PackageVersion=$env:NugetPackageVersion" -p:EnableAuthenticodeSigning=true "-p:SignToolPath=$env:SignToolPath" "-p:CodeSigningCertificateThumbprint=$env:CodeSigningCertificateThumbprint"
- name: Verify Authenticode Signatures in NuGet Packages
run: |
From 238ac087b3e454af4b8c3a1c020a6f07b180f77b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 15 Oct 2026 03:23:59 +0000
Subject: [PATCH 4/5] Build, version, and attach Release NuGet packages when a
GitHub Release is published
Co-authored-by: TheCodeTraveler <13558917+TheCodeTraveler@users.noreply.github.com>
---
.github/workflows/dotnet-build.yml | 67 ++++++++++++++++++++++++++----
1 file changed, 59 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/dotnet-build.yml b/.github/workflows/dotnet-build.yml
index a843237..58e12a4 100644
--- a/.github/workflows/dotnet-build.yml
+++ b/.github/workflows/dotnet-build.yml
@@ -7,11 +7,15 @@ on:
push:
branches:
- main
- tags:
- - '**'
pull_request:
branches:
- main
+ # Publishing a GitHub Release builds production NuGet packages versioned from the release tag
+ # and attaches them to the release. Publishing a release also pushes its tag, so this workflow
+ # does not also trigger on tag pushes; that would build every release twice.
+ release:
+ types:
+ - published
permissions:
contents: read
@@ -109,8 +113,20 @@ jobs:
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
+ RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
- if ($env:GITHUB_EVENT_NAME -eq 'pull_request') {
+ if ($env:GITHUB_EVENT_NAME -eq 'release') {
+ # Release tags are the package version, optionally prefixed with `v` (e.g. `v10.1.0` or `10.1.0-pre1`).
+ $nugetPackageVersion = $env:RELEASE_TAG -replace '^v', ''
+ if ($nugetPackageVersion -notmatch '^(\d+\.\d+\.\d+)(-[0-9A-Za-z.-]+)?$') {
+ throw "Release tag '$($env:RELEASE_TAG)' is not a valid NuGet version. Use a tag such as v10.1.0 or v10.1.0-pre1."
+ }
+
+ # AssemblyVersion and AssemblyFileVersion come from the NuGetVersion MSBuild property, which must not contain a prerelease suffix.
+ "ReleaseNuGetVersion=$($Matches[1])" >> $env:GITHUB_ENV
+ Write-Host "GitHub Release Tag = $($env:RELEASE_TAG)"
+ }
+ elseif ($env:GITHUB_EVENT_NAME -eq 'pull_request') {
$commitId = $env:PR_HEAD_SHA.Substring(0, 7)
$nugetPackageVersion = "$($env:CurrentSemanticVersionBase)-build-$($env:PR_NUMBER).$($env:GITHUB_RUN_NUMBER)+$commitId"
Write-Host "GitHub PR = $($env:PR_NUMBER), Commit = $commitId"
@@ -244,10 +260,22 @@ jobs:
"CodeSigningCertificateThumbprint=$($certificate.Thumbprint)" >> $env:GITHUB_ENV
- name: Pack AsyncAwaitBestPractices NuGet
- run: dotnet pack $env:PathToAsyncAwaitBestPracticesCsproj -c Release "-p:PackageVersion=$env:NugetPackageVersion" -p:EnableAuthenticodeSigning=true "-p:SignToolPath=$env:SignToolPath" "-p:CodeSigningCertificateThumbprint=$env:CodeSigningCertificateThumbprint"
+ run: |
+ $packArguments = @("-p:PackageVersion=$env:NugetPackageVersion", '-p:EnableAuthenticodeSigning=true', "-p:SignToolPath=$env:SignToolPath", "-p:CodeSigningCertificateThumbprint=$env:CodeSigningCertificateThumbprint")
+ if ($env:ReleaseNuGetVersion) {
+ $packArguments += "-p:NuGetVersion=$env:ReleaseNuGetVersion"
+ }
+ dotnet pack $env:PathToAsyncAwaitBestPracticesCsproj -c Release @packArguments
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: Pack AsyncAwaitBestPractices.MVVM NuGet
- run: dotnet pack $env:PathToAsyncAwaitBestPracticesMVVMCsproj -c Release "-p:PackageVersion=$env:NugetPackageVersion" -p:EnableAuthenticodeSigning=true "-p:SignToolPath=$env:SignToolPath" "-p:CodeSigningCertificateThumbprint=$env:CodeSigningCertificateThumbprint"
+ run: |
+ $packArguments = @("-p:PackageVersion=$env:NugetPackageVersion", '-p:EnableAuthenticodeSigning=true', "-p:SignToolPath=$env:SignToolPath", "-p:CodeSigningCertificateThumbprint=$env:CodeSigningCertificateThumbprint")
+ if ($env:ReleaseNuGetVersion) {
+ $packArguments += "-p:NuGetVersion=$env:ReleaseNuGetVersion"
+ }
+ dotnet pack $env:PathToAsyncAwaitBestPracticesMVVMCsproj -c Release @packArguments
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: Verify Authenticode Signatures in NuGet Packages
run: |
@@ -297,15 +325,38 @@ jobs:
exit 0
- name: Copy NuGet Packages to Staging Directory
- if: ${{ !startsWith(github.ref, 'refs/tags/') }}
run: |
$stagingDirectory = New-Item -ItemType Directory -Force -Path (Join-Path $env:RUNNER_TEMP 'nuget')
Get-ChildItem -Path ./src -Recurse | Where-Object { $_.Extension -match 'nupkg' } | Copy-Item -Destination $stagingDirectory.FullName
- name: Publish NuGet Packages
- if: ${{ !startsWith(github.ref, 'refs/tags/') }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: nuget
path: ${{ runner.temp }}/nuget
- if-no-files-found: error
\ No newline at end of file
+ if-no-files-found: error
+
+ attach_release_assets:
+ name: Attach NuGet Packages to GitHub Release
+ if: ${{ github.event_name == 'release' }}
+ needs:
+ - build_sample
+ - build_test_library
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ # Only this job can write to the repository, and it runs no build tools or third-party actions besides artifact download.
+ permissions:
+ contents: write
+
+ steps:
+ - name: Download NuGet Packages
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
+ with:
+ name: nuget
+ path: ${{ runner.temp }}/nuget
+
+ - name: Upload NuGet Packages to GitHub Release
+ env:
+ GH_TOKEN: ${{ github.token }}
+ RELEASE_TAG: ${{ github.event.release.tag_name }}
+ run: gh release upload "$RELEASE_TAG" "$RUNNER_TEMP"/nuget/*.nupkg "$RUNNER_TEMP"/nuget/*.snupkg --repo "$GITHUB_REPOSITORY" --clobber
From c350f4bd552f9aba884933e5db99c149f3cc0eb2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 15 Oct 2026 03:37:25 +0000
Subject: [PATCH 5/5] Sign release DLLs with Azure Artifact Signing instead of
a PFX certificate
Co-authored-by: TheCodeTraveler <13558917+TheCodeTraveler@users.noreply.github.com>
---
.github/workflows/dotnet-build.yml | 147 +++++++++++++++--------------
Directory.Build.targets | 9 --
2 files changed, 76 insertions(+), 80 deletions(-)
diff --git a/.github/workflows/dotnet-build.yml b/.github/workflows/dotnet-build.yml
index 58e12a4..f3cd455 100644
--- a/.github/workflows/dotnet-build.yml
+++ b/.github/workflows/dotnet-build.yml
@@ -102,6 +102,12 @@ jobs:
name: Build + Test Library
runs-on: windows-latest
timeout-minutes: 60
+ # Release runs sign the library DLLs with Azure Artifact Signing. Azure only trusts GitHub's OIDC token
+ # from the `release` environment, so pull requests and pushes to main build and pack unsigned DLLs.
+ environment: ${{ github.event_name == 'release' && 'release' || '' }}
+ permissions:
+ contents: read
+ id-token: write
steps:
- name: Checkout
@@ -192,92 +198,87 @@ jobs:
name: code-coverage-report
path: ${{ runner.temp }}/CodeCoverage
- - name: Prepare Authenticode Signing Certificate
- env:
- CODE_SIGNING_CERTIFICATE_PFX_BASE64: ${{ github.event_name != 'pull_request' && secrets.CODE_SIGNING_CERTIFICATE_PFX_BASE64 || '' }}
- CODE_SIGNING_CERTIFICATE_PASSWORD: ${{ github.event_name != 'pull_request' && secrets.CODE_SIGNING_CERTIFICATE_PASSWORD || '' }}
+ # Building, signing, and packing are separate steps so the DLLs are signed after they are built and
+ # `dotnet pack --no-build` packages those signed DLLs instead of rebuilding them.
+ - name: Build AsyncAwaitBestPractices + AsyncAwaitBestPractices.MVVM
run: |
- $signTool = Get-ChildItem -Path "${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x64\signtool.exe" |
- Sort-Object { [version]$_.Directory.Parent.Name } -Descending |
- Select-Object -First 1
- if ($null -eq $signTool) {
- throw "Windows SDK signtool.exe was not found."
+ $buildArguments = @('-c', 'Release', "-p:PackageVersion=$env:NugetPackageVersion")
+ if ($env:ReleaseNuGetVersion) {
+ $buildArguments += "-p:NuGetVersion=$env:ReleaseNuGetVersion"
}
- if ($env:GITHUB_EVENT_NAME -eq 'pull_request') {
- $certificate = New-SelfSignedCertificate -Subject 'CN=AsyncAwaitBestPractices CI' -Type CodeSigningCert -CertStoreLocation 'Cert:\CurrentUser\My' -NotAfter (Get-Date).AddDays(1)
- $store = [System.Security.Cryptography.X509Certificates.X509Store]::new('My', 'CurrentUser')
- $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
- try {
- $store.Add($certificate)
- }
- finally {
- $store.Close()
- }
+ dotnet build $env:PathToAsyncAwaitBestPracticesCsproj @buildArguments
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
+ dotnet build $env:PathToAsyncAwaitBestPracticesMVVMCsproj @buildArguments
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- $rootStore = [System.Security.Cryptography.X509Certificates.X509Store]::new('Root', 'CurrentUser')
- $rootStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
- try {
- $rootStore.Add($certificate)
- }
- finally {
- $rootStore.Close()
- }
+ - name: Check Azure Artifact Signing Configuration
+ if: ${{ github.event_name == 'release' }}
+ env:
+ AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
+ AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
+ AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+ ARTIFACT_SIGNING_ENDPOINT: ${{ vars.ARTIFACT_SIGNING_ENDPOINT }}
+ ARTIFACT_SIGNING_ACCOUNT_NAME: ${{ vars.ARTIFACT_SIGNING_ACCOUNT_NAME }}
+ ARTIFACT_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ vars.ARTIFACT_SIGNING_CERTIFICATE_PROFILE_NAME }}
+ run: |
+ $missing = @('AZURE_CLIENT_ID', 'AZURE_TENANT_ID', 'AZURE_SUBSCRIPTION_ID', 'ARTIFACT_SIGNING_ENDPOINT', 'ARTIFACT_SIGNING_ACCOUNT_NAME', 'ARTIFACT_SIGNING_CERTIFICATE_PROFILE_NAME') |
+ Where-Object { [string]::IsNullOrWhiteSpace([Environment]::GetEnvironmentVariable($_)) }
+ if ($missing) {
+ throw "Add these secrets/variables to the 'release' environment (Settings > Environments > release): $($missing -join ', ')"
}
- else {
- if ([string]::IsNullOrWhiteSpace($env:CODE_SIGNING_CERTIFICATE_PFX_BASE64) -or [string]::IsNullOrWhiteSpace($env:CODE_SIGNING_CERTIFICATE_PASSWORD)) {
- throw "Set the CODE_SIGNING_CERTIFICATE_PFX_BASE64 and CODE_SIGNING_CERTIFICATE_PASSWORD repository secrets to sign release DLLs."
- }
- $certificatePath = Join-Path $env:RUNNER_TEMP 'code-signing-certificate.pfx'
- [System.IO.File]::WriteAllBytes($certificatePath, [Convert]::FromBase64String($env:CODE_SIGNING_CERTIFICATE_PFX_BASE64))
- try {
- $password = ConvertTo-SecureString $env:CODE_SIGNING_CERTIFICATE_PASSWORD -AsPlainText -Force
- $certificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new(
- $certificatePath,
- $password,
- [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet
- )
- $store = [System.Security.Cryptography.X509Certificates.X509Store]::new('My', 'CurrentUser')
- $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
- try {
- $store.Add($certificate)
- }
- finally {
- $store.Close()
- }
- }
- finally {
- Remove-Item -LiteralPath $certificatePath -Force
- }
+ - name: Find Library DLLs to Sign
+ id: files-to-sign
+ if: ${{ github.event_name == 'release' }}
+ run: |
+ $files = @(
+ Get-ChildItem -Path 'src/AsyncAwaitBestPractices/bin/Release/*/AsyncAwaitBestPractices.dll'
+ Get-ChildItem -Path 'src/AsyncAwaitBestPractices.MVVM/bin/Release/*/AsyncAwaitBestPractices.MVVM.dll'
+ )
+ if ($files.Count -eq 0) {
+ throw "No library DLLs were found to sign."
}
- if ($null -eq $certificate -or -not $certificate.HasPrivateKey) {
- throw "The code-signing certificate does not contain a private key."
- }
+ $files.FullName | ForEach-Object { Write-Host $_ }
+ "files<> $env:GITHUB_OUTPUT
+ $files.FullName >> $env:GITHUB_OUTPUT
+ "EOF" >> $env:GITHUB_OUTPUT
- "SignToolPath=$($signTool.FullName)" >> $env:GITHUB_ENV
- "CodeSigningCertificateThumbprint=$($certificate.Thumbprint)" >> $env:GITHUB_ENV
+ - name: Sign In to Azure
+ if: ${{ github.event_name == 'release' }}
+ uses: azure/login@a641126d1b8aa4d1fa005f4f92df94a3a4c4c906 # v3.1.0
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- - name: Pack AsyncAwaitBestPractices NuGet
+ - name: Sign Library DLLs with Azure Artifact Signing
+ if: ${{ github.event_name == 'release' }}
+ uses: azure/artifact-signing-action@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2.0.0
+ with:
+ endpoint: ${{ vars.ARTIFACT_SIGNING_ENDPOINT }}
+ signing-account-name: ${{ vars.ARTIFACT_SIGNING_ACCOUNT_NAME }}
+ certificate-profile-name: ${{ vars.ARTIFACT_SIGNING_CERTIFICATE_PROFILE_NAME }}
+ files: ${{ steps.files-to-sign.outputs.files }}
+ file-digest: SHA256
+ timestamp-rfc3161: http://timestamp.acs.microsoft.com
+ timestamp-digest: SHA256
+
+ - name: Pack AsyncAwaitBestPractices + AsyncAwaitBestPractices.MVVM NuGets
run: |
- $packArguments = @("-p:PackageVersion=$env:NugetPackageVersion", '-p:EnableAuthenticodeSigning=true', "-p:SignToolPath=$env:SignToolPath", "-p:CodeSigningCertificateThumbprint=$env:CodeSigningCertificateThumbprint")
+ $packArguments = @('-c', 'Release', '--no-build', "-p:PackageVersion=$env:NugetPackageVersion")
if ($env:ReleaseNuGetVersion) {
$packArguments += "-p:NuGetVersion=$env:ReleaseNuGetVersion"
}
- dotnet pack $env:PathToAsyncAwaitBestPracticesCsproj -c Release @packArguments
- if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- - name: Pack AsyncAwaitBestPractices.MVVM NuGet
- run: |
- $packArguments = @("-p:PackageVersion=$env:NugetPackageVersion", '-p:EnableAuthenticodeSigning=true', "-p:SignToolPath=$env:SignToolPath", "-p:CodeSigningCertificateThumbprint=$env:CodeSigningCertificateThumbprint")
- if ($env:ReleaseNuGetVersion) {
- $packArguments += "-p:NuGetVersion=$env:ReleaseNuGetVersion"
- }
- dotnet pack $env:PathToAsyncAwaitBestPracticesMVVMCsproj -c Release @packArguments
+ dotnet pack $env:PathToAsyncAwaitBestPracticesCsproj @packArguments
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
+ dotnet pack $env:PathToAsyncAwaitBestPracticesMVVMCsproj @packArguments
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: Verify Authenticode Signatures in NuGet Packages
+ if: ${{ github.event_name == 'release' }}
run: |
Add-Type -AssemblyName System.IO.Compression.FileSystem
$packages = @(Get-ChildItem -Path ./src -Recurse -Filter *.nupkg | Where-Object { $_.FullName -match '[\\/]bin[\\/]Release[\\/]' })
@@ -294,9 +295,13 @@ jobs:
}
foreach ($assembly in $assemblies) {
- & $env:SignToolPath verify /pa /all /v $assembly.FullName
- if ($LASTEXITCODE -ne 0) {
- throw "Authenticode signature verification failed for $($assembly.FullName)."
+ $signature = Get-AuthenticodeSignature -FilePath $assembly.FullName
+ Write-Host "$($package.Name) $($assembly.FullName.Substring($extractPath.Length)): $($signature.Status) ($($signature.SignerCertificate.Subject))"
+ if ($signature.Status -ne 'Valid') {
+ throw "Authenticode signature verification failed for $($assembly.FullName): $($signature.StatusMessage)"
+ }
+ if ($null -eq $signature.TimeStamperCertificate) {
+ throw "The Authenticode signature for $($assembly.FullName) is not timestamped."
}
}
diff --git a/Directory.Build.targets b/Directory.Build.targets
index fb73137..01b7a10 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -42,13 +42,4 @@
-
-
-
-
-
-
-
\ No newline at end of file