Skip to content

Commit

Permalink
Fix project dependencies (microsoft#581)
Browse files Browse the repository at this point in the history
_**Problem**_:
Due the way app folders (`appFolders`, `testFolders` etc.) are resolved,
when using wild characters for specifying the app folders, the project
dependencies are not determined correctly.

Example:

https://github.com/microsoft/ALAppExtensions/blob/builds/22.3.998.0/System%20Application%20Modules/.AL-Go/settings.json
```
{
    "appFolders":  [
        "..\\Modules\\System\\*"
    ],
    "testFolders":  [
        "..\\Modules\\System Test Libraries\\*",
        "..\\Modules\\System Tests\\*"
    ],
    "enableAppSourceCop": false,
    "enablePerTenantExtensionCop":  false,
    "doNotBuildTests": true
}
```


https://github.com/microsoft/ALAppExtensions/actions/runs/5346147623/jobs/9692747522#step:6:200
> - Analyzing project: System Application Modules
Applying settings from
D:\a\ALAppExtensions\ALAppExtensions\.github\AL-Go-Settings.json
Applying settings from D:\a\ALAppExtensions\ALAppExtensions\System
Application Modules\.AL-Go\settings.json
Warning: Folder ..\Modules\System\* doesn't exist, skipping.
Warning: Folder ..\Modules\System Test Libraries\* doesn't exist,
skipping.
Warning: Folder ..\Modules\System Tests\* doesn't exist, skipping.
No apps or tests folders found for project System Application Modules.
Scanning for apps in the project folder.

That way there is a missing dependency from "System Application Modules"
to "Test Framework":
> ProjectDependenciesJson={"System Application":["Test
Framework"],"System Application Modules":[],"Test Framework":[],"Test
Stability Tools":[]}


_**Solution**_:
Unify the way app folders are resolved and add tests to that
functionality.

---------

Co-authored-by: Freddy Kristiansen <[email protected]>
  • Loading branch information
mazhelez and freddydk committed Jul 25, 2023
1 parent 9b34767 commit 98d4f7d
Show file tree
Hide file tree
Showing 2 changed files with 322 additions and 96 deletions.
224 changes: 128 additions & 96 deletions Actions/AL-Go-Helper.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,92 @@ function ExcludeUnneededApps {
}
}

function ResolveProjectFolders {
Param(
[string] $project,
[string] $baseFolder,
[ref] $projectSettings
)
$projectPath = Join-Path $baseFolder $project -Resolve

Push-Location $projectPath

try{
$appFolders = $projectSettings.Value.appFolders
$testFolders = $projectSettings.Value.testFolders
$bcptTestFolders = $projectSettings.Value.bcptTestFolders

# If no app folders are specified in AL-Go settings, check for AL apps in the project folder
if (!$appFolders -and !$testFolders -and !$bcptTestFolders) {
Get-ChildItem -Path $projectPath -Recurse | Where-Object { $_.PSIsContainer -and (Test-Path -Path (Join-Path $_.FullName "app.json")) } | ForEach-Object {
$aLProjectFolder = $_
$appJson = Get-Content (Join-Path $aLProjectFolder.FullName "app.json") -Encoding UTF8 | ConvertFrom-Json

$isTestApp = $false
$isBcptTestApp = $false

# if an AL app has a dependency to a test app, it is a test app
# if an AL app has a dependency to an app from the performance toolkit apps, it is a bcpt test app
if ($appJson.PSObject.Properties.Name -eq "dependencies") {
$appJson.dependencies | ForEach-Object {
$id = $_.Id
if ($_.PSObject.Properties.Name -eq "AppId") {
$id = $_.AppId
}

# Check if the app is a test app or a bcpt app
if ($performanceToolkitApps.Contains($id)) {
$isBcptTestApp = $true
}
elseif ($testRunnerApps.Contains($id)) {
$isTestApp = $true
}
}

}

# Folders are relative to the project folder
$appFolder = Resolve-Path -Path $aLProjectFolder.FullName -Relative
switch ($true) {
$isTestApp { $testFolders += @($appFolder) }
$isBcptTestApp { $bcptTestFolders += @($appFolder) }
Default { $appFolders += @($appFolder) }
}
}
}

# Filter out app folders that doesn't contain an app.json file
function FilterFolders($projectPath, $folders) {
if(!$folders) {
return @()
}

$resolvedPaths = @()

foreach($folder in $folders) {
$aLProjectFolder = Join-Path $projectPath $folder
$resolvedALProjectsPaths = Resolve-Path $aLProjectFolder -Relative -ErrorAction Ignore | Where-Object { Test-Path (Join-Path $_ 'app.json') }

if($resolvedALProjectsPaths) {
$resolvedPaths += @($resolvedALProjectsPaths)
}
else {
OutputWarning "The folder '$folder' for project '$project' cannot be resolved or does not contain an app.json file. Skipping."
}
}

return $resolvedPaths
}

$projectSettings.Value.appFolders = @(FilterFolders $projectPath $appFolders)
$projectSettings.Value.testFolders = @(FilterFolders $projectPath $testFolders)
$projectSettings.Value.bcptTestFolders = @(FilterFolders $projectPath $bcptTestFolders)
}
finally {
Pop-Location
}
}

function AnalyzeRepo {
Param(
[hashTable] $settings,
Expand All @@ -672,7 +758,6 @@ function AnalyzeRepo {
}

$projectPath = Join-Path $baseFolder $project -Resolve
Set-Location $projectPath

# Check applicationDependency
[Version]$settings.applicationDependency | Out-null
Expand Down Expand Up @@ -704,39 +789,7 @@ function AnalyzeRepo {
throw "The type, specified in $RepoSettingsFile, must be either 'PTE' or 'AppSource App'. It is '$($settings.type)'."
}

if (-not (@($settings.appFolders)+@($settings.testFolders)+@($settings.bcptTestFolders))) {
Get-ChildItem -Path $projectPath | Where-Object { $_.PSIsContainer -and (Test-Path -Path (Join-Path $_.FullName "app.json")) } | ForEach-Object {
$folder = $_
$appJson = Get-Content (Join-Path $folder.FullName "app.json") -Encoding UTF8 | ConvertFrom-Json
$isTestApp = $false
$isBcptTestApp = $false
if ($appJson.PSObject.Properties.Name -eq "dependencies") {
$appJson.dependencies | ForEach-Object {
if ($_.PSObject.Properties.Name -eq "AppId") {
$id = $_.AppId
}
else {
$id = $_.Id
}
if ($performanceToolkitApps.Contains($id)) {
$isBcptTestApp = $true
}
elseif ($testRunnerApps.Contains($id)) {
$isTestApp = $true
}
}
}
if ($isBcptTestApp) {
$settings.bcptTestFolders += @($_.Name)
}
elseif ($isTestApp) {
$settings.testFolders += @($_.Name)
}
else {
$settings.appFolders += @($_.Name)
}
}
}
ResolveProjectFolders -baseFolder $baseFolder -project $project -projectSettings ([ref] $settings)

Write-Host "Checking appFolders, testFolders and bcptTestFolders"
$dependencies = [ordered]@{}
Expand All @@ -760,6 +813,7 @@ function AnalyzeRepo {
else {
throw "Internal error"
}

$folders | ForEach-Object {
$folderName = $_
$folder = Join-Path $projectPath $folderName
Expand All @@ -780,59 +834,44 @@ function AnalyzeRepo {
$enumerate = $false
}
if ($enumerate) {
$expandFolders = @(Get-Item $appJsonFile -ErrorAction SilentlyContinue | ForEach-Object { Resolve-Path -Relative $_.Directory })
if ($appFolder) {
$settings.appFolders = @($settings.appFolders | Where-Object { $_ -ne $folderName }) + $expandFolders
}
elseif ($testFolder) {
$settings.testFolders = @($settings.testFolders | Where-Object { $_ -ne $folderName }) + $expandFolders
}
elseif ($bcptTestFolder) {
$settings.bcptTestFolders = @($settings.bcptTestFolders | Where-Object { $_ -ne $folderName }) + $expandFolders
if ($dependencies.Contains($folderName)) {
throw "$descr $folderName, specified in $ALGoSettingsFile, is specified more than once."
}
$expandFolders | ForEach-Object {
$folderName = $_
$folder = Join-Path $projectPath $folderName
$appJsonFile = Join-Path $folder "app.json"
if ($dependencies.Contains($folderName)) {
throw "$descr $folderName, specified in $ALGoSettingsFile, is specified more than once."
$dependencies.Add($folderName, @())
try {
$appJson = Get-Content $appJsonFile -Encoding UTF8 | ConvertFrom-Json
if ($appIdFolders.Contains($appJson.Id)) {
throw "$descr $folderName contains a duplicate AppId ($($appIdFolders."$($appJson.Id)"))"
}
$dependencies.Add($folderName, @())
try {
$appJson = Get-Content $appJsonFile -Encoding UTF8 | ConvertFrom-Json
if ($appIdFolders.Contains($appJson.Id)) {
throw "$descr $folderName contains a duplicate AppId ($($appIdFolders."$($appJson.Id)"))"
}
$appIdFolders.Add($appJson.Id, $folderName)
if ($appJson.PSObject.Properties.Name -eq 'Dependencies') {
$appJson.dependencies | ForEach-Object {
if ($_.PSObject.Properties.Name -eq "AppId") {
$id = $_.AppId
}
else {
$id = $_.Id
}
if ($id -eq $applicationAppId) {
if ([Version]$_.Version -gt [Version]$settings.applicationDependency) {
$settings.applicationDependency = $appDep
}
}
else {
$dependencies."$folderName" += @( [ordered]@{ "id" = $id; "version" = $_.version } )
$appIdFolders.Add($appJson.Id, $folderName)
if ($appJson.PSObject.Properties.Name -eq 'Dependencies') {
$appJson.dependencies | ForEach-Object {
if ($_.PSObject.Properties.Name -eq "AppId") {
$id = $_.AppId
}
else {
$id = $_.Id
}
if ($id -eq $applicationAppId) {
if ([Version]$_.Version -gt [Version]$settings.applicationDependency) {
$settings.applicationDependency = $appDep
}
}
}
if ($appJson.PSObject.Properties.Name -eq 'Application') {
$appDep = $appJson.application
if ([Version]$appDep -gt [Version]$settings.applicationDependency) {
$settings.applicationDependency = $appDep
else {
$dependencies."$folderName" += @( [ordered]@{ "id" = $id; "version" = $_.version } )
}
}
}
catch {
throw "$descr $folderName, specified in $ALGoSettingsFile, contains a corrupt app.json file. Error is $($_.Exception.Message)."
if ($appJson.PSObject.Properties.Name -eq 'Application') {
$appDep = $appJson.application
if ([Version]$appDep -gt [Version]$settings.applicationDependency) {
$settings.applicationDependency = $appDep
}
}
}
catch {
throw "$descr $folderName, specified in $ALGoSettingsFile, contains a corrupt app.json file. Error is $($_.Exception.Message)."
}
}
}
}
Expand Down Expand Up @@ -1790,26 +1829,19 @@ Function AnalyzeProjectDependencies {
$project = $_
Write-Host "- Analyzing project: $project"

# Read project settings
$projectSettings = ReadSettings -baseFolder $baseFolder -project $project

# Filter out app folders that doesn't contain an app.json file
$folders = @($projectSettings.appFolders) + @($projectSettings.testFolders) + @($projectSettings.bcptTestFolders) | ForEach-Object {
$projectFolder = Join-Path $project $_
if (!(Test-Path $projectFolder -PathType Container)) {
Write-Host "::Warning::Folder $_ doesn't exist, skipping."
}
elseif (!(Test-Path (Join-Path $projectFolder 'app.json'))) {
Write-Host "::Warning::Folder $_ doesn't contain an app.json file, skipping."
}
else {
Resolve-Path $projectFolder -Relative
$projectSettings = ReadSettings -project $project -baseFolder $baseFolder
ResolveProjectFolders -baseFolder $baseFolder -project $project -projectSettings ([ref] $projectSettings)

# App folders are relative to the AL-Go project folder. Convert them to relative to the base folder
Push-Location $baseFolder
try {
$projectPath = Join-Path $baseFolder $project
$folders = @($projectSettings.appFolders) + @($projectSettings.testFolders) + @($projectSettings.bcptTestFolders) | ForEach-Object {
return (Resolve-Path (Join-Path $projectPath $_) -Relative)
}
}
# Default to scanning the project folder if no app folders are specified
if (-not $folders) {
Write-Host "No apps or tests folders found for project $project. Scanning for apps in the project folder."
$folders = @(Get-ChildItem -Path (Join-Path $baseFolder $project) -Recurse | Where-Object { $_.PSIsContainer -and (Test-Path (Join-Path $_.FullName 'app.json')) } | ForEach-Object { $_.FullName.Substring($baseFolder.Length+1) } )
finally {
Pop-Location
}

Write-Host "Folders containing apps are $($folders -join ',' )"
Expand Down
Loading

0 comments on commit 98d4f7d

Please sign in to comment.