Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion Actions/.Modules/CompileFromWorkspace.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ function Get-ALTool {
Path to the output folder for compiled .app files. Defaults to PackageCachePath.
.PARAMETER LogDirectory
Path to the directory for compilation log files.
.PARAMETER ErrorLogDirectory
Path to the directory where per-project error log files (SARIF-style diagnostics) are written. When set (and supported by the compiler), each project's alc invocation produces a '<project>_<timestamp>.errorLog.json' file used to surface AL alerts in GitHub.
.PARAMETER MaxCpuCount
Maximum number of parallel compilation processes. Defaults to 1.
.PARAMETER AssemblyProbingPaths
Expand Down Expand Up @@ -287,6 +289,8 @@ function Build-AppsInWorkspace {
[string]$OutFolder,
[Parameter(Mandatory = $false)]
[string]$LogDirectory,
[Parameter(Mandatory = $false)]
[string]$ErrorLogDirectory,
# Optional parameters
[Parameter(Mandatory = $false)]
[int]$MaxCpuCount = 1,
Expand Down Expand Up @@ -359,6 +363,7 @@ function Build-AppsInWorkspace {
PackageCachePath = $PackageCachePath
OutFolder = $OutputFolder
LogDirectory = $LogDirectory
ErrorLogDirectory = $ErrorLogDirectory
AssemblyProbingPaths = $AssemblyProbingPaths
Analyzers = $Analyzers
CustomAnalyzers = $CustomAnalyzers
Expand Down Expand Up @@ -445,11 +450,47 @@ function Copy-CompiledAppsToOutput {
return $generatedAppFiles
}

function CompileAppsInWorkspace {
<#
.SYNOPSIS
Determines whether the AL tool's 'workspace compile' command supports a given option.
.DESCRIPTION
Probes 'altool workspace compile --help' and checks whether the specified option name appears in the output.
Used to remain compatible with compiler versions that predate newly introduced options.
.PARAMETER ALToolPath
Path to the AL tool executable (altool).
.PARAMETER Option
The option name to look for (without leading dashes), e.g. 'errorlogdirectory'.
.OUTPUTS
Boolean indicating whether the option is supported.
#>
function Test-ALToolWorkspaceCompileSupportsOption {
Comment thread
aholstrup1 marked this conversation as resolved.
param(
[Parameter(Mandatory = $true)]
[string]$ALToolPath,
[Parameter(Mandatory = $true)]
[string]$Option
)

try {
$compileHelp = & $ALToolPath workspace compile --help 2>&1 | Out-String
# A native executable does not throw merely because it exits non-zero, so the exit code must be
# checked explicitly. If the probe failed, treat the option as unsupported so the caller takes the
# promised warn-and-skip fallback instead of parsing error/usage output as a positive match.
if ($LASTEXITCODE -ne 0) {
OutputDebug -message "Probing altool workspace compile --help for option '$Option' returned exit code $LASTEXITCODE; treating the option as unsupported."
return $false
}
return ($compileHelp -match [regex]::Escape($Option))
} catch {
OutputDebug -message "Failed to probe altool workspace compile --help for option '$Option': $_"
return $false
}
}

function CompileAppsInWorkspace {
param(
[Parameter(Mandatory = $true)]
[string]$ALToolPath,
[Parameter(Mandatory = $true)]
[string]$WorkspaceFile,

Expand Down Expand Up @@ -499,6 +540,9 @@ function CompileAppsInWorkspace {
[Parameter(Mandatory = $false)]
[string]$LogDirectory,

[Parameter(Mandatory = $false)]
[string]$ErrorLogDirectory,

[Parameter(Mandatory = $false)]
[string]$OutFolder
)
Expand Down Expand Up @@ -602,6 +646,18 @@ function CompileAppsInWorkspace {
$arguments += $defaultLogDir
}

if ($ErrorLogDirectory) {
# The --errorlogdirectory option emits one '<project>_<timestamp>.errorLog.json' per project,
# which ProcessALCodeAnalysisLogs consumes to surface AL alerts in GitHub.
# It may not exist in the consumed compiler version yet, so probe --help before using it.
if (Test-ALToolWorkspaceCompileSupportsOption -ALToolPath $ALToolPath -Option 'errorlogdirectory') {
$arguments += "--errorlogdirectory"
$arguments += $ErrorLogDirectory
} else {
OutputWarning "--errorlogdirectory is not supported by this compiler version and will be ignored. AL code alerts will not be generated for the workspace compilation build."
}
}

$generatedAppFiles = @()
$originalEncoding = [Console]::OutputEncoding
try {
Expand Down
11 changes: 11 additions & 0 deletions Actions/CompileApps/Compile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,17 @@ try {
$allAnalyzers = @(Get-CodeAnalyzers -Settings $settings)
$allCustomAnalyzers = @(Get-CustomAnalyzers -Settings $settings -CompilerFolder $compilerFolder)

# When AL alert tracking is enabled, direct per-project error logs to the same folder the classic
# Run-AlPipeline path uses (.buildartifacts/ErrorLogs), so ProcessALCodeAnalysisLogs and the
# ErrorLogs artifact-publish step pick them up unchanged.
if ($settings.trackALAlertsInGitHub) {
$errorLogsFolder = Join-Path $buildArtifactFolder "ErrorLogs"
if (-not (Test-Path $errorLogsFolder)) {
New-Item $errorLogsFolder -ItemType Directory -Force | Out-Null
}
$buildParams.ErrorLogDirectory = $errorLogsFolder
}

# Start compilation - only compile folders that need building (all in full build, modified-only in incremental)
$appFiles = @()
$testAppFiles = @()
Expand Down
4 changes: 4 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ As part of this, the warning comparison now also parses the raw AL compiler outp

`ProcessALCodeAnalysisLogs` now URI-encodes each segment of the artifact location path when writing SARIF (for example `1.Setup Data/Foo.al` becomes `1.Setup%20Data/Foo.al`). Paths that contain spaces or other characters that are not valid in a URI previously caused `github/codeql-action/upload-sarif` to log "is not a valid URI" warnings and could prevent AL code scanning alerts from mapping to the correct files. The `/` path separators are preserved so the path structure is unchanged.

### AL alerts for the workspace compilation build

The `trackALAlertsInGitHub` setting now also works when `workspaceCompilation` (preview) is enabled. When both are turned on, AL-Go passes `--errorlogdirectory` to `altool workspace compile` so each project emits an `*.errorLog.json` diagnostics file into `.buildartifacts/ErrorLogs/`, which is processed into SARIF and surfaced as code scanning alerts — matching the classic Run-AlPipeline behavior. If the consumed compiler version does not yet support `--errorlogdirectory`, the option is skipped and a warning is logged (the rest of the build is unaffected).

## v9.1

### Resilient Pull Request Status Check for large builds
Expand Down
110 changes: 110 additions & 0 deletions Tests/CompileFromWorkspace.Test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -1302,6 +1302,116 @@ Write-Host "Post-compile: $($appFiles.Count) apps"
$script:capturedArguments | Should -Contain '--logdirectory'
}
}

It 'Includes --errorlogdirectory when ErrorLogDirectory is set and the compiler supports it' {
InModuleScope CompileFromWorkspace {
$script:capturedArguments = @()
$wsFile = Join-Path $TestDrive 'test.code-workspace'
Set-Content -Path $wsFile -Value '{}'
$outDir = Join-Path $TestDrive 'out-args-errorlog1'
New-Item -Path $outDir -ItemType Directory -Force | Out-Null
$errorLogDir = Join-Path $TestDrive 'ErrorLogs'
Mock RunAndCheck {
$script:capturedArguments = $args
}
Mock Copy-CompiledAppsToOutput { return @() }
# Simulate a compiler that advertises the --errorlogdirectory option in its help
Mock Test-ALToolWorkspaceCompileSupportsOption { return $true }
Comment thread
aholstrup1 marked this conversation as resolved.

CompileAppsInWorkspace -ALToolPath 'altool.exe' -WorkspaceFile $wsFile -MaxCpuCount 1 -OutFolder $outDir -PackageCachePath $outDir -ErrorLogDirectory $errorLogDir

$script:capturedArguments | Should -Contain '--errorlogdirectory'
$script:capturedArguments | Should -Contain $errorLogDir
}
}

It 'Omits --errorlogdirectory and warns when the compiler does not support it' {
InModuleScope CompileFromWorkspace {
$script:capturedArguments = @()
$wsFile = Join-Path $TestDrive 'test.code-workspace'
Set-Content -Path $wsFile -Value '{}'
$outDir = Join-Path $TestDrive 'out-args-errorlog2'
New-Item -Path $outDir -ItemType Directory -Force | Out-Null
$errorLogDir = Join-Path $TestDrive 'ErrorLogs'
Mock RunAndCheck {
$script:capturedArguments = $args
}
Mock Copy-CompiledAppsToOutput { return @() }
Mock OutputWarning {}
# Simulate a compiler whose help does not mention the option
Mock Test-ALToolWorkspaceCompileSupportsOption { return $false }

CompileAppsInWorkspace -ALToolPath 'altool.exe' -WorkspaceFile $wsFile -MaxCpuCount 1 -OutFolder $outDir -PackageCachePath $outDir -ErrorLogDirectory $errorLogDir

$script:capturedArguments | Should -Not -Contain '--errorlogdirectory'
Should -Invoke OutputWarning -Times 1
}
}

It 'Omits --errorlogdirectory when ErrorLogDirectory is not set' {
InModuleScope CompileFromWorkspace {
$script:capturedArguments = @()
$wsFile = Join-Path $TestDrive 'test.code-workspace'
Set-Content -Path $wsFile -Value '{}'
$outDir = Join-Path $TestDrive 'out-args-errorlog3'
New-Item -Path $outDir -ItemType Directory -Force | Out-Null
Mock RunAndCheck {
$script:capturedArguments = $args
}
Mock Copy-CompiledAppsToOutput { return @() }

CompileAppsInWorkspace -ALToolPath 'altool.exe' -WorkspaceFile $wsFile -MaxCpuCount 1 -OutFolder $outDir -PackageCachePath $outDir

$script:capturedArguments | Should -Not -Contain '--errorlogdirectory'
}
}
}

Describe 'Test-ALToolWorkspaceCompileSupportsOption' {
BeforeAll {
# The probe invokes '& $ALToolPath workspace compile --help'. We stand in a fake altool as a
# .ps1 script (invoked via the call operator on both PS5 and PS7) whose output and exit code we
# control per test, so the real help-matching and exit-code handling are exercised - not mocked.
$script:fakeAltool = Join-Path $TestDrive "fake-altool.ps1"
}

It 'Returns true when the option appears in the help output and the probe succeeds' {
Set-Content -Path $script:fakeAltool -Value @'
Write-Output "Usage: altool workspace compile [options]"
Write-Output " --errorlogdirectory <dir> Write diagnostics to <dir>"
exit 0
'@
InModuleScope CompileFromWorkspace -Parameters @{ altool = $script:fakeAltool } {
param($altool)
Test-ALToolWorkspaceCompileSupportsOption -ALToolPath $altool -Option 'errorlogdirectory' | Should -BeTrue
}
}

It 'Returns false when the option is absent from the help output' {
Set-Content -Path $script:fakeAltool -Value @'
Write-Output "Usage: altool workspace compile [options]"
Write-Output " --outfolder <dir> Output folder"
exit 0
'@
InModuleScope CompileFromWorkspace -Parameters @{ altool = $script:fakeAltool } {
param($altool)
Test-ALToolWorkspaceCompileSupportsOption -ALToolPath $altool -Option 'errorlogdirectory' | Should -BeFalse
}
}

It 'Returns false when the probe fails (non-zero exit) even if the output contains the option' {
# An older altool may emit usage text mentioning the option while still failing. A non-zero exit
# must win so the caller falls back to warn-and-skip rather than passing an unsupported argument.
Set-Content -Path $script:fakeAltool -Value @'
Write-Output "error: unknown command 'workspace'"
Write-Output "did you mean --errorlogdirectory?"
exit 1
'@
InModuleScope CompileFromWorkspace -Parameters @{ altool = $script:fakeAltool } {
param($altool)
Test-ALToolWorkspaceCompileSupportsOption -ALToolPath $altool -Option 'errorlogdirectory' | Should -BeFalse
}
}
}

Describe 'New-AppSourceCopJson' {
Expand Down
Loading