From 87b1420138c519e1e2819c203d85ac41fbf5be63 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Sat, 28 Mar 2026 01:46:47 +0000 Subject: [PATCH 001/102] Add initial Azure validation pipeline --- .github/workflows/test.yml | 4 +- azure-pipelines.yml | 281 +++++++++++++++++++++++++++++++++++++ 2 files changed, 283 insertions(+), 2 deletions(-) create mode 100644 azure-pipelines.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e5c015e..7f21868 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,9 +2,9 @@ name: Run Tests on: push: - branches: [ main, develop ] + branches: [ main ] pull_request: - branches: [ main, develop ] + branches: [ main ] jobs: test: diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..4cb063e --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,281 @@ +# Hybrid validation pipeline for the GitHub-hosted UE customization repo. +# +# This pipeline is intended to be connected to GitHub through the Azure Pipelines GitHub App +# so its checks surface back into GitHub pull requests targeting main. +# +# The hosted test stage can run immediately. +# The security stage is disabled by default until the Technology project has: +# - a self-hosted Windows pool with UE installed and MSBuild available +# - service connections named `Coverity on Polaris` and `BlackDuckScanner` +# - a variable group named `BlackDuck on Polaris` exposing BLACKDUCK_ACCESS_TOKEN + +resources: + repositories: + - repository: Templates + type: git + name: Dabacon Products/Templates + ref: refs/heads/main + +variables: + - template: variables.yml@Templates + - name: HostedVmImage + value: windows-latest + - name: SecurityPoolName + value: Technology UE Validation + - name: EnableSecurityScans + value: 'false' + - name: CoverityServiceConnection + value: Coverity on Polaris + - name: BlackDuckServiceConnection + value: BlackDuckScanner + - name: BlackDuckGroup + value: DabaconProductsGroup + - name: CoverityProjectTeam + value: .RnD.Polaris.Dabacon-Products.SecurityAdvisors + - name: CoverityCliVersion + value: 2025.9.0 + +trigger: + branches: + include: + - main + +pr: + autoCancel: 'true' + branches: + include: + - main + +stages: + - stage: HostedTests + displayName: Hosted Tests + jobs: + - job: Pester + displayName: Pester + pool: + vmImage: $(HostedVmImage) + steps: + - checkout: self + fetchDepth: '0' + + - pwsh: | + Write-Host "PowerShell version: $($PSVersionTable.PSVersion)" + displayName: Show PowerShell version + + - pwsh: | + if (-not (Get-PackageProvider -Name NuGet -ListAvailable -ErrorAction SilentlyContinue)) { + Write-Host "Installing NuGet provider..." + Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force | Out-Null + } + displayName: Install NuGet provider + + - pwsh: | + $psGallery = Get-PSRepository -Name 'PSGallery' -ErrorAction SilentlyContinue + if ($psGallery -and $psGallery.InstallationPolicy -ne 'Trusted') { + Write-Host "Trusting PSGallery..." + Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted + } + displayName: Trust PSGallery + + - pwsh: | + if (-not (Get-Module -ListAvailable -Name Pester)) { + Write-Host "Installing Pester module..." + Install-Module Pester -Scope CurrentUser -Force -AllowClobber -SkipPublisherCheck | Out-Null + } + displayName: Install Pester + + - pwsh: | + ./scripts/Tests/Run-Tests.ps1 -Quick + displayName: Run tests + + - stage: SecurityValidation + displayName: Security Validation + dependsOn: HostedTests + condition: and(succeeded(), eq(variables['EnableSecurityScans'], 'true')) + jobs: + - job: CoverityAndBlackDuck + displayName: Coverity And Black Duck + timeoutInMinutes: '180' + pool: + name: $(SecurityPoolName) + variables: + - group: BlackDuck on Polaris + steps: + - checkout: self + fetchDepth: '0' + + - checkout: Templates + fetchDepth: '1' + + - pwsh: | + $ueCommand = Get-Command UE -ErrorAction SilentlyContinue + if (-not $ueCommand) { + throw 'UE command is not available on this agent. Use a self-hosted Windows agent with AVEVA Unified Engineering installed.' + } + + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + if (-not (Test-Path -Path $vswhere)) { + throw 'vswhere.exe is not available on this agent. Install Visual Studio Build Tools or Visual Studio.' + } + displayName: Validate agent prerequisites + + - pwsh: | + ./scripts/Setup-UECustomizationEnvironment.ps1 + displayName: Prepare UE customization environment + + - pwsh: | + ./scripts/Build-AddIns.ps1 -Directory templates -Configuration Release + ./scripts/Build-AddIns.ps1 -Directory Examples -Configuration Release + displayName: Build add-in projects + + - pwsh: | + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + $msbuildPath = & $vswhere -latest -products * -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | Select-Object -First 1 + + if (-not $msbuildPath) { + throw 'MSBuild.exe could not be located with vswhere.' + } + + $solutions = Get-ChildItem -Path $env:BUILD_SOURCESDIRECTORY -Recurse -Filter *.sln -File | + Where-Object { + $_.FullName -like "*\\Examples\\*" -or $_.FullName -like "*\\templates\\*" + } | + Sort-Object -Property FullName + + if (-not $solutions) { + throw 'No solution files were found under Examples or templates.' + } + + $branchName = "$env:BUILD_SOURCEBRANCH" -replace '^refs/heads/', '' + $commitDate = ([DateTime](git -C $env:BUILD_SOURCESDIRECTORY log -1 --format=%ci)).ToString('o') + + $cleanCommands = foreach ($solution in $solutions) { + " - shell: ['$msbuildPath', '$($solution.FullName)', '/t:Clean', '/p:Configuration=Release']" + } + + $buildCommands = foreach ($solution in $solutions) { + " - shell: ['$msbuildPath', '$($solution.FullName)', '/t:Build', '/p:Configuration=Release']" + } + + $polarisContent = @( + 'version: 1' + 'project:' + " name: $env:BUILD_REPOSITORY_NAME" + " branch: $branchName" + " projectDir: $env:BUILD_SOURCESDIRECTORY" + ' revision:' + " name: $env:BUILD_SOURCEVERSION" + " date: $commitDate" + 'capture:' + ' build:' + ' cleanCommands:' + $cleanCommands + ' buildCommands:' + $buildCommands + 'analyze:' + ' mode: central' + 'install:' + ' coverity:' + " version: $(CoverityCliVersion)" + 'serverUrl: https://osisoft.cop.blackduck.com' + ) -join "`n" + + $polarisPath = Join-Path $env:PIPELINE_WORKSPACE 'polaris.yml' + $polarisContent | Out-File -FilePath $polarisPath -Encoding ascii -Force + Get-Content -Path $polarisPath + displayName: Generate polaris.yml + + - task: Cache@2 + displayName: Restore Coverity CLI cache + inputs: + key: CoverityCLI | $(CoverityCliVersion) | v1 + path: $(Pipeline.Workspace)\Coverity-CLI + cacheHitVar: COVERITY_CLI_RESTORED + + - pwsh: | + $toolPath = "$(Pipeline.Workspace)\Coverity-CLI" + if (-not (Test-Path -Path $toolPath)) { + New-Item -ItemType Directory -Path $toolPath | Out-Null + } + displayName: Ensure Coverity CLI folder + + - task: BlackduckCoverityOnPolaris@2 + displayName: Coverity scan + inputs: + polarisService: $(CoverityServiceConnection) + polarisCommand: > + -c $(Pipeline.Workspace)\polaris.yml + analyze + -w + env: + AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) + POLARIS_HOME: $(Pipeline.Workspace)\Coverity-CLI + + - publish: $(Build.SourcesDirectory)\.blackduck + artifact: Coverity + displayName: Publish Coverity artifacts + condition: always() + + - pwsh: | + if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { + $versionName = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)" + } + else { + $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' + $versionName = "$branchName-$($env:BUILD_BUILDID)" + } + + Write-Host "##vso[task.setvariable variable=BlackDuckProjectVersion]$versionName" + Write-Host "Black Duck project version: $versionName" + displayName: Calculate Black Duck version + + - task: ArchiveFiles@2 + displayName: Archive sources and build outputs + inputs: + rootFolderOrFile: $(Build.SourcesDirectory) + includeRootFolder: true + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)\blackduck-input.zip + replaceExistingArchive: true + + - task: BlackDuckDetectTask@10 + displayName: Black Duck scan + inputs: + BlackDuckScaService: $(BlackDuckServiceConnection) + DetectVersion: latest + DetectFolder: $(Pipeline.Workspace)\detect + DetectArguments: | + --detect.project.name=$(Build.Repository.Name) + --detect.project.version.name=$(BlackDuckProjectVersion) + --detect.project.group.name=$(BlackDuckGroup) + --detect.source.path=$(Build.SourcesDirectory) + --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)\blackduck-input.zip + --detect.cleanup=true + --detect.output.path=$(Build.ArtifactStagingDirectory)\blackduck-logs + --detect.detector.search.depth=10 + --detect.timeout=1800 + --detect.wait.for.results=true + --detect.wait.for.results.timeout=6000000 + --logging.level.detect=DEBUG + --detect.diagnostic=true + + - publish: $(Build.ArtifactStagingDirectory)\blackduck-logs + artifact: BlackDuckLogs + displayName: Publish Black Duck logs + condition: always() + + - pwsh: | + $outputPath = Join-Path $env:PIPELINE_WORKSPACE 'BlackDuckComponentNotices.txt' + & "$(Pipeline.Workspace)\Templates\scripts\BlackDuck\Generate-BlackDuckNotice.ps1" ` + -ProjectName "$(Build.Repository.Name)" ` + -ProjectVersion "$(BlackDuckProjectVersion)" ` + -OutputPath $outputPath + displayName: Generate Black Duck NOTICE + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/main')) + env: + BlackduckToken: $(BLACKDUCK_ACCESS_TOKEN) + + - publish: $(Pipeline.Workspace)\BlackDuckComponentNotices.txt + artifact: BlackDuckNotice + displayName: Publish Black Duck NOTICE + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/main')) \ No newline at end of file From b992bbf606870beabed9cbdfb2446b1e250d05dd Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Sat, 28 Mar 2026 04:53:05 +0000 Subject: [PATCH 002/102] take out blackduck until we get service connection shared --- .github/workflows/test.yml | 80 +++++++-------- azure-pipelines.yml | 198 ++++++++++++++++++++----------------- 2 files changed, 148 insertions(+), 130 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7f21868..a2bdd2e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,50 +2,50 @@ name: Run Tests on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] jobs: test: runs-on: windows-latest steps: - - uses: actions/checkout@v4 - - - name: Set up PowerShell - shell: pwsh - run: | - Write-Host "PowerShell version: $($PSVersionTable.PSVersion)" - - - name: Install NuGet provider - shell: pwsh - run: | - if (-not (Get-PackageProvider -Name NuGet -ListAvailable -ErrorAction SilentlyContinue)) { - Write-Host "Installing NuGet provider..." - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force | Out-Null - } - - - name: Trust PSGallery - shell: pwsh - run: | - $psGallery = Get-PSRepository -Name 'PSGallery' -ErrorAction SilentlyContinue - if ($psGallery -and $psGallery.InstallationPolicy -ne 'Trusted') { - Write-Host "Trusting PSGallery..." - Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted - } - - - name: Install Pester - shell: pwsh - run: | - if (-not (Get-Module -ListAvailable -Name Pester)) { - Write-Host "Installing Pester module..." - Install-Module Pester -Scope CurrentUser -Force -AllowClobber -SkipPublisherCheck | Out-Null - } - - - name: Run tests - shell: pwsh - run: | - cd ${{ github.workspace }} - & .\scripts\Tests\Run-Tests.ps1 -Quick - continue-on-error: false + - uses: actions/checkout@v4 + + - name: Set up PowerShell + shell: pwsh + run: | + Write-Host "PowerShell version: $($PSVersionTable.PSVersion)" + + - name: Install NuGet provider + shell: pwsh + run: | + if (-not (Get-PackageProvider -Name NuGet -ListAvailable -ErrorAction SilentlyContinue)) { + Write-Host "Installing NuGet provider..." + Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force | Out-Null + } + + - name: Trust PSGallery + shell: pwsh + run: | + $psGallery = Get-PSRepository -Name 'PSGallery' -ErrorAction SilentlyContinue + if ($psGallery -and $psGallery.InstallationPolicy -ne 'Trusted') { + Write-Host "Trusting PSGallery..." + Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted + } + + - name: Install Pester + shell: pwsh + run: | + if (-not (Get-Module -ListAvailable -Name Pester)) { + Write-Host "Installing Pester module..." + Install-Module Pester -Scope CurrentUser -Force -AllowClobber -SkipPublisherCheck | Out-Null + } + + - name: Run tests + shell: pwsh + run: | + cd ${{ github.workspace }} + & .\scripts\Tests\Run-Tests.ps1 -Quick + continue-on-error: false diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4cb063e..4c6dca6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -4,7 +4,8 @@ # so its checks surface back into GitHub pull requests targeting main. # # The hosted test stage can run immediately. -# The security stage is disabled by default until the Technology project has: +# The security stage runs on CI builds from main or when doBuildAnalysis=true. +# The Technology project still needs: # - a self-hosted Windows pool with UE installed and MSBuild available # - service connections named `Coverity on Polaris` and `BlackDuckScanner` # - a variable group named `BlackDuck on Polaris` exposing BLACKDUCK_ACCESS_TOKEN @@ -22,8 +23,6 @@ variables: value: windows-latest - name: SecurityPoolName value: Technology UE Validation - - name: EnableSecurityScans - value: 'false' - name: CoverityServiceConnection value: Coverity on Polaris - name: BlackDuckServiceConnection @@ -41,7 +40,7 @@ trigger: - main pr: - autoCancel: 'true' + autoCancel: "true" branches: include: - main @@ -56,7 +55,7 @@ stages: vmImage: $(HostedVmImage) steps: - checkout: self - fetchDepth: '0' + fetchDepth: "0" - pwsh: | Write-Host "PowerShell version: $($PSVersionTable.PSVersion)" @@ -91,21 +90,28 @@ stages: - stage: SecurityValidation displayName: Security Validation dependsOn: HostedTests - condition: and(succeeded(), eq(variables['EnableSecurityScans'], 'true')) + condition: | + and( + succeeded(), + or( + eq(variables['Build.SourceBranch'], 'refs/heads/main'), + eq(variables['doBuildAnalysis'], 'true') + ) + ) jobs: - job: CoverityAndBlackDuck displayName: Coverity And Black Duck - timeoutInMinutes: '180' + timeoutInMinutes: "180" pool: name: $(SecurityPoolName) variables: - group: BlackDuck on Polaris steps: - checkout: self - fetchDepth: '0' + fetchDepth: "0" - checkout: Templates - fetchDepth: '1' + fetchDepth: "1" - pwsh: | $ueCommand = Get-Command UE -ErrorAction SilentlyContinue @@ -147,6 +153,11 @@ stages: } $branchName = "$env:BUILD_SOURCEBRANCH" -replace '^refs/heads/', '' + $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') + $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` + ? "@$($templatesBranch)" ` + : '' + $polarisBranch = "$branchName$templatesPostfix" $commitDate = ([DateTime](git -C $env:BUILD_SOURCESDIRECTORY log -1 --format=%ci)).ToString('o') $cleanCommands = foreach ($solution in $solutions) { @@ -161,7 +172,7 @@ stages: 'version: 1' 'project:' " name: $env:BUILD_REPOSITORY_NAME" - " branch: $branchName" + " branch: $polarisBranch" " projectDir: $env:BUILD_SOURCESDIRECTORY" ' revision:' " name: $env:BUILD_SOURCEVERSION" @@ -199,83 +210,90 @@ stages: } displayName: Ensure Coverity CLI folder - - task: BlackduckCoverityOnPolaris@2 - displayName: Coverity scan - inputs: - polarisService: $(CoverityServiceConnection) - polarisCommand: > - -c $(Pipeline.Workspace)\polaris.yml - analyze - -w - env: - AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) - POLARIS_HOME: $(Pipeline.Workspace)\Coverity-CLI - - - publish: $(Build.SourcesDirectory)\.blackduck - artifact: Coverity - displayName: Publish Coverity artifacts - condition: always() - - - pwsh: | - if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { - $versionName = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)" - } - else { - $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' - $versionName = "$branchName-$($env:BUILD_BUILDID)" - } - - Write-Host "##vso[task.setvariable variable=BlackDuckProjectVersion]$versionName" - Write-Host "Black Duck project version: $versionName" - displayName: Calculate Black Duck version - - - task: ArchiveFiles@2 - displayName: Archive sources and build outputs - inputs: - rootFolderOrFile: $(Build.SourcesDirectory) - includeRootFolder: true - archiveType: zip - archiveFile: $(Build.ArtifactStagingDirectory)\blackduck-input.zip - replaceExistingArchive: true - - - task: BlackDuckDetectTask@10 - displayName: Black Duck scan - inputs: - BlackDuckScaService: $(BlackDuckServiceConnection) - DetectVersion: latest - DetectFolder: $(Pipeline.Workspace)\detect - DetectArguments: | - --detect.project.name=$(Build.Repository.Name) - --detect.project.version.name=$(BlackDuckProjectVersion) - --detect.project.group.name=$(BlackDuckGroup) - --detect.source.path=$(Build.SourcesDirectory) - --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)\blackduck-input.zip - --detect.cleanup=true - --detect.output.path=$(Build.ArtifactStagingDirectory)\blackduck-logs - --detect.detector.search.depth=10 - --detect.timeout=1800 - --detect.wait.for.results=true - --detect.wait.for.results.timeout=6000000 - --logging.level.detect=DEBUG - --detect.diagnostic=true - - - publish: $(Build.ArtifactStagingDirectory)\blackduck-logs - artifact: BlackDuckLogs - displayName: Publish Black Duck logs - condition: always() - - - pwsh: | - $outputPath = Join-Path $env:PIPELINE_WORKSPACE 'BlackDuckComponentNotices.txt' - & "$(Pipeline.Workspace)\Templates\scripts\BlackDuck\Generate-BlackDuckNotice.ps1" ` - -ProjectName "$(Build.Repository.Name)" ` - -ProjectVersion "$(BlackDuckProjectVersion)" ` - -OutputPath $outputPath - displayName: Generate Black Duck NOTICE - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/main')) - env: - BlackduckToken: $(BLACKDUCK_ACCESS_TOKEN) - - - publish: $(Pipeline.Workspace)\BlackDuckComponentNotices.txt - artifact: BlackDuckNotice - displayName: Publish Black Duck NOTICE - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/main')) \ No newline at end of file + # - task: BlackduckCoverityOnPolaris@2 + # displayName: Coverity scan + # inputs: + # polarisService: $(CoverityServiceConnection) + # polarisCommand: > + # -c $(Pipeline.Workspace)\polaris.yml + # analyze + # -w + # env: + # AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) + # POLARIS_HOME: $(Pipeline.Workspace)\Coverity-CLI + + # - publish: $(Build.SourcesDirectory)\.blackduck + # artifact: Coverity + # displayName: Publish Coverity artifacts + # condition: always() + + # - pwsh: | + # $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') + # $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` + # ? "@$($templatesBranch)" ` + # : '' + + # if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { + # $versionName = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)" + # } + # else { + # $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' + # $versionName = "$branchName-$($env:BUILD_BUILDID)" + # } + + # $versionName = "$versionName$templatesPostfix" + + # Write-Host "##vso[task.setvariable variable=BlackDuckProjectVersion]$versionName" + # Write-Host "Black Duck project version: $versionName" + # displayName: Calculate Black Duck version + + # - task: ArchiveFiles@2 + # displayName: Archive sources and build outputs + # inputs: + # rootFolderOrFile: $(Build.SourcesDirectory) + # includeRootFolder: true + # archiveType: zip + # archiveFile: $(Build.ArtifactStagingDirectory)\blackduck-input.zip + # replaceExistingArchive: true + + # - task: BlackDuckDetectTask@10 + # displayName: Black Duck scan + # inputs: + # BlackDuckScaService: $(BlackDuckServiceConnection) + # DetectVersion: latest + # DetectFolder: $(Pipeline.Workspace)\detect + # DetectArguments: | + # --detect.project.name=$(Build.Repository.Name) + # --detect.project.version.name=$(BlackDuckProjectVersion) + # --detect.project.group.name=$(BlackDuckGroup) + # --detect.source.path=$(Build.SourcesDirectory) + # --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)\blackduck-input.zip + # --detect.cleanup=true + # --detect.output.path=$(Build.ArtifactStagingDirectory)\blackduck-logs + # --detect.detector.search.depth=10 + # --detect.timeout=1800 + # --detect.wait.for.results=true + # --detect.wait.for.results.timeout=6000000 + # --logging.level.detect=DEBUG + # --detect.diagnostic=true + + # - publish: $(Build.ArtifactStagingDirectory)\blackduck-logs + # artifact: BlackDuckLogs + # displayName: Publish Black Duck logs + # condition: always() + + # - pwsh: | + # $outputPath = Join-Path $env:PIPELINE_WORKSPACE 'BlackDuckComponentNotices.txt' + # & "$(Pipeline.Workspace)\Templates\scripts\BlackDuck\Generate-BlackDuckNotice.ps1" ` + # -ProjectName "$(Build.Repository.Name)" ` + # -ProjectVersion "$(BlackDuckProjectVersion)" ` + # -OutputPath $outputPath + # displayName: Generate Black Duck NOTICE + # condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/main')) + # env: + # BlackduckToken: $(BLACKDUCK_ACCESS_TOKEN) + + # - publish: $(Pipeline.Workspace)\BlackDuckComponentNotices.txt + # artifact: BlackDuckNotice + # displayName: Publish Black Duck NOTICE + # condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/main')) From b102e739b0643583ef8cb3f046a25c7cd00083f1 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Sat, 28 Mar 2026 05:06:47 +0000 Subject: [PATCH 003/102] refactor: update Azure pipeline to use specific Ubuntu pool and remove unused variables --- azure-pipelines.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4c6dca6..6dbcb6e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,10 +19,10 @@ resources: variables: - template: variables.yml@Templates - - name: HostedVmImage - value: windows-latest - - name: SecurityPoolName - value: Technology UE Validation + # - name: HostedVmImage + # value: windows-latest + # - name: SecurityPoolName + # value: Technology UE Validation - name: CoverityServiceConnection value: Coverity on Polaris - name: BlackDuckServiceConnection @@ -51,8 +51,14 @@ stages: jobs: - job: Pester displayName: Pester + # pool: + # vmImage: $(HostedVmImage) + container: mcr.microsoft.com/dotnet/sdk:9.0 pool: - vmImage: $(HostedVmImage) + name: Dabacon-Products-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage steps: - checkout: self fetchDepth: "0" @@ -102,8 +108,14 @@ stages: - job: CoverityAndBlackDuck displayName: Coverity And Black Duck timeoutInMinutes: "180" + # pool: + # name: $(SecurityPoolName) + container: mcr.microsoft.com/dotnet/sdk:9.0 pool: - name: $(SecurityPoolName) + name: Dabacon-Products-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage variables: - group: BlackDuck on Polaris steps: From 48664f600f27b545651234186b37d3a1eee0f520 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Sat, 28 Mar 2026 05:11:12 +0000 Subject: [PATCH 004/102] HostedVmImage --- azure-pipelines.yml | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6dbcb6e..e0280e5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,8 +19,8 @@ resources: variables: - template: variables.yml@Templates - # - name: HostedVmImage - # value: windows-latest + - name: HostedVmImage + value: windows-latest # - name: SecurityPoolName # value: Technology UE Validation - name: CoverityServiceConnection @@ -51,14 +51,14 @@ stages: jobs: - job: Pester displayName: Pester - # pool: - # vmImage: $(HostedVmImage) - container: mcr.microsoft.com/dotnet/sdk:9.0 pool: - name: Dabacon-Products-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage + vmImage: $(HostedVmImage) + # container: mcr.microsoft.com/dotnet/sdk:9.0 + # pool: + # name: Dabacon-Products-Microsoft-Hosted-Ubuntu + # demands: + # - ImageOverride -equals ubuntu-latest + # - WorkFolder -equals /mnt/storage/sdc/storage steps: - checkout: self fetchDepth: "0" @@ -108,14 +108,16 @@ stages: - job: CoverityAndBlackDuck displayName: Coverity And Black Duck timeoutInMinutes: "180" + pool: + vmImage: $(HostedVmImage) # pool: # name: $(SecurityPoolName) - container: mcr.microsoft.com/dotnet/sdk:9.0 - pool: - name: Dabacon-Products-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage + # container: mcr.microsoft.com/dotnet/sdk:9.0 + # pool: + # name: Dabacon-Products-Microsoft-Hosted-Ubuntu + # demands: + # - ImageOverride -equals ubuntu-latest + # - WorkFolder -equals /mnt/storage/sdc/storage variables: - group: BlackDuck on Polaris steps: From ef7bb9cf0035280287d381d16cee9094c2a08c0f Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Sun, 29 Mar 2026 02:08:40 +0100 Subject: [PATCH 005/102] Use Technology Templates fork for pipeline checkout --- azure-pipelines.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e0280e5..da9c572 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -5,6 +5,7 @@ # # The hosted test stage can run immediately. # The security stage runs on CI builds from main or when doBuildAnalysis=true. +# Templates is consumed from a Technology fork to avoid cross-project checkout permissions. # The Technology project still needs: # - a self-hosted Windows pool with UE installed and MSBuild available # - service connections named `Coverity on Polaris` and `BlackDuckScanner` @@ -14,7 +15,7 @@ resources: repositories: - repository: Templates type: git - name: Dabacon Products/Templates + name: Technology/Templates ref: refs/heads/main variables: From aa1f31d1427a9e624de7784e031d8d7bb03b02ff Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Sun, 29 Mar 2026 16:52:45 +0100 Subject: [PATCH 006/102] Gate UE security validation until infrastructure exists --- azure-pipelines.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index da9c572..e18d06e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -4,7 +4,8 @@ # so its checks surface back into GitHub pull requests targeting main. # # The hosted test stage can run immediately. -# The security stage runs on CI builds from main or when doBuildAnalysis=true. +# The security stage remains opt-in until the Technology project has a UE-capable +# self-hosted Windows pool or the lane is rewritten into a hosted-compatible flow. # Templates is consumed from a Technology fork to avoid cross-project checkout permissions. # The Technology project still needs: # - a self-hosted Windows pool with UE installed and MSBuild available @@ -22,6 +23,8 @@ variables: - template: variables.yml@Templates - name: HostedVmImage value: windows-latest + - name: EnableSecurityValidation + value: 'false' # - name: SecurityPoolName # value: Technology UE Validation - name: CoverityServiceConnection @@ -97,14 +100,7 @@ stages: - stage: SecurityValidation displayName: Security Validation dependsOn: HostedTests - condition: | - and( - succeeded(), - or( - eq(variables['Build.SourceBranch'], 'refs/heads/main'), - eq(variables['doBuildAnalysis'], 'true') - ) - ) + condition: and(succeeded(), eq(variables['EnableSecurityValidation'], 'true')) jobs: - job: CoverityAndBlackDuck displayName: Coverity And Black Duck From c9ab255fcecb538f3ef1f02bdcfb7eedb545293d Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Sun, 29 Mar 2026 22:37:26 +0100 Subject: [PATCH 007/102] Enable hosted fake compile for Coverity --- azure-pipelines.yml | 88 +-- scripts/Invoke-FakeUECompile.ps1 | 76 +++ ...a.ApplicationFramework.Presentation.csproj | 6 + shims/Aveva.ApplicationFramework.csproj | 6 + shims/Aveva.Core.Database.Filters.csproj | 6 + shims/Aveva.Core.Database.csproj | 15 + shims/Aveva.Core.Geometry.csproj | 6 + shims/Aveva.Core.Implementation.csproj | 6 + shims/Aveva.Core.Presentation.csproj | 6 + shims/Aveva.Core.Utilities.csproj | 6 + shims/Aveva.Core3D.Clasher.csproj | 6 + shims/Directory.Build.props | 14 + shims/StartUp.csproj | 6 + shims/src/ApplicationFrameworkPresentation.cs | 17 + shims/src/Clasher.cs | 82 +++ shims/src/Database.cs | 614 ++++++++++++++++++ shims/src/Filters.cs | 86 +++ shims/src/Geometry.cs | 45 ++ shims/src/Presentation.cs | 69 ++ shims/src/Utilities.cs | 88 +++ 20 files changed, 1188 insertions(+), 60 deletions(-) create mode 100644 scripts/Invoke-FakeUECompile.ps1 create mode 100644 shims/Aveva.ApplicationFramework.Presentation.csproj create mode 100644 shims/Aveva.ApplicationFramework.csproj create mode 100644 shims/Aveva.Core.Database.Filters.csproj create mode 100644 shims/Aveva.Core.Database.csproj create mode 100644 shims/Aveva.Core.Geometry.csproj create mode 100644 shims/Aveva.Core.Implementation.csproj create mode 100644 shims/Aveva.Core.Presentation.csproj create mode 100644 shims/Aveva.Core.Utilities.csproj create mode 100644 shims/Aveva.Core3D.Clasher.csproj create mode 100644 shims/Directory.Build.props create mode 100644 shims/StartUp.csproj create mode 100644 shims/src/ApplicationFrameworkPresentation.cs create mode 100644 shims/src/Clasher.cs create mode 100644 shims/src/Database.cs create mode 100644 shims/src/Filters.cs create mode 100644 shims/src/Geometry.cs create mode 100644 shims/src/Presentation.cs create mode 100644 shims/src/Utilities.cs diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e18d06e..13db6b8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -115,8 +115,6 @@ stages: # demands: # - ImageOverride -equals ubuntu-latest # - WorkFolder -equals /mnt/storage/sdc/storage - variables: - - group: BlackDuck on Polaris steps: - checkout: self fetchDepth: "0" @@ -125,43 +123,12 @@ stages: fetchDepth: "1" - pwsh: | - $ueCommand = Get-Command UE -ErrorAction SilentlyContinue - if (-not $ueCommand) { - throw 'UE command is not available on this agent. Use a self-hosted Windows agent with AVEVA Unified Engineering installed.' - } - - $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" - if (-not (Test-Path -Path $vswhere)) { - throw 'vswhere.exe is not available on this agent. Install Visual Studio Build Tools or Visual Studio.' - } - displayName: Validate agent prerequisites - - - pwsh: | - ./scripts/Setup-UECustomizationEnvironment.ps1 - displayName: Prepare UE customization environment + ./scripts/Invoke-FakeUECompile.ps1 -Directory Examples -Configuration Release + displayName: Build example projects with fake UE references - pwsh: | - ./scripts/Build-AddIns.ps1 -Directory templates -Configuration Release - ./scripts/Build-AddIns.ps1 -Directory Examples -Configuration Release - displayName: Build add-in projects - - - pwsh: | - $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" - $msbuildPath = & $vswhere -latest -products * -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | Select-Object -First 1 - - if (-not $msbuildPath) { - throw 'MSBuild.exe could not be located with vswhere.' - } - - $solutions = Get-ChildItem -Path $env:BUILD_SOURCESDIRECTORY -Recurse -Filter *.sln -File | - Where-Object { - $_.FullName -like "*\\Examples\\*" -or $_.FullName -like "*\\templates\\*" - } | - Sort-Object -Property FullName - - if (-not $solutions) { - throw 'No solution files were found under Examples or templates.' - } + $pwshPath = (Get-Command pwsh -ErrorAction Stop).Source + $fakeCompileScriptPath = Join-Path $env:BUILD_SOURCESDIRECTORY 'scripts\Invoke-FakeUECompile.ps1' $branchName = "$env:BUILD_SOURCEBRANCH" -replace '^refs/heads/', '' $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') @@ -171,13 +138,13 @@ stages: $polarisBranch = "$branchName$templatesPostfix" $commitDate = ([DateTime](git -C $env:BUILD_SOURCESDIRECTORY log -1 --format=%ci)).ToString('o') - $cleanCommands = foreach ($solution in $solutions) { - " - shell: ['$msbuildPath', '$($solution.FullName)', '/t:Clean', '/p:Configuration=Release']" - } + $cleanCommands = @( + " - shell: ['$pwshPath', '-NoProfile', '-Command', 'Write-Host fake-clean-step']" + ) - $buildCommands = foreach ($solution in $solutions) { - " - shell: ['$msbuildPath', '$($solution.FullName)', '/t:Build', '/p:Configuration=Release']" - } + $buildCommands = @( + " - shell: ['$pwshPath', '-NoProfile', '-File', '$fakeCompileScriptPath', '-Directory', 'Examples', '-Configuration', 'Release']" + ) $polarisContent = @( 'version: 1' @@ -209,8 +176,9 @@ stages: - task: Cache@2 displayName: Restore Coverity CLI cache + retryCountOnTaskFailure: 3 inputs: - key: CoverityCLI | $(CoverityCliVersion) | v1 + key: '"CoverityCLI" | "$(CoverityCliVersion)" | "v1"' path: $(Pipeline.Workspace)\Coverity-CLI cacheHitVar: COVERITY_CLI_RESTORED @@ -221,22 +189,22 @@ stages: } displayName: Ensure Coverity CLI folder - # - task: BlackduckCoverityOnPolaris@2 - # displayName: Coverity scan - # inputs: - # polarisService: $(CoverityServiceConnection) - # polarisCommand: > - # -c $(Pipeline.Workspace)\polaris.yml - # analyze - # -w - # env: - # AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) - # POLARIS_HOME: $(Pipeline.Workspace)\Coverity-CLI - - # - publish: $(Build.SourcesDirectory)\.blackduck - # artifact: Coverity - # displayName: Publish Coverity artifacts - # condition: always() + - task: BlackduckCoverityOnPolaris@2 + displayName: Coverity scan + inputs: + polarisService: $(CoverityServiceConnection) + polarisCommand: > + -c $(Pipeline.Workspace)\polaris.yml + analyze + -w + env: + AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) + POLARIS_HOME: $(Pipeline.Workspace)\Coverity-CLI + + - publish: $(Build.SourcesDirectory)\.blackduck + artifact: Coverity + displayName: Publish Coverity artifacts + condition: always() # - pwsh: | # $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') diff --git a/scripts/Invoke-FakeUECompile.ps1 b/scripts/Invoke-FakeUECompile.ps1 new file mode 100644 index 0000000..9f3e94f --- /dev/null +++ b/scripts/Invoke-FakeUECompile.ps1 @@ -0,0 +1,76 @@ +#Requires -Version 7.0 + +[CmdletBinding()] +param( + [string[]]$Directory = @('Examples'), + [string]$Configuration = 'Release', + [string]$ShimProjectsPath, + [string]$ShimOutputPath +) + +$ErrorActionPreference = 'Stop' + +$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path + +if (-not $ShimProjectsPath) { + $ShimProjectsPath = Join-Path $repoRoot 'shims' +} + +if (-not $ShimOutputPath) { + $ShimOutputPath = Join-Path $ShimProjectsPath "artifacts\$Configuration" +} + +$resolvedShimProjectsPath = (Resolve-Path $ShimProjectsPath).Path +$buildScriptPath = Join-Path $PSScriptRoot 'Build-AddIns.ps1' + +$projects = Get-ChildItem -Path $resolvedShimProjectsPath -Filter '*.csproj' -File | Sort-Object Name +if (-not $projects) { + throw "No shim projects found under $resolvedShimProjectsPath" +} + +foreach ($project in $projects) { + Write-Host ("Building shim assembly: {0}" -f $project.Name) -ForegroundColor Cyan + dotnet build $project.FullName -c $Configuration -nologo + if ($LASTEXITCODE -ne 0) { + throw ("Shim build failed: {0}" -f $project.FullName) + } +} + +$resolvedShimOutputPath = (Resolve-Path $ShimOutputPath).Path +$previousReferencePath = $env:UNIFIED_ENGINEERING_REFERENCE_PATH +$targetDirectories = foreach ($entry in $Directory) { + foreach ($candidate in ($entry -split ',')) { + $trimmed = $candidate.Trim().Trim([char[]]@("'", '"')) + if (-not [string]::IsNullOrWhiteSpace($trimmed)) { + $trimmed + } + } +} + +try { + $env:UNIFIED_ENGINEERING_REFERENCE_PATH = $resolvedShimOutputPath + + if ($env:TF_BUILD) { + Write-Host ("##vso[task.setvariable variable=UNIFIED_ENGINEERING_REFERENCE_PATH]{0}" -f $resolvedShimOutputPath) + } + + Write-Host ("Using fake UE references from: {0}" -f $resolvedShimOutputPath) -ForegroundColor Yellow + + foreach ($targetDirectory in $targetDirectories) { + Write-Host ("Compiling with fake UE references: {0}" -f $targetDirectory) -ForegroundColor Yellow + & $buildScriptPath -Directory $targetDirectory -Configuration $Configuration + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + } + + exit 0 +} +finally { + if ([string]::IsNullOrWhiteSpace($previousReferencePath)) { + Remove-Item Env:UNIFIED_ENGINEERING_REFERENCE_PATH -ErrorAction SilentlyContinue + } + else { + $env:UNIFIED_ENGINEERING_REFERENCE_PATH = $previousReferencePath + } +} \ No newline at end of file diff --git a/shims/Aveva.ApplicationFramework.Presentation.csproj b/shims/Aveva.ApplicationFramework.Presentation.csproj new file mode 100644 index 0000000..9e8e054 --- /dev/null +++ b/shims/Aveva.ApplicationFramework.Presentation.csproj @@ -0,0 +1,6 @@ + + + Aveva.ApplicationFramework.Presentation + Aveva.ApplicationFramework.Presentation + + \ No newline at end of file diff --git a/shims/Aveva.ApplicationFramework.csproj b/shims/Aveva.ApplicationFramework.csproj new file mode 100644 index 0000000..a2e5b91 --- /dev/null +++ b/shims/Aveva.ApplicationFramework.csproj @@ -0,0 +1,6 @@ + + + Aveva.ApplicationFramework + Aveva.ApplicationFramework + + \ No newline at end of file diff --git a/shims/Aveva.Core.Database.Filters.csproj b/shims/Aveva.Core.Database.Filters.csproj new file mode 100644 index 0000000..dc9aa9a --- /dev/null +++ b/shims/Aveva.Core.Database.Filters.csproj @@ -0,0 +1,6 @@ + + + Aveva.Core.Database.Filters + Aveva.Core.Database.Filters + + \ No newline at end of file diff --git a/shims/Aveva.Core.Database.csproj b/shims/Aveva.Core.Database.csproj new file mode 100644 index 0000000..2e48bcf --- /dev/null +++ b/shims/Aveva.Core.Database.csproj @@ -0,0 +1,15 @@ + + + Aveva.Core.Database + Aveva.Core.Database + + + + + + + + + + + \ No newline at end of file diff --git a/shims/Aveva.Core.Geometry.csproj b/shims/Aveva.Core.Geometry.csproj new file mode 100644 index 0000000..5e9fd28 --- /dev/null +++ b/shims/Aveva.Core.Geometry.csproj @@ -0,0 +1,6 @@ + + + Aveva.Core.Geometry + Aveva.Core.Geometry + + \ No newline at end of file diff --git a/shims/Aveva.Core.Implementation.csproj b/shims/Aveva.Core.Implementation.csproj new file mode 100644 index 0000000..d4f4ed5 --- /dev/null +++ b/shims/Aveva.Core.Implementation.csproj @@ -0,0 +1,6 @@ + + + Aveva.Core.Implementation + Aveva.Core.Implementation + + \ No newline at end of file diff --git a/shims/Aveva.Core.Presentation.csproj b/shims/Aveva.Core.Presentation.csproj new file mode 100644 index 0000000..9033125 --- /dev/null +++ b/shims/Aveva.Core.Presentation.csproj @@ -0,0 +1,6 @@ + + + Aveva.Core.Presentation + Aveva.Core.Presentation + + \ No newline at end of file diff --git a/shims/Aveva.Core.Utilities.csproj b/shims/Aveva.Core.Utilities.csproj new file mode 100644 index 0000000..f3972c3 --- /dev/null +++ b/shims/Aveva.Core.Utilities.csproj @@ -0,0 +1,6 @@ + + + Aveva.Core.Utilities + Aveva.Core.Utilities + + \ No newline at end of file diff --git a/shims/Aveva.Core3D.Clasher.csproj b/shims/Aveva.Core3D.Clasher.csproj new file mode 100644 index 0000000..e32e24a --- /dev/null +++ b/shims/Aveva.Core3D.Clasher.csproj @@ -0,0 +1,6 @@ + + + Aveva.Core3D.Clasher + Aveva.Core3D.Clasher + + \ No newline at end of file diff --git a/shims/Directory.Build.props b/shims/Directory.Build.props new file mode 100644 index 0000000..45bc4f9 --- /dev/null +++ b/shims/Directory.Build.props @@ -0,0 +1,14 @@ + + + netstandard2.0 + latest + disable + disable + false + false + 1591 + $(MSBuildThisFileDirectory)artifacts\$(Configuration)\ + false + false + + \ No newline at end of file diff --git a/shims/StartUp.csproj b/shims/StartUp.csproj new file mode 100644 index 0000000..fc28b78 --- /dev/null +++ b/shims/StartUp.csproj @@ -0,0 +1,6 @@ + + + StartUp + StartUp + + \ No newline at end of file diff --git a/shims/src/ApplicationFrameworkPresentation.cs b/shims/src/ApplicationFrameworkPresentation.cs new file mode 100644 index 0000000..3beee45 --- /dev/null +++ b/shims/src/ApplicationFrameworkPresentation.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Aveva.ApplicationFramework.Presentation +{ + public class Command + { + public string Key { get; set; } + + public IList List { get; } = new List(); + + public object Value { get; set; } + + public virtual void Execute() + { + } + } +} \ No newline at end of file diff --git a/shims/src/Clasher.cs b/shims/src/Clasher.cs new file mode 100644 index 0000000..61101e0 --- /dev/null +++ b/shims/src/Clasher.cs @@ -0,0 +1,82 @@ +using System; +using Aveva.Core.Database; +using Aveva.Core.Geometry; + +namespace Aveva.Core3D.Clasher +{ + public enum SpatialMapStatus + { + Unknown, + Complete, + } + + public class SpatialMap + { + public static SpatialMap Instance { get; } = new SpatialMap(); + + public SpatialMapStatus CheckSpatialMap(Db db) + { + return SpatialMapStatus.Complete; + } + + public void BuildSpatialMap() + { + } + } + + public class ClashOptions + { + public bool IncludeTouches { get; set; } + + public double Clearance { get; set; } + + public static ClashOptions Create() + { + return new ClashOptions(); + } + } + + public class ObstructionList + { + public static ObstructionList Create() + { + return new ObstructionList(); + } + } + + public class ClashSet + { + public Clash[] Clashes { get; set; } = Array.Empty(); + + public static ClashSet Create() + { + return new ClashSet(); + } + } + + public class Clasher + { + public static Clasher Instance { get; } = new Clasher(); + + public bool CheckAll(ClashOptions options, ObstructionList obstructions, ClashSet clashSet) + { + return false; + } + } + + public class Clash + { + public DbElement First { get; set; } = new DbElement(); + + public DbElement Second { get; set; } = new DbElement(); + + public ClashType Type { get; set; } + + public Position ClashPosition { get; set; } = Position.Create(0d, 0d, 0d); + } + + public enum ClashType + { + None, + } +} \ No newline at end of file diff --git a/shims/src/Database.cs b/shims/src/Database.cs new file mode 100644 index 0000000..cf0147a --- /dev/null +++ b/shims/src/Database.cs @@ -0,0 +1,614 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Aveva.Core.Geometry; +using Aveva.Core.Utilities.Messaging; + +namespace Aveva.Core.Database +{ + public class DbAttribute + { + public DbAttribute() + { + } + + public DbAttribute(string name) + { + Name = name; + } + + public string Name { get; set; } + + public static DbAttribute GetDbAttribute(string name) + { + return new DbAttribute(name); + } + } + + public static class DbAttributeInstance + { + public static readonly DbAttribute NAME = new DbAttribute(nameof(NAME)); + public static readonly DbAttribute DESC = new DbAttribute(nameof(DESC)); + public static readonly DbAttribute BUIL = new DbAttribute(nameof(BUIL)); + public static readonly DbAttribute REV = new DbAttribute(nameof(REV)); + public static readonly DbAttribute PRES = new DbAttribute(nameof(PRES)); + public static readonly DbAttribute HREF = new DbAttribute(nameof(HREF)); + public static readonly DbAttribute LNTP = new DbAttribute(nameof(LNTP)); + public static readonly DbAttribute HDIR = new DbAttribute(nameof(HDIR)); + public static readonly DbAttribute POS = new DbAttribute(nameof(POS)); + public static readonly DbAttribute ORI = new DbAttribute(nameof(ORI)); + public static readonly DbAttribute FLNN = new DbAttribute(nameof(FLNN)); + public static readonly DbAttribute AREA = new DbAttribute(nameof(AREA)); + public static readonly DbAttribute CREF = new DbAttribute(nameof(CREF)); + public static readonly DbAttribute ISNAME = new DbAttribute(nameof(ISNAME)); + public static readonly DbAttribute MEMB = new DbAttribute(nameof(MEMB)); + public static readonly DbAttribute DIAM = new DbAttribute(nameof(DIAM)); + public static readonly DbAttribute LCLM = new DbAttribute(nameof(LCLM)); + public static readonly DbAttribute LCLMH = new DbAttribute(nameof(LCLMH)); + public static readonly DbAttribute XLEN = new DbAttribute(nameof(XLEN)); + public static readonly DbAttribute YLEN = new DbAttribute(nameof(YLEN)); + public static readonly DbAttribute ZLEN = new DbAttribute(nameof(ZLEN)); + } + + public class DbElementType + { + public DbElementType() + { + } + + public DbElementType(string name) + { + Name = name; + } + + public string Name { get; set; } + + public static DbElementType GetElementType(int hash) + { + return new DbElementType(hash.ToString()); + } + + public static DbElementType GetElementType(string name) + { + return new DbElementType(name); + } + + public DbAttribute[] SystemAttributes() + { + return Array.Empty(); + } + } + + public static class DbElementTypeInstance + { + public static readonly DbElementType SITE = new DbElementType(nameof(SITE)); + public static readonly DbElementType ZONE = new DbElementType(nameof(ZONE)); + public static readonly DbElementType EQUIPMENT = new DbElementType(nameof(EQUIPMENT)); + public static readonly DbElementType CYLINDER = new DbElementType(nameof(CYLINDER)); + public static readonly DbElementType BOX = new DbElementType(nameof(BOX)); + public static readonly DbElementType NOZZLE = new DbElementType(nameof(NOZZLE)); + public static readonly DbElementType PIPE = new DbElementType(nameof(PIPE)); + public static readonly DbElementType BRANCH = new DbElementType(nameof(BRANCH)); + public static readonly DbElementType GASKET = new DbElementType(nameof(GASKET)); + public static readonly DbElementType ELBOW = new DbElementType(nameof(ELBOW)); + public static readonly DbElementType TEE = new DbElementType(nameof(TEE)); + } + + public class DbElement + { + public static DbElement GetElement(string name) + { + return new DbElement + { + Name = name, + IsValid = true, + }; + } + + public string Name { get; set; } + + public bool IsValid { get; set; } = true; + + public DbElement Previous { get; set; } = new DbElement(false); + + public DbElement() + { + } + + private DbElement(bool valid) + { + IsValid = valid; + } + + public void Delete() + { + } + + public DbElement Create(int position, DbElementType elementType) + { + return new DbElement(); + } + + public DbElement CreateAfter(DbElementType elementType) + { + return new DbElement(); + } + + public DbElement CreateFirst(DbElementType elementType) + { + return new DbElement(); + } + + public DbElement CreateLast(DbElementType elementType) + { + return new DbElement(); + } + + public void SetAttribute(DbAttribute attribute, object value) + { + } + + public string GetString(DbAttribute attribute) + { + return string.Empty; + } + + public bool GetBool(DbAttribute attribute) + { + return false; + } + + public int GetInteger(DbAttribute attribute) + { + return 0; + } + + public double GetDouble(DbAttribute attribute) + { + return 0d; + } + + public DbElement GetElement(DbAttribute attribute) + { + return new DbElement(); + } + + public Direction GetDirection(DbAttribute attribute) + { + return new Direction(); + } + + public Position GetPosition(DbAttribute attribute) + { + return Position.Create(0d, 0d, 0d); + } + + public Orientation GetOrientation(DbAttribute attribute) + { + return new Orientation(); + } + + public string GetAsString(DbAttribute attribute) + { + return string.Empty; + } + + public void Copy(DbElement element) + { + } + + public void CopyHierarchy(DbElement element, DbCopyOption options) + { + } + + public DbElement FirstMember() + { + return new DbElement(); + } + + public DbElement FirstMember(DbElementType type) + { + return new DbElement(); + } + + public void InsertAfterLast(DbElement element) + { + } + + public void InsertAfter(DbElement element) + { + } + + public DbElement Next() + { + return new DbElement(); + } + + public DbElement Next(DbElementType type) + { + return new DbElement(); + } + + public DbElement[] Members() + { + return Array.Empty(); + } + + public DbElement[] Members(DbElementType type) + { + return Array.Empty(); + } + + public DbElement Member(int index) + { + return new DbElement(); + } + + public DbElement[] GetElementArray(DbAttribute attribute, DbElementType type) + { + return Array.Empty(); + } + + public double EvaluateDouble(DbExpression expression, DbAttributeUnit unit) + { + return 0d; + } + + public void SetRule(DbAttribute attribute, DbRule rule) + { + } + + public DbRule GetRule(DbAttribute attribute) + { + return new DbRule(); + } + + public void DeleteRule(DbAttribute attribute) + { + } + + public bool ExistRule(DbAttribute attribute) + { + return false; + } + + public bool VerifyRule(DbAttribute attribute) + { + return false; + } + + public void ExecuteRule(DbAttribute attribute) + { + } + + public void ExecuteAllRules() + { + } + + public void PropagateRules(DbAttribute attribute) + { + } + + public void Claim() + { + } + + public void Release() + { + } + + public void ClaimHierarchy() + { + } + + public void ReleaseHierarchy() + { + } + + public DbElementType GetElementType() + { + return new DbElementType(); + } + + public void ChangeType(DbElementType type) + { + } + + public bool AtDefault(DbAttribute attribute) + { + return true; + } + + public bool IsDescendant(DbElement element) + { + return false; + } + + public void SetAttributeDefault(DbAttribute attribute) + { + } + } + + public class DBElementCollection : IEnumerable + { + public DBElementCollection(DbElement root) + { + } + + public DBElementCollection(DbElement root, object filter) + { + } + + public bool IncludeRoot { get; set; } + + public object Filter { get; set; } + + public DBElementEnumerator GetEnumerator() + { + return new DBElementEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public class DBElementEnumerator : IEnumerator + { + public DbElement Current => new DbElement(); + + object IEnumerator.Current => Current; + + public bool MoveNext() + { + return false; + } + + public void Reset() + { + } + + public void Dispose() + { + } + } + + public class DbCopyOption + { + public string FromName { get; set; } + + public string ToName { get; set; } + + public bool Rename { get; set; } + } + + public class DbExpression + { + public static DbExpression Parse(string expression) + { + return new DbExpression(); + } + } + + public enum DbAttributeUnit + { + DIST, + } + + public enum DbRuleStatus + { + DYNAMIC, + } + + public enum DbExpressionType + { + REAL, + } + + public class DbRule + { + public static DbRule CreateDbRule(DbExpression expression, DbRuleStatus status, DbExpressionType expressionType) + { + return new DbRule(); + } + } + + public class Project + { + public static Project CurrentProject { get; } = new Project(); + + public string Name { get; set; } = string.Empty; + + public string UserName { get; set; } = string.Empty; + + public bool IsOpen() + { + return false; + } + + public void Open(string project, string username, string password) + { + Name = project; + UserName = username; + } + + public void Close() + { + } + } + + public class MDB + { + public static MDB CurrentMDB { get; } = new MDB(); + + public string Name { get; set; } = string.Empty; + + public void SaveWork(string description) + { + } + + public void CloseMDB() + { + } + + public void Open(MDBSetup setup, out PdmsMessage error) + { + error = new PdmsMessage(); + } + + public Db[] GetDBArray(DbType type) + { + return Array.Empty(); + } + + public DbElement GetFirstWorld(DbType type) + { + return new DbElement(); + } + + public void QuitWork() + { + } + } + + public class MDBSetup + { + public static MDBSetup CreateMDBSetup(string name, out PdmsMessage error) + { + error = new PdmsMessage(); + return new MDBSetup(); + } + } + + public class Db + { + public string Name { get; set; } = string.Empty; + } + + public enum DbType + { + Design, + } + + public class NameTable : IEnumerable + { + public static NameTable GetNameTable(Db db, DbAttribute attribute, string prefix, string suffix) + { + return new NameTable(); + } + + public IEnumerator GetEnumerator() + { + return ((IEnumerable)Array.Empty()).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public class ElementTreeNavigator + { + public ElementTreeNavigator(DbElement root, object filter) + { + } + + public DbElement FirstMemberInScan(DbElement element) + { + return new DbElement(); + } + + public DbElement NextInScan(DbElement element) + { + return new DbElement(); + } + + public DbElement Parent(DbElement element) + { + return new DbElement(); + } + + public DbElement[] MembersInScan(DbElement element) + { + return Array.Empty(); + } + } + + public class DbQualifier + { + } + + public static class DbPseudoAttribute + { + public delegate double GetDoubleDelegate(DbElement element, DbAttribute attribute, DbQualifier qualifier); + + public static void AddGetDoubleAttribute(DbAttribute attribute, DbElementType type, GetDoubleDelegate callback) + { + } + } + + public static class DbPostElementChange + { + public delegate void PostCreateDelegate(DbElement element); + + public delegate void PreDeleteDelegate(DbElement element); + + public delegate void PostMoveDelegate(DbElement element, DbElement oldOwner, int oldPosition); + + public delegate void PostAttributeChangeDelegate(DbElement element, DbAttribute attribute); + + public delegate void PostRefAttributeChangeDelegate(DbElement element, DbAttribute attribute, DbElement oldReference); + + public static void AddPostCreateElement(DbElementType type, PostCreateDelegate callback) + { + } + + public static void AddPreDeleteElement(DbElementType type, PreDeleteDelegate callback) + { + } + + public static void AddPostMoveElement(DbElementType type, PostMoveDelegate callback) + { + } + + public static void AddPostAttributeChange(DbAttribute attribute, PostAttributeChangeDelegate callback) + { + } + + public static void AddPostRefAttributeChange(DbAttribute attribute, PostRefAttributeChangeDelegate callback) + { + } + } + + public class Spatial + { + public static Spatial Instance { get; } = new Spatial(); + + public DbElement[] ElementsInBox(LimitsBox limitsBox, bool fullyWithin) + { + return Array.Empty(); + } + + public DbElement[] ElementsInElementBox(DbElement element, bool fullyWithin) + { + return Array.Empty(); + } + + public bool ElementInBox(DbElement element, LimitsBox limitsBox) + { + return false; + } + + public bool ElementInElementBox(DbElement first, DbElement second) + { + return false; + } + } +} \ No newline at end of file diff --git a/shims/src/Filters.cs b/shims/src/Filters.cs new file mode 100644 index 0000000..7186cd6 --- /dev/null +++ b/shims/src/Filters.cs @@ -0,0 +1,86 @@ +using Aveva.Core.Database; + +namespace Aveva.Core.Database.Filters +{ + public class TypeFilter + { + public TypeFilter() + { + } + + public TypeFilter(DbElementType type) + { + } + + public void Add(DbElementType type) + { + } + + public DbElement FirstMember(DbElement element) + { + return new DbElement(); + } + + public DbElement Next(DbElement element) + { + return new DbElement(); + } + + public DbElement[] Members(DbElement element) + { + return System.Array.Empty(); + } + + public DbElement Parent(DbElement element) + { + return new DbElement(); + } + } + + public class CompoundFilter + { + public void AddShow(object filter) + { + } + } + + public class AndFilter : CompoundFilter + { + public void Add(object filter) + { + } + + public bool Valid(DbElement element) + { + return true; + } + } + + public class TrueFilter + { + } + + public class AttributeTrueFilter + { + public AttributeTrueFilter(DbAttribute attribute) + { + } + + public bool Valid(DbElement element) + { + return true; + } + } + + public class BelowOrAtType + { + public BelowOrAtType(DbElementType type) + { + } + + public bool ScanBelow(DbElement element) + { + return true; + } + } +} \ No newline at end of file diff --git a/shims/src/Geometry.cs b/shims/src/Geometry.cs new file mode 100644 index 0000000..6d241e6 --- /dev/null +++ b/shims/src/Geometry.cs @@ -0,0 +1,45 @@ +namespace Aveva.Core.Geometry +{ + public class Direction + { + public static Direction Create(double x, double y, double z) + { + return new Direction(); + } + } + + public class Position + { + public double X { get; set; } + + public double Y { get; set; } + + public double Z { get; set; } + + public static Position Create(double x, double y, double z) + { + return new Position + { + X = x, + Y = y, + Z = z, + }; + } + } + + public class Orientation + { + public static Orientation Create(Direction xAxis, Direction yAxis) + { + return new Orientation(); + } + } + + public class LimitsBox + { + public static LimitsBox Create(Position first, Position second) + { + return new LimitsBox(); + } + } +} \ No newline at end of file diff --git a/shims/src/Presentation.cs b/shims/src/Presentation.cs new file mode 100644 index 0000000..678c97f --- /dev/null +++ b/shims/src/Presentation.cs @@ -0,0 +1,69 @@ +using System; +using Aveva.Core.Database; + +namespace Aveva.Core.Presentation +{ + public delegate void CurrentElementChangedEventHandler(object sender, CurrentElementChangedEventArgs e); + + public class CurrentElementChangedEventArgs : EventArgs + { + public DbElement Element { get; set; } = new DbElement(); + } + + public static class CurrentElement + { + private static DbElement s_element = new DbElement(); + + public static event CurrentElementChangedEventHandler CurrentElementChanged; + + public static DbElement Element + { + get => s_element; + set + { + s_element = value; + CurrentElementChanged?.Invoke(null, new CurrentElementChangedEventArgs { Element = value }); + } + } + } + + public delegate void DbChangesEventHandler(object sender, DbChangesEventArgs e); + + public class DbChangesEventArgs : EventArgs + { + public ChangeList ChangeList { get; } = new ChangeList(); + } + + public class ChangeList + { + public DbElement[] GetCreations() + { + return Array.Empty(); + } + + public DbElement[] GetDeletions() + { + return Array.Empty(); + } + + public DbElement[] GetMemberChanges() + { + return Array.Empty(); + } + + public DbElement[] GetModified() + { + return Array.Empty(); + } + } + + public static class DatabaseService + { + public static event DbChangesEventHandler Changes; + + public static void RaiseChanges() + { + Changes?.Invoke(null, new DbChangesEventArgs()); + } + } +} \ No newline at end of file diff --git a/shims/src/Utilities.cs b/shims/src/Utilities.cs new file mode 100644 index 0000000..f22c493 --- /dev/null +++ b/shims/src/Utilities.cs @@ -0,0 +1,88 @@ +using System; + +namespace Aveva.Core.Utilities.Messaging +{ + public class PdmsMessage + { + } + + public class PdmsException : Exception + { + public PdmsException() + { + } + + public PdmsException(string message) : base(message) + { + } + } +} + +namespace Aveva.Core.Utilities.Undo +{ + public class UndoTransaction + { + public static UndoTransaction GetUndoTransaction() + { + return new UndoTransaction(); + } + + public void StartTransaction(string description) + { + } + + public void EndTransaction() + { + } + + public static void PerformUndo() + { + } + + public static void PerformRedo() + { + } + } + + public abstract class UndoSubscriber + { + public virtual object GetBeginState() + { + return null; + } + + public virtual object GetEndState() + { + return null; + } + + public virtual void RestoreBeginState(object state) + { + } + + public virtual void RestoreEndState(object state) + { + } + } + + public static class UndoCaretaker + { + public static void RegisterUndoSubscriber(UndoSubscriber subscriber) + { + } + + public static void RemoveUndoSubscriber(UndoSubscriber subscriber) + { + } + } +} + +namespace Aveva.Core.Utilities.CommandLine +{ + public static class Command + { + public static void Update() + { + } + } +} \ No newline at end of file From bb191d74e23c4b1e7de36d62dec47be2919c88a8 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Sun, 29 Mar 2026 23:15:07 +0100 Subject: [PATCH 008/102] Expand fake compile shims for templates --- azure-pipelines.yml | 6 +- scripts/Build-AddIns.ps1 | 19 +- scripts/Invoke-FakeUECompile.ps1 | 2 +- ...ApplicationFramework.Implementation.csproj | 6 + shims/Aveva.Core.Database.csproj | 8 +- ...Aveva.Core.FlexibleExplorer.Control.csproj | 6 + shims/Aveva.Core.FlexibleExplorer.Core.csproj | 6 + shims/Directory.Build.props | 3 +- shims/GridControl.csproj | 6 + shims/Microsoft.Web.WebView2.Core.csproj | 6 + shims/Microsoft.Web.WebView2.WinForms.csproj | 6 + shims/Newtonsoft.Json.csproj | 6 + shims/PMLNet.csproj | 6 + shims/src/ApplicationFrameworkPresentation.cs | 282 ++++++++++++++++++ shims/src/Database.cs | 6 + shims/src/FlexibleExplorer.cs | 73 +++++ shims/src/Json.cs | 80 +++++ shims/src/PmlNet.cs | 15 + shims/src/Presentation.cs | 72 +++++ shims/src/Utilities.cs | 10 + shims/src/WebView2.cs | 65 ++++ .../FlexibleExplorerAddInTemplate.cs | 1 + .../MyFlexibleExplorerControl.cs | 6 +- 23 files changed, 679 insertions(+), 17 deletions(-) create mode 100644 shims/Aveva.ApplicationFramework.Implementation.csproj create mode 100644 shims/Aveva.Core.FlexibleExplorer.Control.csproj create mode 100644 shims/Aveva.Core.FlexibleExplorer.Core.csproj create mode 100644 shims/GridControl.csproj create mode 100644 shims/Microsoft.Web.WebView2.Core.csproj create mode 100644 shims/Microsoft.Web.WebView2.WinForms.csproj create mode 100644 shims/Newtonsoft.Json.csproj create mode 100644 shims/PMLNet.csproj create mode 100644 shims/src/FlexibleExplorer.cs create mode 100644 shims/src/Json.cs create mode 100644 shims/src/PmlNet.cs create mode 100644 shims/src/WebView2.cs diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 13db6b8..4bb3922 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -123,8 +123,8 @@ stages: fetchDepth: "1" - pwsh: | - ./scripts/Invoke-FakeUECompile.ps1 -Directory Examples -Configuration Release - displayName: Build example projects with fake UE references + ./scripts/Invoke-FakeUECompile.ps1 -Directory templates,Examples -Configuration Release + displayName: Build template and example projects with fake UE references - pwsh: | $pwshPath = (Get-Command pwsh -ErrorAction Stop).Source @@ -143,7 +143,7 @@ stages: ) $buildCommands = @( - " - shell: ['$pwshPath', '-NoProfile', '-File', '$fakeCompileScriptPath', '-Directory', 'Examples', '-Configuration', 'Release']" + " - shell: ['$pwshPath', '-NoProfile', '-File', '$fakeCompileScriptPath', '-Directory', 'templates,Examples', '-Configuration', 'Release']" ) $polarisContent = @( diff --git a/scripts/Build-AddIns.ps1 b/scripts/Build-AddIns.ps1 index 932ddde..4bc8019 100644 --- a/scripts/Build-AddIns.ps1 +++ b/scripts/Build-AddIns.ps1 @@ -1,7 +1,8 @@ param( [Parameter(Mandatory = $false)] [string]$Directory = "source", - [string]$Configuration = "Debug" + [string]$Configuration = "Debug", + [switch]$SkipPostBuildEvent ) $ErrorActionPreference = "Stop" @@ -33,7 +34,21 @@ if (-not $projects) { $failed = @() foreach ($proj in $projects) { Write-Host "--- Building $($proj.FullName) ---" -ForegroundColor Cyan - dotnet build $proj.FullName -c $Configuration -clp:Summary -nologo + $buildArgs = @( + 'build' + $proj.FullName + '-c' + $Configuration + '-clp:Summary' + '-nologo' + ) + + if ($SkipPostBuildEvent) { + $buildArgs += '-p:RunPostBuildEvent=Never' + $buildArgs += '-p:PostBuildEvent=' + } + + dotnet @buildArgs if ($LASTEXITCODE -ne 0) { $failed += $proj.FullName } diff --git a/scripts/Invoke-FakeUECompile.ps1 b/scripts/Invoke-FakeUECompile.ps1 index 9f3e94f..9cf4aee 100644 --- a/scripts/Invoke-FakeUECompile.ps1 +++ b/scripts/Invoke-FakeUECompile.ps1 @@ -58,7 +58,7 @@ try { foreach ($targetDirectory in $targetDirectories) { Write-Host ("Compiling with fake UE references: {0}" -f $targetDirectory) -ForegroundColor Yellow - & $buildScriptPath -Directory $targetDirectory -Configuration $Configuration + & $buildScriptPath -Directory $targetDirectory -Configuration $Configuration -SkipPostBuildEvent if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/shims/Aveva.ApplicationFramework.Implementation.csproj b/shims/Aveva.ApplicationFramework.Implementation.csproj new file mode 100644 index 0000000..2ea7f56 --- /dev/null +++ b/shims/Aveva.ApplicationFramework.Implementation.csproj @@ -0,0 +1,6 @@ + + + Aveva.ApplicationFramework.Implementation + Aveva.ApplicationFramework.Implementation + + \ No newline at end of file diff --git a/shims/Aveva.Core.Database.csproj b/shims/Aveva.Core.Database.csproj index 2e48bcf..925e3b9 100644 --- a/shims/Aveva.Core.Database.csproj +++ b/shims/Aveva.Core.Database.csproj @@ -4,12 +4,6 @@ Aveva.Core.Database - - - - - - - + \ No newline at end of file diff --git a/shims/Aveva.Core.FlexibleExplorer.Control.csproj b/shims/Aveva.Core.FlexibleExplorer.Control.csproj new file mode 100644 index 0000000..df56982 --- /dev/null +++ b/shims/Aveva.Core.FlexibleExplorer.Control.csproj @@ -0,0 +1,6 @@ + + + Aveva.Core.FlexibleExplorer.Control + Aveva.Core.FlexibleExplorer.Control + + \ No newline at end of file diff --git a/shims/Aveva.Core.FlexibleExplorer.Core.csproj b/shims/Aveva.Core.FlexibleExplorer.Core.csproj new file mode 100644 index 0000000..4aaf960 --- /dev/null +++ b/shims/Aveva.Core.FlexibleExplorer.Core.csproj @@ -0,0 +1,6 @@ + + + Aveva.Core.FlexibleExplorer.Core + Aveva.Core.FlexibleExplorer.Core + + \ No newline at end of file diff --git a/shims/Directory.Build.props b/shims/Directory.Build.props index 45bc4f9..8adb7f3 100644 --- a/shims/Directory.Build.props +++ b/shims/Directory.Build.props @@ -1,9 +1,10 @@ - netstandard2.0 + net48 latest disable disable + true false false 1591 diff --git a/shims/GridControl.csproj b/shims/GridControl.csproj new file mode 100644 index 0000000..414d294 --- /dev/null +++ b/shims/GridControl.csproj @@ -0,0 +1,6 @@ + + + GridControl + GridControl + + \ No newline at end of file diff --git a/shims/Microsoft.Web.WebView2.Core.csproj b/shims/Microsoft.Web.WebView2.Core.csproj new file mode 100644 index 0000000..4fc1c1b --- /dev/null +++ b/shims/Microsoft.Web.WebView2.Core.csproj @@ -0,0 +1,6 @@ + + + Microsoft.Web.WebView2.Core + Microsoft.Web.WebView2.Core + + \ No newline at end of file diff --git a/shims/Microsoft.Web.WebView2.WinForms.csproj b/shims/Microsoft.Web.WebView2.WinForms.csproj new file mode 100644 index 0000000..d422918 --- /dev/null +++ b/shims/Microsoft.Web.WebView2.WinForms.csproj @@ -0,0 +1,6 @@ + + + Microsoft.Web.WebView2.WinForms + Microsoft.Web.WebView2.WinForms + + \ No newline at end of file diff --git a/shims/Newtonsoft.Json.csproj b/shims/Newtonsoft.Json.csproj new file mode 100644 index 0000000..ccdbc9f --- /dev/null +++ b/shims/Newtonsoft.Json.csproj @@ -0,0 +1,6 @@ + + + Newtonsoft.Json + Newtonsoft.Json + + \ No newline at end of file diff --git a/shims/PMLNet.csproj b/shims/PMLNet.csproj new file mode 100644 index 0000000..cfef247 --- /dev/null +++ b/shims/PMLNet.csproj @@ -0,0 +1,6 @@ + + + PMLNet + PMLNet + + \ No newline at end of file diff --git a/shims/src/ApplicationFrameworkPresentation.cs b/shims/src/ApplicationFrameworkPresentation.cs index 3beee45..cf16152 100644 --- a/shims/src/ApplicationFrameworkPresentation.cs +++ b/shims/src/ApplicationFrameworkPresentation.cs @@ -1,4 +1,70 @@ +using System; using System.Collections.Generic; +using System.ComponentModel; +using System.Windows.Forms; + +namespace Aveva.ApplicationFramework +{ + public interface IAddinInjected + { + string Name { get; } + + string Description { get; } + + void Start(IDependencyResolver resolver); + + void Start(ServiceManager serviceManager); + + void Stop(); + } + + public interface IDependencyResolver + { + } + + public sealed class ServiceManager + { + } + + public static class DependencyResolver + { + private static readonly Aveva.ApplicationFramework.Presentation.WindowManager s_windowManager = new Aveva.ApplicationFramework.Presentation.WindowManager(); + private static readonly Aveva.ApplicationFramework.Presentation.CommandManager s_commandManager = new Aveva.ApplicationFramework.Presentation.CommandManager(); + private static readonly Dictionary s_services = new Dictionary + { + { typeof(Aveva.ApplicationFramework.Presentation.IWindowManager), s_windowManager }, + { typeof(Aveva.ApplicationFramework.Presentation.ICommandManager), s_commandManager }, + { typeof(Aveva.ApplicationFramework.Presentation.ICommandBarManager), s_windowManager.CommandBarManager }, + }; + + public static T GetImplementationOf() where T : class + { + if (s_services.TryGetValue(typeof(T), out object service)) + { + return service as T; + } + + return null; + } + } + + public static class Log + { + private static readonly LoggerProxy s_logger = new LoggerProxy(); + + public static LoggerProxy Logger() + { + return s_logger; + } + } + + public sealed class LoggerProxy + { + public void MessageFormat(string format, params object[] args) + { + } + } +} namespace Aveva.ApplicationFramework.Presentation { @@ -10,8 +76,224 @@ public class Command public object Value { get; set; } + public bool Checked { get; set; } + public virtual void Execute() { } } + + public enum DockedPosition + { + Left, + Right, + Top, + Bottom, + } + + public class DockedWindow + { + public event CancelEventHandler Closing; + + public event EventHandler Closed; + + public string Key { get; set; } + + public string Title { get; set; } + + public Control Content { get; set; } + + public DockedPosition Position { get; set; } + + public int Width { get; set; } + + public bool SaveLayout { get; set; } + + public bool Visible { get; private set; } + + public void Show() + { + Visible = true; + } + + public void Hide() + { + Visible = false; + } + + public void Close() + { + CancelEventArgs args = new CancelEventArgs(); + Closing?.Invoke(this, args); + if (args.Cancel) + { + return; + } + + Visible = false; + Closed?.Invoke(this, EventArgs.Empty); + } + } + + public interface IWindowManager + { + event EventHandler WindowLayoutLoaded; + + ICommandBarManager CommandBarManager { get; } + + DockedWindow CreateDockedWindow(string key, string title, Control control, DockedPosition position); + } + + public interface ICommandManager + { + CommandCollection Commands { get; } + } + + public interface ICommandBarManager + { + event EventHandler UILoaded; + + ToolCollection RootTools { get; } + } + + public interface ITool + { + string Key { get; set; } + + bool Checked { get; set; } + + event EventHandler ToolClick; + } + + public sealed class CommandCollection : List + { + } + + public class CommandManager : ICommandManager + { + public CommandCollection Commands { get; } = new CommandCollection(); + } + + public class WindowManager : IWindowManager + { + public event EventHandler WindowLayoutLoaded; + + public ICommandBarManager CommandBarManager { get; } = new CommandBarManager(); + + public DockedWindow CreateDockedWindow(string key, string title, Control control, DockedPosition position) + { + WindowLayoutLoaded?.Invoke(this, EventArgs.Empty); + return new DockedWindow + { + Key = key, + Title = title, + Content = control, + Position = position, + }; + } + } + + public class CommandBarManager : ICommandBarManager + { + public event EventHandler UILoaded; + + public ToolCollection RootTools { get; } = new ToolCollection(); + + public void RaiseUiLoaded() + { + UILoaded?.Invoke(this, EventArgs.Empty); + } + } + + public class ToolEventArgs : EventArgs + { + public string Key { get; set; } + } + + public delegate void ToolEventHandler(object sender, ToolEventArgs args); + + public class ToolBase : ITool + { + public string Key { get; set; } + + public bool Checked { get; set; } + + public event EventHandler ToolClick; + + protected void RaiseToolClick() + { + ToolClick?.Invoke(this, EventArgs.Empty); + } + } + + public class ButtonTool : ToolBase + { + } + + public class StateButtonTool : ToolBase + { + } + + public class MenuTool : ToolBase + { + public bool IsContextMenu { get; set; } + + public ToolCollection Tools { get; } = new ToolCollection(); + } + + public class ToolCollection + { + private readonly Dictionary _items = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public event ToolEventHandler ToolAdded; + + public ITool this[string key] => _items.TryGetValue(key, out ITool tool) ? tool : null; + + public bool Contains(string key) + { + return _items.ContainsKey(key); + } + + public MenuTool AddMenuTool(string key, string caption, object image, string category) + { + MenuTool tool = new MenuTool { Key = key }; + _items[key] = tool; + ToolAdded?.Invoke(this, new ToolEventArgs { Key = key }); + return tool; + } + + public ButtonTool AddButtonTool(string key, string caption, object image, string category) + { + ButtonTool tool = new ButtonTool { Key = key }; + _items[key] = tool; + ToolAdded?.Invoke(this, new ToolEventArgs { Key = key }); + return tool; + } + + public ITool AddTool(string key) + { + if (_items.TryGetValue(key, out ITool existing)) + { + return existing; + } + + ButtonTool tool = new ButtonTool { Key = key }; + _items[key] = tool; + ToolAdded?.Invoke(this, new ToolEventArgs { Key = key }); + return tool; + } + } + + public static class MessageBoxEx + { + public static DialogResult Show(string text) + { + return DialogResult.OK; + } + + public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) + { + return DialogResult.OK; + } + } } \ No newline at end of file diff --git a/shims/src/Database.cs b/shims/src/Database.cs index cf0147a..1f48955 100644 --- a/shims/src/Database.cs +++ b/shims/src/Database.cs @@ -38,6 +38,7 @@ public static class DbAttributeInstance public static readonly DbAttribute POS = new DbAttribute(nameof(POS)); public static readonly DbAttribute ORI = new DbAttribute(nameof(ORI)); public static readonly DbAttribute FLNN = new DbAttribute(nameof(FLNN)); + public static readonly DbAttribute FLNM = new DbAttribute(nameof(FLNM)); public static readonly DbAttribute AREA = new DbAttribute(nameof(AREA)); public static readonly DbAttribute CREF = new DbAttribute(nameof(CREF)); public static readonly DbAttribute ISNAME = new DbAttribute(nameof(ISNAME)); @@ -193,6 +194,11 @@ public string GetAsString(DbAttribute attribute) return string.Empty; } + public DbAttribute[] GetAttributes() + { + return Array.Empty(); + } + public void Copy(DbElement element) { } diff --git a/shims/src/FlexibleExplorer.cs b/shims/src/FlexibleExplorer.cs new file mode 100644 index 0000000..b8ea1ca --- /dev/null +++ b/shims/src/FlexibleExplorer.cs @@ -0,0 +1,73 @@ +using System.IO; +using System.Windows.Forms; +using Aveva.ApplicationFramework; +using Aveva.Core.Presentation; + +namespace Aveva.Core.FlexibleExplorer +{ + public enum RefreshOperation + { + None, + NodesOnly, + NodesWithCollections, + WholeTree, + } + + public class RefreshArguments + { + public DbChangesEventArgs DbChangesEventArgs { get; set; } + + public object DbRawChanges { get; set; } + + public RefreshOperation RefreshType { get; set; } + } + + public interface IFlexibleExplorerRefreshManager + { + RefreshArguments GetRefreshType(RefreshArguments refreshArguments); + } + + public class XmlDefinitionProvider + { + public XmlDefinitionProvider(string xmlContent, string fileName) + { + } + + public object RootDefinitions { get; } = new object[0]; + + public static string GetLocalFile(string path) + { + return File.Exists(path) ? path : null; + } + } +} + +namespace Aveva.Core.FlexibleExplorer +{ + public class FlexibleExplorer : Control + { + public FlexibleExplorer(IDependencyResolver dependencyResolver, object viewContext, bool useDefaultImages) + { + } + + public BorderStyle BorderStyle { get; set; } + + public bool DisplayCheckBox { get; set; } + + public bool DisplayLockIndicator { get; set; } + + public bool DisplayClaimIndicator { get; set; } + + public bool DisplayReadOnlyIndicator { get; set; } + + public Aveva.Core.FlexibleExplorer.IFlexibleExplorerRefreshManager CustomRefreshManager { get; set; } + + public void MultiSelect(bool enabled) + { + } + + public void CreateRoots(object rootDefinitions, bool preserveExisting) + { + } + } +} \ No newline at end of file diff --git a/shims/src/Json.cs b/shims/src/Json.cs new file mode 100644 index 0000000..853fb74 --- /dev/null +++ b/shims/src/Json.cs @@ -0,0 +1,80 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Linq +{ + public enum JTokenType + { + Object, + Array, + String, + Integer, + Float, + Boolean, + Null, + } + + public abstract class JToken + { + public abstract JTokenType Type { get; } + + public static JToken Parse(string json) + { + return new JObject(); + } + + public virtual T Value() + { + return default(T); + } + + public override string ToString() + { + return string.Empty; + } + } + + public sealed class JObject : JToken + { + public override JTokenType Type => JTokenType.Object; + + public IEnumerable Properties() + { + return new JProperty[0]; + } + } + + public sealed class JArray : JToken, IEnumerable + { + public override JTokenType Type => JTokenType.Array; + + public int Count => 0; + + public IEnumerator GetEnumerator() + { + return ((IEnumerable)new JToken[0]).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public sealed class JProperty + { + public string Name { get; set; } = string.Empty; + + public JToken Value { get; set; } = new JValue(); + } + + public sealed class JValue : JToken + { + public override JTokenType Type => JTokenType.String; + + public override T Value() + { + return default(T); + } + } +} \ No newline at end of file diff --git a/shims/src/PmlNet.cs b/shims/src/PmlNet.cs new file mode 100644 index 0000000..3606942 --- /dev/null +++ b/shims/src/PmlNet.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections; + +namespace Aveva.Core.PMLNet +{ + [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] + public sealed class PMLNetCallableAttribute : Attribute + { + } + + public static class PMLNetDelegate + { + public delegate void PMLNetEventHandler(ArrayList args); + } +} \ No newline at end of file diff --git a/shims/src/Presentation.cs b/shims/src/Presentation.cs index 678c97f..5116337 100644 --- a/shims/src/Presentation.cs +++ b/shims/src/Presentation.cs @@ -1,5 +1,9 @@ using System; +using System.Collections; +using System.Windows.Forms; +using Aveva.ApplicationFramework.Presentation; using Aveva.Core.Database; +using Aveva.Core.PMLNet; namespace Aveva.Core.Presentation { @@ -66,4 +70,72 @@ public static void RaiseChanges() Changes?.Invoke(null, new DbChangesEventArgs()); } } + + public class NetDataSource + { + public NetDataSource(string tableName, Hashtable attributes, Hashtable titles, Hashtable items) + { + } + } + + public class NetGridControl : Control + { + public bool allGridEvents { get; set; } + + public bool ColumnExcelFilter { get; set; } + + public bool ColumnSummaries { get; set; } + + public bool EditableGrid { get; set; } + + public bool ErrorIcon { get; set; } + + public bool ExtendLastColumn { get; set; } + + public bool FixedHeaders { get; set; } + + public bool FixedRows { get; set; } + + public int GridHeight { get; set; } + + public bool HeaderSort { get; set; } + + public bool HideGroupByBox { get; set; } + + public bool OutlookGroupStyle { get; set; } + + public bool RowAddDeleteGrid { get; set; } + + public bool ScrollSelectedRowToView { get; set; } + + public bool SingleRowSelection { get; set; } + + public bool Updates { get; set; } + + public MenuTool PopupMenu { get; set; } + + public MenuTool PopupMenuHeader { get; set; } + + public event PMLNetDelegate.PMLNetEventHandler AfterSelectChange; + + public void BindToDataSource(NetDataSource dataSource) + { + } + + public void setNameColumnImage() + { + } + + public void saveGridToExcel(string path) + { + } + + public void printPreview() + { + } + + public void setRowColor(double row, string colour) + { + } + } } \ No newline at end of file diff --git a/shims/src/Utilities.cs b/shims/src/Utilities.cs index f22c493..4671bda 100644 --- a/shims/src/Utilities.cs +++ b/shims/src/Utilities.cs @@ -6,8 +6,18 @@ public class PdmsMessage { } + public class PdmsError + { + public string MessageText() + { + return string.Empty; + } + } + public class PdmsException : Exception { + public PdmsError Error { get; } = new PdmsError(); + public PdmsException() { } diff --git a/shims/src/WebView2.cs b/shims/src/WebView2.cs new file mode 100644 index 0000000..f946c15 --- /dev/null +++ b/shims/src/WebView2.cs @@ -0,0 +1,65 @@ +using System; +using System.ComponentModel; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Microsoft.Web.WebView2.Core +{ + public class CoreWebView2Settings + { + public bool AreDefaultContextMenusEnabled { get; set; } + + public bool IsStatusBarEnabled { get; set; } + + public bool IsZoomControlEnabled { get; set; } + + public bool AreDevToolsEnabled { get; set; } + } + + public class CoreWebView2NavigationStartingEventArgs : EventArgs + { + } + + public class CoreWebView2NavigationCompletedEventArgs : EventArgs + { + } + + public class CoreWebView2 + { + public CoreWebView2Settings Settings { get; } = new CoreWebView2Settings(); + + public event EventHandler NavigationStarting; + + public event EventHandler NavigationCompleted; + } +} + +namespace Microsoft.Web.WebView2.WinForms +{ + public class WebView2 : Control, ISupportInitialize + { + public Microsoft.Web.WebView2.Core.CoreWebView2 CoreWebView2 { get; private set; } = new Microsoft.Web.WebView2.Core.CoreWebView2(); + + public double ZoomFactor { get; set; } + + public Uri Source { get; set; } + + public Task EnsureCoreWebView2Async(object environment) + { + if (CoreWebView2 == null) + { + CoreWebView2 = new Microsoft.Web.WebView2.Core.CoreWebView2(); + } + + return Task.CompletedTask; + } + + public void BeginInit() + { + } + + public void EndInit() + { + } + } +} \ No newline at end of file diff --git a/templates/FlexibleExplorerAddInTemplate/FlexibleExplorerAddInTemplate.cs b/templates/FlexibleExplorerAddInTemplate/FlexibleExplorerAddInTemplate.cs index a4f350a..b100b17 100644 --- a/templates/FlexibleExplorerAddInTemplate/FlexibleExplorerAddInTemplate.cs +++ b/templates/FlexibleExplorerAddInTemplate/FlexibleExplorerAddInTemplate.cs @@ -2,6 +2,7 @@ using Aveva.ApplicationFramework; using Aveva.ApplicationFramework.Presentation; using Aveva.Core.Database; +using Aveva.Core.Presentation; namespace Aveva.Core.Samples { diff --git a/templates/FlexibleExplorerAddInTemplate/MyFlexibleExplorerControl.cs b/templates/FlexibleExplorerAddInTemplate/MyFlexibleExplorerControl.cs index 6124ce1..a8b8733 100644 --- a/templates/FlexibleExplorerAddInTemplate/MyFlexibleExplorerControl.cs +++ b/templates/FlexibleExplorerAddInTemplate/MyFlexibleExplorerControl.cs @@ -17,7 +17,7 @@ public partial class MyFlexibleExplorerControl : UserControl /// /// The flexible explorer. /// - private FlexibleExplorer.FlexibleExplorer _flexibleExplorer; + private global::Aveva.Core.FlexibleExplorer.FlexibleExplorer _flexibleExplorer; /// /// The dependency resolver. @@ -134,7 +134,7 @@ private string ValidatePath(string filePath, string baseDirectory) /// /// A FlexibleExplorer. /// - private FlexibleExplorer.FlexibleExplorer InitialiseFlexibleExplorer(string xmlFileName) + private global::Aveva.Core.FlexibleExplorer.FlexibleExplorer InitialiseFlexibleExplorer(string xmlFileName) { if (_flexibleExplorer != null) { @@ -142,7 +142,7 @@ private FlexibleExplorer.FlexibleExplorer InitialiseFlexibleExplorer(string xmlF } else { - _flexibleExplorer = new FlexibleExplorer.FlexibleExplorer(_dependencyResolver, null, true); + _flexibleExplorer = new global::Aveva.Core.FlexibleExplorer.FlexibleExplorer(_dependencyResolver, null, true); _flexibleExplorer.Dock = DockStyle.Fill; _flexibleExplorer.BorderStyle = BorderStyle.FixedSingle; From 1a416ac7aaf76a8f1476cfd25fdc6d937f383da4 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Sun, 29 Mar 2026 23:17:25 +0100 Subject: [PATCH 009/102] Make security validation queueable --- azure-pipelines.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4bb3922..5a21234 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,12 +19,15 @@ resources: name: Technology/Templates ref: refs/heads/main +parameters: + - name: EnableSecurityValidation + type: string + default: 'false' + variables: - template: variables.yml@Templates - name: HostedVmImage value: windows-latest - - name: EnableSecurityValidation - value: 'false' # - name: SecurityPoolName # value: Technology UE Validation - name: CoverityServiceConnection @@ -100,7 +103,7 @@ stages: - stage: SecurityValidation displayName: Security Validation dependsOn: HostedTests - condition: and(succeeded(), eq(variables['EnableSecurityValidation'], 'true')) + condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: - job: CoverityAndBlackDuck displayName: Coverity And Black Duck From 63638d281beb9ff55e62d3e71a0f20640e0a23c5 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Mon, 30 Mar 2026 00:09:51 +0100 Subject: [PATCH 010/102] Fix fake compile path for multi-checkout --- azure-pipelines.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5a21234..4059945 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -128,10 +128,12 @@ stages: - pwsh: | ./scripts/Invoke-FakeUECompile.ps1 -Directory templates,Examples -Configuration Release displayName: Build template and example projects with fake UE references + workingDirectory: $(Build.Repository.LocalPath) - pwsh: | $pwshPath = (Get-Command pwsh -ErrorAction Stop).Source - $fakeCompileScriptPath = Join-Path $env:BUILD_SOURCESDIRECTORY 'scripts\Invoke-FakeUECompile.ps1' + $repoRoot = $env:BUILD_REPOSITORY_LOCALPATH + $fakeCompileScriptPath = Join-Path $repoRoot 'scripts\Invoke-FakeUECompile.ps1' $branchName = "$env:BUILD_SOURCEBRANCH" -replace '^refs/heads/', '' $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') @@ -139,7 +141,7 @@ stages: ? "@$($templatesBranch)" ` : '' $polarisBranch = "$branchName$templatesPostfix" - $commitDate = ([DateTime](git -C $env:BUILD_SOURCESDIRECTORY log -1 --format=%ci)).ToString('o') + $commitDate = ([DateTime](git -C $repoRoot log -1 --format=%ci)).ToString('o') $cleanCommands = @( " - shell: ['$pwshPath', '-NoProfile', '-Command', 'Write-Host fake-clean-step']" @@ -154,7 +156,7 @@ stages: 'project:' " name: $env:BUILD_REPOSITORY_NAME" " branch: $polarisBranch" - " projectDir: $env:BUILD_SOURCESDIRECTORY" + " projectDir: $repoRoot" ' revision:' " name: $env:BUILD_SOURCEVERSION" " date: $commitDate" From 32079ab786151ac83239d3e007067cc9f140833e Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Mon, 30 Mar 2026 00:13:30 +0100 Subject: [PATCH 011/102] Use explicit self checkout path in security job --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4059945..c61450d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -126,13 +126,13 @@ stages: fetchDepth: "1" - pwsh: | - ./scripts/Invoke-FakeUECompile.ps1 -Directory templates,Examples -Configuration Release + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's\UnifiedEngineeringCustomizationFramework' + & (Join-Path $repoRoot 'scripts\Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release displayName: Build template and example projects with fake UE references - workingDirectory: $(Build.Repository.LocalPath) - pwsh: | $pwshPath = (Get-Command pwsh -ErrorAction Stop).Source - $repoRoot = $env:BUILD_REPOSITORY_LOCALPATH + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's\UnifiedEngineeringCustomizationFramework' $fakeCompileScriptPath = Join-Path $repoRoot 'scripts\Invoke-FakeUECompile.ps1' $branchName = "$env:BUILD_SOURCEBRANCH" -replace '^refs/heads/', '' From edded37701b46e8a88c411afa2f9794620db6933 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Mon, 30 Mar 2026 00:29:25 +0100 Subject: [PATCH 012/102] Force rebuild during Coverity capture --- azure-pipelines.yml | 8 ++++---- scripts/Build-AddIns.ps1 | 7 ++++++- scripts/Invoke-FakeUECompile.ps1 | 19 ++++++++++++++++--- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c61450d..d041ccb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -22,7 +22,7 @@ resources: parameters: - name: EnableSecurityValidation type: string - default: 'false' + default: "false" variables: - template: variables.yml@Templates @@ -65,7 +65,7 @@ stages: # name: Dabacon-Products-Microsoft-Hosted-Ubuntu # demands: # - ImageOverride -equals ubuntu-latest - # - WorkFolder -equals /mnt/storage/sdc/storage + # - WorkFolder -equals /mnt/storage/sdc/storage steps: - checkout: self fetchDepth: "0" @@ -117,7 +117,7 @@ stages: # name: Dabacon-Products-Microsoft-Hosted-Ubuntu # demands: # - ImageOverride -equals ubuntu-latest - # - WorkFolder -equals /mnt/storage/sdc/storage + # - WorkFolder -equals /mnt/storage/sdc/storage steps: - checkout: self fetchDepth: "0" @@ -148,7 +148,7 @@ stages: ) $buildCommands = @( - " - shell: ['$pwshPath', '-NoProfile', '-File', '$fakeCompileScriptPath', '-Directory', 'templates,Examples', '-Configuration', 'Release']" + " - shell: ['$pwshPath', '-NoProfile', '-File', '$fakeCompileScriptPath', '-Directory', 'templates,Examples', '-Configuration', 'Release', '-Rebuild']" ) $polarisContent = @( diff --git a/scripts/Build-AddIns.ps1 b/scripts/Build-AddIns.ps1 index 4bc8019..6031bf2 100644 --- a/scripts/Build-AddIns.ps1 +++ b/scripts/Build-AddIns.ps1 @@ -2,7 +2,8 @@ param( [Parameter(Mandatory = $false)] [string]$Directory = "source", [string]$Configuration = "Debug", - [switch]$SkipPostBuildEvent + [switch]$SkipPostBuildEvent, + [switch]$Rebuild ) $ErrorActionPreference = "Stop" @@ -43,6 +44,10 @@ foreach ($proj in $projects) { '-nologo' ) + if ($Rebuild) { + $buildArgs += '-t:Rebuild' + } + if ($SkipPostBuildEvent) { $buildArgs += '-p:RunPostBuildEvent=Never' $buildArgs += '-p:PostBuildEvent=' diff --git a/scripts/Invoke-FakeUECompile.ps1 b/scripts/Invoke-FakeUECompile.ps1 index 9cf4aee..d558c98 100644 --- a/scripts/Invoke-FakeUECompile.ps1 +++ b/scripts/Invoke-FakeUECompile.ps1 @@ -5,7 +5,8 @@ param( [string[]]$Directory = @('Examples'), [string]$Configuration = 'Release', [string]$ShimProjectsPath, - [string]$ShimOutputPath + [string]$ShimOutputPath, + [switch]$Rebuild ) $ErrorActionPreference = 'Stop' @@ -30,7 +31,19 @@ if (-not $projects) { foreach ($project in $projects) { Write-Host ("Building shim assembly: {0}" -f $project.Name) -ForegroundColor Cyan - dotnet build $project.FullName -c $Configuration -nologo + $shimBuildArgs = @( + 'build' + $project.FullName + '-c' + $Configuration + '-nologo' + ) + + if ($Rebuild) { + $shimBuildArgs += '-t:Rebuild' + } + + dotnet @shimBuildArgs if ($LASTEXITCODE -ne 0) { throw ("Shim build failed: {0}" -f $project.FullName) } @@ -58,7 +71,7 @@ try { foreach ($targetDirectory in $targetDirectories) { Write-Host ("Compiling with fake UE references: {0}" -f $targetDirectory) -ForegroundColor Yellow - & $buildScriptPath -Directory $targetDirectory -Configuration $Configuration -SkipPostBuildEvent + & $buildScriptPath -Directory $targetDirectory -Configuration $Configuration -SkipPostBuildEvent -Rebuild:$Rebuild if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } From 6d60120e53e898e12adf398e4358d6362d3b20d0 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Mon, 30 Mar 2026 00:46:35 +0100 Subject: [PATCH 013/102] Fix Coverity artifact publish path --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d041ccb..91721d2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -206,7 +206,7 @@ stages: AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) POLARIS_HOME: $(Pipeline.Workspace)\Coverity-CLI - - publish: $(Build.SourcesDirectory)\.blackduck + - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\.blackduck artifact: Coverity displayName: Publish Coverity artifacts condition: always() From ac458d1c19b1bd847d8650e9cf5b1073e83ad7f8 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Mon, 30 Mar 2026 01:36:02 +0100 Subject: [PATCH 014/102] Ignore generated pmllib output --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 18a9596..4aa022d 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,8 @@ bld/ # Build results on 'Bin' directories **/[Bb]in/* +# Local UE customization runtime output +pmllib/ # Uncomment if you have tasks that rely on *.refresh files to move binaries # (https://github.com/github/gitignore/pull/3736) #!**/[Bb]in/*.refresh From 18b6c25288b06a9a2ca9cea15aeaafcb212916f9 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Mon, 30 Mar 2026 02:01:34 +0100 Subject: [PATCH 015/102] Document hosted validation agent guidance --- .../agents/ue-customization-manager.agent.md | 27 +++++++++++++++++++ README.md | 7 +++++ 2 files changed, 34 insertions(+) diff --git a/.github/agents/ue-customization-manager.agent.md b/.github/agents/ue-customization-manager.agent.md index 2cadceb..d419423 100644 --- a/.github/agents/ue-customization-manager.agent.md +++ b/.github/agents/ue-customization-manager.agent.md @@ -45,6 +45,13 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz - **CRITICAL**: After invoking a script with `run_in_terminal`, remain absolutely silent. Do not send any messages, status updates, or comments about the script running or the terminal awaiting input. - **IMPORTANT**: The scripts do NOT wait for user input after they start running. All input is gathered upfront via `vscode/askQuestions`. Never say the script is "waiting for input" because it is not - it runs autonomously after the initial parameter gathering. +7. **Understand Hosted Validation**: + - Recognize that [`azure-pipelines.yml`](../../azure-pipelines.yml) contains a hosted security-validation lane that uses fake UE references to validate both `templates/` and `Examples/`. + - Use [`scripts/Invoke-FakeUECompile.ps1`](../../scripts/Invoke-FakeUECompile.ps1) when reasoning about hosted compile-only validation. It builds shim assemblies under `shims/`, sets `UNIFIED_ENGINEERING_REFERENCE_PATH`, and compiles selected directories against the fake UE surface. + - Treat `shims/` as compile-only stand-ins for hosted validation and static analysis, not runtime replacements for a real UE installation. + - Remember that `pmllib/` is generated local output for PML template builds and is intentionally ignored in git. + - When investigating hosted Coverity capture problems, verify that the Polaris-wrapped fake compile is using `-Rebuild` so compiler activity is emitted during capture. + ## Key Script Understanding ### Setup-UECustomizationEnvironment.ps1 @@ -219,6 +226,26 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz - "For security, I'll collect your password via PowerShell credential prompt instead of asking in chat" - "The password will be cleared from memory immediately after launching UE" +### Hosted Validation and Fake Compile +**Purpose**: Help users understand and troubleshoot the hosted validation path that cannot depend on a real UE installation. + +**Key components**: +- [`azure-pipelines.yml`](../../azure-pipelines.yml): Contains the hosted test lane and the optional hosted security-validation lane. +- [`scripts/Invoke-FakeUECompile.ps1`](../../scripts/Invoke-FakeUECompile.ps1): Builds compile-only shim assemblies and compiles `templates/` and `Examples/` against them. +- [`scripts/Build-AddIns.ps1`](../../scripts/Build-AddIns.ps1): Supports `-SkipPostBuildEvent` and `-Rebuild` for deterministic compile-only builds. +- [`shims/`](../../shims): Contains compile-only UE API stand-ins used for hosted validation and Coverity capture. + +**Important notes**: +- Hosted fake compile is for validation only. It proves the code compiles against the expected API surface but does not replace testing with a real UE runtime. +- The security lane uses explicit self-checkout paths in the multi-checkout job. Do not assume relative paths like `./scripts/...` will be correct in that job. +- The Polaris-wrapped fake compile must force rebuild behavior; otherwise Coverity can report zero captured files because the build becomes an incremental no-op. +- `pmllib/` is generated by PML template build steps and should be treated as ignored local output, not authored source. + +**Example guidance**: +- "If hosted validation fails, I can inspect the fake-compile path and the multi-checkout pipeline job before assuming a UE runtime issue." +- "If Coverity reports zero captured files, I will verify that the Polaris build path is using `Invoke-FakeUECompile.ps1 -Rebuild`." +- "If you see a local `pmllib/` folder after building a PML template, that is generated output rather than source to commit." + ## Typical Workflows ### Build Projects diff --git a/README.md b/README.md index 604efbd..e76eb9b 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ The UE Customization Manager agent: - **Explains workflows** - Helps you understand the proper sequence of operations - **Validates operations** - Warns about destructive operations and validates parameters - **Troubleshoots issues** - Provides context-aware assistance based on README documentation +- **Explains hosted validation** - Understands the fake-compile, shim, and hosted security-validation path used in CI - **Launches UE** - Can start AVEVA Unified Engineering when requested - **Manages the lifecycle** - From initial setup through add-in creation to final cleanup @@ -111,6 +112,12 @@ You can interact with the agent using natural language in the GitHub Copilot cha @ue-customization-manager The script says the environment doesn't exist @ue-customization-manager What files will be deleted if I run Clear? + +@ue-customization-manager Why did hosted security validation fail? + +@ue-customization-manager How does fake compile work for templates and Examples? + +@ue-customization-manager Why did a local pmllib folder appear after building a PML template? ``` ### Benefits of Using the Agent From 22b578c59652b72a46674a118ec03f7a5fe4ec65 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Mon, 30 Mar 2026 02:10:42 +0100 Subject: [PATCH 016/102] Enhance documentation for Setup-UECustomizationEnvironment.ps1 and New-UEAddin.ps1 scripts --- .../agents/ue-customization-manager.agent.md | 81 +++++++++++++++++-- 1 file changed, 74 insertions(+), 7 deletions(-) diff --git a/.github/agents/ue-customization-manager.agent.md b/.github/agents/ue-customization-manager.agent.md index d419423..aa64ade 100644 --- a/.github/agents/ue-customization-manager.agent.md +++ b/.github/agents/ue-customization-manager.agent.md @@ -55,25 +55,30 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz ## Key Script Understanding ### Setup-UECustomizationEnvironment.ps1 + **Purpose**: Creates the initial customization environment structure and unpacks AVEVA binaries. **When to use**: + - First time setting up a UE customization environment - Need to reinitialize environment components - Setting up on a new machine - Reusing an existing environment without unpacking again **Key parameters**: + - -ReuseEnvironment (optional): If specified, skips unpacking and reuses the existing configuration (script validates environment exists). - CustomizationRootDir is automatically set to the parent of the scripts folder (non-configurable) **Parameter Gathering Workflow**: + 1. Use `vscode/askQuestions` to ask the user if they want to reuse an existing environment 2. If yes, pass -ReuseEnvironment to skip unpacking and reuse configuration 3. If no, perform full setup (script will validate and handle all checks) 4. **After invoking the script, remain completely silent** - do not send any messages about progress, terminal status, or awaiting input **Important notes**: + - Requires UE command in PATH - Sets environment variables: CAF_ADDINS_PATH, CAF_UIC_PATH at User level - Creates subdirectories: addins/, samples/ @@ -84,76 +89,91 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz - To create add-ins after setup, use New-UEAddin.ps1 in a separate step **Example guidance**: + - "This script creates the foundation. Run it first to initialize your environment." - "After setup completes, you can create add-ins using New-UEAddin.ps1" - "If you want to skip unpacking and reuse your existing environment, I can pass -ReuseEnvironment." ### New-UEAddin.ps1 + **Purpose**: Creates a new add-in configuration and source code by copying, renaming, and customizing a template folder. **When to use**: + - After Setup-UECustomizationEnvironment.ps1 has been run - Adding a new add-in to the environment - Creating multiple add-ins in the same environment **Key parameters**: + - -AddinName (mandatory): Name of the add-in to create. Use `vscode/askQuestions` to gather this if not provided by user. - -Template (mandatory): Name of the template to use. Available templates are in the `templates/` folder. Use `vscode/askQuestions` to present available template options and gather the selection. - -Module (mandatory): UE module to target. Valid values: Model, Engineering, EngineeringConfiguration, Isodraft, Draw, Paragon, Spooler, Monitor. Use `vscode/askQuestions` to present these as options and gather the selection. - CustomizationRootDir is automatically set to the parent of the scripts folder (non-configurable) **Parameter Gathering Workflow**: + 1. List available templates from the `templates/` directory using: `Get-ChildItem -Path templates -Directory | Select-Object -ExpandProperty Name | Out-String` (the `| Out-String` is critical to ensure all templates are captured in terminal output) 2. Use `vscode/askQuestions` to gather AddinName, Module, and Template in a single call 3. Pass all parameters to the script - never let the script prompt interactively **Validation**: + - -AddinName must contain only A-Za-z characters (no numbers, spaces, or special characters) **Important notes**: + - Requires existing customization environment (will fail if Setup hasn't been run) - Always gather template, module, and add-in name via `vscode/askQuestions` before calling the script - Creates XML entries in the module-specific Addins and Customization files (e.g., Engineering → TagsAddins.xml and TagsCustomization.xml) when the selected template contains a `.uic` file - **Copies templates/[TemplateName]/ to source/[AddinName]/** using robocopy - Excludes build artifacts (.vs, bin, obj, logs, log directories) -- Excludes compiler/cache files (*.user, *.suo, *.pdb, etc.) +- Excludes compiler/cache files (_.user, _.suo, \*.pdb, etc.) - Renames all files/folders/references containing the template name to the new add-in name - Replaces all text occurrences of the template name in copied files with the new add-in name - Can be called multiple times for different add-ins, with same or different templates **Example guidance**: + - "I'll gather the required information for creating your add-in" - "I need to know the add-in name, which template to use, and which UE module to target" - "Each add-in needs a unique name that will be referenced in XML configuration files" ### Remove-UEAddin.ps1 + **Purpose**: Removes an existing add-in configuration and source code from the customization environment. **When to use**: + - Need to remove an add-in that's no longer needed - Cleaning up specific add-ins while keeping others - Removing configurations before environment cleanup **⚠️ SAFETY REQUIREMENT**: + - **ALWAYS use `vscode/askQuestions` to confirm with the user before running this script** - Ask: "Are you sure you want to remove the add-in '[AddinName]'? This will delete the source code folder and all configuration files." - Only proceed if the user confirms with "yes" or similar affirmative response - If the user declines, do not run the script **Key parameters**: + - -AddinName (mandatory): Name of the add-in to remove. Use `vscode/askQuestions` to gather this if not provided. - CustomizationRootDir is automatically set to the parent of the scripts folder (non-configurable) **Parameter Gathering Workflow**: + 1. Use `vscode/askQuestions` to gather AddinName 2. Use `vscode/askQuestions` to confirm the deletion with the user 3. Only proceed if user confirms 4. Pass parameters to the script - never let the script prompt interactively **Validation**: + - -AddinName must contain only A-Za-z characters (no numbers, spaces, or special characters) **Important notes**: + - Scans and removes XML entries across all module-specific `*Addins.xml` and `*Customization.xml` files under the `addins/` folder - Deletes the corresponding `.uic` file and the add-in DLL (`addins/[AddinName].dll`) if present - **Deletes the entire `source/[AddinName]/` folder and all its contents** @@ -162,27 +182,33 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz - Does not affect environment structure or other add-ins **Example guidance**: + - "I'll gather the add-in name and confirmation before removing" - "This operation is destructive and will delete the add-in configuration and source code" ### Clear-UECustomizationEnvironment.ps1 + **Purpose**: Removes customization environment folders (samples and addins) while preserving custom add-in source code. **When to use**: + - Cleaning up after development - Removing unpacked samples and configuration files - Starting fresh with a new environment without losing custom add-in projects **Key parameters**: + - CustomizationRootDir is automatically set to the parent of the scripts folder (non-configurable) **⚠️ SAFETY REQUIREMENT**: + - **ALWAYS use `vscode/askQuestions` to confirm with the user before running this script** - Ask: "Are you sure you want to clear the customization environment? This will delete the samples/ and addins/ folders (but preserve your source/ code)." - Only proceed if the user confirms with "yes" or similar affirmative response - If the user declines, do not run the script **Important notes**: + - **Deletes samples/ and addins/ folders** (unpacked binaries and configurations) - **Preserves the source/ folder** with all custom add-in projects - To remove individual add-ins, use Remove-UEAddin.ps1 instead @@ -191,28 +217,34 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz - Use this when you want to clean the environment but keep your custom add-in code **Example guidance**: + - "This cleans up the unpacked files but keeps your custom add-in source code intact" - "To remove specific add-ins, use Remove-UEAddin.ps1 first" - Runs non-interactively without confirmation prompts - Environment variables are NOT unset (use manual cleanup if needed) **Example guidance**: + - "This removes the source, addins, and binaries folders but keeps your root directory intact" - "Safe to run after you're done with an environment setup" ### Start-UE.ps1 + **Purpose**: Launches AVEVA Unified Engineering with properly restored environment variables. **When to use**: + - Starting UE for testing or development - Ensuring environment variables are correctly set before launching - After building add-ins and ready to test **Key parameters**: + - Optional: `-Module`, `-Project`, `-Username`, `-Mdb` (forwarded to UE as `--module`, `--project`, `--username`, `--mdb`) - CustomizationRootDir is automatically set to the parent of the scripts folder (non-configurable) **Important notes**: + - Restores environment variables from `environment.json` using `Restore-EnvironmentVariables` - Requires that `Setup-UECustomizationEnvironment.ps1` has been run previously - If `environment.json` is not found, warns the user but attempts to launch UE anyway @@ -221,27 +253,32 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz - When asked to launch UE, prompt the user to confirm whether to include any of the optional UE arguments and pass those values into `Start-UE.ps1`. **Example guidance**: + - "Use this script instead of running 'ue' directly to ensure your environment is properly configured" - "After building your add-ins, run Start-UE.ps1 to test them in AVEVA Unified Engineering" - "For security, I'll collect your password via PowerShell credential prompt instead of asking in chat" - "The password will be cleared from memory immediately after launching UE" ### Hosted Validation and Fake Compile + **Purpose**: Help users understand and troubleshoot the hosted validation path that cannot depend on a real UE installation. **Key components**: + - [`azure-pipelines.yml`](../../azure-pipelines.yml): Contains the hosted test lane and the optional hosted security-validation lane. - [`scripts/Invoke-FakeUECompile.ps1`](../../scripts/Invoke-FakeUECompile.ps1): Builds compile-only shim assemblies and compiles `templates/` and `Examples/` against them. - [`scripts/Build-AddIns.ps1`](../../scripts/Build-AddIns.ps1): Supports `-SkipPostBuildEvent` and `-Rebuild` for deterministic compile-only builds. - [`shims/`](../../shims): Contains compile-only UE API stand-ins used for hosted validation and Coverity capture. **Important notes**: + - Hosted fake compile is for validation only. It proves the code compiles against the expected API surface but does not replace testing with a real UE runtime. - The security lane uses explicit self-checkout paths in the multi-checkout job. Do not assume relative paths like `./scripts/...` will be correct in that job. - The Polaris-wrapped fake compile must force rebuild behavior; otherwise Coverity can report zero captured files because the build becomes an incremental no-op. - `pmllib/` is generated by PML template build steps and should be treated as ignored local output, not authored source. **Example guidance**: + - "If hosted validation fails, I can inspect the fake-compile path and the multi-checkout pipeline job before assuming a UE runtime issue." - "If Coverity reports zero captured files, I will verify that the Polaris build path is using `Invoke-FakeUECompile.ps1 -Rebuild`." - "If you see a local `pmllib/` folder after building a PML template, that is generated output rather than source to commit." @@ -249,77 +286,105 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz ## Typical Workflows ### Build Projects + `powershell + # Build all sample projects + pwsh -NoProfile -File scripts/Build-AddIns.ps1 -Directory samples # Build all source projects + pwsh -NoProfile -File scripts/Build-AddIns.ps1 -Directory source # Build any custom path + pwsh -NoProfile -File scripts/Build-AddIns.ps1 -Directory C:\Path\To\Projects -Configuration Release ` ### Initial Setup with Add-ins + `powershell + # 1. Setup the environment + .\scripts\Setup-UECustomizationEnvironment.ps1 # 2. Create the first add-in + .\scripts\New-UEAddin.ps1 -AddinName "MyFirstAddin" # 3. Create more add-ins as needed + .\scripts\New-UEAddin.ps1 -AddinName "MySecondAddin" # 4. Build your add-ins + .\scripts\Build-AddIns.ps1 -Directory source # 5. Launch UE to test + .\scripts\Start-UE.ps1 # 6. When done, clean up + .\scripts\Clear-UECustomizationEnvironment.ps1 ` ### Multiple Add-in Development + `powershell + # 1. Setup environment + .\scripts\Setup-UECustomizationEnvironment.ps1 # 2. Create multiple add-ins over time + .\scripts\New-UEAddin.ps1 -AddinName "Addin1" .\scripts\New-UEAddin.ps1 -AddinName "Addin2" .\scripts\New-UEAddin.ps1 -AddinName "Addin3" # 3. Build and test + .\scripts\Build-AddIns.ps1 -Directory source .\scripts\Start-UE.ps1 # 4. Remove one that's no longer needed + .\scripts\Remove-UEAddin.ps1 -AddinName "Addin2" # 5. Clean up when done + .\scripts\Clear-UECustomizationEnvironment.ps1 ` ### Iterative Development + `powershell + # 1. Initial setup + .\scripts\Setup-UECustomizationEnvironment.ps1 # 2. Create add-in + .\scripts\New-UEAddin.ps1 -AddinName "MyAddin" # 3. Build and test cycle + .\scripts\Build-AddIns.ps1 -Directory source .\scripts\Start-UE.ps1 + # Modify code, rebuild, relaunch as needed... # 4. If you need to remove and recreate + .\scripts\Remove-UEAddin.ps1 -AddinName "MyAddin" .\scripts\New-UEAddin.ps1 -AddinName "MyAddin" -Template "TemplateAddIn" -Module "Model" # 5. Final cleanup + .\scripts\Clear-UECustomizationEnvironment.ps1 ` @@ -335,8 +400,8 @@ These variables are available in the current session immediately and in future P ## Important Considerations ### Directory Structure -` -CustomizationRootDir/ + +`CustomizationRootDir/ source/ # Add-in source code directory TemplateAddIn/ # Template (preserved by Clear script) [AddinName]/ # Auto-generated copies with substitutions @@ -346,10 +411,10 @@ CustomizationRootDir/ DesignCustomization.xml, TagsCustomization.xml, ConfigurationCustomization.xml, ... [AddinName].uic [AddinName].dll - binaries/ # Supporting binaries from MSIX unpack -` + binaries/ # Supporting binaries from MSIX unpack` ### File Operations + - All scripts run non-interactively without prompting - Scripts validate paths and parameters before operations - AddinName validation enforces A-Za-z characters only @@ -357,6 +422,7 @@ CustomizationRootDir/ - Errors are thrown with clear messages about what went wrong ### Error Handling + - Scripts check for required files before operations - Missing files in remove operations generate warnings, not errors - Invalid paths or missing prerequisites generate clear error messages @@ -375,7 +441,7 @@ CustomizationRootDir/ 3. **Sequential Guidance**: Help users understand the order of operations. Setup must run before New-UEAddin. -4. **Patience with Long-Running Operations**: +4. **Patience with Long-Running Operations**: - The MSIX unpack operation (during Setup-UECustomizationEnvironment.ps1) can take several minutes to complete - Use appropriate timeouts (300000ms or 5 minutes minimum) when running setup scripts - **The scripts do NOT require any user input after they start** - all parameters are gathered upfront via `vscode/askQuestions` @@ -407,6 +473,7 @@ CustomizationRootDir/ ## When to Ask Clarifying Questions Ask for clarification when: + - User intent is ambiguous (is this first setup or modification?) - Required parameters are missing - Script prerequisites might not be met @@ -417,9 +484,9 @@ Example: "Have you already run Setup-UECustomizationEnvironment.ps1? The New-UEA ## Available Documentation The repository includes: + - **README.md**: Complete documentation of all scripts with detailed examples - **Instructions.md**: Original instructions and setup guidance - Each script is self-documented with inline comments Always leverage the README documentation when helping users understand requirements or troubleshoot issues. - From 13c6b2391691f7158c903c262e3537238c6f8bba Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 10:57:57 +0100 Subject: [PATCH 017/102] Refactor Azure pipeline for hosted security validation and testing stages --- azure-pipelines.yml | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 91721d2..594e687 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -3,9 +3,9 @@ # This pipeline is intended to be connected to GitHub through the Azure Pipelines GitHub App # so its checks surface back into GitHub pull requests targeting main. # -# The hosted test stage can run immediately. -# The security stage remains opt-in until the Technology project has a UE-capable -# self-hosted Windows pool or the lane is rewritten into a hosted-compatible flow. +# The hosted test stage can run immediately on the Technology stateful hosted pools. +# The security stage remains opt-in, but now uses the hosted-compatible fake-compile flow +# on the Technology stateful hosted Ubuntu pool instead of generic vmImage selection. # Templates is consumed from a Technology fork to avoid cross-project checkout permissions. # The Technology project still needs: # - a self-hosted Windows pool with UE installed and MSBuild available @@ -26,10 +26,6 @@ parameters: variables: - template: variables.yml@Templates - - name: HostedVmImage - value: windows-latest - # - name: SecurityPoolName - # value: Technology UE Validation - name: CoverityServiceConnection value: Coverity on Polaris - name: BlackDuckServiceConnection @@ -59,13 +55,13 @@ stages: - job: Pester displayName: Pester pool: - vmImage: $(HostedVmImage) - # container: mcr.microsoft.com/dotnet/sdk:9.0 - # pool: - # name: Dabacon-Products-Microsoft-Hosted-Ubuntu - # demands: - # - ImageOverride -equals ubuntu-latest - # - WorkFolder -equals /mnt/storage/sdc/storage + name: Technology-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/powershell:7.4-ubuntu-22.04 + workspace: + clean: all steps: - checkout: self fetchDepth: "0" @@ -109,15 +105,13 @@ stages: displayName: Coverity And Black Duck timeoutInMinutes: "180" pool: - vmImage: $(HostedVmImage) - # pool: - # name: $(SecurityPoolName) - # container: mcr.microsoft.com/dotnet/sdk:9.0 - # pool: - # name: Dabacon-Products-Microsoft-Hosted-Ubuntu - # demands: - # - ImageOverride -equals ubuntu-latest - # - WorkFolder -equals /mnt/storage/sdc/storage + name: Technology-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/powershell:7.4-ubuntu-22.04 + workspace: + clean: all steps: - checkout: self fetchDepth: "0" From eec31544afb0638fa71ccd03d46b9d66baa8866f Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 11:10:12 +0100 Subject: [PATCH 018/102] Refactor Azure pipeline for Black Duck integration and cleanup --- azure-pipelines.yml | 155 ++++++++++++++++++++++---------------------- 1 file changed, 76 insertions(+), 79 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 594e687..e0634e2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -10,7 +10,6 @@ # The Technology project still needs: # - a self-hosted Windows pool with UE installed and MSBuild available # - service connections named `Coverity on Polaris` and `BlackDuckScanner` -# - a variable group named `BlackDuck on Polaris` exposing BLACKDUCK_ACCESS_TOKEN resources: repositories: @@ -26,12 +25,9 @@ parameters: variables: - template: variables.yml@Templates - - name: CoverityServiceConnection - value: Coverity on Polaris - - name: BlackDuckServiceConnection - value: BlackDuckScanner - - name: BlackDuckGroup - value: DabaconProductsGroup + + + - name: CoverityProjectTeam value: .RnD.Polaris.Dabacon-Products.SecurityAdvisors - name: CoverityCliVersion @@ -177,7 +173,7 @@ stages: displayName: Restore Coverity CLI cache retryCountOnTaskFailure: 3 inputs: - key: '"CoverityCLI" | "$(CoverityCliVersion)" | "v1"' + key: "\"CoverityCLI\" | \"$(CoverityCliVersion)\" | \"v1\"" path: $(Pipeline.Workspace)\Coverity-CLI cacheHitVar: COVERITY_CLI_RESTORED @@ -191,7 +187,7 @@ stages: - task: BlackduckCoverityOnPolaris@2 displayName: Coverity scan inputs: - polarisService: $(CoverityServiceConnection) + polarisService: Coverity on Polaris polarisCommand: > -c $(Pipeline.Workspace)\polaris.yml analyze @@ -205,73 +201,74 @@ stages: displayName: Publish Coverity artifacts condition: always() - # - pwsh: | - # $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') - # $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` - # ? "@$($templatesBranch)" ` - # : '' - - # if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { - # $versionName = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)" - # } - # else { - # $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' - # $versionName = "$branchName-$($env:BUILD_BUILDID)" - # } - - # $versionName = "$versionName$templatesPostfix" - - # Write-Host "##vso[task.setvariable variable=BlackDuckProjectVersion]$versionName" - # Write-Host "Black Duck project version: $versionName" - # displayName: Calculate Black Duck version - - # - task: ArchiveFiles@2 - # displayName: Archive sources and build outputs - # inputs: - # rootFolderOrFile: $(Build.SourcesDirectory) - # includeRootFolder: true - # archiveType: zip - # archiveFile: $(Build.ArtifactStagingDirectory)\blackduck-input.zip - # replaceExistingArchive: true - - # - task: BlackDuckDetectTask@10 - # displayName: Black Duck scan - # inputs: - # BlackDuckScaService: $(BlackDuckServiceConnection) - # DetectVersion: latest - # DetectFolder: $(Pipeline.Workspace)\detect - # DetectArguments: | - # --detect.project.name=$(Build.Repository.Name) - # --detect.project.version.name=$(BlackDuckProjectVersion) - # --detect.project.group.name=$(BlackDuckGroup) - # --detect.source.path=$(Build.SourcesDirectory) - # --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)\blackduck-input.zip - # --detect.cleanup=true - # --detect.output.path=$(Build.ArtifactStagingDirectory)\blackduck-logs - # --detect.detector.search.depth=10 - # --detect.timeout=1800 - # --detect.wait.for.results=true - # --detect.wait.for.results.timeout=6000000 - # --logging.level.detect=DEBUG - # --detect.diagnostic=true - - # - publish: $(Build.ArtifactStagingDirectory)\blackduck-logs - # artifact: BlackDuckLogs - # displayName: Publish Black Duck logs - # condition: always() - - # - pwsh: | - # $outputPath = Join-Path $env:PIPELINE_WORKSPACE 'BlackDuckComponentNotices.txt' - # & "$(Pipeline.Workspace)\Templates\scripts\BlackDuck\Generate-BlackDuckNotice.ps1" ` - # -ProjectName "$(Build.Repository.Name)" ` - # -ProjectVersion "$(BlackDuckProjectVersion)" ` - # -OutputPath $outputPath - # displayName: Generate Black Duck NOTICE - # condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/main')) - # env: - # BlackduckToken: $(BLACKDUCK_ACCESS_TOKEN) - - # - publish: $(Pipeline.Workspace)\BlackDuckComponentNotices.txt - # artifact: BlackDuckNotice - # displayName: Publish Black Duck NOTICE - # condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/main')) + - pwsh: | + $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') + $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` + ? "@$($templatesBranch)" ` + : '' + + if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { + $versionName = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)" + } + else { + $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' + $versionName = "$branchName-$($env:BUILD_BUILDID)" + } + + $versionName = "$versionName$templatesPostfix" + + Write-Host "##vso[task.setvariable variable=BlackDuckProjectVersion]$versionName" + Write-Host "Black Duck project version: $versionName" + displayName: Calculate Black Duck version + + - template: steps/install-python.yml + parameters: + pythonVersion: 3.11 + condition: not(canceled()) + + - task: ArchiveFiles@2 + displayName: Archive sources and build outputs + inputs: + rootFolderOrFile: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework + includeRootFolder: true + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)\blackduck-input.zip + replaceExistingArchive: true + verbose: true + + - task: BlackDuckDetectTask@10 + displayName: Black Duck scan + retryCountOnTaskFailure: 0 + inputs: + BlackDuckScaService: BlackDuckScanner + DetectVersion: latest + DetectFolder: $(Pipeline.Workspace)\detect + DetectArguments: | + --detect.project.name=$(Build.Repository.Name) + --detect.project.version.name=$(BlackDuckProjectVersion) + --detect.project.group.name=DabaconProductsGroup + --detect.source.path=$(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework + --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)\blackduck-input.zip + --detect.cleanup=true + --detect.output.path=$(Build.ArtifactStagingDirectory)\blackduck-logs + --detect.detector.search.depth=10 + --detect.detector.search.continue=true + --detect.blackduck.signature.scanner.upload.source.mode=true + --detect.blackduck.signature.scanner.copyright.search=true + --detect.blackduck.signature.scanner.license.search=true + --logging.level.detect=DEBUG + --detect.diagnostic=true + --detect.timeout=1800 + --detect.blackduck.signature.scanner.arguments= --fs-wait-time=90 + --detect.wait.for.results=true + --detect.wait.for.results.timeout=6000000 + + - publish: $(Build.ArtifactStagingDirectory)\blackduck-logs + artifact: BlackDuckLogs + displayName: Publish Black Duck logs + condition: always() + + - publish: $(Build.ArtifactStagingDirectory)\blackduck-input.zip + artifact: BlackDuckInput + displayName: Publish Black Duck input archive + condition: always() From bd1122a3ac82337b88d2cd1529b1fac7e19b47c2 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 11:25:03 +0100 Subject: [PATCH 019/102] Enhance Azure pipeline and scripts for shim project generation and publishing --- .../agents/ue-customization-manager.agent.md | 4 +- .gitignore | 1 + azure-pipelines.yml | 13 +++- scripts/Invoke-FakeUECompile.ps1 | 63 ++++++++++++++++++- ...ApplicationFramework.Implementation.csproj | 6 -- ...a.ApplicationFramework.Presentation.csproj | 6 -- shims/Aveva.ApplicationFramework.csproj | 6 -- shims/Aveva.Core.Database.Filters.csproj | 6 -- shims/Aveva.Core.Database.csproj | 9 --- ...Aveva.Core.FlexibleExplorer.Control.csproj | 6 -- shims/Aveva.Core.FlexibleExplorer.Core.csproj | 6 -- shims/Aveva.Core.Geometry.csproj | 6 -- shims/Aveva.Core.Implementation.csproj | 6 -- shims/Aveva.Core.Presentation.csproj | 6 -- shims/Aveva.Core.Utilities.csproj | 6 -- shims/Aveva.Core3D.Clasher.csproj | 6 -- shims/GridControl.csproj | 6 -- shims/Microsoft.Web.WebView2.Core.csproj | 6 -- shims/Microsoft.Web.WebView2.WinForms.csproj | 6 -- shims/Newtonsoft.Json.csproj | 6 -- shims/PMLNet.csproj | 6 -- shims/StartUp.csproj | 6 -- shims/shim-projects.psd1 | 24 +++++++ 23 files changed, 98 insertions(+), 118 deletions(-) delete mode 100644 shims/Aveva.ApplicationFramework.Implementation.csproj delete mode 100644 shims/Aveva.ApplicationFramework.Presentation.csproj delete mode 100644 shims/Aveva.ApplicationFramework.csproj delete mode 100644 shims/Aveva.Core.Database.Filters.csproj delete mode 100644 shims/Aveva.Core.Database.csproj delete mode 100644 shims/Aveva.Core.FlexibleExplorer.Control.csproj delete mode 100644 shims/Aveva.Core.FlexibleExplorer.Core.csproj delete mode 100644 shims/Aveva.Core.Geometry.csproj delete mode 100644 shims/Aveva.Core.Implementation.csproj delete mode 100644 shims/Aveva.Core.Presentation.csproj delete mode 100644 shims/Aveva.Core.Utilities.csproj delete mode 100644 shims/Aveva.Core3D.Clasher.csproj delete mode 100644 shims/GridControl.csproj delete mode 100644 shims/Microsoft.Web.WebView2.Core.csproj delete mode 100644 shims/Microsoft.Web.WebView2.WinForms.csproj delete mode 100644 shims/Newtonsoft.Json.csproj delete mode 100644 shims/PMLNet.csproj delete mode 100644 shims/StartUp.csproj create mode 100644 shims/shim-projects.psd1 diff --git a/.github/agents/ue-customization-manager.agent.md b/.github/agents/ue-customization-manager.agent.md index aa64ade..cf1c66a 100644 --- a/.github/agents/ue-customization-manager.agent.md +++ b/.github/agents/ue-customization-manager.agent.md @@ -266,9 +266,9 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz **Key components**: - [`azure-pipelines.yml`](../../azure-pipelines.yml): Contains the hosted test lane and the optional hosted security-validation lane. -- [`scripts/Invoke-FakeUECompile.ps1`](../../scripts/Invoke-FakeUECompile.ps1): Builds compile-only shim assemblies and compiles `templates/` and `Examples/` against them. +- [`scripts/Invoke-FakeUECompile.ps1`](../../scripts/Invoke-FakeUECompile.ps1): Generates shim project files from `shims/shim-projects.psd1`, builds compile-only shim assemblies, and compiles `templates/` and `Examples/` against them. - [`scripts/Build-AddIns.ps1`](../../scripts/Build-AddIns.ps1): Supports `-SkipPostBuildEvent` and `-Rebuild` for deterministic compile-only builds. -- [`shims/`](../../shims): Contains compile-only UE API stand-ins used for hosted validation and Coverity capture. +- [`shims/`](../../shims): Contains compile-only UE API stand-ins used for hosted validation and Coverity capture, plus the `shim-projects.psd1` manifest used to generate shim project files at build time. **Important notes**: diff --git a/.gitignore b/.gitignore index 4aa022d..8b4abf6 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ bld/ **/[Bb]in/* # Local UE customization runtime output pmllib/ +shims/generated/ # Uncomment if you have tasks that rely on *.refresh files to move binaries # (https://github.com/github/gitignore/pull/3736) #!**/[Bb]in/*.refresh diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e0634e2..e662db4 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -25,9 +25,6 @@ parameters: variables: - template: variables.yml@Templates - - - - name: CoverityProjectTeam value: .RnD.Polaris.Dabacon-Products.SecurityAdvisors - name: CoverityCliVersion @@ -120,6 +117,16 @@ stages: & (Join-Path $repoRoot 'scripts\Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release displayName: Build template and example projects with fake UE references + - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\shims + artifact: ShimSources + displayName: Publish shim sources + condition: always() + + - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\shims\artifacts\Release + artifact: ShimAssemblies + displayName: Publish compiled shim assemblies + condition: always() + - pwsh: | $pwshPath = (Get-Command pwsh -ErrorAction Stop).Source $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's\UnifiedEngineeringCustomizationFramework' diff --git a/scripts/Invoke-FakeUECompile.ps1 b/scripts/Invoke-FakeUECompile.ps1 index d558c98..b152539 100644 --- a/scripts/Invoke-FakeUECompile.ps1 +++ b/scripts/Invoke-FakeUECompile.ps1 @@ -5,6 +5,8 @@ param( [string[]]$Directory = @('Examples'), [string]$Configuration = 'Release', [string]$ShimProjectsPath, + [string]$ShimManifestPath, + [string]$GeneratedShimProjectsPath, [string]$ShimOutputPath, [switch]$Rebuild ) @@ -17,16 +19,73 @@ if (-not $ShimProjectsPath) { $ShimProjectsPath = Join-Path $repoRoot 'shims' } +if (-not $ShimManifestPath) { + $ShimManifestPath = Join-Path $ShimProjectsPath 'shim-projects.psd1' +} + +if (-not $GeneratedShimProjectsPath) { + $GeneratedShimProjectsPath = Join-Path $ShimProjectsPath 'generated' +} + if (-not $ShimOutputPath) { $ShimOutputPath = Join-Path $ShimProjectsPath "artifacts\$Configuration" } $resolvedShimProjectsPath = (Resolve-Path $ShimProjectsPath).Path +$resolvedShimManifestPath = (Resolve-Path $ShimManifestPath).Path +$resolvedGeneratedShimProjectsPath = [System.IO.Path]::GetFullPath($GeneratedShimProjectsPath) $buildScriptPath = Join-Path $PSScriptRoot 'Build-AddIns.ps1' -$projects = Get-ChildItem -Path $resolvedShimProjectsPath -Filter '*.csproj' -File | Sort-Object Name +$shimManifest = Import-PowerShellDataFile -Path $resolvedShimManifestPath +$shimProjects = if ($shimManifest.ContainsKey('Projects')) { + $shimManifest.Projects +} +else { + $null +} + +if (-not $shimProjects -or $shimProjects.Count -eq 0) { + throw "No shim projects were defined in $resolvedShimManifestPath" +} + +if (Test-Path -LiteralPath $resolvedGeneratedShimProjectsPath) { + Remove-Item -LiteralPath $resolvedGeneratedShimProjectsPath -Recurse -Force +} + +$null = New-Item -ItemType Directory -Path $resolvedGeneratedShimProjectsPath -Force + +foreach ($projectEntry in $shimProjects.GetEnumerator()) { + $projectName = [string]$projectEntry.Key + $projectConfig = $projectEntry.Value + $rootNamespace = if ($projectConfig.RootNamespace) { + [string]$projectConfig.RootNamespace + } + else { + $projectName + } + + $projectLines = [System.Collections.Generic.List[string]]::new() + $projectLines.Add('') + $projectLines.Add(' ') + $projectLines.Add(" $projectName") + $projectLines.Add(" $rootNamespace") + $projectLines.Add(' ') + + if ($projectConfig.ContainsKey('CompileInclude')) { + $projectLines.Add(' ') + $projectLines.Add((' ' -f $projectConfig.CompileInclude)) + $projectLines.Add(' ') + } + + $projectLines.Add('') + + $generatedProjectPath = Join-Path $resolvedGeneratedShimProjectsPath "$projectName.csproj" + $projectLines -join [Environment]::NewLine | Set-Content -LiteralPath $generatedProjectPath -Encoding utf8 +} + +$projects = Get-ChildItem -Path $resolvedGeneratedShimProjectsPath -Filter '*.csproj' -File | Sort-Object Name if (-not $projects) { - throw "No shim projects found under $resolvedShimProjectsPath" + throw "No generated shim projects found under $resolvedGeneratedShimProjectsPath" } foreach ($project in $projects) { diff --git a/shims/Aveva.ApplicationFramework.Implementation.csproj b/shims/Aveva.ApplicationFramework.Implementation.csproj deleted file mode 100644 index 2ea7f56..0000000 --- a/shims/Aveva.ApplicationFramework.Implementation.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Aveva.ApplicationFramework.Implementation - Aveva.ApplicationFramework.Implementation - - \ No newline at end of file diff --git a/shims/Aveva.ApplicationFramework.Presentation.csproj b/shims/Aveva.ApplicationFramework.Presentation.csproj deleted file mode 100644 index 9e8e054..0000000 --- a/shims/Aveva.ApplicationFramework.Presentation.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Aveva.ApplicationFramework.Presentation - Aveva.ApplicationFramework.Presentation - - \ No newline at end of file diff --git a/shims/Aveva.ApplicationFramework.csproj b/shims/Aveva.ApplicationFramework.csproj deleted file mode 100644 index a2e5b91..0000000 --- a/shims/Aveva.ApplicationFramework.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Aveva.ApplicationFramework - Aveva.ApplicationFramework - - \ No newline at end of file diff --git a/shims/Aveva.Core.Database.Filters.csproj b/shims/Aveva.Core.Database.Filters.csproj deleted file mode 100644 index dc9aa9a..0000000 --- a/shims/Aveva.Core.Database.Filters.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Aveva.Core.Database.Filters - Aveva.Core.Database.Filters - - \ No newline at end of file diff --git a/shims/Aveva.Core.Database.csproj b/shims/Aveva.Core.Database.csproj deleted file mode 100644 index 925e3b9..0000000 --- a/shims/Aveva.Core.Database.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - Aveva.Core.Database - Aveva.Core.Database - - - - - \ No newline at end of file diff --git a/shims/Aveva.Core.FlexibleExplorer.Control.csproj b/shims/Aveva.Core.FlexibleExplorer.Control.csproj deleted file mode 100644 index df56982..0000000 --- a/shims/Aveva.Core.FlexibleExplorer.Control.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Aveva.Core.FlexibleExplorer.Control - Aveva.Core.FlexibleExplorer.Control - - \ No newline at end of file diff --git a/shims/Aveva.Core.FlexibleExplorer.Core.csproj b/shims/Aveva.Core.FlexibleExplorer.Core.csproj deleted file mode 100644 index 4aaf960..0000000 --- a/shims/Aveva.Core.FlexibleExplorer.Core.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Aveva.Core.FlexibleExplorer.Core - Aveva.Core.FlexibleExplorer.Core - - \ No newline at end of file diff --git a/shims/Aveva.Core.Geometry.csproj b/shims/Aveva.Core.Geometry.csproj deleted file mode 100644 index 5e9fd28..0000000 --- a/shims/Aveva.Core.Geometry.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Aveva.Core.Geometry - Aveva.Core.Geometry - - \ No newline at end of file diff --git a/shims/Aveva.Core.Implementation.csproj b/shims/Aveva.Core.Implementation.csproj deleted file mode 100644 index d4f4ed5..0000000 --- a/shims/Aveva.Core.Implementation.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Aveva.Core.Implementation - Aveva.Core.Implementation - - \ No newline at end of file diff --git a/shims/Aveva.Core.Presentation.csproj b/shims/Aveva.Core.Presentation.csproj deleted file mode 100644 index 9033125..0000000 --- a/shims/Aveva.Core.Presentation.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Aveva.Core.Presentation - Aveva.Core.Presentation - - \ No newline at end of file diff --git a/shims/Aveva.Core.Utilities.csproj b/shims/Aveva.Core.Utilities.csproj deleted file mode 100644 index f3972c3..0000000 --- a/shims/Aveva.Core.Utilities.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Aveva.Core.Utilities - Aveva.Core.Utilities - - \ No newline at end of file diff --git a/shims/Aveva.Core3D.Clasher.csproj b/shims/Aveva.Core3D.Clasher.csproj deleted file mode 100644 index e32e24a..0000000 --- a/shims/Aveva.Core3D.Clasher.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Aveva.Core3D.Clasher - Aveva.Core3D.Clasher - - \ No newline at end of file diff --git a/shims/GridControl.csproj b/shims/GridControl.csproj deleted file mode 100644 index 414d294..0000000 --- a/shims/GridControl.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - GridControl - GridControl - - \ No newline at end of file diff --git a/shims/Microsoft.Web.WebView2.Core.csproj b/shims/Microsoft.Web.WebView2.Core.csproj deleted file mode 100644 index 4fc1c1b..0000000 --- a/shims/Microsoft.Web.WebView2.Core.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Microsoft.Web.WebView2.Core - Microsoft.Web.WebView2.Core - - \ No newline at end of file diff --git a/shims/Microsoft.Web.WebView2.WinForms.csproj b/shims/Microsoft.Web.WebView2.WinForms.csproj deleted file mode 100644 index d422918..0000000 --- a/shims/Microsoft.Web.WebView2.WinForms.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Microsoft.Web.WebView2.WinForms - Microsoft.Web.WebView2.WinForms - - \ No newline at end of file diff --git a/shims/Newtonsoft.Json.csproj b/shims/Newtonsoft.Json.csproj deleted file mode 100644 index ccdbc9f..0000000 --- a/shims/Newtonsoft.Json.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Newtonsoft.Json - Newtonsoft.Json - - \ No newline at end of file diff --git a/shims/PMLNet.csproj b/shims/PMLNet.csproj deleted file mode 100644 index cfef247..0000000 --- a/shims/PMLNet.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - PMLNet - PMLNet - - \ No newline at end of file diff --git a/shims/StartUp.csproj b/shims/StartUp.csproj deleted file mode 100644 index fc28b78..0000000 --- a/shims/StartUp.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - StartUp - StartUp - - \ No newline at end of file diff --git a/shims/shim-projects.psd1 b/shims/shim-projects.psd1 new file mode 100644 index 0000000..91779f5 --- /dev/null +++ b/shims/shim-projects.psd1 @@ -0,0 +1,24 @@ +@{ + Projects = @{ + 'Aveva.ApplicationFramework' = @{} + 'Aveva.ApplicationFramework.Implementation' = @{} + 'Aveva.ApplicationFramework.Presentation' = @{} + 'Aveva.Core.Database' = @{ + CompileInclude = '..\src\**\*.cs' + } + 'Aveva.Core.Database.Filters' = @{} + 'Aveva.Core.FlexibleExplorer.Control' = @{} + 'Aveva.Core.FlexibleExplorer.Core' = @{} + 'Aveva.Core.Geometry' = @{} + 'Aveva.Core.Implementation' = @{} + 'Aveva.Core.Presentation' = @{} + 'Aveva.Core.Utilities' = @{} + 'Aveva.Core3D.Clasher' = @{} + 'GridControl' = @{} + 'Microsoft.Web.WebView2.Core' = @{} + 'Microsoft.Web.WebView2.WinForms' = @{} + 'Newtonsoft.Json' = @{} + 'PMLNet' = @{} + 'StartUp' = @{} + } +} \ No newline at end of file From fc29375b8474dbebd24c62ad9d52337d23a5bba5 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 11:36:25 +0100 Subject: [PATCH 020/102] Update shim project generation and Azure pipeline for CI validation --- .github/agents/ue-customization-manager.agent.md | 4 ++-- azure-pipelines.yml | 10 ++++++++++ scripts/Invoke-FakeUECompile.ps1 | 14 ++++++++++++-- shims/shim-projects.psd1 | 2 +- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.github/agents/ue-customization-manager.agent.md b/.github/agents/ue-customization-manager.agent.md index cf1c66a..0746217 100644 --- a/.github/agents/ue-customization-manager.agent.md +++ b/.github/agents/ue-customization-manager.agent.md @@ -266,13 +266,13 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz **Key components**: - [`azure-pipelines.yml`](../../azure-pipelines.yml): Contains the hosted test lane and the optional hosted security-validation lane. -- [`scripts/Invoke-FakeUECompile.ps1`](../../scripts/Invoke-FakeUECompile.ps1): Generates shim project files from `shims/shim-projects.psd1`, builds compile-only shim assemblies, and compiles `templates/` and `Examples/` against them. +- [`scripts/Invoke-FakeUECompile.ps1`](../../scripts/Invoke-FakeUECompile.ps1): Generates CI-only shim project files from `shims/shim-projects.psd1` into `.tmp/generated-shims`, builds compile-only shim assemblies, and compiles `templates/` and `Examples/` against them. - [`scripts/Build-AddIns.ps1`](../../scripts/Build-AddIns.ps1): Supports `-SkipPostBuildEvent` and `-Rebuild` for deterministic compile-only builds. - [`shims/`](../../shims): Contains compile-only UE API stand-ins used for hosted validation and Coverity capture, plus the `shim-projects.psd1` manifest used to generate shim project files at build time. **Important notes**: -- Hosted fake compile is for validation only. It proves the code compiles against the expected API surface but does not replace testing with a real UE runtime. +- Hosted fake compile is for validation only. It proves the code compiles against the expected API surface for CI and Coverity capture, but does not replace development or testing with a real UE runtime. - The security lane uses explicit self-checkout paths in the multi-checkout job. Do not assume relative paths like `./scripts/...` will be correct in that job. - The Polaris-wrapped fake compile must force rebuild behavior; otherwise Coverity can report zero captured files because the build becomes an incremental no-op. - `pmllib/` is generated by PML template build steps and should be treated as ignored local output, not authored source. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e662db4..b5018c0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -122,6 +122,16 @@ stages: displayName: Publish shim sources condition: always() + - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\shims\shim-projects.psd1 + artifact: ShimManifest + displayName: Publish shim manifest + condition: always() + + - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\.tmp\generated-shims + artifact: GeneratedShimProjects + displayName: Publish generated shim project files + condition: always() + - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\shims\artifacts\Release artifact: ShimAssemblies displayName: Publish compiled shim assemblies diff --git a/scripts/Invoke-FakeUECompile.ps1 b/scripts/Invoke-FakeUECompile.ps1 index b152539..3885ac4 100644 --- a/scripts/Invoke-FakeUECompile.ps1 +++ b/scripts/Invoke-FakeUECompile.ps1 @@ -1,5 +1,8 @@ #Requires -Version 7.0 +# Builds compile-only shim assemblies for hosted validation and Coverity capture. +# This is a CI-focused path; developers with a real UE installation should use the normal UE references. + [CmdletBinding()] param( [string[]]$Directory = @('Examples'), @@ -24,7 +27,7 @@ if (-not $ShimManifestPath) { } if (-not $GeneratedShimProjectsPath) { - $GeneratedShimProjectsPath = Join-Path $ShimProjectsPath 'generated' + $GeneratedShimProjectsPath = Join-Path $repoRoot '.tmp\generated-shims' } if (-not $ShimOutputPath) { @@ -34,6 +37,7 @@ if (-not $ShimOutputPath) { $resolvedShimProjectsPath = (Resolve-Path $ShimProjectsPath).Path $resolvedShimManifestPath = (Resolve-Path $ShimManifestPath).Path $resolvedGeneratedShimProjectsPath = [System.IO.Path]::GetFullPath($GeneratedShimProjectsPath) +$shimDirectoryBuildPropsPath = Join-Path $resolvedShimProjectsPath 'Directory.Build.props' $buildScriptPath = Join-Path $PSScriptRoot 'Build-AddIns.ps1' $shimManifest = Import-PowerShellDataFile -Path $resolvedShimManifestPath @@ -66,14 +70,20 @@ foreach ($projectEntry in $shimProjects.GetEnumerator()) { $projectLines = [System.Collections.Generic.List[string]]::new() $projectLines.Add('') + $projectLines.Add((' ' -f $shimDirectoryBuildPropsPath.Replace('&', '&'))) $projectLines.Add(' ') $projectLines.Add(" $projectName") $projectLines.Add(" $rootNamespace") $projectLines.Add(' ') if ($projectConfig.ContainsKey('CompileInclude')) { + $compileIncludePath = [string]$projectConfig.CompileInclude + if (-not [System.IO.Path]::IsPathRooted($compileIncludePath)) { + $compileIncludePath = [System.IO.Path]::GetFullPath((Join-Path $resolvedShimProjectsPath $compileIncludePath)) + } + $projectLines.Add(' ') - $projectLines.Add((' ' -f $projectConfig.CompileInclude)) + $projectLines.Add((' ' -f $compileIncludePath.Replace('&', '&'))) $projectLines.Add(' ') } diff --git a/shims/shim-projects.psd1 b/shims/shim-projects.psd1 index 91779f5..207fef3 100644 --- a/shims/shim-projects.psd1 +++ b/shims/shim-projects.psd1 @@ -4,7 +4,7 @@ 'Aveva.ApplicationFramework.Implementation' = @{} 'Aveva.ApplicationFramework.Presentation' = @{} 'Aveva.Core.Database' = @{ - CompileInclude = '..\src\**\*.cs' + CompileInclude = 'src\**\*.cs' } 'Aveva.Core.Database.Filters' = @{} 'Aveva.Core.FlexibleExplorer.Control' = @{} From 7d58a97329b24e6e2b432e5ce227391c3c9702d1 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 11:41:25 +0100 Subject: [PATCH 021/102] Update Azure pipeline to reference specific template for Python installation --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b5018c0..e0e8362 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -238,7 +238,7 @@ stages: Write-Host "Black Duck project version: $versionName" displayName: Calculate Black Duck version - - template: steps/install-python.yml + - template: steps/install-python.yml@Templates parameters: pythonVersion: 3.11 condition: not(canceled()) From 1b0e5ef3c955f9db9135a5c86fcf1af7a99b3af0 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 12:27:18 +0100 Subject: [PATCH 022/102] Fix hosted test temp path handling --- .../Tests/UECustomizationFunctions.Tests.ps1 | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/scripts/Tests/UECustomizationFunctions.Tests.ps1 b/scripts/Tests/UECustomizationFunctions.Tests.ps1 index 64fcef1..bb8a9d8 100644 --- a/scripts/Tests/UECustomizationFunctions.Tests.ps1 +++ b/scripts/Tests/UECustomizationFunctions.Tests.ps1 @@ -5,6 +5,8 @@ Import-Module -Name $modulePath -Force -ErrorAction Stop Describe 'UECustomizationFunctions Module' { BeforeAll { + $script:TempRoot = [System.IO.Path]::GetTempPath() + Mock -CommandName Set-EnvironmentVariableUserAndSession -ModuleName UECustomizationFunctions -MockWith { Set-Item -Path ("Env:{0}" -f $Name) -Value $Value } @@ -45,7 +47,7 @@ Describe 'UECustomizationFunctions Module' { Context 'Find-RequiredFile' { It 'throws if file not found' { - $temp = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("UE-Test-" + [Guid]::NewGuid())) + $temp = New-Item -ItemType Directory -Path (Join-Path $script:TempRoot ("UE-Test-" + [Guid]::NewGuid())) try { { Find-RequiredFile -RootPath $temp.FullName -FileName 'nope.txt' } | Should -Throw } @@ -55,7 +57,7 @@ Describe 'UECustomizationFunctions Module' { } It 'returns full path when file exists' { - $temp = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("UE-Test-" + [Guid]::NewGuid())) + $temp = New-Item -ItemType Directory -Path (Join-Path $script:TempRoot ("UE-Test-" + [Guid]::NewGuid())) try { $file = Join-Path $temp.FullName 'found.txt' Set-Content -Path $file -Value 'test' -Encoding UTF8 @@ -68,7 +70,7 @@ Describe 'UECustomizationFunctions Module' { } It 'finds files in subdirectories' { - $temp = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("UE-Test-" + [Guid]::NewGuid())) + $temp = New-Item -ItemType Directory -Path (Join-Path $script:TempRoot ("UE-Test-" + [Guid]::NewGuid())) try { $subdir = New-Item -Path (Join-Path $temp.FullName "subdir") -ItemType Directory $file = Join-Path $subdir.FullName 'nested.txt' @@ -84,7 +86,7 @@ Describe 'UECustomizationFunctions Module' { Context 'Initialize-DirectoryStructure' { It 'creates addins and pmllib directories' { - $tempRoot = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("UE-Test-" + [Guid]::NewGuid())) + $tempRoot = New-Item -ItemType Directory -Path (Join-Path $script:TempRoot ("UE-Test-" + [Guid]::NewGuid())) try { $addins = Join-Path $tempRoot.FullName 'addins' $pmllib = Join-Path $tempRoot.FullName 'pmllib' @@ -98,7 +100,7 @@ Describe 'UECustomizationFunctions Module' { } It 'creates nested directory paths' { - $tempRoot = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("UE-Test-" + [Guid]::NewGuid())) + $tempRoot = New-Item -ItemType Directory -Path (Join-Path $script:TempRoot ("UE-Test-" + [Guid]::NewGuid())) try { $addins = Join-Path $tempRoot.FullName 'nested\path\addins' $pmllib = Join-Path $tempRoot.FullName 'nested\path\pmllib' @@ -114,7 +116,7 @@ Describe 'UECustomizationFunctions Module' { Context 'Set-EnvironmentConfiguration' { It 'creates configuration file with correct content' { - $tempRoot = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("UE-Test-" + [Guid]::NewGuid())) + $tempRoot = New-Item -ItemType Directory -Path (Join-Path $script:TempRoot ("UE-Test-" + [Guid]::NewGuid())) try { $configPath = Join-Path $tempRoot.FullName 'environment.json' $addins = Join-Path $tempRoot.FullName 'addins' @@ -134,7 +136,7 @@ Describe 'UECustomizationFunctions Module' { } It 'sets environment variables in session' { - $tempRoot = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("UE-Test-" + [Guid]::NewGuid())) + $tempRoot = New-Item -ItemType Directory -Path (Join-Path $script:TempRoot ("UE-Test-" + [Guid]::NewGuid())) try { $configPath = Join-Path $tempRoot.FullName 'environment.json' $addins = Join-Path $tempRoot.FullName 'addins' @@ -159,7 +161,7 @@ Describe 'UECustomizationFunctions Module' { } It 'restores environment variables from config file' { - $tempRoot = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("UE-Test-" + [Guid]::NewGuid())) + $tempRoot = New-Item -ItemType Directory -Path (Join-Path $script:TempRoot ("UE-Test-" + [Guid]::NewGuid())) try { $configPath = Join-Path $tempRoot.FullName 'environment.json' $addins = Join-Path $tempRoot.FullName 'addins' @@ -193,7 +195,7 @@ Describe 'UECustomizationFunctions Module' { } It 'throws if destination is invalid' { - $temp = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("UE-Test-" + [Guid]::NewGuid())) + $temp = New-Item -ItemType Directory -Path (Join-Path $script:TempRoot ("UE-Test-" + [Guid]::NewGuid())) try { $zipPath = Join-Path $temp.FullName 'test.zip' Set-Content -Path $zipPath -Value 'dummy' @@ -209,10 +211,10 @@ Describe 'UECustomizationFunctions Module' { Context 'Install-UEFiles' { It 'does not throw if source directory has no config files' { - $temp = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("UE-Test-" + [Guid]::NewGuid())) + $temp = New-Item -ItemType Directory -Path (Join-Path $script:TempRoot ("UE-Test-" + [Guid]::NewGuid())) try { # Should warn but not throw when no config files are found - { Install-UEFiles -UnpackPath $temp.FullName -AddinsFolder (Join-Path $env:TEMP 'addins') } | Should -Not -Throw + { Install-UEFiles -UnpackPath $temp.FullName -AddinsFolder (Join-Path $script:TempRoot 'addins') } | Should -Not -Throw } finally { Remove-Item -Path $temp.FullName -Recurse -Force @@ -228,7 +230,7 @@ Describe 'UECustomizationFunctions Module' { Context 'Clear-UECustomizationEnvironment.ps1 Script' { It 'removes environment.json during cleanup' { - $tempRoot = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("UE-Test-" + [Guid]::NewGuid())) + $tempRoot = New-Item -ItemType Directory -Path (Join-Path $script:TempRoot ("UE-Test-" + [Guid]::NewGuid())) try { # Create a mock environment.json $configPath = Join-Path $tempRoot.FullName 'environment.json' @@ -267,7 +269,7 @@ Describe 'UECustomizationFunctions Module' { } It 'updates DesignCustomization.xml only when template contains .uic' { - $tempRoot = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("UE-Test-" + [Guid]::NewGuid())) + $tempRoot = New-Item -ItemType Directory -Path (Join-Path $script:TempRoot ("UE-Test-" + [Guid]::NewGuid())) try { # Arrange environment structure $addins = Join-Path $tempRoot.FullName 'addins' From 2250503bbfe88436f5952aca7562d922e574516f Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 12:29:49 +0100 Subject: [PATCH 023/102] Make addin template copy cross-platform --- scripts/UECustomizationFunctions.psm1 | 46 +++++++++++++++++++-------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/scripts/UECustomizationFunctions.psm1 b/scripts/UECustomizationFunctions.psm1 index 3c5c361..5c8ae82 100644 --- a/scripts/UECustomizationFunctions.psm1 +++ b/scripts/UECustomizationFunctions.psm1 @@ -808,20 +808,38 @@ function Invoke-EnvironmentCleanup { $excludeDirs = @('bin', 'obj', '.vs', 'logs', 'log') $excludeFiles = @('*.user', '*.suo', '*.rsuser', '*.userprefs', '*.pdb', '*.ipdb', '*.iobj', '*.obj', '*.pch', '*.log', '*.tlog', '*.tmp', '*.cache', '*.ilk') - $xdArgs = $excludeDirs | ForEach-Object { '/XD', $_ } - $xfArgs = $excludeFiles | ForEach-Object { '/XF', $_ } - $robocopyArgs = @($sourceTemplateDir, $targetAddinDir, '/E', '/NFL', '/NDL', '/NJH', '/NJS', '/NC', '/NS', '/R:2', '/W:1') + $xdArgs + $xfArgs - & robocopy @robocopyArgs | Out-Null - $rc = $LASTEXITCODE - if ($rc -ge 8) { - $exception = [System.InvalidOperationException]::new("Robocopy failed with exit code $rc while copying template.") - $errorRecord = [System.Management.Automation.ErrorRecord]::new( - $exception, - 'RobocopyFailed', - [System.Management.Automation.ErrorCategory]::InvalidOperation, - $rc - ) - $PSCmdlet.ThrowTerminatingError($errorRecord) + $robocopyCommand = Get-Command -Name 'robocopy' -ErrorAction SilentlyContinue + if ($robocopyCommand) { + $xdArgs = $excludeDirs | ForEach-Object { '/XD', $_ } + $xfArgs = $excludeFiles | ForEach-Object { '/XF', $_ } + $robocopyArgs = @($sourceTemplateDir, $targetAddinDir, '/E', '/NFL', '/NDL', '/NJH', '/NJS', '/NC', '/NS', '/R:2', '/W:1') + $xdArgs + $xfArgs + & $robocopyCommand.Source @robocopyArgs | Out-Null + $rc = $LASTEXITCODE + if ($rc -ge 8) { + $exception = [System.InvalidOperationException]::new("Robocopy failed with exit code $rc while copying template.") + $errorRecord = [System.Management.Automation.ErrorRecord]::new( + $exception, + 'RobocopyFailed', + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $rc + ) + $PSCmdlet.ThrowTerminatingError($errorRecord) + } + } + else { + Copy-Item -Path $sourceTemplateDir -Destination $targetAddinDir -Recurse -Force + + foreach ($excludedDir in $excludeDirs) { + Get-ChildItem -Path $targetAddinDir -Directory -Recurse -Force -ErrorAction SilentlyContinue | + Where-Object { $_.Name -eq $excludedDir } | + Remove-Item -Recurse -Force -ErrorAction SilentlyContinue + } + + foreach ($excludedPattern in $excludeFiles) { + Get-ChildItem -Path $targetAddinDir -File -Recurse -Force -ErrorAction SilentlyContinue | + Where-Object { $_.Name -like $excludedPattern } | + Remove-Item -Force -ErrorAction SilentlyContinue + } } $itemsToRename = Get-ChildItem -Path $targetAddinDir -Recurse -Force | Where-Object { $_.Name -like "*$Template*" } From 3dce5deeb7e238a090ed0a317917d0510e11d633 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 13:10:11 +0100 Subject: [PATCH 024/102] Install dotnet inside security container --- azure-pipelines.yml | 104 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 88 insertions(+), 16 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e0e8362..bf47385 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -112,30 +112,90 @@ stages: - checkout: Templates fetchDepth: "1" + - bash: | + set -euo pipefail + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -y curl ca-certificates + + curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh + chmod +x /tmp/dotnet-install.sh + /tmp/dotnet-install.sh --channel 9.0 --install-dir /usr/share/dotnet + ln -sf /usr/share/dotnet/dotnet /usr/local/bin/dotnet + dotnet --info + displayName: Install .NET SDK + retryCountOnTaskFailure: 3 + + - bash: | + # Install Python 3 and pip in the Linux container + set -euo pipefail + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -y python3 python3-pip + + if ! command -v python >/dev/null 2>&1; then + ln -sf /usr/bin/python3 /usr/local/bin/python + fi + displayName: Install Python 3 and pip + retryCountOnTaskFailure: 3 + + - pwsh: | + Write-Host "Verifying Python installation..." + $pythonVersion = python --version 2>&1 + if ($LASTEXITCODE -ne 0) { + throw "Python verification failed: $pythonVersion" + } + + $pipVersion = python -m pip --version 2>&1 + if ($LASTEXITCODE -ne 0) { + throw "Pip verification failed: $pipVersion" + } + + Write-Host "Python version: $pythonVersion" + Write-Host "Pip version: $pipVersion" + displayName: Verify Python and pip + - pwsh: | $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's\UnifiedEngineeringCustomizationFramework' & (Join-Path $repoRoot 'scripts\Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release displayName: Build template and example projects with fake UE references + - pwsh: | + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's\UnifiedEngineeringCustomizationFramework' + $paths = @{ + HasShimSources = Test-Path -LiteralPath (Join-Path $repoRoot 'shims') + HasShimManifest = Test-Path -LiteralPath (Join-Path $repoRoot 'shims\shim-projects.psd1') + HasGeneratedShimProjects = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp\generated-shims') + HasShimAssemblies = Test-Path -LiteralPath (Join-Path $repoRoot 'shims\artifacts\Release') + } + + foreach ($entry in $paths.GetEnumerator()) { + $value = if ($entry.Value) { 'true' } else { 'false' } + Write-Host "##vso[task.setvariable variable=$($entry.Key)]$value" + Write-Host "$($entry.Key): $value" + } + displayName: Detect shim artifacts + condition: always() + - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\shims artifact: ShimSources displayName: Publish shim sources - condition: always() + condition: and(always(), eq(variables.HasShimSources, 'true')) - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\shims\shim-projects.psd1 artifact: ShimManifest displayName: Publish shim manifest - condition: always() + condition: and(always(), eq(variables.HasShimManifest, 'true')) - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\.tmp\generated-shims artifact: GeneratedShimProjects displayName: Publish generated shim project files - condition: always() + condition: and(always(), eq(variables.HasGeneratedShimProjects, 'true')) - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\shims\artifacts\Release artifact: ShimAssemblies displayName: Publish compiled shim assemblies - condition: always() + condition: and(always(), eq(variables.HasShimAssemblies, 'true')) - pwsh: | $pwshPath = (Get-Command pwsh -ErrorAction Stop).Source @@ -213,11 +273,6 @@ stages: AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) POLARIS_HOME: $(Pipeline.Workspace)\Coverity-CLI - - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\.blackduck - artifact: Coverity - displayName: Publish Coverity artifacts - condition: always() - - pwsh: | $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` @@ -238,11 +293,6 @@ stages: Write-Host "Black Duck project version: $versionName" displayName: Calculate Black Duck version - - template: steps/install-python.yml@Templates - parameters: - pythonVersion: 3.11 - condition: not(canceled()) - - task: ArchiveFiles@2 displayName: Archive sources and build outputs inputs: @@ -280,12 +330,34 @@ stages: --detect.wait.for.results=true --detect.wait.for.results.timeout=6000000 + - pwsh: | + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's\UnifiedEngineeringCustomizationFramework' + $artifactStaging = $env:BUILD_ARTIFACTSTAGINGDIRECTORY + $paths = @{ + HasCoverityArtifacts = Test-Path -LiteralPath (Join-Path $repoRoot '.blackduck') + HasBlackDuckLogs = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-logs') + HasBlackDuckInput = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-input.zip') + } + + foreach ($entry in $paths.GetEnumerator()) { + $value = if ($entry.Value) { 'true' } else { 'false' } + Write-Host "##vso[task.setvariable variable=$($entry.Key)]$value" + Write-Host "$($entry.Key): $value" + } + displayName: Detect security artifacts + condition: always() + + - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\.blackduck + artifact: Coverity + displayName: Publish Coverity artifacts + condition: and(always(), eq(variables.HasCoverityArtifacts, 'true')) + - publish: $(Build.ArtifactStagingDirectory)\blackduck-logs artifact: BlackDuckLogs displayName: Publish Black Duck logs - condition: always() + condition: and(always(), eq(variables.HasBlackDuckLogs, 'true')) - publish: $(Build.ArtifactStagingDirectory)\blackduck-input.zip artifact: BlackDuckInput displayName: Publish Black Duck input archive - condition: always() + condition: and(always(), eq(variables.HasBlackDuckInput, 'true')) From c18a4da0dbf8276f15157dfe2dec1b760e6df955 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 13:40:06 +0100 Subject: [PATCH 025/102] Use non-root dotnet install in security job --- azure-pipelines.yml | 49 +++++++++------------------------------------ 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bf47385..2d44705 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -112,48 +112,19 @@ stages: - checkout: Templates fetchDepth: "1" - - bash: | - set -euo pipefail - export DEBIAN_FRONTEND=noninteractive - apt-get update - apt-get install -y curl ca-certificates - - curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh - chmod +x /tmp/dotnet-install.sh - /tmp/dotnet-install.sh --channel 9.0 --install-dir /usr/share/dotnet - ln -sf /usr/share/dotnet/dotnet /usr/local/bin/dotnet - dotnet --info - displayName: Install .NET SDK - retryCountOnTaskFailure: 3 - - - bash: | - # Install Python 3 and pip in the Linux container - set -euo pipefail - export DEBIAN_FRONTEND=noninteractive - apt-get update - apt-get install -y python3 python3-pip - - if ! command -v python >/dev/null 2>&1; then - ln -sf /usr/bin/python3 /usr/local/bin/python - fi - displayName: Install Python 3 and pip - retryCountOnTaskFailure: 3 - - pwsh: | - Write-Host "Verifying Python installation..." - $pythonVersion = python --version 2>&1 - if ($LASTEXITCODE -ne 0) { - throw "Python verification failed: $pythonVersion" - } + $dotnetRoot = Join-Path $env:PIPELINE_WORKSPACE 'dotnet' + $installScript = Join-Path $env:PIPELINE_WORKSPACE 'dotnet-install.ps1' - $pipVersion = python -m pip --version 2>&1 - if ($LASTEXITCODE -ne 0) { - throw "Pip verification failed: $pipVersion" - } + Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile $installScript + & $installScript -Channel '9.0' -InstallDir $dotnetRoot + + Write-Host "##vso[task.prependpath]$dotnetRoot" + Write-Host "##vso[task.setvariable variable=DOTNET_ROOT]$dotnetRoot" - Write-Host "Python version: $pythonVersion" - Write-Host "Pip version: $pipVersion" - displayName: Verify Python and pip + & (Join-Path $dotnetRoot 'dotnet') --info + displayName: Install .NET SDK + retryCountOnTaskFailure: 3 - pwsh: | $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's\UnifiedEngineeringCustomizationFramework' From c8504d20fae11941735fff37ea7333b7973e5124 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 13:50:02 +0100 Subject: [PATCH 026/102] Specify dotnet install architecture --- azure-pipelines.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2d44705..ef6634d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -115,9 +115,15 @@ stages: - pwsh: | $dotnetRoot = Join-Path $env:PIPELINE_WORKSPACE 'dotnet' $installScript = Join-Path $env:PIPELINE_WORKSPACE 'dotnet-install.ps1' + $processArchitecture = [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant() + $dotnetArchitecture = switch ($processArchitecture) { + 'x64' { 'x64' } + 'arm64' { 'arm64' } + default { throw "Unsupported .NET install architecture: $processArchitecture" } + } Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile $installScript - & $installScript -Channel '9.0' -InstallDir $dotnetRoot + & $installScript -Channel '9.0' -Architecture $dotnetArchitecture -InstallDir $dotnetRoot Write-Host "##vso[task.prependpath]$dotnetRoot" Write-Host "##vso[task.setvariable variable=DOTNET_ROOT]$dotnetRoot" From f2f6c5029fed4745c163b45f40c87240133a9bd6 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 15:27:34 +0100 Subject: [PATCH 027/102] Align security job with Templates dotnet container --- azure-pipelines.yml | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ef6634d..8023eee 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -102,7 +102,7 @@ stages: demands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/powershell:7.4-ubuntu-22.04 + container: mcr.microsoft.com/dotnet/sdk:9.0 workspace: clean: all steps: @@ -113,24 +113,19 @@ stages: fetchDepth: "1" - pwsh: | - $dotnetRoot = Join-Path $env:PIPELINE_WORKSPACE 'dotnet' - $installScript = Join-Path $env:PIPELINE_WORKSPACE 'dotnet-install.ps1' - $processArchitecture = [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant() - $dotnetArchitecture = switch ($processArchitecture) { - 'x64' { 'x64' } - 'arm64' { 'arm64' } - default { throw "Unsupported .NET install architecture: $processArchitecture" } - } - - Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile $installScript - & $installScript -Channel '9.0' -Architecture $dotnetArchitecture -InstallDir $dotnetRoot - - Write-Host "##vso[task.prependpath]$dotnetRoot" - Write-Host "##vso[task.setvariable variable=DOTNET_ROOT]$dotnetRoot" + dotnet --list-sdks + displayName: List globally installed .NET SDKs - & (Join-Path $dotnetRoot 'dotnet') --info - displayName: Install .NET SDK + - task: UseDotNet@2 + displayName: Ensure .NET 9 SDK for Linux retryCountOnTaskFailure: 3 + inputs: + packageType: sdk + version: 9.0.x + + - pwsh: | + dotnet --info + displayName: Show .NET SDK info - pwsh: | $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's\UnifiedEngineeringCustomizationFramework' From a3ced0ca3009bbe2d382f8602c4d596de84c0383 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 16:04:57 +0100 Subject: [PATCH 028/102] Bootstrap net481 reference assemblies for fake compile --- scripts/Build-AddIns.ps1 | 8 ++++ scripts/Invoke-FakeUECompile.ps1 | 63 ++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/scripts/Build-AddIns.ps1 b/scripts/Build-AddIns.ps1 index 6031bf2..a329f78 100644 --- a/scripts/Build-AddIns.ps1 +++ b/scripts/Build-AddIns.ps1 @@ -44,6 +44,14 @@ foreach ($proj in $projects) { '-nologo' ) + if ($env:FrameworkPathOverride) { + $buildArgs += "-p:FrameworkPathOverride=$($env:FrameworkPathOverride)" + } + + if ($env:TargetFrameworkRootPath) { + $buildArgs += "-p:TargetFrameworkRootPath=$($env:TargetFrameworkRootPath)" + } + if ($Rebuild) { $buildArgs += '-t:Rebuild' } diff --git a/scripts/Invoke-FakeUECompile.ps1 b/scripts/Invoke-FakeUECompile.ps1 index 3885ac4..c1111fe 100644 --- a/scripts/Invoke-FakeUECompile.ps1 +++ b/scripts/Invoke-FakeUECompile.ps1 @@ -40,6 +40,45 @@ $resolvedGeneratedShimProjectsPath = [System.IO.Path]::GetFullPath($GeneratedShi $shimDirectoryBuildPropsPath = Join-Path $resolvedShimProjectsPath 'Directory.Build.props' $buildScriptPath = Join-Path $PSScriptRoot 'Build-AddIns.ps1' +function Ensure-NetFrameworkReferenceAssemblies { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$WorkspaceRoot + ) + + $packageId = 'Microsoft.NETFramework.ReferenceAssemblies.net481' + $packageVersion = '1.0.3' + $packageRoot = Join-Path $WorkspaceRoot '.tmp\netfx-reference-assemblies' + $packageDir = Join-Path $packageRoot "$packageId.$packageVersion" + $packageArchive = Join-Path $packageRoot "$packageId.$packageVersion.nupkg" + $referenceDir = Join-Path $packageDir 'build\.NETFramework\v4.8.1' + $frameworkRoot = Join-Path $packageDir 'build\' + + if (-not (Test-Path -LiteralPath $referenceDir)) { + $null = New-Item -ItemType Directory -Path $packageRoot -Force + + $packageUrl = "https://www.nuget.org/api/v2/package/$packageId/$packageVersion" + Write-Host ("Downloading .NET Framework reference assemblies from {0}" -f $packageUrl) -ForegroundColor Cyan + Invoke-WebRequest -Uri $packageUrl -OutFile $packageArchive + + if (Test-Path -LiteralPath $packageDir) { + Remove-Item -LiteralPath $packageDir -Recurse -Force + } + + Expand-Archive -Path $packageArchive -DestinationPath $packageDir -Force + } + + if (-not (Test-Path -LiteralPath $referenceDir)) { + throw "Reference assembly directory not found after package extraction: $referenceDir" + } + + return [PSCustomObject]@{ + FrameworkPathOverride = $referenceDir + TargetFrameworkRootPath = $frameworkRoot + } +} + $shimManifest = Import-PowerShellDataFile -Path $resolvedShimManifestPath $shimProjects = if ($shimManifest.ContainsKey('Projects')) { $shimManifest.Projects @@ -120,6 +159,8 @@ foreach ($project in $projects) { $resolvedShimOutputPath = (Resolve-Path $ShimOutputPath).Path $previousReferencePath = $env:UNIFIED_ENGINEERING_REFERENCE_PATH +$previousFrameworkPathOverride = $env:FrameworkPathOverride +$previousTargetFrameworkRootPath = $env:TargetFrameworkRootPath $targetDirectories = foreach ($entry in $Directory) { foreach ($candidate in ($entry -split ',')) { $trimmed = $candidate.Trim().Trim([char[]]@("'", '"')) @@ -132,6 +173,14 @@ $targetDirectories = foreach ($entry in $Directory) { try { $env:UNIFIED_ENGINEERING_REFERENCE_PATH = $resolvedShimOutputPath + if (-not $IsWindows) { + $netFrameworkReferences = Ensure-NetFrameworkReferenceAssemblies -WorkspaceRoot $repoRoot + $env:FrameworkPathOverride = $netFrameworkReferences.FrameworkPathOverride + $env:TargetFrameworkRootPath = $netFrameworkReferences.TargetFrameworkRootPath + + Write-Host ("Using .NET Framework reference assemblies from: {0}" -f $env:FrameworkPathOverride) -ForegroundColor Yellow + } + if ($env:TF_BUILD) { Write-Host ("##vso[task.setvariable variable=UNIFIED_ENGINEERING_REFERENCE_PATH]{0}" -f $resolvedShimOutputPath) } @@ -155,4 +204,18 @@ finally { else { $env:UNIFIED_ENGINEERING_REFERENCE_PATH = $previousReferencePath } + + if ([string]::IsNullOrWhiteSpace($previousFrameworkPathOverride)) { + Remove-Item Env:FrameworkPathOverride -ErrorAction SilentlyContinue + } + else { + $env:FrameworkPathOverride = $previousFrameworkPathOverride + } + + if ([string]::IsNullOrWhiteSpace($previousTargetFrameworkRootPath)) { + Remove-Item Env:TargetFrameworkRootPath -ErrorAction SilentlyContinue + } + else { + $env:TargetFrameworkRootPath = $previousTargetFrameworkRootPath + } } \ No newline at end of file From 8667dce744067f73af2b39f26bd18a42917e38a5 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 16:12:40 +0100 Subject: [PATCH 029/102] Normalize Linux security stage paths --- azure-pipelines.yml | 50 ++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8023eee..5b99626 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -128,17 +128,17 @@ stages: displayName: Show .NET SDK info - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's\UnifiedEngineeringCustomizationFramework' + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' & (Join-Path $repoRoot 'scripts\Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release displayName: Build template and example projects with fake UE references - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's\UnifiedEngineeringCustomizationFramework' + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' $paths = @{ HasShimSources = Test-Path -LiteralPath (Join-Path $repoRoot 'shims') - HasShimManifest = Test-Path -LiteralPath (Join-Path $repoRoot 'shims\shim-projects.psd1') - HasGeneratedShimProjects = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp\generated-shims') - HasShimAssemblies = Test-Path -LiteralPath (Join-Path $repoRoot 'shims\artifacts\Release') + HasShimManifest = Test-Path -LiteralPath (Join-Path $repoRoot 'shims/shim-projects.psd1') + HasGeneratedShimProjects = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shims') + HasShimAssemblies = Test-Path -LiteralPath (Join-Path $repoRoot 'shims/artifacts/Release') } foreach ($entry in $paths.GetEnumerator()) { @@ -149,30 +149,30 @@ stages: displayName: Detect shim artifacts condition: always() - - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\shims + - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/shims artifact: ShimSources displayName: Publish shim sources condition: and(always(), eq(variables.HasShimSources, 'true')) - - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\shims\shim-projects.psd1 + - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/shims/shim-projects.psd1 artifact: ShimManifest displayName: Publish shim manifest condition: and(always(), eq(variables.HasShimManifest, 'true')) - - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\.tmp\generated-shims + - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/.tmp/generated-shims artifact: GeneratedShimProjects displayName: Publish generated shim project files condition: and(always(), eq(variables.HasGeneratedShimProjects, 'true')) - - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\shims\artifacts\Release + - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/shims/artifacts/Release artifact: ShimAssemblies displayName: Publish compiled shim assemblies condition: and(always(), eq(variables.HasShimAssemblies, 'true')) - pwsh: | $pwshPath = (Get-Command pwsh -ErrorAction Stop).Source - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's\UnifiedEngineeringCustomizationFramework' - $fakeCompileScriptPath = Join-Path $repoRoot 'scripts\Invoke-FakeUECompile.ps1' + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' + $fakeCompileScriptPath = Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1' $branchName = "$env:BUILD_SOURCEBRANCH" -replace '^refs/heads/', '' $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') @@ -223,11 +223,11 @@ stages: retryCountOnTaskFailure: 3 inputs: key: "\"CoverityCLI\" | \"$(CoverityCliVersion)\" | \"v1\"" - path: $(Pipeline.Workspace)\Coverity-CLI + path: $(Pipeline.Workspace)/Coverity-CLI cacheHitVar: COVERITY_CLI_RESTORED - pwsh: | - $toolPath = "$(Pipeline.Workspace)\Coverity-CLI" + $toolPath = "$(Pipeline.Workspace)/Coverity-CLI" if (-not (Test-Path -Path $toolPath)) { New-Item -ItemType Directory -Path $toolPath | Out-Null } @@ -238,12 +238,12 @@ stages: inputs: polarisService: Coverity on Polaris polarisCommand: > - -c $(Pipeline.Workspace)\polaris.yml + -c $(Pipeline.Workspace)/polaris.yml analyze -w env: AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) - POLARIS_HOME: $(Pipeline.Workspace)\Coverity-CLI + POLARIS_HOME: $(Pipeline.Workspace)/Coverity-CLI - pwsh: | $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') @@ -268,10 +268,10 @@ stages: - task: ArchiveFiles@2 displayName: Archive sources and build outputs inputs: - rootFolderOrFile: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework + rootFolderOrFile: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework includeRootFolder: true archiveType: zip - archiveFile: $(Build.ArtifactStagingDirectory)\blackduck-input.zip + archiveFile: $(Build.ArtifactStagingDirectory)/blackduck-input.zip replaceExistingArchive: true verbose: true @@ -281,15 +281,15 @@ stages: inputs: BlackDuckScaService: BlackDuckScanner DetectVersion: latest - DetectFolder: $(Pipeline.Workspace)\detect + DetectFolder: $(Pipeline.Workspace)/detect DetectArguments: | --detect.project.name=$(Build.Repository.Name) --detect.project.version.name=$(BlackDuckProjectVersion) --detect.project.group.name=DabaconProductsGroup - --detect.source.path=$(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework - --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)\blackduck-input.zip + --detect.source.path=$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework + --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)/blackduck-input.zip --detect.cleanup=true - --detect.output.path=$(Build.ArtifactStagingDirectory)\blackduck-logs + --detect.output.path=$(Build.ArtifactStagingDirectory)/blackduck-logs --detect.detector.search.depth=10 --detect.detector.search.continue=true --detect.blackduck.signature.scanner.upload.source.mode=true @@ -303,7 +303,7 @@ stages: --detect.wait.for.results.timeout=6000000 - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's\UnifiedEngineeringCustomizationFramework' + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' $artifactStaging = $env:BUILD_ARTIFACTSTAGINGDIRECTORY $paths = @{ HasCoverityArtifacts = Test-Path -LiteralPath (Join-Path $repoRoot '.blackduck') @@ -319,17 +319,17 @@ stages: displayName: Detect security artifacts condition: always() - - publish: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework\.blackduck + - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/.blackduck artifact: Coverity displayName: Publish Coverity artifacts condition: and(always(), eq(variables.HasCoverityArtifacts, 'true')) - - publish: $(Build.ArtifactStagingDirectory)\blackduck-logs + - publish: $(Build.ArtifactStagingDirectory)/blackduck-logs artifact: BlackDuckLogs displayName: Publish Black Duck logs condition: and(always(), eq(variables.HasBlackDuckLogs, 'true')) - - publish: $(Build.ArtifactStagingDirectory)\blackduck-input.zip + - publish: $(Build.ArtifactStagingDirectory)/blackduck-input.zip artifact: BlackDuckInput displayName: Publish Black Duck input archive condition: and(always(), eq(variables.HasBlackDuckInput, 'true')) From 39b684e8553a1c6646175e49b6a9b755315e542d Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 16:21:55 +0100 Subject: [PATCH 030/102] Use stable pwsh path for Polaris capture --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5b99626..0d444ac 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -170,7 +170,7 @@ stages: condition: and(always(), eq(variables.HasShimAssemblies, 'true')) - pwsh: | - $pwshPath = (Get-Command pwsh -ErrorAction Stop).Source + $pwshPath = if ($IsLinux) { '/usr/bin/pwsh' } else { (Get-Command pwsh -ErrorAction Stop).Source } $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' $fakeCompileScriptPath = Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1' From 9f62355d7be0f4fdfff40846ff30e7bcd90f2a06 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 16:30:37 +0100 Subject: [PATCH 031/102] Use PowerShell zip for Black Duck input --- azure-pipelines.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0d444ac..f5f7d05 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -265,15 +265,17 @@ stages: Write-Host "Black Duck project version: $versionName" displayName: Calculate Black Duck version - - task: ArchiveFiles@2 + - pwsh: | + $sourceRoot = "$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework" + $archivePath = "$(Build.ArtifactStagingDirectory)/blackduck-input.zip" + + if (Test-Path -LiteralPath $archivePath) { + Remove-Item -LiteralPath $archivePath -Force + } + + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::CreateFromDirectory($sourceRoot, $archivePath) displayName: Archive sources and build outputs - inputs: - rootFolderOrFile: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework - includeRootFolder: true - archiveType: zip - archiveFile: $(Build.ArtifactStagingDirectory)/blackduck-input.zip - replaceExistingArchive: true - verbose: true - task: BlackDuckDetectTask@10 displayName: Black Duck scan From ee43bf4be36cd82c5417c0b2ad172ca7b610d3a0 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 16:44:24 +0100 Subject: [PATCH 032/102] Split Coverity and Black Duck jobs --- azure-pipelines.yml | 78 +++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f5f7d05..ed31eab 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -94,8 +94,8 @@ stages: dependsOn: HostedTests condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: - - job: CoverityAndBlackDuck - displayName: Coverity And Black Duck + - job: CoverityScan + displayName: Coverity Scan timeoutInMinutes: "180" pool: name: Technology-Microsoft-Hosted-Ubuntu @@ -245,6 +245,45 @@ stages: AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) POLARIS_HOME: $(Pipeline.Workspace)/Coverity-CLI + - pwsh: | + - pwsh: | + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' + $paths = @{ + HasCoverityArtifacts = Test-Path -LiteralPath (Join-Path $repoRoot '.blackduck') + } + + foreach ($entry in $paths.GetEnumerator()) { + $value = if ($entry.Value) { 'true' } else { 'false' } + Write-Host "##vso[task.setvariable variable=$($entry.Key)]$value" + Write-Host "$($entry.Key): $value" + } + displayName: Detect security artifacts + condition: always() + + - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/.blackduck + artifact: Coverity + displayName: Publish Coverity artifacts + condition: and(always(), eq(variables.HasCoverityArtifacts, 'true')) + + - job: BlackDuckScan + displayName: Black Duck Scan + dependsOn: CoverityScan + condition: succeededOrFailed() + timeoutInMinutes: "120" + pool: + name: Technology-Microsoft-Hosted-Windows + demands: + - ImageOverride -equals windows-latest + container: mcr.microsoft.com/dotnet/sdk:9.0-windowsservercore-ltsc2022 + workspace: + clean: all + steps: + - checkout: self + fetchDepth: "0" + + - checkout: Templates + fetchDepth: "1" + - pwsh: | $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` @@ -265,17 +304,15 @@ stages: Write-Host "Black Duck project version: $versionName" displayName: Calculate Black Duck version - - pwsh: | - $sourceRoot = "$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework" - $archivePath = "$(Build.ArtifactStagingDirectory)/blackduck-input.zip" - - if (Test-Path -LiteralPath $archivePath) { - Remove-Item -LiteralPath $archivePath -Force - } - - Add-Type -AssemblyName System.IO.Compression.FileSystem - [System.IO.Compression.ZipFile]::CreateFromDirectory($sourceRoot, $archivePath) + - task: ArchiveFiles@2 displayName: Archive sources and build outputs + inputs: + rootFolderOrFile: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework + includeRootFolder: true + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)\blackduck-input.zip + replaceExistingArchive: true + verbose: true - task: BlackDuckDetectTask@10 displayName: Black Duck scan @@ -283,15 +320,15 @@ stages: inputs: BlackDuckScaService: BlackDuckScanner DetectVersion: latest - DetectFolder: $(Pipeline.Workspace)/detect + DetectFolder: $(Pipeline.Workspace)\detect DetectArguments: | --detect.project.name=$(Build.Repository.Name) --detect.project.version.name=$(BlackDuckProjectVersion) --detect.project.group.name=DabaconProductsGroup - --detect.source.path=$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework - --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)/blackduck-input.zip + --detect.source.path=$(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework + --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)\blackduck-input.zip --detect.cleanup=true - --detect.output.path=$(Build.ArtifactStagingDirectory)/blackduck-logs + --detect.output.path=$(Build.ArtifactStagingDirectory)\blackduck-logs --detect.detector.search.depth=10 --detect.detector.search.continue=true --detect.blackduck.signature.scanner.upload.source.mode=true @@ -305,10 +342,8 @@ stages: --detect.wait.for.results.timeout=6000000 - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' $artifactStaging = $env:BUILD_ARTIFACTSTAGINGDIRECTORY $paths = @{ - HasCoverityArtifacts = Test-Path -LiteralPath (Join-Path $repoRoot '.blackduck') HasBlackDuckLogs = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-logs') HasBlackDuckInput = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-input.zip') } @@ -318,14 +353,9 @@ stages: Write-Host "##vso[task.setvariable variable=$($entry.Key)]$value" Write-Host "$($entry.Key): $value" } - displayName: Detect security artifacts + displayName: Detect Black Duck artifacts condition: always() - - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/.blackduck - artifact: Coverity - displayName: Publish Coverity artifacts - condition: and(always(), eq(variables.HasCoverityArtifacts, 'true')) - - publish: $(Build.ArtifactStagingDirectory)/blackduck-logs artifact: BlackDuckLogs displayName: Publish Black Duck logs From 16901633d244998f3f8af0b3747169173db57055 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 16:56:39 +0100 Subject: [PATCH 033/102] Add Ubuntu Black Duck comparison job --- azure-pipelines.yml | 105 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ed31eab..80cfb21 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -245,7 +245,6 @@ stages: AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) POLARIS_HOME: $(Pipeline.Workspace)/Coverity-CLI - - pwsh: | - pwsh: | $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' $paths = @{ @@ -365,3 +364,107 @@ stages: artifact: BlackDuckInput displayName: Publish Black Duck input archive condition: and(always(), eq(variables.HasBlackDuckInput, 'true')) + + - job: BlackDuckScanUbuntu + displayName: Black Duck Scan Ubuntu + dependsOn: CoverityScan + condition: succeededOrFailed() + timeoutInMinutes: "120" + pool: + name: Technology-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/dotnet/sdk:9.0 + workspace: + clean: all + steps: + - checkout: self + fetchDepth: "0" + + - checkout: Templates + fetchDepth: "1" + + - pwsh: | + $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') + $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` + ? "@$($templatesBranch)" ` + : '' + + if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { + $versionName = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)-ubuntu" + } + else { + $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' + $versionName = "$branchName-$($env:BUILD_BUILDID)-ubuntu" + } + + $versionName = "$versionName$templatesPostfix" + + Write-Host "##vso[task.setvariable variable=BlackDuckProjectVersion]$versionName" + Write-Host "Black Duck project version: $versionName" + displayName: Calculate Black Duck version (Ubuntu) + + - pwsh: | + $sourceRoot = "$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework" + $archivePath = "$(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip" + + if (Test-Path -LiteralPath $archivePath) { + Remove-Item -LiteralPath $archivePath -Force + } + + Compress-Archive -Path $sourceRoot -DestinationPath $archivePath -CompressionLevel Optimal + Write-Host "Created archive at $archivePath" + displayName: Archive sources and build outputs (Ubuntu) + + - task: BlackDuckDetectTask@10 + displayName: Black Duck scan (Ubuntu) + retryCountOnTaskFailure: 0 + inputs: + BlackDuckScaService: BlackDuckScanner + DetectVersion: latest + DetectFolder: $(Pipeline.Workspace)/detect-ubuntu + DetectArguments: | + --detect.project.name=$(Build.Repository.Name) + --detect.project.version.name=$(BlackDuckProjectVersion) + --detect.project.group.name=DabaconProductsGroup + --detect.source.path=$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework + --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip + --detect.cleanup=true + --detect.output.path=$(Build.ArtifactStagingDirectory)/blackduck-logs-ubuntu + --detect.detector.search.depth=10 + --detect.detector.search.continue=true + --detect.blackduck.signature.scanner.upload.source.mode=true + --detect.blackduck.signature.scanner.copyright.search=true + --detect.blackduck.signature.scanner.license.search=true + --logging.level.detect=DEBUG + --detect.diagnostic=true + --detect.timeout=1800 + --detect.blackduck.signature.scanner.arguments= --fs-wait-time=90 + --detect.wait.for.results=true + --detect.wait.for.results.timeout=6000000 + + - pwsh: | + $artifactStaging = $env:BUILD_ARTIFACTSTAGINGDIRECTORY + $paths = @{ + HasBlackDuckLogsUbuntu = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-logs-ubuntu') + HasBlackDuckInputUbuntu = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-input-ubuntu.zip') + } + + foreach ($entry in $paths.GetEnumerator()) { + $value = if ($entry.Value) { 'true' } else { 'false' } + Write-Host "##vso[task.setvariable variable=$($entry.Key)]$value" + Write-Host "$($entry.Key): $value" + } + displayName: Detect Black Duck artifacts (Ubuntu) + condition: always() + + - publish: $(Build.ArtifactStagingDirectory)/blackduck-logs-ubuntu + artifact: BlackDuckLogsUbuntu + displayName: Publish Black Duck logs (Ubuntu) + condition: and(always(), eq(variables.HasBlackDuckLogsUbuntu, 'true')) + + - publish: $(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip + artifact: BlackDuckInputUbuntu + displayName: Publish Black Duck input archive (Ubuntu) + condition: and(always(), eq(variables.HasBlackDuckInputUbuntu, 'true')) From 548480f4d294f1cbb767050032d54d2cf6f1a4fe Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 17:08:20 +0100 Subject: [PATCH 034/102] Align Black Duck path with Templates --- azure-pipelines.yml | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 80cfb21..013a224 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -22,6 +22,9 @@ parameters: - name: EnableSecurityValidation type: string default: "false" + - name: EnableUbuntuBlackDuckComparison + type: string + default: "false" variables: - template: variables.yml@Templates @@ -283,6 +286,42 @@ stages: - checkout: Templates fetchDepth: "1" + - pwsh: | + $workdir = "$(Pipeline.Workspace)\JDK" + New-Item -Path $workdir -ItemType Directory -Force | Out-Null + + $url = "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u452-b09/OpenJDK8U-jdk_x64_windows_hotspot_8u452b09.zip" + $zipPath = "$workdir\Temurin8.zip" + + Write-Host "Invoke-WebRequest -Uri $url -OutFile $zipPath" + [Net.ServicePointManager]::SecurityProtocol = "Tls12" + Invoke-WebRequest -Uri $url -OutFile $zipPath + + Write-Host "Expand-Archive -LiteralPath $zipPath -DestinationPath $workdir -Force" + Expand-Archive -LiteralPath $zipPath -DestinationPath $workdir -Force + displayName: Download Temurin JDK 8 + + - pwsh: | + $jdkFolder = Get-ChildItem -Path "$(Pipeline.Workspace)\JDK" -Directory | + Where-Object { $_.Name -like 'jdk8*' } | + Select-Object -First 1 + + if (-not $jdkFolder) { + Write-Error "Cannot locate a jdk8* folder under $(Pipeline.Workspace)\JDK" + exit 1 + } + + $javaHome = $jdkFolder.FullName + Write-Host "##vso[task.setvariable variable=JAVA_HOME]$javaHome" + Write-Host "##vso[task.setvariable variable=PATH]$javaHome\bin;$($env:PATH)" + Write-Host "JAVA_HOME is now set to $javaHome" + displayName: Configure JAVA_HOME for Temurin JDK 8 + + - pwsh: | + Write-Host "Verifying Java..." + java -version + displayName: Verify Java Installation + - pwsh: | $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` @@ -368,7 +407,7 @@ stages: - job: BlackDuckScanUbuntu displayName: Black Duck Scan Ubuntu dependsOn: CoverityScan - condition: succeededOrFailed() + condition: and(succeededOrFailed(), ${{ eq(parameters.EnableUbuntuBlackDuckComparison, 'true') }}) timeoutInMinutes: "120" pool: name: Technology-Microsoft-Hosted-Ubuntu From eb19f757ef40127033147d5e31c9415ffc365ad7 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 17:59:41 +0100 Subject: [PATCH 035/102] Remove invalid Black Duck project group --- azure-pipelines.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 013a224..46dc15f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -362,7 +362,6 @@ stages: DetectArguments: | --detect.project.name=$(Build.Repository.Name) --detect.project.version.name=$(BlackDuckProjectVersion) - --detect.project.group.name=DabaconProductsGroup --detect.source.path=$(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)\blackduck-input.zip --detect.cleanup=true @@ -466,7 +465,6 @@ stages: DetectArguments: | --detect.project.name=$(Build.Repository.Name) --detect.project.version.name=$(BlackDuckProjectVersion) - --detect.project.group.name=DabaconProductsGroup --detect.source.path=$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip --detect.cleanup=true From 6087ce7930b08ee860d2efbdf09d6cc290a5fedd Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 18:37:58 +0100 Subject: [PATCH 036/102] Align Black Duck flow with Templates --- azure-pipelines.yml | 224 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 180 insertions(+), 44 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 46dc15f..1080c39 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -97,6 +97,170 @@ stages: dependsOn: HostedTests condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: + - job: BlackDuckInitialisation + displayName: BlackDuck Initialise + timeoutInMinutes: "10" + pool: + name: Technology-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/dotnet/sdk:9.0 + workspace: + clean: all + variables: + - group: BlackDuck on Polaris + steps: + - checkout: none + + - pwsh: | + $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') + $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` + ? "@$($templatesBranch)" ` + : '' + + $branchVersion = "$env:BUILD_SOURCEBRANCHNAME$templatesPostfix" + + if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { + $semanticVersion = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)$templatesPostfix" + $ubuntuComparisonVersion = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)-ubuntu$templatesPostfix" + } + else { + $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' + $semanticVersion = "$branchName-$($env:BUILD_BUILDID)$templatesPostfix" + $ubuntuComparisonVersion = "$branchName-$($env:BUILD_BUILDID)-ubuntu$templatesPostfix" + } + + Write-Host "##vso[task.setvariable variable=BranchVersion]$branchVersion" + Write-Host "##vso[task.setvariable variable=BranchVersion;isOutput=true]$branchVersion" + Write-Host "##vso[task.setvariable variable=SemanticVersion]$semanticVersion" + Write-Host "##vso[task.setvariable variable=SemanticVersion;isOutput=true]$semanticVersion" + Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion]$ubuntuComparisonVersion" + Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion;isOutput=true]$ubuntuComparisonVersion" + displayName: Calculate Black Duck versions + name: CalculateVersion + + - pwsh: | + $headers = @{ + Authorization = "token $env:BLACKDUCK_ACCESS_TOKEN" + Accept = 'application/vnd.blackducksoftware.user-4+json' + } + + $uri = "$env:BLACKDUCK_URL/api/tokens/authenticate" + $response = Invoke-RestMethod -Method POST -Uri $uri -Headers $headers + + Write-Host "##vso[task.setvariable variable=BaseUrl]$env:BLACKDUCK_URL" + Write-Host "##vso[task.setvariable variable=BaseUrl;isOutput=true]$env:BLACKDUCK_URL" + Write-Host "##vso[task.setvariable variable=BearerToken]$($response.bearerToken)" + Write-Host "##vso[task.setvariable variable=BearerToken;isOutput=true]$($response.bearerToken)" + displayName: Authenticate to Black Duck + name: Authenticate + retryCountOnTaskFailure: 3 + env: + BLACKDUCK_URL: https://aveva.app.blackduck.com + BLACKDUCK_ACCESS_TOKEN: $(BLACKDUCK_ACCESS_TOKEN) + + - pwsh: | + $headers = @{ + Authorization = "Bearer $env:BearerToken" + Accept = 'application/vnd.blackducksoftware.project-detail-7+json' + } + + $projectName = $env:BUILD_REPOSITORY_NAME + $limit = 100 + $offset = 0 + $allProjects = @() + + do { + $uri = "$env:BaseUrl/api/projects?limit=$limit&offset=$offset&q=name:$projectName" + $response = Invoke-RestMethod -Method GET -Uri $uri -Headers $headers + if ($response.items) { + $allProjects += $response.items + } + $offset += $limit + } while ($offset -lt $response.totalCount) + + $matchedProject = $allProjects | Where-Object { $_.name -eq $projectName } + + if (-not $matchedProject) { + Write-Host "##vso[task.setvariable variable=ProjectExists]false" + Write-Host "##vso[task.setvariable variable=ProjectExists;isOutput=true]false" + exit 0 + } + + $projectId = $matchedProject._meta.href.Split('/')[-1] + Write-Host "##vso[task.setvariable variable=ProjectId]$projectId" + Write-Host "##vso[task.setvariable variable=ProjectId;isOutput=true]$projectId" + Write-Host "##vso[task.setvariable variable=ProjectExists]true" + Write-Host "##vso[task.setvariable variable=ProjectExists;isOutput=true]true" + displayName: Get Black Duck project ID + name: ProjectId + retryCountOnTaskFailure: 3 + env: + BearerToken: $(BearerToken) + BaseUrl: $(BaseUrl) + + - job: BlackDuckClone + displayName: BlackDuck Clone + dependsOn: BlackDuckInitialisation + timeoutInMinutes: "10" + pool: + name: Technology-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/dotnet/sdk:9.0 + workspace: + clean: all + variables: + - group: BlackDuck on Polaris + - name: BearerToken + value: $[ dependencies.BlackDuckInitialisation.outputs['Authenticate.BearerToken'] ] + - name: BaseUrl + value: $[ dependencies.BlackDuckInitialisation.outputs['Authenticate.BaseUrl'] ] + - name: ProjectId + value: $[ dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectId'] ] + - name: ProjectExists + value: $[ dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'] ] + - name: BranchVersion + value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.BranchVersion'] ] + - name: SemanticVersion + value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.SemanticVersion'] ] + - name: UbuntuComparisonVersion + value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.UbuntuComparisonVersion'] ] + steps: + - checkout: none + + - template: steps/blackduck-clone-version.yml@Templates + parameters: + BearerToken: $(BearerToken) + BaseUrl: $(BaseUrl) + ProjectId: $(ProjectId) + SourceVersion: main + TargetVersion: $(BranchVersion) + Phase: DEVELOPMENT + RemoveTargetVersion: false + + - template: steps/blackduck-clone-version.yml@Templates + parameters: + BearerToken: $(BearerToken) + BaseUrl: $(BaseUrl) + ProjectId: $(ProjectId) + SourceVersion: $(BranchVersion) + TargetVersion: $(SemanticVersion) + Phase: PLANNING + RemoveTargetVersion: true + + - template: steps/blackduck-clone-version.yml@Templates + parameters: + BearerToken: $(BearerToken) + BaseUrl: $(BaseUrl) + ProjectId: $(ProjectId) + SourceVersion: $(BranchVersion) + TargetVersion: $(UbuntuComparisonVersion) + Phase: PLANNING + RemoveTargetVersion: true + - job: CoverityScan displayName: Coverity Scan timeoutInMinutes: "180" @@ -269,7 +433,10 @@ stages: - job: BlackDuckScan displayName: Black Duck Scan - dependsOn: CoverityScan + dependsOn: + - BlackDuckInitialisation + - BlackDuckClone + - CoverityScan condition: succeededOrFailed() timeoutInMinutes: "120" pool: @@ -279,6 +446,9 @@ stages: container: mcr.microsoft.com/dotnet/sdk:9.0-windowsservercore-ltsc2022 workspace: clean: all + variables: + - name: SemanticVersion + value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.SemanticVersion'] ] steps: - checkout: self fetchDepth: "0" @@ -322,26 +492,6 @@ stages: java -version displayName: Verify Java Installation - - pwsh: | - $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') - $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` - ? "@$($templatesBranch)" ` - : '' - - if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { - $versionName = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)" - } - else { - $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' - $versionName = "$branchName-$($env:BUILD_BUILDID)" - } - - $versionName = "$versionName$templatesPostfix" - - Write-Host "##vso[task.setvariable variable=BlackDuckProjectVersion]$versionName" - Write-Host "Black Duck project version: $versionName" - displayName: Calculate Black Duck version - - task: ArchiveFiles@2 displayName: Archive sources and build outputs inputs: @@ -361,7 +511,7 @@ stages: DetectFolder: $(Pipeline.Workspace)\detect DetectArguments: | --detect.project.name=$(Build.Repository.Name) - --detect.project.version.name=$(BlackDuckProjectVersion) + --detect.project.version.name=$(SemanticVersion) --detect.source.path=$(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)\blackduck-input.zip --detect.cleanup=true @@ -405,7 +555,10 @@ stages: - job: BlackDuckScanUbuntu displayName: Black Duck Scan Ubuntu - dependsOn: CoverityScan + dependsOn: + - BlackDuckInitialisation + - BlackDuckClone + - CoverityScan condition: and(succeededOrFailed(), ${{ eq(parameters.EnableUbuntuBlackDuckComparison, 'true') }}) timeoutInMinutes: "120" pool: @@ -416,6 +569,9 @@ stages: container: mcr.microsoft.com/dotnet/sdk:9.0 workspace: clean: all + variables: + - name: UbuntuComparisonVersion + value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.UbuntuComparisonVersion'] ] steps: - checkout: self fetchDepth: "0" @@ -423,26 +579,6 @@ stages: - checkout: Templates fetchDepth: "1" - - pwsh: | - $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') - $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` - ? "@$($templatesBranch)" ` - : '' - - if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { - $versionName = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)-ubuntu" - } - else { - $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' - $versionName = "$branchName-$($env:BUILD_BUILDID)-ubuntu" - } - - $versionName = "$versionName$templatesPostfix" - - Write-Host "##vso[task.setvariable variable=BlackDuckProjectVersion]$versionName" - Write-Host "Black Duck project version: $versionName" - displayName: Calculate Black Duck version (Ubuntu) - - pwsh: | $sourceRoot = "$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework" $archivePath = "$(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip" @@ -464,7 +600,7 @@ stages: DetectFolder: $(Pipeline.Workspace)/detect-ubuntu DetectArguments: | --detect.project.name=$(Build.Repository.Name) - --detect.project.version.name=$(BlackDuckProjectVersion) + --detect.project.version.name=$(UbuntuComparisonVersion) --detect.source.path=$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip --detect.cleanup=true From afb01f87a113e2fa737ded54b236d25e5490bd7c Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 18:44:15 +0100 Subject: [PATCH 037/102] Split Coverity and Black Duck stages --- azure-pipelines.yml | 339 ++++++++++++++++++++++---------------------- 1 file changed, 171 insertions(+), 168 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1080c39..532332f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -92,175 +92,11 @@ stages: ./scripts/Tests/Run-Tests.ps1 -Quick displayName: Run tests - - stage: SecurityValidation - displayName: Security Validation + - stage: CoverityValidation + displayName: Coverity Validation dependsOn: HostedTests condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: - - job: BlackDuckInitialisation - displayName: BlackDuck Initialise - timeoutInMinutes: "10" - pool: - name: Technology-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/dotnet/sdk:9.0 - workspace: - clean: all - variables: - - group: BlackDuck on Polaris - steps: - - checkout: none - - - pwsh: | - $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') - $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` - ? "@$($templatesBranch)" ` - : '' - - $branchVersion = "$env:BUILD_SOURCEBRANCHNAME$templatesPostfix" - - if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { - $semanticVersion = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)$templatesPostfix" - $ubuntuComparisonVersion = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)-ubuntu$templatesPostfix" - } - else { - $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' - $semanticVersion = "$branchName-$($env:BUILD_BUILDID)$templatesPostfix" - $ubuntuComparisonVersion = "$branchName-$($env:BUILD_BUILDID)-ubuntu$templatesPostfix" - } - - Write-Host "##vso[task.setvariable variable=BranchVersion]$branchVersion" - Write-Host "##vso[task.setvariable variable=BranchVersion;isOutput=true]$branchVersion" - Write-Host "##vso[task.setvariable variable=SemanticVersion]$semanticVersion" - Write-Host "##vso[task.setvariable variable=SemanticVersion;isOutput=true]$semanticVersion" - Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion]$ubuntuComparisonVersion" - Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion;isOutput=true]$ubuntuComparisonVersion" - displayName: Calculate Black Duck versions - name: CalculateVersion - - - pwsh: | - $headers = @{ - Authorization = "token $env:BLACKDUCK_ACCESS_TOKEN" - Accept = 'application/vnd.blackducksoftware.user-4+json' - } - - $uri = "$env:BLACKDUCK_URL/api/tokens/authenticate" - $response = Invoke-RestMethod -Method POST -Uri $uri -Headers $headers - - Write-Host "##vso[task.setvariable variable=BaseUrl]$env:BLACKDUCK_URL" - Write-Host "##vso[task.setvariable variable=BaseUrl;isOutput=true]$env:BLACKDUCK_URL" - Write-Host "##vso[task.setvariable variable=BearerToken]$($response.bearerToken)" - Write-Host "##vso[task.setvariable variable=BearerToken;isOutput=true]$($response.bearerToken)" - displayName: Authenticate to Black Duck - name: Authenticate - retryCountOnTaskFailure: 3 - env: - BLACKDUCK_URL: https://aveva.app.blackduck.com - BLACKDUCK_ACCESS_TOKEN: $(BLACKDUCK_ACCESS_TOKEN) - - - pwsh: | - $headers = @{ - Authorization = "Bearer $env:BearerToken" - Accept = 'application/vnd.blackducksoftware.project-detail-7+json' - } - - $projectName = $env:BUILD_REPOSITORY_NAME - $limit = 100 - $offset = 0 - $allProjects = @() - - do { - $uri = "$env:BaseUrl/api/projects?limit=$limit&offset=$offset&q=name:$projectName" - $response = Invoke-RestMethod -Method GET -Uri $uri -Headers $headers - if ($response.items) { - $allProjects += $response.items - } - $offset += $limit - } while ($offset -lt $response.totalCount) - - $matchedProject = $allProjects | Where-Object { $_.name -eq $projectName } - - if (-not $matchedProject) { - Write-Host "##vso[task.setvariable variable=ProjectExists]false" - Write-Host "##vso[task.setvariable variable=ProjectExists;isOutput=true]false" - exit 0 - } - - $projectId = $matchedProject._meta.href.Split('/')[-1] - Write-Host "##vso[task.setvariable variable=ProjectId]$projectId" - Write-Host "##vso[task.setvariable variable=ProjectId;isOutput=true]$projectId" - Write-Host "##vso[task.setvariable variable=ProjectExists]true" - Write-Host "##vso[task.setvariable variable=ProjectExists;isOutput=true]true" - displayName: Get Black Duck project ID - name: ProjectId - retryCountOnTaskFailure: 3 - env: - BearerToken: $(BearerToken) - BaseUrl: $(BaseUrl) - - - job: BlackDuckClone - displayName: BlackDuck Clone - dependsOn: BlackDuckInitialisation - timeoutInMinutes: "10" - pool: - name: Technology-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/dotnet/sdk:9.0 - workspace: - clean: all - variables: - - group: BlackDuck on Polaris - - name: BearerToken - value: $[ dependencies.BlackDuckInitialisation.outputs['Authenticate.BearerToken'] ] - - name: BaseUrl - value: $[ dependencies.BlackDuckInitialisation.outputs['Authenticate.BaseUrl'] ] - - name: ProjectId - value: $[ dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectId'] ] - - name: ProjectExists - value: $[ dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'] ] - - name: BranchVersion - value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.BranchVersion'] ] - - name: SemanticVersion - value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.SemanticVersion'] ] - - name: UbuntuComparisonVersion - value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.UbuntuComparisonVersion'] ] - steps: - - checkout: none - - - template: steps/blackduck-clone-version.yml@Templates - parameters: - BearerToken: $(BearerToken) - BaseUrl: $(BaseUrl) - ProjectId: $(ProjectId) - SourceVersion: main - TargetVersion: $(BranchVersion) - Phase: DEVELOPMENT - RemoveTargetVersion: false - - - template: steps/blackduck-clone-version.yml@Templates - parameters: - BearerToken: $(BearerToken) - BaseUrl: $(BaseUrl) - ProjectId: $(ProjectId) - SourceVersion: $(BranchVersion) - TargetVersion: $(SemanticVersion) - Phase: PLANNING - RemoveTargetVersion: true - - - template: steps/blackduck-clone-version.yml@Templates - parameters: - BearerToken: $(BearerToken) - BaseUrl: $(BaseUrl) - ProjectId: $(ProjectId) - SourceVersion: $(BranchVersion) - TargetVersion: $(UbuntuComparisonVersion) - Phase: PLANNING - RemoveTargetVersion: true - - job: CoverityScan displayName: Coverity Scan timeoutInMinutes: "180" @@ -431,12 +267,180 @@ stages: displayName: Publish Coverity artifacts condition: and(always(), eq(variables.HasCoverityArtifacts, 'true')) + - stage: BlackDuckValidation + displayName: Black Duck Validation + dependsOn: HostedTests + condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) + jobs: + - job: BlackDuckInitialisation + displayName: BlackDuck Initialise + timeoutInMinutes: "10" + pool: + name: Technology-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/dotnet/sdk:9.0 + workspace: + clean: all + variables: + - group: BlackDuck on Polaris + steps: + - checkout: none + + - pwsh: | + $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') + $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` + ? "@$($templatesBranch)" ` + : '' + + $branchVersion = "$env:BUILD_SOURCEBRANCHNAME$templatesPostfix" + + if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { + $semanticVersion = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)$templatesPostfix" + $ubuntuComparisonVersion = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)-ubuntu$templatesPostfix" + } + else { + $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' + $semanticVersion = "$branchName-$($env:BUILD_BUILDID)$templatesPostfix" + $ubuntuComparisonVersion = "$branchName-$($env:BUILD_BUILDID)-ubuntu$templatesPostfix" + } + + Write-Host "##vso[task.setvariable variable=BranchVersion]$branchVersion" + Write-Host "##vso[task.setvariable variable=BranchVersion;isOutput=true]$branchVersion" + Write-Host "##vso[task.setvariable variable=SemanticVersion]$semanticVersion" + Write-Host "##vso[task.setvariable variable=SemanticVersion;isOutput=true]$semanticVersion" + Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion]$ubuntuComparisonVersion" + Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion;isOutput=true]$ubuntuComparisonVersion" + displayName: Calculate Black Duck versions + name: CalculateVersion + + - pwsh: | + $headers = @{ + Authorization = "token $env:BLACKDUCK_ACCESS_TOKEN" + Accept = 'application/vnd.blackducksoftware.user-4+json' + } + + $uri = "$env:BLACKDUCK_URL/api/tokens/authenticate" + $response = Invoke-RestMethod -Method POST -Uri $uri -Headers $headers + + Write-Host "##vso[task.setvariable variable=BaseUrl]$env:BLACKDUCK_URL" + Write-Host "##vso[task.setvariable variable=BaseUrl;isOutput=true]$env:BLACKDUCK_URL" + Write-Host "##vso[task.setvariable variable=BearerToken]$($response.bearerToken)" + Write-Host "##vso[task.setvariable variable=BearerToken;isOutput=true]$($response.bearerToken)" + displayName: Authenticate to Black Duck + name: Authenticate + retryCountOnTaskFailure: 3 + env: + BLACKDUCK_URL: https://aveva.app.blackduck.com + BLACKDUCK_ACCESS_TOKEN: $(BLACKDUCK_ACCESS_TOKEN) + + - pwsh: | + $headers = @{ + Authorization = "Bearer $env:BearerToken" + Accept = 'application/vnd.blackducksoftware.project-detail-7+json' + } + + $projectName = $env:BUILD_REPOSITORY_NAME + $limit = 100 + $offset = 0 + $allProjects = @() + + do { + $uri = "$env:BaseUrl/api/projects?limit=$limit&offset=$offset&q=name:$projectName" + $response = Invoke-RestMethod -Method GET -Uri $uri -Headers $headers + if ($response.items) { + $allProjects += $response.items + } + $offset += $limit + } while ($offset -lt $response.totalCount) + + $matchedProject = $allProjects | Where-Object { $_.name -eq $projectName } + + if (-not $matchedProject) { + Write-Host "##vso[task.setvariable variable=ProjectExists]false" + Write-Host "##vso[task.setvariable variable=ProjectExists;isOutput=true]false" + exit 0 + } + + $projectId = $matchedProject._meta.href.Split('/')[-1] + Write-Host "##vso[task.setvariable variable=ProjectId]$projectId" + Write-Host "##vso[task.setvariable variable=ProjectId;isOutput=true]$projectId" + Write-Host "##vso[task.setvariable variable=ProjectExists]true" + Write-Host "##vso[task.setvariable variable=ProjectExists;isOutput=true]true" + displayName: Get Black Duck project ID + name: ProjectId + retryCountOnTaskFailure: 3 + env: + BearerToken: $(BearerToken) + BaseUrl: $(BaseUrl) + + - job: BlackDuckClone + displayName: BlackDuck Clone + dependsOn: BlackDuckInitialisation + timeoutInMinutes: "10" + pool: + name: Technology-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/dotnet/sdk:9.0 + workspace: + clean: all + variables: + - group: BlackDuck on Polaris + - name: BearerToken + value: $[ dependencies.BlackDuckInitialisation.outputs['Authenticate.BearerToken'] ] + - name: BaseUrl + value: $[ dependencies.BlackDuckInitialisation.outputs['Authenticate.BaseUrl'] ] + - name: ProjectId + value: $[ dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectId'] ] + - name: ProjectExists + value: $[ dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'] ] + - name: BranchVersion + value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.BranchVersion'] ] + - name: SemanticVersion + value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.SemanticVersion'] ] + - name: UbuntuComparisonVersion + value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.UbuntuComparisonVersion'] ] + steps: + - checkout: none + + - template: steps/blackduck-clone-version.yml@Templates + parameters: + BearerToken: $(BearerToken) + BaseUrl: $(BaseUrl) + ProjectId: $(ProjectId) + SourceVersion: main + TargetVersion: $(BranchVersion) + Phase: DEVELOPMENT + RemoveTargetVersion: false + + - template: steps/blackduck-clone-version.yml@Templates + parameters: + BearerToken: $(BearerToken) + BaseUrl: $(BaseUrl) + ProjectId: $(ProjectId) + SourceVersion: $(BranchVersion) + TargetVersion: $(SemanticVersion) + Phase: PLANNING + RemoveTargetVersion: true + + - template: steps/blackduck-clone-version.yml@Templates + parameters: + BearerToken: $(BearerToken) + BaseUrl: $(BaseUrl) + ProjectId: $(ProjectId) + SourceVersion: $(BranchVersion) + TargetVersion: $(UbuntuComparisonVersion) + Phase: PLANNING + RemoveTargetVersion: true + - job: BlackDuckScan displayName: Black Duck Scan dependsOn: - BlackDuckInitialisation - BlackDuckClone - - CoverityScan condition: succeededOrFailed() timeoutInMinutes: "120" pool: @@ -558,7 +562,6 @@ stages: dependsOn: - BlackDuckInitialisation - BlackDuckClone - - CoverityScan condition: and(succeededOrFailed(), ${{ eq(parameters.EnableUbuntuBlackDuckComparison, 'true') }}) timeoutInMinutes: "120" pool: From c939e703f2ca75ca8ab0cf0d951deb042cd4ed05 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 19:36:02 +0100 Subject: [PATCH 038/102] Fail early when Black Duck project is missing --- azure-pipelines.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 532332f..54b0505 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -358,9 +358,11 @@ stages: $matchedProject = $allProjects | Where-Object { $_.name -eq $projectName } if (-not $matchedProject) { + Write-Host "Project '$projectName' not found in Black Duck." Write-Host "##vso[task.setvariable variable=ProjectExists]false" Write-Host "##vso[task.setvariable variable=ProjectExists;isOutput=true]false" - exit 0 + Write-Host "##vso[task.logissue type=error]Black Duck project '$projectName' must exist before the scan runs. Create it or grant the scanner access to the existing project." + exit 1 } $projectId = $matchedProject._meta.href.Split('/')[-1] @@ -378,6 +380,7 @@ stages: - job: BlackDuckClone displayName: BlackDuck Clone dependsOn: BlackDuckInitialisation + condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true')) timeoutInMinutes: "10" pool: name: Technology-Microsoft-Hosted-Ubuntu @@ -441,7 +444,7 @@ stages: dependsOn: - BlackDuckInitialisation - BlackDuckClone - condition: succeededOrFailed() + condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true')) timeoutInMinutes: "120" pool: name: Technology-Microsoft-Hosted-Windows @@ -562,7 +565,7 @@ stages: dependsOn: - BlackDuckInitialisation - BlackDuckClone - condition: and(succeededOrFailed(), ${{ eq(parameters.EnableUbuntuBlackDuckComparison, 'true') }}) + condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true'), ${{ eq(parameters.EnableUbuntuBlackDuckComparison, 'true') }}) timeoutInMinutes: "120" pool: name: Technology-Microsoft-Hosted-Ubuntu From 072ef8bff3a7fcdce4c9e12f98e590e6e1d9c21a Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 19:51:58 +0100 Subject: [PATCH 039/102] Add component Black Duck template comparison --- azure-pipelines.yml | 61 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 54b0505..5235131 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -25,6 +25,9 @@ parameters: - name: EnableUbuntuBlackDuckComparison type: string default: "false" + - name: EnableComponentTemplateBlackDuckComparison + type: string + default: "false" variables: - template: variables.yml@Templates @@ -647,3 +650,61 @@ stages: artifact: BlackDuckInputUbuntu displayName: Publish Black Duck input archive (Ubuntu) condition: and(always(), eq(variables.HasBlackDuckInputUbuntu, 'true')) + + - stage: BuildWithShims + displayName: Build With Shims + dependsOn: HostedTests + condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}, ${{ eq(parameters.EnableComponentTemplateBlackDuckComparison, 'true') }}) + jobs: + - job: BuildWithShims + displayName: Build With Shims + timeoutInMinutes: "120" + pool: + name: Technology-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/dotnet/sdk:9.0 + workspace: + clean: all + steps: + - checkout: self + fetchDepth: "0" + + - checkout: Templates + fetchDepth: "1" + + - task: UseDotNet@2 + displayName: Ensure .NET 9 SDK for shim build + retryCountOnTaskFailure: 3 + inputs: + packageType: sdk + version: 9.0.x + + - pwsh: | + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' + & (Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release + displayName: Build template and example projects with fake UE references + + - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework + artifact: Source + displayName: Publish Source artifact for component template comparison + + - publish: $(Pipeline.Workspace)/s/Templates + artifact: Templates + displayName: Publish Templates artifact for component template comparison + + - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/shims/artifacts/Release + artifact: Build.Release + displayName: Publish build artifact for component template comparison + + - stage: ComponentTemplateBlackDuckComparison + displayName: Component Template Black Duck Comparison + dependsOn: BuildWithShims + condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}, ${{ eq(parameters.EnableComponentTemplateBlackDuckComparison, 'true') }}) + jobs: + - template: component-blackduck.yml@Templates + parameters: + BuildArtifact: Build.Release + BlackDuckGroup: DabaconGroup + IsIPR: false From 361a4a66d588e868f4d8bca85e3f8c6ab0714317 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 19:54:31 +0100 Subject: [PATCH 040/102] Use custom Templates branch for Black Duck comparison --- azure-pipelines.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5235131..1133eac 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -16,7 +16,7 @@ resources: - repository: Templates type: git name: Technology/Templates - ref: refs/heads/main + ref: refs/heads/feature/uecf-blackduck-pool-params parameters: - name: EnableSecurityValidation @@ -708,3 +708,10 @@ stages: BuildArtifact: Build.Release BlackDuckGroup: DabaconGroup IsIPR: false + UbuntuPoolName: Technology-Microsoft-Hosted-Ubuntu + UbuntuPoolDemands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + WindowsPoolName: Technology-Microsoft-Hosted-Windows + WindowsPoolDemands: + - ImageOverride -equals windows-latest From 740ae01a177f0ee56ec74cfb38940df3462ee836 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 19:55:08 +0100 Subject: [PATCH 041/102] Use dedicated Templates resource for comparison --- azure-pipelines.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1133eac..349550b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -16,6 +16,10 @@ resources: - repository: Templates type: git name: Technology/Templates + ref: refs/heads/main + - repository: TemplatesCustom + type: git + name: Dabacon Products/Templates ref: refs/heads/feature/uecf-blackduck-pool-params parameters: @@ -703,7 +707,7 @@ stages: dependsOn: BuildWithShims condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}, ${{ eq(parameters.EnableComponentTemplateBlackDuckComparison, 'true') }}) jobs: - - template: component-blackduck.yml@Templates + - template: component-blackduck.yml@TemplatesCustom parameters: BuildArtifact: Build.Release BlackDuckGroup: DabaconGroup From 935d7dfb715f61260f72e81650bed89a003e957a Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 20:56:50 +0100 Subject: [PATCH 042/102] Set explicit Black Duck project name --- azure-pipelines.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 349550b..6ef55d0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -39,6 +39,8 @@ variables: value: .RnD.Polaris.Dabacon-Products.SecurityAdvisors - name: CoverityCliVersion value: 2025.9.0 + - name: BlackDuckProjectName + value: AVEVA.UnifiedEngineeringCustomizationFramework trigger: branches: @@ -203,7 +205,7 @@ stages: $polarisContent = @( 'version: 1' 'project:' - " name: $env:BUILD_REPOSITORY_NAME" + " name: $(BlackDuckProjectName)" " branch: $polarisBranch" " projectDir: $repoRoot" ' revision:' @@ -348,7 +350,7 @@ stages: Accept = 'application/vnd.blackducksoftware.project-detail-7+json' } - $projectName = $env:BUILD_REPOSITORY_NAME + $projectName = '$(BlackDuckProjectName)' $limit = 100 $offset = 0 $allProjects = @() @@ -524,7 +526,7 @@ stages: DetectVersion: latest DetectFolder: $(Pipeline.Workspace)\detect DetectArguments: | - --detect.project.name=$(Build.Repository.Name) + --detect.project.name=$(BlackDuckProjectName) --detect.project.version.name=$(SemanticVersion) --detect.source.path=$(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)\blackduck-input.zip @@ -612,7 +614,7 @@ stages: DetectVersion: latest DetectFolder: $(Pipeline.Workspace)/detect-ubuntu DetectArguments: | - --detect.project.name=$(Build.Repository.Name) + --detect.project.name=$(BlackDuckProjectName) --detect.project.version.name=$(UbuntuComparisonVersion) --detect.source.path=$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip From 033ecf35ef3cbce75b2b3f00b58830d6ed3e70d9 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 21:49:05 +0100 Subject: [PATCH 043/102] Retry Ubuntu Black Duck download --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6ef55d0..365addd 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -608,7 +608,7 @@ stages: - task: BlackDuckDetectTask@10 displayName: Black Duck scan (Ubuntu) - retryCountOnTaskFailure: 0 + retryCountOnTaskFailure: 3 inputs: BlackDuckScaService: BlackDuckScanner DetectVersion: latest From 75f543badc294cd1eddc5ab57ee55f8f201bf785 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 22:09:57 +0100 Subject: [PATCH 044/102] Add Ubuntu Black Duck network diagnostics --- azure-pipelines.yml | 53 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 365addd..70811f5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -606,6 +606,53 @@ stages: Write-Host "Created archive at $archivePath" displayName: Archive sources and build outputs (Ubuntu) + - pwsh: | + $diagnosticPath = "$(Build.ArtifactStagingDirectory)/blackduck-network-ubuntu.txt" + $targets = @( + 'detect.blackduck.com', + 'aveva.app.blackduck.com' + ) + + $lines = New-Object System.Collections.Generic.List[string] + + foreach ($target in $targets) { + $lines.Add("=== $target ===") + + try { + $addresses = [System.Net.Dns]::GetHostAddresses($target) | ForEach-Object { $_.IPAddressToString } + $lines.Add("DNS: $($addresses -join ', ')") + } + catch { + $lines.Add("DNS ERROR: $($_.Exception.Message)") + } + + try { + $pingOutput = & ping -c 2 $target 2>&1 + $lines.Add('PING:') + foreach ($line in $pingOutput) { + $lines.Add([string]$line) + } + } + catch { + $lines.Add("PING ERROR: $($_.Exception.Message)") + } + + try { + $response = Invoke-WebRequest -Uri ("https://{0}" -f $target) -Method Head -SkipHttpErrorCheck -TimeoutSec 30 + $lines.Add("HTTPS: StatusCode=$($response.StatusCode)") + } + catch { + $lines.Add("HTTPS ERROR: $($_.Exception.Message)") + } + + $lines.Add('') + } + + Set-Content -LiteralPath $diagnosticPath -Value $lines -Encoding utf8 + Get-Content -LiteralPath $diagnosticPath + displayName: Diagnose Black Duck network reachability (Ubuntu) + condition: always() + - task: BlackDuckDetectTask@10 displayName: Black Duck scan (Ubuntu) retryCountOnTaskFailure: 3 @@ -637,6 +684,7 @@ stages: $paths = @{ HasBlackDuckLogsUbuntu = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-logs-ubuntu') HasBlackDuckInputUbuntu = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-input-ubuntu.zip') + HasBlackDuckNetworkDiagnosticsUbuntu = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-network-ubuntu.txt') } foreach ($entry in $paths.GetEnumerator()) { @@ -657,6 +705,11 @@ stages: displayName: Publish Black Duck input archive (Ubuntu) condition: and(always(), eq(variables.HasBlackDuckInputUbuntu, 'true')) + - publish: $(Build.ArtifactStagingDirectory)/blackduck-network-ubuntu.txt + artifact: BlackDuckNetworkDiagnosticsUbuntu + displayName: Publish Black Duck network diagnostics (Ubuntu) + condition: and(always(), eq(variables.HasBlackDuckNetworkDiagnosticsUbuntu, 'true')) + - stage: BuildWithShims displayName: Build With Shims dependsOn: HostedTests From d8b9adaac38ffa31fe01b5e1a4f38567e77ace07 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 22:52:18 +0100 Subject: [PATCH 045/102] Use manual Detect on Ubuntu comparison --- azure-pipelines.yml | 64 ++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 70811f5..bf648b5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -585,6 +585,7 @@ stages: workspace: clean: all variables: + - group: BlackDuck on Polaris - name: UbuntuComparisonVersion value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.UbuntuComparisonVersion'] ] steps: @@ -653,31 +654,46 @@ stages: displayName: Diagnose Black Duck network reachability (Ubuntu) condition: always() - - task: BlackDuckDetectTask@10 - displayName: Black Duck scan (Ubuntu) + - pwsh: | + $detectRoot = "$(Pipeline.Workspace)/detect-ubuntu" + $outputPath = "$(Build.ArtifactStagingDirectory)/blackduck-logs-ubuntu" + $detectScript = Join-Path $detectRoot 'detect10.sh' + $sourcePath = "$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework" + $binaryScanPath = "$(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip" + + New-Item -Path $detectRoot -ItemType Directory -Force | Out-Null + New-Item -Path $outputPath -ItemType Directory -Force | Out-Null + + Invoke-WebRequest -Uri 'https://detect.blackduck.com/detect10.sh' -OutFile $detectScript + & chmod +x $detectScript + + $arguments = @( + '--blackduck.url=https://aveva.app.blackduck.com' + "--blackduck.api.token=$env:BLACKDUCK_ACCESS_TOKEN" + '--detect.project.name=$(BlackDuckProjectName)' + '--detect.project.version.name=$(UbuntuComparisonVersion)' + "--detect.source.path=$sourcePath" + "--detect.binary.scan.file.path=$binaryScanPath" + '--detect.cleanup=true' + "--detect.output.path=$outputPath" + '--detect.detector.search.depth=10' + '--detect.detector.search.continue=true' + '--detect.blackduck.signature.scanner.upload.source.mode=true' + '--detect.blackduck.signature.scanner.copyright.search=true' + '--detect.blackduck.signature.scanner.license.search=true' + '--logging.level.detect=DEBUG' + '--detect.diagnostic=true' + '--detect.timeout=1800' + '--detect.blackduck.signature.scanner.arguments=--fs-wait-time=90' + '--detect.wait.for.results=true' + '--detect.wait.for.results.timeout=6000000' + ) + + & bash $detectScript @arguments + displayName: Black Duck scan (Ubuntu manual Detect) retryCountOnTaskFailure: 3 - inputs: - BlackDuckScaService: BlackDuckScanner - DetectVersion: latest - DetectFolder: $(Pipeline.Workspace)/detect-ubuntu - DetectArguments: | - --detect.project.name=$(BlackDuckProjectName) - --detect.project.version.name=$(UbuntuComparisonVersion) - --detect.source.path=$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework - --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip - --detect.cleanup=true - --detect.output.path=$(Build.ArtifactStagingDirectory)/blackduck-logs-ubuntu - --detect.detector.search.depth=10 - --detect.detector.search.continue=true - --detect.blackduck.signature.scanner.upload.source.mode=true - --detect.blackduck.signature.scanner.copyright.search=true - --detect.blackduck.signature.scanner.license.search=true - --logging.level.detect=DEBUG - --detect.diagnostic=true - --detect.timeout=1800 - --detect.blackduck.signature.scanner.arguments= --fs-wait-time=90 - --detect.wait.for.results=true - --detect.wait.for.results.timeout=6000000 + env: + BLACKDUCK_ACCESS_TOKEN: $(BLACKDUCK_ACCESS_TOKEN) - pwsh: | $artifactStaging = $env:BUILD_ARTIFACTSTAGINGDIRECTORY From d8a3999deb56fae7164e6e5320f058f336720049 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Tue, 31 Mar 2026 23:14:11 +0100 Subject: [PATCH 046/102] Bootstrap Java for Ubuntu Black Duck comparison --- azure-pipelines.yml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bf648b5..18ba758 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -654,6 +654,45 @@ stages: displayName: Diagnose Black Duck network reachability (Ubuntu) condition: always() + - task: Cache@2 + displayName: Restore JDK cache (Ubuntu) + retryCountOnTaskFailure: 3 + inputs: + key: 'jdk | temurin17 | $(Agent.OS) | v1' + path: $(Pipeline.Workspace)/JDK-Ubuntu + cacheHitVar: JDK_UBUNTU_RESTORED + + - pwsh: | + if ($env:JDK_UBUNTU_RESTORED -eq 'true') { + Write-Host 'JDK cache restored.' + exit 0 + } + + $workdir = "$(Pipeline.Workspace)/JDK-Ubuntu" + $archivePath = Join-Path $workdir 'Temurin17.tar.gz' + $url = 'https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.14%2B7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.14_7.tar.gz' + + New-Item -Path $workdir -ItemType Directory -Force | Out-Null + Invoke-WebRequest -Uri $url -OutFile $archivePath + & tar -xzf $archivePath -C $workdir + displayName: Download Temurin JDK 17 (Ubuntu) + condition: ne(variables.JDK_UBUNTU_RESTORED, 'true') + + - pwsh: | + $jdkFolder = Get-ChildItem -Path "$(Pipeline.Workspace)/JDK-Ubuntu" -Directory | + Where-Object { $_.Name -like 'jdk-17*' -or $_.Name -like 'jdk17*' } | + Select-Object -First 1 + + if (-not $jdkFolder) { + throw 'Unable to locate extracted Temurin JDK 17 folder under $(Pipeline.Workspace)/JDK-Ubuntu.' + } + + $javaHome = $jdkFolder.FullName + Write-Host "##vso[task.setvariable variable=JAVA_HOME]$javaHome" + Write-Host "##vso[task.prependpath]$javaHome/bin" + & "$javaHome/bin/java" -version + displayName: Configure JAVA_HOME (Ubuntu) + - pwsh: | $detectRoot = "$(Pipeline.Workspace)/detect-ubuntu" $outputPath = "$(Build.ArtifactStagingDirectory)/blackduck-logs-ubuntu" @@ -694,6 +733,7 @@ stages: retryCountOnTaskFailure: 3 env: BLACKDUCK_ACCESS_TOKEN: $(BLACKDUCK_ACCESS_TOKEN) + JAVA_HOME: $(JAVA_HOME) - pwsh: | $artifactStaging = $env:BUILD_ARTIFACTSTAGINGDIRECTORY From db73a4e97b9c339027ecd9d77528bef2a1b4c8df Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Wed, 1 Apr 2026 02:52:06 +0100 Subject: [PATCH 047/102] Prepare component-style Source artifact --- azure-pipelines.yml | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 18ba758..7944824 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -801,7 +801,29 @@ stages: & (Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release displayName: Build template and example projects with fake UE references - - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework + - pwsh: | + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' + $preparedRoot = Join-Path $env:PIPELINE_WORKSPACE 'prepared-component-source' + $preparedSource = Join-Path $preparedRoot 'Source' + $preparedBuildRelease = Join-Path $preparedSource 'Build/Release' + $shimArtifacts = Join-Path $repoRoot 'shims/artifacts/Release' + + if (Test-Path -LiteralPath $preparedRoot) { + Remove-Item -LiteralPath $preparedRoot -Recurse -Force + } + + New-Item -Path $preparedSource -ItemType Directory -Force | Out-Null + Copy-Item -Path (Join-Path $repoRoot '*') -Destination $preparedSource -Recurse -Force + + if (Test-Path -LiteralPath $shimArtifacts) { + New-Item -Path $preparedBuildRelease -ItemType Directory -Force | Out-Null + Copy-Item -Path (Join-Path $shimArtifacts '*') -Destination $preparedBuildRelease -Recurse -Force + } + + Write-Host "Prepared component-template source artifact at $preparedSource" + displayName: Prepare component-style Source artifact + + - publish: $(Pipeline.Workspace)/prepared-component-source/Source artifact: Source displayName: Publish Source artifact for component template comparison @@ -823,6 +845,7 @@ stages: BuildArtifact: Build.Release BlackDuckGroup: DabaconGroup IsIPR: false + ProjectName: $(BlackDuckProjectName) UbuntuPoolName: Technology-Microsoft-Hosted-Ubuntu UbuntuPoolDemands: - ImageOverride -equals ubuntu-latest From eec2903b0f06a021ef20af12b6a09eeed78e16b1 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Wed, 1 Apr 2026 03:50:12 +0100 Subject: [PATCH 048/102] Refine Black Duck source artifact flow --- azure-pipelines.yml | 195 ++++++++++++++++++++++++-------------------- 1 file changed, 106 insertions(+), 89 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7944824..c01ecf1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -276,9 +276,92 @@ stages: displayName: Publish Coverity artifacts condition: and(always(), eq(variables.HasCoverityArtifacts, 'true')) + - stage: BuildWithShims + displayName: Build With Shims + dependsOn: HostedTests + condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) + jobs: + - job: BuildWithShims + displayName: Build With Shims + timeoutInMinutes: "120" + pool: + name: Technology-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/dotnet/sdk:9.0 + workspace: + clean: all + steps: + - checkout: self + fetchDepth: "0" + + - checkout: Templates + fetchDepth: "1" + + - task: UseDotNet@2 + displayName: Ensure .NET 9 SDK for shim build + retryCountOnTaskFailure: 3 + inputs: + packageType: sdk + version: 9.0.x + + - pwsh: | + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' + & (Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release + displayName: Build template and example projects with fake UE references + + - pwsh: | + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' + $preparedRoot = Join-Path $env:PIPELINE_WORKSPACE 'prepared-component-source' + $preparedSource = Join-Path $preparedRoot 'Source' + $preparedBuildRelease = Join-Path $preparedSource 'Build/Release' + $shimArtifacts = Join-Path $repoRoot 'shims/artifacts/Release' + $probeRoot = Join-Path $preparedSource 'detector-coverage-probe' + + if (Test-Path -LiteralPath $preparedRoot) { + Remove-Item -LiteralPath $preparedRoot -Recurse -Force + } + + New-Item -Path $preparedSource -ItemType Directory -Force | Out-Null + Get-ChildItem -Path $repoRoot -Force | ForEach-Object { + Copy-Item -LiteralPath $_.FullName -Destination $preparedSource -Recurse -Force + } + + if (Test-Path -LiteralPath $shimArtifacts) { + New-Item -Path $preparedBuildRelease -ItemType Directory -Force | Out-Null + Copy-Item -Path (Join-Path $shimArtifacts '*') -Destination $preparedBuildRelease -Recurse -Force + } + + New-Item -Path $probeRoot -ItemType Directory -Force | Out-Null + $packagesConfig = @( + '' + '' + ' ' + '' + ) -join "`n" + Set-Content -LiteralPath (Join-Path $probeRoot 'packages.config') -Value $packagesConfig -Encoding utf8 + + New-Item -Path (Join-Path $preparedSource '.artifactignore') -ItemType File -Force | Out-Null + + Write-Host "Prepared component-template source artifact at $preparedSource" + displayName: Prepare component-style Source artifact + + - publish: $(Pipeline.Workspace)/prepared-component-source/Source + artifact: Source + displayName: Publish Source artifact for Black Duck scans + + - publish: $(Pipeline.Workspace)/s/Templates + artifact: Templates + displayName: Publish Templates artifact for component template comparison + + - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/shims/artifacts/Release + artifact: Build.Release + displayName: Publish build artifact for component template comparison + - stage: BlackDuckValidation displayName: Black Duck Validation - dependsOn: HostedTests + dependsOn: BuildWithShims condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: - job: BlackDuckInitialisation @@ -299,28 +382,32 @@ stages: - pwsh: | $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') - $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` + $templatesPostfix = ($templatesBranch -and $templatesBranch -ne 'main') ` ? "@$($templatesBranch)" ` : '' $branchVersion = "$env:BUILD_SOURCEBRANCHNAME$templatesPostfix" if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { - $semanticVersion = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)$templatesPostfix" - $ubuntuComparisonVersion = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)-ubuntu$templatesPostfix" + $baseSemanticVersion = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)" } else { $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' - $semanticVersion = "$branchName-$($env:BUILD_BUILDID)$templatesPostfix" - $ubuntuComparisonVersion = "$branchName-$($env:BUILD_BUILDID)-ubuntu$templatesPostfix" + $baseSemanticVersion = "$branchName-$($env:BUILD_BUILDID)" } + $semanticVersion = "$baseSemanticVersion-windows$templatesPostfix" + $ubuntuComparisonVersion = "$baseSemanticVersion-linux$templatesPostfix" + $componentComparisonVersion = "$baseSemanticVersion-components$templatesPostfix" + Write-Host "##vso[task.setvariable variable=BranchVersion]$branchVersion" Write-Host "##vso[task.setvariable variable=BranchVersion;isOutput=true]$branchVersion" Write-Host "##vso[task.setvariable variable=SemanticVersion]$semanticVersion" Write-Host "##vso[task.setvariable variable=SemanticVersion;isOutput=true]$semanticVersion" Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion]$ubuntuComparisonVersion" Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion;isOutput=true]$ubuntuComparisonVersion" + Write-Host "##vso[task.setvariable variable=ComponentComparisonVersion]$componentComparisonVersion" + Write-Host "##vso[task.setvariable variable=ComponentComparisonVersion;isOutput=true]$componentComparisonVersion" displayName: Calculate Black Duck versions name: CalculateVersion @@ -466,11 +553,10 @@ stages: - name: SemanticVersion value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.SemanticVersion'] ] steps: - - checkout: self - fetchDepth: "0" - - - checkout: Templates - fetchDepth: "1" + - download: current + artifact: Source + displayName: Download Source artifact + patterns: "**" - pwsh: | $workdir = "$(Pipeline.Workspace)\JDK" @@ -511,7 +597,7 @@ stages: - task: ArchiveFiles@2 displayName: Archive sources and build outputs inputs: - rootFolderOrFile: $(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework + rootFolderOrFile: $(Pipeline.Workspace)\Source includeRootFolder: true archiveType: zip archiveFile: $(Build.ArtifactStagingDirectory)\blackduck-input.zip @@ -528,7 +614,7 @@ stages: DetectArguments: | --detect.project.name=$(BlackDuckProjectName) --detect.project.version.name=$(SemanticVersion) - --detect.source.path=$(Pipeline.Workspace)\s\UnifiedEngineeringCustomizationFramework + --detect.source.path=$(Pipeline.Workspace)\Source --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)\blackduck-input.zip --detect.cleanup=true --detect.output.path=$(Build.ArtifactStagingDirectory)\blackduck-logs @@ -589,14 +675,13 @@ stages: - name: UbuntuComparisonVersion value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.UbuntuComparisonVersion'] ] steps: - - checkout: self - fetchDepth: "0" - - - checkout: Templates - fetchDepth: "1" + - download: current + artifact: Source + displayName: Download Source artifact + patterns: "**" - pwsh: | - $sourceRoot = "$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework" + $sourceRoot = "$(Pipeline.Workspace)/Source" $archivePath = "$(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip" if (Test-Path -LiteralPath $archivePath) { @@ -697,7 +782,7 @@ stages: $detectRoot = "$(Pipeline.Workspace)/detect-ubuntu" $outputPath = "$(Build.ArtifactStagingDirectory)/blackduck-logs-ubuntu" $detectScript = Join-Path $detectRoot 'detect10.sh' - $sourcePath = "$(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework" + $sourcePath = "$(Pipeline.Workspace)/Source" $binaryScanPath = "$(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip" New-Item -Path $detectRoot -ItemType Directory -Force | Out-Null @@ -766,75 +851,6 @@ stages: displayName: Publish Black Duck network diagnostics (Ubuntu) condition: and(always(), eq(variables.HasBlackDuckNetworkDiagnosticsUbuntu, 'true')) - - stage: BuildWithShims - displayName: Build With Shims - dependsOn: HostedTests - condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}, ${{ eq(parameters.EnableComponentTemplateBlackDuckComparison, 'true') }}) - jobs: - - job: BuildWithShims - displayName: Build With Shims - timeoutInMinutes: "120" - pool: - name: Technology-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/dotnet/sdk:9.0 - workspace: - clean: all - steps: - - checkout: self - fetchDepth: "0" - - - checkout: Templates - fetchDepth: "1" - - - task: UseDotNet@2 - displayName: Ensure .NET 9 SDK for shim build - retryCountOnTaskFailure: 3 - inputs: - packageType: sdk - version: 9.0.x - - - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' - & (Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release - displayName: Build template and example projects with fake UE references - - - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' - $preparedRoot = Join-Path $env:PIPELINE_WORKSPACE 'prepared-component-source' - $preparedSource = Join-Path $preparedRoot 'Source' - $preparedBuildRelease = Join-Path $preparedSource 'Build/Release' - $shimArtifacts = Join-Path $repoRoot 'shims/artifacts/Release' - - if (Test-Path -LiteralPath $preparedRoot) { - Remove-Item -LiteralPath $preparedRoot -Recurse -Force - } - - New-Item -Path $preparedSource -ItemType Directory -Force | Out-Null - Copy-Item -Path (Join-Path $repoRoot '*') -Destination $preparedSource -Recurse -Force - - if (Test-Path -LiteralPath $shimArtifacts) { - New-Item -Path $preparedBuildRelease -ItemType Directory -Force | Out-Null - Copy-Item -Path (Join-Path $shimArtifacts '*') -Destination $preparedBuildRelease -Recurse -Force - } - - Write-Host "Prepared component-template source artifact at $preparedSource" - displayName: Prepare component-style Source artifact - - - publish: $(Pipeline.Workspace)/prepared-component-source/Source - artifact: Source - displayName: Publish Source artifact for component template comparison - - - publish: $(Pipeline.Workspace)/s/Templates - artifact: Templates - displayName: Publish Templates artifact for component template comparison - - - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/shims/artifacts/Release - artifact: Build.Release - displayName: Publish build artifact for component template comparison - - stage: ComponentTemplateBlackDuckComparison displayName: Component Template Black Duck Comparison dependsOn: BuildWithShims @@ -846,6 +862,7 @@ stages: BlackDuckGroup: DabaconGroup IsIPR: false ProjectName: $(BlackDuckProjectName) + PackageVersion: $(Build.SourceBranchName)-$(Build.BuildId)-components UbuntuPoolName: Technology-Microsoft-Hosted-Ubuntu UbuntuPoolDemands: - ImageOverride -equals ubuntu-latest From 9d92a38741de1a6672a9d8a2774271ef711c7b01 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Wed, 1 Apr 2026 04:07:26 +0100 Subject: [PATCH 049/102] Publish Source artifact earlier --- azure-pipelines.yml | 152 +++++++++++++++++++++++++------------------- 1 file changed, 85 insertions(+), 67 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c01ecf1..d5afb30 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -54,8 +54,55 @@ pr: - main stages: + - stage: InitialiseSource + displayName: Initialise Source + jobs: + - job: Source + displayName: Publish Source + pool: + name: Technology-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/dotnet/sdk:9.0 + workspace: + clean: all + steps: + - checkout: self + fetchDepth: "0" + path: Source + + - checkout: Templates + fetchDepth: "1" + path: Templates + + - pwsh: | + $sourceRoot = "$(Pipeline.Workspace)/Source" + $probeRoot = Join-Path $sourceRoot 'detector-coverage-probe' + + New-Item -Path $probeRoot -ItemType Directory -Force | Out-Null + $packagesConfig = @( + '' + '' + ' ' + '' + ) -join "`n" + Set-Content -LiteralPath (Join-Path $probeRoot 'packages.config') -Value $packagesConfig -Encoding utf8 + + New-Item -Path (Join-Path $sourceRoot '.artifactignore') -ItemType File -Force | Out-Null + displayName: Prepare Source artifact + + - publish: $(Pipeline.Workspace)/Source + artifact: Source + displayName: Publish Source + + - publish: $(Pipeline.Workspace)/Templates + artifact: Templates + displayName: Publish Templates + - stage: HostedTests displayName: Hosted Tests + dependsOn: InitialiseSource jobs: - job: Pester displayName: Pester @@ -68,8 +115,10 @@ stages: workspace: clean: all steps: - - checkout: self - fetchDepth: "0" + - download: current + artifact: Source + displayName: Download Source artifact + patterns: "**" - pwsh: | Write-Host "PowerShell version: $($PSVersionTable.PSVersion)" @@ -100,10 +149,13 @@ stages: - pwsh: | ./scripts/Tests/Run-Tests.ps1 -Quick displayName: Run tests + workingDirectory: $(Pipeline.Workspace)/Source - stage: CoverityValidation displayName: Coverity Validation - dependsOn: HostedTests + dependsOn: + - InitialiseSource + - HostedTests condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: - job: CoverityScan @@ -118,11 +170,15 @@ stages: workspace: clean: all steps: - - checkout: self - fetchDepth: "0" + - download: current + artifact: Source + displayName: Download Source artifact + patterns: "**" - - checkout: Templates - fetchDepth: "1" + - download: current + artifact: Templates + displayName: Download Templates artifact + patterns: "**" - pwsh: | dotnet --list-sdks @@ -140,12 +196,12 @@ stages: displayName: Show .NET SDK info - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' & (Join-Path $repoRoot 'scripts\Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release displayName: Build template and example projects with fake UE references - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' $paths = @{ HasShimSources = Test-Path -LiteralPath (Join-Path $repoRoot 'shims') HasShimManifest = Test-Path -LiteralPath (Join-Path $repoRoot 'shims/shim-projects.psd1') @@ -161,29 +217,29 @@ stages: displayName: Detect shim artifacts condition: always() - - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/shims + - publish: $(Pipeline.Workspace)/Source/shims artifact: ShimSources displayName: Publish shim sources condition: and(always(), eq(variables.HasShimSources, 'true')) - - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/shims/shim-projects.psd1 + - publish: $(Pipeline.Workspace)/Source/shims/shim-projects.psd1 artifact: ShimManifest displayName: Publish shim manifest condition: and(always(), eq(variables.HasShimManifest, 'true')) - - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/.tmp/generated-shims + - publish: $(Pipeline.Workspace)/Source/.tmp/generated-shims artifact: GeneratedShimProjects displayName: Publish generated shim project files condition: and(always(), eq(variables.HasGeneratedShimProjects, 'true')) - - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/shims/artifacts/Release + - publish: $(Pipeline.Workspace)/Source/shims/artifacts/Release artifact: ShimAssemblies displayName: Publish compiled shim assemblies condition: and(always(), eq(variables.HasShimAssemblies, 'true')) - pwsh: | $pwshPath = if ($IsLinux) { '/usr/bin/pwsh' } else { (Get-Command pwsh -ErrorAction Stop).Source } - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' $fakeCompileScriptPath = Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1' $branchName = "$env:BUILD_SOURCEBRANCH" -replace '^refs/heads/', '' @@ -258,7 +314,7 @@ stages: POLARIS_HOME: $(Pipeline.Workspace)/Coverity-CLI - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' $paths = @{ HasCoverityArtifacts = Test-Path -LiteralPath (Join-Path $repoRoot '.blackduck') } @@ -271,14 +327,16 @@ stages: displayName: Detect security artifacts condition: always() - - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/.blackduck + - publish: $(Pipeline.Workspace)/Source/.blackduck artifact: Coverity displayName: Publish Coverity artifacts condition: and(always(), eq(variables.HasCoverityArtifacts, 'true')) - stage: BuildWithShims displayName: Build With Shims - dependsOn: HostedTests + dependsOn: + - InitialiseSource + - HostedTests condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: - job: BuildWithShims @@ -293,11 +351,15 @@ stages: workspace: clean: all steps: - - checkout: self - fetchDepth: "0" + - download: current + artifact: Source + displayName: Download Source artifact + patterns: "**" - - checkout: Templates - fetchDepth: "1" + - download: current + artifact: Templates + displayName: Download Templates artifact + patterns: "**" - task: UseDotNet@2 displayName: Ensure .NET 9 SDK for shim build @@ -307,55 +369,11 @@ stages: version: 9.0.x - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' & (Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release displayName: Build template and example projects with fake UE references - - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 's/UnifiedEngineeringCustomizationFramework' - $preparedRoot = Join-Path $env:PIPELINE_WORKSPACE 'prepared-component-source' - $preparedSource = Join-Path $preparedRoot 'Source' - $preparedBuildRelease = Join-Path $preparedSource 'Build/Release' - $shimArtifacts = Join-Path $repoRoot 'shims/artifacts/Release' - $probeRoot = Join-Path $preparedSource 'detector-coverage-probe' - - if (Test-Path -LiteralPath $preparedRoot) { - Remove-Item -LiteralPath $preparedRoot -Recurse -Force - } - - New-Item -Path $preparedSource -ItemType Directory -Force | Out-Null - Get-ChildItem -Path $repoRoot -Force | ForEach-Object { - Copy-Item -LiteralPath $_.FullName -Destination $preparedSource -Recurse -Force - } - - if (Test-Path -LiteralPath $shimArtifacts) { - New-Item -Path $preparedBuildRelease -ItemType Directory -Force | Out-Null - Copy-Item -Path (Join-Path $shimArtifacts '*') -Destination $preparedBuildRelease -Recurse -Force - } - - New-Item -Path $probeRoot -ItemType Directory -Force | Out-Null - $packagesConfig = @( - '' - '' - ' ' - '' - ) -join "`n" - Set-Content -LiteralPath (Join-Path $probeRoot 'packages.config') -Value $packagesConfig -Encoding utf8 - - New-Item -Path (Join-Path $preparedSource '.artifactignore') -ItemType File -Force | Out-Null - - Write-Host "Prepared component-template source artifact at $preparedSource" - displayName: Prepare component-style Source artifact - - - publish: $(Pipeline.Workspace)/prepared-component-source/Source - artifact: Source - displayName: Publish Source artifact for Black Duck scans - - - publish: $(Pipeline.Workspace)/s/Templates - artifact: Templates - displayName: Publish Templates artifact for component template comparison - - - publish: $(Pipeline.Workspace)/s/UnifiedEngineeringCustomizationFramework/shims/artifacts/Release + - publish: $(Pipeline.Workspace)/Source/shims/artifacts/Release artifact: Build.Release displayName: Publish build artifact for component template comparison From 3447db08deeb98ccc4b50b8db39755fa16981680 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Wed, 1 Apr 2026 04:22:37 +0100 Subject: [PATCH 050/102] Use shared retrieve-source flow --- azure-pipelines.yml | 142 +++++++++++++++++++++++--------------------- 1 file changed, 74 insertions(+), 68 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d5afb30..b77ad60 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -100,6 +100,51 @@ stages: artifact: Templates displayName: Publish Templates + - stage: BuildWithShims + displayName: Build With Shims + dependsOn: InitialiseSource + condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) + jobs: + - job: BuildWithShims + displayName: Build With Shims + timeoutInMinutes: "120" + pool: + name: Technology-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/dotnet/sdk:9.0 + workspace: + clean: all + steps: + - template: ../_templates/steps/retrieve-source.yml@Templates + parameters: + IsIPR: false + ArtifactName: Source + DownloadPath: $(Pipeline.Workspace)/Source + FetchDepth: 0 + + - download: current + artifact: Templates + displayName: Download Templates artifact + patterns: "**" + + - task: UseDotNet@2 + displayName: Ensure .NET 9 SDK for shim build + retryCountOnTaskFailure: 3 + inputs: + packageType: sdk + version: 9.0.x + + - pwsh: | + $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' + & (Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release + displayName: Build template and example projects with fake UE references + + - publish: $(Pipeline.Workspace)/Source/shims/artifacts/Release + artifact: Build.Release + displayName: Publish build artifact for component template comparison + - stage: HostedTests displayName: Hosted Tests dependsOn: InitialiseSource @@ -115,10 +160,12 @@ stages: workspace: clean: all steps: - - download: current - artifact: Source - displayName: Download Source artifact - patterns: "**" + - template: ../_templates/steps/retrieve-source.yml@Templates + parameters: + IsIPR: false + ArtifactName: Source + DownloadPath: $(Pipeline.Workspace)/Source + FetchDepth: 0 - pwsh: | Write-Host "PowerShell version: $($PSVersionTable.PSVersion)" @@ -151,11 +198,9 @@ stages: displayName: Run tests workingDirectory: $(Pipeline.Workspace)/Source - - stage: CoverityValidation - displayName: Coverity Validation - dependsOn: - - InitialiseSource - - HostedTests + - stage: Coverity + displayName: Coverity + dependsOn: InitialiseSource condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: - job: CoverityScan @@ -170,10 +215,12 @@ stages: workspace: clean: all steps: - - download: current - artifact: Source - displayName: Download Source artifact - patterns: "**" + - template: ../_templates/steps/retrieve-source.yml@Templates + parameters: + IsIPR: false + ArtifactName: Source + DownloadPath: $(Pipeline.Workspace)/Source + FetchDepth: 0 - download: current artifact: Templates @@ -332,53 +379,8 @@ stages: displayName: Publish Coverity artifacts condition: and(always(), eq(variables.HasCoverityArtifacts, 'true')) - - stage: BuildWithShims - displayName: Build With Shims - dependsOn: - - InitialiseSource - - HostedTests - condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) - jobs: - - job: BuildWithShims - displayName: Build With Shims - timeoutInMinutes: "120" - pool: - name: Technology-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/dotnet/sdk:9.0 - workspace: - clean: all - steps: - - download: current - artifact: Source - displayName: Download Source artifact - patterns: "**" - - - download: current - artifact: Templates - displayName: Download Templates artifact - patterns: "**" - - - task: UseDotNet@2 - displayName: Ensure .NET 9 SDK for shim build - retryCountOnTaskFailure: 3 - inputs: - packageType: sdk - version: 9.0.x - - - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' - & (Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release - displayName: Build template and example projects with fake UE references - - - publish: $(Pipeline.Workspace)/Source/shims/artifacts/Release - artifact: Build.Release - displayName: Publish build artifact for component template comparison - - - stage: BlackDuckValidation - displayName: Black Duck Validation + - stage: BlackDuck + displayName: Black Duck dependsOn: BuildWithShims condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: @@ -571,10 +573,12 @@ stages: - name: SemanticVersion value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.SemanticVersion'] ] steps: - - download: current - artifact: Source - displayName: Download Source artifact - patterns: "**" + - template: ../_templates/steps/retrieve-source.yml@Templates + parameters: + IsIPR: false + ArtifactName: Source + DownloadPath: $(Pipeline.Workspace)/Source + FetchDepth: 0 - pwsh: | $workdir = "$(Pipeline.Workspace)\JDK" @@ -693,10 +697,12 @@ stages: - name: UbuntuComparisonVersion value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.UbuntuComparisonVersion'] ] steps: - - download: current - artifact: Source - displayName: Download Source artifact - patterns: "**" + - template: ../_templates/steps/retrieve-source.yml@Templates + parameters: + IsIPR: false + ArtifactName: Source + DownloadPath: $(Pipeline.Workspace)/Source + FetchDepth: 0 - pwsh: | $sourceRoot = "$(Pipeline.Workspace)/Source" From f2232c533ae669cc5755ed02232a68e92778fc6d Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Wed, 1 Apr 2026 04:22:58 +0100 Subject: [PATCH 051/102] Fix retrieve-source template paths --- azure-pipelines.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b77ad60..821a268 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -117,7 +117,7 @@ stages: workspace: clean: all steps: - - template: ../_templates/steps/retrieve-source.yml@Templates + - template: steps/retrieve-source.yml@Templates parameters: IsIPR: false ArtifactName: Source @@ -160,7 +160,7 @@ stages: workspace: clean: all steps: - - template: ../_templates/steps/retrieve-source.yml@Templates + - template: steps/retrieve-source.yml@Templates parameters: IsIPR: false ArtifactName: Source @@ -215,7 +215,7 @@ stages: workspace: clean: all steps: - - template: ../_templates/steps/retrieve-source.yml@Templates + - template: steps/retrieve-source.yml@Templates parameters: IsIPR: false ArtifactName: Source @@ -573,7 +573,7 @@ stages: - name: SemanticVersion value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.SemanticVersion'] ] steps: - - template: ../_templates/steps/retrieve-source.yml@Templates + - template: steps/retrieve-source.yml@Templates parameters: IsIPR: false ArtifactName: Source @@ -697,7 +697,7 @@ stages: - name: UbuntuComparisonVersion value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.UbuntuComparisonVersion'] ] steps: - - template: ../_templates/steps/retrieve-source.yml@Templates + - template: steps/retrieve-source.yml@Templates parameters: IsIPR: false ArtifactName: Source From 82e9445c5bb068dc1c7e131281ca7b117ba511d3 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Wed, 1 Apr 2026 04:36:23 +0100 Subject: [PATCH 052/102] Split Black Duck Windows and Ubuntu stages --- azure-pipelines.yml | 164 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 821a268..2311050 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -156,7 +156,7 @@ stages: demands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/powershell:7.4-ubuntu-22.04 + container: mcr.microsoft.com/dotnet/sdk:9.0 workspace: clean: all steps: @@ -379,8 +379,8 @@ stages: displayName: Publish Coverity artifacts condition: and(always(), eq(variables.HasCoverityArtifacts, 'true')) - - stage: BlackDuck - displayName: Black Duck + - stage: BlackDuckWindows + displayName: Black Duck - Windows dependsOn: BuildWithShims condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: @@ -677,6 +677,164 @@ stages: displayName: Publish Black Duck input archive condition: and(always(), eq(variables.HasBlackDuckInput, 'true')) + - stage: BlackDuckUbuntu + displayName: Black Duck - Ubuntu + dependsOn: BuildWithShims + condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) + jobs: + - job: BlackDuckInitialisation + displayName: BlackDuck Initialise + timeoutInMinutes: "10" + pool: + name: Technology-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/dotnet/sdk:9.0 + workspace: + clean: all + variables: + - group: BlackDuck on Polaris + steps: + - checkout: none + + - pwsh: | + $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') + $templatesPostfix = ($templatesBranch -and $templatesBranch -ne 'main') ` + ? "@$($templatesBranch)" ` + : '' + + $branchVersion = "$env:BUILD_SOURCEBRANCHNAME$templatesPostfix" + + if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { + $baseSemanticVersion = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)" + } + else { + $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' + $baseSemanticVersion = "$branchName-$($env:BUILD_BUILDID)" + } + + $ubuntuComparisonVersion = "$baseSemanticVersion-linux$templatesPostfix" + + Write-Host "##vso[task.setvariable variable=BranchVersion]$branchVersion" + Write-Host "##vso[task.setvariable variable=BranchVersion;isOutput=true]$branchVersion" + Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion]$ubuntuComparisonVersion" + Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion;isOutput=true]$ubuntuComparisonVersion" + displayName: Calculate Black Duck versions + name: CalculateVersion + + - pwsh: | + $headers = @{ + Authorization = "token $env:BLACKDUCK_ACCESS_TOKEN" + Accept = 'application/vnd.blackducksoftware.user-4+json' + } + + $uri = "$env:BLACKDUCK_URL/api/tokens/authenticate" + $response = Invoke-RestMethod -Method POST -Uri $uri -Headers $headers + + Write-Host "##vso[task.setvariable variable=BaseUrl]$env:BLACKDUCK_URL" + Write-Host "##vso[task.setvariable variable=BaseUrl;isOutput=true]$env:BLACKDUCK_URL" + Write-Host "##vso[task.setvariable variable=BearerToken]$($response.bearerToken)" + Write-Host "##vso[task.setvariable variable=BearerToken;isOutput=true]$($response.bearerToken)" + displayName: Authenticate to Black Duck + name: Authenticate + retryCountOnTaskFailure: 3 + env: + BLACKDUCK_URL: https://aveva.app.blackduck.com + BLACKDUCK_ACCESS_TOKEN: $(BLACKDUCK_ACCESS_TOKEN) + + - pwsh: | + $headers = @{ + Authorization = "Bearer $env:BearerToken" + Accept = 'application/vnd.blackducksoftware.project-detail-7+json' + } + + $projectName = '$(BlackDuckProjectName)' + $limit = 100 + $offset = 0 + $allProjects = @() + + do { + $uri = "$env:BaseUrl/api/projects?limit=$limit&offset=$offset&q=name:$projectName" + $response = Invoke-RestMethod -Method GET -Uri $uri -Headers $headers + if ($response.items) { + $allProjects += $response.items + } + $offset += $limit + } while ($offset -lt $response.totalCount) + + $matchedProject = $allProjects | Where-Object { $_.name -eq $projectName } + + if (-not $matchedProject) { + Write-Host "Project '$projectName' not found in Black Duck." + Write-Host "##vso[task.setvariable variable=ProjectExists]false" + Write-Host "##vso[task.setvariable variable=ProjectExists;isOutput=true]false" + Write-Host "##vso[task.logissue type=error]Black Duck project '$projectName' must exist before the scan runs. Create it or grant the scanner access to the existing project." + exit 1 + } + + $projectId = $matchedProject._meta.href.Split('/')[-1] + Write-Host "##vso[task.setvariable variable=ProjectId]$projectId" + Write-Host "##vso[task.setvariable variable=ProjectId;isOutput=true]$projectId" + Write-Host "##vso[task.setvariable variable=ProjectExists]true" + Write-Host "##vso[task.setvariable variable=ProjectExists;isOutput=true]true" + displayName: Get Black Duck project ID + name: ProjectId + retryCountOnTaskFailure: 3 + env: + BearerToken: $(BearerToken) + BaseUrl: $(BaseUrl) + + - job: BlackDuckClone + displayName: BlackDuck Clone + dependsOn: BlackDuckInitialisation + condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true')) + timeoutInMinutes: "10" + pool: + name: Technology-Microsoft-Hosted-Ubuntu + demands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/dotnet/sdk:9.0 + workspace: + clean: all + variables: + - group: BlackDuck on Polaris + - name: BearerToken + value: $[ dependencies.BlackDuckInitialisation.outputs['Authenticate.BearerToken'] ] + - name: BaseUrl + value: $[ dependencies.BlackDuckInitialisation.outputs['Authenticate.BaseUrl'] ] + - name: ProjectId + value: $[ dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectId'] ] + - name: ProjectExists + value: $[ dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'] ] + - name: BranchVersion + value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.BranchVersion'] ] + - name: UbuntuComparisonVersion + value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.UbuntuComparisonVersion'] ] + steps: + - checkout: none + + - template: steps/blackduck-clone-version.yml@Templates + parameters: + BearerToken: $(BearerToken) + BaseUrl: $(BaseUrl) + ProjectId: $(ProjectId) + SourceVersion: main + TargetVersion: $(BranchVersion) + Phase: DEVELOPMENT + RemoveTargetVersion: false + + - template: steps/blackduck-clone-version.yml@Templates + parameters: + BearerToken: $(BearerToken) + BaseUrl: $(BaseUrl) + ProjectId: $(ProjectId) + SourceVersion: $(BranchVersion) + TargetVersion: $(UbuntuComparisonVersion) + Phase: PLANNING + RemoveTargetVersion: true + - job: BlackDuckScanUbuntu displayName: Black Duck Scan Ubuntu dependsOn: From c847e3d679a03f17a6ad8ef40a44b3db4112916b Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Wed, 1 Apr 2026 05:11:53 +0100 Subject: [PATCH 053/102] Add richer Black Duck detector probes --- azure-pipelines.yml | 58 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2311050..c30c434 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -79,15 +79,69 @@ stages: - pwsh: | $sourceRoot = "$(Pipeline.Workspace)/Source" $probeRoot = Join-Path $sourceRoot 'detector-coverage-probe' + $classicProbeRoot = Join-Path $probeRoot 'ClassicProbe' + $sdkProbeRoot = Join-Path $probeRoot 'SdkProbe' New-Item -Path $probeRoot -ItemType Directory -Force | Out-Null - $packagesConfig = @( + New-Item -Path $classicProbeRoot -ItemType Directory -Force | Out-Null + New-Item -Path $sdkProbeRoot -ItemType Directory -Force | Out-Null + + $classicPackagesConfig = @( '' '' ' ' '' ) -join "`n" - Set-Content -LiteralPath (Join-Path $probeRoot 'packages.config') -Value $packagesConfig -Encoding utf8 + + $classicProject = @( + '' + ' ' + ' Debug' + ' AnyCPU' + ' Library' + ' v4.8.1' + ' ClassicProbe' + ' ClassicProbe' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '' + ) -join "`n" + + $classicSource = @( + 'namespace ClassicProbe' + '{' + ' public class Class1 { }' + '}' + ) -join "`n" + + $sdkProject = @( + '' + ' ' + ' net8.0' + ' disable' + ' disable' + ' ' + ' ' + ' ' + ' ' + '' + ) -join "`n" + + $sdkSource = @( + 'namespace SdkProbe;' + 'public class Class1 { }' + ) -join "`n" + + Set-Content -LiteralPath (Join-Path $classicProbeRoot 'packages.config') -Value $classicPackagesConfig -Encoding utf8 + Set-Content -LiteralPath (Join-Path $classicProbeRoot 'ClassicProbe.csproj') -Value $classicProject -Encoding utf8 + Set-Content -LiteralPath (Join-Path $classicProbeRoot 'Class1.cs') -Value $classicSource -Encoding utf8 + + Set-Content -LiteralPath (Join-Path $sdkProbeRoot 'SdkProbe.csproj') -Value $sdkProject -Encoding utf8 + Set-Content -LiteralPath (Join-Path $sdkProbeRoot 'Class1.cs') -Value $sdkSource -Encoding utf8 New-Item -Path (Join-Path $sourceRoot '.artifactignore') -ItemType File -Force | Out-Null displayName: Prepare Source artifact From 6ded21a05e49c633977082fcfb15e423cf19d1cc Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Thu, 2 Apr 2026 17:26:53 +0100 Subject: [PATCH 054/102] Refactor Azure pipeline to use main Templates repository and adjust Black Duck parameters --- azure-pipelines.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c30c434..07d5502 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -17,10 +17,6 @@ resources: type: git name: Technology/Templates ref: refs/heads/main - - repository: TemplatesCustom - type: git - name: Dabacon Products/Templates - ref: refs/heads/feature/uecf-blackduck-pool-params parameters: - name: EnableSecurityValidation @@ -607,7 +603,7 @@ stages: SourceVersion: $(BranchVersion) TargetVersion: $(UbuntuComparisonVersion) Phase: PLANNING - RemoveTargetVersion: true + RemoveTargetVersion: false - job: BlackDuckScan displayName: Black Duck Scan @@ -1092,7 +1088,7 @@ stages: dependsOn: BuildWithShims condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}, ${{ eq(parameters.EnableComponentTemplateBlackDuckComparison, 'true') }}) jobs: - - template: component-blackduck.yml@TemplatesCustom + - template: component-blackduck.yml@Templates parameters: BuildArtifact: Build.Release BlackDuckGroup: DabaconGroup From 78b0a9704c0a831b38f3acf941495814376bffa6 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Thu, 2 Apr 2026 17:36:12 +0100 Subject: [PATCH 055/102] Fix component Black Duck template parameters --- azure-pipelines.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 07d5502..b3fb966 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1095,10 +1095,3 @@ stages: IsIPR: false ProjectName: $(BlackDuckProjectName) PackageVersion: $(Build.SourceBranchName)-$(Build.BuildId)-components - UbuntuPoolName: Technology-Microsoft-Hosted-Ubuntu - UbuntuPoolDemands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - WindowsPoolName: Technology-Microsoft-Hosted-Windows - WindowsPoolDemands: - - ImageOverride -equals windows-latest From e88549f656486643ac91e5adf843a014757124d3 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Thu, 2 Apr 2026 17:53:35 +0100 Subject: [PATCH 056/102] Use Dabacon Templates directly --- azure-pipelines.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b3fb966..29a586f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,7 +6,7 @@ # The hosted test stage can run immediately on the Technology stateful hosted pools. # The security stage remains opt-in, but now uses the hosted-compatible fake-compile flow # on the Technology stateful hosted Ubuntu pool instead of generic vmImage selection. -# Templates is consumed from a Technology fork to avoid cross-project checkout permissions. +# Templates is consumed directly from Dabacon Products to avoid fork drift. # The Technology project still needs: # - a self-hosted Windows pool with UE installed and MSBuild available # - service connections named `Coverity on Polaris` and `BlackDuckScanner` @@ -15,7 +15,7 @@ resources: repositories: - repository: Templates type: git - name: Technology/Templates + name: Dabacon Products/Templates ref: refs/heads/main parameters: @@ -1095,3 +1095,10 @@ stages: IsIPR: false ProjectName: $(BlackDuckProjectName) PackageVersion: $(Build.SourceBranchName)-$(Build.BuildId)-components + UbuntuPoolName: Technology-Microsoft-Hosted-Ubuntu + UbuntuPoolDemands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + WindowsPoolName: Technology-Microsoft-Hosted-Windows + WindowsPoolDemands: + - ImageOverride -equals windows-latest From 0cd237cf29b188cf8df66fb564e7da2b60d7fca5 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Thu, 2 Apr 2026 19:09:27 +0100 Subject: [PATCH 057/102] Repoint UE pipeline to Technology Templates --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 29a586f..c455403 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,7 +6,7 @@ # The hosted test stage can run immediately on the Technology stateful hosted pools. # The security stage remains opt-in, but now uses the hosted-compatible fake-compile flow # on the Technology stateful hosted Ubuntu pool instead of generic vmImage selection. -# Templates is consumed directly from Dabacon Products to avoid fork drift. +# Templates is consumed from Technology/Templates. # The Technology project still needs: # - a self-hosted Windows pool with UE installed and MSBuild available # - service connections named `Coverity on Polaris` and `BlackDuckScanner` @@ -15,7 +15,7 @@ resources: repositories: - repository: Templates type: git - name: Dabacon Products/Templates + name: Technology/Templates ref: refs/heads/main parameters: From 7c9348625a6044fb5eab2ae2091cc6de10e0063f Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski Date: Thu, 2 Apr 2026 20:00:34 +0100 Subject: [PATCH 058/102] Remove unshallow from source artifact retrieval --- azure-pipelines.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c455403..64baf44 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -172,7 +172,6 @@ stages: IsIPR: false ArtifactName: Source DownloadPath: $(Pipeline.Workspace)/Source - FetchDepth: 0 - download: current artifact: Templates @@ -215,7 +214,6 @@ stages: IsIPR: false ArtifactName: Source DownloadPath: $(Pipeline.Workspace)/Source - FetchDepth: 0 - pwsh: | Write-Host "PowerShell version: $($PSVersionTable.PSVersion)" @@ -270,7 +268,6 @@ stages: IsIPR: false ArtifactName: Source DownloadPath: $(Pipeline.Workspace)/Source - FetchDepth: 0 - download: current artifact: Templates @@ -628,7 +625,6 @@ stages: IsIPR: false ArtifactName: Source DownloadPath: $(Pipeline.Workspace)/Source - FetchDepth: 0 - pwsh: | $workdir = "$(Pipeline.Workspace)\JDK" @@ -910,7 +906,6 @@ stages: IsIPR: false ArtifactName: Source DownloadPath: $(Pipeline.Workspace)/Source - FetchDepth: 0 - pwsh: | $sourceRoot = "$(Pipeline.Workspace)/Source" From d6f16cb25f7c0713c05c832678e93b06747fbda1 Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:24:11 +0100 Subject: [PATCH 059/102] Add dedicated anti-malware pipeline job --- azure-pipelines.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 64baf44..48bda45 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -17,6 +17,10 @@ resources: type: git name: Technology/Templates ref: refs/heads/main + - repository: antiMalwareTemplate + type: git + name: Architecture/Architecture + ref: refs/heads/antiMalwareVersions/latest parameters: - name: EnableSecurityValidation @@ -602,11 +606,44 @@ stages: Phase: PLANNING RemoveTargetVersion: false + - job: AntiMalwareScan + displayName: AntiMalware Scan + dependsOn: + - BlackDuckInitialisation + - BlackDuckClone + condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true')) + timeoutInMinutes: "60" + pool: + name: Technology-Microsoft-Hosted-Windows + demands: + - ImageOverride -equals windows-latest + workspace: + clean: all + steps: + - template: steps/retrieve-source.yml@Templates + parameters: + IsIPR: false + ArtifactName: Source + DownloadPath: $(Pipeline.Workspace)/Source + + - pwsh: | + Start-Process powershell.exe -ArgumentList "Update-MpSignature -UpdateSource MicrosoftUpdateServer" -Wait + Write-Output "updated the definitions successfully" + displayName: Antimalware Scan task + + - template: Pipelines/Templates/AntiMalware/AntiMalware.step.yml@antiMalwareTemplate + parameters: + scanType: Directory + scanDirectory: $(Pipeline.Workspace)\Source + artifactName: AntimalwareResults_Source + os: Windows_NT + - job: BlackDuckScan displayName: Black Duck Scan dependsOn: - BlackDuckInitialisation - BlackDuckClone + - AntiMalwareScan condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true')) timeoutInMinutes: "120" pool: From cd46b9f9e23e86838efa821df771372507281fdd Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Wed, 15 Apr 2026 09:53:03 +0100 Subject: [PATCH 060/102] Refine generated shim project graph --- scripts/Invoke-FakeUECompile.ps1 | 94 ++++++++++++++- shims/shim-projects.psd1 | 113 +++++++++++++++--- shims/src/ApplicationFramework.cs | 65 ++++++++++ shims/src/ApplicationFrameworkPresentation.cs | 63 ---------- shims/src/FlexibleExplorer.cs | 40 ------- shims/src/FlexibleExplorerCore.cs | 41 +++++++ shims/src/WebView2.cs | 31 ----- shims/src/WebView2Core.cs | 32 +++++ 8 files changed, 328 insertions(+), 151 deletions(-) create mode 100644 shims/src/ApplicationFramework.cs create mode 100644 shims/src/FlexibleExplorerCore.cs create mode 100644 shims/src/WebView2Core.cs diff --git a/scripts/Invoke-FakeUECompile.ps1 b/scripts/Invoke-FakeUECompile.ps1 index c1111fe..7d1a847 100644 --- a/scripts/Invoke-FakeUECompile.ps1 +++ b/scripts/Invoke-FakeUECompile.ps1 @@ -37,6 +37,7 @@ if (-not $ShimOutputPath) { $resolvedShimProjectsPath = (Resolve-Path $ShimProjectsPath).Path $resolvedShimManifestPath = (Resolve-Path $ShimManifestPath).Path $resolvedGeneratedShimProjectsPath = [System.IO.Path]::GetFullPath($GeneratedShimProjectsPath) +$resolvedShimOutputDirectory = [System.IO.Path]::GetFullPath($ShimOutputPath) $shimDirectoryBuildPropsPath = Join-Path $resolvedShimProjectsPath 'Directory.Build.props' $buildScriptPath = Join-Path $PSScriptRoot 'Build-AddIns.ps1' @@ -115,14 +116,46 @@ foreach ($projectEntry in $shimProjects.GetEnumerator()) { $projectLines.Add(" $rootNamespace") $projectLines.Add(' ') - if ($projectConfig.ContainsKey('CompileInclude')) { + $sourcePaths = [System.Collections.Generic.List[string]]::new() + if ($projectConfig.ContainsKey('Sources')) { + foreach ($sourceEntry in @($projectConfig.Sources)) { + if (-not [string]::IsNullOrWhiteSpace([string]$sourceEntry)) { + $sourcePath = [string]$sourceEntry + if (-not [System.IO.Path]::IsPathRooted($sourcePath)) { + $sourcePath = [System.IO.Path]::GetFullPath((Join-Path $resolvedShimProjectsPath $sourcePath)) + } + + $null = $sourcePaths.Add($sourcePath) + } + } + } + elseif ($projectConfig.ContainsKey('CompileInclude')) { $compileIncludePath = [string]$projectConfig.CompileInclude if (-not [System.IO.Path]::IsPathRooted($compileIncludePath)) { $compileIncludePath = [System.IO.Path]::GetFullPath((Join-Path $resolvedShimProjectsPath $compileIncludePath)) } + $null = $sourcePaths.Add($compileIncludePath) + } + + if ($sourcePaths.Count -gt 0) { + $projectLines.Add(' ') + foreach ($sourcePath in $sourcePaths) { + $projectLines.Add((' ' -f $sourcePath.Replace('&', '&'))) + } + $projectLines.Add(' ') + } + + if ($projectConfig.ContainsKey('ProjectReferences') -and @($projectConfig.ProjectReferences).Count -gt 0) { $projectLines.Add(' ') - $projectLines.Add((' ' -f $compileIncludePath.Replace('&', '&'))) + foreach ($projectReference in @($projectConfig.ProjectReferences)) { + $projectReferenceName = [string]$projectReference + $projectReferenceDllPath = Join-Path $resolvedShimOutputDirectory "$projectReferenceName.dll" + $projectLines.Add((' ' -f $projectReferenceName.Replace('&', '&'))) + $projectLines.Add((' {0}' -f $projectReferenceDllPath.Replace('&', '&'))) + $projectLines.Add(' false') + $projectLines.Add(' ') + } $projectLines.Add(' ') } @@ -132,7 +165,62 @@ foreach ($projectEntry in $shimProjects.GetEnumerator()) { $projectLines -join [Environment]::NewLine | Set-Content -LiteralPath $generatedProjectPath -Encoding utf8 } -$projects = Get-ChildItem -Path $resolvedGeneratedShimProjectsPath -Filter '*.csproj' -File | Sort-Object Name +function Add-ShimProjectBuildOrder { + param( + [Parameter(Mandatory = $true)] + [string]$ProjectName, + + [Parameter(Mandatory = $true)] + [hashtable]$ProjectsByName, + + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [System.Collections.Generic.HashSet[string]]$Visiting, + + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [System.Collections.Generic.HashSet[string]]$Visited, + + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [System.Collections.Generic.List[string]]$BuildOrder + ) + + if ($Visited.Contains($ProjectName)) { + return + } + + if (-not $Visiting.Add($ProjectName)) { + throw "Detected circular shim dependency involving '$ProjectName'" + } + + $projectConfig = $ProjectsByName[$ProjectName] + if ($projectConfig -and $projectConfig.ContainsKey('ProjectReferences')) { + foreach ($projectReference in @($projectConfig.ProjectReferences)) { + $projectReferenceName = [string]$projectReference + if (-not $ProjectsByName.ContainsKey($projectReferenceName)) { + throw "Shim project '$ProjectName' references undefined shim project '$projectReferenceName'" + } + + Add-ShimProjectBuildOrder -ProjectName $projectReferenceName -ProjectsByName $ProjectsByName -Visiting $Visiting -Visited $Visited -BuildOrder $BuildOrder + } + } + + $null = $Visiting.Remove($ProjectName) + $null = $Visited.Add($ProjectName) + $BuildOrder.Add($ProjectName) +} + +$orderedProjectNames = [System.Collections.Generic.List[string]]::new() +$visitingProjects = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) +$visitedProjects = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) +foreach ($projectName in $shimProjects.Keys) { + Add-ShimProjectBuildOrder -ProjectName ([string]$projectName) -ProjectsByName $shimProjects -Visiting $visitingProjects -Visited $visitedProjects -BuildOrder $orderedProjectNames +} + +$projects = foreach ($projectName in $orderedProjectNames) { + Get-Item -LiteralPath (Join-Path $resolvedGeneratedShimProjectsPath "$projectName.csproj") +} if (-not $projects) { throw "No generated shim projects found under $resolvedGeneratedShimProjectsPath" } diff --git a/shims/shim-projects.psd1 b/shims/shim-projects.psd1 index 207fef3..0c8734e 100644 --- a/shims/shim-projects.psd1 +++ b/shims/shim-projects.psd1 @@ -1,24 +1,109 @@ @{ Projects = @{ - 'Aveva.ApplicationFramework' = @{} + 'Aveva.ApplicationFramework' = @{ + Sources = @( + 'src\ApplicationFramework.cs' + ) + ProjectReferences = @( + 'Aveva.ApplicationFramework.Presentation' + ) + } 'Aveva.ApplicationFramework.Implementation' = @{} - 'Aveva.ApplicationFramework.Presentation' = @{} + 'Aveva.ApplicationFramework.Presentation' = @{ + Sources = @( + 'src\ApplicationFrameworkPresentation.cs' + ) + } 'Aveva.Core.Database' = @{ - CompileInclude = 'src\**\*.cs' + Sources = @( + 'src\Database.cs' + ) + ProjectReferences = @( + 'Aveva.Core.Geometry' + 'Aveva.Core.Utilities' + ) + } + 'Aveva.Core.Database.Filters' = @{ + Sources = @( + 'src\Filters.cs' + ) + ProjectReferences = @( + 'Aveva.Core.Database' + ) + } + 'Aveva.Core.FlexibleExplorer.Control' = @{ + Sources = @( + 'src\FlexibleExplorer.cs' + ) + ProjectReferences = @( + 'Aveva.ApplicationFramework' + 'Aveva.Core.FlexibleExplorer.Core' + 'Aveva.Core.Presentation' + ) + } + 'Aveva.Core.FlexibleExplorer.Core' = @{ + Sources = @( + 'src\FlexibleExplorerCore.cs' + ) + ProjectReferences = @( + 'Aveva.Core.Presentation' + ) + } + 'Aveva.Core.Geometry' = @{ + Sources = @( + 'src\Geometry.cs' + ) } - 'Aveva.Core.Database.Filters' = @{} - 'Aveva.Core.FlexibleExplorer.Control' = @{} - 'Aveva.Core.FlexibleExplorer.Core' = @{} - 'Aveva.Core.Geometry' = @{} 'Aveva.Core.Implementation' = @{} - 'Aveva.Core.Presentation' = @{} - 'Aveva.Core.Utilities' = @{} - 'Aveva.Core3D.Clasher' = @{} + 'Aveva.Core.Presentation' = @{ + Sources = @( + 'src\Presentation.cs' + ) + ProjectReferences = @( + 'Aveva.ApplicationFramework.Presentation' + 'Aveva.Core.Database' + 'PMLNet' + ) + } + 'Aveva.Core.Utilities' = @{ + Sources = @( + 'src\Utilities.cs' + ) + } + 'Aveva.Core3D.Clasher' = @{ + Sources = @( + 'src\Clasher.cs' + ) + ProjectReferences = @( + 'Aveva.Core.Database' + 'Aveva.Core.Geometry' + ) + } 'GridControl' = @{} - 'Microsoft.Web.WebView2.Core' = @{} - 'Microsoft.Web.WebView2.WinForms' = @{} - 'Newtonsoft.Json' = @{} - 'PMLNet' = @{} + 'Microsoft.Web.WebView2.Core' = @{ + Sources = @( + 'src\WebView2Core.cs' + ) + } + 'Microsoft.Web.WebView2.WinForms' = @{ + Sources = @( + 'src\WebView2.cs' + ) + ProjectReferences = @( + 'Microsoft.Web.WebView2.Core' + ) + } + 'Newtonsoft.Json' = @{ + Sources = @( + 'src\Json.cs' + ) + } + 'PMLNet' = @{ + Sources = @( + 'src\PmlNet.cs' + ) + RootNamespace = 'Aveva.Core.PMLNet' + } 'StartUp' = @{} } } \ No newline at end of file diff --git a/shims/src/ApplicationFramework.cs b/shims/src/ApplicationFramework.cs new file mode 100644 index 0000000..c9636ca --- /dev/null +++ b/shims/src/ApplicationFramework.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; + +namespace Aveva.ApplicationFramework +{ + public interface IAddinInjected + { + string Name { get; } + + string Description { get; } + + void Start(IDependencyResolver resolver); + + void Start(ServiceManager serviceManager); + + void Stop(); + } + + public interface IDependencyResolver + { + } + + public sealed class ServiceManager + { + } + + public static class DependencyResolver + { + private static readonly Aveva.ApplicationFramework.Presentation.WindowManager s_windowManager = new Aveva.ApplicationFramework.Presentation.WindowManager(); + private static readonly Aveva.ApplicationFramework.Presentation.CommandManager s_commandManager = new Aveva.ApplicationFramework.Presentation.CommandManager(); + private static readonly Dictionary s_services = new Dictionary + { + { typeof(Aveva.ApplicationFramework.Presentation.IWindowManager), s_windowManager }, + { typeof(Aveva.ApplicationFramework.Presentation.ICommandManager), s_commandManager }, + { typeof(Aveva.ApplicationFramework.Presentation.ICommandBarManager), s_windowManager.CommandBarManager }, + }; + + public static T GetImplementationOf() where T : class + { + if (s_services.TryGetValue(typeof(T), out object service)) + { + return service as T; + } + + return null; + } + } + + public static class Log + { + private static readonly LoggerProxy s_logger = new LoggerProxy(); + + public static LoggerProxy Logger() + { + return s_logger; + } + } + + public sealed class LoggerProxy + { + public void MessageFormat(string format, params object[] args) + { + } + } +} \ No newline at end of file diff --git a/shims/src/ApplicationFrameworkPresentation.cs b/shims/src/ApplicationFrameworkPresentation.cs index cf16152..89218c3 100644 --- a/shims/src/ApplicationFrameworkPresentation.cs +++ b/shims/src/ApplicationFrameworkPresentation.cs @@ -3,69 +3,6 @@ using System.ComponentModel; using System.Windows.Forms; -namespace Aveva.ApplicationFramework -{ - public interface IAddinInjected - { - string Name { get; } - - string Description { get; } - - void Start(IDependencyResolver resolver); - - void Start(ServiceManager serviceManager); - - void Stop(); - } - - public interface IDependencyResolver - { - } - - public sealed class ServiceManager - { - } - - public static class DependencyResolver - { - private static readonly Aveva.ApplicationFramework.Presentation.WindowManager s_windowManager = new Aveva.ApplicationFramework.Presentation.WindowManager(); - private static readonly Aveva.ApplicationFramework.Presentation.CommandManager s_commandManager = new Aveva.ApplicationFramework.Presentation.CommandManager(); - private static readonly Dictionary s_services = new Dictionary - { - { typeof(Aveva.ApplicationFramework.Presentation.IWindowManager), s_windowManager }, - { typeof(Aveva.ApplicationFramework.Presentation.ICommandManager), s_commandManager }, - { typeof(Aveva.ApplicationFramework.Presentation.ICommandBarManager), s_windowManager.CommandBarManager }, - }; - - public static T GetImplementationOf() where T : class - { - if (s_services.TryGetValue(typeof(T), out object service)) - { - return service as T; - } - - return null; - } - } - - public static class Log - { - private static readonly LoggerProxy s_logger = new LoggerProxy(); - - public static LoggerProxy Logger() - { - return s_logger; - } - } - - public sealed class LoggerProxy - { - public void MessageFormat(string format, params object[] args) - { - } - } -} - namespace Aveva.ApplicationFramework.Presentation { public class Command diff --git a/shims/src/FlexibleExplorer.cs b/shims/src/FlexibleExplorer.cs index b8ea1ca..0b95ee8 100644 --- a/shims/src/FlexibleExplorer.cs +++ b/shims/src/FlexibleExplorer.cs @@ -1,47 +1,7 @@ -using System.IO; using System.Windows.Forms; using Aveva.ApplicationFramework; using Aveva.Core.Presentation; -namespace Aveva.Core.FlexibleExplorer -{ - public enum RefreshOperation - { - None, - NodesOnly, - NodesWithCollections, - WholeTree, - } - - public class RefreshArguments - { - public DbChangesEventArgs DbChangesEventArgs { get; set; } - - public object DbRawChanges { get; set; } - - public RefreshOperation RefreshType { get; set; } - } - - public interface IFlexibleExplorerRefreshManager - { - RefreshArguments GetRefreshType(RefreshArguments refreshArguments); - } - - public class XmlDefinitionProvider - { - public XmlDefinitionProvider(string xmlContent, string fileName) - { - } - - public object RootDefinitions { get; } = new object[0]; - - public static string GetLocalFile(string path) - { - return File.Exists(path) ? path : null; - } - } -} - namespace Aveva.Core.FlexibleExplorer { public class FlexibleExplorer : Control diff --git a/shims/src/FlexibleExplorerCore.cs b/shims/src/FlexibleExplorerCore.cs new file mode 100644 index 0000000..64080d1 --- /dev/null +++ b/shims/src/FlexibleExplorerCore.cs @@ -0,0 +1,41 @@ +using System.IO; +using Aveva.Core.Presentation; + +namespace Aveva.Core.FlexibleExplorer +{ + public enum RefreshOperation + { + None, + NodesOnly, + NodesWithCollections, + WholeTree, + } + + public class RefreshArguments + { + public DbChangesEventArgs DbChangesEventArgs { get; set; } + + public object DbRawChanges { get; set; } + + public RefreshOperation RefreshType { get; set; } + } + + public interface IFlexibleExplorerRefreshManager + { + RefreshArguments GetRefreshType(RefreshArguments refreshArguments); + } + + public class XmlDefinitionProvider + { + public XmlDefinitionProvider(string xmlContent, string fileName) + { + } + + public object RootDefinitions { get; } = new object[0]; + + public static string GetLocalFile(string path) + { + return File.Exists(path) ? path : null; + } + } +} \ No newline at end of file diff --git a/shims/src/WebView2.cs b/shims/src/WebView2.cs index f946c15..b0a8221 100644 --- a/shims/src/WebView2.cs +++ b/shims/src/WebView2.cs @@ -3,37 +3,6 @@ using System.Threading.Tasks; using System.Windows.Forms; -namespace Microsoft.Web.WebView2.Core -{ - public class CoreWebView2Settings - { - public bool AreDefaultContextMenusEnabled { get; set; } - - public bool IsStatusBarEnabled { get; set; } - - public bool IsZoomControlEnabled { get; set; } - - public bool AreDevToolsEnabled { get; set; } - } - - public class CoreWebView2NavigationStartingEventArgs : EventArgs - { - } - - public class CoreWebView2NavigationCompletedEventArgs : EventArgs - { - } - - public class CoreWebView2 - { - public CoreWebView2Settings Settings { get; } = new CoreWebView2Settings(); - - public event EventHandler NavigationStarting; - - public event EventHandler NavigationCompleted; - } -} - namespace Microsoft.Web.WebView2.WinForms { public class WebView2 : Control, ISupportInitialize diff --git a/shims/src/WebView2Core.cs b/shims/src/WebView2Core.cs new file mode 100644 index 0000000..ff9eced --- /dev/null +++ b/shims/src/WebView2Core.cs @@ -0,0 +1,32 @@ +using System; + +namespace Microsoft.Web.WebView2.Core +{ + public class CoreWebView2Settings + { + public bool AreDefaultContextMenusEnabled { get; set; } + + public bool IsStatusBarEnabled { get; set; } + + public bool IsZoomControlEnabled { get; set; } + + public bool AreDevToolsEnabled { get; set; } + } + + public class CoreWebView2NavigationStartingEventArgs : EventArgs + { + } + + public class CoreWebView2NavigationCompletedEventArgs : EventArgs + { + } + + public class CoreWebView2 + { + public CoreWebView2Settings Settings { get; } = new CoreWebView2Settings(); + + public event EventHandler NavigationStarting; + + public event EventHandler NavigationCompleted; + } +} \ No newline at end of file From f5b9bc0cedca9c0c1b1123e93d62a143f7c9d99d Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Wed, 15 Apr 2026 15:08:35 +0100 Subject: [PATCH 061/102] Remove unsupported Black Duck pool overrides --- azure-pipelines.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 48bda45..fec6024 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1127,10 +1127,3 @@ stages: IsIPR: false ProjectName: $(BlackDuckProjectName) PackageVersion: $(Build.SourceBranchName)-$(Build.BuildId)-components - UbuntuPoolName: Technology-Microsoft-Hosted-Ubuntu - UbuntuPoolDemands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - WindowsPoolName: Technology-Microsoft-Hosted-Windows - WindowsPoolDemands: - - ImageOverride -equals windows-latest From f5eb8657497d12563e044e8e13bf82e21bff4787 Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Wed, 15 Apr 2026 15:20:06 +0100 Subject: [PATCH 062/102] Gate component template comparison at compile time --- azure-pipelines.yml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fec6024..338b9e1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1115,15 +1115,16 @@ stages: displayName: Publish Black Duck network diagnostics (Ubuntu) condition: and(always(), eq(variables.HasBlackDuckNetworkDiagnosticsUbuntu, 'true')) - - stage: ComponentTemplateBlackDuckComparison - displayName: Component Template Black Duck Comparison - dependsOn: BuildWithShims - condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}, ${{ eq(parameters.EnableComponentTemplateBlackDuckComparison, 'true') }}) - jobs: - - template: component-blackduck.yml@Templates - parameters: - BuildArtifact: Build.Release - BlackDuckGroup: DabaconGroup - IsIPR: false - ProjectName: $(BlackDuckProjectName) - PackageVersion: $(Build.SourceBranchName)-$(Build.BuildId)-components + - ${{ if and(eq(parameters.EnableSecurityValidation, 'true'), eq(parameters.EnableComponentTemplateBlackDuckComparison, 'true')) }}: + - stage: ComponentTemplateBlackDuckComparison + displayName: Component Template Black Duck Comparison + dependsOn: BuildWithShims + condition: succeeded() + jobs: + - template: component-blackduck.yml@Templates + parameters: + BuildArtifact: Build.Release + BlackDuckGroup: DabaconGroup + IsIPR: false + ProjectName: $(BlackDuckProjectName) + PackageVersion: $(Build.SourceBranchName)-$(Build.BuildId)-components From 4ea430d1b8788377c8a445fc36fa48c1c6299add Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Wed, 15 Apr 2026 15:36:21 +0100 Subject: [PATCH 063/102] Always include component template comparison in security path --- azure-pipelines.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 338b9e1..2361d51 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -29,9 +29,6 @@ parameters: - name: EnableUbuntuBlackDuckComparison type: string default: "false" - - name: EnableComponentTemplateBlackDuckComparison - type: string - default: "false" variables: - template: variables.yml@Templates @@ -1115,7 +1112,7 @@ stages: displayName: Publish Black Duck network diagnostics (Ubuntu) condition: and(always(), eq(variables.HasBlackDuckNetworkDiagnosticsUbuntu, 'true')) - - ${{ if and(eq(parameters.EnableSecurityValidation, 'true'), eq(parameters.EnableComponentTemplateBlackDuckComparison, 'true')) }}: + - ${{ if eq(parameters.EnableSecurityValidation, 'true') }}: - stage: ComponentTemplateBlackDuckComparison displayName: Component Template Black Duck Comparison dependsOn: BuildWithShims From 94cbca7a02d31c763645211f8f536ff4162aa3fe Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Wed, 15 Apr 2026 15:41:06 +0100 Subject: [PATCH 064/102] Enable security validation by default --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2361d51..5e58d73 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -25,7 +25,7 @@ resources: parameters: - name: EnableSecurityValidation type: string - default: "false" + default: "true" - name: EnableUbuntuBlackDuckComparison type: string default: "false" From 08a47393efc4d6cb453337b6d5e9d318a1b48ce2 Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:19:47 +0100 Subject: [PATCH 065/102] Gate component comparison behind explicit flag --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5e58d73..e2b603b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1112,7 +1112,7 @@ stages: displayName: Publish Black Duck network diagnostics (Ubuntu) condition: and(always(), eq(variables.HasBlackDuckNetworkDiagnosticsUbuntu, 'true')) - - ${{ if eq(parameters.EnableSecurityValidation, 'true') }}: + - ${{ if and(eq(parameters.EnableSecurityValidation, 'true'), eq(parameters.EnableComponentTemplateBlackDuckComparison, 'true')) }}: - stage: ComponentTemplateBlackDuckComparison displayName: Component Template Black Duck Comparison dependsOn: BuildWithShims From 7f35980cdc71e13111facb3316cdb9486f316e09 Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:20:01 +0100 Subject: [PATCH 066/102] Restore Black Duck pool overrides --- azure-pipelines.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e2b603b..b7c92e0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1122,6 +1122,13 @@ stages: parameters: BuildArtifact: Build.Release BlackDuckGroup: DabaconGroup + UbuntuPoolName: Technology-Microsoft-Hosted-Ubuntu + UbuntuPoolDemands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + WindowsPoolName: Technology-Microsoft-Hosted-Windows + WindowsPoolDemands: + - ImageOverride -equals windows-latest IsIPR: false ProjectName: $(BlackDuckProjectName) PackageVersion: $(Build.SourceBranchName)-$(Build.BuildId)-components From b7ccb83bf9fcab41c16e0dc3bd7c3ff314df343a Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:21:10 +0100 Subject: [PATCH 067/102] Restore component comparison parameter --- azure-pipelines.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b7c92e0..7a37bff 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -29,6 +29,9 @@ parameters: - name: EnableUbuntuBlackDuckComparison type: string default: "false" + - name: EnableComponentTemplateBlackDuckComparison + type: string + default: "false" variables: - template: variables.yml@Templates From bc90dded99619370bfca764f05c334e18a8e58b1 Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:22:17 +0100 Subject: [PATCH 068/102] Fix component comparison gate --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7a37bff..8f9b71e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1115,7 +1115,7 @@ stages: displayName: Publish Black Duck network diagnostics (Ubuntu) condition: and(always(), eq(variables.HasBlackDuckNetworkDiagnosticsUbuntu, 'true')) - - ${{ if and(eq(parameters.EnableSecurityValidation, 'true'), eq(parameters.EnableComponentTemplateBlackDuckComparison, 'true')) }}: + - ${{ if eq(parameters.EnableSecurityValidation, 'true') }}: - stage: ComponentTemplateBlackDuckComparison displayName: Component Template Black Duck Comparison dependsOn: BuildWithShims From 28b49265d800232cb465c05024a90ea0c42bd9ba Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Wed, 15 Apr 2026 18:18:28 +0100 Subject: [PATCH 069/102] Temporarily test Templates fix branch --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8f9b71e..74fbb81 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -16,7 +16,7 @@ resources: - repository: Templates type: git name: Technology/Templates - ref: refs/heads/main + ref: refs/heads/feature/uecf-blackduck-pool-overrides - repository: antiMalwareTemplate type: git name: Architecture/Architecture From 4ca6a3b4ebf91db52842539ddae9bfccd321712b Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Thu, 16 Apr 2026 00:00:16 +0100 Subject: [PATCH 070/102] Separate anti-malware into its own stage --- azure-pipelines.yml | 66 ++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 74fbb81..fbe8287 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -430,6 +430,39 @@ stages: displayName: Publish Coverity artifacts condition: and(always(), eq(variables.HasCoverityArtifacts, 'true')) + - stage: AntiMalware + displayName: AntiMalware + dependsOn: InitialiseSource + condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) + jobs: + - job: AntiMalwareScan + displayName: AntiMalware Scan + timeoutInMinutes: "60" + pool: + name: Technology-Microsoft-Hosted-Windows + demands: + - ImageOverride -equals windows-latest + workspace: + clean: all + steps: + - template: steps/retrieve-source.yml@Templates + parameters: + IsIPR: false + ArtifactName: Source + DownloadPath: $(Pipeline.Workspace)/Source + + - pwsh: | + Start-Process powershell.exe -ArgumentList "Update-MpSignature -UpdateSource MicrosoftUpdateServer" -Wait + Write-Output "updated the definitions successfully" + displayName: Antimalware Scan task + + - template: Pipelines/Templates/AntiMalware/AntiMalware.step.yml@antiMalwareTemplate + parameters: + scanType: Directory + scanDirectory: $(Pipeline.Workspace)\Source + artifactName: AntimalwareResults_Source + os: Windows_NT + - stage: BlackDuckWindows displayName: Black Duck - Windows dependsOn: BuildWithShims @@ -606,44 +639,11 @@ stages: Phase: PLANNING RemoveTargetVersion: false - - job: AntiMalwareScan - displayName: AntiMalware Scan - dependsOn: - - BlackDuckInitialisation - - BlackDuckClone - condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true')) - timeoutInMinutes: "60" - pool: - name: Technology-Microsoft-Hosted-Windows - demands: - - ImageOverride -equals windows-latest - workspace: - clean: all - steps: - - template: steps/retrieve-source.yml@Templates - parameters: - IsIPR: false - ArtifactName: Source - DownloadPath: $(Pipeline.Workspace)/Source - - - pwsh: | - Start-Process powershell.exe -ArgumentList "Update-MpSignature -UpdateSource MicrosoftUpdateServer" -Wait - Write-Output "updated the definitions successfully" - displayName: Antimalware Scan task - - - template: Pipelines/Templates/AntiMalware/AntiMalware.step.yml@antiMalwareTemplate - parameters: - scanType: Directory - scanDirectory: $(Pipeline.Workspace)\Source - artifactName: AntimalwareResults_Source - os: Windows_NT - - job: BlackDuckScan displayName: Black Duck Scan dependsOn: - BlackDuckInitialisation - BlackDuckClone - - AntiMalwareScan condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true')) timeoutInMinutes: "120" pool: From a7103edc956274a0e8f18c0a1a41e1ac5f307daf Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Thu, 16 Apr 2026 00:01:27 +0100 Subject: [PATCH 071/102] Fix pipeline indentation after rebase --- azure-pipelines.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fbe8287..f71edec 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1116,20 +1116,20 @@ stages: condition: and(always(), eq(variables.HasBlackDuckNetworkDiagnosticsUbuntu, 'true')) - ${{ if eq(parameters.EnableSecurityValidation, 'true') }}: - - stage: ComponentTemplateBlackDuckComparison - displayName: Component Template Black Duck Comparison - dependsOn: BuildWithShims - condition: succeeded() - jobs: - - template: component-blackduck.yml@Templates - parameters: - BuildArtifact: Build.Release - BlackDuckGroup: DabaconGroup - UbuntuPoolName: Technology-Microsoft-Hosted-Ubuntu - UbuntuPoolDemands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - WindowsPoolName: Technology-Microsoft-Hosted-Windows + - stage: ComponentTemplateBlackDuckComparison + displayName: Component Template Black Duck Comparison + dependsOn: BuildWithShims + condition: succeeded() + jobs: + - template: component-blackduck.yml@Templates + parameters: + BuildArtifact: Build.Release + BlackDuckGroup: DabaconGroup + UbuntuPoolName: Technology-Microsoft-Hosted-Ubuntu + UbuntuPoolDemands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage + WindowsPoolName: Technology-Microsoft-Hosted-Windows WindowsPoolDemands: - ImageOverride -equals windows-latest IsIPR: false From 1e0fecfff0e91736c68ff55d9558a6e5314b73ec Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Thu, 16 Apr 2026 01:23:16 +0100 Subject: [PATCH 072/102] Fix component comparison template indentation --- azure-pipelines.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f71edec..6045c92 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1130,8 +1130,8 @@ stages: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage WindowsPoolName: Technology-Microsoft-Hosted-Windows - WindowsPoolDemands: - - ImageOverride -equals windows-latest - IsIPR: false - ProjectName: $(BlackDuckProjectName) - PackageVersion: $(Build.SourceBranchName)-$(Build.BuildId)-components + WindowsPoolDemands: + - ImageOverride -equals windows-latest + IsIPR: false + ProjectName: $(BlackDuckProjectName) + PackageVersion: $(Build.SourceBranchName)-$(Build.BuildId)-components From 6e0c601b54ebdd9c236020b163ef9ea997a3d7a2 Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Thu, 16 Apr 2026 12:01:51 +0100 Subject: [PATCH 073/102] Publish anti-malware SARIF reports --- azure-pipelines.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6045c92..3a50ac7 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -463,6 +463,17 @@ stages: artifactName: AntimalwareResults_Source os: Windows_NT + - stage: AntiMalwareSarif + displayName: AntiMalware SARIF + dependsOn: AntiMalware + condition: and(not(canceled()), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) + jobs: + - template: jobs/publish-CodeAnalysisLogs.yml@Templates + parameters: + Analysers: [] + DirectSarif: + - AntimalwareResults_Source + - stage: BlackDuckWindows displayName: Black Duck - Windows dependsOn: BuildWithShims From b336b0dd659748cb64c426477f83f89f7f6aaad4 Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Thu, 16 Apr 2026 12:49:33 +0100 Subject: [PATCH 074/102] Use Technology pool for anti-malware SARIF publishing --- azure-pipelines.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3a50ac7..77c37ab 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -473,6 +473,10 @@ stages: Analysers: [] DirectSarif: - AntimalwareResults_Source + UbuntuPoolName: Technology-Microsoft-Hosted-Ubuntu + UbuntuPoolDemands: + - ImageOverride -equals ubuntu-latest + - WorkFolder -equals /mnt/storage/sdc/storage - stage: BlackDuckWindows displayName: Black Duck - Windows From 55df7fbf5e4432d2a0f1a20e60c5507c0bb347c4 Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:43:02 +0100 Subject: [PATCH 075/102] Scan source and build artifacts with anti-malware --- azure-pipelines.yml | 86 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 77c37ab..bec174d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -432,7 +432,9 @@ stages: - stage: AntiMalware displayName: AntiMalware - dependsOn: InitialiseSource + dependsOn: + - InitialiseSource + - BuildWithShims condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: - job: AntiMalwareScan @@ -451,6 +453,11 @@ stages: ArtifactName: Source DownloadPath: $(Pipeline.Workspace)/Source + - download: current + artifact: Build.Release + displayName: Download build artifact + patterns: "**" + - pwsh: | Start-Process powershell.exe -ArgumentList "Update-MpSignature -UpdateSource MicrosoftUpdateServer" -Wait Write-Output "updated the definitions successfully" @@ -463,20 +470,83 @@ stages: artifactName: AntimalwareResults_Source os: Windows_NT + - template: Pipelines/Templates/AntiMalware/AntiMalware.step.yml@antiMalwareTemplate + parameters: + scanType: Directory + scanDirectory: $(Pipeline.Workspace)\Build.Release + artifactName: AntimalwareResults_Build_Release + os: Windows_NT + - stage: AntiMalwareSarif displayName: AntiMalware SARIF dependsOn: AntiMalware condition: and(not(canceled()), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: - - template: jobs/publish-CodeAnalysisLogs.yml@Templates - parameters: - Analysers: [] - DirectSarif: - - AntimalwareResults_Source - UbuntuPoolName: Technology-Microsoft-Hosted-Ubuntu - UbuntuPoolDemands: + - job: CodeAnalysisLogs + displayName: CodeAnalysisLogs + pool: + name: Technology-Microsoft-Hosted-Ubuntu + demands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage + container: mcr.microsoft.com/dotnet/sdk:9.0 + workspace: + clean: all + steps: + - pwsh: | + $ErrorActionPreference = 'Stop' + + $headers = @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } + $artifactsUri = "$(System.CollectionUri)$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/artifacts?api-version=7.2-preview.5" + $response = Invoke-RestMethod -Uri $artifactsUri -Headers $headers -Method GET + $antiMalwareArtifacts = @($response.value | Where-Object { $_.name -like 'AntimalwareResults_*' }) + + if ($antiMalwareArtifacts.Count -eq 0) { + throw 'No AntiMalware artifacts were found for this build.' + } + + $codeAnalysisLogs = Join-Path $env:PIPELINE_WORKSPACE 'CodeAnalysisLogs' + New-Item -Path $codeAnalysisLogs -ItemType Directory -Force | Out-Null + + foreach ($artifact in $antiMalwareArtifacts) { + $artifactName = [string]$artifact.name + $downloadUrl = [string]$artifact.resource.downloadUrl + $zipPath = Join-Path $env:AGENT_TEMPDIRECTORY "$artifactName.zip" + $extractPath = Join-Path $env:AGENT_TEMPDIRECTORY $artifactName + + Invoke-WebRequest -Uri $downloadUrl -Headers $headers -OutFile $zipPath + + if (Test-Path -LiteralPath $extractPath) { + Remove-Item -LiteralPath $extractPath -Recurse -Force + } + + Expand-Archive -LiteralPath $zipPath -DestinationPath $extractPath -Force + + $sarifFiles = Get-ChildItem -Path $extractPath -Filter '*.sarif' -Recurse -File + if (-not $sarifFiles) { + Write-Warning "No SARIF file found in $artifactName" + continue + } + + foreach ($sarifFile in $sarifFiles) { + $destinationName = if ($sarifFiles.Count -eq 1) { + "$artifactName.sarif" + } + else { + "$artifactName-$($sarifFile.BaseName).sarif" + } + + Copy-Item -LiteralPath $sarifFile.FullName -Destination (Join-Path $codeAnalysisLogs $destinationName) -Force + } + } + displayName: Collect AntiMalware SARIF files + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + + - publish: $(Pipeline.Workspace)/CodeAnalysisLogs + artifact: CodeAnalysisLogs + displayName: Publish CodeAnalysisLogs + condition: always() - stage: BlackDuckWindows displayName: Black Duck - Windows From a2383bd79d9a69a1fec344937140ddda2c343157 Mon Sep 17 00:00:00 2001 From: quasarea <10334982+quasarea@users.noreply.github.com> Date: Thu, 16 Apr 2026 16:09:27 +0100 Subject: [PATCH 076/102] Gate component Black Duck comparison correctly --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bec174d..ce64dc6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1200,7 +1200,7 @@ stages: displayName: Publish Black Duck network diagnostics (Ubuntu) condition: and(always(), eq(variables.HasBlackDuckNetworkDiagnosticsUbuntu, 'true')) - - ${{ if eq(parameters.EnableSecurityValidation, 'true') }}: + - ${{ if and(eq(parameters.EnableSecurityValidation, 'true'), eq(parameters.EnableComponentTemplateBlackDuckComparison, 'true')) }}: - stage: ComponentTemplateBlackDuckComparison displayName: Component Template Black Duck Comparison dependsOn: BuildWithShims From 089ba55c35e483cc4b20778f6c9b0d28adca2c2a Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 01:18:17 +0100 Subject: [PATCH 077/102] Convert shim manifest to pipeline-generated YAML --- .../agents/ue-customization-manager.agent.md | 4 +- azure-pipelines.yml | 32 ++++- scripts/Invoke-FakeUECompile.ps1 | 31 ++++- shims/generate-shim-manifest.yml | 48 ++++++++ shims/shim-projects.psd1 | 109 ----------------- shims/shim-projects.yml | 113 ++++++++++++++++++ 6 files changed, 219 insertions(+), 118 deletions(-) create mode 100644 shims/generate-shim-manifest.yml delete mode 100644 shims/shim-projects.psd1 create mode 100644 shims/shim-projects.yml diff --git a/.github/agents/ue-customization-manager.agent.md b/.github/agents/ue-customization-manager.agent.md index 0746217..18ae696 100644 --- a/.github/agents/ue-customization-manager.agent.md +++ b/.github/agents/ue-customization-manager.agent.md @@ -266,9 +266,9 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz **Key components**: - [`azure-pipelines.yml`](../../azure-pipelines.yml): Contains the hosted test lane and the optional hosted security-validation lane. -- [`scripts/Invoke-FakeUECompile.ps1`](../../scripts/Invoke-FakeUECompile.ps1): Generates CI-only shim project files from `shims/shim-projects.psd1` into `.tmp/generated-shims`, builds compile-only shim assemblies, and compiles `templates/` and `Examples/` against them. +- [`scripts/Invoke-FakeUECompile.ps1`](../../scripts/Invoke-FakeUECompile.ps1): Consumes the generated shim manifest from `shims/shim-projects.yml`, emits CI-only shim project files into `.tmp/generated-shims`, builds compile-only shim assemblies, and compiles `templates/` and `Examples/` against them. - [`scripts/Build-AddIns.ps1`](../../scripts/Build-AddIns.ps1): Supports `-SkipPostBuildEvent` and `-Rebuild` for deterministic compile-only builds. -- [`shims/`](../../shims): Contains compile-only UE API stand-ins used for hosted validation and Coverity capture, plus the `shim-projects.psd1` manifest used to generate shim project files at build time. +- [`shims/`](../../shims): Contains compile-only UE API stand-ins used for hosted validation and Coverity capture, plus the YAML shim definition and generation templates used to produce the runtime shim manifest at build time. **Important notes**: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ce64dc6..926007a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -189,9 +189,14 @@ stages: packageType: sdk version: 9.0.x + - template: shims/shim-projects.yml + parameters: + OutputPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json + - pwsh: | $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' - & (Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release + $shimManifestPath = Join-Path $repoRoot '.tmp/generated-shim-manifest.json' + & (Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release -ShimManifestPath $shimManifestPath displayName: Build template and example projects with fake UE references - publish: $(Pipeline.Workspace)/Source/shims/artifacts/Release @@ -293,16 +298,22 @@ stages: dotnet --info displayName: Show .NET SDK info + - template: shims/shim-projects.yml + parameters: + OutputPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json + - pwsh: | $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' - & (Join-Path $repoRoot 'scripts\Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release + $shimManifestPath = Join-Path $repoRoot '.tmp/generated-shim-manifest.json' + & (Join-Path $repoRoot 'scripts\Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release -ShimManifestPath $shimManifestPath displayName: Build template and example projects with fake UE references - pwsh: | $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' $paths = @{ HasShimSources = Test-Path -LiteralPath (Join-Path $repoRoot 'shims') - HasShimManifest = Test-Path -LiteralPath (Join-Path $repoRoot 'shims/shim-projects.psd1') + HasShimManifest = Test-Path -LiteralPath (Join-Path $repoRoot 'shims/shim-projects.yml') + HasGeneratedShimManifest = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shim-manifest.json') HasGeneratedShimProjects = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shims') HasShimAssemblies = Test-Path -LiteralPath (Join-Path $repoRoot 'shims/artifacts/Release') } @@ -320,11 +331,16 @@ stages: displayName: Publish shim sources condition: and(always(), eq(variables.HasShimSources, 'true')) - - publish: $(Pipeline.Workspace)/Source/shims/shim-projects.psd1 + - publish: $(Pipeline.Workspace)/Source/shims/shim-projects.yml artifact: ShimManifest displayName: Publish shim manifest condition: and(always(), eq(variables.HasShimManifest, 'true')) + - publish: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json + artifact: GeneratedShimManifest + displayName: Publish generated shim manifest + condition: and(always(), eq(variables.HasGeneratedShimManifest, 'true')) + - publish: $(Pipeline.Workspace)/Source/.tmp/generated-shims artifact: GeneratedShimProjects displayName: Publish generated shim project files @@ -339,6 +355,7 @@ stages: $pwshPath = if ($IsLinux) { '/usr/bin/pwsh' } else { (Get-Command pwsh -ErrorAction Stop).Source } $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' $fakeCompileScriptPath = Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1' + $shimManifestPath = Join-Path $repoRoot '.tmp/generated-shim-manifest.json' $branchName = "$env:BUILD_SOURCEBRANCH" -replace '^refs/heads/', '' $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') @@ -352,8 +369,13 @@ stages: " - shell: ['$pwshPath', '-NoProfile', '-Command', 'Write-Host fake-clean-step']" ) + $buildCommand = + " - shell: ['$pwshPath', '-NoProfile', '-File', '$fakeCompileScriptPath'" + + ", '-Directory', 'templates,Examples', '-Configuration', 'Release'" + + ", '-ShimManifestPath', '$shimManifestPath', '-Rebuild']" + $buildCommands = @( - " - shell: ['$pwshPath', '-NoProfile', '-File', '$fakeCompileScriptPath', '-Directory', 'templates,Examples', '-Configuration', 'Release', '-Rebuild']" + $buildCommand ) $polarisContent = @( diff --git a/scripts/Invoke-FakeUECompile.ps1 b/scripts/Invoke-FakeUECompile.ps1 index 7d1a847..340be3c 100644 --- a/scripts/Invoke-FakeUECompile.ps1 +++ b/scripts/Invoke-FakeUECompile.ps1 @@ -17,13 +17,19 @@ param( $ErrorActionPreference = 'Stop' $repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path +$defaultGeneratedShimManifestPath = Join-Path $repoRoot '.tmp\generated-shim-manifest.json' if (-not $ShimProjectsPath) { $ShimProjectsPath = Join-Path $repoRoot 'shims' } if (-not $ShimManifestPath) { - $ShimManifestPath = Join-Path $ShimProjectsPath 'shim-projects.psd1' + if (Test-Path -LiteralPath $defaultGeneratedShimManifestPath) { + $ShimManifestPath = $defaultGeneratedShimManifestPath + } + else { + throw 'Shim manifest path was not provided and no generated manifest was found. Generate .tmp/generated-shim-manifest.json from shims/shim-projects.yml or pass -ShimManifestPath explicitly.' + } } if (-not $GeneratedShimProjectsPath) { @@ -80,7 +86,28 @@ function Ensure-NetFrameworkReferenceAssemblies { } } -$shimManifest = Import-PowerShellDataFile -Path $resolvedShimManifestPath +function Import-ShimManifest { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Path + ) + + $extension = [System.IO.Path]::GetExtension($Path) + switch ($extension.ToLowerInvariant()) { + '.json' { + return Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json -AsHashtable + } + '.psd1' { + return Import-PowerShellDataFile -Path $Path + } + default { + throw "Unsupported shim manifest format '$extension'. Supported formats are .json and .psd1." + } + } +} + +$shimManifest = Import-ShimManifest -Path $resolvedShimManifestPath $shimProjects = if ($shimManifest.ContainsKey('Projects')) { $shimManifest.Projects } diff --git a/shims/generate-shim-manifest.yml b/shims/generate-shim-manifest.yml new file mode 100644 index 0000000..ad4e9f8 --- /dev/null +++ b/shims/generate-shim-manifest.yml @@ -0,0 +1,48 @@ +parameters: + - name: OutputPath + type: string + default: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json + - name: Shims + type: object + default: [] + +steps: + - pwsh: | + $outputPath = '${{ parameters.OutputPath }}' + $outputDirectory = Split-Path -Path $outputPath -Parent + if (-not [string]::IsNullOrWhiteSpace($outputDirectory)) { + $null = New-Item -ItemType Directory -Path $outputDirectory -Force + } + + $shimDefinitions = $env:SHIMS_JSON | ConvertFrom-Json + $projects = [ordered]@{} + foreach ($shim in @($shimDefinitions)) { + $project = [ordered]@{} + + $projectSources = @($shim.sources | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) }) + if ($projectSources.Count -gt 0) { + $project['Sources'] = @($projectSources | ForEach-Object { [string]$_ }) + } + + $projectReferences = @($shim.projectReferences | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) }) + if ($projectReferences.Count -gt 0) { + $project['ProjectReferences'] = @($projectReferences | ForEach-Object { [string]$_ }) + } + + $rootNamespace = [string]$shim.rootNamespace + if (-not [string]::IsNullOrWhiteSpace($rootNamespace)) { + $project['RootNamespace'] = $rootNamespace + } + + $projects[[string]$shim.name] = $project + } + + $manifest = @{ + Projects = $projects + } + + $manifest | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $outputPath -Encoding utf8 + Write-Host "Generated shim manifest: $outputPath" + displayName: Generate shim manifest + env: + SHIMS_JSON: ${{ convertToJson(parameters.Shims) }} diff --git a/shims/shim-projects.psd1 b/shims/shim-projects.psd1 deleted file mode 100644 index 0c8734e..0000000 --- a/shims/shim-projects.psd1 +++ /dev/null @@ -1,109 +0,0 @@ -@{ - Projects = @{ - 'Aveva.ApplicationFramework' = @{ - Sources = @( - 'src\ApplicationFramework.cs' - ) - ProjectReferences = @( - 'Aveva.ApplicationFramework.Presentation' - ) - } - 'Aveva.ApplicationFramework.Implementation' = @{} - 'Aveva.ApplicationFramework.Presentation' = @{ - Sources = @( - 'src\ApplicationFrameworkPresentation.cs' - ) - } - 'Aveva.Core.Database' = @{ - Sources = @( - 'src\Database.cs' - ) - ProjectReferences = @( - 'Aveva.Core.Geometry' - 'Aveva.Core.Utilities' - ) - } - 'Aveva.Core.Database.Filters' = @{ - Sources = @( - 'src\Filters.cs' - ) - ProjectReferences = @( - 'Aveva.Core.Database' - ) - } - 'Aveva.Core.FlexibleExplorer.Control' = @{ - Sources = @( - 'src\FlexibleExplorer.cs' - ) - ProjectReferences = @( - 'Aveva.ApplicationFramework' - 'Aveva.Core.FlexibleExplorer.Core' - 'Aveva.Core.Presentation' - ) - } - 'Aveva.Core.FlexibleExplorer.Core' = @{ - Sources = @( - 'src\FlexibleExplorerCore.cs' - ) - ProjectReferences = @( - 'Aveva.Core.Presentation' - ) - } - 'Aveva.Core.Geometry' = @{ - Sources = @( - 'src\Geometry.cs' - ) - } - 'Aveva.Core.Implementation' = @{} - 'Aveva.Core.Presentation' = @{ - Sources = @( - 'src\Presentation.cs' - ) - ProjectReferences = @( - 'Aveva.ApplicationFramework.Presentation' - 'Aveva.Core.Database' - 'PMLNet' - ) - } - 'Aveva.Core.Utilities' = @{ - Sources = @( - 'src\Utilities.cs' - ) - } - 'Aveva.Core3D.Clasher' = @{ - Sources = @( - 'src\Clasher.cs' - ) - ProjectReferences = @( - 'Aveva.Core.Database' - 'Aveva.Core.Geometry' - ) - } - 'GridControl' = @{} - 'Microsoft.Web.WebView2.Core' = @{ - Sources = @( - 'src\WebView2Core.cs' - ) - } - 'Microsoft.Web.WebView2.WinForms' = @{ - Sources = @( - 'src\WebView2.cs' - ) - ProjectReferences = @( - 'Microsoft.Web.WebView2.Core' - ) - } - 'Newtonsoft.Json' = @{ - Sources = @( - 'src\Json.cs' - ) - } - 'PMLNet' = @{ - Sources = @( - 'src\PmlNet.cs' - ) - RootNamespace = 'Aveva.Core.PMLNet' - } - 'StartUp' = @{} - } -} \ No newline at end of file diff --git a/shims/shim-projects.yml b/shims/shim-projects.yml new file mode 100644 index 0000000..b033e02 --- /dev/null +++ b/shims/shim-projects.yml @@ -0,0 +1,113 @@ +parameters: + - name: OutputPath + type: string + default: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json + - name: Shims + type: object + default: + - name: "Aveva.ApplicationFramework" + rootNamespace: "" + sources: + - "src/ApplicationFramework.cs" + projectReferences: + - "Aveva.ApplicationFramework.Presentation" + - name: "Aveva.ApplicationFramework.Implementation" + rootNamespace: "" + sources: [] + projectReferences: [] + - name: "Aveva.ApplicationFramework.Presentation" + rootNamespace: "" + sources: + - "src/ApplicationFrameworkPresentation.cs" + projectReferences: [] + - name: "Aveva.Core.Database" + rootNamespace: "" + sources: + - "src/Database.cs" + projectReferences: + - "Aveva.Core.Geometry" + - "Aveva.Core.Utilities" + - name: "Aveva.Core.Database.Filters" + rootNamespace: "" + sources: + - "src/Filters.cs" + projectReferences: + - "Aveva.Core.Database" + - name: "Aveva.Core.FlexibleExplorer.Control" + rootNamespace: "" + sources: + - "src/FlexibleExplorer.cs" + projectReferences: + - "Aveva.ApplicationFramework" + - "Aveva.Core.FlexibleExplorer.Core" + - "Aveva.Core.Presentation" + - name: "Aveva.Core.FlexibleExplorer.Core" + rootNamespace: "" + sources: + - "src/FlexibleExplorerCore.cs" + projectReferences: + - "Aveva.Core.Presentation" + - name: "Aveva.Core.Geometry" + rootNamespace: "" + sources: + - "src/Geometry.cs" + projectReferences: [] + - name: "Aveva.Core.Implementation" + rootNamespace: "" + sources: [] + projectReferences: [] + - name: "Aveva.Core.Presentation" + rootNamespace: "" + sources: + - "src/Presentation.cs" + projectReferences: + - "Aveva.ApplicationFramework.Presentation" + - "Aveva.Core.Database" + - "PMLNet" + - name: "Aveva.Core.Utilities" + rootNamespace: "" + sources: + - "src/Utilities.cs" + projectReferences: [] + - name: "Aveva.Core3D.Clasher" + rootNamespace: "" + sources: + - "src/Clasher.cs" + projectReferences: + - "Aveva.Core.Database" + - "Aveva.Core.Geometry" + - name: "GridControl" + rootNamespace: "" + sources: [] + projectReferences: [] + - name: "Microsoft.Web.WebView2.Core" + rootNamespace: "" + sources: + - "src/WebView2Core.cs" + projectReferences: [] + - name: "Microsoft.Web.WebView2.WinForms" + rootNamespace: "" + sources: + - "src/WebView2.cs" + projectReferences: + - "Microsoft.Web.WebView2.Core" + - name: "Newtonsoft.Json" + rootNamespace: "" + sources: + - "src/Json.cs" + projectReferences: [] + - name: "PMLNet" + rootNamespace: "Aveva.Core.PMLNet" + sources: + - "src/PmlNet.cs" + projectReferences: [] + - name: "StartUp" + rootNamespace: "" + sources: [] + projectReferences: [] + +steps: + - template: generate-shim-manifest.yml + parameters: + OutputPath: ${{ parameters.OutputPath }} + Shims: ${{ parameters.Shims }} From 3b3c91e8a4ff81c8d891f9bbf11321bb41efa554 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 01:56:39 +0100 Subject: [PATCH 078/102] Make shim YAML the full source of truth --- azure-pipelines.yml | 6 + scripts/Invoke-FakeUECompile.ps1 | 44 +- shims/generate-shim-manifest.yml | 23 +- shims/shim-projects.yml | 1686 ++++++++++++++++- shims/src/ApplicationFramework.cs | 65 - shims/src/ApplicationFrameworkPresentation.cs | 236 --- shims/src/Clasher.cs | 82 - shims/src/Database.cs | 620 ------ shims/src/Filters.cs | 86 - shims/src/FlexibleExplorer.cs | 33 - shims/src/FlexibleExplorerCore.cs | 41 - shims/src/Geometry.cs | 45 - shims/src/Json.cs | 80 - shims/src/PmlNet.cs | 15 - shims/src/Presentation.cs | 141 -- shims/src/Utilities.cs | 98 - shims/src/WebView2.cs | 34 - shims/src/WebView2Core.cs | 32 - 18 files changed, 1723 insertions(+), 1644 deletions(-) delete mode 100644 shims/src/ApplicationFramework.cs delete mode 100644 shims/src/ApplicationFrameworkPresentation.cs delete mode 100644 shims/src/Clasher.cs delete mode 100644 shims/src/Database.cs delete mode 100644 shims/src/Filters.cs delete mode 100644 shims/src/FlexibleExplorer.cs delete mode 100644 shims/src/FlexibleExplorerCore.cs delete mode 100644 shims/src/Geometry.cs delete mode 100644 shims/src/Json.cs delete mode 100644 shims/src/PmlNet.cs delete mode 100644 shims/src/Presentation.cs delete mode 100644 shims/src/Utilities.cs delete mode 100644 shims/src/WebView2.cs delete mode 100644 shims/src/WebView2Core.cs diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 926007a..9a747f7 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -314,6 +314,7 @@ stages: HasShimSources = Test-Path -LiteralPath (Join-Path $repoRoot 'shims') HasShimManifest = Test-Path -LiteralPath (Join-Path $repoRoot 'shims/shim-projects.yml') HasGeneratedShimManifest = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shim-manifest.json') + HasGeneratedShimSources = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shim-sources') HasGeneratedShimProjects = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shims') HasShimAssemblies = Test-Path -LiteralPath (Join-Path $repoRoot 'shims/artifacts/Release') } @@ -341,6 +342,11 @@ stages: displayName: Publish generated shim manifest condition: and(always(), eq(variables.HasGeneratedShimManifest, 'true')) + - publish: $(Pipeline.Workspace)/Source/.tmp/generated-shim-sources + artifact: GeneratedShimSources + displayName: Publish generated shim sources + condition: and(always(), eq(variables.HasGeneratedShimSources, 'true')) + - publish: $(Pipeline.Workspace)/Source/.tmp/generated-shims artifact: GeneratedShimProjects displayName: Publish generated shim project files diff --git a/scripts/Invoke-FakeUECompile.ps1 b/scripts/Invoke-FakeUECompile.ps1 index 340be3c..9f7dde5 100644 --- a/scripts/Invoke-FakeUECompile.ps1 +++ b/scripts/Invoke-FakeUECompile.ps1 @@ -10,6 +10,7 @@ param( [string]$ShimProjectsPath, [string]$ShimManifestPath, [string]$GeneratedShimProjectsPath, + [string]$GeneratedShimSourcesPath, [string]$ShimOutputPath, [switch]$Rebuild ) @@ -36,6 +37,10 @@ if (-not $GeneratedShimProjectsPath) { $GeneratedShimProjectsPath = Join-Path $repoRoot '.tmp\generated-shims' } +if (-not $GeneratedShimSourcesPath) { + $GeneratedShimSourcesPath = Join-Path $repoRoot '.tmp\generated-shim-sources' +} + if (-not $ShimOutputPath) { $ShimOutputPath = Join-Path $ShimProjectsPath "artifacts\$Configuration" } @@ -43,6 +48,7 @@ if (-not $ShimOutputPath) { $resolvedShimProjectsPath = (Resolve-Path $ShimProjectsPath).Path $resolvedShimManifestPath = (Resolve-Path $ShimManifestPath).Path $resolvedGeneratedShimProjectsPath = [System.IO.Path]::GetFullPath($GeneratedShimProjectsPath) +$resolvedGeneratedShimSourcesPath = [System.IO.Path]::GetFullPath($GeneratedShimSourcesPath) $resolvedShimOutputDirectory = [System.IO.Path]::GetFullPath($ShimOutputPath) $shimDirectoryBuildPropsPath = Join-Path $resolvedShimProjectsPath 'Directory.Build.props' $buildScriptPath = Join-Path $PSScriptRoot 'Build-AddIns.ps1' @@ -123,7 +129,12 @@ if (Test-Path -LiteralPath $resolvedGeneratedShimProjectsPath) { Remove-Item -LiteralPath $resolvedGeneratedShimProjectsPath -Recurse -Force } +if (Test-Path -LiteralPath $resolvedGeneratedShimSourcesPath) { + Remove-Item -LiteralPath $resolvedGeneratedShimSourcesPath -Recurse -Force +} + $null = New-Item -ItemType Directory -Path $resolvedGeneratedShimProjectsPath -Force +$null = New-Item -ItemType Directory -Path $resolvedGeneratedShimSourcesPath -Force foreach ($projectEntry in $shimProjects.GetEnumerator()) { $projectName = [string]$projectEntry.Key @@ -144,7 +155,38 @@ foreach ($projectEntry in $shimProjects.GetEnumerator()) { $projectLines.Add(' ') $sourcePaths = [System.Collections.Generic.List[string]]::new() - if ($projectConfig.ContainsKey('Sources')) { + if ($projectConfig.ContainsKey('SourceFiles')) { + $projectSourceRoot = Join-Path $resolvedGeneratedShimSourcesPath $projectName + $null = New-Item -ItemType Directory -Path $projectSourceRoot -Force + + foreach ($sourceFile in @($projectConfig.SourceFiles)) { + if ($null -eq $sourceFile) { + continue + } + + $sourceRelativePath = [string]$sourceFile.Path + if ([string]::IsNullOrWhiteSpace($sourceRelativePath)) { + continue + } + + $sourceOutputPath = Join-Path $projectSourceRoot $sourceRelativePath + $sourceOutputDirectory = Split-Path -Path $sourceOutputPath -Parent + if (-not [string]::IsNullOrWhiteSpace($sourceOutputDirectory)) { + $null = New-Item -ItemType Directory -Path $sourceOutputDirectory -Force + } + + $sourceContent = if ($sourceFile.ContainsKey('Content')) { + [string]$sourceFile.Content + } + else { + [string]$sourceFile.content + } + + Set-Content -LiteralPath $sourceOutputPath -Value $sourceContent -Encoding utf8 + $null = $sourcePaths.Add($sourceOutputPath) + } + } + elseif ($projectConfig.ContainsKey('Sources')) { foreach ($sourceEntry in @($projectConfig.Sources)) { if (-not [string]::IsNullOrWhiteSpace([string]$sourceEntry)) { $sourcePath = [string]$sourceEntry diff --git a/shims/generate-shim-manifest.yml b/shims/generate-shim-manifest.yml index ad4e9f8..b0f641b 100644 --- a/shims/generate-shim-manifest.yml +++ b/shims/generate-shim-manifest.yml @@ -19,9 +19,26 @@ steps: foreach ($shim in @($shimDefinitions)) { $project = [ordered]@{} - $projectSources = @($shim.sources | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) }) - if ($projectSources.Count -gt 0) { - $project['Sources'] = @($projectSources | ForEach-Object { [string]$_ }) + $sourceFiles = [System.Collections.Generic.List[object]]::new() + foreach ($sourceFile in @($shim.sourceFiles)) { + if ($null -eq $sourceFile) { + continue + } + + $sourcePath = [string]$sourceFile.path + $sourceContent = [string]$sourceFile.content + if ([string]::IsNullOrWhiteSpace($sourcePath)) { + continue + } + + $null = $sourceFiles.Add([ordered]@{ + Path = $sourcePath + Content = $sourceContent + }) + } + + if ($sourceFiles.Count -gt 0) { + $project['SourceFiles'] = @($sourceFiles) } $projectReferences = @($shim.projectReferences | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) }) diff --git a/shims/shim-projects.yml b/shims/shim-projects.yml index b033e02..b84057a 100644 --- a/shims/shim-projects.yml +++ b/shims/shim-projects.yml @@ -7,103 +7,1725 @@ parameters: default: - name: "Aveva.ApplicationFramework" rootNamespace: "" - sources: - - "src/ApplicationFramework.cs" + sourceFiles: + - path: "src/ApplicationFramework.cs" + content: |- + using System; + using System.Collections.Generic; + + namespace Aveva.ApplicationFramework + { + public interface IAddinInjected + { + string Name { get; } + + string Description { get; } + + void Start(IDependencyResolver resolver); + + void Start(ServiceManager serviceManager); + + void Stop(); + } + + public interface IDependencyResolver + { + } + + public sealed class ServiceManager + { + } + + public static class DependencyResolver + { + private static readonly Aveva.ApplicationFramework.Presentation.WindowManager s_windowManager = new Aveva.ApplicationFramework.Presentation.WindowManager(); + private static readonly Aveva.ApplicationFramework.Presentation.CommandManager s_commandManager = new Aveva.ApplicationFramework.Presentation.CommandManager(); + private static readonly Dictionary s_services = new Dictionary + { + { typeof(Aveva.ApplicationFramework.Presentation.IWindowManager), s_windowManager }, + { typeof(Aveva.ApplicationFramework.Presentation.ICommandManager), s_commandManager }, + { typeof(Aveva.ApplicationFramework.Presentation.ICommandBarManager), s_windowManager.CommandBarManager }, + }; + + public static T GetImplementationOf() where T : class + { + if (s_services.TryGetValue(typeof(T), out object service)) + { + return service as T; + } + + return null; + } + } + + public static class Log + { + private static readonly LoggerProxy s_logger = new LoggerProxy(); + + public static LoggerProxy Logger() + { + return s_logger; + } + } + + public sealed class LoggerProxy + { + public void MessageFormat(string format, params object[] args) + { + } + } + } projectReferences: - "Aveva.ApplicationFramework.Presentation" - name: "Aveva.ApplicationFramework.Implementation" rootNamespace: "" - sources: [] + sourceFiles: [] projectReferences: [] - name: "Aveva.ApplicationFramework.Presentation" rootNamespace: "" - sources: - - "src/ApplicationFrameworkPresentation.cs" + sourceFiles: + - path: "src/ApplicationFrameworkPresentation.cs" + content: |- + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.Windows.Forms; + + namespace Aveva.ApplicationFramework.Presentation + { + public class Command + { + public string Key { get; set; } + + public IList List { get; } = new List(); + + public object Value { get; set; } + + public bool Checked { get; set; } + + public virtual void Execute() + { + } + } + + public enum DockedPosition + { + Left, + Right, + Top, + Bottom, + } + + public class DockedWindow + { + public event CancelEventHandler Closing; + + public event EventHandler Closed; + + public string Key { get; set; } + + public string Title { get; set; } + + public Control Content { get; set; } + + public DockedPosition Position { get; set; } + + public int Width { get; set; } + + public bool SaveLayout { get; set; } + + public bool Visible { get; private set; } + + public void Show() + { + Visible = true; + } + + public void Hide() + { + Visible = false; + } + + public void Close() + { + CancelEventArgs args = new CancelEventArgs(); + Closing?.Invoke(this, args); + if (args.Cancel) + { + return; + } + + Visible = false; + Closed?.Invoke(this, EventArgs.Empty); + } + } + + public interface IWindowManager + { + event EventHandler WindowLayoutLoaded; + + ICommandBarManager CommandBarManager { get; } + + DockedWindow CreateDockedWindow(string key, string title, Control control, DockedPosition position); + } + + public interface ICommandManager + { + CommandCollection Commands { get; } + } + + public interface ICommandBarManager + { + event EventHandler UILoaded; + + ToolCollection RootTools { get; } + } + + public interface ITool + { + string Key { get; set; } + + bool Checked { get; set; } + + event EventHandler ToolClick; + } + + public sealed class CommandCollection : List + { + } + + public class CommandManager : ICommandManager + { + public CommandCollection Commands { get; } = new CommandCollection(); + } + + public class WindowManager : IWindowManager + { + public event EventHandler WindowLayoutLoaded; + + public ICommandBarManager CommandBarManager { get; } = new CommandBarManager(); + + public DockedWindow CreateDockedWindow(string key, string title, Control control, DockedPosition position) + { + WindowLayoutLoaded?.Invoke(this, EventArgs.Empty); + return new DockedWindow + { + Key = key, + Title = title, + Content = control, + Position = position, + }; + } + } + + public class CommandBarManager : ICommandBarManager + { + public event EventHandler UILoaded; + + public ToolCollection RootTools { get; } = new ToolCollection(); + + public void RaiseUiLoaded() + { + UILoaded?.Invoke(this, EventArgs.Empty); + } + } + + public class ToolEventArgs : EventArgs + { + public string Key { get; set; } + } + + public delegate void ToolEventHandler(object sender, ToolEventArgs args); + + public class ToolBase : ITool + { + public string Key { get; set; } + + public bool Checked { get; set; } + + public event EventHandler ToolClick; + + protected void RaiseToolClick() + { + ToolClick?.Invoke(this, EventArgs.Empty); + } + } + + public class ButtonTool : ToolBase + { + } + + public class StateButtonTool : ToolBase + { + } + + public class MenuTool : ToolBase + { + public bool IsContextMenu { get; set; } + + public ToolCollection Tools { get; } = new ToolCollection(); + } + + public class ToolCollection + { + private readonly Dictionary _items = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public event ToolEventHandler ToolAdded; + + public ITool this[string key] => _items.TryGetValue(key, out ITool tool) ? tool : null; + + public bool Contains(string key) + { + return _items.ContainsKey(key); + } + + public MenuTool AddMenuTool(string key, string caption, object image, string category) + { + MenuTool tool = new MenuTool { Key = key }; + _items[key] = tool; + ToolAdded?.Invoke(this, new ToolEventArgs { Key = key }); + return tool; + } + + public ButtonTool AddButtonTool(string key, string caption, object image, string category) + { + ButtonTool tool = new ButtonTool { Key = key }; + _items[key] = tool; + ToolAdded?.Invoke(this, new ToolEventArgs { Key = key }); + return tool; + } + + public ITool AddTool(string key) + { + if (_items.TryGetValue(key, out ITool existing)) + { + return existing; + } + + ButtonTool tool = new ButtonTool { Key = key }; + _items[key] = tool; + ToolAdded?.Invoke(this, new ToolEventArgs { Key = key }); + return tool; + } + } + + public static class MessageBoxEx + { + public static DialogResult Show(string text) + { + return DialogResult.OK; + } + + public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) + { + return DialogResult.OK; + } + } + } projectReferences: [] - name: "Aveva.Core.Database" rootNamespace: "" - sources: - - "src/Database.cs" + sourceFiles: + - path: "src/Database.cs" + content: |- + using System; + using System.Collections; + using System.Collections.Generic; + using Aveva.Core.Geometry; + using Aveva.Core.Utilities.Messaging; + + namespace Aveva.Core.Database + { + public class DbAttribute + { + public DbAttribute() + { + } + + public DbAttribute(string name) + { + Name = name; + } + + public string Name { get; set; } + + public static DbAttribute GetDbAttribute(string name) + { + return new DbAttribute(name); + } + } + + public static class DbAttributeInstance + { + public static readonly DbAttribute NAME = new DbAttribute(nameof(NAME)); + public static readonly DbAttribute DESC = new DbAttribute(nameof(DESC)); + public static readonly DbAttribute BUIL = new DbAttribute(nameof(BUIL)); + public static readonly DbAttribute REV = new DbAttribute(nameof(REV)); + public static readonly DbAttribute PRES = new DbAttribute(nameof(PRES)); + public static readonly DbAttribute HREF = new DbAttribute(nameof(HREF)); + public static readonly DbAttribute LNTP = new DbAttribute(nameof(LNTP)); + public static readonly DbAttribute HDIR = new DbAttribute(nameof(HDIR)); + public static readonly DbAttribute POS = new DbAttribute(nameof(POS)); + public static readonly DbAttribute ORI = new DbAttribute(nameof(ORI)); + public static readonly DbAttribute FLNN = new DbAttribute(nameof(FLNN)); + public static readonly DbAttribute FLNM = new DbAttribute(nameof(FLNM)); + public static readonly DbAttribute AREA = new DbAttribute(nameof(AREA)); + public static readonly DbAttribute CREF = new DbAttribute(nameof(CREF)); + public static readonly DbAttribute ISNAME = new DbAttribute(nameof(ISNAME)); + public static readonly DbAttribute MEMB = new DbAttribute(nameof(MEMB)); + public static readonly DbAttribute DIAM = new DbAttribute(nameof(DIAM)); + public static readonly DbAttribute LCLM = new DbAttribute(nameof(LCLM)); + public static readonly DbAttribute LCLMH = new DbAttribute(nameof(LCLMH)); + public static readonly DbAttribute XLEN = new DbAttribute(nameof(XLEN)); + public static readonly DbAttribute YLEN = new DbAttribute(nameof(YLEN)); + public static readonly DbAttribute ZLEN = new DbAttribute(nameof(ZLEN)); + } + + public class DbElementType + { + public DbElementType() + { + } + + public DbElementType(string name) + { + Name = name; + } + + public string Name { get; set; } + + public static DbElementType GetElementType(int hash) + { + return new DbElementType(hash.ToString()); + } + + public static DbElementType GetElementType(string name) + { + return new DbElementType(name); + } + + public DbAttribute[] SystemAttributes() + { + return Array.Empty(); + } + } + + public static class DbElementTypeInstance + { + public static readonly DbElementType SITE = new DbElementType(nameof(SITE)); + public static readonly DbElementType ZONE = new DbElementType(nameof(ZONE)); + public static readonly DbElementType EQUIPMENT = new DbElementType(nameof(EQUIPMENT)); + public static readonly DbElementType CYLINDER = new DbElementType(nameof(CYLINDER)); + public static readonly DbElementType BOX = new DbElementType(nameof(BOX)); + public static readonly DbElementType NOZZLE = new DbElementType(nameof(NOZZLE)); + public static readonly DbElementType PIPE = new DbElementType(nameof(PIPE)); + public static readonly DbElementType BRANCH = new DbElementType(nameof(BRANCH)); + public static readonly DbElementType GASKET = new DbElementType(nameof(GASKET)); + public static readonly DbElementType ELBOW = new DbElementType(nameof(ELBOW)); + public static readonly DbElementType TEE = new DbElementType(nameof(TEE)); + } + + public class DbElement + { + public static DbElement GetElement(string name) + { + return new DbElement + { + Name = name, + IsValid = true, + }; + } + + public string Name { get; set; } + + public bool IsValid { get; set; } = true; + + public DbElement Previous { get; set; } = new DbElement(false); + + public DbElement() + { + } + + private DbElement(bool valid) + { + IsValid = valid; + } + + public void Delete() + { + } + + public DbElement Create(int position, DbElementType elementType) + { + return new DbElement(); + } + + public DbElement CreateAfter(DbElementType elementType) + { + return new DbElement(); + } + + public DbElement CreateFirst(DbElementType elementType) + { + return new DbElement(); + } + + public DbElement CreateLast(DbElementType elementType) + { + return new DbElement(); + } + + public void SetAttribute(DbAttribute attribute, object value) + { + } + + public string GetString(DbAttribute attribute) + { + return string.Empty; + } + + public bool GetBool(DbAttribute attribute) + { + return false; + } + + public int GetInteger(DbAttribute attribute) + { + return 0; + } + + public double GetDouble(DbAttribute attribute) + { + return 0d; + } + + public DbElement GetElement(DbAttribute attribute) + { + return new DbElement(); + } + + public Direction GetDirection(DbAttribute attribute) + { + return new Direction(); + } + + public Position GetPosition(DbAttribute attribute) + { + return Position.Create(0d, 0d, 0d); + } + + public Orientation GetOrientation(DbAttribute attribute) + { + return new Orientation(); + } + + public string GetAsString(DbAttribute attribute) + { + return string.Empty; + } + + public DbAttribute[] GetAttributes() + { + return Array.Empty(); + } + + public void Copy(DbElement element) + { + } + + public void CopyHierarchy(DbElement element, DbCopyOption options) + { + } + + public DbElement FirstMember() + { + return new DbElement(); + } + + public DbElement FirstMember(DbElementType type) + { + return new DbElement(); + } + + public void InsertAfterLast(DbElement element) + { + } + + public void InsertAfter(DbElement element) + { + } + + public DbElement Next() + { + return new DbElement(); + } + + public DbElement Next(DbElementType type) + { + return new DbElement(); + } + + public DbElement[] Members() + { + return Array.Empty(); + } + + public DbElement[] Members(DbElementType type) + { + return Array.Empty(); + } + + public DbElement Member(int index) + { + return new DbElement(); + } + + public DbElement[] GetElementArray(DbAttribute attribute, DbElementType type) + { + return Array.Empty(); + } + + public double EvaluateDouble(DbExpression expression, DbAttributeUnit unit) + { + return 0d; + } + + public void SetRule(DbAttribute attribute, DbRule rule) + { + } + + public DbRule GetRule(DbAttribute attribute) + { + return new DbRule(); + } + + public void DeleteRule(DbAttribute attribute) + { + } + + public bool ExistRule(DbAttribute attribute) + { + return false; + } + + public bool VerifyRule(DbAttribute attribute) + { + return false; + } + + public void ExecuteRule(DbAttribute attribute) + { + } + + public void ExecuteAllRules() + { + } + + public void PropagateRules(DbAttribute attribute) + { + } + + public void Claim() + { + } + + public void Release() + { + } + + public void ClaimHierarchy() + { + } + + public void ReleaseHierarchy() + { + } + + public DbElementType GetElementType() + { + return new DbElementType(); + } + + public void ChangeType(DbElementType type) + { + } + + public bool AtDefault(DbAttribute attribute) + { + return true; + } + + public bool IsDescendant(DbElement element) + { + return false; + } + + public void SetAttributeDefault(DbAttribute attribute) + { + } + } + + public class DBElementCollection : IEnumerable + { + public DBElementCollection(DbElement root) + { + } + + public DBElementCollection(DbElement root, object filter) + { + } + + public bool IncludeRoot { get; set; } + + public object Filter { get; set; } + + public DBElementEnumerator GetEnumerator() + { + return new DBElementEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public class DBElementEnumerator : IEnumerator + { + public DbElement Current => new DbElement(); + + object IEnumerator.Current => Current; + + public bool MoveNext() + { + return false; + } + + public void Reset() + { + } + + public void Dispose() + { + } + } + + public class DbCopyOption + { + public string FromName { get; set; } + + public string ToName { get; set; } + + public bool Rename { get; set; } + } + + public class DbExpression + { + public static DbExpression Parse(string expression) + { + return new DbExpression(); + } + } + + public enum DbAttributeUnit + { + DIST, + } + + public enum DbRuleStatus + { + DYNAMIC, + } + + public enum DbExpressionType + { + REAL, + } + + public class DbRule + { + public static DbRule CreateDbRule(DbExpression expression, DbRuleStatus status, DbExpressionType expressionType) + { + return new DbRule(); + } + } + + public class Project + { + public static Project CurrentProject { get; } = new Project(); + + public string Name { get; set; } = string.Empty; + + public string UserName { get; set; } = string.Empty; + + public bool IsOpen() + { + return false; + } + + public void Open(string project, string username, string password) + { + Name = project; + UserName = username; + } + + public void Close() + { + } + } + + public class MDB + { + public static MDB CurrentMDB { get; } = new MDB(); + + public string Name { get; set; } = string.Empty; + + public void SaveWork(string description) + { + } + + public void CloseMDB() + { + } + + public void Open(MDBSetup setup, out PdmsMessage error) + { + error = new PdmsMessage(); + } + + public Db[] GetDBArray(DbType type) + { + return Array.Empty(); + } + + public DbElement GetFirstWorld(DbType type) + { + return new DbElement(); + } + + public void QuitWork() + { + } + } + + public class MDBSetup + { + public static MDBSetup CreateMDBSetup(string name, out PdmsMessage error) + { + error = new PdmsMessage(); + return new MDBSetup(); + } + } + + public class Db + { + public string Name { get; set; } = string.Empty; + } + + public enum DbType + { + Design, + } + + public class NameTable : IEnumerable + { + public static NameTable GetNameTable(Db db, DbAttribute attribute, string prefix, string suffix) + { + return new NameTable(); + } + + public IEnumerator GetEnumerator() + { + return ((IEnumerable)Array.Empty()).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public class ElementTreeNavigator + { + public ElementTreeNavigator(DbElement root, object filter) + { + } + + public DbElement FirstMemberInScan(DbElement element) + { + return new DbElement(); + } + + public DbElement NextInScan(DbElement element) + { + return new DbElement(); + } + + public DbElement Parent(DbElement element) + { + return new DbElement(); + } + + public DbElement[] MembersInScan(DbElement element) + { + return Array.Empty(); + } + } + + public class DbQualifier + { + } + + public static class DbPseudoAttribute + { + public delegate double GetDoubleDelegate(DbElement element, DbAttribute attribute, DbQualifier qualifier); + + public static void AddGetDoubleAttribute(DbAttribute attribute, DbElementType type, GetDoubleDelegate callback) + { + } + } + + public static class DbPostElementChange + { + public delegate void PostCreateDelegate(DbElement element); + + public delegate void PreDeleteDelegate(DbElement element); + + public delegate void PostMoveDelegate(DbElement element, DbElement oldOwner, int oldPosition); + + public delegate void PostAttributeChangeDelegate(DbElement element, DbAttribute attribute); + + public delegate void PostRefAttributeChangeDelegate(DbElement element, DbAttribute attribute, DbElement oldReference); + + public static void AddPostCreateElement(DbElementType type, PostCreateDelegate callback) + { + } + + public static void AddPreDeleteElement(DbElementType type, PreDeleteDelegate callback) + { + } + + public static void AddPostMoveElement(DbElementType type, PostMoveDelegate callback) + { + } + + public static void AddPostAttributeChange(DbAttribute attribute, PostAttributeChangeDelegate callback) + { + } + + public static void AddPostRefAttributeChange(DbAttribute attribute, PostRefAttributeChangeDelegate callback) + { + } + } + + public class Spatial + { + public static Spatial Instance { get; } = new Spatial(); + + public DbElement[] ElementsInBox(LimitsBox limitsBox, bool fullyWithin) + { + return Array.Empty(); + } + + public DbElement[] ElementsInElementBox(DbElement element, bool fullyWithin) + { + return Array.Empty(); + } + + public bool ElementInBox(DbElement element, LimitsBox limitsBox) + { + return false; + } + + public bool ElementInElementBox(DbElement first, DbElement second) + { + return false; + } + } + } projectReferences: - "Aveva.Core.Geometry" - "Aveva.Core.Utilities" - name: "Aveva.Core.Database.Filters" rootNamespace: "" - sources: - - "src/Filters.cs" + sourceFiles: + - path: "src/Filters.cs" + content: |- + using Aveva.Core.Database; + + namespace Aveva.Core.Database.Filters + { + public class TypeFilter + { + public TypeFilter() + { + } + + public TypeFilter(DbElementType type) + { + } + + public void Add(DbElementType type) + { + } + + public DbElement FirstMember(DbElement element) + { + return new DbElement(); + } + + public DbElement Next(DbElement element) + { + return new DbElement(); + } + + public DbElement[] Members(DbElement element) + { + return System.Array.Empty(); + } + + public DbElement Parent(DbElement element) + { + return new DbElement(); + } + } + + public class CompoundFilter + { + public void AddShow(object filter) + { + } + } + + public class AndFilter : CompoundFilter + { + public void Add(object filter) + { + } + + public bool Valid(DbElement element) + { + return true; + } + } + + public class TrueFilter + { + } + + public class AttributeTrueFilter + { + public AttributeTrueFilter(DbAttribute attribute) + { + } + + public bool Valid(DbElement element) + { + return true; + } + } + + public class BelowOrAtType + { + public BelowOrAtType(DbElementType type) + { + } + + public bool ScanBelow(DbElement element) + { + return true; + } + } + } projectReferences: - "Aveva.Core.Database" - name: "Aveva.Core.FlexibleExplorer.Control" rootNamespace: "" - sources: - - "src/FlexibleExplorer.cs" + sourceFiles: + - path: "src/FlexibleExplorer.cs" + content: |- + using System.Windows.Forms; + using Aveva.ApplicationFramework; + using Aveva.Core.Presentation; + + namespace Aveva.Core.FlexibleExplorer + { + public class FlexibleExplorer : Control + { + public FlexibleExplorer(IDependencyResolver dependencyResolver, object viewContext, bool useDefaultImages) + { + } + + public BorderStyle BorderStyle { get; set; } + + public bool DisplayCheckBox { get; set; } + + public bool DisplayLockIndicator { get; set; } + + public bool DisplayClaimIndicator { get; set; } + + public bool DisplayReadOnlyIndicator { get; set; } + + public Aveva.Core.FlexibleExplorer.IFlexibleExplorerRefreshManager CustomRefreshManager { get; set; } + + public void MultiSelect(bool enabled) + { + } + + public void CreateRoots(object rootDefinitions, bool preserveExisting) + { + } + } + } projectReferences: - "Aveva.ApplicationFramework" - "Aveva.Core.FlexibleExplorer.Core" - "Aveva.Core.Presentation" - name: "Aveva.Core.FlexibleExplorer.Core" rootNamespace: "" - sources: - - "src/FlexibleExplorerCore.cs" + sourceFiles: + - path: "src/FlexibleExplorerCore.cs" + content: |- + using System.IO; + using Aveva.Core.Presentation; + + namespace Aveva.Core.FlexibleExplorer + { + public enum RefreshOperation + { + None, + NodesOnly, + NodesWithCollections, + WholeTree, + } + + public class RefreshArguments + { + public DbChangesEventArgs DbChangesEventArgs { get; set; } + + public object DbRawChanges { get; set; } + + public RefreshOperation RefreshType { get; set; } + } + + public interface IFlexibleExplorerRefreshManager + { + RefreshArguments GetRefreshType(RefreshArguments refreshArguments); + } + + public class XmlDefinitionProvider + { + public XmlDefinitionProvider(string xmlContent, string fileName) + { + } + + public object RootDefinitions { get; } = new object[0]; + + public static string GetLocalFile(string path) + { + return File.Exists(path) ? path : null; + } + } + } projectReferences: - "Aveva.Core.Presentation" - name: "Aveva.Core.Geometry" rootNamespace: "" - sources: - - "src/Geometry.cs" + sourceFiles: + - path: "src/Geometry.cs" + content: |- + namespace Aveva.Core.Geometry + { + public class Direction + { + public static Direction Create(double x, double y, double z) + { + return new Direction(); + } + } + + public class Position + { + public double X { get; set; } + + public double Y { get; set; } + + public double Z { get; set; } + + public static Position Create(double x, double y, double z) + { + return new Position + { + X = x, + Y = y, + Z = z, + }; + } + } + + public class Orientation + { + public static Orientation Create(Direction xAxis, Direction yAxis) + { + return new Orientation(); + } + } + + public class LimitsBox + { + public static LimitsBox Create(Position first, Position second) + { + return new LimitsBox(); + } + } + } projectReferences: [] - name: "Aveva.Core.Implementation" rootNamespace: "" - sources: [] + sourceFiles: [] projectReferences: [] - name: "Aveva.Core.Presentation" rootNamespace: "" - sources: - - "src/Presentation.cs" + sourceFiles: + - path: "src/Presentation.cs" + content: |- + using System; + using System.Collections; + using System.Windows.Forms; + using Aveva.ApplicationFramework.Presentation; + using Aveva.Core.Database; + using Aveva.Core.PMLNet; + + namespace Aveva.Core.Presentation + { + public delegate void CurrentElementChangedEventHandler(object sender, CurrentElementChangedEventArgs e); + + public class CurrentElementChangedEventArgs : EventArgs + { + public DbElement Element { get; set; } = new DbElement(); + } + + public static class CurrentElement + { + private static DbElement s_element = new DbElement(); + + public static event CurrentElementChangedEventHandler CurrentElementChanged; + + public static DbElement Element + { + get => s_element; + set + { + s_element = value; + CurrentElementChanged?.Invoke(null, new CurrentElementChangedEventArgs { Element = value }); + } + } + } + + public delegate void DbChangesEventHandler(object sender, DbChangesEventArgs e); + + public class DbChangesEventArgs : EventArgs + { + public ChangeList ChangeList { get; } = new ChangeList(); + } + + public class ChangeList + { + public DbElement[] GetCreations() + { + return Array.Empty(); + } + + public DbElement[] GetDeletions() + { + return Array.Empty(); + } + + public DbElement[] GetMemberChanges() + { + return Array.Empty(); + } + + public DbElement[] GetModified() + { + return Array.Empty(); + } + } + + public static class DatabaseService + { + public static event DbChangesEventHandler Changes; + + public static void RaiseChanges() + { + Changes?.Invoke(null, new DbChangesEventArgs()); + } + } + + public class NetDataSource + { + public NetDataSource(string tableName, Hashtable attributes, Hashtable titles, Hashtable items) + { + } + } + + public class NetGridControl : Control + { + public bool allGridEvents { get; set; } + + public bool ColumnExcelFilter { get; set; } + + public bool ColumnSummaries { get; set; } + + public bool EditableGrid { get; set; } + + public bool ErrorIcon { get; set; } + + public bool ExtendLastColumn { get; set; } + + public bool FixedHeaders { get; set; } + + public bool FixedRows { get; set; } + + public int GridHeight { get; set; } + + public bool HeaderSort { get; set; } + + public bool HideGroupByBox { get; set; } + + public bool OutlookGroupStyle { get; set; } + + public bool RowAddDeleteGrid { get; set; } + + public bool ScrollSelectedRowToView { get; set; } + + public bool SingleRowSelection { get; set; } + + public bool Updates { get; set; } + + public MenuTool PopupMenu { get; set; } + + public MenuTool PopupMenuHeader { get; set; } + + public event PMLNetDelegate.PMLNetEventHandler AfterSelectChange; + + public void BindToDataSource(NetDataSource dataSource) + { + } + + public void setNameColumnImage() + { + } + + public void saveGridToExcel(string path) + { + } + + public void printPreview() + { + } + + public void setRowColor(double row, string colour) + { + } + } + } projectReferences: - "Aveva.ApplicationFramework.Presentation" - "Aveva.Core.Database" - "PMLNet" - name: "Aveva.Core.Utilities" rootNamespace: "" - sources: - - "src/Utilities.cs" + sourceFiles: + - path: "src/Utilities.cs" + content: |- + using System; + + namespace Aveva.Core.Utilities.Messaging + { + public class PdmsMessage + { + } + + public class PdmsError + { + public string MessageText() + { + return string.Empty; + } + } + + public class PdmsException : Exception + { + public PdmsError Error { get; } = new PdmsError(); + + public PdmsException() + { + } + + public PdmsException(string message) : base(message) + { + } + } + } + + namespace Aveva.Core.Utilities.Undo + { + public class UndoTransaction + { + public static UndoTransaction GetUndoTransaction() + { + return new UndoTransaction(); + } + + public void StartTransaction(string description) + { + } + + public void EndTransaction() + { + } + + public static void PerformUndo() + { + } + + public static void PerformRedo() + { + } + } + + public abstract class UndoSubscriber + { + public virtual object GetBeginState() + { + return null; + } + + public virtual object GetEndState() + { + return null; + } + + public virtual void RestoreBeginState(object state) + { + } + + public virtual void RestoreEndState(object state) + { + } + } + + public static class UndoCaretaker + { + public static void RegisterUndoSubscriber(UndoSubscriber subscriber) + { + } + + public static void RemoveUndoSubscriber(UndoSubscriber subscriber) + { + } + } + } + + namespace Aveva.Core.Utilities.CommandLine + { + public static class Command + { + public static void Update() + { + } + } + } projectReferences: [] - name: "Aveva.Core3D.Clasher" rootNamespace: "" - sources: - - "src/Clasher.cs" + sourceFiles: + - path: "src/Clasher.cs" + content: |- + using System; + using Aveva.Core.Database; + using Aveva.Core.Geometry; + + namespace Aveva.Core3D.Clasher + { + public enum SpatialMapStatus + { + Unknown, + Complete, + } + + public class SpatialMap + { + public static SpatialMap Instance { get; } = new SpatialMap(); + + public SpatialMapStatus CheckSpatialMap(Db db) + { + return SpatialMapStatus.Complete; + } + + public void BuildSpatialMap() + { + } + } + + public class ClashOptions + { + public bool IncludeTouches { get; set; } + + public double Clearance { get; set; } + + public static ClashOptions Create() + { + return new ClashOptions(); + } + } + + public class ObstructionList + { + public static ObstructionList Create() + { + return new ObstructionList(); + } + } + + public class ClashSet + { + public Clash[] Clashes { get; set; } = Array.Empty(); + + public static ClashSet Create() + { + return new ClashSet(); + } + } + + public class Clasher + { + public static Clasher Instance { get; } = new Clasher(); + + public bool CheckAll(ClashOptions options, ObstructionList obstructions, ClashSet clashSet) + { + return false; + } + } + + public class Clash + { + public DbElement First { get; set; } = new DbElement(); + + public DbElement Second { get; set; } = new DbElement(); + + public ClashType Type { get; set; } + + public Position ClashPosition { get; set; } = Position.Create(0d, 0d, 0d); + } + + public enum ClashType + { + None, + } + } projectReferences: - "Aveva.Core.Database" - "Aveva.Core.Geometry" - name: "GridControl" rootNamespace: "" - sources: [] + sourceFiles: [] projectReferences: [] - name: "Microsoft.Web.WebView2.Core" rootNamespace: "" - sources: - - "src/WebView2Core.cs" + sourceFiles: + - path: "src/WebView2Core.cs" + content: |- + using System; + + namespace Microsoft.Web.WebView2.Core + { + public class CoreWebView2Settings + { + public bool AreDefaultContextMenusEnabled { get; set; } + + public bool IsStatusBarEnabled { get; set; } + + public bool IsZoomControlEnabled { get; set; } + + public bool AreDevToolsEnabled { get; set; } + } + + public class CoreWebView2NavigationStartingEventArgs : EventArgs + { + } + + public class CoreWebView2NavigationCompletedEventArgs : EventArgs + { + } + + public class CoreWebView2 + { + public CoreWebView2Settings Settings { get; } = new CoreWebView2Settings(); + + public event EventHandler NavigationStarting; + + public event EventHandler NavigationCompleted; + } + } projectReferences: [] - name: "Microsoft.Web.WebView2.WinForms" rootNamespace: "" - sources: - - "src/WebView2.cs" + sourceFiles: + - path: "src/WebView2.cs" + content: |- + using System; + using System.ComponentModel; + using System.Threading.Tasks; + using System.Windows.Forms; + + namespace Microsoft.Web.WebView2.WinForms + { + public class WebView2 : Control, ISupportInitialize + { + public Microsoft.Web.WebView2.Core.CoreWebView2 CoreWebView2 { get; private set; } = new Microsoft.Web.WebView2.Core.CoreWebView2(); + + public double ZoomFactor { get; set; } + + public Uri Source { get; set; } + + public Task EnsureCoreWebView2Async(object environment) + { + if (CoreWebView2 == null) + { + CoreWebView2 = new Microsoft.Web.WebView2.Core.CoreWebView2(); + } + + return Task.CompletedTask; + } + + public void BeginInit() + { + } + + public void EndInit() + { + } + } + } projectReferences: - "Microsoft.Web.WebView2.Core" - name: "Newtonsoft.Json" rootNamespace: "" - sources: - - "src/Json.cs" + sourceFiles: + - path: "src/Json.cs" + content: |- + using System.Collections; + using System.Collections.Generic; + + namespace Newtonsoft.Json.Linq + { + public enum JTokenType + { + Object, + Array, + String, + Integer, + Float, + Boolean, + Null, + } + + public abstract class JToken + { + public abstract JTokenType Type { get; } + + public static JToken Parse(string json) + { + return new JObject(); + } + + public virtual T Value() + { + return default(T); + } + + public override string ToString() + { + return string.Empty; + } + } + + public sealed class JObject : JToken + { + public override JTokenType Type => JTokenType.Object; + + public IEnumerable Properties() + { + return new JProperty[0]; + } + } + + public sealed class JArray : JToken, IEnumerable + { + public override JTokenType Type => JTokenType.Array; + + public int Count => 0; + + public IEnumerator GetEnumerator() + { + return ((IEnumerable)new JToken[0]).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public sealed class JProperty + { + public string Name { get; set; } = string.Empty; + + public JToken Value { get; set; } = new JValue(); + } + + public sealed class JValue : JToken + { + public override JTokenType Type => JTokenType.String; + + public override T Value() + { + return default(T); + } + } + } projectReferences: [] - name: "PMLNet" rootNamespace: "Aveva.Core.PMLNet" - sources: - - "src/PmlNet.cs" + sourceFiles: + - path: "src/PmlNet.cs" + content: |- + using System; + using System.Collections; + + namespace Aveva.Core.PMLNet + { + [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] + public sealed class PMLNetCallableAttribute : Attribute + { + } + + public static class PMLNetDelegate + { + public delegate void PMLNetEventHandler(ArrayList args); + } + } projectReferences: [] - name: "StartUp" rootNamespace: "" - sources: [] + sourceFiles: [] projectReferences: [] steps: diff --git a/shims/src/ApplicationFramework.cs b/shims/src/ApplicationFramework.cs deleted file mode 100644 index c9636ca..0000000 --- a/shims/src/ApplicationFramework.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Aveva.ApplicationFramework -{ - public interface IAddinInjected - { - string Name { get; } - - string Description { get; } - - void Start(IDependencyResolver resolver); - - void Start(ServiceManager serviceManager); - - void Stop(); - } - - public interface IDependencyResolver - { - } - - public sealed class ServiceManager - { - } - - public static class DependencyResolver - { - private static readonly Aveva.ApplicationFramework.Presentation.WindowManager s_windowManager = new Aveva.ApplicationFramework.Presentation.WindowManager(); - private static readonly Aveva.ApplicationFramework.Presentation.CommandManager s_commandManager = new Aveva.ApplicationFramework.Presentation.CommandManager(); - private static readonly Dictionary s_services = new Dictionary - { - { typeof(Aveva.ApplicationFramework.Presentation.IWindowManager), s_windowManager }, - { typeof(Aveva.ApplicationFramework.Presentation.ICommandManager), s_commandManager }, - { typeof(Aveva.ApplicationFramework.Presentation.ICommandBarManager), s_windowManager.CommandBarManager }, - }; - - public static T GetImplementationOf() where T : class - { - if (s_services.TryGetValue(typeof(T), out object service)) - { - return service as T; - } - - return null; - } - } - - public static class Log - { - private static readonly LoggerProxy s_logger = new LoggerProxy(); - - public static LoggerProxy Logger() - { - return s_logger; - } - } - - public sealed class LoggerProxy - { - public void MessageFormat(string format, params object[] args) - { - } - } -} \ No newline at end of file diff --git a/shims/src/ApplicationFrameworkPresentation.cs b/shims/src/ApplicationFrameworkPresentation.cs deleted file mode 100644 index 89218c3..0000000 --- a/shims/src/ApplicationFrameworkPresentation.cs +++ /dev/null @@ -1,236 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Windows.Forms; - -namespace Aveva.ApplicationFramework.Presentation -{ - public class Command - { - public string Key { get; set; } - - public IList List { get; } = new List(); - - public object Value { get; set; } - - public bool Checked { get; set; } - - public virtual void Execute() - { - } - } - - public enum DockedPosition - { - Left, - Right, - Top, - Bottom, - } - - public class DockedWindow - { - public event CancelEventHandler Closing; - - public event EventHandler Closed; - - public string Key { get; set; } - - public string Title { get; set; } - - public Control Content { get; set; } - - public DockedPosition Position { get; set; } - - public int Width { get; set; } - - public bool SaveLayout { get; set; } - - public bool Visible { get; private set; } - - public void Show() - { - Visible = true; - } - - public void Hide() - { - Visible = false; - } - - public void Close() - { - CancelEventArgs args = new CancelEventArgs(); - Closing?.Invoke(this, args); - if (args.Cancel) - { - return; - } - - Visible = false; - Closed?.Invoke(this, EventArgs.Empty); - } - } - - public interface IWindowManager - { - event EventHandler WindowLayoutLoaded; - - ICommandBarManager CommandBarManager { get; } - - DockedWindow CreateDockedWindow(string key, string title, Control control, DockedPosition position); - } - - public interface ICommandManager - { - CommandCollection Commands { get; } - } - - public interface ICommandBarManager - { - event EventHandler UILoaded; - - ToolCollection RootTools { get; } - } - - public interface ITool - { - string Key { get; set; } - - bool Checked { get; set; } - - event EventHandler ToolClick; - } - - public sealed class CommandCollection : List - { - } - - public class CommandManager : ICommandManager - { - public CommandCollection Commands { get; } = new CommandCollection(); - } - - public class WindowManager : IWindowManager - { - public event EventHandler WindowLayoutLoaded; - - public ICommandBarManager CommandBarManager { get; } = new CommandBarManager(); - - public DockedWindow CreateDockedWindow(string key, string title, Control control, DockedPosition position) - { - WindowLayoutLoaded?.Invoke(this, EventArgs.Empty); - return new DockedWindow - { - Key = key, - Title = title, - Content = control, - Position = position, - }; - } - } - - public class CommandBarManager : ICommandBarManager - { - public event EventHandler UILoaded; - - public ToolCollection RootTools { get; } = new ToolCollection(); - - public void RaiseUiLoaded() - { - UILoaded?.Invoke(this, EventArgs.Empty); - } - } - - public class ToolEventArgs : EventArgs - { - public string Key { get; set; } - } - - public delegate void ToolEventHandler(object sender, ToolEventArgs args); - - public class ToolBase : ITool - { - public string Key { get; set; } - - public bool Checked { get; set; } - - public event EventHandler ToolClick; - - protected void RaiseToolClick() - { - ToolClick?.Invoke(this, EventArgs.Empty); - } - } - - public class ButtonTool : ToolBase - { - } - - public class StateButtonTool : ToolBase - { - } - - public class MenuTool : ToolBase - { - public bool IsContextMenu { get; set; } - - public ToolCollection Tools { get; } = new ToolCollection(); - } - - public class ToolCollection - { - private readonly Dictionary _items = new Dictionary(StringComparer.OrdinalIgnoreCase); - - public event ToolEventHandler ToolAdded; - - public ITool this[string key] => _items.TryGetValue(key, out ITool tool) ? tool : null; - - public bool Contains(string key) - { - return _items.ContainsKey(key); - } - - public MenuTool AddMenuTool(string key, string caption, object image, string category) - { - MenuTool tool = new MenuTool { Key = key }; - _items[key] = tool; - ToolAdded?.Invoke(this, new ToolEventArgs { Key = key }); - return tool; - } - - public ButtonTool AddButtonTool(string key, string caption, object image, string category) - { - ButtonTool tool = new ButtonTool { Key = key }; - _items[key] = tool; - ToolAdded?.Invoke(this, new ToolEventArgs { Key = key }); - return tool; - } - - public ITool AddTool(string key) - { - if (_items.TryGetValue(key, out ITool existing)) - { - return existing; - } - - ButtonTool tool = new ButtonTool { Key = key }; - _items[key] = tool; - ToolAdded?.Invoke(this, new ToolEventArgs { Key = key }); - return tool; - } - } - - public static class MessageBoxEx - { - public static DialogResult Show(string text) - { - return DialogResult.OK; - } - - public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) - { - return DialogResult.OK; - } - } -} \ No newline at end of file diff --git a/shims/src/Clasher.cs b/shims/src/Clasher.cs deleted file mode 100644 index 61101e0..0000000 --- a/shims/src/Clasher.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using Aveva.Core.Database; -using Aveva.Core.Geometry; - -namespace Aveva.Core3D.Clasher -{ - public enum SpatialMapStatus - { - Unknown, - Complete, - } - - public class SpatialMap - { - public static SpatialMap Instance { get; } = new SpatialMap(); - - public SpatialMapStatus CheckSpatialMap(Db db) - { - return SpatialMapStatus.Complete; - } - - public void BuildSpatialMap() - { - } - } - - public class ClashOptions - { - public bool IncludeTouches { get; set; } - - public double Clearance { get; set; } - - public static ClashOptions Create() - { - return new ClashOptions(); - } - } - - public class ObstructionList - { - public static ObstructionList Create() - { - return new ObstructionList(); - } - } - - public class ClashSet - { - public Clash[] Clashes { get; set; } = Array.Empty(); - - public static ClashSet Create() - { - return new ClashSet(); - } - } - - public class Clasher - { - public static Clasher Instance { get; } = new Clasher(); - - public bool CheckAll(ClashOptions options, ObstructionList obstructions, ClashSet clashSet) - { - return false; - } - } - - public class Clash - { - public DbElement First { get; set; } = new DbElement(); - - public DbElement Second { get; set; } = new DbElement(); - - public ClashType Type { get; set; } - - public Position ClashPosition { get; set; } = Position.Create(0d, 0d, 0d); - } - - public enum ClashType - { - None, - } -} \ No newline at end of file diff --git a/shims/src/Database.cs b/shims/src/Database.cs deleted file mode 100644 index 1f48955..0000000 --- a/shims/src/Database.cs +++ /dev/null @@ -1,620 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using Aveva.Core.Geometry; -using Aveva.Core.Utilities.Messaging; - -namespace Aveva.Core.Database -{ - public class DbAttribute - { - public DbAttribute() - { - } - - public DbAttribute(string name) - { - Name = name; - } - - public string Name { get; set; } - - public static DbAttribute GetDbAttribute(string name) - { - return new DbAttribute(name); - } - } - - public static class DbAttributeInstance - { - public static readonly DbAttribute NAME = new DbAttribute(nameof(NAME)); - public static readonly DbAttribute DESC = new DbAttribute(nameof(DESC)); - public static readonly DbAttribute BUIL = new DbAttribute(nameof(BUIL)); - public static readonly DbAttribute REV = new DbAttribute(nameof(REV)); - public static readonly DbAttribute PRES = new DbAttribute(nameof(PRES)); - public static readonly DbAttribute HREF = new DbAttribute(nameof(HREF)); - public static readonly DbAttribute LNTP = new DbAttribute(nameof(LNTP)); - public static readonly DbAttribute HDIR = new DbAttribute(nameof(HDIR)); - public static readonly DbAttribute POS = new DbAttribute(nameof(POS)); - public static readonly DbAttribute ORI = new DbAttribute(nameof(ORI)); - public static readonly DbAttribute FLNN = new DbAttribute(nameof(FLNN)); - public static readonly DbAttribute FLNM = new DbAttribute(nameof(FLNM)); - public static readonly DbAttribute AREA = new DbAttribute(nameof(AREA)); - public static readonly DbAttribute CREF = new DbAttribute(nameof(CREF)); - public static readonly DbAttribute ISNAME = new DbAttribute(nameof(ISNAME)); - public static readonly DbAttribute MEMB = new DbAttribute(nameof(MEMB)); - public static readonly DbAttribute DIAM = new DbAttribute(nameof(DIAM)); - public static readonly DbAttribute LCLM = new DbAttribute(nameof(LCLM)); - public static readonly DbAttribute LCLMH = new DbAttribute(nameof(LCLMH)); - public static readonly DbAttribute XLEN = new DbAttribute(nameof(XLEN)); - public static readonly DbAttribute YLEN = new DbAttribute(nameof(YLEN)); - public static readonly DbAttribute ZLEN = new DbAttribute(nameof(ZLEN)); - } - - public class DbElementType - { - public DbElementType() - { - } - - public DbElementType(string name) - { - Name = name; - } - - public string Name { get; set; } - - public static DbElementType GetElementType(int hash) - { - return new DbElementType(hash.ToString()); - } - - public static DbElementType GetElementType(string name) - { - return new DbElementType(name); - } - - public DbAttribute[] SystemAttributes() - { - return Array.Empty(); - } - } - - public static class DbElementTypeInstance - { - public static readonly DbElementType SITE = new DbElementType(nameof(SITE)); - public static readonly DbElementType ZONE = new DbElementType(nameof(ZONE)); - public static readonly DbElementType EQUIPMENT = new DbElementType(nameof(EQUIPMENT)); - public static readonly DbElementType CYLINDER = new DbElementType(nameof(CYLINDER)); - public static readonly DbElementType BOX = new DbElementType(nameof(BOX)); - public static readonly DbElementType NOZZLE = new DbElementType(nameof(NOZZLE)); - public static readonly DbElementType PIPE = new DbElementType(nameof(PIPE)); - public static readonly DbElementType BRANCH = new DbElementType(nameof(BRANCH)); - public static readonly DbElementType GASKET = new DbElementType(nameof(GASKET)); - public static readonly DbElementType ELBOW = new DbElementType(nameof(ELBOW)); - public static readonly DbElementType TEE = new DbElementType(nameof(TEE)); - } - - public class DbElement - { - public static DbElement GetElement(string name) - { - return new DbElement - { - Name = name, - IsValid = true, - }; - } - - public string Name { get; set; } - - public bool IsValid { get; set; } = true; - - public DbElement Previous { get; set; } = new DbElement(false); - - public DbElement() - { - } - - private DbElement(bool valid) - { - IsValid = valid; - } - - public void Delete() - { - } - - public DbElement Create(int position, DbElementType elementType) - { - return new DbElement(); - } - - public DbElement CreateAfter(DbElementType elementType) - { - return new DbElement(); - } - - public DbElement CreateFirst(DbElementType elementType) - { - return new DbElement(); - } - - public DbElement CreateLast(DbElementType elementType) - { - return new DbElement(); - } - - public void SetAttribute(DbAttribute attribute, object value) - { - } - - public string GetString(DbAttribute attribute) - { - return string.Empty; - } - - public bool GetBool(DbAttribute attribute) - { - return false; - } - - public int GetInteger(DbAttribute attribute) - { - return 0; - } - - public double GetDouble(DbAttribute attribute) - { - return 0d; - } - - public DbElement GetElement(DbAttribute attribute) - { - return new DbElement(); - } - - public Direction GetDirection(DbAttribute attribute) - { - return new Direction(); - } - - public Position GetPosition(DbAttribute attribute) - { - return Position.Create(0d, 0d, 0d); - } - - public Orientation GetOrientation(DbAttribute attribute) - { - return new Orientation(); - } - - public string GetAsString(DbAttribute attribute) - { - return string.Empty; - } - - public DbAttribute[] GetAttributes() - { - return Array.Empty(); - } - - public void Copy(DbElement element) - { - } - - public void CopyHierarchy(DbElement element, DbCopyOption options) - { - } - - public DbElement FirstMember() - { - return new DbElement(); - } - - public DbElement FirstMember(DbElementType type) - { - return new DbElement(); - } - - public void InsertAfterLast(DbElement element) - { - } - - public void InsertAfter(DbElement element) - { - } - - public DbElement Next() - { - return new DbElement(); - } - - public DbElement Next(DbElementType type) - { - return new DbElement(); - } - - public DbElement[] Members() - { - return Array.Empty(); - } - - public DbElement[] Members(DbElementType type) - { - return Array.Empty(); - } - - public DbElement Member(int index) - { - return new DbElement(); - } - - public DbElement[] GetElementArray(DbAttribute attribute, DbElementType type) - { - return Array.Empty(); - } - - public double EvaluateDouble(DbExpression expression, DbAttributeUnit unit) - { - return 0d; - } - - public void SetRule(DbAttribute attribute, DbRule rule) - { - } - - public DbRule GetRule(DbAttribute attribute) - { - return new DbRule(); - } - - public void DeleteRule(DbAttribute attribute) - { - } - - public bool ExistRule(DbAttribute attribute) - { - return false; - } - - public bool VerifyRule(DbAttribute attribute) - { - return false; - } - - public void ExecuteRule(DbAttribute attribute) - { - } - - public void ExecuteAllRules() - { - } - - public void PropagateRules(DbAttribute attribute) - { - } - - public void Claim() - { - } - - public void Release() - { - } - - public void ClaimHierarchy() - { - } - - public void ReleaseHierarchy() - { - } - - public DbElementType GetElementType() - { - return new DbElementType(); - } - - public void ChangeType(DbElementType type) - { - } - - public bool AtDefault(DbAttribute attribute) - { - return true; - } - - public bool IsDescendant(DbElement element) - { - return false; - } - - public void SetAttributeDefault(DbAttribute attribute) - { - } - } - - public class DBElementCollection : IEnumerable - { - public DBElementCollection(DbElement root) - { - } - - public DBElementCollection(DbElement root, object filter) - { - } - - public bool IncludeRoot { get; set; } - - public object Filter { get; set; } - - public DBElementEnumerator GetEnumerator() - { - return new DBElementEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - public class DBElementEnumerator : IEnumerator - { - public DbElement Current => new DbElement(); - - object IEnumerator.Current => Current; - - public bool MoveNext() - { - return false; - } - - public void Reset() - { - } - - public void Dispose() - { - } - } - - public class DbCopyOption - { - public string FromName { get; set; } - - public string ToName { get; set; } - - public bool Rename { get; set; } - } - - public class DbExpression - { - public static DbExpression Parse(string expression) - { - return new DbExpression(); - } - } - - public enum DbAttributeUnit - { - DIST, - } - - public enum DbRuleStatus - { - DYNAMIC, - } - - public enum DbExpressionType - { - REAL, - } - - public class DbRule - { - public static DbRule CreateDbRule(DbExpression expression, DbRuleStatus status, DbExpressionType expressionType) - { - return new DbRule(); - } - } - - public class Project - { - public static Project CurrentProject { get; } = new Project(); - - public string Name { get; set; } = string.Empty; - - public string UserName { get; set; } = string.Empty; - - public bool IsOpen() - { - return false; - } - - public void Open(string project, string username, string password) - { - Name = project; - UserName = username; - } - - public void Close() - { - } - } - - public class MDB - { - public static MDB CurrentMDB { get; } = new MDB(); - - public string Name { get; set; } = string.Empty; - - public void SaveWork(string description) - { - } - - public void CloseMDB() - { - } - - public void Open(MDBSetup setup, out PdmsMessage error) - { - error = new PdmsMessage(); - } - - public Db[] GetDBArray(DbType type) - { - return Array.Empty(); - } - - public DbElement GetFirstWorld(DbType type) - { - return new DbElement(); - } - - public void QuitWork() - { - } - } - - public class MDBSetup - { - public static MDBSetup CreateMDBSetup(string name, out PdmsMessage error) - { - error = new PdmsMessage(); - return new MDBSetup(); - } - } - - public class Db - { - public string Name { get; set; } = string.Empty; - } - - public enum DbType - { - Design, - } - - public class NameTable : IEnumerable - { - public static NameTable GetNameTable(Db db, DbAttribute attribute, string prefix, string suffix) - { - return new NameTable(); - } - - public IEnumerator GetEnumerator() - { - return ((IEnumerable)Array.Empty()).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - public class ElementTreeNavigator - { - public ElementTreeNavigator(DbElement root, object filter) - { - } - - public DbElement FirstMemberInScan(DbElement element) - { - return new DbElement(); - } - - public DbElement NextInScan(DbElement element) - { - return new DbElement(); - } - - public DbElement Parent(DbElement element) - { - return new DbElement(); - } - - public DbElement[] MembersInScan(DbElement element) - { - return Array.Empty(); - } - } - - public class DbQualifier - { - } - - public static class DbPseudoAttribute - { - public delegate double GetDoubleDelegate(DbElement element, DbAttribute attribute, DbQualifier qualifier); - - public static void AddGetDoubleAttribute(DbAttribute attribute, DbElementType type, GetDoubleDelegate callback) - { - } - } - - public static class DbPostElementChange - { - public delegate void PostCreateDelegate(DbElement element); - - public delegate void PreDeleteDelegate(DbElement element); - - public delegate void PostMoveDelegate(DbElement element, DbElement oldOwner, int oldPosition); - - public delegate void PostAttributeChangeDelegate(DbElement element, DbAttribute attribute); - - public delegate void PostRefAttributeChangeDelegate(DbElement element, DbAttribute attribute, DbElement oldReference); - - public static void AddPostCreateElement(DbElementType type, PostCreateDelegate callback) - { - } - - public static void AddPreDeleteElement(DbElementType type, PreDeleteDelegate callback) - { - } - - public static void AddPostMoveElement(DbElementType type, PostMoveDelegate callback) - { - } - - public static void AddPostAttributeChange(DbAttribute attribute, PostAttributeChangeDelegate callback) - { - } - - public static void AddPostRefAttributeChange(DbAttribute attribute, PostRefAttributeChangeDelegate callback) - { - } - } - - public class Spatial - { - public static Spatial Instance { get; } = new Spatial(); - - public DbElement[] ElementsInBox(LimitsBox limitsBox, bool fullyWithin) - { - return Array.Empty(); - } - - public DbElement[] ElementsInElementBox(DbElement element, bool fullyWithin) - { - return Array.Empty(); - } - - public bool ElementInBox(DbElement element, LimitsBox limitsBox) - { - return false; - } - - public bool ElementInElementBox(DbElement first, DbElement second) - { - return false; - } - } -} \ No newline at end of file diff --git a/shims/src/Filters.cs b/shims/src/Filters.cs deleted file mode 100644 index 7186cd6..0000000 --- a/shims/src/Filters.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Aveva.Core.Database; - -namespace Aveva.Core.Database.Filters -{ - public class TypeFilter - { - public TypeFilter() - { - } - - public TypeFilter(DbElementType type) - { - } - - public void Add(DbElementType type) - { - } - - public DbElement FirstMember(DbElement element) - { - return new DbElement(); - } - - public DbElement Next(DbElement element) - { - return new DbElement(); - } - - public DbElement[] Members(DbElement element) - { - return System.Array.Empty(); - } - - public DbElement Parent(DbElement element) - { - return new DbElement(); - } - } - - public class CompoundFilter - { - public void AddShow(object filter) - { - } - } - - public class AndFilter : CompoundFilter - { - public void Add(object filter) - { - } - - public bool Valid(DbElement element) - { - return true; - } - } - - public class TrueFilter - { - } - - public class AttributeTrueFilter - { - public AttributeTrueFilter(DbAttribute attribute) - { - } - - public bool Valid(DbElement element) - { - return true; - } - } - - public class BelowOrAtType - { - public BelowOrAtType(DbElementType type) - { - } - - public bool ScanBelow(DbElement element) - { - return true; - } - } -} \ No newline at end of file diff --git a/shims/src/FlexibleExplorer.cs b/shims/src/FlexibleExplorer.cs deleted file mode 100644 index 0b95ee8..0000000 --- a/shims/src/FlexibleExplorer.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Windows.Forms; -using Aveva.ApplicationFramework; -using Aveva.Core.Presentation; - -namespace Aveva.Core.FlexibleExplorer -{ - public class FlexibleExplorer : Control - { - public FlexibleExplorer(IDependencyResolver dependencyResolver, object viewContext, bool useDefaultImages) - { - } - - public BorderStyle BorderStyle { get; set; } - - public bool DisplayCheckBox { get; set; } - - public bool DisplayLockIndicator { get; set; } - - public bool DisplayClaimIndicator { get; set; } - - public bool DisplayReadOnlyIndicator { get; set; } - - public Aveva.Core.FlexibleExplorer.IFlexibleExplorerRefreshManager CustomRefreshManager { get; set; } - - public void MultiSelect(bool enabled) - { - } - - public void CreateRoots(object rootDefinitions, bool preserveExisting) - { - } - } -} \ No newline at end of file diff --git a/shims/src/FlexibleExplorerCore.cs b/shims/src/FlexibleExplorerCore.cs deleted file mode 100644 index 64080d1..0000000 --- a/shims/src/FlexibleExplorerCore.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.IO; -using Aveva.Core.Presentation; - -namespace Aveva.Core.FlexibleExplorer -{ - public enum RefreshOperation - { - None, - NodesOnly, - NodesWithCollections, - WholeTree, - } - - public class RefreshArguments - { - public DbChangesEventArgs DbChangesEventArgs { get; set; } - - public object DbRawChanges { get; set; } - - public RefreshOperation RefreshType { get; set; } - } - - public interface IFlexibleExplorerRefreshManager - { - RefreshArguments GetRefreshType(RefreshArguments refreshArguments); - } - - public class XmlDefinitionProvider - { - public XmlDefinitionProvider(string xmlContent, string fileName) - { - } - - public object RootDefinitions { get; } = new object[0]; - - public static string GetLocalFile(string path) - { - return File.Exists(path) ? path : null; - } - } -} \ No newline at end of file diff --git a/shims/src/Geometry.cs b/shims/src/Geometry.cs deleted file mode 100644 index 6d241e6..0000000 --- a/shims/src/Geometry.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Aveva.Core.Geometry -{ - public class Direction - { - public static Direction Create(double x, double y, double z) - { - return new Direction(); - } - } - - public class Position - { - public double X { get; set; } - - public double Y { get; set; } - - public double Z { get; set; } - - public static Position Create(double x, double y, double z) - { - return new Position - { - X = x, - Y = y, - Z = z, - }; - } - } - - public class Orientation - { - public static Orientation Create(Direction xAxis, Direction yAxis) - { - return new Orientation(); - } - } - - public class LimitsBox - { - public static LimitsBox Create(Position first, Position second) - { - return new LimitsBox(); - } - } -} \ No newline at end of file diff --git a/shims/src/Json.cs b/shims/src/Json.cs deleted file mode 100644 index 853fb74..0000000 --- a/shims/src/Json.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.Collections; -using System.Collections.Generic; - -namespace Newtonsoft.Json.Linq -{ - public enum JTokenType - { - Object, - Array, - String, - Integer, - Float, - Boolean, - Null, - } - - public abstract class JToken - { - public abstract JTokenType Type { get; } - - public static JToken Parse(string json) - { - return new JObject(); - } - - public virtual T Value() - { - return default(T); - } - - public override string ToString() - { - return string.Empty; - } - } - - public sealed class JObject : JToken - { - public override JTokenType Type => JTokenType.Object; - - public IEnumerable Properties() - { - return new JProperty[0]; - } - } - - public sealed class JArray : JToken, IEnumerable - { - public override JTokenType Type => JTokenType.Array; - - public int Count => 0; - - public IEnumerator GetEnumerator() - { - return ((IEnumerable)new JToken[0]).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - public sealed class JProperty - { - public string Name { get; set; } = string.Empty; - - public JToken Value { get; set; } = new JValue(); - } - - public sealed class JValue : JToken - { - public override JTokenType Type => JTokenType.String; - - public override T Value() - { - return default(T); - } - } -} \ No newline at end of file diff --git a/shims/src/PmlNet.cs b/shims/src/PmlNet.cs deleted file mode 100644 index 3606942..0000000 --- a/shims/src/PmlNet.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections; - -namespace Aveva.Core.PMLNet -{ - [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] - public sealed class PMLNetCallableAttribute : Attribute - { - } - - public static class PMLNetDelegate - { - public delegate void PMLNetEventHandler(ArrayList args); - } -} \ No newline at end of file diff --git a/shims/src/Presentation.cs b/shims/src/Presentation.cs deleted file mode 100644 index 5116337..0000000 --- a/shims/src/Presentation.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System; -using System.Collections; -using System.Windows.Forms; -using Aveva.ApplicationFramework.Presentation; -using Aveva.Core.Database; -using Aveva.Core.PMLNet; - -namespace Aveva.Core.Presentation -{ - public delegate void CurrentElementChangedEventHandler(object sender, CurrentElementChangedEventArgs e); - - public class CurrentElementChangedEventArgs : EventArgs - { - public DbElement Element { get; set; } = new DbElement(); - } - - public static class CurrentElement - { - private static DbElement s_element = new DbElement(); - - public static event CurrentElementChangedEventHandler CurrentElementChanged; - - public static DbElement Element - { - get => s_element; - set - { - s_element = value; - CurrentElementChanged?.Invoke(null, new CurrentElementChangedEventArgs { Element = value }); - } - } - } - - public delegate void DbChangesEventHandler(object sender, DbChangesEventArgs e); - - public class DbChangesEventArgs : EventArgs - { - public ChangeList ChangeList { get; } = new ChangeList(); - } - - public class ChangeList - { - public DbElement[] GetCreations() - { - return Array.Empty(); - } - - public DbElement[] GetDeletions() - { - return Array.Empty(); - } - - public DbElement[] GetMemberChanges() - { - return Array.Empty(); - } - - public DbElement[] GetModified() - { - return Array.Empty(); - } - } - - public static class DatabaseService - { - public static event DbChangesEventHandler Changes; - - public static void RaiseChanges() - { - Changes?.Invoke(null, new DbChangesEventArgs()); - } - } - - public class NetDataSource - { - public NetDataSource(string tableName, Hashtable attributes, Hashtable titles, Hashtable items) - { - } - } - - public class NetGridControl : Control - { - public bool allGridEvents { get; set; } - - public bool ColumnExcelFilter { get; set; } - - public bool ColumnSummaries { get; set; } - - public bool EditableGrid { get; set; } - - public bool ErrorIcon { get; set; } - - public bool ExtendLastColumn { get; set; } - - public bool FixedHeaders { get; set; } - - public bool FixedRows { get; set; } - - public int GridHeight { get; set; } - - public bool HeaderSort { get; set; } - - public bool HideGroupByBox { get; set; } - - public bool OutlookGroupStyle { get; set; } - - public bool RowAddDeleteGrid { get; set; } - - public bool ScrollSelectedRowToView { get; set; } - - public bool SingleRowSelection { get; set; } - - public bool Updates { get; set; } - - public MenuTool PopupMenu { get; set; } - - public MenuTool PopupMenuHeader { get; set; } - - public event PMLNetDelegate.PMLNetEventHandler AfterSelectChange; - - public void BindToDataSource(NetDataSource dataSource) - { - } - - public void setNameColumnImage() - { - } - - public void saveGridToExcel(string path) - { - } - - public void printPreview() - { - } - - public void setRowColor(double row, string colour) - { - } - } -} \ No newline at end of file diff --git a/shims/src/Utilities.cs b/shims/src/Utilities.cs deleted file mode 100644 index 4671bda..0000000 --- a/shims/src/Utilities.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; - -namespace Aveva.Core.Utilities.Messaging -{ - public class PdmsMessage - { - } - - public class PdmsError - { - public string MessageText() - { - return string.Empty; - } - } - - public class PdmsException : Exception - { - public PdmsError Error { get; } = new PdmsError(); - - public PdmsException() - { - } - - public PdmsException(string message) : base(message) - { - } - } -} - -namespace Aveva.Core.Utilities.Undo -{ - public class UndoTransaction - { - public static UndoTransaction GetUndoTransaction() - { - return new UndoTransaction(); - } - - public void StartTransaction(string description) - { - } - - public void EndTransaction() - { - } - - public static void PerformUndo() - { - } - - public static void PerformRedo() - { - } - } - - public abstract class UndoSubscriber - { - public virtual object GetBeginState() - { - return null; - } - - public virtual object GetEndState() - { - return null; - } - - public virtual void RestoreBeginState(object state) - { - } - - public virtual void RestoreEndState(object state) - { - } - } - - public static class UndoCaretaker - { - public static void RegisterUndoSubscriber(UndoSubscriber subscriber) - { - } - - public static void RemoveUndoSubscriber(UndoSubscriber subscriber) - { - } - } -} - -namespace Aveva.Core.Utilities.CommandLine -{ - public static class Command - { - public static void Update() - { - } - } -} \ No newline at end of file diff --git a/shims/src/WebView2.cs b/shims/src/WebView2.cs deleted file mode 100644 index b0a8221..0000000 --- a/shims/src/WebView2.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.ComponentModel; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace Microsoft.Web.WebView2.WinForms -{ - public class WebView2 : Control, ISupportInitialize - { - public Microsoft.Web.WebView2.Core.CoreWebView2 CoreWebView2 { get; private set; } = new Microsoft.Web.WebView2.Core.CoreWebView2(); - - public double ZoomFactor { get; set; } - - public Uri Source { get; set; } - - public Task EnsureCoreWebView2Async(object environment) - { - if (CoreWebView2 == null) - { - CoreWebView2 = new Microsoft.Web.WebView2.Core.CoreWebView2(); - } - - return Task.CompletedTask; - } - - public void BeginInit() - { - } - - public void EndInit() - { - } - } -} \ No newline at end of file diff --git a/shims/src/WebView2Core.cs b/shims/src/WebView2Core.cs deleted file mode 100644 index ff9eced..0000000 --- a/shims/src/WebView2Core.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; - -namespace Microsoft.Web.WebView2.Core -{ - public class CoreWebView2Settings - { - public bool AreDefaultContextMenusEnabled { get; set; } - - public bool IsStatusBarEnabled { get; set; } - - public bool IsZoomControlEnabled { get; set; } - - public bool AreDevToolsEnabled { get; set; } - } - - public class CoreWebView2NavigationStartingEventArgs : EventArgs - { - } - - public class CoreWebView2NavigationCompletedEventArgs : EventArgs - { - } - - public class CoreWebView2 - { - public CoreWebView2Settings Settings { get; } = new CoreWebView2Settings(); - - public event EventHandler NavigationStarting; - - public event EventHandler NavigationCompleted; - } -} \ No newline at end of file From 36e06c982c95b4c487b6f131c6d277532bd58ebb Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 02:09:47 +0100 Subject: [PATCH 079/102] Move fake UE compile flow into YAML --- .../agents/ue-customization-manager.agent.md | 6 +- azure-pipelines.yml | 44 +- scripts/Invoke-FakeUECompile.ps1 | 378 --------------- shims/Directory.Build.props | 15 - shims/run-fake-ue-compile.yml | 459 ++++++++++++++++++ 5 files changed, 495 insertions(+), 407 deletions(-) delete mode 100644 scripts/Invoke-FakeUECompile.ps1 delete mode 100644 shims/Directory.Build.props create mode 100644 shims/run-fake-ue-compile.yml diff --git a/.github/agents/ue-customization-manager.agent.md b/.github/agents/ue-customization-manager.agent.md index 18ae696..6e93ebf 100644 --- a/.github/agents/ue-customization-manager.agent.md +++ b/.github/agents/ue-customization-manager.agent.md @@ -47,7 +47,7 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz 7. **Understand Hosted Validation**: - Recognize that [`azure-pipelines.yml`](../../azure-pipelines.yml) contains a hosted security-validation lane that uses fake UE references to validate both `templates/` and `Examples/`. - - Use [`scripts/Invoke-FakeUECompile.ps1`](../../scripts/Invoke-FakeUECompile.ps1) when reasoning about hosted compile-only validation. It builds shim assemblies under `shims/`, sets `UNIFIED_ENGINEERING_REFERENCE_PATH`, and compiles selected directories against the fake UE surface. + - Use the YAML-hosted fake compile flow in [`shims/run-fake-ue-compile.yml`](../../shims/run-fake-ue-compile.yml) when reasoning about hosted compile-only validation. It generates the transient runner, shim props, shim projects, shim sources, sets `UNIFIED_ENGINEERING_REFERENCE_PATH`, and compiles selected directories against the fake UE surface. - Treat `shims/` as compile-only stand-ins for hosted validation and static analysis, not runtime replacements for a real UE installation. - Remember that `pmllib/` is generated local output for PML template builds and is intentionally ignored in git. - When investigating hosted Coverity capture problems, verify that the Polaris-wrapped fake compile is using `-Rebuild` so compiler activity is emitted during capture. @@ -266,7 +266,7 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz **Key components**: - [`azure-pipelines.yml`](../../azure-pipelines.yml): Contains the hosted test lane and the optional hosted security-validation lane. -- [`scripts/Invoke-FakeUECompile.ps1`](../../scripts/Invoke-FakeUECompile.ps1): Consumes the generated shim manifest from `shims/shim-projects.yml`, emits CI-only shim project files into `.tmp/generated-shims`, builds compile-only shim assemblies, and compiles `templates/` and `Examples/` against them. +- [`shims/run-fake-ue-compile.yml`](../../shims/run-fake-ue-compile.yml): Generates the transient fake compile runner plus `.tmp` shim props, sources, and project files from `shims/shim-projects.yml`, builds the compile-only shim assemblies, and compiles `templates/` and `Examples/` against them. - [`scripts/Build-AddIns.ps1`](../../scripts/Build-AddIns.ps1): Supports `-SkipPostBuildEvent` and `-Rebuild` for deterministic compile-only builds. - [`shims/`](../../shims): Contains compile-only UE API stand-ins used for hosted validation and Coverity capture, plus the YAML shim definition and generation templates used to produce the runtime shim manifest at build time. @@ -280,7 +280,7 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz **Example guidance**: - "If hosted validation fails, I can inspect the fake-compile path and the multi-checkout pipeline job before assuming a UE runtime issue." -- "If Coverity reports zero captured files, I will verify that the Polaris build path is using `Invoke-FakeUECompile.ps1 -Rebuild`." +- "If Coverity reports zero captured files, I will verify that the Polaris build path is using the generated fake compile runner with `-Rebuild`." - "If you see a local `pmllib/` folder after building a PML template, that is generated output rather than source to commit." ## Typical Workflows diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9a747f7..476a7b3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -193,11 +193,16 @@ stages: parameters: OutputPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json - - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' - $shimManifestPath = Join-Path $repoRoot '.tmp/generated-shim-manifest.json' - & (Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release -ShimManifestPath $shimManifestPath - displayName: Build template and example projects with fake UE references + - template: shims/run-fake-ue-compile.yml + parameters: + Directory: templates,Examples + Configuration: Release + ShimManifestPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json + GeneratedRunnerPath: $(Pipeline.Workspace)/Source/.tmp/generated-fake-ue-compile.ps1 + GeneratedShimProjectsPath: $(Pipeline.Workspace)/Source/.tmp/generated-shims + GeneratedShimSourcesPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-sources + GeneratedShimPropsPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-props/Directory.Build.props + ShimOutputPath: $(Pipeline.Workspace)/Source/shims/artifacts/Release - publish: $(Pipeline.Workspace)/Source/shims/artifacts/Release artifact: Build.Release @@ -302,11 +307,16 @@ stages: parameters: OutputPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json - - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' - $shimManifestPath = Join-Path $repoRoot '.tmp/generated-shim-manifest.json' - & (Join-Path $repoRoot 'scripts\Invoke-FakeUECompile.ps1') -Directory templates,Examples -Configuration Release -ShimManifestPath $shimManifestPath - displayName: Build template and example projects with fake UE references + - template: shims/run-fake-ue-compile.yml + parameters: + Directory: templates,Examples + Configuration: Release + ShimManifestPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json + GeneratedRunnerPath: $(Pipeline.Workspace)/Source/.tmp/generated-fake-ue-compile.ps1 + GeneratedShimProjectsPath: $(Pipeline.Workspace)/Source/.tmp/generated-shims + GeneratedShimSourcesPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-sources + GeneratedShimPropsPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-props/Directory.Build.props + ShimOutputPath: $(Pipeline.Workspace)/Source/shims/artifacts/Release - pwsh: | $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' @@ -314,7 +324,9 @@ stages: HasShimSources = Test-Path -LiteralPath (Join-Path $repoRoot 'shims') HasShimManifest = Test-Path -LiteralPath (Join-Path $repoRoot 'shims/shim-projects.yml') HasGeneratedShimManifest = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shim-manifest.json') + HasGeneratedShimRunner = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-fake-ue-compile.ps1') HasGeneratedShimSources = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shim-sources') + HasGeneratedShimProps = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shim-props/Directory.Build.props') HasGeneratedShimProjects = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shims') HasShimAssemblies = Test-Path -LiteralPath (Join-Path $repoRoot 'shims/artifacts/Release') } @@ -342,11 +354,21 @@ stages: displayName: Publish generated shim manifest condition: and(always(), eq(variables.HasGeneratedShimManifest, 'true')) + - publish: $(Pipeline.Workspace)/Source/.tmp/generated-fake-ue-compile.ps1 + artifact: GeneratedShimRunner + displayName: Publish generated fake compile runner + condition: and(always(), eq(variables.HasGeneratedShimRunner, 'true')) + - publish: $(Pipeline.Workspace)/Source/.tmp/generated-shim-sources artifact: GeneratedShimSources displayName: Publish generated shim sources condition: and(always(), eq(variables.HasGeneratedShimSources, 'true')) + - publish: $(Pipeline.Workspace)/Source/.tmp/generated-shim-props + artifact: GeneratedShimProps + displayName: Publish generated shim props + condition: and(always(), eq(variables.HasGeneratedShimProps, 'true')) + - publish: $(Pipeline.Workspace)/Source/.tmp/generated-shims artifact: GeneratedShimProjects displayName: Publish generated shim project files @@ -360,7 +382,7 @@ stages: - pwsh: | $pwshPath = if ($IsLinux) { '/usr/bin/pwsh' } else { (Get-Command pwsh -ErrorAction Stop).Source } $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' - $fakeCompileScriptPath = Join-Path $repoRoot 'scripts/Invoke-FakeUECompile.ps1' + $fakeCompileScriptPath = Join-Path $repoRoot '.tmp/generated-fake-ue-compile.ps1' $shimManifestPath = Join-Path $repoRoot '.tmp/generated-shim-manifest.json' $branchName = "$env:BUILD_SOURCEBRANCH" -replace '^refs/heads/', '' diff --git a/scripts/Invoke-FakeUECompile.ps1 b/scripts/Invoke-FakeUECompile.ps1 deleted file mode 100644 index 9f7dde5..0000000 --- a/scripts/Invoke-FakeUECompile.ps1 +++ /dev/null @@ -1,378 +0,0 @@ -#Requires -Version 7.0 - -# Builds compile-only shim assemblies for hosted validation and Coverity capture. -# This is a CI-focused path; developers with a real UE installation should use the normal UE references. - -[CmdletBinding()] -param( - [string[]]$Directory = @('Examples'), - [string]$Configuration = 'Release', - [string]$ShimProjectsPath, - [string]$ShimManifestPath, - [string]$GeneratedShimProjectsPath, - [string]$GeneratedShimSourcesPath, - [string]$ShimOutputPath, - [switch]$Rebuild -) - -$ErrorActionPreference = 'Stop' - -$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path -$defaultGeneratedShimManifestPath = Join-Path $repoRoot '.tmp\generated-shim-manifest.json' - -if (-not $ShimProjectsPath) { - $ShimProjectsPath = Join-Path $repoRoot 'shims' -} - -if (-not $ShimManifestPath) { - if (Test-Path -LiteralPath $defaultGeneratedShimManifestPath) { - $ShimManifestPath = $defaultGeneratedShimManifestPath - } - else { - throw 'Shim manifest path was not provided and no generated manifest was found. Generate .tmp/generated-shim-manifest.json from shims/shim-projects.yml or pass -ShimManifestPath explicitly.' - } -} - -if (-not $GeneratedShimProjectsPath) { - $GeneratedShimProjectsPath = Join-Path $repoRoot '.tmp\generated-shims' -} - -if (-not $GeneratedShimSourcesPath) { - $GeneratedShimSourcesPath = Join-Path $repoRoot '.tmp\generated-shim-sources' -} - -if (-not $ShimOutputPath) { - $ShimOutputPath = Join-Path $ShimProjectsPath "artifacts\$Configuration" -} - -$resolvedShimProjectsPath = (Resolve-Path $ShimProjectsPath).Path -$resolvedShimManifestPath = (Resolve-Path $ShimManifestPath).Path -$resolvedGeneratedShimProjectsPath = [System.IO.Path]::GetFullPath($GeneratedShimProjectsPath) -$resolvedGeneratedShimSourcesPath = [System.IO.Path]::GetFullPath($GeneratedShimSourcesPath) -$resolvedShimOutputDirectory = [System.IO.Path]::GetFullPath($ShimOutputPath) -$shimDirectoryBuildPropsPath = Join-Path $resolvedShimProjectsPath 'Directory.Build.props' -$buildScriptPath = Join-Path $PSScriptRoot 'Build-AddIns.ps1' - -function Ensure-NetFrameworkReferenceAssemblies { - [CmdletBinding()] - param( - [Parameter(Mandatory = $true)] - [string]$WorkspaceRoot - ) - - $packageId = 'Microsoft.NETFramework.ReferenceAssemblies.net481' - $packageVersion = '1.0.3' - $packageRoot = Join-Path $WorkspaceRoot '.tmp\netfx-reference-assemblies' - $packageDir = Join-Path $packageRoot "$packageId.$packageVersion" - $packageArchive = Join-Path $packageRoot "$packageId.$packageVersion.nupkg" - $referenceDir = Join-Path $packageDir 'build\.NETFramework\v4.8.1' - $frameworkRoot = Join-Path $packageDir 'build\' - - if (-not (Test-Path -LiteralPath $referenceDir)) { - $null = New-Item -ItemType Directory -Path $packageRoot -Force - - $packageUrl = "https://www.nuget.org/api/v2/package/$packageId/$packageVersion" - Write-Host ("Downloading .NET Framework reference assemblies from {0}" -f $packageUrl) -ForegroundColor Cyan - Invoke-WebRequest -Uri $packageUrl -OutFile $packageArchive - - if (Test-Path -LiteralPath $packageDir) { - Remove-Item -LiteralPath $packageDir -Recurse -Force - } - - Expand-Archive -Path $packageArchive -DestinationPath $packageDir -Force - } - - if (-not (Test-Path -LiteralPath $referenceDir)) { - throw "Reference assembly directory not found after package extraction: $referenceDir" - } - - return [PSCustomObject]@{ - FrameworkPathOverride = $referenceDir - TargetFrameworkRootPath = $frameworkRoot - } -} - -function Import-ShimManifest { - [CmdletBinding()] - param( - [Parameter(Mandatory = $true)] - [string]$Path - ) - - $extension = [System.IO.Path]::GetExtension($Path) - switch ($extension.ToLowerInvariant()) { - '.json' { - return Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json -AsHashtable - } - '.psd1' { - return Import-PowerShellDataFile -Path $Path - } - default { - throw "Unsupported shim manifest format '$extension'. Supported formats are .json and .psd1." - } - } -} - -$shimManifest = Import-ShimManifest -Path $resolvedShimManifestPath -$shimProjects = if ($shimManifest.ContainsKey('Projects')) { - $shimManifest.Projects -} -else { - $null -} - -if (-not $shimProjects -or $shimProjects.Count -eq 0) { - throw "No shim projects were defined in $resolvedShimManifestPath" -} - -if (Test-Path -LiteralPath $resolvedGeneratedShimProjectsPath) { - Remove-Item -LiteralPath $resolvedGeneratedShimProjectsPath -Recurse -Force -} - -if (Test-Path -LiteralPath $resolvedGeneratedShimSourcesPath) { - Remove-Item -LiteralPath $resolvedGeneratedShimSourcesPath -Recurse -Force -} - -$null = New-Item -ItemType Directory -Path $resolvedGeneratedShimProjectsPath -Force -$null = New-Item -ItemType Directory -Path $resolvedGeneratedShimSourcesPath -Force - -foreach ($projectEntry in $shimProjects.GetEnumerator()) { - $projectName = [string]$projectEntry.Key - $projectConfig = $projectEntry.Value - $rootNamespace = if ($projectConfig.RootNamespace) { - [string]$projectConfig.RootNamespace - } - else { - $projectName - } - - $projectLines = [System.Collections.Generic.List[string]]::new() - $projectLines.Add('') - $projectLines.Add((' ' -f $shimDirectoryBuildPropsPath.Replace('&', '&'))) - $projectLines.Add(' ') - $projectLines.Add(" $projectName") - $projectLines.Add(" $rootNamespace") - $projectLines.Add(' ') - - $sourcePaths = [System.Collections.Generic.List[string]]::new() - if ($projectConfig.ContainsKey('SourceFiles')) { - $projectSourceRoot = Join-Path $resolvedGeneratedShimSourcesPath $projectName - $null = New-Item -ItemType Directory -Path $projectSourceRoot -Force - - foreach ($sourceFile in @($projectConfig.SourceFiles)) { - if ($null -eq $sourceFile) { - continue - } - - $sourceRelativePath = [string]$sourceFile.Path - if ([string]::IsNullOrWhiteSpace($sourceRelativePath)) { - continue - } - - $sourceOutputPath = Join-Path $projectSourceRoot $sourceRelativePath - $sourceOutputDirectory = Split-Path -Path $sourceOutputPath -Parent - if (-not [string]::IsNullOrWhiteSpace($sourceOutputDirectory)) { - $null = New-Item -ItemType Directory -Path $sourceOutputDirectory -Force - } - - $sourceContent = if ($sourceFile.ContainsKey('Content')) { - [string]$sourceFile.Content - } - else { - [string]$sourceFile.content - } - - Set-Content -LiteralPath $sourceOutputPath -Value $sourceContent -Encoding utf8 - $null = $sourcePaths.Add($sourceOutputPath) - } - } - elseif ($projectConfig.ContainsKey('Sources')) { - foreach ($sourceEntry in @($projectConfig.Sources)) { - if (-not [string]::IsNullOrWhiteSpace([string]$sourceEntry)) { - $sourcePath = [string]$sourceEntry - if (-not [System.IO.Path]::IsPathRooted($sourcePath)) { - $sourcePath = [System.IO.Path]::GetFullPath((Join-Path $resolvedShimProjectsPath $sourcePath)) - } - - $null = $sourcePaths.Add($sourcePath) - } - } - } - elseif ($projectConfig.ContainsKey('CompileInclude')) { - $compileIncludePath = [string]$projectConfig.CompileInclude - if (-not [System.IO.Path]::IsPathRooted($compileIncludePath)) { - $compileIncludePath = [System.IO.Path]::GetFullPath((Join-Path $resolvedShimProjectsPath $compileIncludePath)) - } - - $null = $sourcePaths.Add($compileIncludePath) - } - - if ($sourcePaths.Count -gt 0) { - $projectLines.Add(' ') - foreach ($sourcePath in $sourcePaths) { - $projectLines.Add((' ' -f $sourcePath.Replace('&', '&'))) - } - $projectLines.Add(' ') - } - - if ($projectConfig.ContainsKey('ProjectReferences') -and @($projectConfig.ProjectReferences).Count -gt 0) { - $projectLines.Add(' ') - foreach ($projectReference in @($projectConfig.ProjectReferences)) { - $projectReferenceName = [string]$projectReference - $projectReferenceDllPath = Join-Path $resolvedShimOutputDirectory "$projectReferenceName.dll" - $projectLines.Add((' ' -f $projectReferenceName.Replace('&', '&'))) - $projectLines.Add((' {0}' -f $projectReferenceDllPath.Replace('&', '&'))) - $projectLines.Add(' false') - $projectLines.Add(' ') - } - $projectLines.Add(' ') - } - - $projectLines.Add('') - - $generatedProjectPath = Join-Path $resolvedGeneratedShimProjectsPath "$projectName.csproj" - $projectLines -join [Environment]::NewLine | Set-Content -LiteralPath $generatedProjectPath -Encoding utf8 -} - -function Add-ShimProjectBuildOrder { - param( - [Parameter(Mandatory = $true)] - [string]$ProjectName, - - [Parameter(Mandatory = $true)] - [hashtable]$ProjectsByName, - - [Parameter(Mandatory = $true)] - [AllowEmptyCollection()] - [System.Collections.Generic.HashSet[string]]$Visiting, - - [Parameter(Mandatory = $true)] - [AllowEmptyCollection()] - [System.Collections.Generic.HashSet[string]]$Visited, - - [Parameter(Mandatory = $true)] - [AllowEmptyCollection()] - [System.Collections.Generic.List[string]]$BuildOrder - ) - - if ($Visited.Contains($ProjectName)) { - return - } - - if (-not $Visiting.Add($ProjectName)) { - throw "Detected circular shim dependency involving '$ProjectName'" - } - - $projectConfig = $ProjectsByName[$ProjectName] - if ($projectConfig -and $projectConfig.ContainsKey('ProjectReferences')) { - foreach ($projectReference in @($projectConfig.ProjectReferences)) { - $projectReferenceName = [string]$projectReference - if (-not $ProjectsByName.ContainsKey($projectReferenceName)) { - throw "Shim project '$ProjectName' references undefined shim project '$projectReferenceName'" - } - - Add-ShimProjectBuildOrder -ProjectName $projectReferenceName -ProjectsByName $ProjectsByName -Visiting $Visiting -Visited $Visited -BuildOrder $BuildOrder - } - } - - $null = $Visiting.Remove($ProjectName) - $null = $Visited.Add($ProjectName) - $BuildOrder.Add($ProjectName) -} - -$orderedProjectNames = [System.Collections.Generic.List[string]]::new() -$visitingProjects = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) -$visitedProjects = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) -foreach ($projectName in $shimProjects.Keys) { - Add-ShimProjectBuildOrder -ProjectName ([string]$projectName) -ProjectsByName $shimProjects -Visiting $visitingProjects -Visited $visitedProjects -BuildOrder $orderedProjectNames -} - -$projects = foreach ($projectName in $orderedProjectNames) { - Get-Item -LiteralPath (Join-Path $resolvedGeneratedShimProjectsPath "$projectName.csproj") -} -if (-not $projects) { - throw "No generated shim projects found under $resolvedGeneratedShimProjectsPath" -} - -foreach ($project in $projects) { - Write-Host ("Building shim assembly: {0}" -f $project.Name) -ForegroundColor Cyan - $shimBuildArgs = @( - 'build' - $project.FullName - '-c' - $Configuration - '-nologo' - ) - - if ($Rebuild) { - $shimBuildArgs += '-t:Rebuild' - } - - dotnet @shimBuildArgs - if ($LASTEXITCODE -ne 0) { - throw ("Shim build failed: {0}" -f $project.FullName) - } -} - -$resolvedShimOutputPath = (Resolve-Path $ShimOutputPath).Path -$previousReferencePath = $env:UNIFIED_ENGINEERING_REFERENCE_PATH -$previousFrameworkPathOverride = $env:FrameworkPathOverride -$previousTargetFrameworkRootPath = $env:TargetFrameworkRootPath -$targetDirectories = foreach ($entry in $Directory) { - foreach ($candidate in ($entry -split ',')) { - $trimmed = $candidate.Trim().Trim([char[]]@("'", '"')) - if (-not [string]::IsNullOrWhiteSpace($trimmed)) { - $trimmed - } - } -} - -try { - $env:UNIFIED_ENGINEERING_REFERENCE_PATH = $resolvedShimOutputPath - - if (-not $IsWindows) { - $netFrameworkReferences = Ensure-NetFrameworkReferenceAssemblies -WorkspaceRoot $repoRoot - $env:FrameworkPathOverride = $netFrameworkReferences.FrameworkPathOverride - $env:TargetFrameworkRootPath = $netFrameworkReferences.TargetFrameworkRootPath - - Write-Host ("Using .NET Framework reference assemblies from: {0}" -f $env:FrameworkPathOverride) -ForegroundColor Yellow - } - - if ($env:TF_BUILD) { - Write-Host ("##vso[task.setvariable variable=UNIFIED_ENGINEERING_REFERENCE_PATH]{0}" -f $resolvedShimOutputPath) - } - - Write-Host ("Using fake UE references from: {0}" -f $resolvedShimOutputPath) -ForegroundColor Yellow - - foreach ($targetDirectory in $targetDirectories) { - Write-Host ("Compiling with fake UE references: {0}" -f $targetDirectory) -ForegroundColor Yellow - & $buildScriptPath -Directory $targetDirectory -Configuration $Configuration -SkipPostBuildEvent -Rebuild:$Rebuild - if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE - } - } - - exit 0 -} -finally { - if ([string]::IsNullOrWhiteSpace($previousReferencePath)) { - Remove-Item Env:UNIFIED_ENGINEERING_REFERENCE_PATH -ErrorAction SilentlyContinue - } - else { - $env:UNIFIED_ENGINEERING_REFERENCE_PATH = $previousReferencePath - } - - if ([string]::IsNullOrWhiteSpace($previousFrameworkPathOverride)) { - Remove-Item Env:FrameworkPathOverride -ErrorAction SilentlyContinue - } - else { - $env:FrameworkPathOverride = $previousFrameworkPathOverride - } - - if ([string]::IsNullOrWhiteSpace($previousTargetFrameworkRootPath)) { - Remove-Item Env:TargetFrameworkRootPath -ErrorAction SilentlyContinue - } - else { - $env:TargetFrameworkRootPath = $previousTargetFrameworkRootPath - } -} \ No newline at end of file diff --git a/shims/Directory.Build.props b/shims/Directory.Build.props deleted file mode 100644 index 8adb7f3..0000000 --- a/shims/Directory.Build.props +++ /dev/null @@ -1,15 +0,0 @@ - - - net48 - latest - disable - disable - true - false - false - 1591 - $(MSBuildThisFileDirectory)artifacts\$(Configuration)\ - false - false - - \ No newline at end of file diff --git a/shims/run-fake-ue-compile.yml b/shims/run-fake-ue-compile.yml new file mode 100644 index 0000000..6245e8e --- /dev/null +++ b/shims/run-fake-ue-compile.yml @@ -0,0 +1,459 @@ +parameters: + - name: Directory + type: string + default: templates,Examples + - name: Configuration + type: string + default: Release + - name: ShimManifestPath + type: string + default: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json + - name: GeneratedRunnerPath + type: string + default: $(Pipeline.Workspace)/Source/.tmp/generated-fake-ue-compile.ps1 + - name: GeneratedShimProjectsPath + type: string + default: $(Pipeline.Workspace)/Source/.tmp/generated-shims + - name: GeneratedShimSourcesPath + type: string + default: $(Pipeline.Workspace)/Source/.tmp/generated-shim-sources + - name: GeneratedShimPropsPath + type: string + default: $(Pipeline.Workspace)/Source/.tmp/generated-shim-props/Directory.Build.props + - name: ShimOutputPath + type: string + default: $(Pipeline.Workspace)/Source/shims/artifacts/Release + - name: Rebuild + type: boolean + default: false + +steps: + - pwsh: | + $generatedRunnerPath = '${{ parameters.GeneratedRunnerPath }}' + $generatedRunnerDirectory = Split-Path -Path $generatedRunnerPath -Parent + if (-not [string]::IsNullOrWhiteSpace($generatedRunnerDirectory)) { + $null = New-Item -ItemType Directory -Path $generatedRunnerDirectory -Force + } + + $runnerScript = @' + #Requires -Version 7.0 + + [CmdletBinding()] + param( + [string[]]$Directory = @('Examples'), + [string]$Configuration = 'Release', + [string]$ShimProjectsPath, + [string]$ShimManifestPath, + [string]$GeneratedShimProjectsPath, + [string]$GeneratedShimSourcesPath, + [string]$GeneratedShimPropsPath, + [string]$ShimOutputPath, + [switch]$Rebuild + ) + + $ErrorActionPreference = 'Stop' + + $repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path + $defaultGeneratedShimManifestPath = Join-Path $repoRoot '.tmp\generated-shim-manifest.json' + $defaultGeneratedShimProjectsPath = Join-Path $repoRoot '.tmp\generated-shims' + $defaultGeneratedShimSourcesPath = Join-Path $repoRoot '.tmp\generated-shim-sources' + $defaultGeneratedShimPropsPath = Join-Path $repoRoot '.tmp\generated-shim-props\Directory.Build.props' + + if (-not $ShimProjectsPath) { + $ShimProjectsPath = Join-Path $repoRoot 'shims' + } + + if (-not $ShimManifestPath) { + if (Test-Path -LiteralPath $defaultGeneratedShimManifestPath) { + $ShimManifestPath = $defaultGeneratedShimManifestPath + } + else { + throw ( + 'Shim manifest path was not provided and no generated manifest was found. ' + + 'Generate .tmp/generated-shim-manifest.json from shims/shim-projects.yml ' + + 'or pass -ShimManifestPath explicitly.' + ) + } + } + + if (-not $GeneratedShimProjectsPath) { + $GeneratedShimProjectsPath = $defaultGeneratedShimProjectsPath + } + + if (-not $GeneratedShimSourcesPath) { + $GeneratedShimSourcesPath = $defaultGeneratedShimSourcesPath + } + + if (-not $GeneratedShimPropsPath) { + $GeneratedShimPropsPath = $defaultGeneratedShimPropsPath + } + + if (-not $ShimOutputPath) { + $ShimOutputPath = Join-Path $ShimProjectsPath "artifacts\$Configuration" + } + + $resolvedShimProjectsPath = (Resolve-Path $ShimProjectsPath).Path + $resolvedShimManifestPath = (Resolve-Path $ShimManifestPath).Path + $resolvedGeneratedShimProjectsPath = [System.IO.Path]::GetFullPath($GeneratedShimProjectsPath) + $resolvedGeneratedShimSourcesPath = [System.IO.Path]::GetFullPath($GeneratedShimSourcesPath) + $resolvedGeneratedShimPropsPath = [System.IO.Path]::GetFullPath($GeneratedShimPropsPath) + $resolvedShimOutputDirectory = [System.IO.Path]::GetFullPath($ShimOutputPath) + $resolvedShimPropsDirectory = Split-Path -Path $resolvedGeneratedShimPropsPath -Parent + $buildScriptPath = Join-Path $repoRoot 'scripts\Build-AddIns.ps1' + + function Ensure-NetFrameworkReferenceAssemblies { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$WorkspaceRoot + ) + + $packageId = 'Microsoft.NETFramework.ReferenceAssemblies.net481' + $packageVersion = '1.0.3' + $packageRoot = Join-Path $WorkspaceRoot '.tmp\netfx-reference-assemblies' + $packageDir = Join-Path $packageRoot "$packageId.$packageVersion" + $packageArchive = Join-Path $packageRoot "$packageId.$packageVersion.nupkg" + $referenceDir = Join-Path $packageDir 'build\.NETFramework\v4.8.1' + $frameworkRoot = Join-Path $packageDir 'build\' + + if (-not (Test-Path -LiteralPath $referenceDir)) { + $null = New-Item -ItemType Directory -Path $packageRoot -Force + + $packageUrl = "https://www.nuget.org/api/v2/package/$packageId/$packageVersion" + Write-Host ("Downloading .NET Framework reference assemblies from {0}" -f $packageUrl) -ForegroundColor Cyan + Invoke-WebRequest -Uri $packageUrl -OutFile $packageArchive + + if (Test-Path -LiteralPath $packageDir) { + Remove-Item -LiteralPath $packageDir -Recurse -Force + } + + Expand-Archive -Path $packageArchive -DestinationPath $packageDir -Force + } + + if (-not (Test-Path -LiteralPath $referenceDir)) { + throw "Reference assembly directory not found after package extraction: $referenceDir" + } + + return [PSCustomObject]@{ + FrameworkPathOverride = $referenceDir + TargetFrameworkRootPath = $frameworkRoot + } + } + + function Import-ShimManifest { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Path + ) + + $extension = [System.IO.Path]::GetExtension($Path) + switch ($extension.ToLowerInvariant()) { + '.json' { + return Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json -AsHashtable + } + '.psd1' { + return Import-PowerShellDataFile -Path $Path + } + default { + throw "Unsupported shim manifest format '$extension'. Supported formats are .json and .psd1." + } + } + } + + $shimManifest = Import-ShimManifest -Path $resolvedShimManifestPath + $shimProjects = if ($shimManifest.ContainsKey('Projects')) { + $shimManifest.Projects + } + else { + $null + } + + if (-not $shimProjects -or $shimProjects.Count -eq 0) { + throw "No shim projects were defined in $resolvedShimManifestPath" + } + + if (Test-Path -LiteralPath $resolvedGeneratedShimProjectsPath) { + Remove-Item -LiteralPath $resolvedGeneratedShimProjectsPath -Recurse -Force + } + + if (Test-Path -LiteralPath $resolvedGeneratedShimSourcesPath) { + Remove-Item -LiteralPath $resolvedGeneratedShimSourcesPath -Recurse -Force + } + + if (Test-Path -LiteralPath $resolvedShimPropsDirectory) { + Remove-Item -LiteralPath $resolvedShimPropsDirectory -Recurse -Force + } + + $null = New-Item -ItemType Directory -Path $resolvedGeneratedShimProjectsPath -Force + $null = New-Item -ItemType Directory -Path $resolvedGeneratedShimSourcesPath -Force + $null = New-Item -ItemType Directory -Path $resolvedShimPropsDirectory -Force + + @' + + + net48 + latest + disable + disable + true + false + false + 1591 + $(MSBuildThisFileDirectory)..\..\shims\artifacts\$(Configuration)\ + false + false + + + '@ | Set-Content -LiteralPath $resolvedGeneratedShimPropsPath -Encoding utf8 + + foreach ($projectEntry in $shimProjects.GetEnumerator()) { + $projectName = [string]$projectEntry.Key + $projectConfig = $projectEntry.Value + $rootNamespace = if ($projectConfig.RootNamespace) { + [string]$projectConfig.RootNamespace + } + else { + $projectName + } + + $projectLines = [System.Collections.Generic.List[string]]::new() + $projectLines.Add('') + $projectLines.Add((' ' -f $resolvedGeneratedShimPropsPath.Replace('&', '&'))) + $projectLines.Add(' ') + $projectLines.Add(" $projectName") + $projectLines.Add(" $rootNamespace") + $projectLines.Add(' ') + + $sourcePaths = [System.Collections.Generic.List[string]]::new() + if ($projectConfig.ContainsKey('SourceFiles')) { + $projectSourceRoot = Join-Path $resolvedGeneratedShimSourcesPath $projectName + $null = New-Item -ItemType Directory -Path $projectSourceRoot -Force + + foreach ($sourceFile in @($projectConfig.SourceFiles)) { + if ($null -eq $sourceFile) { + continue + } + + $sourceRelativePath = [string]$sourceFile.Path + if ([string]::IsNullOrWhiteSpace($sourceRelativePath)) { + continue + } + + $sourceOutputPath = Join-Path $projectSourceRoot $sourceRelativePath + $sourceOutputDirectory = Split-Path -Path $sourceOutputPath -Parent + if (-not [string]::IsNullOrWhiteSpace($sourceOutputDirectory)) { + $null = New-Item -ItemType Directory -Path $sourceOutputDirectory -Force + } + + $sourceContent = if ($sourceFile.ContainsKey('Content')) { + [string]$sourceFile.Content + } + else { + [string]$sourceFile.content + } + + Set-Content -LiteralPath $sourceOutputPath -Value $sourceContent -Encoding utf8 + $null = $sourcePaths.Add($sourceOutputPath) + } + } + + if ($sourcePaths.Count -gt 0) { + $projectLines.Add(' ') + foreach ($sourcePath in $sourcePaths) { + $projectLines.Add((' ' -f $sourcePath.Replace('&', '&'))) + } + $projectLines.Add(' ') + } + + if ($projectConfig.ContainsKey('ProjectReferences') -and @($projectConfig.ProjectReferences).Count -gt 0) { + $projectLines.Add(' ') + foreach ($projectReference in @($projectConfig.ProjectReferences)) { + $projectReferenceName = [string]$projectReference + $projectReferenceDllPath = Join-Path $resolvedShimOutputDirectory "$projectReferenceName.dll" + $projectLines.Add((' ' -f $projectReferenceName.Replace('&', '&'))) + $projectLines.Add((' {0}' -f $projectReferenceDllPath.Replace('&', '&'))) + $projectLines.Add(' false') + $projectLines.Add(' ') + } + $projectLines.Add(' ') + } + + $projectLines.Add('') + + $generatedProjectPath = Join-Path $resolvedGeneratedShimProjectsPath "$projectName.csproj" + $projectLines -join [Environment]::NewLine | Set-Content -LiteralPath $generatedProjectPath -Encoding utf8 + } + + function Add-ShimProjectBuildOrder { + param( + [Parameter(Mandatory = $true)] + [string]$ProjectName, + + [Parameter(Mandatory = $true)] + [hashtable]$ProjectsByName, + + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [System.Collections.Generic.HashSet[string]]$Visiting, + + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [System.Collections.Generic.HashSet[string]]$Visited, + + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [System.Collections.Generic.List[string]]$BuildOrder + ) + + if ($Visited.Contains($ProjectName)) { + return + } + + if (-not $Visiting.Add($ProjectName)) { + throw "Detected circular shim dependency involving '$ProjectName'" + } + + $projectConfig = $ProjectsByName[$ProjectName] + if ($projectConfig -and $projectConfig.ContainsKey('ProjectReferences')) { + foreach ($projectReference in @($projectConfig.ProjectReferences)) { + $projectReferenceName = [string]$projectReference + if (-not $ProjectsByName.ContainsKey($projectReferenceName)) { + throw "Shim project '$ProjectName' references undefined shim project '$projectReferenceName'" + } + + Add-ShimProjectBuildOrder -ProjectName $projectReferenceName -ProjectsByName $ProjectsByName -Visiting $Visiting -Visited $Visited -BuildOrder $BuildOrder + } + } + + $null = $Visiting.Remove($ProjectName) + $null = $Visited.Add($ProjectName) + $BuildOrder.Add($ProjectName) + } + + $orderedProjectNames = [System.Collections.Generic.List[string]]::new() + $visitingProjects = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) + $visitedProjects = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) + foreach ($projectName in $shimProjects.Keys) { + Add-ShimProjectBuildOrder -ProjectName ([string]$projectName) -ProjectsByName $shimProjects -Visiting $visitingProjects -Visited $visitedProjects -BuildOrder $orderedProjectNames + } + + $projects = foreach ($projectName in $orderedProjectNames) { + Get-Item -LiteralPath (Join-Path $resolvedGeneratedShimProjectsPath "$projectName.csproj") + } + if (-not $projects) { + throw "No generated shim projects found under $resolvedGeneratedShimProjectsPath" + } + + foreach ($project in $projects) { + Write-Host ("Building shim assembly: {0}" -f $project.Name) -ForegroundColor Cyan + $shimBuildArgs = @( + 'build' + $project.FullName + '-c' + $Configuration + '-nologo' + ) + + if ($Rebuild) { + $shimBuildArgs += '-t:Rebuild' + } + + dotnet @shimBuildArgs + if ($LASTEXITCODE -ne 0) { + throw ("Shim build failed: {0}" -f $project.FullName) + } + } + + $resolvedShimOutputPath = (Resolve-Path $ShimOutputPath).Path + $previousReferencePath = $env:UNIFIED_ENGINEERING_REFERENCE_PATH + $previousFrameworkPathOverride = $env:FrameworkPathOverride + $previousTargetFrameworkRootPath = $env:TargetFrameworkRootPath + $targetDirectories = foreach ($entry in $Directory) { + foreach ($candidate in ($entry -split ',')) { + $trimmed = $candidate.Trim().Trim([char[]]@("'", '"')) + if (-not [string]::IsNullOrWhiteSpace($trimmed)) { + $trimmed + } + } + } + + try { + $env:UNIFIED_ENGINEERING_REFERENCE_PATH = $resolvedShimOutputPath + + if (-not $IsWindows) { + $netFrameworkReferences = Ensure-NetFrameworkReferenceAssemblies -WorkspaceRoot $repoRoot + $env:FrameworkPathOverride = $netFrameworkReferences.FrameworkPathOverride + $env:TargetFrameworkRootPath = $netFrameworkReferences.TargetFrameworkRootPath + + Write-Host ("Using .NET Framework reference assemblies from: {0}" -f $env:FrameworkPathOverride) -ForegroundColor Yellow + } + + if ($env:TF_BUILD) { + Write-Host ("##vso[task.setvariable variable=UNIFIED_ENGINEERING_REFERENCE_PATH]{0}" -f $resolvedShimOutputPath) + } + + Write-Host ("Using fake UE references from: {0}" -f $resolvedShimOutputPath) -ForegroundColor Yellow + + foreach ($targetDirectory in $targetDirectories) { + Write-Host ("Compiling with fake UE references: {0}" -f $targetDirectory) -ForegroundColor Yellow + & $buildScriptPath -Directory $targetDirectory -Configuration $Configuration -SkipPostBuildEvent -Rebuild:$Rebuild + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + } + + exit 0 + } + finally { + if ([string]::IsNullOrWhiteSpace($previousReferencePath)) { + Remove-Item Env:UNIFIED_ENGINEERING_REFERENCE_PATH -ErrorAction SilentlyContinue + } + else { + $env:UNIFIED_ENGINEERING_REFERENCE_PATH = $previousReferencePath + } + + if ([string]::IsNullOrWhiteSpace($previousFrameworkPathOverride)) { + Remove-Item Env:FrameworkPathOverride -ErrorAction SilentlyContinue + } + else { + $env:FrameworkPathOverride = $previousFrameworkPathOverride + } + + if ([string]::IsNullOrWhiteSpace($previousTargetFrameworkRootPath)) { + Remove-Item Env:TargetFrameworkRootPath -ErrorAction SilentlyContinue + } + else { + $env:TargetFrameworkRootPath = $previousTargetFrameworkRootPath + } + } + '@ + + Set-Content -LiteralPath $generatedRunnerPath -Value $runnerScript -Encoding utf8 + + $runnerArgs = @( + '-NoProfile' + '-File' + $generatedRunnerPath + '-Directory' + '${{ parameters.Directory }}' + '-Configuration' + '${{ parameters.Configuration }}' + '-ShimManifestPath' + '${{ parameters.ShimManifestPath }}' + '-GeneratedShimProjectsPath' + '${{ parameters.GeneratedShimProjectsPath }}' + '-GeneratedShimSourcesPath' + '${{ parameters.GeneratedShimSourcesPath }}' + '-GeneratedShimPropsPath' + '${{ parameters.GeneratedShimPropsPath }}' + '-ShimOutputPath' + '${{ parameters.ShimOutputPath }}' + ) + + if ('${{ parameters.Rebuild }}' -eq 'True') { + $runnerArgs += '-Rebuild' + } + + & pwsh @runnerArgs + displayName: Generate and run fake UE compile helper From dde85e8681396b3f3fb9d33db6c10787efa271dc Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 02:34:57 +0100 Subject: [PATCH 080/102] Fix fake compile template PowerShell arrays --- shims/run-fake-ue-compile.yml | 42 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/shims/run-fake-ue-compile.yml b/shims/run-fake-ue-compile.yml index 6245e8e..80a9c03 100644 --- a/shims/run-fake-ue-compile.yml +++ b/shims/run-fake-ue-compile.yml @@ -348,10 +348,10 @@ steps: foreach ($project in $projects) { Write-Host ("Building shim assembly: {0}" -f $project.Name) -ForegroundColor Cyan $shimBuildArgs = @( - 'build' - $project.FullName - '-c' - $Configuration + 'build', + $project.FullName, + '-c', + $Configuration, '-nologo' ) @@ -432,23 +432,23 @@ steps: Set-Content -LiteralPath $generatedRunnerPath -Value $runnerScript -Encoding utf8 $runnerArgs = @( - '-NoProfile' - '-File' - $generatedRunnerPath - '-Directory' - '${{ parameters.Directory }}' - '-Configuration' - '${{ parameters.Configuration }}' - '-ShimManifestPath' - '${{ parameters.ShimManifestPath }}' - '-GeneratedShimProjectsPath' - '${{ parameters.GeneratedShimProjectsPath }}' - '-GeneratedShimSourcesPath' - '${{ parameters.GeneratedShimSourcesPath }}' - '-GeneratedShimPropsPath' - '${{ parameters.GeneratedShimPropsPath }}' - '-ShimOutputPath' - '${{ parameters.ShimOutputPath }}' + '-NoProfile', + '-File', + $generatedRunnerPath, + '-Directory', + '${{ parameters.Directory }}', + '-Configuration', + '${{ parameters.Configuration }}', + '-ShimManifestPath', + '${{ parameters.ShimManifestPath }}', + '-GeneratedShimProjectsPath', + '${{ parameters.GeneratedShimProjectsPath }}', + '-GeneratedShimSourcesPath', + '${{ parameters.GeneratedShimSourcesPath }}', + '-GeneratedShimPropsPath', + '${{ parameters.GeneratedShimPropsPath }}', + '-ShimOutputPath', + '${{ parameters.ShimOutputPath }}' ) if ('${{ parameters.Rebuild }}' -eq 'True') { From 33fac60392499391c0bc68e4d904367ab7649a8f Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 02:49:36 +0100 Subject: [PATCH 081/102] Fix fake compile runner argument syntax --- shims/run-fake-ue-compile.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/shims/run-fake-ue-compile.yml b/shims/run-fake-ue-compile.yml index 80a9c03..e4774d1 100644 --- a/shims/run-fake-ue-compile.yml +++ b/shims/run-fake-ue-compile.yml @@ -347,13 +347,7 @@ steps: foreach ($project in $projects) { Write-Host ("Building shim assembly: {0}" -f $project.Name) -ForegroundColor Cyan - $shimBuildArgs = @( - 'build', - $project.FullName, - '-c', - $Configuration, - '-nologo' - ) + $shimBuildArgs = 'build', $project.FullName, '-c', $Configuration, '-nologo' if ($Rebuild) { $shimBuildArgs += '-t:Rebuild' @@ -431,7 +425,7 @@ steps: Set-Content -LiteralPath $generatedRunnerPath -Value $runnerScript -Encoding utf8 - $runnerArgs = @( + $runnerArgs = '-NoProfile', '-File', $generatedRunnerPath, @@ -449,7 +443,6 @@ steps: '${{ parameters.GeneratedShimPropsPath }}', '-ShimOutputPath', '${{ parameters.ShimOutputPath }}' - ) if ('${{ parameters.Rebuild }}' -eq 'True') { $runnerArgs += '-Rebuild' From 810862372a7bf02e48ec6461c4fc7043e46db32b Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 02:57:06 +0100 Subject: [PATCH 082/102] Fix fake compile runner args array literal --- shims/run-fake-ue-compile.yml | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/shims/run-fake-ue-compile.yml b/shims/run-fake-ue-compile.yml index e4774d1..d8ab4a5 100644 --- a/shims/run-fake-ue-compile.yml +++ b/shims/run-fake-ue-compile.yml @@ -425,24 +425,25 @@ steps: Set-Content -LiteralPath $generatedRunnerPath -Value $runnerScript -Encoding utf8 - $runnerArgs = - '-NoProfile', - '-File', - $generatedRunnerPath, - '-Directory', - '${{ parameters.Directory }}', - '-Configuration', - '${{ parameters.Configuration }}', - '-ShimManifestPath', - '${{ parameters.ShimManifestPath }}', - '-GeneratedShimProjectsPath', - '${{ parameters.GeneratedShimProjectsPath }}', - '-GeneratedShimSourcesPath', - '${{ parameters.GeneratedShimSourcesPath }}', - '-GeneratedShimPropsPath', - '${{ parameters.GeneratedShimPropsPath }}', - '-ShimOutputPath', + $runnerArgs = @( + '-NoProfile' + '-File' + $generatedRunnerPath + '-Directory' + '${{ parameters.Directory }}' + '-Configuration' + '${{ parameters.Configuration }}' + '-ShimManifestPath' + '${{ parameters.ShimManifestPath }}' + '-GeneratedShimProjectsPath' + '${{ parameters.GeneratedShimProjectsPath }}' + '-GeneratedShimSourcesPath' + '${{ parameters.GeneratedShimSourcesPath }}' + '-GeneratedShimPropsPath' + '${{ parameters.GeneratedShimPropsPath }}' + '-ShimOutputPath' '${{ parameters.ShimOutputPath }}' + ) if ('${{ parameters.Rebuild }}' -eq 'True') { $runnerArgs += '-Rebuild' From 6d417d0b1d69089491924b51a08c985e0ff9435b Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 05:02:05 +0100 Subject: [PATCH 083/102] Inline fake compile runner invocation --- shims/run-fake-ue-compile.yml | 46 ++++++++++++++++------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/shims/run-fake-ue-compile.yml b/shims/run-fake-ue-compile.yml index d8ab4a5..9abe9b9 100644 --- a/shims/run-fake-ue-compile.yml +++ b/shims/run-fake-ue-compile.yml @@ -425,29 +425,25 @@ steps: Set-Content -LiteralPath $generatedRunnerPath -Value $runnerScript -Encoding utf8 - $runnerArgs = @( - '-NoProfile' - '-File' - $generatedRunnerPath - '-Directory' - '${{ parameters.Directory }}' - '-Configuration' - '${{ parameters.Configuration }}' - '-ShimManifestPath' - '${{ parameters.ShimManifestPath }}' - '-GeneratedShimProjectsPath' - '${{ parameters.GeneratedShimProjectsPath }}' - '-GeneratedShimSourcesPath' - '${{ parameters.GeneratedShimSourcesPath }}' - '-GeneratedShimPropsPath' - '${{ parameters.GeneratedShimPropsPath }}' - '-ShimOutputPath' - '${{ parameters.ShimOutputPath }}' - ) - - if ('${{ parameters.Rebuild }}' -eq 'True') { - $runnerArgs += '-Rebuild' - } - - & pwsh @runnerArgs + if ('${{ parameters.Rebuild }}' -eq 'True') { + & pwsh -NoProfile -File $generatedRunnerPath ` + -Directory '${{ parameters.Directory }}' ` + -Configuration '${{ parameters.Configuration }}' ` + -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` + -GeneratedShimProjectsPath '${{ parameters.GeneratedShimProjectsPath }}' ` + -GeneratedShimSourcesPath '${{ parameters.GeneratedShimSourcesPath }}' ` + -GeneratedShimPropsPath '${{ parameters.GeneratedShimPropsPath }}' ` + -ShimOutputPath '${{ parameters.ShimOutputPath }}' ` + -Rebuild + } + else { + & pwsh -NoProfile -File $generatedRunnerPath ` + -Directory '${{ parameters.Directory }}' ` + -Configuration '${{ parameters.Configuration }}' ` + -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` + -GeneratedShimProjectsPath '${{ parameters.GeneratedShimProjectsPath }}' ` + -GeneratedShimSourcesPath '${{ parameters.GeneratedShimSourcesPath }}' ` + -GeneratedShimPropsPath '${{ parameters.GeneratedShimPropsPath }}' ` + -ShimOutputPath '${{ parameters.ShimOutputPath }}' + } displayName: Generate and run fake UE compile helper From 5faf2ff8d80ba3d2023802a2ce66c4d69f5be5aa Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 11:17:42 +0100 Subject: [PATCH 084/102] Simplify fake compile rebuild args --- shims/run-fake-ue-compile.yml | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/shims/run-fake-ue-compile.yml b/shims/run-fake-ue-compile.yml index 9abe9b9..c08caf5 100644 --- a/shims/run-fake-ue-compile.yml +++ b/shims/run-fake-ue-compile.yml @@ -423,27 +423,20 @@ steps: } '@ - Set-Content -LiteralPath $generatedRunnerPath -Value $runnerScript -Encoding utf8 + Set-Content -LiteralPath $generatedRunnerPath -Value $runnerScript -Encoding utf8; + $rebuildArgs = @() if ('${{ parameters.Rebuild }}' -eq 'True') { - & pwsh -NoProfile -File $generatedRunnerPath ` - -Directory '${{ parameters.Directory }}' ` - -Configuration '${{ parameters.Configuration }}' ` - -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` - -GeneratedShimProjectsPath '${{ parameters.GeneratedShimProjectsPath }}' ` - -GeneratedShimSourcesPath '${{ parameters.GeneratedShimSourcesPath }}' ` - -GeneratedShimPropsPath '${{ parameters.GeneratedShimPropsPath }}' ` - -ShimOutputPath '${{ parameters.ShimOutputPath }}' ` - -Rebuild - } - else { - & pwsh -NoProfile -File $generatedRunnerPath ` - -Directory '${{ parameters.Directory }}' ` - -Configuration '${{ parameters.Configuration }}' ` - -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` - -GeneratedShimProjectsPath '${{ parameters.GeneratedShimProjectsPath }}' ` - -GeneratedShimSourcesPath '${{ parameters.GeneratedShimSourcesPath }}' ` - -GeneratedShimPropsPath '${{ parameters.GeneratedShimPropsPath }}' ` - -ShimOutputPath '${{ parameters.ShimOutputPath }}' + $rebuildArgs = @('-Rebuild') } + + & pwsh -NoProfile -File $generatedRunnerPath ` + -Directory '${{ parameters.Directory }}' ` + -Configuration '${{ parameters.Configuration }}' ` + -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` + -GeneratedShimProjectsPath '${{ parameters.GeneratedShimProjectsPath }}' ` + -GeneratedShimSourcesPath '${{ parameters.GeneratedShimSourcesPath }}' ` + -GeneratedShimPropsPath '${{ parameters.GeneratedShimPropsPath }}' ` + -ShimOutputPath '${{ parameters.ShimOutputPath }}' ` + @rebuildArgs displayName: Generate and run fake UE compile helper From f2c949a29ae2fa3876557a14f10080be0d3ae69a Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 12:42:20 +0100 Subject: [PATCH 085/102] Split fake compile generation and execution --- shims/run-fake-ue-compile.yml | 44 +++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/shims/run-fake-ue-compile.yml b/shims/run-fake-ue-compile.yml index c08caf5..5698b77 100644 --- a/shims/run-fake-ue-compile.yml +++ b/shims/run-fake-ue-compile.yml @@ -423,20 +423,30 @@ steps: } '@ - Set-Content -LiteralPath $generatedRunnerPath -Value $runnerScript -Encoding utf8; - - $rebuildArgs = @() - if ('${{ parameters.Rebuild }}' -eq 'True') { - $rebuildArgs = @('-Rebuild') - } - - & pwsh -NoProfile -File $generatedRunnerPath ` - -Directory '${{ parameters.Directory }}' ` - -Configuration '${{ parameters.Configuration }}' ` - -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` - -GeneratedShimProjectsPath '${{ parameters.GeneratedShimProjectsPath }}' ` - -GeneratedShimSourcesPath '${{ parameters.GeneratedShimSourcesPath }}' ` - -GeneratedShimPropsPath '${{ parameters.GeneratedShimPropsPath }}' ` - -ShimOutputPath '${{ parameters.ShimOutputPath }}' ` - @rebuildArgs - displayName: Generate and run fake UE compile helper + Set-Content -LiteralPath $generatedRunnerPath -Value $runnerScript -Encoding utf8 + displayName: Generate fake UE compile helper + + - ${{ if ne(parameters.Rebuild, true) }}: + - pwsh: | + & pwsh -NoProfile -File '${{ parameters.GeneratedRunnerPath }}' ` + -Directory '${{ parameters.Directory }}' ` + -Configuration '${{ parameters.Configuration }}' ` + -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` + -GeneratedShimProjectsPath '${{ parameters.GeneratedShimProjectsPath }}' ` + -GeneratedShimSourcesPath '${{ parameters.GeneratedShimSourcesPath }}' ` + -GeneratedShimPropsPath '${{ parameters.GeneratedShimPropsPath }}' ` + -ShimOutputPath '${{ parameters.ShimOutputPath }}' + displayName: Run fake UE compile helper + + - ${{ if eq(parameters.Rebuild, true) }}: + - pwsh: | + & pwsh -NoProfile -File '${{ parameters.GeneratedRunnerPath }}' ` + -Directory '${{ parameters.Directory }}' ` + -Configuration '${{ parameters.Configuration }}' ` + -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` + -GeneratedShimProjectsPath '${{ parameters.GeneratedShimProjectsPath }}' ` + -GeneratedShimSourcesPath '${{ parameters.GeneratedShimSourcesPath }}' ` + -GeneratedShimPropsPath '${{ parameters.GeneratedShimPropsPath }}' ` + -ShimOutputPath '${{ parameters.ShimOutputPath }}' ` + -Rebuild + displayName: Run fake UE compile helper (Rebuild) From f4132f3f82c13b6f301d9545a4458cb55fbd9469 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 13:00:11 +0100 Subject: [PATCH 086/102] Fix fake compile YAML step indentation --- shims/run-fake-ue-compile.yml | 54 +++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/shims/run-fake-ue-compile.yml b/shims/run-fake-ue-compile.yml index 5698b77..b2e6ba1 100644 --- a/shims/run-fake-ue-compile.yml +++ b/shims/run-fake-ue-compile.yml @@ -423,30 +423,30 @@ steps: } '@ - Set-Content -LiteralPath $generatedRunnerPath -Value $runnerScript -Encoding utf8 - displayName: Generate fake UE compile helper - - - ${{ if ne(parameters.Rebuild, true) }}: - - pwsh: | - & pwsh -NoProfile -File '${{ parameters.GeneratedRunnerPath }}' ` - -Directory '${{ parameters.Directory }}' ` - -Configuration '${{ parameters.Configuration }}' ` - -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` - -GeneratedShimProjectsPath '${{ parameters.GeneratedShimProjectsPath }}' ` - -GeneratedShimSourcesPath '${{ parameters.GeneratedShimSourcesPath }}' ` - -GeneratedShimPropsPath '${{ parameters.GeneratedShimPropsPath }}' ` - -ShimOutputPath '${{ parameters.ShimOutputPath }}' - displayName: Run fake UE compile helper - - - ${{ if eq(parameters.Rebuild, true) }}: - - pwsh: | - & pwsh -NoProfile -File '${{ parameters.GeneratedRunnerPath }}' ` - -Directory '${{ parameters.Directory }}' ` - -Configuration '${{ parameters.Configuration }}' ` - -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` - -GeneratedShimProjectsPath '${{ parameters.GeneratedShimProjectsPath }}' ` - -GeneratedShimSourcesPath '${{ parameters.GeneratedShimSourcesPath }}' ` - -GeneratedShimPropsPath '${{ parameters.GeneratedShimPropsPath }}' ` - -ShimOutputPath '${{ parameters.ShimOutputPath }}' ` - -Rebuild - displayName: Run fake UE compile helper (Rebuild) + Set-Content -LiteralPath $generatedRunnerPath -Value $runnerScript -Encoding utf8 + displayName: Generate fake UE compile helper + + - ${{ if ne(parameters.Rebuild, true) }}: + - pwsh: | + & pwsh -NoProfile -File '${{ parameters.GeneratedRunnerPath }}' ` + -Directory '${{ parameters.Directory }}' ` + -Configuration '${{ parameters.Configuration }}' ` + -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` + -GeneratedShimProjectsPath '${{ parameters.GeneratedShimProjectsPath }}' ` + -GeneratedShimSourcesPath '${{ parameters.GeneratedShimSourcesPath }}' ` + -GeneratedShimPropsPath '${{ parameters.GeneratedShimPropsPath }}' ` + -ShimOutputPath '${{ parameters.ShimOutputPath }}' + displayName: Run fake UE compile helper + + - ${{ if eq(parameters.Rebuild, true) }}: + - pwsh: | + & pwsh -NoProfile -File '${{ parameters.GeneratedRunnerPath }}' ` + -Directory '${{ parameters.Directory }}' ` + -Configuration '${{ parameters.Configuration }}' ` + -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` + -GeneratedShimProjectsPath '${{ parameters.GeneratedShimProjectsPath }}' ` + -GeneratedShimSourcesPath '${{ parameters.GeneratedShimSourcesPath }}' ` + -GeneratedShimPropsPath '${{ parameters.GeneratedShimPropsPath }}' ` + -ShimOutputPath '${{ parameters.ShimOutputPath }}' ` + -Rebuild + displayName: Run fake UE compile helper (Rebuild) From 2133aa59064d83403fd7f818485f542ca7defe71 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:09:41 +0100 Subject: [PATCH 087/102] Fix nested fake compile here-string parsing --- shims/run-fake-ue-compile.yml | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/shims/run-fake-ue-compile.yml b/shims/run-fake-ue-compile.yml index b2e6ba1..f7d7ac1 100644 --- a/shims/run-fake-ue-compile.yml +++ b/shims/run-fake-ue-compile.yml @@ -189,23 +189,23 @@ steps: $null = New-Item -ItemType Directory -Path $resolvedGeneratedShimSourcesPath -Force $null = New-Item -ItemType Directory -Path $resolvedShimPropsDirectory -Force - @' - - - net48 - latest - disable - disable - true - false - false - 1591 - $(MSBuildThisFileDirectory)..\..\shims\artifacts\$(Configuration)\ - false - false - - - '@ | Set-Content -LiteralPath $resolvedGeneratedShimPropsPath -Encoding utf8 + @( + '', + ' ', + ' net48', + ' latest', + ' disable', + ' disable', + ' true', + ' false', + ' false', + ' 1591', + ' $(MSBuildThisFileDirectory)..\..\shims\artifacts\$(Configuration)\', + ' false', + ' false', + ' ', + '' + ) -join [Environment]::NewLine | Set-Content -LiteralPath $resolvedGeneratedShimPropsPath -Encoding utf8 foreach ($projectEntry in $shimProjects.GetEnumerator()) { $projectName = [string]$projectEntry.Key From 4ec8a696ec93c2a08dbfe7409ef0026d81e3d465 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 17:29:06 +0100 Subject: [PATCH 088/102] Fix-direct-fake-compile-helper --- shims/run-fake-ue-compile.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shims/run-fake-ue-compile.yml b/shims/run-fake-ue-compile.yml index f7d7ac1..d3ff76a 100644 --- a/shims/run-fake-ue-compile.yml +++ b/shims/run-fake-ue-compile.yml @@ -428,7 +428,7 @@ steps: - ${{ if ne(parameters.Rebuild, true) }}: - pwsh: | - & pwsh -NoProfile -File '${{ parameters.GeneratedRunnerPath }}' ` + & '${{ parameters.GeneratedRunnerPath }}' ` -Directory '${{ parameters.Directory }}' ` -Configuration '${{ parameters.Configuration }}' ` -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` @@ -440,7 +440,7 @@ steps: - ${{ if eq(parameters.Rebuild, true) }}: - pwsh: | - & pwsh -NoProfile -File '${{ parameters.GeneratedRunnerPath }}' ` + & '${{ parameters.GeneratedRunnerPath }}' ` -Directory '${{ parameters.Directory }}' ` -Configuration '${{ parameters.Configuration }}' ` -ShimManifestPath '${{ parameters.ShimManifestPath }}' ` From ca9e5c3ba1456d25ab3e77099339e9d42800306e Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:43:30 +0100 Subject: [PATCH 089/102] Update Azure pipeline to use Dabacon Products resources and pools --- azure-pipelines.yml | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 476a7b3..1fabd35 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -3,11 +3,11 @@ # This pipeline is intended to be connected to GitHub through the Azure Pipelines GitHub App # so its checks surface back into GitHub pull requests targeting main. # -# The hosted test stage can run immediately on the Technology stateful hosted pools. +# The hosted test stage can run immediately on the Dabacon Products stateful hosted pools. # The security stage remains opt-in, but now uses the hosted-compatible fake-compile flow -# on the Technology stateful hosted Ubuntu pool instead of generic vmImage selection. -# Templates is consumed from Technology/Templates. -# The Technology project still needs: +# on the Dabacon Products stateful hosted Ubuntu pool instead of generic vmImage selection. +# Templates is consumed from Dabacon Products/Templates. +# The Dabacon Products project still needs: # - a self-hosted Windows pool with UE installed and MSBuild available # - service connections named `Coverity on Polaris` and `BlackDuckScanner` @@ -15,8 +15,8 @@ resources: repositories: - repository: Templates type: git - name: Technology/Templates - ref: refs/heads/feature/uecf-blackduck-pool-overrides + name: Dabacon Products/Templates + ref: refs/heads/develop - repository: antiMalwareTemplate type: git name: Architecture/Architecture @@ -60,7 +60,7 @@ stages: - job: Source displayName: Publish Source pool: - name: Technology-Microsoft-Hosted-Ubuntu + name: Dabacon-Products-Microsoft-Hosted-Ubuntu demands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage @@ -163,7 +163,7 @@ stages: displayName: Build With Shims timeoutInMinutes: "120" pool: - name: Technology-Microsoft-Hosted-Ubuntu + name: Dabacon-Products-Microsoft-Hosted-Ubuntu demands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage @@ -215,7 +215,7 @@ stages: - job: Pester displayName: Pester pool: - name: Technology-Microsoft-Hosted-Ubuntu + name: Dabacon-Products-Microsoft-Hosted-Ubuntu demands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage @@ -269,7 +269,7 @@ stages: displayName: Coverity Scan timeoutInMinutes: "180" pool: - name: Technology-Microsoft-Hosted-Ubuntu + name: Dabacon-Products-Microsoft-Hosted-Ubuntu demands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage @@ -491,7 +491,7 @@ stages: displayName: AntiMalware Scan timeoutInMinutes: "60" pool: - name: Technology-Microsoft-Hosted-Windows + name: Dabacon-Products-Microsoft-Hosted-Windows demands: - ImageOverride -equals windows-latest workspace: @@ -535,7 +535,7 @@ stages: - job: CodeAnalysisLogs displayName: CodeAnalysisLogs pool: - name: Technology-Microsoft-Hosted-Ubuntu + name: Dabacon-Products-Microsoft-Hosted-Ubuntu demands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage @@ -607,7 +607,7 @@ stages: displayName: BlackDuck Initialise timeoutInMinutes: "10" pool: - name: Technology-Microsoft-Hosted-Ubuntu + name: Dabacon-Products-Microsoft-Hosted-Ubuntu demands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage @@ -718,7 +718,7 @@ stages: condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true')) timeoutInMinutes: "10" pool: - name: Technology-Microsoft-Hosted-Ubuntu + name: Dabacon-Products-Microsoft-Hosted-Ubuntu demands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage @@ -782,7 +782,7 @@ stages: condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true')) timeoutInMinutes: "120" pool: - name: Technology-Microsoft-Hosted-Windows + name: Dabacon-Products-Microsoft-Hosted-Windows demands: - ImageOverride -equals windows-latest container: mcr.microsoft.com/dotnet/sdk:9.0-windowsservercore-ltsc2022 @@ -904,7 +904,7 @@ stages: displayName: BlackDuck Initialise timeoutInMinutes: "10" pool: - name: Technology-Microsoft-Hosted-Ubuntu + name: Dabacon-Products-Microsoft-Hosted-Ubuntu demands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage @@ -1009,7 +1009,7 @@ stages: condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true')) timeoutInMinutes: "10" pool: - name: Technology-Microsoft-Hosted-Ubuntu + name: Dabacon-Products-Microsoft-Hosted-Ubuntu demands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage @@ -1061,7 +1061,7 @@ stages: condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true'), ${{ eq(parameters.EnableUbuntuBlackDuckComparison, 'true') }}) timeoutInMinutes: "120" pool: - name: Technology-Microsoft-Hosted-Ubuntu + name: Dabacon-Products-Microsoft-Hosted-Ubuntu demands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage @@ -1260,11 +1260,11 @@ stages: parameters: BuildArtifact: Build.Release BlackDuckGroup: DabaconGroup - UbuntuPoolName: Technology-Microsoft-Hosted-Ubuntu + UbuntuPoolName: Dabacon-Products-Microsoft-Hosted-Ubuntu UbuntuPoolDemands: - ImageOverride -equals ubuntu-latest - WorkFolder -equals /mnt/storage/sdc/storage - WindowsPoolName: Technology-Microsoft-Hosted-Windows + WindowsPoolName: Dabacon-Products-Microsoft-Hosted-Windows WindowsPoolDemands: - ImageOverride -equals windows-latest IsIPR: false From ded5fbb416f98d03b8bc019e8be2ab9e6dfd1dd4 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Tue, 21 Apr 2026 19:28:00 +0100 Subject: [PATCH 090/102] Switch security stages to shared templates --- azure-pipelines.yml | 909 +------------------------------------------- 1 file changed, 11 insertions(+), 898 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1fabd35..c865817 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -16,7 +16,7 @@ resources: - repository: Templates type: git name: Dabacon Products/Templates - ref: refs/heads/develop + ref: refs/heads/feature/uecf-security-template-offload - repository: antiMalwareTemplate type: git name: Architecture/Architecture @@ -26,12 +26,6 @@ parameters: - name: EnableSecurityValidation type: string default: "true" - - name: EnableUbuntuBlackDuckComparison - type: string - default: "false" - - name: EnableComponentTemplateBlackDuckComparison - type: string - default: "false" variables: - template: variables.yml@Templates @@ -260,225 +254,12 @@ stages: displayName: Run tests workingDirectory: $(Pipeline.Workspace)/Source - - stage: Coverity - displayName: Coverity - dependsOn: InitialiseSource - condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) - jobs: - - job: CoverityScan - displayName: Coverity Scan - timeoutInMinutes: "180" - pool: - name: Dabacon-Products-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/dotnet/sdk:9.0 - workspace: - clean: all - steps: - - template: steps/retrieve-source.yml@Templates - parameters: - IsIPR: false - ArtifactName: Source - DownloadPath: $(Pipeline.Workspace)/Source - - - download: current - artifact: Templates - displayName: Download Templates artifact - patterns: "**" - - - pwsh: | - dotnet --list-sdks - displayName: List globally installed .NET SDKs - - - task: UseDotNet@2 - displayName: Ensure .NET 9 SDK for Linux - retryCountOnTaskFailure: 3 - inputs: - packageType: sdk - version: 9.0.x - - - pwsh: | - dotnet --info - displayName: Show .NET SDK info - - - template: shims/shim-projects.yml - parameters: - OutputPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json - - - template: shims/run-fake-ue-compile.yml - parameters: - Directory: templates,Examples - Configuration: Release - ShimManifestPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json - GeneratedRunnerPath: $(Pipeline.Workspace)/Source/.tmp/generated-fake-ue-compile.ps1 - GeneratedShimProjectsPath: $(Pipeline.Workspace)/Source/.tmp/generated-shims - GeneratedShimSourcesPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-sources - GeneratedShimPropsPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-props/Directory.Build.props - ShimOutputPath: $(Pipeline.Workspace)/Source/shims/artifacts/Release - - - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' - $paths = @{ - HasShimSources = Test-Path -LiteralPath (Join-Path $repoRoot 'shims') - HasShimManifest = Test-Path -LiteralPath (Join-Path $repoRoot 'shims/shim-projects.yml') - HasGeneratedShimManifest = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shim-manifest.json') - HasGeneratedShimRunner = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-fake-ue-compile.ps1') - HasGeneratedShimSources = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shim-sources') - HasGeneratedShimProps = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shim-props/Directory.Build.props') - HasGeneratedShimProjects = Test-Path -LiteralPath (Join-Path $repoRoot '.tmp/generated-shims') - HasShimAssemblies = Test-Path -LiteralPath (Join-Path $repoRoot 'shims/artifacts/Release') - } - - foreach ($entry in $paths.GetEnumerator()) { - $value = if ($entry.Value) { 'true' } else { 'false' } - Write-Host "##vso[task.setvariable variable=$($entry.Key)]$value" - Write-Host "$($entry.Key): $value" - } - displayName: Detect shim artifacts - condition: always() - - - publish: $(Pipeline.Workspace)/Source/shims - artifact: ShimSources - displayName: Publish shim sources - condition: and(always(), eq(variables.HasShimSources, 'true')) - - - publish: $(Pipeline.Workspace)/Source/shims/shim-projects.yml - artifact: ShimManifest - displayName: Publish shim manifest - condition: and(always(), eq(variables.HasShimManifest, 'true')) - - - publish: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json - artifact: GeneratedShimManifest - displayName: Publish generated shim manifest - condition: and(always(), eq(variables.HasGeneratedShimManifest, 'true')) - - - publish: $(Pipeline.Workspace)/Source/.tmp/generated-fake-ue-compile.ps1 - artifact: GeneratedShimRunner - displayName: Publish generated fake compile runner - condition: and(always(), eq(variables.HasGeneratedShimRunner, 'true')) - - - publish: $(Pipeline.Workspace)/Source/.tmp/generated-shim-sources - artifact: GeneratedShimSources - displayName: Publish generated shim sources - condition: and(always(), eq(variables.HasGeneratedShimSources, 'true')) - - - publish: $(Pipeline.Workspace)/Source/.tmp/generated-shim-props - artifact: GeneratedShimProps - displayName: Publish generated shim props - condition: and(always(), eq(variables.HasGeneratedShimProps, 'true')) - - - publish: $(Pipeline.Workspace)/Source/.tmp/generated-shims - artifact: GeneratedShimProjects - displayName: Publish generated shim project files - condition: and(always(), eq(variables.HasGeneratedShimProjects, 'true')) - - - publish: $(Pipeline.Workspace)/Source/shims/artifacts/Release - artifact: ShimAssemblies - displayName: Publish compiled shim assemblies - condition: and(always(), eq(variables.HasShimAssemblies, 'true')) - - - pwsh: | - $pwshPath = if ($IsLinux) { '/usr/bin/pwsh' } else { (Get-Command pwsh -ErrorAction Stop).Source } - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' - $fakeCompileScriptPath = Join-Path $repoRoot '.tmp/generated-fake-ue-compile.ps1' - $shimManifestPath = Join-Path $repoRoot '.tmp/generated-shim-manifest.json' - - $branchName = "$env:BUILD_SOURCEBRANCH" -replace '^refs/heads/', '' - $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') - $templatesPostfix = ($env:DOBUILDANALYSIS -eq 'true' -and $templatesBranch -and $templatesBranch -ne 'main') ` - ? "@$($templatesBranch)" ` - : '' - $polarisBranch = "$branchName$templatesPostfix" - $commitDate = ([DateTime](git -C $repoRoot log -1 --format=%ci)).ToString('o') - - $cleanCommands = @( - " - shell: ['$pwshPath', '-NoProfile', '-Command', 'Write-Host fake-clean-step']" - ) - - $buildCommand = - " - shell: ['$pwshPath', '-NoProfile', '-File', '$fakeCompileScriptPath'" + - ", '-Directory', 'templates,Examples', '-Configuration', 'Release'" + - ", '-ShimManifestPath', '$shimManifestPath', '-Rebuild']" - - $buildCommands = @( - $buildCommand - ) - - $polarisContent = @( - 'version: 1' - 'project:' - " name: $(BlackDuckProjectName)" - " branch: $polarisBranch" - " projectDir: $repoRoot" - ' revision:' - " name: $env:BUILD_SOURCEVERSION" - " date: $commitDate" - 'capture:' - ' build:' - ' cleanCommands:' - $cleanCommands - ' buildCommands:' - $buildCommands - 'analyze:' - ' mode: central' - 'install:' - ' coverity:' - " version: $(CoverityCliVersion)" - 'serverUrl: https://osisoft.cop.blackduck.com' - ) -join "`n" - - $polarisPath = Join-Path $env:PIPELINE_WORKSPACE 'polaris.yml' - $polarisContent | Out-File -FilePath $polarisPath -Encoding ascii -Force - Get-Content -Path $polarisPath - displayName: Generate polaris.yml - - - task: Cache@2 - displayName: Restore Coverity CLI cache - retryCountOnTaskFailure: 3 - inputs: - key: "\"CoverityCLI\" | \"$(CoverityCliVersion)\" | \"v1\"" - path: $(Pipeline.Workspace)/Coverity-CLI - cacheHitVar: COVERITY_CLI_RESTORED - - - pwsh: | - $toolPath = "$(Pipeline.Workspace)/Coverity-CLI" - if (-not (Test-Path -Path $toolPath)) { - New-Item -ItemType Directory -Path $toolPath | Out-Null - } - displayName: Ensure Coverity CLI folder - - - task: BlackduckCoverityOnPolaris@2 - displayName: Coverity scan - inputs: - polarisService: Coverity on Polaris - polarisCommand: > - -c $(Pipeline.Workspace)/polaris.yml - analyze - -w - env: - AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) - POLARIS_HOME: $(Pipeline.Workspace)/Coverity-CLI - - - pwsh: | - $repoRoot = Join-Path $env:PIPELINE_WORKSPACE 'Source' - $paths = @{ - HasCoverityArtifacts = Test-Path -LiteralPath (Join-Path $repoRoot '.blackduck') - } - - foreach ($entry in $paths.GetEnumerator()) { - $value = if ($entry.Value) { 'true' } else { 'false' } - Write-Host "##vso[task.setvariable variable=$($entry.Key)]$value" - Write-Host "$($entry.Key): $value" - } - displayName: Detect security artifacts - condition: always() - - - publish: $(Pipeline.Workspace)/Source/.blackduck - artifact: Coverity - displayName: Publish Coverity artifacts - condition: and(always(), eq(variables.HasCoverityArtifacts, 'true')) + - template: uecf/component-coverity-stage.yml@Templates + parameters: + EnableSecurityValidation: ${{ parameters.EnableSecurityValidation }} + CoverityTeamName: $(CoverityProjectTeam) + CoverityVersion: $(CoverityCliVersion) + ProjectName: $(BlackDuckProjectName) - stage: AntiMalware displayName: AntiMalware @@ -598,675 +379,7 @@ stages: displayName: Publish CodeAnalysisLogs condition: always() - - stage: BlackDuckWindows - displayName: Black Duck - Windows - dependsOn: BuildWithShims - condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) - jobs: - - job: BlackDuckInitialisation - displayName: BlackDuck Initialise - timeoutInMinutes: "10" - pool: - name: Dabacon-Products-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/dotnet/sdk:9.0 - workspace: - clean: all - variables: - - group: BlackDuck on Polaris - steps: - - checkout: none - - - pwsh: | - $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') - $templatesPostfix = ($templatesBranch -and $templatesBranch -ne 'main') ` - ? "@$($templatesBranch)" ` - : '' - - $branchVersion = "$env:BUILD_SOURCEBRANCHNAME$templatesPostfix" - - if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { - $baseSemanticVersion = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)" - } - else { - $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' - $baseSemanticVersion = "$branchName-$($env:BUILD_BUILDID)" - } - - $semanticVersion = "$baseSemanticVersion-windows$templatesPostfix" - $ubuntuComparisonVersion = "$baseSemanticVersion-linux$templatesPostfix" - $componentComparisonVersion = "$baseSemanticVersion-components$templatesPostfix" - - Write-Host "##vso[task.setvariable variable=BranchVersion]$branchVersion" - Write-Host "##vso[task.setvariable variable=BranchVersion;isOutput=true]$branchVersion" - Write-Host "##vso[task.setvariable variable=SemanticVersion]$semanticVersion" - Write-Host "##vso[task.setvariable variable=SemanticVersion;isOutput=true]$semanticVersion" - Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion]$ubuntuComparisonVersion" - Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion;isOutput=true]$ubuntuComparisonVersion" - Write-Host "##vso[task.setvariable variable=ComponentComparisonVersion]$componentComparisonVersion" - Write-Host "##vso[task.setvariable variable=ComponentComparisonVersion;isOutput=true]$componentComparisonVersion" - displayName: Calculate Black Duck versions - name: CalculateVersion - - - pwsh: | - $headers = @{ - Authorization = "token $env:BLACKDUCK_ACCESS_TOKEN" - Accept = 'application/vnd.blackducksoftware.user-4+json' - } - - $uri = "$env:BLACKDUCK_URL/api/tokens/authenticate" - $response = Invoke-RestMethod -Method POST -Uri $uri -Headers $headers - - Write-Host "##vso[task.setvariable variable=BaseUrl]$env:BLACKDUCK_URL" - Write-Host "##vso[task.setvariable variable=BaseUrl;isOutput=true]$env:BLACKDUCK_URL" - Write-Host "##vso[task.setvariable variable=BearerToken]$($response.bearerToken)" - Write-Host "##vso[task.setvariable variable=BearerToken;isOutput=true]$($response.bearerToken)" - displayName: Authenticate to Black Duck - name: Authenticate - retryCountOnTaskFailure: 3 - env: - BLACKDUCK_URL: https://aveva.app.blackduck.com - BLACKDUCK_ACCESS_TOKEN: $(BLACKDUCK_ACCESS_TOKEN) - - - pwsh: | - $headers = @{ - Authorization = "Bearer $env:BearerToken" - Accept = 'application/vnd.blackducksoftware.project-detail-7+json' - } - - $projectName = '$(BlackDuckProjectName)' - $limit = 100 - $offset = 0 - $allProjects = @() - - do { - $uri = "$env:BaseUrl/api/projects?limit=$limit&offset=$offset&q=name:$projectName" - $response = Invoke-RestMethod -Method GET -Uri $uri -Headers $headers - if ($response.items) { - $allProjects += $response.items - } - $offset += $limit - } while ($offset -lt $response.totalCount) - - $matchedProject = $allProjects | Where-Object { $_.name -eq $projectName } - - if (-not $matchedProject) { - Write-Host "Project '$projectName' not found in Black Duck." - Write-Host "##vso[task.setvariable variable=ProjectExists]false" - Write-Host "##vso[task.setvariable variable=ProjectExists;isOutput=true]false" - Write-Host "##vso[task.logissue type=error]Black Duck project '$projectName' must exist before the scan runs. Create it or grant the scanner access to the existing project." - exit 1 - } - - $projectId = $matchedProject._meta.href.Split('/')[-1] - Write-Host "##vso[task.setvariable variable=ProjectId]$projectId" - Write-Host "##vso[task.setvariable variable=ProjectId;isOutput=true]$projectId" - Write-Host "##vso[task.setvariable variable=ProjectExists]true" - Write-Host "##vso[task.setvariable variable=ProjectExists;isOutput=true]true" - displayName: Get Black Duck project ID - name: ProjectId - retryCountOnTaskFailure: 3 - env: - BearerToken: $(BearerToken) - BaseUrl: $(BaseUrl) - - - job: BlackDuckClone - displayName: BlackDuck Clone - dependsOn: BlackDuckInitialisation - condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true')) - timeoutInMinutes: "10" - pool: - name: Dabacon-Products-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/dotnet/sdk:9.0 - workspace: - clean: all - variables: - - group: BlackDuck on Polaris - - name: BearerToken - value: $[ dependencies.BlackDuckInitialisation.outputs['Authenticate.BearerToken'] ] - - name: BaseUrl - value: $[ dependencies.BlackDuckInitialisation.outputs['Authenticate.BaseUrl'] ] - - name: ProjectId - value: $[ dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectId'] ] - - name: ProjectExists - value: $[ dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'] ] - - name: BranchVersion - value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.BranchVersion'] ] - - name: SemanticVersion - value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.SemanticVersion'] ] - - name: UbuntuComparisonVersion - value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.UbuntuComparisonVersion'] ] - steps: - - checkout: none - - - template: steps/blackduck-clone-version.yml@Templates - parameters: - BearerToken: $(BearerToken) - BaseUrl: $(BaseUrl) - ProjectId: $(ProjectId) - SourceVersion: main - TargetVersion: $(BranchVersion) - Phase: DEVELOPMENT - RemoveTargetVersion: false - - - template: steps/blackduck-clone-version.yml@Templates - parameters: - BearerToken: $(BearerToken) - BaseUrl: $(BaseUrl) - ProjectId: $(ProjectId) - SourceVersion: $(BranchVersion) - TargetVersion: $(SemanticVersion) - Phase: PLANNING - RemoveTargetVersion: true - - - template: steps/blackduck-clone-version.yml@Templates - parameters: - BearerToken: $(BearerToken) - BaseUrl: $(BaseUrl) - ProjectId: $(ProjectId) - SourceVersion: $(BranchVersion) - TargetVersion: $(UbuntuComparisonVersion) - Phase: PLANNING - RemoveTargetVersion: false - - - job: BlackDuckScan - displayName: Black Duck Scan - dependsOn: - - BlackDuckInitialisation - - BlackDuckClone - condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true')) - timeoutInMinutes: "120" - pool: - name: Dabacon-Products-Microsoft-Hosted-Windows - demands: - - ImageOverride -equals windows-latest - container: mcr.microsoft.com/dotnet/sdk:9.0-windowsservercore-ltsc2022 - workspace: - clean: all - variables: - - name: SemanticVersion - value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.SemanticVersion'] ] - steps: - - template: steps/retrieve-source.yml@Templates - parameters: - IsIPR: false - ArtifactName: Source - DownloadPath: $(Pipeline.Workspace)/Source - - - pwsh: | - $workdir = "$(Pipeline.Workspace)\JDK" - New-Item -Path $workdir -ItemType Directory -Force | Out-Null - - $url = "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u452-b09/OpenJDK8U-jdk_x64_windows_hotspot_8u452b09.zip" - $zipPath = "$workdir\Temurin8.zip" - - Write-Host "Invoke-WebRequest -Uri $url -OutFile $zipPath" - [Net.ServicePointManager]::SecurityProtocol = "Tls12" - Invoke-WebRequest -Uri $url -OutFile $zipPath - - Write-Host "Expand-Archive -LiteralPath $zipPath -DestinationPath $workdir -Force" - Expand-Archive -LiteralPath $zipPath -DestinationPath $workdir -Force - displayName: Download Temurin JDK 8 - - - pwsh: | - $jdkFolder = Get-ChildItem -Path "$(Pipeline.Workspace)\JDK" -Directory | - Where-Object { $_.Name -like 'jdk8*' } | - Select-Object -First 1 - - if (-not $jdkFolder) { - Write-Error "Cannot locate a jdk8* folder under $(Pipeline.Workspace)\JDK" - exit 1 - } - - $javaHome = $jdkFolder.FullName - Write-Host "##vso[task.setvariable variable=JAVA_HOME]$javaHome" - Write-Host "##vso[task.setvariable variable=PATH]$javaHome\bin;$($env:PATH)" - Write-Host "JAVA_HOME is now set to $javaHome" - displayName: Configure JAVA_HOME for Temurin JDK 8 - - - pwsh: | - Write-Host "Verifying Java..." - java -version - displayName: Verify Java Installation - - - task: ArchiveFiles@2 - displayName: Archive sources and build outputs - inputs: - rootFolderOrFile: $(Pipeline.Workspace)\Source - includeRootFolder: true - archiveType: zip - archiveFile: $(Build.ArtifactStagingDirectory)\blackduck-input.zip - replaceExistingArchive: true - verbose: true - - - task: BlackDuckDetectTask@10 - displayName: Black Duck scan - retryCountOnTaskFailure: 0 - inputs: - BlackDuckScaService: BlackDuckScanner - DetectVersion: latest - DetectFolder: $(Pipeline.Workspace)\detect - DetectArguments: | - --detect.project.name=$(BlackDuckProjectName) - --detect.project.version.name=$(SemanticVersion) - --detect.source.path=$(Pipeline.Workspace)\Source - --detect.binary.scan.file.path=$(Build.ArtifactStagingDirectory)\blackduck-input.zip - --detect.cleanup=true - --detect.output.path=$(Build.ArtifactStagingDirectory)\blackduck-logs - --detect.detector.search.depth=10 - --detect.detector.search.continue=true - --detect.blackduck.signature.scanner.upload.source.mode=true - --detect.blackduck.signature.scanner.copyright.search=true - --detect.blackduck.signature.scanner.license.search=true - --logging.level.detect=DEBUG - --detect.diagnostic=true - --detect.timeout=1800 - --detect.blackduck.signature.scanner.arguments= --fs-wait-time=90 - --detect.wait.for.results=true - --detect.wait.for.results.timeout=6000000 - - - pwsh: | - $artifactStaging = $env:BUILD_ARTIFACTSTAGINGDIRECTORY - $paths = @{ - HasBlackDuckLogs = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-logs') - HasBlackDuckInput = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-input.zip') - } - - foreach ($entry in $paths.GetEnumerator()) { - $value = if ($entry.Value) { 'true' } else { 'false' } - Write-Host "##vso[task.setvariable variable=$($entry.Key)]$value" - Write-Host "$($entry.Key): $value" - } - displayName: Detect Black Duck artifacts - condition: always() - - - publish: $(Build.ArtifactStagingDirectory)/blackduck-logs - artifact: BlackDuckLogs - displayName: Publish Black Duck logs - condition: and(always(), eq(variables.HasBlackDuckLogs, 'true')) - - - publish: $(Build.ArtifactStagingDirectory)/blackduck-input.zip - artifact: BlackDuckInput - displayName: Publish Black Duck input archive - condition: and(always(), eq(variables.HasBlackDuckInput, 'true')) - - - stage: BlackDuckUbuntu - displayName: Black Duck - Ubuntu - dependsOn: BuildWithShims - condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) - jobs: - - job: BlackDuckInitialisation - displayName: BlackDuck Initialise - timeoutInMinutes: "10" - pool: - name: Dabacon-Products-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/dotnet/sdk:9.0 - workspace: - clean: all - variables: - - group: BlackDuck on Polaris - steps: - - checkout: none - - - pwsh: | - $templatesBranch = "$env:TEMPLATES_REF".replace('refs/heads/', '') - $templatesPostfix = ($templatesBranch -and $templatesBranch -ne 'main') ` - ? "@$($templatesBranch)" ` - : '' - - $branchVersion = "$env:BUILD_SOURCEBRANCHNAME$templatesPostfix" - - if ($env:BUILD_REASON -eq 'PullRequest' -and $env:SYSTEM_PULLREQUEST_PULLREQUESTID) { - $baseSemanticVersion = "pr-$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)-$($env:BUILD_BUILDID)" - } - else { - $branchName = $env:BUILD_SOURCEBRANCHNAME -replace '[^A-Za-z0-9._-]', '-' - $baseSemanticVersion = "$branchName-$($env:BUILD_BUILDID)" - } - - $ubuntuComparisonVersion = "$baseSemanticVersion-linux$templatesPostfix" - - Write-Host "##vso[task.setvariable variable=BranchVersion]$branchVersion" - Write-Host "##vso[task.setvariable variable=BranchVersion;isOutput=true]$branchVersion" - Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion]$ubuntuComparisonVersion" - Write-Host "##vso[task.setvariable variable=UbuntuComparisonVersion;isOutput=true]$ubuntuComparisonVersion" - displayName: Calculate Black Duck versions - name: CalculateVersion - - - pwsh: | - $headers = @{ - Authorization = "token $env:BLACKDUCK_ACCESS_TOKEN" - Accept = 'application/vnd.blackducksoftware.user-4+json' - } - - $uri = "$env:BLACKDUCK_URL/api/tokens/authenticate" - $response = Invoke-RestMethod -Method POST -Uri $uri -Headers $headers - - Write-Host "##vso[task.setvariable variable=BaseUrl]$env:BLACKDUCK_URL" - Write-Host "##vso[task.setvariable variable=BaseUrl;isOutput=true]$env:BLACKDUCK_URL" - Write-Host "##vso[task.setvariable variable=BearerToken]$($response.bearerToken)" - Write-Host "##vso[task.setvariable variable=BearerToken;isOutput=true]$($response.bearerToken)" - displayName: Authenticate to Black Duck - name: Authenticate - retryCountOnTaskFailure: 3 - env: - BLACKDUCK_URL: https://aveva.app.blackduck.com - BLACKDUCK_ACCESS_TOKEN: $(BLACKDUCK_ACCESS_TOKEN) - - - pwsh: | - $headers = @{ - Authorization = "Bearer $env:BearerToken" - Accept = 'application/vnd.blackducksoftware.project-detail-7+json' - } - - $projectName = '$(BlackDuckProjectName)' - $limit = 100 - $offset = 0 - $allProjects = @() - - do { - $uri = "$env:BaseUrl/api/projects?limit=$limit&offset=$offset&q=name:$projectName" - $response = Invoke-RestMethod -Method GET -Uri $uri -Headers $headers - if ($response.items) { - $allProjects += $response.items - } - $offset += $limit - } while ($offset -lt $response.totalCount) - - $matchedProject = $allProjects | Where-Object { $_.name -eq $projectName } - - if (-not $matchedProject) { - Write-Host "Project '$projectName' not found in Black Duck." - Write-Host "##vso[task.setvariable variable=ProjectExists]false" - Write-Host "##vso[task.setvariable variable=ProjectExists;isOutput=true]false" - Write-Host "##vso[task.logissue type=error]Black Duck project '$projectName' must exist before the scan runs. Create it or grant the scanner access to the existing project." - exit 1 - } - - $projectId = $matchedProject._meta.href.Split('/')[-1] - Write-Host "##vso[task.setvariable variable=ProjectId]$projectId" - Write-Host "##vso[task.setvariable variable=ProjectId;isOutput=true]$projectId" - Write-Host "##vso[task.setvariable variable=ProjectExists]true" - Write-Host "##vso[task.setvariable variable=ProjectExists;isOutput=true]true" - displayName: Get Black Duck project ID - name: ProjectId - retryCountOnTaskFailure: 3 - env: - BearerToken: $(BearerToken) - BaseUrl: $(BaseUrl) - - - job: BlackDuckClone - displayName: BlackDuck Clone - dependsOn: BlackDuckInitialisation - condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true')) - timeoutInMinutes: "10" - pool: - name: Dabacon-Products-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/dotnet/sdk:9.0 - workspace: - clean: all - variables: - - group: BlackDuck on Polaris - - name: BearerToken - value: $[ dependencies.BlackDuckInitialisation.outputs['Authenticate.BearerToken'] ] - - name: BaseUrl - value: $[ dependencies.BlackDuckInitialisation.outputs['Authenticate.BaseUrl'] ] - - name: ProjectId - value: $[ dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectId'] ] - - name: ProjectExists - value: $[ dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'] ] - - name: BranchVersion - value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.BranchVersion'] ] - - name: UbuntuComparisonVersion - value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.UbuntuComparisonVersion'] ] - steps: - - checkout: none - - - template: steps/blackduck-clone-version.yml@Templates - parameters: - BearerToken: $(BearerToken) - BaseUrl: $(BaseUrl) - ProjectId: $(ProjectId) - SourceVersion: main - TargetVersion: $(BranchVersion) - Phase: DEVELOPMENT - RemoveTargetVersion: false - - - template: steps/blackduck-clone-version.yml@Templates - parameters: - BearerToken: $(BearerToken) - BaseUrl: $(BaseUrl) - ProjectId: $(ProjectId) - SourceVersion: $(BranchVersion) - TargetVersion: $(UbuntuComparisonVersion) - Phase: PLANNING - RemoveTargetVersion: true - - - job: BlackDuckScanUbuntu - displayName: Black Duck Scan Ubuntu - dependsOn: - - BlackDuckInitialisation - - BlackDuckClone - condition: and(succeeded(), eq(dependencies.BlackDuckInitialisation.outputs['ProjectId.ProjectExists'], 'true'), ${{ eq(parameters.EnableUbuntuBlackDuckComparison, 'true') }}) - timeoutInMinutes: "120" - pool: - name: Dabacon-Products-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/dotnet/sdk:9.0 - workspace: - clean: all - variables: - - group: BlackDuck on Polaris - - name: UbuntuComparisonVersion - value: $[ dependencies.BlackDuckInitialisation.outputs['CalculateVersion.UbuntuComparisonVersion'] ] - steps: - - template: steps/retrieve-source.yml@Templates - parameters: - IsIPR: false - ArtifactName: Source - DownloadPath: $(Pipeline.Workspace)/Source - - - pwsh: | - $sourceRoot = "$(Pipeline.Workspace)/Source" - $archivePath = "$(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip" - - if (Test-Path -LiteralPath $archivePath) { - Remove-Item -LiteralPath $archivePath -Force - } - - Compress-Archive -Path $sourceRoot -DestinationPath $archivePath -CompressionLevel Optimal - Write-Host "Created archive at $archivePath" - displayName: Archive sources and build outputs (Ubuntu) - - - pwsh: | - $diagnosticPath = "$(Build.ArtifactStagingDirectory)/blackduck-network-ubuntu.txt" - $targets = @( - 'detect.blackduck.com', - 'aveva.app.blackduck.com' - ) - - $lines = New-Object System.Collections.Generic.List[string] - - foreach ($target in $targets) { - $lines.Add("=== $target ===") - - try { - $addresses = [System.Net.Dns]::GetHostAddresses($target) | ForEach-Object { $_.IPAddressToString } - $lines.Add("DNS: $($addresses -join ', ')") - } - catch { - $lines.Add("DNS ERROR: $($_.Exception.Message)") - } - - try { - $pingOutput = & ping -c 2 $target 2>&1 - $lines.Add('PING:') - foreach ($line in $pingOutput) { - $lines.Add([string]$line) - } - } - catch { - $lines.Add("PING ERROR: $($_.Exception.Message)") - } - - try { - $response = Invoke-WebRequest -Uri ("https://{0}" -f $target) -Method Head -SkipHttpErrorCheck -TimeoutSec 30 - $lines.Add("HTTPS: StatusCode=$($response.StatusCode)") - } - catch { - $lines.Add("HTTPS ERROR: $($_.Exception.Message)") - } - - $lines.Add('') - } - - Set-Content -LiteralPath $diagnosticPath -Value $lines -Encoding utf8 - Get-Content -LiteralPath $diagnosticPath - displayName: Diagnose Black Duck network reachability (Ubuntu) - condition: always() - - - task: Cache@2 - displayName: Restore JDK cache (Ubuntu) - retryCountOnTaskFailure: 3 - inputs: - key: 'jdk | temurin17 | $(Agent.OS) | v1' - path: $(Pipeline.Workspace)/JDK-Ubuntu - cacheHitVar: JDK_UBUNTU_RESTORED - - - pwsh: | - if ($env:JDK_UBUNTU_RESTORED -eq 'true') { - Write-Host 'JDK cache restored.' - exit 0 - } - - $workdir = "$(Pipeline.Workspace)/JDK-Ubuntu" - $archivePath = Join-Path $workdir 'Temurin17.tar.gz' - $url = 'https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.14%2B7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.14_7.tar.gz' - - New-Item -Path $workdir -ItemType Directory -Force | Out-Null - Invoke-WebRequest -Uri $url -OutFile $archivePath - & tar -xzf $archivePath -C $workdir - displayName: Download Temurin JDK 17 (Ubuntu) - condition: ne(variables.JDK_UBUNTU_RESTORED, 'true') - - - pwsh: | - $jdkFolder = Get-ChildItem -Path "$(Pipeline.Workspace)/JDK-Ubuntu" -Directory | - Where-Object { $_.Name -like 'jdk-17*' -or $_.Name -like 'jdk17*' } | - Select-Object -First 1 - - if (-not $jdkFolder) { - throw 'Unable to locate extracted Temurin JDK 17 folder under $(Pipeline.Workspace)/JDK-Ubuntu.' - } - - $javaHome = $jdkFolder.FullName - Write-Host "##vso[task.setvariable variable=JAVA_HOME]$javaHome" - Write-Host "##vso[task.prependpath]$javaHome/bin" - & "$javaHome/bin/java" -version - displayName: Configure JAVA_HOME (Ubuntu) - - - pwsh: | - $detectRoot = "$(Pipeline.Workspace)/detect-ubuntu" - $outputPath = "$(Build.ArtifactStagingDirectory)/blackduck-logs-ubuntu" - $detectScript = Join-Path $detectRoot 'detect10.sh' - $sourcePath = "$(Pipeline.Workspace)/Source" - $binaryScanPath = "$(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip" - - New-Item -Path $detectRoot -ItemType Directory -Force | Out-Null - New-Item -Path $outputPath -ItemType Directory -Force | Out-Null - - Invoke-WebRequest -Uri 'https://detect.blackduck.com/detect10.sh' -OutFile $detectScript - & chmod +x $detectScript - - $arguments = @( - '--blackduck.url=https://aveva.app.blackduck.com' - "--blackduck.api.token=$env:BLACKDUCK_ACCESS_TOKEN" - '--detect.project.name=$(BlackDuckProjectName)' - '--detect.project.version.name=$(UbuntuComparisonVersion)' - "--detect.source.path=$sourcePath" - "--detect.binary.scan.file.path=$binaryScanPath" - '--detect.cleanup=true' - "--detect.output.path=$outputPath" - '--detect.detector.search.depth=10' - '--detect.detector.search.continue=true' - '--detect.blackduck.signature.scanner.upload.source.mode=true' - '--detect.blackduck.signature.scanner.copyright.search=true' - '--detect.blackduck.signature.scanner.license.search=true' - '--logging.level.detect=DEBUG' - '--detect.diagnostic=true' - '--detect.timeout=1800' - '--detect.blackduck.signature.scanner.arguments=--fs-wait-time=90' - '--detect.wait.for.results=true' - '--detect.wait.for.results.timeout=6000000' - ) - - & bash $detectScript @arguments - displayName: Black Duck scan (Ubuntu manual Detect) - retryCountOnTaskFailure: 3 - env: - BLACKDUCK_ACCESS_TOKEN: $(BLACKDUCK_ACCESS_TOKEN) - JAVA_HOME: $(JAVA_HOME) - - - pwsh: | - $artifactStaging = $env:BUILD_ARTIFACTSTAGINGDIRECTORY - $paths = @{ - HasBlackDuckLogsUbuntu = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-logs-ubuntu') - HasBlackDuckInputUbuntu = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-input-ubuntu.zip') - HasBlackDuckNetworkDiagnosticsUbuntu = Test-Path -LiteralPath (Join-Path $artifactStaging 'blackduck-network-ubuntu.txt') - } - - foreach ($entry in $paths.GetEnumerator()) { - $value = if ($entry.Value) { 'true' } else { 'false' } - Write-Host "##vso[task.setvariable variable=$($entry.Key)]$value" - Write-Host "$($entry.Key): $value" - } - displayName: Detect Black Duck artifacts (Ubuntu) - condition: always() - - - publish: $(Build.ArtifactStagingDirectory)/blackduck-logs-ubuntu - artifact: BlackDuckLogsUbuntu - displayName: Publish Black Duck logs (Ubuntu) - condition: and(always(), eq(variables.HasBlackDuckLogsUbuntu, 'true')) - - - publish: $(Build.ArtifactStagingDirectory)/blackduck-input-ubuntu.zip - artifact: BlackDuckInputUbuntu - displayName: Publish Black Duck input archive (Ubuntu) - condition: and(always(), eq(variables.HasBlackDuckInputUbuntu, 'true')) - - - publish: $(Build.ArtifactStagingDirectory)/blackduck-network-ubuntu.txt - artifact: BlackDuckNetworkDiagnosticsUbuntu - displayName: Publish Black Duck network diagnostics (Ubuntu) - condition: and(always(), eq(variables.HasBlackDuckNetworkDiagnosticsUbuntu, 'true')) - - - ${{ if and(eq(parameters.EnableSecurityValidation, 'true'), eq(parameters.EnableComponentTemplateBlackDuckComparison, 'true')) }}: - - stage: ComponentTemplateBlackDuckComparison - displayName: Component Template Black Duck Comparison - dependsOn: BuildWithShims - condition: succeeded() - jobs: - - template: component-blackduck.yml@Templates - parameters: - BuildArtifact: Build.Release - BlackDuckGroup: DabaconGroup - UbuntuPoolName: Dabacon-Products-Microsoft-Hosted-Ubuntu - UbuntuPoolDemands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - WindowsPoolName: Dabacon-Products-Microsoft-Hosted-Windows - WindowsPoolDemands: - - ImageOverride -equals windows-latest - IsIPR: false - ProjectName: $(BlackDuckProjectName) - PackageVersion: $(Build.SourceBranchName)-$(Build.BuildId)-components + - template: uecf/component-blackduck-stage.yml@Templates + parameters: + EnableSecurityValidation: ${{ parameters.EnableSecurityValidation }} + BlackDuckProjectName: $(BlackDuckProjectName) From 84505c621dc36bee76fcbf339f49d7f9637f881b Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Wed, 22 Apr 2026 00:47:11 +0100 Subject: [PATCH 091/102] Refactor Azure pipeline stages to use templates for source publishing and anti-malware scanning --- azure-pipelines.yml | 301 +++++++++++++------------------------------- 1 file changed, 88 insertions(+), 213 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c865817..972edd2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -22,21 +22,15 @@ resources: name: Architecture/Architecture ref: refs/heads/antiMalwareVersions/latest -parameters: - - name: EnableSecurityValidation - type: string - default: "true" - variables: - template: variables.yml@Templates - name: CoverityProjectTeam value: .RnD.Polaris.Dabacon-Products.SecurityAdvisors - - name: CoverityCliVersion - value: 2025.9.0 - name: BlackDuckProjectName value: AVEVA.UnifiedEngineeringCustomizationFramework trigger: + batch: true branches: include: - main @@ -51,107 +45,89 @@ stages: - stage: InitialiseSource displayName: Initialise Source jobs: - - job: Source - displayName: Publish Source - pool: - name: Dabacon-Products-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/dotnet/sdk:9.0 - workspace: - clean: all - steps: - - checkout: self - fetchDepth: "0" - path: Source - - - checkout: Templates - fetchDepth: "1" - path: Templates - - - pwsh: | - $sourceRoot = "$(Pipeline.Workspace)/Source" - $probeRoot = Join-Path $sourceRoot 'detector-coverage-probe' - $classicProbeRoot = Join-Path $probeRoot 'ClassicProbe' - $sdkProbeRoot = Join-Path $probeRoot 'SdkProbe' - - New-Item -Path $probeRoot -ItemType Directory -Force | Out-Null - New-Item -Path $classicProbeRoot -ItemType Directory -Force | Out-Null - New-Item -Path $sdkProbeRoot -ItemType Directory -Force | Out-Null - - $classicPackagesConfig = @( - '' - '' - ' ' - '' - ) -join "`n" - - $classicProject = @( - '' - ' ' - ' Debug' - ' AnyCPU' - ' Library' - ' v4.8.1' - ' ClassicProbe' - ' ClassicProbe' - ' ' - ' ' - ' ' - ' ' - ' ' - ' ' - '' - ) -join "`n" - - $classicSource = @( - 'namespace ClassicProbe' - '{' - ' public class Class1 { }' - '}' - ) -join "`n" - - $sdkProject = @( - '' - ' ' - ' net8.0' - ' disable' - ' disable' - ' ' - ' ' - ' ' - ' ' - '' - ) -join "`n" - - $sdkSource = @( - 'namespace SdkProbe;' - 'public class Class1 { }' - ) -join "`n" - - Set-Content -LiteralPath (Join-Path $classicProbeRoot 'packages.config') -Value $classicPackagesConfig -Encoding utf8 - Set-Content -LiteralPath (Join-Path $classicProbeRoot 'ClassicProbe.csproj') -Value $classicProject -Encoding utf8 - Set-Content -LiteralPath (Join-Path $classicProbeRoot 'Class1.cs') -Value $classicSource -Encoding utf8 - - Set-Content -LiteralPath (Join-Path $sdkProbeRoot 'SdkProbe.csproj') -Value $sdkProject -Encoding utf8 - Set-Content -LiteralPath (Join-Path $sdkProbeRoot 'Class1.cs') -Value $sdkSource -Encoding utf8 - - New-Item -Path (Join-Path $sourceRoot '.artifactignore') -ItemType File -Force | Out-Null - displayName: Prepare Source artifact - - - publish: $(Pipeline.Workspace)/Source - artifact: Source - displayName: Publish Source - - - publish: $(Pipeline.Workspace)/Templates - artifact: Templates - displayName: Publish Templates + - template: jobs/publish-source.yml@Templates + parameters: + JobName: Source + DisplayName: Publish Source + CheckoutTemplates: true + PreparationSteps: + - pwsh: | + $sourceRoot = "$(Pipeline.Workspace)/Source" + $probeRoot = Join-Path $sourceRoot 'detector-coverage-probe' + $classicProbeRoot = Join-Path $probeRoot 'ClassicProbe' + $sdkProbeRoot = Join-Path $probeRoot 'SdkProbe' + + New-Item -Path $probeRoot -ItemType Directory -Force | Out-Null + New-Item -Path $classicProbeRoot -ItemType Directory -Force | Out-Null + New-Item -Path $sdkProbeRoot -ItemType Directory -Force | Out-Null + + $classicPackagesConfig = @( + '' + '' + ' ' + '' + ) -join "`n" + + $classicProject = @( + '' + ' ' + ' Debug' + ' AnyCPU' + ' Library' + ' v4.8.1' + ' ClassicProbe' + ' ClassicProbe' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '' + ) -join "`n" + + $classicSource = @( + 'namespace ClassicProbe' + '{' + ' public class Class1 { }' + '}' + ) -join "`n" + + $sdkProject = @( + '' + ' ' + ' net8.0' + ' disable' + ' disable' + ' ' + ' ' + ' ' + ' ' + '' + ) -join "`n" + + $sdkSource = @( + 'namespace SdkProbe;' + 'public class Class1 { }' + ) -join "`n" + + Set-Content -LiteralPath (Join-Path $classicProbeRoot 'packages.config') -Value $classicPackagesConfig -Encoding utf8 + Set-Content -LiteralPath (Join-Path $classicProbeRoot 'ClassicProbe.csproj') -Value $classicProject -Encoding utf8 + Set-Content -LiteralPath (Join-Path $classicProbeRoot 'Class1.cs') -Value $classicSource -Encoding utf8 + + Set-Content -LiteralPath (Join-Path $sdkProbeRoot 'SdkProbe.csproj') -Value $sdkProject -Encoding utf8 + Set-Content -LiteralPath (Join-Path $sdkProbeRoot 'Class1.cs') -Value $sdkSource -Encoding utf8 + + New-Item -Path (Join-Path $sourceRoot '.artifactignore') -ItemType File -Force | Out-Null + displayName: Prepare Source artifact + + - template: jobs/publish-templates.yml@Templates + parameters: + JobName: ArtifactTemplates - stage: BuildWithShims displayName: Build With Shims dependsOn: InitialiseSource - condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: - job: BuildWithShims displayName: Build With Shims @@ -256,9 +232,7 @@ stages: - template: uecf/component-coverity-stage.yml@Templates parameters: - EnableSecurityValidation: ${{ parameters.EnableSecurityValidation }} CoverityTeamName: $(CoverityProjectTeam) - CoverityVersion: $(CoverityCliVersion) ProjectName: $(BlackDuckProjectName) - stage: AntiMalware @@ -266,120 +240,21 @@ stages: dependsOn: - InitialiseSource - BuildWithShims - condition: and(succeeded(), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: - - job: AntiMalwareScan - displayName: AntiMalware Scan - timeoutInMinutes: "60" - pool: - name: Dabacon-Products-Microsoft-Hosted-Windows - demands: - - ImageOverride -equals windows-latest - workspace: - clean: all - steps: - - template: steps/retrieve-source.yml@Templates - parameters: - IsIPR: false - ArtifactName: Source - DownloadPath: $(Pipeline.Workspace)/Source - - - download: current - artifact: Build.Release - displayName: Download build artifact - patterns: "**" - - - pwsh: | - Start-Process powershell.exe -ArgumentList "Update-MpSignature -UpdateSource MicrosoftUpdateServer" -Wait - Write-Output "updated the definitions successfully" - displayName: Antimalware Scan task - - - template: Pipelines/Templates/AntiMalware/AntiMalware.step.yml@antiMalwareTemplate - parameters: - scanType: Directory - scanDirectory: $(Pipeline.Workspace)\Source - artifactName: AntimalwareResults_Source - os: Windows_NT - - - template: Pipelines/Templates/AntiMalware/AntiMalware.step.yml@antiMalwareTemplate - parameters: - scanType: Directory - scanDirectory: $(Pipeline.Workspace)\Build.Release - artifactName: AntimalwareResults_Build_Release - os: Windows_NT + - template: jobs/antimalware.yml@Templates + parameters: + JobName: AntiMalwareScan - stage: AntiMalwareSarif displayName: AntiMalware SARIF dependsOn: AntiMalware - condition: and(not(canceled()), ${{ eq(parameters.EnableSecurityValidation, 'true') }}) jobs: - - job: CodeAnalysisLogs - displayName: CodeAnalysisLogs - pool: - name: Dabacon-Products-Microsoft-Hosted-Ubuntu - demands: - - ImageOverride -equals ubuntu-latest - - WorkFolder -equals /mnt/storage/sdc/storage - container: mcr.microsoft.com/dotnet/sdk:9.0 - workspace: - clean: all - steps: - - pwsh: | - $ErrorActionPreference = 'Stop' - - $headers = @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } - $artifactsUri = "$(System.CollectionUri)$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/artifacts?api-version=7.2-preview.5" - $response = Invoke-RestMethod -Uri $artifactsUri -Headers $headers -Method GET - $antiMalwareArtifacts = @($response.value | Where-Object { $_.name -like 'AntimalwareResults_*' }) - - if ($antiMalwareArtifacts.Count -eq 0) { - throw 'No AntiMalware artifacts were found for this build.' - } - - $codeAnalysisLogs = Join-Path $env:PIPELINE_WORKSPACE 'CodeAnalysisLogs' - New-Item -Path $codeAnalysisLogs -ItemType Directory -Force | Out-Null - - foreach ($artifact in $antiMalwareArtifacts) { - $artifactName = [string]$artifact.name - $downloadUrl = [string]$artifact.resource.downloadUrl - $zipPath = Join-Path $env:AGENT_TEMPDIRECTORY "$artifactName.zip" - $extractPath = Join-Path $env:AGENT_TEMPDIRECTORY $artifactName - - Invoke-WebRequest -Uri $downloadUrl -Headers $headers -OutFile $zipPath - - if (Test-Path -LiteralPath $extractPath) { - Remove-Item -LiteralPath $extractPath -Recurse -Force - } - - Expand-Archive -LiteralPath $zipPath -DestinationPath $extractPath -Force - - $sarifFiles = Get-ChildItem -Path $extractPath -Filter '*.sarif' -Recurse -File - if (-not $sarifFiles) { - Write-Warning "No SARIF file found in $artifactName" - continue - } - - foreach ($sarifFile in $sarifFiles) { - $destinationName = if ($sarifFiles.Count -eq 1) { - "$artifactName.sarif" - } - else { - "$artifactName-$($sarifFile.BaseName).sarif" - } - - Copy-Item -LiteralPath $sarifFile.FullName -Destination (Join-Path $codeAnalysisLogs $destinationName) -Force - } - } - displayName: Collect AntiMalware SARIF files - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - - - publish: $(Pipeline.Workspace)/CodeAnalysisLogs - artifact: CodeAnalysisLogs - displayName: Publish CodeAnalysisLogs - condition: always() + - template: jobs/publish-CodeAnalysisLogs.yml@Templates + parameters: + DirectSarif: + - AntimalwareResults_Source + - AntimalwareResults_Build_Release - template: uecf/component-blackduck-stage.yml@Templates parameters: - EnableSecurityValidation: ${{ parameters.EnableSecurityValidation }} BlackDuckProjectName: $(BlackDuckProjectName) From ced8cbdc671483ac1a474693fe8db96f595895fd Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Wed, 22 Apr 2026 01:48:16 +0100 Subject: [PATCH 092/102] Remove unnecessary preparation steps from source publishing job in pipeline --- azure-pipelines.yml | 72 +-------------------------------------------- 1 file changed, 1 insertion(+), 71 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 972edd2..b5c3e8b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -49,77 +49,7 @@ stages: parameters: JobName: Source DisplayName: Publish Source - CheckoutTemplates: true - PreparationSteps: - - pwsh: | - $sourceRoot = "$(Pipeline.Workspace)/Source" - $probeRoot = Join-Path $sourceRoot 'detector-coverage-probe' - $classicProbeRoot = Join-Path $probeRoot 'ClassicProbe' - $sdkProbeRoot = Join-Path $probeRoot 'SdkProbe' - - New-Item -Path $probeRoot -ItemType Directory -Force | Out-Null - New-Item -Path $classicProbeRoot -ItemType Directory -Force | Out-Null - New-Item -Path $sdkProbeRoot -ItemType Directory -Force | Out-Null - - $classicPackagesConfig = @( - '' - '' - ' ' - '' - ) -join "`n" - - $classicProject = @( - '' - ' ' - ' Debug' - ' AnyCPU' - ' Library' - ' v4.8.1' - ' ClassicProbe' - ' ClassicProbe' - ' ' - ' ' - ' ' - ' ' - ' ' - ' ' - '' - ) -join "`n" - - $classicSource = @( - 'namespace ClassicProbe' - '{' - ' public class Class1 { }' - '}' - ) -join "`n" - - $sdkProject = @( - '' - ' ' - ' net8.0' - ' disable' - ' disable' - ' ' - ' ' - ' ' - ' ' - '' - ) -join "`n" - - $sdkSource = @( - 'namespace SdkProbe;' - 'public class Class1 { }' - ) -join "`n" - - Set-Content -LiteralPath (Join-Path $classicProbeRoot 'packages.config') -Value $classicPackagesConfig -Encoding utf8 - Set-Content -LiteralPath (Join-Path $classicProbeRoot 'ClassicProbe.csproj') -Value $classicProject -Encoding utf8 - Set-Content -LiteralPath (Join-Path $classicProbeRoot 'Class1.cs') -Value $classicSource -Encoding utf8 - - Set-Content -LiteralPath (Join-Path $sdkProbeRoot 'SdkProbe.csproj') -Value $sdkProject -Encoding utf8 - Set-Content -LiteralPath (Join-Path $sdkProbeRoot 'Class1.cs') -Value $sdkSource -Encoding utf8 - - New-Item -Path (Join-Path $sourceRoot '.artifactignore') -ItemType File -Force | Out-Null - displayName: Prepare Source artifact + SourceFetchDepth: 1 - template: jobs/publish-templates.yml@Templates parameters: From 58a0e011bb91a8d452c68d62703eff1def026609 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Wed, 22 Apr 2026 01:58:29 +0100 Subject: [PATCH 093/102] Refactor Azure Pipelines configuration and remove obsolete scripts - Updated azure-pipelines.yml to improve readability and consistency. - Removed generate-shim-manifest.yml and run-fake-ue-compile.yml scripts as they are no longer needed. - Consolidated shim manifest generation directly into shim-projects.yml. - Introduced compile-with-shims.yml to streamline the compilation process. --- .../agents/ue-customization-manager.agent.md | 4 +- azure-pipelines.yml | 17 ++--- ...-ue-compile.yml => compile-with-shims.yml} | 0 shims/generate-shim-manifest.yml | 65 ------------------- shims/shim-projects.yml | 60 +++++++++++++++-- 5 files changed, 65 insertions(+), 81 deletions(-) rename shims/{run-fake-ue-compile.yml => compile-with-shims.yml} (100%) delete mode 100644 shims/generate-shim-manifest.yml diff --git a/.github/agents/ue-customization-manager.agent.md b/.github/agents/ue-customization-manager.agent.md index 6e93ebf..24c672f 100644 --- a/.github/agents/ue-customization-manager.agent.md +++ b/.github/agents/ue-customization-manager.agent.md @@ -47,7 +47,7 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz 7. **Understand Hosted Validation**: - Recognize that [`azure-pipelines.yml`](../../azure-pipelines.yml) contains a hosted security-validation lane that uses fake UE references to validate both `templates/` and `Examples/`. - - Use the YAML-hosted fake compile flow in [`shims/run-fake-ue-compile.yml`](../../shims/run-fake-ue-compile.yml) when reasoning about hosted compile-only validation. It generates the transient runner, shim props, shim projects, shim sources, sets `UNIFIED_ENGINEERING_REFERENCE_PATH`, and compiles selected directories against the fake UE surface. + - Use the YAML-hosted fake compile flow in [`shims/compile-with-shims.yml`](../../shims/compile-with-shims.yml) when reasoning about hosted compile-only validation. It generates the transient runner, shim props, shim projects, shim sources, sets `UNIFIED_ENGINEERING_REFERENCE_PATH`, and compiles selected directories against the fake UE surface. - Treat `shims/` as compile-only stand-ins for hosted validation and static analysis, not runtime replacements for a real UE installation. - Remember that `pmllib/` is generated local output for PML template builds and is intentionally ignored in git. - When investigating hosted Coverity capture problems, verify that the Polaris-wrapped fake compile is using `-Rebuild` so compiler activity is emitted during capture. @@ -266,7 +266,7 @@ You are an expert assistant for managing AVEVA Unified Engineering (UE) customiz **Key components**: - [`azure-pipelines.yml`](../../azure-pipelines.yml): Contains the hosted test lane and the optional hosted security-validation lane. -- [`shims/run-fake-ue-compile.yml`](../../shims/run-fake-ue-compile.yml): Generates the transient fake compile runner plus `.tmp` shim props, sources, and project files from `shims/shim-projects.yml`, builds the compile-only shim assemblies, and compiles `templates/` and `Examples/` against them. +- [`shims/compile-with-shims.yml`](../../shims/compile-with-shims.yml): Generates the transient fake compile runner plus `.tmp` shim props, sources, and project files from `shims/shim-projects.yml`, builds the compile-only shim assemblies, and compiles `templates/` and `Examples/` against them. - [`scripts/Build-AddIns.ps1`](../../scripts/Build-AddIns.ps1): Supports `-SkipPostBuildEvent` and `-Rebuild` for deterministic compile-only builds. - [`shims/`](../../shims): Contains compile-only UE API stand-ins used for hosted validation and Coverity capture, plus the YAML shim definition and generation templates used to produce the runtime shim manifest at build time. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b5c3e8b..ce2a5cd 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -36,32 +36,29 @@ trigger: - main pr: - autoCancel: "true" + autoCancel: true branches: include: - main stages: - - stage: InitialiseSource - displayName: Initialise Source + - stage: Initialise + displayName: Initialise jobs: - template: jobs/publish-source.yml@Templates parameters: - JobName: Source - DisplayName: Publish Source SourceFetchDepth: 1 - template: jobs/publish-templates.yml@Templates - parameters: - JobName: ArtifactTemplates + - stage: BuildWithShims displayName: Build With Shims - dependsOn: InitialiseSource + dependsOn: Initialise jobs: - job: BuildWithShims displayName: Build With Shims - timeoutInMinutes: "120" + timeoutInMinutes: 120 pool: name: Dabacon-Products-Microsoft-Hosted-Ubuntu demands: @@ -93,7 +90,7 @@ stages: parameters: OutputPath: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json - - template: shims/run-fake-ue-compile.yml + - template: shims/compile-with-shims.yml parameters: Directory: templates,Examples Configuration: Release diff --git a/shims/run-fake-ue-compile.yml b/shims/compile-with-shims.yml similarity index 100% rename from shims/run-fake-ue-compile.yml rename to shims/compile-with-shims.yml diff --git a/shims/generate-shim-manifest.yml b/shims/generate-shim-manifest.yml deleted file mode 100644 index b0f641b..0000000 --- a/shims/generate-shim-manifest.yml +++ /dev/null @@ -1,65 +0,0 @@ -parameters: - - name: OutputPath - type: string - default: $(Pipeline.Workspace)/Source/.tmp/generated-shim-manifest.json - - name: Shims - type: object - default: [] - -steps: - - pwsh: | - $outputPath = '${{ parameters.OutputPath }}' - $outputDirectory = Split-Path -Path $outputPath -Parent - if (-not [string]::IsNullOrWhiteSpace($outputDirectory)) { - $null = New-Item -ItemType Directory -Path $outputDirectory -Force - } - - $shimDefinitions = $env:SHIMS_JSON | ConvertFrom-Json - $projects = [ordered]@{} - foreach ($shim in @($shimDefinitions)) { - $project = [ordered]@{} - - $sourceFiles = [System.Collections.Generic.List[object]]::new() - foreach ($sourceFile in @($shim.sourceFiles)) { - if ($null -eq $sourceFile) { - continue - } - - $sourcePath = [string]$sourceFile.path - $sourceContent = [string]$sourceFile.content - if ([string]::IsNullOrWhiteSpace($sourcePath)) { - continue - } - - $null = $sourceFiles.Add([ordered]@{ - Path = $sourcePath - Content = $sourceContent - }) - } - - if ($sourceFiles.Count -gt 0) { - $project['SourceFiles'] = @($sourceFiles) - } - - $projectReferences = @($shim.projectReferences | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) }) - if ($projectReferences.Count -gt 0) { - $project['ProjectReferences'] = @($projectReferences | ForEach-Object { [string]$_ }) - } - - $rootNamespace = [string]$shim.rootNamespace - if (-not [string]::IsNullOrWhiteSpace($rootNamespace)) { - $project['RootNamespace'] = $rootNamespace - } - - $projects[[string]$shim.name] = $project - } - - $manifest = @{ - Projects = $projects - } - - $manifest | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $outputPath -Encoding utf8 - Write-Host "Generated shim manifest: $outputPath" - displayName: Generate shim manifest - env: - SHIMS_JSON: ${{ convertToJson(parameters.Shims) }} diff --git a/shims/shim-projects.yml b/shims/shim-projects.yml index b84057a..285fc96 100644 --- a/shims/shim-projects.yml +++ b/shims/shim-projects.yml @@ -1729,7 +1729,59 @@ parameters: projectReferences: [] steps: - - template: generate-shim-manifest.yml - parameters: - OutputPath: ${{ parameters.OutputPath }} - Shims: ${{ parameters.Shims }} + - pwsh: | # Generate shim manifest + $outputPath = '${{ parameters.OutputPath }}' + $outputDirectory = Split-Path -Path $outputPath -Parent + if (-not [string]::IsNullOrWhiteSpace($outputDirectory)) { + $null = New-Item -ItemType Directory -Path $outputDirectory -Force + } + + $shimDefinitions = $env:SHIMS_JSON | ConvertFrom-Json + $projects = [ordered]@{} + foreach ($shim in @($shimDefinitions)) { + $project = [ordered]@{} + + $sourceFiles = [System.Collections.Generic.List[object]]::new() + foreach ($sourceFile in @($shim.sourceFiles)) { + if ($null -eq $sourceFile) { + continue + } + + $sourcePath = [string]$sourceFile.path + $sourceContent = [string]$sourceFile.content + if ([string]::IsNullOrWhiteSpace($sourcePath)) { + continue + } + + $null = $sourceFiles.Add([ordered]@{ + Path = $sourcePath + Content = $sourceContent + }) + } + + if ($sourceFiles.Count -gt 0) { + $project['SourceFiles'] = @($sourceFiles) + } + + $projectReferences = @($shim.projectReferences | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) }) + if ($projectReferences.Count -gt 0) { + $project['ProjectReferences'] = @($projectReferences | ForEach-Object { [string]$_ }) + } + + $rootNamespace = [string]$shim.rootNamespace + if (-not [string]::IsNullOrWhiteSpace($rootNamespace)) { + $project['RootNamespace'] = $rootNamespace + } + + $projects[[string]$shim.name] = $project + } + + $manifest = @{ + Projects = $projects + } + + $manifest | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $outputPath -Encoding utf8 + Write-Host "Generated shim manifest: $outputPath" + displayName: Generate shim manifest + env: + SHIMS_JSON: ${{ convertToJson(parameters.Shims) }} From e70437d3199816929824fe4bfbfdaf956fa0c742 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Wed, 22 Apr 2026 02:09:37 +0100 Subject: [PATCH 094/102] Add BlackDuck stage to Azure pipeline with specific parameters --- azure-pipelines.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ce2a5cd..3a645d6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -182,6 +182,14 @@ stages: - AntimalwareResults_Source - AntimalwareResults_Build_Release - - template: uecf/component-blackduck-stage.yml@Templates - parameters: - BlackDuckProjectName: $(BlackDuckProjectName) + - stage: BlackDuck + displayName: BlackDuck + dependsOn: BuildWithShims + jobs: + - template: component-blackduck.yml@Templates + parameters: + BuildArtifact: Build.Release + BlackDuckGroup: DabaconGroup + IsIPR: false + ProjectName: $(BlackDuckProjectName) + PackageVersion: $(Build.SourceBranchName)-$(Build.BuildId) From f753148dde8cb852eb8b84270dcab9fffe64bf3d Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Wed, 22 Apr 2026 02:13:35 +0100 Subject: [PATCH 095/102] Add empty Analysers parameter to CodeAnalysisLogs job in Azure pipeline --- azure-pipelines.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3a645d6..67cdc83 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -178,6 +178,8 @@ stages: jobs: - template: jobs/publish-CodeAnalysisLogs.yml@Templates parameters: + Analysers: + [] DirectSarif: - AntimalwareResults_Source - AntimalwareResults_Build_Release From 93b0d67cbd6aaba239055707d9e58af4e38a74ae Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Wed, 22 Apr 2026 02:17:39 +0100 Subject: [PATCH 096/102] Fix dependencies in HostedTests and AntiMalware stages of Azure pipeline --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 67cdc83..752a8df 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -107,7 +107,7 @@ stages: - stage: HostedTests displayName: Hosted Tests - dependsOn: InitialiseSource + dependsOn: Initialise jobs: - job: Pester displayName: Pester @@ -165,7 +165,7 @@ stages: - stage: AntiMalware displayName: AntiMalware dependsOn: - - InitialiseSource + - Initialise - BuildWithShims jobs: - template: jobs/antimalware.yml@Templates From a3a9ef8d06402f29911bfa5f5995a74720c56fea Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Wed, 22 Apr 2026 02:26:03 +0100 Subject: [PATCH 097/102] Rename AntiMalwareSarif stage to CodeAnalysisLogs in Azure pipeline --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 752a8df..a4afa6f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -172,8 +172,8 @@ stages: parameters: JobName: AntiMalwareScan - - stage: AntiMalwareSarif - displayName: AntiMalware SARIF + - stage: CodeAnalysisLogs + displayName: CodeAnalysisLogs dependsOn: AntiMalware jobs: - template: jobs/publish-CodeAnalysisLogs.yml@Templates From ca2228d82e1b2f5f6f975be69550f64eaa467c1f Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Wed, 22 Apr 2026 02:26:19 +0100 Subject: [PATCH 098/102] Publish CodeAnalysisLogs --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a4afa6f..56404af 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -173,7 +173,7 @@ stages: JobName: AntiMalwareScan - stage: CodeAnalysisLogs - displayName: CodeAnalysisLogs + displayName: Publish CodeAnalysisLogs dependsOn: AntiMalware jobs: - template: jobs/publish-CodeAnalysisLogs.yml@Templates From 81473f639dbcda569bb2b0897dc870b166e9d148 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Wed, 22 Apr 2026 02:26:35 +0100 Subject: [PATCH 099/102] CodeAnalysisLogs --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 56404af..a4afa6f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -173,7 +173,7 @@ stages: JobName: AntiMalwareScan - stage: CodeAnalysisLogs - displayName: Publish CodeAnalysisLogs + displayName: CodeAnalysisLogs dependsOn: AntiMalware jobs: - template: jobs/publish-CodeAnalysisLogs.yml@Templates From 6bfc7207cbfbb0a702e3f52890f801e10a2591e9 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Wed, 22 Apr 2026 02:34:36 +0100 Subject: [PATCH 100/102] Remove missing antimalware SARIF downloads --- azure-pipelines.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a4afa6f..ef2bc7b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -180,9 +180,6 @@ stages: parameters: Analysers: [] - DirectSarif: - - AntimalwareResults_Source - - AntimalwareResults_Build_Release - stage: BlackDuck displayName: BlackDuck From fa946bba254c4df5df6f3718e9ddbb30860ad331 Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Wed, 22 Apr 2026 02:56:31 +0100 Subject: [PATCH 101/102] Fix hosted fake compile regressions after restack --- shims/shim-projects.yml | 7 +++++ .../MyFlexibleExplorerControl.cs | 31 ------------------- .../WebpageAddInTemplate.csproj | 1 + 3 files changed, 8 insertions(+), 31 deletions(-) diff --git a/shims/shim-projects.yml b/shims/shim-projects.yml index 285fc96..c60f6b5 100644 --- a/shims/shim-projects.yml +++ b/shims/shim-projects.yml @@ -1346,6 +1346,13 @@ parameters: content: |- using System; + namespace Aveva.Core.Utilities.AvevaConnect + { + public interface IAvevaConnectAuthenticationManager + { + } + } + namespace Aveva.Core.Utilities.Messaging { public class PdmsMessage diff --git a/templates/FlexibleExplorerAddInTemplate/MyFlexibleExplorerControl.cs b/templates/FlexibleExplorerAddInTemplate/MyFlexibleExplorerControl.cs index a8b8733..f491d80 100644 --- a/templates/FlexibleExplorerAddInTemplate/MyFlexibleExplorerControl.cs +++ b/templates/FlexibleExplorerAddInTemplate/MyFlexibleExplorerControl.cs @@ -185,37 +185,6 @@ private string ValidatePath(string filePath, string baseDirectory) _flexibleExplorer.CreateRoots(definitionProvider.RootDefinitions, false); } catch (SecurityException ex) - { - // Handle path traversal attempts - Trace.TraceWarning("[SECURITY] Security exception loading XML '{0}': {1}", xmlFileName, ex.Message); - Log.Logger().MessageFormat("Security error loading {0}: {1}", xmlFileName, ex.Message); - ApplicationFramework.Presentation.MessageBoxEx.Show( - $"Security policy violation: {ex.Message}", - "Security Error", - MessageBoxButtons.OK, - MessageBoxIcon.Error); - } - catch (XmlException ex) - { - // Handle XML parsing errors - Log.Logger().MessageFormat("XML parsing error in {0}: {1}", xmlFileName, ex.Message); - ApplicationFramework.Presentation.MessageBoxEx.Show( - $"Failed to load XML configuration: {ex.Message}", - "Configuration Error", - MessageBoxButtons.OK, - MessageBoxIcon.Error); - } - catch (FileNotFoundException ex) - { - // Handle missing file errors - Log.Logger().MessageFormat("File not found: {0}", ex.Message); - ApplicationFramework.Presentation.MessageBoxEx.Show( - $"Configuration file not found: {ex.Message}", - "File Not Found", - MessageBoxButtons.OK, - MessageBoxIcon.Error); - } - catch (SecurityException ex) { // Security-related initialization failure Trace.TraceWarning("[SECURITY] Security exception during Flexible Explorer initialization: {0}", ex.GetType().Name); diff --git a/templates/WebpageAddInTemplate/WebpageAddInTemplate.csproj b/templates/WebpageAddInTemplate/WebpageAddInTemplate.csproj index 63fb234..6821ad3 100644 --- a/templates/WebpageAddInTemplate/WebpageAddInTemplate.csproj +++ b/templates/WebpageAddInTemplate/WebpageAddInTemplate.csproj @@ -57,6 +57,7 @@ + From aadd6474b497435c934cfab0a9e4604c2932b40d Mon Sep 17 00:00:00 2001 From: Jakub Pawlinski <10334982+quasarea@users.noreply.github.com> Date: Wed, 22 Apr 2026 12:04:30 +0100 Subject: [PATCH 102/102] Add missing CoreConnectManager shim --- shims/shim-projects.yml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/shims/shim-projects.yml b/shims/shim-projects.yml index c60f6b5..d246f19 100644 --- a/shims/shim-projects.yml +++ b/shims/shim-projects.yml @@ -1346,12 +1346,20 @@ parameters: content: |- using System; - namespace Aveva.Core.Utilities.AvevaConnect - { - public interface IAvevaConnectAuthenticationManager - { - } - } + namespace Aveva.Core.Utilities.AvevaConnect + { + public interface IAvevaConnectAuthenticationManager + { + } + + public static class CoreConnectManager + { + public static string GetAccessToken() + { + return string.Empty; + } + } + } namespace Aveva.Core.Utilities.Messaging {