Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
4f14364
use private containerhelper
freddydk Dec 25, 2025
001c298
check for apps/TestApps
freddydk Dec 25, 2025
bbcbdcd
add concept of Solo projects
freddydk Dec 25, 2025
ad0fb9b
change solo mechanism
freddydk Dec 25, 2025
d27ff30
dump msg
freddydk Dec 25, 2025
b164aea
array
freddydk Dec 25, 2025
45127f4
push to ghcr
freddydk Dec 26, 2025
969a48e
pull
freddydk Dec 26, 2025
55ad671
pull
freddydk Dec 26, 2025
a0755d6
dump fullname
freddydk Dec 26, 2025
80b189a
use full
freddydk Dec 26, 2025
d261c93
lower
freddydk Dec 26, 2025
c5ea8f3
revert
freddydk Dec 27, 2025
371ab33
use bhg containerhelper
freddydk Dec 29, 2025
ec14162
swallow errors
freddydk Dec 29, 2025
0b6e66f
Only grab the latest version
freddydk Dec 29, 2025
515ee0e
dumps
freddydk Dec 29, 2025
ad29d15
select Obj
freddydk Dec 29, 2025
b402ad0
Grab latest version
freddydk Dec 29, 2025
7e94d82
revert
freddydk Jan 5, 2026
fdbb93f
better output
freddydk Jan 6, 2026
9567259
spell
freddydk Jan 6, 2026
aeaf93f
only one project that builds the app is needed
freddydk Jan 6, 2026
0673701
check previously build projects
freddydk Jan 6, 2026
541b465
just check previously build projects
freddydk Jan 6, 2026
a698be7
just check
freddydk Jan 6, 2026
2de55ca
try
freddydk Jan 6, 2026
27c6a1b
recalc
freddydk Jan 6, 2026
e823b15
check built
freddydk Jan 6, 2026
b674381
check other
freddydk Jan 6, 2026
6b3ab8d
dumps
freddydk Jan 6, 2026
8c99adc
check alreadybuilt
freddydk Jan 6, 2026
c43f03a
correct dump
freddydk Jan 6, 2026
b58b310
use projectswithoutdependants
freddydk Jan 6, 2026
acee7d5
remove comment
freddydk Jan 6, 2026
7a11bbe
try old build mechanism
freddydk Jan 6, 2026
feabb73
sort projects
freddydk Jan 6, 2026
0b0b476
Merge branch 'main' into bhg
freddydk Jan 7, 2026
167d3d3
Get branch for artifacts
freddydk Jan 11, 2026
48773ba
Merge branch 'bhg' of https://github.com/freddydk/AL-Go into bhg
freddydk Jan 11, 2026
09dd39d
dump branch
freddydk Jan 11, 2026
92d8916
comments
freddydk Jan 11, 2026
13e36b8
sort pr builds
freddydk Jan 11, 2026
88031ae
use trim
freddydk Jan 15, 2026
8cd6ca2
Merge branch 'main' into bhg
freddydk Jan 16, 2026
9d153ae
remove dump and fix comments
freddydk Jan 18, 2026
6993edb
release notes
freddydk Jan 18, 2026
716979e
Update Actions/AL-Go-Helper.ps1
freddydk Jan 19, 2026
7c4ca5d
review
freddydk Jan 19, 2026
a1f74a5
Merge branch 'bhg' of https://github.com/freddydk/AL-Go into bhg
freddydk Jan 19, 2026
6098e23
use preview
freddydk Jan 19, 2026
ea880dd
Merge branch 'main' into bhg
freddydk Jan 22, 2026
640923e
Implement postponeProjectInBuildOrder setting
freddydk Jan 22, 2026
e660cda
Merge branch 'bhg' of https://github.com/freddydk/AL-Go into bhg
freddydk Jan 22, 2026
9a19dfd
add test for postpone
freddydk Jan 22, 2026
e44504d
add docs
freddydk Jan 22, 2026
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
3 changes: 2 additions & 1 deletion Actions/.Modules/ReadSettings.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,10 @@ function GetDefaultSettings
"reportSuppressedDiagnostics" = $false
"workflowDefaultInputs" = @()
"customALGoFiles" = [ordered]@{
"filesToInclude" = @()
"filesToInclude" = @()
"filesToExclude" = @()
}
"postponeProjectInBuildOrder" = $false
}
}

