From 84e3705980eafea0cc54d343888cd596ec7b22cb Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Thu, 26 Mar 2026 15:33:08 +0100 Subject: [PATCH 1/7] Add support for BCPT and previous apps --- Actions/.Modules/CompileFromWorkspace.psm1 | 81 ++++++++++ Actions/CompileApps/Compile.ps1 | 32 +++- Actions/CompileApps/action.yaml | 7 +- .../DownloadPreviousRelease.ps1 | 37 +++++ Actions/DownloadPreviousRelease/action.yaml | 32 ++++ Actions/RunPipeline/RunPipeline.ps1 | 52 ++----- Actions/RunPipeline/action.yaml | 7 +- .../.github/workflows/_BuildALGoProject.yaml | 12 +- .../.github/workflows/_BuildALGoProject.yaml | 12 +- Tests/CompileFromWorkspace.Test.ps1 | 138 ++++++++++++++++++ Tests/DetermineProjectsToBuild.Test.ps1 | 2 +- 11 files changed, 364 insertions(+), 48 deletions(-) create mode 100644 Actions/DownloadPreviousRelease/DownloadPreviousRelease.ps1 create mode 100644 Actions/DownloadPreviousRelease/action.yaml diff --git a/Actions/.Modules/CompileFromWorkspace.psm1 b/Actions/.Modules/CompileFromWorkspace.psm1 index 3557892e71..1f270fd5c0 100644 --- a/Actions/.Modules/CompileFromWorkspace.psm1 +++ b/Actions/.Modules/CompileFromWorkspace.psm1 @@ -846,6 +846,85 @@ function New-BuildOutputFile { return $buildOutputPath } +<# +.SYNOPSIS + Generates AppSourceCop.json files for app folders with baseline version information. +.DESCRIPTION + For each app folder, creates an AppSourceCop.json file containing the previous version + of the app as a baseline for breaking change detection. Also includes mandatory affixes, + supported countries, and obsolete tag settings from the project settings. +.PARAMETER AppFolders + Array of app folder paths to generate AppSourceCop.json for. +.PARAMETER PreviousApps + Array of file paths to previous release .app files. +.PARAMETER CompilerFolder + Path to the compiler folder containing the AL tool. +.PARAMETER Settings + Hashtable containing the build settings with AppSourceCop configuration. +#> +function New-AppSourceCopJson { + param( + [Parameter(Mandatory = $true)] + [string[]] $AppFolders, + [Parameter(Mandatory = $true)] + [string[]] $PreviousApps, + [Parameter(Mandatory = $true)] + [string] $CompilerFolder, + [Parameter(Mandatory = $true)] + [hashtable] $Settings + ) + + # Extract version info from previous apps using the AL tool + $previousAppVersions = @{} + foreach ($appFile in $PreviousApps) { + try { + $alToolPath = Get-ALTool -CompilerFolder $CompilerFolder + $appInfo = RunAndCheck $alToolPath GetPackageManifest $appFile | ConvertFrom-Json + $key = "$($appInfo.Publisher)_$($appInfo.Name)" + $previousAppVersions[$key] = $appInfo.Version.ToString() + } + catch { + OutputWarning -message "Failed to read manifest from '$appFile': $($_.Exception.Message)" + } + } + + # Create AppSourceCop.json for each app folder with the previous version as baseline and settings from the project configuration + foreach ($folder in $AppFolders) { + $appSourceCopJsonFile = Join-Path $folder "AppSourceCop.json" + $appSourceCopJson = @{} + + # Set mandatory affixes if specified in settings + if ($Settings.appSourceCopMandatoryAffixes -and $Settings.appSourceCopMandatoryAffixes.Count -gt 0) { + $appSourceCopJson["mandatoryAffixes"] = @() + $Settings.appSourceCopMandatoryAffixes + } + + # Set obsolete tag minimum allowed major.minor version if specified in settings + if ($Settings.obsoleteTagMinAllowedMajorMinor) { + $appSourceCopJson["obsoleteTagMinAllowedMajorMinor"] = $Settings.obsoleteTagMinAllowedMajorMinor + } + + # Match previous app version by Publisher_Name + $appJsonPath = Join-Path $folder "app.json" + if (Test-Path $appJsonPath) { + $appJson = Get-Content -Path $appJsonPath -Raw | ConvertFrom-Json + $key = "$($appJson.Publisher)_$($appJson.Name)" + if ($previousAppVersions.ContainsKey($key)) { + $appSourceCopJson["Publisher"] = $appJson.Publisher + $appSourceCopJson["Name"] = $appJson.Name + $appSourceCopJson["Version"] = $previousAppVersions[$key] + } + } + + if ($appSourceCopJson.Count -gt 0) { + Write-Host "Creating AppSourceCop.json for $folder" + $appSourceCopJson | ConvertTo-Json -Depth 99 | Set-Content $appSourceCopJsonFile + } + elseif (Test-Path $appSourceCopJsonFile) { + Remove-Item $appSourceCopJsonFile -Force + } + } +} + Export-ModuleMember -Function Build-AppsInWorkspace Export-ModuleMember -Function New-BuildOutputFile Export-ModuleMember -Function Get-BuildMetadata @@ -853,3 +932,5 @@ Export-ModuleMember -Function Get-CodeAnalyzers Export-ModuleMember -Function Get-CustomAnalyzers Export-ModuleMember -Function Get-AssemblyProbingPaths Export-ModuleMember -Function Update-AppJsonProperties +Export-ModuleMember -Function Get-AppIdForAppFile +Export-ModuleMember -Function New-AppSourceCopJson diff --git a/Actions/CompileApps/Compile.ps1 b/Actions/CompileApps/Compile.ps1 index 6689a6f78f..12213c1d5e 100644 --- a/Actions/CompileApps/Compile.ps1 +++ b/Actions/CompileApps/Compile.ps1 @@ -14,7 +14,9 @@ Param( [Parameter(HelpMessage = "RunId of the baseline workflow run", Mandatory = $false)] [string] $baselineWorkflowRunId = '0', [Parameter(HelpMessage = "SHA of the baseline workflow run", Mandatory = $false)] - [string] $baselineWorkflowSHA = '' + [string] $baselineWorkflowSHA = '', + [Parameter(HelpMessage = "Path to folder containing previous release apps for AppSourceCop baseline", Mandatory = $false)] + [string] $previousAppsPath = '' ) . (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve) @@ -30,7 +32,7 @@ $settings = AnalyzeRepo -settings $settings -baseFolder $baseFolder -project $pr $settings = CheckAppDependencyProbingPaths -settings $settings -token $token -baseFolder $baseFolder -project $project # Check if there are any app folders or test app folders to compile -if ($settings.appFolders.Count -eq 0 -and $settings.testFolders.Count -eq 0) { +if ($settings.appFolders.Count -eq 0 -and $settings.testFolders.Count -eq 0 -and $settings.bcptTestFolders.Count -eq 0) { Write-Host "No app folders or test app folders specified for compilation. Skipping compilation step." return } @@ -117,9 +119,17 @@ try { Write-Host "Incremental builds based on modified apps is not yet implemented." } - if ((-not $settings.skipUpgrade) -and $settings.enableAppSourceCop) { - # TODO: Missing implementation of around using latest release as a baseline (skipUpgrade) / Appsourcecop.json baseline implementation (AB#620310) - Write-Host "Checking for required upgrades using AppSourceCop..." + if ($settings.enableAppSourceCop -and $previousAppsPath -and (Test-Path $previousAppsPath)) { + $previousApps = @(Get-ChildItem -Path $previousAppsPath -Recurse -Filter "*.app" | ForEach-Object { $_.FullName }) + if ($previousApps.Count -gt 0) { + # Copy previous apps to the package cache so AppSourceCop can resolve them + $previousApps | ForEach-Object { + Copy-Item -Path $_ -Destination $packageCachePath -Force + } + + # Generate AppSourceCop.json files for app folders + New-AppSourceCopJson -AppFolders $settings.appFolders -PreviousApps $previousApps -CompilerFolder $compilerFolder -Settings $settings + } } # Update the app jsons with version number (and other properties) from the app manifest files @@ -174,6 +184,18 @@ try { -AppType 'testApp' } + if ($settings.bcptTestFolders.Count -gt 0) { + if (-not ($settings.enableCodeAnalyzersOnTestApps)) { + $buildParams.Analyzers = @() + } + + # Compile BCPT Test Apps + $testAppFiles += Build-AppsInWorkspace @buildParams ` + -Folders $settings.bcptTestFolders ` + -OutFolder $testAppOutputFolder ` + -AppType 'testApp' + } + } finally { New-BuildOutputFile -BuildArtifactFolder $buildArtifactFolder -BuildOutputPath (Join-Path $projectFolder "BuildOutput.txt") -DisplayInConsole -FailOn $settings.failOn } diff --git a/Actions/CompileApps/action.yaml b/Actions/CompileApps/action.yaml index e702881a8c..6c32b12aba 100644 --- a/Actions/CompileApps/action.yaml +++ b/Actions/CompileApps/action.yaml @@ -37,6 +37,10 @@ inputs: description: SHA of the baseline workflow run required: false default: '' + previousAppsPath: + description: Path to folder containing previous release apps for AppSourceCop baseline + required: false + default: '' runs: using: composite steps: @@ -51,9 +55,10 @@ runs: _dependencyTestAppsJson: ${{ inputs.dependencyTestAppsJson }} _baselineWorkflowRunId: ${{ inputs.baselineWorkflowRunId }} _baselineWorkflowSHA: ${{ inputs.baselineWorkflowSHA }} + _previousAppsPath: ${{ inputs.previousAppsPath }} run: | ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "Compile Apps" -Action { - ${{ github.action_path }}/Compile.ps1 -token $ENV:_token -artifact $ENV:_artifact -project $ENV:_project -buildMode $ENV:_buildMode -dependencyAppsJson $ENV:_dependencyAppsJson -dependencyTestAppsJson $ENV:_dependencyTestAppsJson -baselineWorkflowRunId $ENV:_baselineWorkflowRunId -baselineWorkflowSHA $ENV:_baselineWorkflowSHA + ${{ github.action_path }}/Compile.ps1 -token $ENV:_token -artifact $ENV:_artifact -project $ENV:_project -buildMode $ENV:_buildMode -dependencyAppsJson $ENV:_dependencyAppsJson -dependencyTestAppsJson $ENV:_dependencyTestAppsJson -baselineWorkflowRunId $ENV:_baselineWorkflowRunId -baselineWorkflowSHA $ENV:_baselineWorkflowSHA -previousAppsPath $ENV:_previousAppsPath } branding: icon: terminal diff --git a/Actions/DownloadPreviousRelease/DownloadPreviousRelease.ps1 b/Actions/DownloadPreviousRelease/DownloadPreviousRelease.ps1 new file mode 100644 index 0000000000..f64afdadfc --- /dev/null +++ b/Actions/DownloadPreviousRelease/DownloadPreviousRelease.ps1 @@ -0,0 +1,37 @@ +Param( + [Parameter(HelpMessage = "The GitHub token running the action", Mandatory = $false)] + [string] $token, + [Parameter(HelpMessage = "Project folder", Mandatory = $false)] + [string] $project = "" +) + +. (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve) + +$baseFolder = (Get-BasePath) +$projectFolder = Join-Path $baseFolder $project +$previousAppsPath = Join-Path $projectFolder ".previousRelease" + +OutputGroupStart -Message "Locating previous release" +try { + $branchForRelease = if ($ENV:GITHUB_BASE_REF) { $ENV:GITHUB_BASE_REF } else { $ENV:GITHUB_REF_NAME } + $latestRelease = GetLatestRelease -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -ref $branchForRelease + if ($latestRelease) { + Write-Host "Using $($latestRelease.name) (tag $($latestRelease.tag_name)) as previous release" + New-Item $previousAppsPath -ItemType Directory -Force | Out-Null + DownloadRelease -token $token -projects $project -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -release $latestRelease -path $previousAppsPath -mask "Apps" -unpack + $previousApps = @(Get-ChildItem -Path $previousAppsPath -Recurse -Filter "*.app" | ForEach-Object { $_.FullName }) + Write-Host "Downloaded $($previousApps.Count) previous release app(s)" + Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "PreviousAppsPath=$previousAppsPath" + } + else { + OutputWarning -message "No previous release found" + Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "PreviousAppsPath=" + } +} +catch { + OutputWarning -message "Error trying to locate previous release: $($_.Exception.Message). Continuing without baseline." + Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "PreviousAppsPath=" +} +finally { + OutputGroupEnd +} diff --git a/Actions/DownloadPreviousRelease/action.yaml b/Actions/DownloadPreviousRelease/action.yaml new file mode 100644 index 0000000000..523abc9eeb --- /dev/null +++ b/Actions/DownloadPreviousRelease/action.yaml @@ -0,0 +1,32 @@ +name: Download Previous Release +author: Microsoft Corporation +description: Downloads the latest release apps for use as a baseline in AppSourceCop validation and upgrade testing. +inputs: + shell: + description: Shell in which you want to run the action (powershell or pwsh) + required: false + default: powershell + project: + description: Project folder + required: false + default: '.' +outputs: + PreviousAppsPath: + description: Path to the folder containing the downloaded previous release apps. Empty if no release was found. + value: ${{ steps.DownloadPreviousRelease.outputs.PreviousAppsPath }} +runs: + using: composite + steps: + - name: Download Previous Release + shell: ${{ inputs.shell }} + id: DownloadPreviousRelease + env: + _token: ${{ github.token }} + _project: ${{ inputs.project }} + run: | + ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "Download Previous Release" -Action { + ${{ github.action_path }}/DownloadPreviousRelease.ps1 -token $ENV:_token -project $ENV:_project + } +branding: + icon: terminal + color: blue diff --git a/Actions/RunPipeline/RunPipeline.ps1 b/Actions/RunPipeline/RunPipeline.ps1 index f539ae41fb..3e4717ada0 100644 --- a/Actions/RunPipeline/RunPipeline.ps1 +++ b/Actions/RunPipeline/RunPipeline.ps1 @@ -14,7 +14,9 @@ Param( [Parameter(HelpMessage = "RunId of the baseline workflow run", Mandatory = $false)] [string] $baselineWorkflowRunId = '0', [Parameter(HelpMessage = "SHA of the baseline workflow run", Mandatory = $false)] - [string] $baselineWorkflowSHA = '' + [string] $baselineWorkflowSHA = '', + [Parameter(HelpMessage = "Path to folder containing previous release apps for upgrade testing", Mandatory = $false)] + [string] $previousAppsPath = '' ) $containerBaseFolder = $null @@ -138,17 +140,12 @@ try { OutputDebug -message "Build artifacts folder $buildArtifactFolder already exists. Previous build artifacts might interfere with the current build." } - # When using workspace compilation, apps are already compiled - pass empty folders to Run-AlPipeline - if ($settings.workspaceCompilation.enabled) { - $appFolders = @() - $testFolders = @() - $bcptTestFolders = $settings.bcptTestFolders - } - else { - $appFolders = $settings.appFolders - $testFolders = $settings.testFolders - $bcptTestFolders = $settings.bcptTestFolders - } + # When using workspace compilation, apps are already compiled in .buildartifacts. + # Pass appFolders/testFolders to Run-AlPipeline so its prebuilt detection picks them up + # and publishes them through the proper upgrade testing path. + $appFolders = $settings.appFolders + $testFolders = $settings.testFolders + $bcptTestFolders = $settings.bcptTestFolders if ((-not $settings.workspaceCompilation.enabled) -and $baselineWorkflowSHA -and $baselineWorkflowRunId -ne '0' -and $settings.incrementalBuilds.mode -eq 'modifiedApps') { # Incremental builds are enabled and we are only building modified apps @@ -275,33 +272,12 @@ try { $previousApps = @() if (!$settings.skipUpgrade) { - if ($settings.workspaceCompilation.enabled) { - OutputWarning -message "skipUpgrade is ignored when workspaceCompilation is enabled." # TODO: Missing implementation when workspace compilation is enabled (AB#620310) + if ($previousAppsPath -and (Test-Path $previousAppsPath)) { + # Use previously downloaded release apps + $previousApps = @(Get-ChildItem -Path $previousAppsPath -Recurse -Filter "*.app" | ForEach-Object { $_.FullName }) + Write-Host "Using $($previousApps.Count) previous release app(s) from $previousAppsPath" } else { - OutputGroupStart -Message "Locating previous release" - try { - $branchForRelease = if ($ENV:GITHUB_BASE_REF) { $ENV:GITHUB_BASE_REF } else { $ENV:GITHUB_REF_NAME } - $latestRelease = GetLatestRelease -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -ref $branchForRelease - if ($latestRelease) { - Write-Host "Using $($latestRelease.name) (tag $($latestRelease.tag_name)) as previous release" - $artifactsFolder = Join-Path $baseFolder "artifacts" - if(-not (Test-Path $artifactsFolder)) { - New-Item $artifactsFolder -ItemType Directory | Out-Null - } - DownloadRelease -token $token -projects $project -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -release $latestRelease -path $artifactsFolder -mask "Apps" - $previousApps += @(Get-ChildItem -Path $artifactsFolder | ForEach-Object { $_.FullName }) - } - else { - OutputWarning -message "No previous release found" - } - } - catch { - OutputError -message "Error trying to locate previous release. Error was $($_.Exception.Message)" - exit - } - finally { - OutputGroupEnd - } + OutputWarning -message "Could not locate previous release apps for upgrade testing." } } diff --git a/Actions/RunPipeline/action.yaml b/Actions/RunPipeline/action.yaml index cd5003f7b4..623306f369 100644 --- a/Actions/RunPipeline/action.yaml +++ b/Actions/RunPipeline/action.yaml @@ -37,6 +37,10 @@ inputs: description: SHA of the baseline workflow run required: false default: '' + previousAppsPath: + description: Path to folder containing previous release apps for upgrade testing + required: false + default: '' runs: using: composite steps: @@ -51,9 +55,10 @@ runs: _installTestAppsJson: ${{ inputs.installTestAppsJson }} _baselineWorkflowRunId: ${{ inputs.baselineWorkflowRunId }} _baselineWorkflowSHA: ${{ inputs.baselineWorkflowSHA }} + _previousAppsPath: ${{ inputs.previousAppsPath }} run: | ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "RunPipeline" -Action { - ${{ github.action_path }}/RunPipeline.ps1 -token $ENV:_token -artifact $ENV:_artifact -project $ENV:_project -buildMode $ENV:_buildMode -installAppsJson $ENV:_installAppsJson -installTestAppsJson $ENV:_installTestAppsJson -baselineWorkflowRunId $ENV:_baselineWorkflowRunId -baselineWorkflowSHA $ENV:_baselineWorkflowSHA + ${{ github.action_path }}/RunPipeline.ps1 -token $ENV:_token -artifact $ENV:_artifact -project $ENV:_project -buildMode $ENV:_buildMode -installAppsJson $ENV:_installAppsJson -installTestAppsJson $ENV:_installTestAppsJson -baselineWorkflowRunId $ENV:_baselineWorkflowRunId -baselineWorkflowSHA $ENV:_baselineWorkflowSHA -previousAppsPath $ENV:_previousAppsPath } branding: icon: terminal diff --git a/Templates/AppSource App/.github/workflows/_BuildALGoProject.yaml b/Templates/AppSource App/.github/workflows/_BuildALGoProject.yaml index 88909a44a7..3fe1d5b5d5 100644 --- a/Templates/AppSource App/.github/workflows/_BuildALGoProject.yaml +++ b/Templates/AppSource App/.github/workflows/_BuildALGoProject.yaml @@ -109,7 +109,7 @@ jobs: shell: ${{ inputs.shell }} project: ${{ inputs.project }} buildMode: ${{ inputs.buildMode }} - get: useCompilerFolder,workspaceCompilation,keyVaultCodesignCertificateName,doNotSignApps,doNotRunTests,doNotRunBcptTests,doNotRunpageScriptingTests,artifact,generateDependencyArtifact,trustedSigning,useGitSubmodules,trackALAlertsInGitHub + get: useCompilerFolder,workspaceCompilation,keyVaultCodesignCertificateName,doNotSignApps,doNotRunTests,doNotRunBcptTests,doNotRunpageScriptingTests,artifact,generateDependencyArtifact,trustedSigning,useGitSubmodules,trackALAlertsInGitHub,skipUpgrade - name: Determine whether to build project id: DetermineBuildProject @@ -166,6 +166,14 @@ jobs: projectDependenciesJson: ${{ inputs.projectDependenciesJson }} baselineWorkflowRunId: ${{ inputs.baselineWorkflowRunId }} + - name: Download Previous Release + id: DownloadPreviousRelease + if: steps.DetermineBuildProject.outputs.BuildIt == 'True' && env.skipUpgrade == 'False' + uses: microsoft/AL-Go-Actions/DownloadPreviousRelease@main + with: + shell: ${{ inputs.shell }} + project: ${{ inputs.project }} + - name: Compile Apps id: compile uses: microsoft/AL-Go-Actions/CompileApps@main @@ -182,6 +190,7 @@ jobs: dependencyTestAppsJson: ${{ steps.DownloadProjectDependencies.outputs.DownloadedTestApps }} baselineWorkflowRunId: ${{ inputs.baselineWorkflowRunId }} baselineWorkflowSHA: ${{ inputs.baselineWorkflowSHA }} + previousAppsPath: ${{ steps.DownloadPreviousRelease.outputs.PreviousAppsPath }} - name: Save needs context to file shell: pwsh @@ -209,6 +218,7 @@ jobs: installTestAppsJson: ${{ steps.DownloadProjectDependencies.outputs.DownloadedTestApps }} baselineWorkflowRunId: ${{ inputs.baselineWorkflowRunId }} baselineWorkflowSHA: ${{ inputs.baselineWorkflowSHA }} + previousAppsPath: ${{ steps.DownloadPreviousRelease.outputs.PreviousAppsPath }} - name: Sign id: sign diff --git a/Templates/Per Tenant Extension/.github/workflows/_BuildALGoProject.yaml b/Templates/Per Tenant Extension/.github/workflows/_BuildALGoProject.yaml index 88909a44a7..3fe1d5b5d5 100644 --- a/Templates/Per Tenant Extension/.github/workflows/_BuildALGoProject.yaml +++ b/Templates/Per Tenant Extension/.github/workflows/_BuildALGoProject.yaml @@ -109,7 +109,7 @@ jobs: shell: ${{ inputs.shell }} project: ${{ inputs.project }} buildMode: ${{ inputs.buildMode }} - get: useCompilerFolder,workspaceCompilation,keyVaultCodesignCertificateName,doNotSignApps,doNotRunTests,doNotRunBcptTests,doNotRunpageScriptingTests,artifact,generateDependencyArtifact,trustedSigning,useGitSubmodules,trackALAlertsInGitHub + get: useCompilerFolder,workspaceCompilation,keyVaultCodesignCertificateName,doNotSignApps,doNotRunTests,doNotRunBcptTests,doNotRunpageScriptingTests,artifact,generateDependencyArtifact,trustedSigning,useGitSubmodules,trackALAlertsInGitHub,skipUpgrade - name: Determine whether to build project id: DetermineBuildProject @@ -166,6 +166,14 @@ jobs: projectDependenciesJson: ${{ inputs.projectDependenciesJson }} baselineWorkflowRunId: ${{ inputs.baselineWorkflowRunId }} + - name: Download Previous Release + id: DownloadPreviousRelease + if: steps.DetermineBuildProject.outputs.BuildIt == 'True' && env.skipUpgrade == 'False' + uses: microsoft/AL-Go-Actions/DownloadPreviousRelease@main + with: + shell: ${{ inputs.shell }} + project: ${{ inputs.project }} + - name: Compile Apps id: compile uses: microsoft/AL-Go-Actions/CompileApps@main @@ -182,6 +190,7 @@ jobs: dependencyTestAppsJson: ${{ steps.DownloadProjectDependencies.outputs.DownloadedTestApps }} baselineWorkflowRunId: ${{ inputs.baselineWorkflowRunId }} baselineWorkflowSHA: ${{ inputs.baselineWorkflowSHA }} + previousAppsPath: ${{ steps.DownloadPreviousRelease.outputs.PreviousAppsPath }} - name: Save needs context to file shell: pwsh @@ -209,6 +218,7 @@ jobs: installTestAppsJson: ${{ steps.DownloadProjectDependencies.outputs.DownloadedTestApps }} baselineWorkflowRunId: ${{ inputs.baselineWorkflowRunId }} baselineWorkflowSHA: ${{ inputs.baselineWorkflowSHA }} + previousAppsPath: ${{ steps.DownloadPreviousRelease.outputs.PreviousAppsPath }} - name: Sign id: sign diff --git a/Tests/CompileFromWorkspace.Test.ps1 b/Tests/CompileFromWorkspace.Test.ps1 index b6b0c70112..a39211f60a 100644 --- a/Tests/CompileFromWorkspace.Test.ps1 +++ b/Tests/CompileFromWorkspace.Test.ps1 @@ -866,4 +866,142 @@ Write-Host "Post-compile: $($appFiles.Count) apps" } } } + + Describe 'New-AppSourceCopJson' { + BeforeEach { + # Create app folders with app.json + $script:appFolder = Join-Path $TestDrive "MyApp" + New-Item -Path $script:appFolder -ItemType Directory -Force | Out-Null + @{ + id = "11111111-1111-1111-1111-111111111111" + name = "MyApp" + publisher = "Contoso" + version = "2.0.0.0" + } | ConvertTo-Json | Set-Content (Join-Path $script:appFolder "app.json") + + # Mock the AL tool to return manifest info from previous apps + Mock Get-ALTool { return "altool.exe" } -ModuleName CompileFromWorkspace + Mock RunAndCheck { + return '{"Publisher":"Contoso","Name":"MyApp","Version":"1.0.0.0"}' + } -ModuleName CompileFromWorkspace + } + + It 'Creates AppSourceCop.json with baseline version from previous app' { + New-AppSourceCopJson -AppFolders @($script:appFolder) -PreviousApps @("dummy.app") -CompilerFolder "c:\compiler" -Settings @{ + appSourceCopMandatoryAffixes = @() + obsoleteTagMinAllowedMajorMinor = "" + } + + $result = Get-Content (Join-Path $script:appFolder "AppSourceCop.json") -Raw | ConvertFrom-Json + $result.Publisher | Should -Be "Contoso" + $result.Name | Should -Be "MyApp" + $result.Version | Should -Be "1.0.0.0" + } + + It 'Includes mandatory affixes from settings' { + $settings = @{ + appSourceCopMandatoryAffixes = @("Test", "Contoso") + obsoleteTagMinAllowedMajorMinor = "" + } + + New-AppSourceCopJson -AppFolders @($script:appFolder) -PreviousApps @("dummy.app") -CompilerFolder "c:\compiler" -Settings $settings + + $result = Get-Content (Join-Path $script:appFolder "AppSourceCop.json") -Raw | ConvertFrom-Json + $result.mandatoryAffixes | Should -Be @("Test", "Contoso") + } + + It 'Includes obsoleteTagMinAllowedMajorMinor from settings' { + $settings = @{ + appSourceCopMandatoryAffixes = @() + obsoleteTagMinAllowedMajorMinor = "24.0" + } + + New-AppSourceCopJson -AppFolders @($script:appFolder) -PreviousApps @("dummy.app") -CompilerFolder "c:\compiler" -Settings $settings + + $result = Get-Content (Join-Path $script:appFolder "AppSourceCop.json") -Raw | ConvertFrom-Json + $result.obsoleteTagMinAllowedMajorMinor | Should -Be "24.0" + } + + It 'Combines settings and baseline version in one file' { + $settings = @{ + appSourceCopMandatoryAffixes = @("Test") + obsoleteTagMinAllowedMajorMinor = "23.0" + } + + New-AppSourceCopJson -AppFolders @($script:appFolder) -PreviousApps @("dummy.app") -CompilerFolder "c:\compiler" -Settings $settings + + $result = Get-Content (Join-Path $script:appFolder "AppSourceCop.json") -Raw | ConvertFrom-Json + $result.Publisher | Should -Be "Contoso" + $result.Version | Should -Be "1.0.0.0" + $result.mandatoryAffixes | Should -Be @("Test") + $result.obsoleteTagMinAllowedMajorMinor | Should -Be "23.0" + } + + It 'Does not create file when no previous app matches and no settings apply' { + Mock RunAndCheck { + return '{"Publisher":"OtherPublisher","Name":"OtherApp","Version":"1.0.0.0"}' + } -ModuleName CompileFromWorkspace + + New-AppSourceCopJson -AppFolders @($script:appFolder) -PreviousApps @("dummy.app") -CompilerFolder "c:\compiler" -Settings @{ + appSourceCopMandatoryAffixes = @() + obsoleteTagMinAllowedMajorMinor = "" + } + + Test-Path (Join-Path $script:appFolder "AppSourceCop.json") | Should -Be $false + } + + It 'Removes existing AppSourceCop.json when nothing to write' { + # Pre-create an AppSourceCop.json + $copJsonPath = Join-Path $script:appFolder "AppSourceCop.json" + '{"old":"content"}' | Set-Content $copJsonPath + + Mock RunAndCheck { + return '{"Publisher":"OtherPublisher","Name":"OtherApp","Version":"1.0.0.0"}' + } -ModuleName CompileFromWorkspace + + New-AppSourceCopJson -AppFolders @($script:appFolder) -PreviousApps @("dummy.app") -CompilerFolder "c:\compiler" -Settings @{ + appSourceCopMandatoryAffixes = @() + obsoleteTagMinAllowedMajorMinor = "" + } + + Test-Path $copJsonPath | Should -Be $false + } + + It 'Handles multiple app folders, only writing for matching ones' { + $appFolder2 = Join-Path $TestDrive "OtherApp" + New-Item -Path $appFolder2 -ItemType Directory -Force | Out-Null + @{ + id = "22222222-2222-2222-2222-222222222222" + name = "OtherApp" + publisher = "OtherPublisher" + version = "1.0.0.0" + } | ConvertTo-Json | Set-Content (Join-Path $appFolder2 "app.json") + + # RunAndCheck only returns a match for MyApp/Contoso + New-AppSourceCopJson -AppFolders @($script:appFolder, $appFolder2) -PreviousApps @("dummy.app") -CompilerFolder "c:\compiler" -Settings @{ + appSourceCopMandatoryAffixes = @() + obsoleteTagMinAllowedMajorMinor = "" + } + + # MyApp should have AppSourceCop.json (publisher/name match) + Test-Path (Join-Path $script:appFolder "AppSourceCop.json") | Should -Be $true + # OtherApp should not (no match from RunAndCheck mock) + Test-Path (Join-Path $appFolder2 "AppSourceCop.json") | Should -Be $false + } + + It 'Warns and continues when reading manifest fails' { + Mock RunAndCheck { throw "Failed to read app" } -ModuleName CompileFromWorkspace + Mock OutputWarning {} -ModuleName CompileFromWorkspace + + New-AppSourceCopJson -AppFolders @($script:appFolder) -PreviousApps @("bad.app") -CompilerFolder "c:\compiler" -Settings @{ + appSourceCopMandatoryAffixes = @("Test") + obsoleteTagMinAllowedMajorMinor = "" + } + + # Should still create the file with just the affixes + $result = Get-Content (Join-Path $script:appFolder "AppSourceCop.json") -Raw | ConvertFrom-Json + $result.mandatoryAffixes | Should -Be @("Test") + Should -Invoke OutputWarning -ModuleName CompileFromWorkspace -Times 1 + } + } } diff --git a/Tests/DetermineProjectsToBuild.Test.ps1 b/Tests/DetermineProjectsToBuild.Test.ps1 index 2d7ca5baf2..5cf14530b1 100644 --- a/Tests/DetermineProjectsToBuild.Test.ps1 +++ b/Tests/DetermineProjectsToBuild.Test.ps1 @@ -1116,7 +1116,7 @@ Describe "Get-ProjectsToBuild" { # Project1 exists Mock OutputError {} -ModuleName DetermineProjectsToBuild - $project1AppFile = @{ id = '83fb8305-4079-415d-a25d-8132f0436fd1'; name = 'First App'; publisher = 'Contoso'; version = '1.0.0.0'; dependencies = @() } + $project1AppFile= @{ id = '83fb8305-4079-415d-a25d-8132f0436fd1'; name = 'First App'; publisher = 'Contoso'; version = '1.0.0.0'; dependencies = @() } New-Item -Path "$baseFolder/Project1/.AL-Go/settings.json" -type File -Force New-Item -Path "$baseFolder/Project1/app/app.json" -Value (ConvertTo-Json $project1AppFile -Depth 10) -type File -Force From 9384dd75f7af04eed160fa5c7faa0d856b74b592 Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Thu, 26 Mar 2026 15:37:41 +0100 Subject: [PATCH 2/7] update docs --- Scenarios/settings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scenarios/settings.md b/Scenarios/settings.md index 6a54b92c42..ed1274ee3e 100644 --- a/Scenarios/settings.md +++ b/Scenarios/settings.md @@ -131,7 +131,7 @@ The repository settings are only read from the repository settings file (.github | assignPremiumPlan | Setting assignPremiumPlan to true in your project setting file, causes the build container to be created with the AssignPremiumPlan set. This causes the auto-created user to have Premium Plan enabled. This setting is needed if your tests require premium plan enabled. | false | | enableTaskScheduler | Setting enableTaskScheduler to true in your project setting file, causes the build container to be created with the Task Scheduler running. | false | | useCompilerFolder | Setting useCompilerFolder to true causes your pipelines to use containerless compiling. Unless you also set **doNotPublishApps** to true, setting useCompilerFolder to true won't give you any performance advantage, since AL-Go for GitHub will still need to create a container in order to publish and test the apps. In the future, publishing and testing will be split from building and there will be other options for getting an instance of Business Central for publishing and testing. **Note** when using UseCompilerFolder you need to sign apps using the new signing mechanism described [here](../Scenarios/Codesigning.md). | false | -| workspaceCompilation | **PREVIEW:** Configuration for workspace compilation. This uses the AL tool to compile all apps in the workspace in a single operation, which can improve build performance for repositories with multiple apps. Like **useCompilerFolder**, this is containerless compiling.
**enabled** - Set to true to enable workspace compilation. Default: false.
**parallelism** - The number of parallel compilation processes. Set to 0 or -1 to use all available processors. Default: 1.

