-
Notifications
You must be signed in to change notification settings - Fork 200
Enhance customALGoFiles feature #2273
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
Open
OleWunschmann
wants to merge
32
commits into
microsoft:main
Choose a base branch
from
OleWunschmann:enhance-customALGoFiles-feature
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
0d9e05e
Enhance customALGoFiles feature (#3)
OleWunschmann 73f4ef3
fixed typo
OleWunschmann bb38962
fixed function description
OleWunschmann 50ad11c
Fixed tests for ps5 and string-prefix check for folder traversal
OleWunschmann 7aa8766
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann f7496cc
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann 11e2efc
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann b021013
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann 025747e
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann dd1771a
Merge remote-tracking branch 'origin/main' into enhance-customALGoFil…
OleWunschmann 2d24972
Merge remote-tracking branch 'origin/main' into enhance-customALGoFil…
OleWunschmann 5976abe
Merge remote-tracking branch 'origin/main' into enhance-customALGoFil…
OleWunschmann 7fa3522
Extended settings schema and documentation for "destinationName"
OleWunschmann a5c5fb1
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann 181080e
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann c23a8ae
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann afce6d7
Potential fix for pull request finding
OleWunschmann 00d1902
Fixed copilot review comments
OleWunschmann ee510bd
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann c8ded0d
Fix copilot review comments
OleWunschmann 409371e
Cleanup
OleWunschmann 84a656e
Cleanup
OleWunschmann 8682d62
Fix copilot review comments
OleWunschmann 051d08a
Remove support for fileToRemove
OleWunschmann 1a037b8
Merge remote-tracking branch 'origin/main' into enhance-customALGoFil…
OleWunschmann d412a75
Fixed copilot review comment
OleWunschmann 6376e16
Fixed issues
OleWunschmann 73e159c
Cleanup
OleWunschmann cf71143
Fixed boundary escape for destinationFolder and destinationName
OleWunschmann f94fe4e
Merge branch 'main' into enhance-customALGoFiles-feature
OleWunschmann 61c640d
Fixed boundary escape for destinationName and typos
OleWunschmann ea2688e
Fixed boundry escape for wildcards and for linux
OleWunschmann File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -827,6 +827,17 @@ function ResolveFilePaths { | |
| return @() | ||
| } | ||
|
|
||
| $sourceFolder = [System.IO.Path]::GetFullPath($sourceFolder) # Canonicalize the source folder to an absolute path | ||
| $sourceFolder = Join-Path $sourceFolder '' # Ensure source folder has a trailing slash for correct path resolution | ||
|
|
||
| $destinationFolder = [System.IO.Path]::GetFullPath($destinationFolder) # Canonicalize the destination folder to an absolute path | ||
| $destinationFolder = Join-Path $destinationFolder '' # Ensure destination folder has a trailing slash for correct path resolution | ||
|
Comment on lines
+830
to
+834
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed |
||
|
|
||
| $pathComparison = [System.StringComparison]::OrdinalIgnoreCase | ||
| if ($PSVersionTable.PSVersion.Major -ge 6 -and ($IsLinux -or $IsMacOS)) { | ||
| $pathComparison = [System.StringComparison]::Ordinal | ||
| } | ||
|
|
||
| $fullFilePaths = @() | ||
| foreach($file in $files) { | ||
| if($file.Keys -notcontains 'sourceFolder') { | ||
|
|
@@ -880,7 +891,7 @@ function ResolveFilePaths { | |
| } | ||
|
|
||
| # Check if the source file is under the source folder | ||
| if ($srcFile -notlike "$sourceFolder*") { | ||
| if (-not $srcFile.StartsWith($sourceFolder, $pathComparison)) { | ||
| OutputDebug "Skipping source file '$($srcFile)' as it is not under the source folder '$($sourceFolder)'." | ||
| continue | ||
| } | ||
|
|
@@ -912,11 +923,19 @@ function ResolveFilePaths { | |
| $project = '' # If project is '.', it means the root folder, so we use an empty string | ||
| } | ||
|
|
||
| $fileDestinationFolder = Join-Path $destinationFolder $project | ||
| $fileDestinationFolder = Join-Path $fileDestinationFolder $file.destinationFolder | ||
| $fileDestinationFolder = Join-Path $fileDestinationFolder '' # Ensure file destination folder has a trailing slash for correct path resolution | ||
|
|
||
| $fullProjectFilePath = $fullFilePath.Clone() | ||
| $fullProjectFilePath.destinationFullPath = Join-Path $fileDestinationFolder $destinationName | ||
| $fullProjectFilePath.destinationFullPath = [System.IO.Path]::GetFullPath($fullProjectFilePath.destinationFullPath) # Canonicalize the destination full path to an absolute path | ||
|
|
||
| $fullProjectFilePath.destinationFullPath = Join-Path $destinationFolder $project | ||
| $fullProjectFilePath.destinationFullPath = Join-Path $fullProjectFilePath.destinationFullPath $file.destinationFolder | ||
| $fullProjectFilePath.destinationFullPath = Join-Path $fullProjectFilePath.destinationFullPath $destinationName | ||
| # Check if the destination file is under the file destination folder | ||
| if (-not $fullProjectFilePath.destinationFullPath.StartsWith($fileDestinationFolder, $pathComparison)) { | ||
| OutputWarning "Skipping file '$srcFile' for project '$project': destination file '$($fullProjectFilePath.destinationFullPath)' is outside the destination folder '$fileDestinationFolder'." | ||
| continue | ||
| } | ||
|
|
||
| if($fullFilePaths -and $fullFilePaths.destinationFullPath -contains $fullProjectFilePath.destinationFullPath) { | ||
| OutputDebug "Skipping duplicate per-project file for project '$project': destinationFullPath '$($fullProjectFilePath.destinationFullPath)' already exists" | ||
|
|
@@ -930,8 +949,17 @@ function ResolveFilePaths { | |
| # Single file entry | ||
| # Destination full path is the destination base folder + destinationFolder + destinationName | ||
|
|
||
| $fullFilePath.destinationFullPath = Join-Path $destinationFolder $file.destinationFolder | ||
| $fullFilePath.destinationFullPath = Join-Path $fullFilePath.destinationFullPath $destinationName | ||
| $fileDestinationFolder = Join-Path $destinationFolder $file.destinationFolder | ||
| $fileDestinationFolder = Join-Path $fileDestinationFolder '' # Ensure file destination folder has a trailing slash for correct path resolution | ||
|
|
||
| $fullFilePath.destinationFullPath = Join-Path $fileDestinationFolder $destinationName | ||
| $fullFilePath.destinationFullPath = [System.IO.Path]::GetFullPath($fullFilePath.destinationFullPath) # Canonicalize the destination full path to an absolute path | ||
|
|
||
| # Check if the destination file is under the file destination folder | ||
| if (-not $fullFilePath.destinationFullPath.StartsWith($fileDestinationFolder, $pathComparison)) { | ||
| OutputWarning "Skipping file '$srcFile': destination file '$($fullFilePath.destinationFullPath)' is outside the destination folder '$fileDestinationFolder'." | ||
| continue | ||
| } | ||
|
|
||
| if($fullFilePaths -and $fullFilePaths.destinationFullPath -contains $fullFilePath.destinationFullPath) { | ||
| OutputDebug "Skipping duplicate file: destinationFullPath '$($fullFilePath.destinationFullPath)' already exists" | ||
|
|
@@ -994,28 +1022,88 @@ function GetDefaultFilesToExclude { | |
| return @($filesToExclude) | ||
| } | ||
|
|
||
| <# | ||
| .SYNOPSIS | ||
| Reads settings using the current custom template repository settings without changing the workspace. | ||
| .DESCRIPTION | ||
| Temporarily refreshes the custom template repository settings snapshot, reads the merged settings, and restores | ||
| the snapshot to its original state. This allows the current template settings to affect the current run while | ||
| preserving the workspace state for the normal update comparison. | ||
| .PARAMETER baseFolder | ||
| The base folder of the repository whose settings are read. | ||
| .PARAMETER templateFolder | ||
| The folder where the custom template files are located. | ||
| #> | ||
| function ReadSettingsWithCurrentCustomTemplateRepoSettings { | ||
| Param( | ||
| [Parameter(Mandatory=$true)] | ||
| [string] $baseFolder, | ||
| [Parameter(Mandatory=$true)] | ||
| [string] $templateFolder | ||
| ) | ||
|
|
||
| $templateFolderRepoSettingsPath = Join-Path $templateFolder $RepoSettingsFile | ||
|
|
||
| $baseFolderTemplateSettingsPath = Join-Path $baseFolder $CustomTemplateRepoSettingsFile | ||
| $baseFolderTemplateSettingsBackupPath = $null | ||
|
|
||
| if (Test-Path -LiteralPath $baseFolderTemplateSettingsPath -PathType Leaf) { | ||
| $baseFolderTemplateSettingsBackupPath = Join-Path (GetTemporaryPath) ([Guid]::NewGuid().ToString()) | ||
| Copy-Item -LiteralPath $baseFolderTemplateSettingsPath -Destination $baseFolderTemplateSettingsBackupPath -Force | ||
| } | ||
|
|
||
| try { | ||
| if (Test-Path -LiteralPath $templateFolderRepoSettingsPath -PathType Leaf) { | ||
| Copy-Item -LiteralPath $templateFolderRepoSettingsPath -Destination $baseFolderTemplateSettingsPath -Force | ||
| } | ||
| return ReadSettings -baseFolder $baseFolder -buildMode '' -project '' -workflowName '' -userName '' -branchName '' -trigger '' | ConvertTo-HashTable -recurse | ||
| } | ||
| finally { | ||
| if ($baseFolderTemplateSettingsBackupPath) { | ||
| Copy-Item -LiteralPath $baseFolderTemplateSettingsBackupPath -Destination $baseFolderTemplateSettingsPath -Force | ||
| Remove-Item -LiteralPath $baseFolderTemplateSettingsBackupPath -Force | ||
| } | ||
| elseif (Test-Path -LiteralPath $baseFolderTemplateSettingsPath -PathType Leaf) { | ||
| Remove-Item -LiteralPath $baseFolderTemplateSettingsPath -Force | ||
| } | ||
| } | ||
| } | ||
|
|
||
| <# | ||
| .SYNOPSIS | ||
| Get the list of files from the template repository to include and exclude based on the provided settings. | ||
| .DESCRIPTION | ||
| This function gets the list of files to include and exclude based on the provided settings. | ||
| The unusedALGoSystemFiles setting is also applied to exclude files from the include list and add them to the exclude list. | ||
| Builds two lists by merging defaults, repository settings, and the original AL-Go template (if given): | ||
|
|
||
| 1. filesToInclude: Files to copy from the template or original template to the destination. | ||
| Built from default files to include and customALGoFiles.filesToInclude in settings, resolved against the template folder and original template folder (if any). | ||
| 2. filesToExclude: Files to skip from copying; if they already exist in the destination they should be deleted. | ||
| Built from default files to exclude and customALGoFiles.filesToExclude in settings, resolved against the template folder and original template folder (if any). | ||
|
|
||
| Note: when a custom template is in use, the caller is expected to call | ||
| ReadSettingsWithCurrentCustomTemplateRepoSettings before this function, so that the template's | ||
| customALGoFiles/unusedALGoSystemFiles are already merged into settings. | ||
|
|
||
| The deprecated unusedALGoSystemFiles setting is also applied: matching files are moved from filesToInclude to | ||
| filesToExclude with a deprecation warning. | ||
| .PARAMETER settings | ||
| The settings object containing the customALGoFiles configuration. | ||
| .PARAMETER baseFolder | ||
| The base folder of the repository. This is the target folder where the files will be updated. | ||
| .PARAMETER templateFolder | ||
| The folder where the template files are located. | ||
| .PARAMETER originalTemplateFolder | ||
| The folder where the original template files are located (if any). | ||
| If originalTemplateFolder is provided, it means that there is a custom template in use and custom template files should be included. | ||
| The folder where the original AL-Go template files are located (if any). | ||
| When provided, it signals that a custom template is in use. Both filesToInclude and filesToExclude specs are | ||
| resolved against this folder in addition to templateFolder; entries not already covered by originalSourceFullPath | ||
| tracking are appended to propagate upstream template additions and deletions to consumer repositories. | ||
| .PARAMETER projects | ||
| The list of projects in the repository. | ||
| The projects are used to resolve per-project files. | ||
| .OUTPUTS | ||
| An array containing two elements: the list of files to include and the list of files to exclude. | ||
| Files are represented as hashtables with the following keys: | ||
| - sourceFullPath: The full path to the source file in the template repository. | ||
| - sourceFullPath: The full path to the source file. | ||
| - originalSourceFullPath: The full path to the original source file in the original template repository (if any). | ||
| - type: The type of the file (e.g., workflow, settings). | ||
| - destinationFullPath: The full path to the destination file in the target repository. | ||
|
|
@@ -1032,6 +1120,7 @@ function GetFilesToUpdate { | |
| $projects = @() | ||
| ) | ||
|
|
||
| $hasOriginalTemplate = $null -ne $originalTemplateFolder | ||
| Write-Host "Getting files to update from template folder '$templateFolder', original template folder '$originalTemplateFolder' and base folder '$baseFolder'" | ||
|
|
||
| # Send telemetery about customALGoFiles usage | ||
|
|
@@ -1041,32 +1130,38 @@ function GetFilesToUpdate { | |
| if ($settings.customALGoFiles.filesToExclude.Count -gt 0) { | ||
| Trace-Information -Message "Usage: Custom AL-Go Files (Exclude)" | ||
| } | ||
|
|
||
| $filesToInclude = GetDefaultFilesToInclude -includeCustomTemplateFiles:$($null -ne $originalTemplateFolder) | ||
| $filesToInclude += $settings.customALGoFiles.filesToInclude | ||
| $filesToInclude = @(ResolveFilePaths -sourceFolder $templateFolder -originalSourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToInclude -projects $projects) | ||
|
|
||
| $filesToExclude = GetDefaultFilesToExclude -settings $settings | ||
| $filesToExclude += $settings.customALGoFiles.filesToExclude | ||
| $filesToExclude = @(ResolveFilePaths -sourceFolder $templateFolder -originalSourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToExclude -projects $projects) | ||
|
|
||
| # Exclude files from filesToExclude that are not in filesToInclude | ||
| $filesToExclude = @($filesToExclude | Where-Object { | ||
| $fileToExclude = $_ | ||
| $include = $filesToInclude | Where-Object { $_.sourceFullPath -eq $fileToExclude.sourceFullPath } | ||
| if(-not $include) { | ||
| OutputDebug "Excluding file $($fileToExclude.sourceFullPath) from exclude list as it is not in the include list" | ||
| } | ||
| return $include | ||
| # Determine files to include | ||
| $filesToIncludeUnresolved = GetDefaultFilesToInclude -includeCustomTemplateFiles:$hasOriginalTemplate | ||
| $filesToIncludeUnresolved += $settings.customALGoFiles.filesToInclude | ||
| $filesToInclude = @(ResolveFilePaths -sourceFolder $templateFolder -originalSourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToIncludeUnresolved -projects $projects) | ||
| if ($hasOriginalTemplate) { | ||
| $filesToInclude += @(ResolveFilePaths -sourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToIncludeUnresolved -projects $projects) | ||
| } | ||
| # Deduplicate files to include based on destinationFullPath, keeping the first one (default > settings; template folder > original template folder) | ||
| $filesToInclude = @($filesToInclude | Group-Object { $_.destinationFullPath } | Sort-Object -Property Name | ForEach-Object { $_.Group[0] }) | ||
|
|
||
| # Determine files to exclude | ||
| $filesToExcludeUnresolved = GetDefaultFilesToExclude -settings $settings | ||
| $filesToExcludeUnresolved += $settings.customALGoFiles.filesToExclude | ||
| $filesToExclude = @(ResolveFilePaths -sourceFolder $templateFolder -originalSourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToExcludeUnresolved -projects $projects) | ||
| if ($hasOriginalTemplate) { | ||
| $filesToExclude += @(ResolveFilePaths -sourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToExcludeUnresolved -projects $projects) | ||
| } | ||
| # filesToExclude is not deduplicated by destinationFullPath here. | ||
| # Its destinationFullPath is never part of the actual output; only sourceFullPath is used below to match against filesToInclude. | ||
|
|
||
| # Map files from filesToExclude to files that are in filesToInclude (based on source) | ||
| # Settings for filesToExclude only define the sources (sourceFolder and filter) but not the destinations (destinationFolder, destinationName and perProject) | ||
| $filesToExclude = @($filesToInclude | Where-Object { | ||
| $fileToInclude = $_ | ||
| return $filesToExclude | Where-Object { $_.sourceFullPath -eq $fileToInclude.sourceFullPath } | ||
| }) | ||
|
|
||
| # Exclude files from filesToInclude that are in filesToExclude | ||
| # Exclude files from filesToInclude that are in filesToExclude (based on source) | ||
| $filesToInclude = @($filesToInclude | Where-Object { | ||
| $fileToInclude = $_ | ||
| $include = -not ($filesToExclude | Where-Object { $_.sourceFullPath -eq $fileToInclude.sourceFullPath }) | ||
| if(-not $include) { | ||
| OutputDebug "Excluding file $($fileToInclude.sourceFullPath) from include as it is in the exclude list" | ||
| } | ||
| $file = $_ | ||
| $include = -not ($filesToExclude | Where-Object { $_.sourceFullPath -eq $file.sourceFullPath }) | ||
| if (-not $include) { OutputDebug "Excluding source file '$($file.sourceFullPath)' from include list as it is in the exclude list" } | ||
| return $include | ||
| }) | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.