Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
0d9e05e
Enhance customALGoFiles feature (#3)
OleWunschmann Jun 3, 2026
73f4ef3
fixed typo
OleWunschmann Jun 3, 2026
bb38962
fixed function description
OleWunschmann Jun 3, 2026
50ad11c
Fixed tests for ps5 and string-prefix check for folder traversal
OleWunschmann Jun 9, 2026
7aa8766
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Jun 9, 2026
f7496cc
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Jun 12, 2026
11e2efc
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Jun 16, 2026
b021013
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Jun 17, 2026
025747e
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Jun 22, 2026
dd1771a
Merge remote-tracking branch 'origin/main' into enhance-customALGoFil…
OleWunschmann Jun 30, 2026
2d24972
Merge remote-tracking branch 'origin/main' into enhance-customALGoFil…
OleWunschmann Jul 9, 2026
5976abe
Merge remote-tracking branch 'origin/main' into enhance-customALGoFil…
OleWunschmann Jul 14, 2026
7fa3522
Extended settings schema and documentation for "destinationName"
OleWunschmann Jul 14, 2026
a5c5fb1
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Jul 20, 2026
181080e
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Jul 21, 2026
c23a8ae
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Jul 21, 2026
afce6d7
Potential fix for pull request finding
OleWunschmann Jul 23, 2026
00d1902
Fixed copilot review comments
OleWunschmann Jul 23, 2026
ee510bd
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Jul 23, 2026
c8ded0d
Fix copilot review comments
OleWunschmann Jul 24, 2026
409371e
Cleanup
OleWunschmann Jul 24, 2026
84a656e
Cleanup
OleWunschmann Jul 24, 2026
8682d62
Fix copilot review comments
OleWunschmann Jul 24, 2026
051d08a
Remove support for fileToRemove
OleWunschmann Jul 29, 2026
1a037b8
Merge remote-tracking branch 'origin/main' into enhance-customALGoFil…
OleWunschmann Jul 29, 2026
d412a75
Fixed copilot review comment
OleWunschmann Jul 29, 2026
6376e16
Fixed issues
OleWunschmann Jul 29, 2026
73e159c
Cleanup
OleWunschmann Jul 29, 2026
cf71143
Fixed boundary escape for destinationFolder and destinationName
OleWunschmann Jul 29, 2026
f94fe4e
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Jul 31, 2026
61c640d
Fixed boundary escape for destinationName and typos
OleWunschmann Jul 31, 2026
ea2688e
Fixed boundry escape for wildcards and for linux
OleWunschmann Jul 31, 2026
aab09ca
Merge branch 'main' into enhance-customALGoFiles-feature
aholstrup1 Aug 12, 2026
0747977
Merge branch 'main' into enhance-customALGoFiles-feature
aholstrup1 Aug 14, 2026
fb60e90
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Aug 28, 2026
14ca702
Fix path comparisons with OS specific casings
OleWunschmann Sep 1, 2026
fd18fd4
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Sep 1, 2026
95d9cf4
Resolve symlinks/junctions
OleWunschmann Sep 15, 2026
d8495f7
Merge remote-tracking branch 'origin/main' into enhance-customALGoFil…
OleWunschmann Sep 15, 2026
684ae30
Fix copilot review comments
OleWunschmann Sep 16, 2026
6b0714b
Fix copilot review comment
OleWunschmann Sep 16, 2026
b7e2cd8
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Sep 18, 2026
448f661
Merge branch 'main' into enhance-customALGoFiles-feature
mazhelez Sep 18, 2026
2f41252
Merge branch 'main' into enhance-customALGoFiles-feature
mazhelez Sep 22, 2026
46293f7
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Sep 23, 2026
5d945c0
fix dangling links
OleWunschmann Sep 23, 2026
b42c085
reworked symlink/junction handling
OleWunschmann Sep 23, 2026
cf8671f
reworked symlink/junction handling and tests
OleWunschmann Sep 24, 2026
d9759ed
more rework
OleWunschmann Sep 24, 2026
65911f2
add tests for template settings snapshot
OleWunschmann Sep 24, 2026
d8e4b54
read settings without context, handle path issues, cleanup
OleWunschmann Sep 25, 2026
ff032bd
fixed issues with literal paths, symlinks/junctions and case-sensitivity
OleWunschmann Sep 25, 2026
a1a20dc
Fix typo in comment for clarity
OleWunschmann Sep 25, 2026
0b9dc8c
fix review comments
OleWunschmann Sep 25, 2026
a23d1fb
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Sep 30, 2026
60a175c
Fix review comments
OleWunschmann Sep 30, 2026
f9f9eb1
Fix review comments
OleWunschmann Sep 30, 2026
c54f757
fix e2e test
OleWunschmann Sep 30, 2026
fb139d9
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Oct 6, 2026
dc0dd03
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Oct 7, 2026
b52380d
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Oct 8, 2026
5b542b5
fix resolve of relative file symlinks
OleWunschmann Oct 8, 2026
9c867e2
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Oct 9, 2026
0887510
updated docu and releasenotes
OleWunschmann Oct 9, 2026
4bab4c5
fix PS5 tests
OleWunschmann Oct 12, 2026
d5a3ea5
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Oct 12, 2026
8d973d2
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann Oct 15, 2026
02d1e6c
Guard repository settings updates against redirects
OleWunschmann Oct 15, 2026
8660f72
Update file resolution and destination handling in AL-Go
OleWunschmann Oct 15, 2026
04824bc
Update file resolution and destination handling docu
OleWunschmann Oct 15, 2026
6ecaf7c
Merge branch 'enhance-customALGoFiles-feature' of https://github.com/…
OleWunschmann Oct 15, 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
6 changes: 6 additions & 0 deletions Actions/.Modules/settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@
"type": "object",
"properties": {
"filesToInclude": {
"description": "An array of file specifications to include in the update. Files that match these specifications are copied from the template to the repository. When used in a custom template's settings, inclusions are also propagated from the original template to consumer repos even if the files no longer exist in the custom template.",
"type": "array",
"items": {
"type": "object",
Expand All @@ -760,6 +761,10 @@
"type": "string",
"description": "The destination folder where the files should be updated, relative to the repository root. If not specified, defaults to the same as the source file folder."
},
"destinationName": {
"type": "string",
"description": "The filename to use at the destination. If specified, overrides the source filename, allowing the file to be renamed when copied. Should be used together with a filter that matches a single file."
Comment thread
mazhelez marked this conversation as resolved.
Comment thread
Copilot marked this conversation as resolved.
},
"perProject": {
"type": "boolean",
"description": "Indicates whether the file update should be applied per project. In that case, the destinationFolder is considered relative to each project folder."
Expand All @@ -768,6 +773,7 @@
}
},
"filesToExclude": {
"description": "An array of file specifications to exclude from the update. Files that match these specifications are not copied from the template to the repository. When used in a custom template's settings, exclusions are also propagated from the original template to consumer repos even if the files no longer exist in the custom template.",
"type": "array",
"items": {
"type": "object",
Expand Down
484 changes: 435 additions & 49 deletions Actions/CheckForUpdates/CheckForUpdates.HelperFunctions.ps1

Large diffs are not rendered by default.

80 changes: 68 additions & 12 deletions Actions/CheckForUpdates/CheckForUpdates.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Param(
Param(
[Parameter(HelpMessage = "The GitHub actor running the action", Mandatory = $false)]
[string] $actor,
[Parameter(HelpMessage = "Base64 encoded GhTokenWorkflow secret", Mandatory = $false)]
Expand Down Expand Up @@ -51,8 +51,9 @@ if ($token) {
# 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

# Get Repo settings as a hashtable (do NOT read any specific project settings, nor any specific workflow, user or branch settings)
$repoSettings = ReadSettings -buildMode '' -project '' -workflowName '' -userName '' -branchName '' | ConvertTo-HashTable -recurse
# Get repo settings independent of the build, project, workflow, user, branch, and trigger running this update.
# Repository-scoped and unconditional settings still apply.
$repoSettings = ReadSettings -buildMode '' -project '' -workflowName '' -userName '' -branchName '' -trigger '' | ConvertTo-HashTable -recurse
Comment thread
aholstrup1 marked this conversation as resolved.
$templateSha = $repoSettings.templateSha

# If templateUrl has changed, download latest version of the template repository (ignore templateSha)
Expand All @@ -72,6 +73,9 @@ $templateInfo = "$templateOwner/$($templateUrl.Split('/')[4])"
$isDirectALGo = IsDirectALGo -templateUrl $templateUrl
if (-not $isDirectALGo) {
$templateRepoSettingsFile = Join-Path $templateFolder $RepoSettingsFile
if (-not (Test-PathPhysicallyEqual -Path $templateRepoSettingsFile -AnchorPaths @($templateFolder))) {
throw "Cannot read template repository settings: '$templateRepoSettingsFile' does not physically resolve to itself. This may indicate a symlink/junction redirect."
}
if (Test-Path -Path $templateRepoSettingsFile -PathType Leaf) {
$templateRepoSettings = Get-Content $templateRepoSettingsFile -Encoding UTF8 | ConvertFrom-Json | ConvertTo-HashTable -Recurse
if ($templateRepoSettings.Keys -contains "templateUrl" -and $templateRepoSettings.templateUrl -ne $templateUrl) {
Expand Down Expand Up @@ -113,6 +117,12 @@ if (-not $isDirectALGo) {

# Get the list of projects in the current repository
$baseFolder = $ENV:GITHUB_WORKSPACE

if ($originalTemplateFolder) {
# Use current custom template settings for this run without changing the workspace before comparison.
$repoSettings = ReadSettingsWithCurrentCustomTemplateRepoSettings -baseFolder $baseFolder -customTemplateFolder $templateFolder
}

$projects = @(GetProjectsFromRepository -baseFolder $baseFolder -projectsFromSettings $repoSettings.projects)

$filesToInclude, $filesToExclude = GetFilesToUpdate -settings $repoSettings -projects $projects -baseFolder $baseFolder -templateFolder $templateFolder -originalTemplateFolder $originalTemplateFolder
Expand All @@ -133,6 +143,14 @@ if ($projects.Count -gt 1) {
Write-Host "Calculated dependency depth to be $depth"
}

# Prepare the list of template folders to be used for verification
$templateFolders = @($templateFolder)
$customTemplateFolder = ''
if ($originalTemplateFolder) {
$templateFolders += $originalTemplateFolder
$customTemplateFolder = $templateFolder
}

# Loop through all folders in CheckFiles and check if there are any files that needs to be updated
foreach($fileToInclude in $filesToInclude) {
$type = $fileToInclude.type
Expand All @@ -141,17 +159,37 @@ foreach($fileToInclude in $filesToInclude) {
if(-not $originalSrcPath) {
$originalSrcPath = $srcPath
}
$hasDistinctOriginalSource = -not (GetPathStringComparer).Equals($originalSrcPath, $srcPath)

# Skip files that do not physically resolve to themselves within the template folders
if (-not (Test-PathPhysicallyEqual -Path $srcPath -AnchorPaths $templateFolders)) {
OutputWarning "Skipping update for source file '$srcPath': source does not physically resolve to itself within the template folder(s). This may indicate a symlink/junction redirect."
continue
}
if ($hasDistinctOriginalSource) {
# Skip files with original files that do not physically resolve to themselves within the template folders
if (-not (Test-PathPhysicallyEqual -Path $originalSrcPath -AnchorPaths $templateFolders)) {
OutputWarning "Skipping update for source file '$srcPath': original source '$originalSrcPath' does not physically resolve to itself within the template folder(s). This may indicate a symlink/junction redirect."
continue
}
}

$dstPath = $fileToInclude.destinationFullPath

$dstFileExists = Test-Path -Path $dstPath -PathType Leaf
# Skip files with destinations that do not physically resolve to themselves within the base folder
if (-not (Test-PathPhysicallyEqual -Path $dstPath -AnchorPaths @($baseFolder))) {
OutputWarning "Skipping update for source file '$srcPath': destination '$dstPath' does not physically resolve to itself. This may indicate a symlink/junction redirect."
continue
}

$dstFileExists = Test-Path -LiteralPath $dstPath -PathType Leaf

Write-Host "Processing file: $srcPath -> $dstPath (type: $type)"

switch ($type) {
"workflow" {
# For workflow files, we might need to modify the file based on the settings
$srcContent = GetWorkflowContentWithChangesFromSettings -srcFile $originalSrcPath -repoSettings $repoSettings -depth $depth
$srcContent = GetWorkflowContentWithChangesFromSettings -srcFile $originalSrcPath -repoSettings $repoSettings -depth $depth -customTemplateFolder $customTemplateFolder
# Replace static placeholders
$srcContent = $srcContent.Replace('{TEMPLATEURL}', $templateUrl)
}
Expand All @@ -170,7 +208,7 @@ foreach($fileToInclude in $filesToInclude) {
ReplaceOwnerRepoAndBranch -srcContent ([ref]$srcContent) -templateOwner $templateOwner -templateBranch $templateBranch
}

if ($type -eq 'workflow' -and $originalSrcPath -ne $srcPath) {
if ($type -eq 'workflow' -and $hasDistinctOriginalSource) {
# Apply customizations from custom template repository
Write-Host "Apply customizations from custom template repository, file: $srcPath"
[Yaml]::ApplyTemplateCustomizations([ref] $srcContent, $srcPath)
Expand Down Expand Up @@ -204,8 +242,8 @@ foreach($fileToInclude in $filesToInclude) {

Push-Location -Path $baseFolder
# Remove files that are in $filesToExclude and exist in the repository
$removeFiles = $filesToExclude | Where-Object { $_ -and (Test-Path -Path $_.destinationFullPath -PathType Leaf) } | ForEach-Object {
$relativePath = Resolve-Path -Path $_.destinationFullPath -Relative
$removeFiles = $filesToExclude | Where-Object { $_ -and (Test-Path -LiteralPath $_.destinationFullPath -PathType Leaf) } | ForEach-Object {
$relativePath = Resolve-Path -LiteralPath $_.destinationFullPath -Relative
Comment thread
Copilot marked this conversation as resolved.
Write-Host "File marked for removal: $relativePath"
$relativePath
}
Expand Down Expand Up @@ -256,16 +294,28 @@ else {

invoke-git status

$dstRoot = (Get-Location).Path
$dstRepoSettingsFile = Join-Path $dstRoot $RepoSettingsFile
if (-not (Test-PathPhysicallyEqual -Path $dstRepoSettingsFile -AnchorPaths @($dstRoot))) {
throw "Cannot update repository settings: '$dstRepoSettingsFile' does not physically resolve to itself. This may indicate a symlink/junction redirect."
}

# Update the files
# Calculate the release notes, while updating
$releaseNotes = ""
$updateFiles | ForEach-Object {
# Skip files that do not physically resolve to themselves within the destination root folder
if (-not (Test-PathPhysicallyEqual -Path (Join-Path $dstRoot $_.DstFile) -AnchorPaths @($dstRoot))) {
Comment thread
Copilot marked this conversation as resolved.
OutputWarning "Skipping update of '$($_.DstFile)': destination does not physically resolve to itself. This may indicate a symlink/junction redirect."
return
}

# Create the destination folder if it doesn't exist
$path = [System.IO.Path]::GetDirectoryName($_.DstFile)
if ($path -and -not (Test-Path -path $path -PathType Container)) {
if ($path -and -not (Test-Path -LiteralPath $path -PathType Container)) {
New-Item -Path $path -ItemType Directory | Out-Null
}
if (([System.IO.Path]::GetFileName($_.DstFile) -eq "RELEASENOTES.copy.md") -and (Test-Path $_.DstFile)) {
if (([System.IO.Path]::GetFileName($_.DstFile) -eq "RELEASENOTES.copy.md") -and (Test-Path -LiteralPath $_.DstFile)) {
# Read the release notes of the version currently installed
$oldReleaseNotes = Get-ContentLF -Path $_.DstFile
# Get the release notes of the new version (for the PR body)
Expand All @@ -287,12 +337,18 @@ else {
$releaseNotes = "No release notes available!"
}
$removeFiles | ForEach-Object {
# Skip files that do not physically resolve to themselves within the destination root folder
if (-not (Test-PathPhysicallyEqual -Path (Join-Path $dstRoot $_) -AnchorPaths @($dstRoot))) {
OutputWarning "Skipping removal of '$_': destination does not physically resolve to itself. This may indicate a symlink/junction redirect."
return
}

Write-Host "Remove $_"
Remove-Item (Join-Path (Get-Location).Path $_) -Force
Remove-Item -LiteralPath $_ -Force
}

# Update the templateUrl and templateSha in the repo settings file
UpdateSettingsFile -settingsFile (Join-Path ".github" "AL-Go-Settings.json") -updateSettings @{ "templateUrl" = $templateUrl; "templateSha" = $templateSha }
UpdateSettingsFile -settingsFile $dstRepoSettingsFile -updateSettings @{ "templateUrl" = $templateUrl; "templateSha" = $templateSha }

Write-Host "ReleaseNotes:"
Write-Host $releaseNotes
Expand Down
2 changes: 1 addition & 1 deletion Actions/CheckForUpdates/yamlclass.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Yaml {

# Static load function to load a Yaml file into a Yaml class
static [Yaml] Load([string] $filename) {
$fileContent = Get-Content -Path $filename -Encoding UTF8
$fileContent = Get-Content -LiteralPath $filename -Encoding UTF8
return [Yaml]::new($fileContent)
}

Expand Down
2 changes: 1 addition & 1 deletion Actions/Github-Helper.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -859,7 +859,7 @@ function Get-ContentLF {
)

Process {
(Get-Content -Path $path -Encoding UTF8 -Raw).Replace("`r", "").TrimEnd("`n")
(Get-Content -LiteralPath $path -Encoding UTF8 -Raw).Replace("`r", "").TrimEnd("`n")
}
}

Expand Down
12 changes: 12 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
### Enhanced `customALGoFiles` setting
Comment thread
mazhelez marked this conversation as resolved.
Comment thread
mazhelez marked this conversation as resolved.

The `customALGoFiles` setting of a custom template was only applied on the next Update (from `AL-Go-TemplateRepoSettings.doNotEdit.json`). Now the up-to-date settings of the custom template are used directly during "Update AL-Go System Files". The template's `filesToInclude` and `filesToExclude` settings are merged with the consumer repo's settings before resolution.

- **`filesToInclude`** now also resolves files from the original AL-Go template. Files present in the official template are propagated even when they are absent from your custom template. When a file exists in both, the official template supplies the base content; for workflow files, customizations from the custom template are reapplied.
- **`filesToExclude`** now also resolves files from the original AL-Go template (same dual-resolution as `filesToInclude`). Matching sources are not copied to consumer repos; existing destination files are removed only when no other included source targets the same destination.
- **`destinationName`** (new property on `filesToInclude`): Allows renaming a file at the destination. When set, the file is written to `<destinationFolder>/<destinationName>` instead of keeping the source filename. Changing it does not remove the previously propagated filename; remove that file manually if needed. If rules target the same destination, the first eligible source wins (custom template before original template; within each template, default rules before configured rules).

For safety, updates and removals skip files whose source or destination paths redirect through symlinks or junctions, and emit a warning.

Read more at [Customizing AL-Go for GitHub](Scenarios/CustomizingALGoForGitHub.md#Using-custom-template-files).

### Issues

- Issue 2398 - Preserve dependency paths containing commas when passing a single app to Run-AlPipeline. Download Project Dependencies now warns with the complete path if a resolved dependency file is missing, without changing the output lists.
Expand Down
Loading
Loading