Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow useProjectDependencies as a project-level setting #1393

Merged
merged 21 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
50 changes: 31 additions & 19 deletions Actions/AL-Go-Helper.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2090,11 +2090,11 @@ function CheckAndCreateProjectFolder {
Function AnalyzeProjectDependencies {
Param(
[string] $baseFolder,
[string[]] $projects,
[ref] $buildAlso,
[ref] $projectDependencies
[string[]] $projects
)

$additionalProjectsToBuild = @{}
$projectDependencies = @{}
$appDependencies = @{}
Write-Host "Analyzing projects in $baseFolder"

Expand Down Expand Up @@ -2123,9 +2123,17 @@ Function AnalyzeProjectDependencies {
$unknownDependencies = @()
$apps = @()
Sort-AppFoldersByDependencies -appFolders $folders -baseFolder $baseFolder -WarningAction SilentlyContinue -unknownDependencies ([ref]$unknownDependencies) -knownApps ([ref]$apps) | Out-Null

# If the project is using project dependencies, add the unknown dependencies to the list of dependencies
# If not, the unknown dependencies are ignored
$dependenciesForProject = @()
if ($projectSettings.useProjectDependencies -eq $true) {
$dependenciesForProject = @($unknownDependencies | ForEach-Object { $_.Split(':')[0] })
}

$appDependencies."$project" = @{
"apps" = $apps
"dependencies" = @($unknownDependencies | ForEach-Object { $_.Split(':')[0] })
"dependencies" = $dependenciesForProject
}
}
# AppDependencies is a hashtable with the following structure
Expand Down Expand Up @@ -2160,42 +2168,42 @@ Function AnalyzeProjectDependencies {
# Add this project and all projects on which that project has a dependency to the list of dependencies for the current project
foreach($depProject in $depProjects) {
$foundDependencies += $depProject
if ($projectDependencies.Value.Keys -contains $depProject) {
$foundDependencies += $projectDependencies.value."$depProject"
if ($projectDependencies.Keys -contains $depProject) {
$foundDependencies += $projectDependencies."$depProject"
}
}
}
$foundDependencies = @($foundDependencies | Select-Object -Unique)
# foundDependencies now contains all projects that the current project has a dependency on
# Update ref variable projectDependencies for this project
if ($projectDependencies.Value.Keys -notcontains $project) {
if ($projectDependencies.Keys -notcontains $project) {
# Loop through the list of projects for which we already built a dependency list
# Update the dependency list for that project if it contains the current project, which might lead to a changed dependency list
# This is needed because we are looping through the projects in a any order
$keys = @($projectDependencies.value.Keys)
$keys = @($projectDependencies.Keys)
foreach($key in $keys) {
if ($projectDependencies.value."$key" -contains $project) {
$projectDeps = @( $projectDependencies.value."$key" )
$projectDependencies.value."$key" = @( @($projectDeps + $foundDependencies) | Select-Object -Unique )
if (Compare-Object -ReferenceObject $projectDependencies.value."$key" -differenceObject $projectDeps) {
if ($projectDependencies."$key" -contains $project) {
$projectDeps = @( $projectDependencies."$key" )
$projectDependencies."$key" = @( @($projectDeps + $foundDependencies) | Select-Object -Unique )
if (Compare-Object -ReferenceObject $projectDependencies."$key" -differenceObject $projectDeps) {
Write-Host "Add ProjectDependencies $($foundDependencies -join ',') to $key"
}
}
}
Write-Host "Set ProjectDependencies for $project to $($foundDependencies -join ',')"
$projectDependencies.value."$project" = $foundDependencies
$projectDependencies."$project" = $foundDependencies
}
if ($foundDependencies) {
Write-Host "Found dependencies to projects: $($foundDependencies -join ", ")"
# Add project to buildAlso for this dependency to ensure that this project also gets build when the dependency is built
# Add project to additionalProjectsToBuild for this dependency to ensure that this project also gets build when the dependency is built
foreach($dependency in $foundDependencies) {
if ($buildAlso.value.Keys -contains $dependency) {
if ($buildAlso.value."$dependency" -notcontains $project) {
$buildAlso.value."$dependency" += @( $project )
if ($additionalProjectsToBuild.Keys -contains $dependency) {
if ($additionalProjectsToBuild."$dependency" -notcontains $project) {
$additionalProjectsToBuild."$dependency" += @( $project )
}
}
else {
$buildAlso.value."$dependency" = @( $project )
$additionalProjectsToBuild."$dependency" = @( $project )
}
}
}
Expand All @@ -2215,7 +2223,11 @@ Function AnalyzeProjectDependencies {
$no++
}

return @($projectsOrder)
return [PSCustomObject]@{
FullProjectsOrder = $projectsOrder
AdditionalProjectsToBuild = $additionalProjectsToBuild
ProjectDependencies = $projectDependencies
}
}

function GetBaseFolder {
Expand Down
5 changes: 2 additions & 3 deletions Actions/CheckForUpdates/CheckForUpdates.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ $templateUrl = $templateUrl -replace "^(https:\/\/)(www\.)(.*)$", '$1$3'
# TemplateUrl is now always a full url + @ and a branch name

# CheckForUpdates will read all AL-Go System files from the Template repository and compare them to the ones in the current repository
# CheckForUpdates will apply changes to the AL-Go System files based on AL-Go repo settings, such as "runs-on", "useProjectDependencies", etc.
# CheckForUpdates will apply changes to the AL-Go System files based on AL-Go repo settings, such as "runs-on" etc.
# if $update is set to Y, CheckForUpdates will also update the AL-Go System files in the current repository using a PR or a direct commit (if $directCommit is set to true)
# if $update is set to N, CheckForUpdates will only check for updates and output a warning if there are updates available
# if $downloadLatest is set to true, CheckForUpdates will download the latest version of the template repository, else it will use the templateSha setting in the .github/AL-Go-Settings file
Expand Down Expand Up @@ -115,11 +115,10 @@ $updateFiles = @()
# $removeFiles will hold an array of files, which needs to be removed
$removeFiles = @()

# If useProjectDependencies is true, we need to calculate the dependency depth for all projects
# Dependency depth determines how many build jobs we need to run sequentially
# Every build job might spin up multiple jobs in parallel to build the projects without unresolved deependencies
$depth = 1
if ($repoSettings.useProjectDependencies -and $projects.Count -gt 1) {
if ($projects.Count -gt 1) {
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath "..\DetermineProjectsToBuild\DetermineProjectsToBuild.psm1" -Resolve) -DisableNameChecking
$allProjects, $projectsToBuild, $projectDependencies, $buildOrder = Get-ProjectsToBuild -baseFolder $baseFolder -buildAllProjects $true -maxBuildDepth 100
$depth = $buildOrder.Count
Expand Down
19 changes: 5 additions & 14 deletions Actions/DetermineProjectsToBuild/DetermineProjectsToBuild.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ function Get-ProjectsToBuild {
Write-Host "Found AL-Go Projects: $($projects -join ', ')"

$projectsToBuild = @()
$projectDependencies = @{}
$projectsOrderToBuild = @()

if ($projects) {
Expand All @@ -238,21 +237,13 @@ function Get-ProjectsToBuild {
$projectsToBuild = @($projects | Where-Object { ShouldBuildProject -baseFolder $baseFolder -project $_ -modifiedFiles $modifiedFilesFullPaths })
}

if($settings.useProjectDependencies) {
$buildAlso = @{}
# Calculate the full projects order
$projectBuildInfo = AnalyzeProjectDependencies -baseFolder $baseFolder -projects $projects

# Calculate the full projects order
$fullProjectsOrder = AnalyzeProjectDependencies -baseFolder $baseFolder -projects $projects -buildAlso ([ref]$buildAlso) -projectDependencies ([ref]$projectDependencies)

$projectsToBuild = @($projectsToBuild | ForEach-Object { $_; if ($buildAlso.Keys -contains $_) { $buildAlso."$_" } } | Select-Object -Unique)
}
else {
# Use a flatten build order (all projects on the same level)
$fullProjectsOrder = @(@{ 'projects' = $projectsToBuild; 'projectsCount' = $projectsToBuild.Count})
}
$projectsToBuild = @($projectsToBuild | ForEach-Object { $_; if ($projectBuildInfo.AdditionalProjectsToBuild.Keys -contains $_) { $projectBuildInfo.AdditionalProjectsToBuild."$_" } } | Select-Object -Unique)

# Create a project order based on the projects to build
foreach($depth in $fullProjectsOrder) {
foreach($depth in $projectBuildInfo.FullProjectsOrder) {
$projectsOnDepth = @($depth.projects | Where-Object { $projectsToBuild -contains $_ })

if ($projectsOnDepth) {
Expand Down Expand Up @@ -281,7 +272,7 @@ function Get-ProjectsToBuild {
throw "The build depth is too deep, the maximum build depth is $maxBuildDepth. You need to run 'Update AL-Go System Files' to update the workflows"
}

return $projects, $projectsToBuild, $projectDependencies, $projectsOrderToBuild
return $projects, $projectsToBuild, $projectBuildInfo.projectDependencies, $projectsOrderToBuild
}
finally {
Pop-Location
Expand Down
104 changes: 104 additions & 0 deletions Tests/DetermineProjectsToBuild.Test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,10 @@ Describe "Get-ProjectsToBuild" {

#Add settings file
$alGoSettings = @{ alwaysBuildAllProjects = $false; projects = @(); powerPlatformSolutionFolder = ''; useProjectDependencies = $true }
New-Item -Path "$baseFolder/.github" -type Directory -Force
$alGoSettings | ConvertTo-Json -Depth 99 -Compress | Out-File (Join-Path $baseFolder ".github/AL-Go-Settings.json") -Encoding UTF8

# Add settings as environment variable to simulate we've run ReadSettings
$env:Settings = ConvertTo-Json $alGoSettings -Depth 99 -Compress

$allProjects, $projectsToBuild, $projectDependencies, $buildOrder = Get-ProjectsToBuild -baseFolder $baseFolder
Expand Down Expand Up @@ -661,6 +665,102 @@ Describe "Get-ProjectsToBuild" {
$buildOrder[1].buildDimensions[0].project | Should -BeExactly "Project2"
}

It 'loads dependent projects correctly, if useProjectDependencies is set to false in a project setting' {
# Add three dependent projects
# Project 1
# Project 2 depends on Project 1 - useProjectDependencies is set to true from the repo settings
# Project 3 depends on Project 1, but has useProjectDependencies set to false in the project settings
$dependecyAppFile = @{ 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 $dependecyAppFile -Depth 10) -type File -Force

$dependantAppFile = @{ id = '83fb8305-4079-415d-a25d-8132f0436fd2'; name = 'Second App'; publisher = 'Contoso'; version = '1.0.0.0'; dependencies = @(@{id = '83fb8305-4079-415d-a25d-8132f0436fd1'; name = 'First App'; publisher = 'Contoso'; version = '1.0.0.0'} ) }
New-Item -Path "$baseFolder/Project2/.AL-Go/settings.json" -type File -Force
New-Item -Path "$baseFolder/Project2/app/app.json" -Value (ConvertTo-Json $dependantAppFile -Depth 10) -type File -Force

# Third project that also depends on the first project, but has useProjectDependencies set to false
$dependantAppFile3 = @{ id = '83fb8305-4079-415d-a25d-8132f0436fd3'; name = 'Third App'; publisher = 'Contoso'; version = '1.0.0.0'; dependencies = @(@{id = '83fb8305-4079-415d-a25d-8132f0436fd1'; name = 'First App'; publisher = 'Contoso'; version = '1.0.0.0'} ) }
New-Item -Path "$baseFolder/Project3/.AL-Go/settings.json" -type File -Force
@{ useProjectDependencies = $false } | ConvertTo-Json -Depth 99 -Compress | Out-File (Join-Path $baseFolder "Project3/.AL-Go/settings.json") -Encoding UTF8
New-Item -Path "$baseFolder/Project3/app/app.json" -Value (ConvertTo-Json $dependantAppFile3 -Depth 10) -type File -Force

#Add settings file
$alGoSettings = @{ alwaysBuildAllProjects = $false; projects = @(); powerPlatformSolutionFolder = ''; useProjectDependencies = $true }
New-Item -Path "$baseFolder/.github" -type Directory -Force
$alGoSettings | ConvertTo-Json -Depth 99 -Compress | Out-File (Join-Path $baseFolder ".github/AL-Go-Settings.json") -Encoding UTF8

# Add settings as environment variable to simulate we've run ReadSettings
$env:Settings = ConvertTo-Json $alGoSettings -Depth 99 -Compress

$allProjects, $projectsToBuild, $projectDependencies, $buildOrder = Get-ProjectsToBuild -baseFolder $baseFolder

$allProjects | Should -BeExactly @("Project1", "Project2", "Project3")
$projectsToBuild | Should -BeExactly @('Project1', 'Project2', 'Project3')

$projectDependencies | Should -BeOfType System.Collections.Hashtable
$projectDependencies['Project1'] | Should -BeExactly @()
$projectDependencies['Project2'] | Should -BeExactly @("Project1")
$projectDependencies['Project3'] | Should -BeExactly @()

# Build order should have the following structure:
#[
#{
# "buildDimensions": [
# {
# "projectName": "Project1",
# "buildMode": "Default",
# "project": "Project1",
# "githubRunnerShell": "powershell",
# "gitHubRunner": "\"windows-latest\""
# },
# {
# "projectName": "Project3",
# "buildMode": "Default",
# "project": "Project3",
# "githubRunnerShell": "powershell",
# "gitHubRunner": "\"windows-latest\""
# }
# ],
# "projectsCount": 2,
# "projects": [
# "Project1",
# "Project3"
# ]
#},
#{
# "buildDimensions": [
# {
# "projectName": "Project2",
# "buildMode": "Default",
# "project": "Project2",
# "githubRunnerShell": "powershell",
# "gitHubRunner": "\"windows-latest\""
# }
# ],
# "projectsCount": 1,
# "projects": [
# "Project2"
# ]
#}
#]
$buildOrder.Count | Should -BeExactly 2
$buildOrder[0] | Should -BeOfType System.Collections.Hashtable
$buildOrder[0].projects | Should -BeExactly @("Project1", "Project3")
$buildOrder[0].projectsCount | Should -BeExactly 2
$buildOrder[0].buildDimensions.Count | Should -BeExactly 2
$buildOrder[0].buildDimensions[0].buildMode | Should -BeExactly "Default"
$buildOrder[0].buildDimensions[0].project | Should -BeExactly "Project1"
$buildOrder[0].buildDimensions[1].buildMode | Should -BeExactly "Default"
$buildOrder[0].buildDimensions[1].project | Should -BeExactly "Project3"

$buildOrder[1] | Should -BeOfType System.Collections.Hashtable
$buildOrder[1].projects | Should -BeExactly @("Project2")
$buildOrder[1].projectsCount | Should -BeExactly 1
$buildOrder[1].buildDimensions.Count | Should -BeExactly 1
$buildOrder[1].buildDimensions[0].buildMode | Should -BeExactly "Default"
$buildOrder[1].buildDimensions[0].project | Should -BeExactly "Project2"
}

It 'throws if the calculated build depth is more than the maximum supported' {
# Two dependent projects
$dependecyAppFile = @{ id = '83fb8305-4079-415d-a25d-8132f0436fd1'; name = 'First App'; publisher = 'Contoso'; version = '1.0.0.0'; dependencies = @() }
Expand All @@ -673,6 +773,10 @@ Describe "Get-ProjectsToBuild" {

#Add settings file
$alGoSettings = @{ alwaysBuildAllProjects = $false; projects = @(); powerPlatformSolutionFolder = ''; useProjectDependencies = $true }
New-Item -Path "$baseFolder/.github" -type Directory -Force
$alGoSettings | ConvertTo-Json -Depth 99 -Compress | Out-File (Join-Path $baseFolder ".github/AL-Go-Settings.json") -Encoding UTF8

# Add settings as environment variable to simulate we've run ReadSettings
$env:Settings = ConvertTo-Json $alGoSettings -Depth 99 -Compress

{ Get-ProjectsToBuild -baseFolder $baseFolder -maxBuildDepth 1 } | Should -Throw "The build depth is too deep, the maximum build depth is 1. You need to run 'Update AL-Go System Files' to update the workflows"
Expand Down
Loading