**Current limitations:** | { "enabled": false, "parallelism": 1 } | +| workspaceCompilation | **PREVIEW:** Configuration for workspace compilation. This uses the AL tool to compile all apps in the workspace in a single operation, which can improve build performance for repositories with multiple apps. Like **useCompilerFolder**, this is containerless compiling.
**enabled** - Set to true to enable workspace compilation. Default: false.
**parallelism** - The number of parallel compilation processes. Set to 0 or -1 to use all available processors. Default: 1.

**Note:** Workspace compilation requires the artifact setting to use BC v28 or higher. | { "enabled": false, "parallelism": 1 } | | excludeEnvironments| excludeEnvironments can be an array of GitHub Environments, which should be excluded from the list of environments considered for deployment. github-pages is automatically added to this array and cannot be used as environment for deployment of AL-Go for GitHub projects. | [ ] | | trustMicrosoftNuGetFeeds | Unless this setting is set to false, AL-Go for GitHub will trust the NuGet feeds provided by Microsoft. The feeds provided by Microsoft contains all Microsoft apps, all Microsoft symbols and symbols for all AppSource apps. | true | | trustedNuGetFeeds | trustedNuGetFeeds can be an array of NuGet feed specifications, which AL-Go for GitHub will use for dependency resolution. Every feed specification must include a URL property and can optionally include a few other properties:
**url** = The URL of the feed (examples: https://pkgs.dev.azure.com/myorg/apps/\_packaging/myrepo/nuget/v3/index.json or https://nuget.pkg.github.com/mygithuborg/index.json").
**authTokenSecret** = If the NuGet feed specified by URL is private, the authTokenSecret must be the name of a secret containing the authentication token with permissions to search and read packages from the NuGet feed.
**patterns** = AL-Go for GitHub will only trust packages, where the ID matches this pattern. Default is all packages (\*).
**fingerprints** = If specified, AL-Go for GitHub will only trust packages signed with a certificate with a fingerprint matching one of the fingerprints in this array. | [ ] | From 988ec42cea7f7b8a9fcb7974c7ecb0cbf17b175a Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Thu, 26 Mar 2026 16:25:39 +0100 Subject: [PATCH 3/7] Review findings --- Actions/.Modules/CompileFromWorkspace.psm1 | 3 +-- Actions/CompileApps/Compile.ps1 | 2 +- .../DownloadPreviousRelease.ps1 | 11 ++++---- Actions/DownloadPreviousRelease/README.md | 26 +++++++++++++++++++ Tests/DownloadPreviousRelease.Action.Test.ps1 | 26 +++++++++++++++++++ 5 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 Actions/DownloadPreviousRelease/README.md create mode 100644 Tests/DownloadPreviousRelease.Action.Test.ps1 diff --git a/Actions/.Modules/CompileFromWorkspace.psm1 b/Actions/.Modules/CompileFromWorkspace.psm1 index 1f270fd5c0..a7bf94615d 100644 --- a/Actions/.Modules/CompileFromWorkspace.psm1 +++ b/Actions/.Modules/CompileFromWorkspace.psm1 @@ -876,9 +876,9 @@ function New-AppSourceCopJson { # Extract version info from previous apps using the AL tool $previousAppVersions = @{} + $alToolPath = Get-ALTool -CompilerFolder $CompilerFolder foreach ($appFile in $PreviousApps) { try { - $alToolPath = Get-ALTool -CompilerFolder $CompilerFolder $appInfo = RunAndCheck $alToolPath GetPackageManifest $appFile | ConvertFrom-Json $key = "$($appInfo.Publisher)_$($appInfo.Name)" $previousAppVersions[$key] = $appInfo.Version.ToString() @@ -932,5 +932,4 @@ Export-ModuleMember -Function Get-CodeAnalyzers Export-ModuleMember -Function Get-CustomAnalyzers Export-ModuleMember -Function Get-AssemblyProbingPaths Export-ModuleMember -Function Update-AppJsonProperties -Export-ModuleMember -Function Get-AppIdForAppFile Export-ModuleMember -Function New-AppSourceCopJson diff --git a/Actions/CompileApps/Compile.ps1 b/Actions/CompileApps/Compile.ps1 index 12213c1d5e..1e6c43bd83 100644 --- a/Actions/CompileApps/Compile.ps1 +++ b/Actions/CompileApps/Compile.ps1 @@ -133,7 +133,7 @@ try { } # Update the app jsons with version number (and other properties) from the app manifest files - Update-AppJsonProperties -Folders ($settings.appFolders + $settings.testFolders) ` + Update-AppJsonProperties -Folders $settings.appFolders ` -MajorMinorVersion $versionNumber.MajorMinorVersion -BuildNumber $versionNumber.BuildNumber -RevisionNumber $versionNumber.RevisionNumber ` -BuildBy $buildMetadata.BuildBy -BuildUrl $buildMetadata.BuildUrl diff --git a/Actions/DownloadPreviousRelease/DownloadPreviousRelease.ps1 b/Actions/DownloadPreviousRelease/DownloadPreviousRelease.ps1 index f64afdadfc..5067dab6ab 100644 --- a/Actions/DownloadPreviousRelease/DownloadPreviousRelease.ps1 +++ b/Actions/DownloadPreviousRelease/DownloadPreviousRelease.ps1 @@ -10,6 +10,7 @@ Param( $baseFolder = (Get-BasePath) $projectFolder = Join-Path $baseFolder $project $previousAppsPath = Join-Path $projectFolder ".previousRelease" +New-Item $previousAppsPath -ItemType Directory -Force | Out-Null OutputGroupStart -Message "Locating previous release" try { @@ -17,21 +18,21 @@ try { $latestRelease = GetLatestRelease -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -ref $branchForRelease if ($latestRelease) { Write-Host "Using $($latestRelease.name) (tag $($latestRelease.tag_name)) as previous release" - New-Item $previousAppsPath -ItemType Directory -Force | Out-Null DownloadRelease -token $token -projects $project -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -release $latestRelease -path $previousAppsPath -mask "Apps" -unpack $previousApps = @(Get-ChildItem -Path $previousAppsPath -Recurse -Filter "*.app" | ForEach-Object { $_.FullName }) Write-Host "Downloaded $($previousApps.Count) previous release app(s)" - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "PreviousAppsPath=$previousAppsPath" } else { OutputWarning -message "No previous release found" - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "PreviousAppsPath=" } } catch { - OutputWarning -message "Error trying to locate previous release: $($_.Exception.Message). Continuing without baseline." - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "PreviousAppsPath=" + OutputError -message "Error trying to locate previous release. Error was $($_.Exception.Message)" + exit } finally { OutputGroupEnd } + +# Output variable with path to previous apps for use in subsequent steps +Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "PreviousAppsPath=$previousAppsPath" diff --git a/Actions/DownloadPreviousRelease/README.md b/Actions/DownloadPreviousRelease/README.md new file mode 100644 index 0000000000..5de43a7c59 --- /dev/null +++ b/Actions/DownloadPreviousRelease/README.md @@ -0,0 +1,26 @@ +# Download Previous Release + +Downloads the latest release apps for use as a baseline in AppSourceCop validation and upgrade testing. + +## INPUT + +### ENV variables + +| Name | Description | +| :-- | :-- | +| Settings | env.Settings must be set by a prior call to the ReadSettings Action | + +### Parameters + +| Name | Required | Description | Default value | +| :-- | :-: | :-- | :-- | +| shell | | The shell (powershell or pwsh) in which the PowerShell script in this action should run | powershell | +| project | | The AL-Go project for which to download previous release apps | '.' | + +## OUTPUT + +### OUTPUT variables + +| Name | Description | +| :-- | :-- | +| PreviousAppsPath | Path to the folder containing the downloaded previous release apps. Empty if no release was found. | diff --git a/Tests/DownloadPreviousRelease.Action.Test.ps1 b/Tests/DownloadPreviousRelease.Action.Test.ps1 new file mode 100644 index 0000000000..7733c9e597 --- /dev/null +++ b/Tests/DownloadPreviousRelease.Action.Test.ps1 @@ -0,0 +1,26 @@ +Get-Module TestActionsHelper | Remove-Module -Force +Import-Module (Join-Path $PSScriptRoot 'TestActionsHelper.psm1') +$errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 + +Describe "DownloadPreviousRelease Action Tests" { + BeforeAll { + $actionName = "DownloadPreviousRelease" + $scriptRoot = Join-Path $PSScriptRoot "..\Actions\$actionName" -Resolve + $scriptName = "$actionName.ps1" + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'scriptPath', Justification = 'False positive.')] + $scriptPath = Join-Path $scriptRoot $scriptName + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'actionScript', Justification = 'False positive.')] + $actionScript = GetActionScript -scriptRoot $scriptRoot -scriptName $scriptName + } + + It 'Compile Action' { + Invoke-Expression $actionScript + } + + It 'Test action.yaml matches script' { + $outputs = [ordered]@{ + "PreviousAppsPath" = "Path to the folder containing the downloaded previous release apps. Empty if no release was found." + } + YamlTest -scriptRoot $scriptRoot -actionName $actionName -actionScript $actionScript -outputs $outputs + } +} From 1a0827bb5c8366f8a303940da460cb35215c2a39 Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Fri, 27 Mar 2026 07:39:24 +0100 Subject: [PATCH 4/7] Review --- Actions/CompileApps/Compile.ps1 | 4 ++-- Actions/DownloadPreviousRelease/action.yaml | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Actions/CompileApps/Compile.ps1 b/Actions/CompileApps/Compile.ps1 index 1e6c43bd83..a7793fe19e 100644 --- a/Actions/CompileApps/Compile.ps1 +++ b/Actions/CompileApps/Compile.ps1 @@ -133,7 +133,7 @@ try { } # Update the app jsons with version number (and other properties) from the app manifest files - Update-AppJsonProperties -Folders $settings.appFolders ` + Update-AppJsonProperties -Folders ($settings.appFolders + $settings.testFolders + $settings.bcptTestFolders) ` -MajorMinorVersion $versionNumber.MajorMinorVersion -BuildNumber $versionNumber.BuildNumber -RevisionNumber $versionNumber.RevisionNumber ` -BuildBy $buildMetadata.BuildBy -BuildUrl $buildMetadata.BuildUrl @@ -193,7 +193,7 @@ try { $testAppFiles += Build-AppsInWorkspace @buildParams ` -Folders $settings.bcptTestFolders ` -OutFolder $testAppOutputFolder ` - -AppType 'testApp' + -AppType 'bcptApp' } } finally { diff --git a/Actions/DownloadPreviousRelease/action.yaml b/Actions/DownloadPreviousRelease/action.yaml index 523abc9eeb..9ce7c920b4 100644 --- a/Actions/DownloadPreviousRelease/action.yaml +++ b/Actions/DownloadPreviousRelease/action.yaml @@ -1,11 +1,14 @@ name: Download Previous Release author: Microsoft Corporation -description: Downloads the latest release apps for use as a baseline in AppSourceCop validation and upgrade testing. inputs: shell: description: Shell in which you want to run the action (powershell or pwsh) required: false default: powershell + token: + description: The GitHub token running the action + required: false + default: ${{ github.token }} project: description: Project folder required: false @@ -17,14 +20,14 @@ outputs: runs: using: composite steps: - - name: Download Previous Release + - name: run shell: ${{ inputs.shell }} id: DownloadPreviousRelease env: - _token: ${{ github.token }} + _token: ${{ inputs.token }} _project: ${{ inputs.project }} run: | - ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "Download Previous Release" -Action { + ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "DownloadPreviousRelease" -Action { ${{ github.action_path }}/DownloadPreviousRelease.ps1 -token $ENV:_token -project $ENV:_project } branding: From 1a8e85f11d6d52b9dddc03c0cf3c904b76043f40 Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Fri, 27 Mar 2026 08:05:28 +0100 Subject: [PATCH 5/7] Update previousApps --- Actions/.Modules/CompileFromWorkspace.psm1 | 4 ++-- Actions/CompileApps/Compile.ps1 | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Actions/.Modules/CompileFromWorkspace.psm1 b/Actions/.Modules/CompileFromWorkspace.psm1 index a7bf94615d..ca5b23441c 100644 --- a/Actions/.Modules/CompileFromWorkspace.psm1 +++ b/Actions/.Modules/CompileFromWorkspace.psm1 @@ -866,8 +866,8 @@ function New-AppSourceCopJson { param( [Parameter(Mandatory = $true)] [string[]] $AppFolders, - [Parameter(Mandatory = $true)] - [string[]] $PreviousApps, + [Parameter(Mandatory = $false)] + [string[]] $PreviousApps = @(), [Parameter(Mandatory = $true)] [string] $CompilerFolder, [Parameter(Mandatory = $true)] diff --git a/Actions/CompileApps/Compile.ps1 b/Actions/CompileApps/Compile.ps1 index a7793fe19e..653c4ef4b8 100644 --- a/Actions/CompileApps/Compile.ps1 +++ b/Actions/CompileApps/Compile.ps1 @@ -119,19 +119,22 @@ try { Write-Host "Incremental builds based on modified apps is not yet implemented." } - if ($settings.enableAppSourceCop -and $previousAppsPath -and (Test-Path $previousAppsPath)) { + $previousApps = @() + if ($previousAppsPath -and (Test-Path $previousAppsPath)) { $previousApps = @(Get-ChildItem -Path $previousAppsPath -Recurse -Filter "*.app" | ForEach-Object { $_.FullName }) if ($previousApps.Count -gt 0) { # Copy previous apps to the package cache so AppSourceCop can resolve them $previousApps | ForEach-Object { Copy-Item -Path $_ -Destination $packageCachePath -Force } - - # Generate AppSourceCop.json files for app folders - New-AppSourceCopJson -AppFolders $settings.appFolders -PreviousApps $previousApps -CompilerFolder $compilerFolder -Settings $settings } } + if ($settings.enableAppSourceCop) { + # Generate AppSourceCop.json files for app folders with baseline version and settings + New-AppSourceCopJson -AppFolders $settings.appFolders -PreviousApps $previousApps -CompilerFolder $compilerFolder -Settings $settings + } + # Update the app jsons with version number (and other properties) from the app manifest files Update-AppJsonProperties -Folders ($settings.appFolders + $settings.testFolders + $settings.bcptTestFolders) ` -MajorMinorVersion $versionNumber.MajorMinorVersion -BuildNumber $versionNumber.BuildNumber -RevisionNumber $versionNumber.RevisionNumber ` From 6316eb8e1efe82f94b597de3605c2d9455eee24e Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Fri, 27 Mar 2026 08:37:55 +0100 Subject: [PATCH 6/7] Suggestions --- Actions/.Modules/CompileFromWorkspace.psm1 | 6 +++--- Actions/RunPipeline/RunPipeline.ps1 | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Actions/.Modules/CompileFromWorkspace.psm1 b/Actions/.Modules/CompileFromWorkspace.psm1 index ca5b23441c..e67827190e 100644 --- a/Actions/.Modules/CompileFromWorkspace.psm1 +++ b/Actions/.Modules/CompileFromWorkspace.psm1 @@ -851,8 +851,8 @@ function New-BuildOutputFile { Generates AppSourceCop.json files for app folders with baseline version information. .DESCRIPTION For each app folder, creates an AppSourceCop.json file containing the previous version - of the app as a baseline for breaking change detection. Also includes mandatory affixes, - supported countries, and obsolete tag settings from the project settings. + of the app as a baseline for breaking change detection. Also includes mandatory affixes + and obsolete tag settings from the project settings. .PARAMETER AppFolders Array of app folder paths to generate AppSourceCop.json for. .PARAMETER PreviousApps @@ -917,7 +917,7 @@ function New-AppSourceCopJson { if ($appSourceCopJson.Count -gt 0) { Write-Host "Creating AppSourceCop.json for $folder" - $appSourceCopJson | ConvertTo-Json -Depth 99 | Set-Content $appSourceCopJsonFile + $appSourceCopJson | ConvertTo-Json -Depth 99 | Set-Content -Encoding UTF8 $appSourceCopJsonFile } elseif (Test-Path $appSourceCopJsonFile) { Remove-Item $appSourceCopJsonFile -Force diff --git a/Actions/RunPipeline/RunPipeline.ps1 b/Actions/RunPipeline/RunPipeline.ps1 index 3e4717ada0..20c9d438b7 100644 --- a/Actions/RunPipeline/RunPipeline.ps1 +++ b/Actions/RunPipeline/RunPipeline.ps1 @@ -275,7 +275,11 @@ try { if ($previousAppsPath -and (Test-Path $previousAppsPath)) { # Use previously downloaded release apps $previousApps = @(Get-ChildItem -Path $previousAppsPath -Recurse -Filter "*.app" | ForEach-Object { $_.FullName }) - Write-Host "Using $($previousApps.Count) previous release app(s) from $previousAppsPath" + if ($previousApps.Count -gt 0) { + Write-Host "Using $($previousApps.Count) previous release app(s) from $previousAppsPath" + } else { + OutputWarning -message "No .app files found in '$previousAppsPath' for upgrade testing." + } } else { OutputWarning -message "Could not locate previous release apps for upgrade testing." } From 6608363722bd8b39bc7df608252afa4f2b3c31a2 Mon Sep 17 00:00:00 2001 From: aholstrup1 Date: Fri, 27 Mar 2026 08:42:06 +0100 Subject: [PATCH 7/7] Suggestions --- Actions/DownloadPreviousRelease/README.md | 2 +- Actions/DownloadPreviousRelease/action.yaml | 2 +- Tests/DownloadPreviousRelease.Action.Test.ps1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Actions/DownloadPreviousRelease/README.md b/Actions/DownloadPreviousRelease/README.md index 5de43a7c59..ae7d3e3a17 100644 --- a/Actions/DownloadPreviousRelease/README.md +++ b/Actions/DownloadPreviousRelease/README.md @@ -23,4 +23,4 @@ Downloads the latest release apps for use as a baseline in AppSourceCop validati | Name | Description | | :-- | :-- | -| PreviousAppsPath | Path to the folder containing the downloaded previous release apps. Empty if no release was found. | +| PreviousAppsPath | Path to the folder containing the downloaded previous release apps. | diff --git a/Actions/DownloadPreviousRelease/action.yaml b/Actions/DownloadPreviousRelease/action.yaml index 9ce7c920b4..de2ac603af 100644 --- a/Actions/DownloadPreviousRelease/action.yaml +++ b/Actions/DownloadPreviousRelease/action.yaml @@ -15,7 +15,7 @@ inputs: default: '.' outputs: PreviousAppsPath: - description: Path to the folder containing the downloaded previous release apps. Empty if no release was found. + description: Path to the folder containing the downloaded previous release apps. value: ${{ steps.DownloadPreviousRelease.outputs.PreviousAppsPath }} runs: using: composite diff --git a/Tests/DownloadPreviousRelease.Action.Test.ps1 b/Tests/DownloadPreviousRelease.Action.Test.ps1 index 7733c9e597..e1008f0722 100644 --- a/Tests/DownloadPreviousRelease.Action.Test.ps1 +++ b/Tests/DownloadPreviousRelease.Action.Test.ps1 @@ -19,7 +19,7 @@ Describe "DownloadPreviousRelease Action Tests" { It 'Test action.yaml matches script' { $outputs = [ordered]@{ - "PreviousAppsPath" = "Path to the folder containing the downloaded previous release apps. Empty if no release was found." + "PreviousAppsPath" = "Path to the folder containing the downloaded previous release apps." } YamlTest -scriptRoot $scriptRoot -actionName $actionName -actionScript $actionScript -outputs $outputs }