name: Build, sign, and publish on: workflow_dispatch: pull_request: branches: - main - 'release/**' types: - opened - synchronize - reopened - ready_for_review push: branches: - main - 'release/**' permissions: contents: read # Supersede in-flight runs of this workflow for the same pull request. Pushes to main and # release branches are excluded so every commit there still gets a full, recorded result. concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: build: name: Build and sign if: ${{ github.event_name != 'pull_request' || (github.event.pull_request.draft == false && github.event.pull_request.head.repo.full_name == github.repository) }} runs-on: group: self-hosted labels: [self-hosted, Windows, X64] timeout-minutes: 180 permissions: contents: read outputs: version: ${{ steps.version.outputs.version }} main_version: ${{ steps.version.outputs.main_version }} env: DOTNET_NOLOGO: true DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true DOTNET_CLI_TELEMETRY_OPTOUT: true CENTRON_BUILD_RUNNING_IN_AZURE_PIPELINE: true CENTRON_BUILD_IS_DEV_BUILD: false steps: - name: Show runner information shell: pwsh run: | Write-Host "Runner: $env:RUNNER_NAME" Write-Host "Computer: $env:COMPUTERNAME" Write-Host "PowerShell: $($PSVersionTable.PSVersion)" - name: Check out repository uses: actions/checkout@v7 with: fetch-depth: 0 - name: Normalize Git repository format shell: pwsh run: | $workspace = [IO.Path]::GetFullPath($env:GITHUB_WORKSPACE) $safeDirectories = @(git config --global --get-all safe.directory) $safeDirectoriesExitCode = $LASTEXITCODE if ($safeDirectoriesExitCode -notin 0, 1) { throw 'Could not determine the configured Git safe directories.' } if ($safeDirectories -notcontains $workspace) { git config --global --add safe.directory $workspace if ($LASTEXITCODE -ne 0) { throw "Could not register the GitHub workspace as a safe directory: $workspace" } } $repositoryFormatVersion = git config --local --get core.repositoryFormatVersion if ($LASTEXITCODE -ne 0) { throw 'Could not determine the Git repository format version.' } if ($repositoryFormatVersion -eq '1') { $extensions = @(git config --local --name-only --get-regexp '^extensions\.') $extensionsExitCode = $LASTEXITCODE if ($extensionsExitCode -notin 0, 1) { throw 'Could not determine the active Git repository extensions.' } if ($extensions.Count -gt 0) { throw "Cannot normalize the Git repository while extensions are active: $($extensions -join ', ')" } git config --local core.repositoryFormatVersion 0 if ($LASTEXITCODE -ne 0) { throw 'Could not normalize the Git repository format for Nerdbank.GitVersioning.' } } - name: Verify build environment shell: pwsh run: | $sdkVersion = dotnet --version if ($LASTEXITCODE -ne 0) { throw "The .NET SDK could not be resolved using global.json." } if ($sdkVersion -notmatch '^10\.0\.') { throw "Expected a .NET 10 SDK, but resolved '$sdkVersion'." } if ((git rev-parse --is-shallow-repository) -ne 'false') { throw 'Repository was checked out without its full history.' } Write-Host "Resolved .NET SDK: $sdkVersion" dotnet --info - name: Set version and create NuGet packages shell: pwsh run: | dotnet run ` --project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" ` -- create-nuget-packages - name: Determine build version id: version shell: pwsh run: | $versionText = [string]( & ".\dotnettools\nbgv.exe" get-version -v Version ) if ($LASTEXITCODE -ne 0) { throw "Nerdbank.GitVersioning failed with exit code $LASTEXITCODE." } $version = [Version]$versionText $buildVersion = $version.ToString() $mainVersion = $version.ToString(3) Write-Host "Build version: $buildVersion" Write-Host "Main version: $mainVersion" "version=$buildVersion" >> $env:GITHUB_OUTPUT "main_version=$mainVersion" >> $env:GITHUB_OUTPUT $metadataDirectory = Join-Path $env:RUNNER_TEMP 'centron-build-metadata' New-Item -Path $metadataDirectory -ItemType Directory -Force | Out-Null Set-Content ` -LiteralPath (Join-Path $metadataDirectory 'version.txt') ` -Value $buildVersion ` -Encoding utf8NoBOM - name: Prepare Artifact Signing dependencies shell: powershell run: | if ((Get-PackageProvider -Name NuGet -ErrorAction Ignore) -eq $null) { Install-PackageProvider -Name NuGet -Force -Scope CurrentUser } if ((Get-InstalledModule -Name PowerShellGet -MinimumVersion 2.2.1 -ErrorAction Ignore) -eq $null) { Install-Module -Name PowerShellGet -MinimumVersion 2.2.1 -Scope CurrentUser -Force -AllowClobber } - name: Build Web Service shell: pwsh run: | dotnet run ` --project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" ` -- build-web-service-only - name: Sign Web Service uses: ./.github/actions/sign-artifacts with: azure-tenant-id: ${{ vars.ARTIFACT_SIGNING_TENANT_ID }} azure-client-id: ${{ vars.ARTIFACT_SIGNING_CLIENT_ID }} azure-client-secret: ${{ secrets.ARTIFACT_SIGNING_CLIENT_SECRET }} files: | ${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\Centron.Host.WindowsService.exe ${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\Centron.Host.WindowsService.dll ${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\ConnectionManager\c-entron Connection Manager.exe ${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\ConnectionManager\c-entron Connection Manager.dll ${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\Centron.Interfaces.dll ${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\Centron.WebServices.Core.dll ${{ github.workspace }}\src\webservice\Centron.Host.WindowsService\bin\Release\net10.0-windows\win-x64\publish\Centron.Core.dll - name: Create Web Service installer shell: pwsh run: | dotnet run ` --project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" ` -- set-dependencies-web-service build-web-service-installer-only - name: Sign Web Service installer uses: ./.github/actions/sign-artifacts with: azure-tenant-id: ${{ vars.ARTIFACT_SIGNING_TENANT_ID }} azure-client-id: ${{ vars.ARTIFACT_SIGNING_CLIENT_ID }} azure-client-secret: ${{ secrets.ARTIFACT_SIGNING_CLIENT_SECRET }} files-folder: ${{ github.workspace }}\deployment\centron\WebServiceSetupProject\bin\Release files-folder-filter: exe,dll,msi files-folder-recurse: true - name: Package Web Service installer shell: pwsh run: | dotnet run ` --project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" ` -- set-zip-directory-web-service - name: Build c-entron.NET shell: pwsh run: | dotnet run ` --project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" ` -- build-centron-net-only - name: Sign c-entron.NET uses: ./.github/actions/sign-artifacts with: azure-tenant-id: ${{ vars.ARTIFACT_SIGNING_TENANT_ID }} azure-client-id: ${{ vars.ARTIFACT_SIGNING_CLIENT_ID }} azure-client-secret: ${{ secrets.ARTIFACT_SIGNING_CLIENT_SECRET }} files: | ${{ github.workspace }}\src\centron\Centron.WPF.UI\bin\Release\net10.0-windows\win-x64\publish\c-entron 2.0.exe ${{ github.workspace }}\src\centron\Centron.WPF.UI\bin\Release\net10.0-windows\win-x64\publish\c-entron 2.0.dll - name: Create c-entron.NET installer shell: pwsh run: | dotnet run ` --project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" ` -- set-dependencies-centron-net build-centron-net-installer-only - name: Sign c-entron.NET installer uses: ./.github/actions/sign-artifacts with: azure-tenant-id: ${{ vars.ARTIFACT_SIGNING_TENANT_ID }} azure-client-id: ${{ vars.ARTIFACT_SIGNING_CLIENT_ID }} azure-client-secret: ${{ secrets.ARTIFACT_SIGNING_CLIENT_SECRET }} files-folder: ${{ github.workspace }}\deployment\centron\CentronSetupProject\bin\Release files-folder-filter: exe,dll,msi files-folder-recurse: true - name: Package c-entron.NET installer shell: pwsh run: | dotnet run ` --project ".\scripts\Centron.Scripts\Centron.Scripts.csproj" ` -- set-zip-directory-centron-net - name: Build Nexus shell: pwsh run: | dotnet run ` --project ".\scripts\Scripts\Scripts.csproj" ` -- build-nexus - name: Sign Nexus uses: ./.github/actions/sign-artifacts with: azure-tenant-id: ${{ vars.ARTIFACT_SIGNING_TENANT_ID }} azure-client-id: ${{ vars.ARTIFACT_SIGNING_CLIENT_ID }} azure-client-secret: ${{ secrets.ARTIFACT_SIGNING_CLIENT_SECRET }} files: | ${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\CentronNexus.Host.exe ${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\CentronNexus.Host.dll ${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\CentronNexus.dll ${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\CentronNexus.OutlookAddIn.dll ${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\Centron.Core.dll ${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\Centron.WebServices.Core.dll ${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\Centron.Interfaces.dll ${{ github.workspace }}\src\nexus\CentronNexus.Host\bin\Release\net10.0\win-x64\publish\Centron.Office.Client.dll - name: Prepare Nexus installer shell: pwsh run: | dotnet run ` --project ".\scripts\Scripts\Scripts.csproj" ` -- zip-nexus-directory dotnet tool update --global wix --version 5.0.2 --allow-downgrade if ($LASTEXITCODE -ne 0) { dotnet tool install --global wix --version 5.0.2 } wix extension add -g WixToolset.UI.wixext/5.0.2 dotnet run ` --project ".\scripts\Scripts\Scripts.csproj" ` -- build-nexus-installer - name: Sign Nexus installer uses: ./.github/actions/sign-artifacts with: azure-tenant-id: ${{ vars.ARTIFACT_SIGNING_TENANT_ID }} azure-client-id: ${{ vars.ARTIFACT_SIGNING_CLIENT_ID }} azure-client-secret: ${{ secrets.ARTIFACT_SIGNING_CLIENT_SECRET }} files: ${{ github.workspace }}\deployment\WixSharpInstaller\bin\Release\net10.0-windows\c-entron Nexus.msi - name: Verify signed installers shell: pwsh run: | $files = @( '.\deployment\centron\WebServiceSetupProject\bin\Release\c-entron Web-Service Installer.msi' '.\deployment\centron\CentronSetupProject\bin\Release\c-entron.NET Installer.msi' '.\deployment\WixSharpInstaller\bin\Release\net10.0-windows\c-entron Nexus.msi' ) foreach ($file in $files) { if (-not (Test-Path -LiteralPath $file -PathType Leaf)) { throw "Signed installer not found: $file" } $signature = Get-AuthenticodeSignature -LiteralPath $file if ($signature.Status -ne 'Valid') { throw "Invalid signature for '$file': $($signature.StatusMessage)" } Write-Host "Valid signature: $file" } - name: Package Nexus installer shell: pwsh run: | dotnet run ` --project ".\scripts\Scripts\Scripts.csproj" ` -- zip-nexus-singleFile - name: Upload build artifacts id: build-artifact uses: actions/upload-artifact@v7 with: name: centron-build path: artifacts/ if-no-files-found: error retention-days: 14 - name: Upload build version metadata uses: actions/upload-artifact@v7 with: name: centron-build-version-${{ steps.version.outputs.version }} path: ${{ runner.temp }}/centron-build-metadata/version.txt if-no-files-found: error retention-days: 14 - name: Add artifact download link shell: pwsh run: | "### Signed build artifacts" >> $env:GITHUB_STEP_SUMMARY "Build version: ${{ steps.version.outputs.version }}" >> $env:GITHUB_STEP_SUMMARY "[Download centron-build](${{ steps.build-artifact.outputs.artifact-url }})" >> $env:GITHUB_STEP_SUMMARY upload-centron-net: name: Upload c-entron.NET needs: build if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') }} permissions: actions: read contents: read id-token: write uses: NEXOWARE-Systems/ci-cd-reusable-workflows/.github/workflows/upload-software-build.yml@main with: artifact_name: centron-build source_path: c-entron.NET Installer.zip destination_folder: c-entron.NET destination_file: c-entron.NET Installer.zip version: ${{ needs.build.outputs.version }} main_version: ${{ needs.build.outputs.main_version }} environment_name: SoftwareBuilds azure_client_id: ${{ vars.AZURE_CLIENT_ID }} azure_tenant_id: ${{ vars.AZURE_TENANT_ID }} upload-web-service: name: Upload c-entron Web Service needs: build if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') }} permissions: actions: read contents: read id-token: write uses: NEXOWARE-Systems/ci-cd-reusable-workflows/.github/workflows/upload-software-build.yml@main with: artifact_name: centron-build source_path: c-entron Web-Service Installer.zip destination_folder: c-entron Web-Service destination_file: c-entron Web-Service Installer.zip version: ${{ needs.build.outputs.version }} main_version: ${{ needs.build.outputs.main_version }} environment_name: SoftwareBuilds azure_client_id: ${{ vars.AZURE_CLIENT_ID }} azure_tenant_id: ${{ vars.AZURE_TENANT_ID }} upload-nexus: name: Upload c-entron Nexus needs: build if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') }} permissions: actions: read contents: read id-token: write uses: NEXOWARE-Systems/ci-cd-reusable-workflows/.github/workflows/upload-software-build.yml@main with: artifact_name: centron-build source_path: c-entron Nexus Installer.zip destination_folder: c-entron Nexus destination_file: c-entron Nexus Installer.zip version: ${{ needs.build.outputs.version }} main_version: ${{ needs.build.outputs.main_version }} environment_name: SoftwareBuilds azure_client_id: ${{ vars.AZURE_CLIENT_ID }} azure_tenant_id: ${{ vars.AZURE_TENANT_ID }}