diff --git a/Actions/.Modules/CompileFromWorkspace.psm1 b/Actions/.Modules/CompileFromWorkspace.psm1
index e6afb5d70..dda46c03b 100644
--- a/Actions/.Modules/CompileFromWorkspace.psm1
+++ b/Actions/.Modules/CompileFromWorkspace.psm1
@@ -45,13 +45,21 @@ function Get-CodeAnalyzers {
Gets the list of custom code analyzers to use for compilation.
.DESCRIPTION
Returns an array of custom code analyzer paths based on the settings provided.
- If the custom code cop is a URL, it will be downloaded to the compiler folder and the local path will be returned.
+ URL-based entries are downloaded to the compiler folder. Relative file paths
+ are resolved against the project folder so that workspace compilation passes
+ rooted paths to altool/alc.exe (altool only resolves the first comma-separated
+ --customanalyzers entry against the project root, so relative paths beyond the
+ first one fail to load).
.PARAMETER Settings
Hashtable containing the build settings with custom code cop paths or URLs.
.PARAMETER CompilerFolder
- The folder where the AL compiler tool is located, used for downloading custom analyzers if URLs are provided.
+ The folder where the AL compiler tool is located, used for downloading custom
+ analyzers if URLs are provided.
+.PARAMETER ProjectFolder
+ Folder used as the base for resolving relative customCodeCops entries.
+ Defaults to the current location, which Compile.ps1 sets to the project folder.
.OUTPUTS
- Array of custom analyzer paths to use for compilation.
+ Array of absolute custom analyzer paths to use for compilation.
#>
function Get-CustomAnalyzers {
param(
@@ -59,7 +67,10 @@ function Get-CustomAnalyzers {
[hashtable] $Settings,
[Parameter(Mandatory = $true)]
- [string] $CompilerFolder
+ [string] $CompilerFolder,
+
+ [Parameter(Mandatory = $false)]
+ [string] $ProjectFolder = (Get-Location).Path
)
$analyzers = @()
@@ -79,9 +90,15 @@ function Get-CustomAnalyzers {
}
$analyzers += $analyzerFileName
}
- else {
+ elseif ([System.IO.Path]::IsPathRooted($customCodeCop)) {
$analyzers += $customCodeCop
}
+ else {
+ # Resolve relative paths against the project folder. Use GetFullPath so
+ # this works whether or not the file exists yet (e.g. a PreCompileApp
+ # override may download it later). Combine first so we are PS5-compatible.
+ $analyzers += [System.IO.Path]::GetFullPath((Join-Path $ProjectFolder $customCodeCop))
+ }
}
return $analyzers
diff --git a/Actions/.Modules/settings.schema.json b/Actions/.Modules/settings.schema.json
index 2c90ab3b2..8cf29ab0d 100644
--- a/Actions/.Modules/settings.schema.json
+++ b/Actions/.Modules/settings.schema.json
@@ -295,7 +295,7 @@
"required": [],
"properties": {}
},
- "description": "See https://aka.ms/ALGoSettings#customcodecops"
+ "description": "Array of paths or URLs to custom Code Cop DLLs to enable when building. Relative paths are resolved against the project folder (the folder containing .AL-Go/settings.json). See https://aka.ms/ALGoSettings#customcodecops"
},
"preprocessorSymbols": {
"type": "array",
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index ab0eec036..bfc6927cb 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -8,6 +8,7 @@ The `DownloadProjectDependencies` action now downloads only artifacts from depen
- Issue 2204 - Workspace compilation ignores vsixFile setting
- Issue 2211 - Cannot create a release if a project contains only test apps
- Issue 2214 - Workspace compilation not working with external dependencies
+- Issue 2235 - Workspace compilation: only the first `customCodeCops` entry resolved when multiple relative paths were configured. Relative `customCodeCops` paths are now resolved against the project folder before being passed to the compiler.
## v9.0
diff --git a/Scenarios/settings.md b/Scenarios/settings.md
index 6a54b92c4..2339af2b5 100644
--- a/Scenarios/settings.md
+++ b/Scenarios/settings.md
@@ -111,7 +111,7 @@ The repository settings are only read from the repository settings file (.github
| installOnlyReferencedApps | By default, only the apps referenced in the dependency chain of your apps will be installed when inspecting the settings: InstallApps, InstallTestApps and appDependencyProbingPath. If you change this setting to false, all apps found will be installed. | true |
| enableCodeCop | If enableCodeCop is set to true, the CI/CD workflow will enable the CodeCop analyzer when building. | false |
| enableUICop | If enableUICop is set to true, the CI/CD workflow will enable the UICop analyzer when building. | false |
-| customCodeCops | CustomCodeCops is an array of paths or URLs to custom Code Cop DLLs you want to enable when building. | [ ] |
+| customCodeCops | CustomCodeCops is an array of paths or URLs to custom Code Cop DLLs you want to enable when building. Relative paths are resolved against the project folder (the folder containing `.AL-Go/settings.json`). | [ ] |
| features | Features is an array of compiler features to enable when building. Possible values include **LcgTranslationFile**, **TranslationFile**, and **GenerateCaptions**. | [ ] |
| enableCodeAnalyzersOnTestApps| If enableCodeAnalyzersOnTestApps is set to true, the code analyzers will be enabled when building test apps as well. | false |
| trackALAlertsInGitHub | If trackALAlertsInGitHub is set to true, AL code analysis results will be uploaded and tracked in the GitHub security tab. Additionally, if Advanced Security is enabled in the repo, new AL code alerts will be posted in PRs that introduce them. This setting must be enabled on the repo level, but can be optionally disabled per project.
**Note:** AL Alerts are only enabled for warnings at the moment. Support for displaying errors will come in a future release | false |
diff --git a/Tests/CompileFromWorkspace.Test.ps1 b/Tests/CompileFromWorkspace.Test.ps1
index aabce0a64..12e928205 100644
--- a/Tests/CompileFromWorkspace.Test.ps1
+++ b/Tests/CompileFromWorkspace.Test.ps1
@@ -700,11 +700,61 @@ Write-Host "Post-compile: $($appFiles.Count) apps"
@($result).Count | Should -Be 0
}
- It 'Returns local paths as-is' {
- $result = Get-CustomAnalyzers -Settings @{ CustomCodeCops = @('C:\analyzers\MyAnalyzer.dll') } -CompilerFolder $TestDrive
+ It 'Returns rooted local paths as-is' {
+ $rooted = Join-Path $TestDrive 'analyzers/MyAnalyzer.dll'
+ $result = Get-CustomAnalyzers -Settings @{ CustomCodeCops = @($rooted) } -CompilerFolder $TestDrive
@($result).Count | Should -Be 1
- $result | Should -Contain 'C:\analyzers\MyAnalyzer.dll'
+ $result | Should -Contain $rooted
+ }
+
+ It 'Resolves relative paths against the project folder' {
+ $projectFolder = Join-Path $TestDrive 'project-relative'
+ New-Item -Path $projectFolder -ItemType Directory -Force | Out-Null
+
+ $settings = @{ CustomCodeCops = @('.alcops/A.dll', '.alcops/B.dll') }
+ $result = @(Get-CustomAnalyzers -Settings $settings -CompilerFolder $TestDrive -ProjectFolder $projectFolder)
+
+ $result.Count | Should -Be 2
+ foreach ($path in $result) {
+ [System.IO.Path]::IsPathRooted($path) | Should -BeTrue
+ $path | Should -BeLike "$projectFolder*"
+ }
+ $result[0] | Should -BeLike '*A.dll'
+ $result[1] | Should -BeLike '*B.dll'
+ }
+
+ It 'Resolves relative paths even when the analyzer file does not exist yet' {
+ # PreCompileApp overrides may download analyzer DLLs after Get-CustomAnalyzers
+ # has been called, so resolution must not require the file to exist.
+ $projectFolder = Join-Path $TestDrive 'project-missing'
+ New-Item -Path $projectFolder -ItemType Directory -Force | Out-Null
+
+ $result = @(Get-CustomAnalyzers `
+ -Settings @{ CustomCodeCops = @('.alcops/Not.Yet.Downloaded.dll') } `
+ -CompilerFolder $TestDrive `
+ -ProjectFolder $projectFolder)
+
+ $result.Count | Should -Be 1
+ [System.IO.Path]::IsPathRooted($result[0]) | Should -BeTrue
+ $result[0] | Should -BeLike '*Not.Yet.Downloaded.dll'
+ }
+
+ It 'Defaults ProjectFolder to the current location when not specified' {
+ $projectFolder = Join-Path $TestDrive 'project-default'
+ New-Item -Path $projectFolder -ItemType Directory -Force | Out-Null
+
+ Push-Location $projectFolder
+ try {
+ $result = @(Get-CustomAnalyzers `
+ -Settings @{ CustomCodeCops = @('.alcops/X.dll') } `
+ -CompilerFolder $TestDrive)
+ } finally {
+ Pop-Location
+ }
+
+ $result.Count | Should -Be 1
+ $result[0] | Should -BeLike "$projectFolder*X.dll"
}
It 'Downloads URL-based analyzers to compiler folder' {