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