Expand Down
4 changes: 4 additions & 0 deletions Actions/.Modules/settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,10 @@
}
}
},
"postponeProjectInBuildOrder": {
"type": "boolean",
"description": "Indicates whether the project can be postponed in the build order to optimize build times. See https://aka.ms/ALGoSettings#postponeProjectInBuildOrder"
},
"workflowDefaultInputs": {
"type": "array",
"items": {
Expand Down
58 changes: 55 additions & 3 deletions Actions/AL-Go-Helper.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -1737,10 +1737,14 @@ Function AnalyzeProjectDependencies {
# Loop through all projects
# Get all apps in the project
# Get all dependencies for the apps
$projectsThatCanBePostponed = @()
foreach($project in $projects) {
Write-Host "- Analyzing project: $project"
Write-Host -NoNewline "Analyzing project: $project, "

$projectSettings = ReadSettings -project $project -baseFolder $baseFolder
if ($projectSettings.postponeProjectInBuildOrder) {
$projectsThatCanBePostponed += $project
}
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
Expand All @@ -1754,7 +1758,7 @@ Function AnalyzeProjectDependencies {
Pop-Location
}

OutputMessageAndArray -Message "Folders containing apps" -arrayOfStrings $folders
OutputMessageAndArray -Message "folders containing apps" -arrayOfStrings $folders

$unknownDependencies = @()
$apps = @()
Expand Down Expand Up @@ -1785,6 +1789,11 @@ Function AnalyzeProjectDependencies {
# }
$no = 1
$projectsOrder = @()
# Collect projects without dependants, which can be build later
# This is done to avoid building projects at an earlier stage than needed and increase the time until next job subsequently
# For every time we have determined a set of projects that can be build in parallel, we check whether any of these projects has no dependants
# If so, we remove these projects from the build order and add them at the end of the build order (by adding them to projectsWithoutDependants)
$projectsWithoutDependents = @()
Write-Host "Analyzing dependencies"
while ($projects.Count -gt 0) {
$thisJob = @()
Expand All @@ -1799,6 +1808,11 @@ Function AnalyzeProjectDependencies {
# Loop through all dependencies and locate the projects, containing the apps for which the current project has a dependency
$foundDependencies = @()
foreach($dependency in $dependencies) {
# Check whether dependency is already resolved by a previous build project
$depProject = @($projectsOrder | ForEach-Object { $_.Projects | Where-Object { $_ -ne $project -and $appDependencies."$_".apps -contains $dependency } })
if ($depProject.Count -gt 0) {
continue
}
# Find the project that contains the app for which the current project has a dependency
$depProjects = @($projects | Where-Object { $_ -ne $project -and $appDependencies."$_".apps -contains $dependency })
# Add this project and all projects on which that project has a dependency to the list of dependencies for the current project
Expand Down Expand Up @@ -1851,11 +1865,49 @@ Function AnalyzeProjectDependencies {
if ($thisJob.Count -eq 0) {
throw "Circular project reference encountered, cannot determine build order"
}

# Check whether any of the projects in $thisJob can be built later (has postponeProjectInBuildOrder set to true and no remaining dependents)
$projectsWithoutDependents += @($thisJob | Where-Object { $projectsThatCanBePostponed -contains $_ } | Where-Object {
$hasRemainingDependents = $false
foreach($otherProject in $projects) {
if ($otherProject -ne $_) {
# Grab dependencies from other project, which haven't been build yet
$otherDependencies = $appDependencies."$otherProject".dependencies | Where-Object {
$dependency = $_
$alreadyBuilt = ($projectsOrder | ForEach-Object { $_.Projects | Where-Object { $appDependencies."$_".apps -contains $dependency } })
return -not $alreadyBuilt
}
Write-Host "Other project $otherProject has unbuilt dependencies: $($otherDependencies -join ", ")"
foreach($dependency in $otherDependencies) {
if ($appDependencies."$_".apps -contains $dependency) {
Write-Host "Project $_ is still a dependency for project $otherProject"
$hasRemainingDependents = $true
}
}
}
}
if (!$hasRemainingDependents) {
Write-Host "Project $_ has no remaining dependents, can be built later"
}
return -not $hasRemainingDependents
})

# Remove projects in this job from the list of projects to be built (including the projects without dependents)
$projects = @($projects | Where-Object { $thisJob -notcontains $_ })

# Do not build jobs without dependents until the last job, remove them from this job
$thisJob = @($thisJob | Where-Object { $projectsWithoutDependents -notcontains $_ })

if ($projects.Count -eq 0) {
# Last job, add jobs without dependents
Write-Host "Adding jobs without dependents to last build job"
$thisJob += $projectsWithoutDependents
}

Write-Host "#$no - build projects: $($thisJob -join ", ")"

$projectsOrder += @{'projects' = $thisJob; 'projectsCount' = $thisJob.Count }

$projects = @($projects | Where-Object { $thisJob -notcontains $_ })
$no++
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function DownloadDependenciesFromCurrentBuild {
Write-Host "Dependency projects: $($dependencyProjects -join ', ')"

# For each dependency project, calculate the corresponding probing path
$dependeciesProbingPaths = @()
$dependenciesProbingPaths = @()
foreach($dependencyProject in $dependencyProjects) {
Write-Host "Reading settings for project '$dependencyProject'"
$dependencyProjectSettings = ReadSettings -baseFolder $baseFolder -project $dependencyProject
Expand All @@ -70,7 +70,7 @@ function DownloadDependenciesFromCurrentBuild {
$baseBranch = $ENV:GITHUB_REF_NAME
}

$dependeciesProbingPaths += @(@{
$dependenciesProbingPaths += @(@{
"release_status" = "thisBuild"
"version" = "latest"
"buildMode" = $dependencyBuildMode
Expand All @@ -85,7 +85,7 @@ function DownloadDependenciesFromCurrentBuild {

# For each probing path, download the dependencies
$downloadedDependencies = @()
foreach($probingPath in $dependeciesProbingPaths) {
foreach($probingPath in $dependenciesProbingPaths) {
$buildMode = $probingPath.buildMode
$project = $probingPath.projects
$branch = $probingPath.branch
Expand Down
53 changes: 49 additions & 4 deletions Actions/Github-Helper.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,16 @@ function GetDependencies {
$projects = $dependency.projects
$buildMode = $dependency.buildMode

if ($mask -eq 'TestApps') {
$altMask = 'Apps'
}
else {
$altMask = 'TestApps'
}
# change the mask to include the build mode
if($buildMode -ne "Default") {
$mask = "$buildMode$mask"
$altMask = "$buildMode$altMask"
}

Write-Host "Locating $mask artifacts for projects: $projects"
Expand All @@ -169,8 +176,15 @@ function GetDependencies {
}
}
elseif ($mask -like '*Apps') {
Write-Host "$project not built, downloading from artifacts"
$missingProjects += @($project)
# Check whether Apps/TestApps exists before determining that project isn't built
$altDownloadName = Join-Path $saveToPath "$project-$branchName-$altMask-*"
if (!(Test-Path $altDownloadName -PathType Container)) {
Write-Host "$project not built, downloading from artifacts"
$missingProjects += @($project)
}
else {
Write-Host "$project built, but $mask not found"
}
}
}
if ($missingProjects -and $dependency.baselineWorkflowID) {
Expand Down Expand Up @@ -1015,6 +1029,11 @@ function GetArtifactsFromWorkflowRun {
# Get sanitized project names (the way they appear in the artifact names)
$projectArr = @(@($projects.Split(',')) | ForEach-Object { $_.Replace('\','_').Replace('/','_') })

# Get branch used in workflowRun
$workflowRunInfo = (InvokeWebRequest -Headers $headers -Uri "$api_url/repos/$repository/actions/runs/$workflowRun").Content | ConvertFrom-Json
$branch = $workflowRunInfo.head_branch.Replace('\', '_').Replace('/', '_')
Write-Host "Branch for workflow run $workflowRun is $branch"

# Get the artifacts from the the workflow run
while($true) {
$artifactsURI = "$api_url/repos/$repository/actions/runs/$workflowRun/artifacts?per_page=$per_page&page=$page"
Expand All @@ -1026,14 +1045,40 @@ function GetArtifactsFromWorkflowRun {
}

foreach($project in $projectArr) {
$artifactPattern = "$project-*-$mask-*" # e.g. "MyProject-*-Apps-*", format is: "project-branch-mask-version"
# e.g. "MyProject-main-Apps-*", format is: "project-branch-mask-version"
# Mask might include buildMode like TranslatedTestApps
$artifactPattern = "$project-$branch-$mask-*"
$matchingArtifacts = @($artifacts.artifacts | Where-Object { $_.name -like $artifactPattern })

if ($matchingArtifacts.Count -eq 0) {
continue
}

$matchingArtifacts = @($matchingArtifacts) #enforce array
# If there are reruns of the build we found, we might see artifacts like:
# Test DBC-BHG-SAF-T-main-TestApps-1.0.48.0
# Test DBC-BHG-SAF-T-main-TestApps-1.0.48.1
# We want to keep only the latest version of each artifact (based on the last segment of the version)
$matchingArtifacts = @($matchingArtifacts | ForEach-Object {
# Sort on version number object
if ($_.name -match '^(.*)-(\d+\.\d+\.\d+\.\d+)$') {
[PSCustomObject]@{
Name = $Matches[1]
Version = [version]$Matches[2]
Obj = $_
}
}
else {
# artifacts from PR builds doesn't match the versioning pattern but are sortable
[PSCustomObject]@{
Name = $_.name
Version = $_.name
Obj = $_
}
}
} | Group-Object Name | ForEach-Object {
$_.Group | Sort-Object Version -Descending | Select-Object -First 1
} |
Select-Object -ExpandProperty Obj)

foreach($artifact in $matchingArtifacts) {
Write-Host "Found artifact $($artifact.name) (ID: $($artifact.id)) for mask $mask and project $project"
Expand Down
15 changes: 10 additions & 5 deletions Actions/PipelineCleanup/PipelineCleanup.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
[string] $project = "."
)

. (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve)
DownloadAndImportBcContainerHelper
try {
. (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve)
DownloadAndImportBcContainerHelper

if ($project -eq ".") { $project = "" }
if ($project -eq ".") { $project = "" }

$containerName = GetContainerName($project)
Remove-Bccontainer $containerName
$containerName = GetContainerName($project)
Remove-Bccontainer $containerName
}
catch {
Write-Host "Pipeline Cleanup failed: $($_.Exception.Message)"
}
7 changes: 7 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
### Issues

- Issue 2084 Multiple artifacts failure if you re-run failed jobs after flaky tests
- Issue 2085 Projects that doesn't contain both Apps and TestApps are wrongly seen as not built.
- Issue 2086 Postpone jobs, which doesn't have any dependents to the end of the build order.
- Issue 2082 Sign action no longer fails when repository is empty or no artifacts are generated
- Issue 2078 Workflows run since January 14th '26 have space before CI/CD removed
- Issue 2070 Support public GitHub Packages feeds without requiring a Personal Access Token (PAT)
Expand All @@ -9,6 +12,10 @@
- AL-Go repositories with large amounts of projects may run into issues with too large environment variables
- Discussion 1855 Add trigger 'workflow_call' to workflow 'Update AL-Go System Files' for reusability

### New Settings

- `postponeProjectInBuildOrder` is a new project setting, which will (if set to true) cause the project to be postponed until the last build job when possible. If set on test projects, then all tests can be deferred until all builds have succeeded.

### Set default values for workflow inputs

The `workflowDefaultInputs` setting now also applies to `workflow_call` inputs when an input with the same name exists for `workflow_dispatch`.
Expand Down
1 change: 1 addition & 0 deletions Scenarios/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ When running a workflow or a local script, the settings are applied by reading s
| <a id="appDependencyProbingPaths"></a>appDependencyProbingPaths | Array of dependency specifications, from which apps will be downloaded when the CI/CD workflow is starting. Every dependency specification consists of the following properties:<br />**repo** = repository<br />**version** = version (default latest)<br />**release_status** = latestBuild/release/prerelease/draft (default release)<br />**projects** = projects (default * = all)<br />**branch** = branch (default main)<br />**AuthTokenSecret** = Name of secret containing auth token (default none)<br /> | [ ] |
| <a id="preprocessorSymbols"></a>preprocessorSymbols | List of preprocessor symbols to use when building the apps. This setting can be specified in [workflow specific settings files](https://aka.ms/algosettings#where-are-the-settings-located) or in [conditional settings](https://aka.ms/algosettings#conditional-settings). | [ ] |
| <a id="bcptThresholds"></a>bcptThresholds | Structure with properties for the thresholds when running performance tests using the Business Central Performance Toolkit.<br />**DurationWarning** = a warning is shown if the duration of a bcpt test degrades more than this percentage (default 10)<br />**DurationError** - an error is shown if the duration of a bcpt test degrades more than this percentage (default 25)<br />**NumberOfSqlStmtsWarning** - a warning is shown if the number of SQL statements from a bcpt test increases more than this percentage (default 5)<br />**NumberOfSqlStmtsError** - an error is shown if the number of SQL statements from a bcpt test increases more than this percentage (default 10)<br />*Note that errors and warnings on the build in GitHub are only issued when a threshold is exceeded on the codeunit level, when an individual operation threshold is exceeded, it is only shown in the test results viewer.* |
| <a id="postponeProjectInBuildOrder"></a>postponeProjectInBuildOrder | Setting this project setting to true will cause the project to be postponed until the last build job when possible (i.e. if no other projects depends on it). If set on test projects, then all tests can be deferred until all builds have succeeded. | false |

## AppSource specific basic project settings

Expand Down
Loading