diff --git a/Actions/.Modules/ReadSettings.psm1 b/Actions/.Modules/ReadSettings.psm1
index ec6dc55885..4fd0a84787 100644
--- a/Actions/.Modules/ReadSettings.psm1
+++ b/Actions/.Modules/ReadSettings.psm1
@@ -260,6 +260,7 @@ function GetDefaultSettings
"customALGoFiles" = [ordered]@{
"filesToInclude" = @()
"filesToExclude" = @()
+ "filesToRemove" = @()
}
"postponeProjectInBuildOrder" = $false
}
diff --git a/Actions/.Modules/settings.schema.json b/Actions/.Modules/settings.schema.json
index 8cf29ab0d0..37554a8b7d 100644
--- a/Actions/.Modules/settings.schema.json
+++ b/Actions/.Modules/settings.schema.json
@@ -739,6 +739,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",
@@ -763,6 +764,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",
@@ -777,6 +779,31 @@
}
}
}
+ },
+ "filesToRemove": {
+ "description": "An array of file specifications to unconditionally remove from the repository during 'Update AL-Go System Files', regardless of whether the files exist in the template. Useful for cleaning up files that have been removed from the template but may still exist in repositories.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "sourceFolder": {
+ "type": "string",
+ "description": "The source folder from which to remove files, relative to the template repository root."
+ },
+ "filter": {
+ "type": "string",
+ "description": "A filter string to select which files to remove. It can contain '*' and '?' wildcards."
+ },
+ "destinationFolder": {
+ "type": "string",
+ "description": "The destination folder where the files should be removed, relative to the repository root."
+ },
+ "perProject": {
+ "type": "boolean",
+ "description": "Indicates whether the removal should be applied per project. In that case, destinationFolder is relative to each project folder."
+ }
+ }
+ }
}
}
},
diff --git a/Actions/CheckForUpdates/CheckForUpdates.HelperFunctions.ps1 b/Actions/CheckForUpdates/CheckForUpdates.HelperFunctions.ps1
index 3482ce5d0c..a14ed7c513 100644
--- a/Actions/CheckForUpdates/CheckForUpdates.HelperFunctions.ps1
+++ b/Actions/CheckForUpdates/CheckForUpdates.HelperFunctions.ps1
@@ -943,6 +943,91 @@ function ResolveFilePaths {
return @($fullFilePaths)
}
+<#
+.SYNOPSIS
+Resolves file paths for files that already exist in the destination folder (e.g. files to remove).
+
+.DESCRIPTION
+This function is a wrapper around ResolveFilePaths for resolving file specs that live in the destination
+(target repo) folder rather than in a template folder (source and destination are the same folder).
+
+Before calling ResolveFilePaths, each file spec is normalized:
+- destinationFolder (if specified) is used as the effective sourceFolder, because in the destination repo
+ the file lives at its destination path, not at the template-relative source path.
+- destinationName (if specified) overrides the filter, because the file may have been renamed at the destination.
+- perProject is cleared to $false on the normalized spec; per-project expansion is handled directly here.
+
+For files marked as perProject, the function iterates over all projects and calls ResolveFilePaths once per
+project with a project-scoped folder (destinationFolder/project) as both source and destination.
+For files not marked as perProject, ResolveFilePaths is called once with destinationFolder as both source
+and destination.
+
+.PARAMETER destinationFolder
+The folder that serves as both source and destination. Typically the root of the target repository.
+
+.PARAMETER files
+An array of hashtables specifying the files to resolve. Each hashtable can contain the following keys:
+- sourceFolder: The subfolder within the destination folder to search for files (default is current folder).
+ Note: if destinationFolder is specified, it takes precedence as the effective lookup folder.
+- filter: The file filter to apply when searching for files (default is all files).
+ Note: if destinationName is specified, it overrides the filter.
+- type: The type of the files (default is empty).
+- destinationFolder: The subfolder within the destination folder where the files are located. Takes
+ precedence over sourceFolder as the effective lookup folder.
+- destinationName: The expected filename in the destination folder. Overrides filter when specified.
+- perProject: A boolean indicating whether the files are per project (default is false).
+
+.PARAMETER projects
+An array of project names used when resolving per-project file paths.
+
+.OUTPUTS
+An array of hashtables, each containing:
+- sourceFullPath: The full path to the source file.
+- originalSourceFullPath: Always $null (no original template folder in this context).
+- type: The type of the file.
+- destinationFullPath: The full path to the destination file.
+#>
+function ResolveFilePathsInDestinationFolder {
+ Param(
+ [Parameter(Mandatory=$true)]
+ [string] $destinationFolder,
+ [array] $files = @(),
+ [string[]] $projects = @()
+ )
+ if(-not $files) {
+ return @()
+ }
+
+ $fullFilePaths = @()
+ foreach($file in $files) {
+ $destinationFile = $file.Clone()
+ $destinationFile.perProject = $false
+
+ # Replace sourceFolder with destinationFolder (if specified) since we are resolving against the destination folder
+ if($file.Keys -contains 'destinationFolder') {
+ $destinationFile.sourceFolder = $file.destinationFolder
+ }
+ # Replace filter with destinationName (if specified) since we are resolving against the destination folder
+ if($file.Keys -contains 'destinationName' -and ($file.destinationName)) {
+ $destinationFile.filter = $file.destinationName
+ }
+
+ if ($file.Keys -contains 'perProject' -and $file.perProject -eq $true) {
+ foreach ($project in $projects) {
+ if ($project -eq '.') {
+ $project = '' # If project is '.', it means the root folder, so we use an empty string
+ }
+ $projectFolder = Join-Path $destinationFolder $project
+ $fullFilePaths += ResolveFilePaths -sourceFolder $projectFolder -destinationFolder $projectFolder -files @($destinationFile)
+ }
+ } else {
+ $fullFilePaths += ResolveFilePaths -sourceFolder $destinationFolder -destinationFolder $destinationFolder -files @($destinationFile)
+ }
+ }
+
+ return @($fullFilePaths)
+}
+
function GetDefaultFilesToInclude {
Param(
[switch] $includeCustomTemplateFiles
@@ -993,27 +1078,44 @@ function GetDefaultFilesToExclude {
<#
.SYNOPSIS
- Get the list of files from the template repository to include and exclude based on the provided settings.
+ Compute the lists of files to include, exclude, and remove when synchronizing a repository from its AL-Go template.
.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 three lists by merging defaults, repository settings, template 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 and templateSettings, 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 and templateSettings, resolved against the template folder and original template folder (if any).
+ 3. filesToRemove: Files to unconditionally delete from the destination.
+ Built from customALGoFiles.filesToRemove in settings and templateSettings and resolved against template folder, the original template folder (if any), and the destination folder.
+
+ 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.
+ The settings object containing customALGoFiles (filesToInclude, filesToExclude, filesToRemove) and the
+ deprecated unusedALGoSystemFiles 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 templateSettings
+ The settings object from the template repository (if any). Both customALGoFiles and unusedALGoSystemFiles
+ are merged from this object when it is provided.
.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.
+ An array containing three elements: the list of files to include, the list of files to exclude, and the list of
+ files to remove.
+ All entries are hashtables with the following keys:
+ - sourceFullPath: The full path to the source file.
- originalSourceFullPath: The full path to the original source file in the original template repository (if any).
+ Always $null for filesToRemove entries.
- type: The type of the file (e.g., workflow, settings).
- destinationFullPath: The full path to the destination file in the target repository.
#>
@@ -1025,50 +1127,109 @@ function GetFilesToUpdate {
$baseFolder,
[Parameter(Mandatory=$true)]
$templateFolder,
+ $templateSettings = $null,
$originalTemplateFolder = $null,
$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
if ($settings.customALGoFiles.filesToInclude.Count -gt 0) {
- Trace-Information -Message "Usage: Custom AL-Go Files (Include)"
+ Trace-Information -Message "Usage: Custom AL-Go Files (Include) of repository"
}
if ($settings.customALGoFiles.filesToExclude.Count -gt 0) {
- Trace-Information -Message "Usage: Custom AL-Go Files (Exclude)"
+ Trace-Information -Message "Usage: Custom AL-Go Files (Exclude) of repository"
+ }
+ if ($settings.customALGoFiles.filesToRemove.Count -gt 0) {
+ Trace-Information -Message "Usage: Custom AL-Go Files (Remove) of repository"
+ }
+
+ if ($null -ne $templateSettings) {
+ if ($templateSettings.customALGoFiles.filesToInclude.Count -gt 0) {
+ Trace-Information -Message "Usage: Custom AL-Go Files (Include) of template"
+ }
+ if ($templateSettings.customALGoFiles.filesToExclude.Count -gt 0) {
+ Trace-Information -Message "Usage: Custom AL-Go Files (Exclude) of template"
+ }
+ if ($templateSettings.customALGoFiles.filesToRemove.Count -gt 0) {
+ Trace-Information -Message "Usage: Custom AL-Go Files (Remove) of template"
+ }
}
- $filesToInclude = GetDefaultFilesToInclude -includeCustomTemplateFiles:$($null -ne $originalTemplateFolder)
- $filesToInclude += $settings.customALGoFiles.filesToInclude
- $filesToInclude = @(ResolveFilePaths -sourceFolder $templateFolder -originalSourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToInclude -projects $projects)
+ # Determine files to include
+ $filesToIncludeUnresolved = GetDefaultFilesToInclude -includeCustomTemplateFiles:$hasOriginalTemplate
+ if ($null -ne $templateSettings) {
+ $filesToIncludeUnresolved += $templateSettings.customALGoFiles.filesToInclude
+ }
+ $filesToIncludeUnresolved += $settings.customALGoFiles.filesToInclude
+ $filesToInclude = @()
+ if ($hasOriginalTemplate) {
+ $filesToInclude += @(ResolveFilePaths -sourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToIncludeUnresolved -projects $projects)
+ }
+ $filesToInclude += @(ResolveFilePaths -sourceFolder $templateFolder -originalSourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToIncludeUnresolved -projects $projects)
+ # Remove duplicates based on destinationFullPath, keeping the last one (setting > template settings > defaults; template folder > original template folder)
+ $filesToInclude = @($filesToInclude | Group-Object -Property destinationFullPath | ForEach-Object { $_.Group[-1] })
- $filesToExclude = GetDefaultFilesToExclude -settings $settings
- $filesToExclude += $settings.customALGoFiles.filesToExclude
- $filesToExclude = @(ResolveFilePaths -sourceFolder $templateFolder -originalSourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToExclude -projects $projects)
+ # Determine files to exclude
+ $filesToExcludeUnresolved = GetDefaultFilesToExclude -settings $settings
+ if ($null -ne $templateSettings) {
+ $filesToExcludeUnresolved += $templateSettings.customALGoFiles.filesToExclude
+ }
+ $filesToExcludeUnresolved += $settings.customALGoFiles.filesToExclude
+ $filesToExclude = @()
+ if ($hasOriginalTemplate) {
+ $filesToExclude += @(ResolveFilePaths -sourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToExcludeUnresolved -projects $projects)
+ }
+ $filesToExclude += @(ResolveFilePaths -sourceFolder $templateFolder -originalSourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToExcludeUnresolved -projects $projects)
+ # Remove duplicates based on destinationFullPath, keeping the last one (setting > template settings > defaults; template folder > original template folder)
+ $filesToExclude = @($filesToExclude | Group-Object -Property destinationFullPath | ForEach-Object { $_.Group[-1] })
- # 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"
- }
+ # Determine files to remove
+ $filesToRemoveUnresolved = @()
+ if ($null -ne $templateSettings) {
+ $filesToRemoveUnresolved += $templateSettings.customALGoFiles.filesToRemove
+ }
+ $filesToRemoveUnresolved += $settings.customALGoFiles.filesToRemove
+ $filesToRemove = @()
+ if ($hasOriginalTemplate) {
+ $filesToRemove += @(ResolveFilePaths -sourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToRemoveUnresolved -projects $projects)
+ }
+ $filesToRemove += @(ResolveFilePaths -sourceFolder $templateFolder -originalSourceFolder $originalTemplateFolder -destinationFolder $baseFolder -files $filesToRemoveUnresolved -projects $projects)
+ $filesToRemove += @(ResolveFilePathsInDestinationFolder -destinationFolder $baseFolder -files $filesToRemoveUnresolved -projects $projects)
+ # Remove duplicates based on destinationFullPath, keeping the last one (settings > template settings; base folder > template folder > original template folder)
+ $filesToRemove = @($filesToRemove | Group-Object -Property destinationFullPath | ForEach-Object { $_.Group[-1] })
+
+ # Exclude files from filesToInclude that are in filesToRemove (based on destination)
+ $filesToInclude = @($filesToInclude | Where-Object {
+ $fileToInclude = $_
+ $include = -not ($filesToRemove | Where-Object { $_.destinationFullPath -eq $fileToInclude.destinationFullPath })
+ if (-not $include) { OutputDebug "Excluding destination file '$($fileToInclude.destinationFullPath)' from include list as it is in the remove list" }
return $include
})
- # Exclude files from filesToInclude that are in filesToExclude
- $filesToInclude = @($filesToInclude | Where-Object {
+ # 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 = $_
- $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"
- }
+ return $filesToExclude | Where-Object { $_.sourceFullPath -eq $fileToInclude.sourceFullPath }
+ })
+
+ # Exclude files from filesToInclude that are in filesToExclude (based on source)
+ $filesToInclude = @($filesToInclude | Where-Object {
+ $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
})
# Apply unusedALGoSystemFiles logic
- $unusedALGoSystemFiles = $settings.unusedALGoSystemFiles
+ # Include unusedALGoSystemFiles from the template settings (if any) to also propagate template's deprecated file removals
+ $unusedALGoSystemFiles = @($settings.unusedALGoSystemFiles)
+ if ($null -ne $templateSettings) {
+ $unusedALGoSystemFiles += @($templateSettings.unusedALGoSystemFiles)
+ }
# Exclude unusedALGoSystemFiles from $filesToInclude and add them to $filesToExclude
$unusedFilesToExclude = $filesToInclude | Where-Object { $unusedALGoSystemFiles -contains (Split-Path -Path $_.sourceFullPath -Leaf) }
@@ -1086,6 +1247,7 @@ function GetFilesToUpdate {
$fileFormatter = { param($file) " -Source: $($file.sourceFullPath), Destination: $($file.destinationFullPath), Type: $($file.type), Original Source: $($file.originalSourceFullPath)"}
OutputArray -Message "Files to include: $($filesToInclude.Count)" -Array $filesToInclude -Formatter $fileFormatter
OutputArray -Message "Files to exclude: $($filesToExclude.Count)" -Array $filesToExclude -Formatter $fileFormatter
+ OutputArray -Message "Files to remove: $($filesToRemove.Count)" -Array $filesToRemove -Formatter $fileFormatter
- return @($filesToInclude), @($filesToExclude)
+ return @($filesToInclude), @($filesToExclude), @($filesToRemove)
}
diff --git a/Actions/CheckForUpdates/CheckForUpdates.ps1 b/Actions/CheckForUpdates/CheckForUpdates.ps1
index 46a95da706..bef77b9a4d 100644
--- a/Actions/CheckForUpdates/CheckForUpdates.ps1
+++ b/Actions/CheckForUpdates/CheckForUpdates.ps1
@@ -52,7 +52,7 @@ if ($token) {
# 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
+$repoSettings = ReadSettings -buildMode '' -project '' -workflowName '' -userName '' -branchName '' -trigger '' | ConvertTo-HashTable -recurse
$templateSha = $repoSettings.templateSha
# If templateUrl has changed, download latest version of the template repository (ignore templateSha)
@@ -61,6 +61,7 @@ if ($repoSettings.templateUrl -ne $templateUrl -or $templateSha -eq '') {
}
$originalTemplateFolder = $null
+$templateRepoSettings = $null
$templateFolder = DownloadTemplateRepository -token $token -templateUrl $templateUrl -templateSha ([ref]$templateSha) -downloadLatest $downloadLatest
$templateFolder = GetSrcFolder -repoType $repoSettings.type -templateUrl $templateUrl -templateFolder $templateFolder
Write-Host "Template Folder: $templateFolder"
@@ -71,42 +72,40 @@ $templateInfo = "$templateOwner/$($templateUrl.Split('/')[4])"
$isDirectALGo = IsDirectALGo -templateUrl $templateUrl
if (-not $isDirectALGo) {
- $templateRepoSettingsFile = Join-Path $templateFolder $RepoSettingsFile
- 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) {
- # The template repository is a url to another AL-Go repository (a custom template repository)
- Trace-Information -Message "Using custom AL-Go template repository"
-
- # TemplateUrl and TemplateSha from .github/AL-Go-Settings.json in the custom template repository points to the "original" template repository
- # Copy files and folders from the custom template repository, but grab the unmodified file from the "original" template repository if it exists and apply customizations
- # Copy .github/AL-Go-Settings.json to .github/templateRepoSettings.doNotEdit.json (will be read before .github/AL-Go-Settings.json in the final repo)
- # Copy .AL-Go/settings.json to .github/templateProjectSettings.doNotEdit.json (will be read before .AL-Go/settings.json in the final repo)
-
- Write-Host "Custom AL-Go template repository detected, downloading the 'original' template repository"
- $originalTemplateUrl = $templateRepoSettings.templateUrl
- if ($templateRepoSettings.Keys -contains "templateSha") {
- $originalTemplateSha = $templateRepoSettings.templateSha
- }
- else {
- $originalTemplateSha = ""
- }
+ # Get template Repo settings as a hashtable (do NOT read any variable settings, specific project settings, nor any specific workflow, user or branch settings)
+ $templateRepoSettings = ReadSettings -baseFolder $templateFolder -buildMode '' -project '' -workflowName '' -userName '' -branchName '' -trigger '' -orgSettingsVariableValue '' -repoSettingsVariableValue '' -environmentSettingsVariableValue '' | ConvertTo-HashTable -recurse
+ if ($templateRepoSettings.templateUrl -and $templateRepoSettings.templateUrl -ne $templateUrl) {
+ # The template repository is a url to another AL-Go repository (a custom template repository)
+ Trace-Information -Message "Using custom AL-Go template repository"
+
+ # TemplateUrl and TemplateSha from .github/AL-Go-Settings.json in the custom template repository points to the "original" template repository
+ # Copy files and folders from the custom template repository, but grab the unmodified file from the "original" template repository if it exists and apply customizations
+ # Copy .github/AL-Go-Settings.json to .github/templateRepoSettings.doNotEdit.json (will be read before .github/AL-Go-Settings.json in the final repo)
+ # Copy .AL-Go/settings.json to .github/templateProjectSettings.doNotEdit.json (will be read before .AL-Go/settings.json in the final repo)
+
+ Write-Host "Custom AL-Go template repository detected, downloading the 'original' template repository"
+ $originalTemplateUrl = $templateRepoSettings.templateUrl
+ if ($templateRepoSettings.Keys -contains "templateSha") {
+ $originalTemplateSha = $templateRepoSettings.templateSha
+ }
+ else {
+ $originalTemplateSha = ""
+ }
- # Download the "original" template repository - use downloadLatest if no TemplateSha is specified in the custom template repository
- $originalTemplateFolder = DownloadTemplateRepository -token $token -templateUrl $originalTemplateUrl -templateSha ([ref]$originalTemplateSha) -downloadLatest ($originalTemplateSha -eq '')
- $originalTemplateFolder = GetSrcFolder -repoType $repoSettings.type -templateUrl $originalTemplateUrl -templateFolder $originalTemplateFolder
+ # Download the "original" template repository - use downloadLatest if no TemplateSha is specified in the custom template repository
+ $originalTemplateFolder = DownloadTemplateRepository -token $token -templateUrl $originalTemplateUrl -templateSha ([ref]$originalTemplateSha) -downloadLatest ($originalTemplateSha -eq '')
+ $originalTemplateFolder = GetSrcFolder -repoType $repoSettings.type -templateUrl $originalTemplateUrl -templateFolder $originalTemplateFolder
- Write-Host "Original Template Folder: $originalTemplateFolder"
+ Write-Host "Original Template Folder: $originalTemplateFolder"
- # Set TemplateBranch and TemplateOwner
- # Keep TemplateUrl and TemplateSha pointing to the custom template repository
- $templateBranch = $originalTemplateUrl.Split('@')[1]
- $templateOwner = $originalTemplateUrl.Split('/')[3]
+ # Set TemplateBranch and TemplateOwner
+ # Keep TemplateUrl and TemplateSha pointing to the custom template repository
+ $templateBranch = $originalTemplateUrl.Split('@')[1]
+ $templateOwner = $originalTemplateUrl.Split('/')[3]
- $isDirectALGo = IsDirectALGo -templateUrl $originalTemplateUrl
- if ($isDirectALGo) {
- Trace-Information -Message "Original template repository is direct AL-Go"
- }
+ $isDirectALGo = IsDirectALGo -templateUrl $originalTemplateUrl
+ if ($isDirectALGo) {
+ Trace-Information -Message "Original template repository is direct AL-Go"
}
}
}
@@ -115,7 +114,7 @@ if (-not $isDirectALGo) {
$baseFolder = $ENV:GITHUB_WORKSPACE
$projects = @(GetProjectsFromRepository -baseFolder $baseFolder -projectsFromSettings $repoSettings.projects)
-$filesToInclude, $filesToExclude = GetFilesToUpdate -settings $repoSettings -projects $projects -baseFolder $baseFolder -templateFolder $templateFolder -originalTemplateFolder $originalTemplateFolder
+$filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $repoSettings -projects $projects -baseFolder $baseFolder -templateFolder $templateFolder -templateSettings $templateRepoSettings -originalTemplateFolder $originalTemplateFolder
# $updateFiles will hold an array of files, which needs to be updated
$updateFiles = @()
@@ -203,8 +202,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 {
+# Remove files that are in $filesToExclude or $filesToRemove and exist in the repository
+$removeFiles = @($filesToExclude) + @($filesToRemove) | Where-Object { $_ -and (Test-Path -Path $_.destinationFullPath -PathType Leaf) } | ForEach-Object {
$relativePath = Resolve-Path -Path $_.destinationFullPath -Relative
Write-Host "File marked for removal: $relativePath"
$relativePath
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 007110df8f..e29c5f755f 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -1,3 +1,13 @@
+### Enhanced `customALGoFiles` setting
+
+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`, `filesToExclude`, and `filesToRemove` 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 that are not overridden by your custom template are propagated to consumer repos. When a file exists in both, the custom template version takes precedence.
+- **`filesToExclude`** now also resolves files from the original AL-Go template (same dual-resolution as `filesToInclude`). Files resolved by `filesToInclude` whose source matches a `filesToExclude` entry are not copied to consumer repos, and existing copies are removed.
+- **`filesToRemove`** (new property): Unconditionally removes matching files from consumer repos. Files are searched in both the template and end repository. Takes precedence over `filesToInclude`. Entries use `sourceFolder` (relative to the template), `filter`, and optionally `destinationFolder` and `perProject`.
+
+Read more at [Customizing AL-Go for GitHub](Scenarios/CustomizingALGoForGitHub.md#Using-custom-template-files).
+
### Use artifact manifest to pick .NET runtime for assembly probing
When compiling apps with the workspace compiler, AL-Go now reads the `dotNetVersion` from the BC artifact's `manifest.json` (copied into the compiler folder by BcContainerHelper) and selects an installed .NET runtime whose major version matches. This avoids version drift between the build agent's highest installed runtime and the platform the artifact was built against. If the manifest does not declare a `dotNetVersion`, or no installed runtime matches the required major, versioned .NET assembly probing paths are omitted (a warning is logged in the latter case).
diff --git a/Scenarios/CustomizingALGoForGitHub.md b/Scenarios/CustomizingALGoForGitHub.md
index f5d80b24ad..a3f8738178 100644
--- a/Scenarios/CustomizingALGoForGitHub.md
+++ b/Scenarios/CustomizingALGoForGitHub.md
@@ -231,7 +231,7 @@ Repositories based on your custom template will notify you that changes are avai
When updating AL-Go for GitHub, only specific system files from the template repository are synced to your end repository by default. Files such as `README.md`, `.gitignore`, and other documentation or non-system files are not updated by AL-Go for GitHub. By default, AL-Go syncs workflow files in `.github/workflows`, PowerShell scripts in `.github` and `.AL-Go`, and configuration files required for AL-Go operations. When using custom template repositories, you may need to add additional files related to AL-Go for GitHub, such as script overrides, complementary workflows, or centrally managed files not part of the official AL-Go templates.
-In order to instruct AL-Go which files to look for at the template repository, you need to define the `customALGoFiles` setting. The setting is an object that can contain two properties: `filesToInclude` and `filesToExclude`.
+In order to instruct AL-Go which files to look for at the template repository, you need to define the `customALGoFiles` setting. The setting is an object that can contain three properties: `filesToInclude`, `filesToExclude`, and `filesToRemove`.
`filesToInclude`, as the name suggests, is an array of file configurations that will instruct AL-Go which files to include (create/update). Every item in the array may contain the following properties:
@@ -243,6 +243,17 @@ In order to instruct AL-Go which files to look for at the template repository, y
> [!NOTE]
> `filesToInclude` is used to define all the template files that will be used by AL-Go for GitHub. If a template file is not matched, it will be ignored. Please pay attention, when changing the file configurations: there might be template files that were previously propagated to your repositories. In case these files are no longer matched via `filesToInclude`, AL-Go for GitHub will ignore them and you might have to remove them manually.
+When using a custom template repository, `filesToInclude` also resolves files from the **original** AL-Go template (i.e. the official [AL-Go-PTE](https://github.com/microsoft/AL-Go-PTE) or [AL-Go-AppSource](https://github.com/microsoft/AL-Go-AppSource) template). This means files present in the official AL-Go template that are not overridden by your custom template are still propagated to consumer repositories. When a file exists in both the original template and your custom template, the custom template version takes precedence.
+
+The following table summarizes how `filesToInclude` resolves files when a custom template is in use:
+
+| File is present in original template | File is present in custom template | File is matched by `filesToInclude` | Result |
+|---|---|---|---|
+| Yes | No | Yes | File from **original template** is propagated |
+| No | Yes | Yes | File from **custom template** is propagated |
+| Yes | Yes | Yes | File from **custom template** is used (takes precedence) |
+| Yes/No | Yes/No | No | File is **ignored** |
+
`filesToExclude` is an array of file configurations that will instruct AL-Go which files to exclude (remove) from `filesToInclude`. Every item in the array may contain the following properties:
- `sourceFolder`: A path to a folder, relative to the template, where to look for files. If not specified the root folder is implied. _Example_: `src/scripts`.
@@ -251,18 +262,39 @@ In order to instruct AL-Go which files to look for at the template repository, y
> [!NOTE] `filesToExclude` is an array of file configurations already included in `filesToInclude`. These files are specifically marked to be excluded from the update process.
> This mechanism allows for fine-grained control over which files are propagated to the end repository and which should be explicitly removed, ensuring that unwanted files are not carried forward during updates.
+> [!TIP]
+> When using a custom template repository, you can use `filesToExclude` in the custom template's settings to prevent files from the original AL-Go template from being propagated to consumer repos. For example, if the original template includes a workflow you don't want in your consumer repos, adding it to `filesToExclude` in your custom template's settings will remove it during the next update.
+
The following table summarizes how AL-Go for GitHub manages file updates and exclusions when using custom template files. Say, there is a file (e.g. `file.ps1`) in the template repository.
| File is present in end repo | File is matched by `filesToInclude` | File is matched by `filesToExclude` | Result |
|---|---|---|---|
| Yes/No | Yes | No | The file is **updated/created** in the end repo |
| Yes | Yes | Yes | The file is **removed** from the end repo, as it's matched for exclusion |
-| Yes | No | Yes | The files is **_not_** removed as it was not matched as update |
+| Yes | No | Yes | The file is **_not_** removed as it was not matched as update |
| No | Yes/No | Yes | The file is **_not_ created** in the end repo, as it's matched for exclusion |
+`filesToRemove` is an array of file configurations that will instruct AL-Go which files to unconditionally remove from the end repository. Unlike `filesToExclude`, files matched by `filesToRemove` do not need to be part of `filesToInclude` first — they are removed regardless of whether they are included in the update process. Files are searched in both the template repository and the end repository. Every item in the array may contain the following properties:
+
+- `sourceFolder`: A path to a folder, relative to the template, where to look for files. If not specified the root folder is implied. `*` characters are not supported. _Example_: `.github/workflows`.
+- `filter`: A string to use for filtering in the specified source path. It can contain `*` and `?` wildcards. _Example_: `deprecated-*.yaml` or `oldScript.ps1`.
+- `destinationFolder`: A path to a folder, relative to the end repository, where the files are located. If not specified, defaults to the same as the source file folder. _Example_: `.github/workflows`.
+- `perProject`: A boolean that indicates whether the matched files should be removed for all available AL-Go projects. In that case, `destinationFolder` is relative to the project folder. _Example_: `.AL-Go/scripts`.
+
+> [!NOTE]
+> `filesToRemove` takes precedence over both `filesToInclude` and `filesToExclude`. If a file is matched by `filesToRemove`, it will be removed from the end repository even if it is also matched by `filesToInclude`. The file will also be excluded from the `filesToInclude` and `filesToExclude` lists, so it will not be created or updated.
+
+The following table summarizes how AL-Go for GitHub manages file removals when using `filesToRemove`:
+
+| File is present in end repo | File is matched by `filesToInclude` | File is matched by `filesToExclude` | File is matched by `filesToRemove` | Result |
+|---|---|---|---|---|
+| Yes/No | Yes/No | Yes/No | No | See `filesToInclude`/`filesToExclude` behavior above |
+| Yes | Yes/No | Yes/No | Yes | The file is **removed** from the end repo (remove wins over include/exclude) |
+| No | Yes/No | Yes/No | Yes | The file is **not created** in the end repo (excluded from `filesToInclude`/`filesToExclude`) |
+
### Examples of using custom template files
-Below are examples of how to use the `filesToInclude` and `filesToExclude` settings in your AL-Go configuration.
+Below are examples of how to use the `filesToInclude`, `filesToExclude`, and `filesToRemove` settings in your AL-Go configuration.
#### Example 1: Updating specific scripts for all projects
@@ -348,6 +380,61 @@ Note that AL-Go for GitHub already syncs all workflow files under `.github/workf
This configuration updates all JSON files from `shared/config` and all PowerShell scripts from `.github/scripts`, but excludes `legacy-config.json` from being updated or created.
+#### Example 5: Removing a deprecated workflow from all consumer repos
+
+```json
+"customALGoFiles": {
+ "filesToRemove": [
+ {
+ "sourceFolder": ".github/workflows",
+ "filter": "deprecated-workflow.yaml"
+ }
+ ]
+}
+```
+
+This configuration will remove `deprecated-workflow.yaml` from the `.github/workflows` folder in all consumer repositories, regardless of whether it is still present in the template. This is useful when a workflow has been retired and should be cleaned up from all repositories.
+
+#### Example 6: Removing per-project scripts that are no longer needed
+
+```json
+"customALGoFiles": {
+ "filesToRemove": [
+ {
+ "sourceFolder": ".AL-Go/scripts",
+ "filter": "OldBuildScript.ps1",
+ "perProject": true
+ }
+ ]
+}
+```
+
+This will remove `OldBuildScript.ps1` from the `.AL-Go/scripts` folder of every AL-Go project in the target repository.
+
+#### Example 7: Combining `filesToInclude` with `filesToRemove` for migration
+
+```json
+"customALGoFiles": {
+ "filesToInclude": [
+ {
+ "sourceFolder": ".github/scripts",
+ "filter": "NewBuildHelper.ps1",
+ "destinationFolder": ".AL-Go/scripts",
+ "perProject": true
+ }
+ ],
+ "filesToRemove": [
+ {
+ "sourceFolder": ".AL-Go/scripts",
+ "filter": "LegacyBuildHelper.ps1",
+ "perProject": true
+ }
+ ]
+}
+```
+
+This configuration introduces a new script (`NewBuildHelper.ps1`) to all projects while simultaneously removing the old script (`LegacyBuildHelper.ps1`) it replaces. This pattern is useful for migrating from one file to another across all consumer repositories.
+
These examples demonstrate how you can fine-tune which files are propagated from your template repository and which are excluded, giving you granular control over your AL-Go customization process.
## Forking AL-Go for GitHub and making your "own" **public** version
diff --git a/Scenarios/settings.md b/Scenarios/settings.md
index 6ba69f6d9a..69987b592a 100644
--- a/Scenarios/settings.md
+++ b/Scenarios/settings.md
@@ -247,7 +247,7 @@ Please read the release notes carefully when installing new versions of AL-Go fo
| BcContainerHelperVersion | This setting can be set to a specific version (ex. 3.0.8) of BcContainerHelper to force AL-Go to use this version. **latest** means that AL-Go will use the latest released version. **preview** means that AL-Go will use the latest preview version. **dev** means that AL-Go will use the dev branch of containerhelper. | latest (or preview for AL-Go preview) |
| unusedALGoSystemFiles (**deprecated**) | An array of AL-Go System Files, which won't be updated during Update AL-Go System Files. They will instead be removed.
Use this setting with care, as this can break the AL-Go for GitHub functionality and potentially leave your repo no longer functional. | [ ] |
| reportSuppressedDiagnostics | If this setting is set to true, the AL compiler will report diagnostics which are suppressed in the code using the pragma `#pragma warning disable `. This can be useful if you want to ensure that no warnings are suppressed in your code. | false |
-| customALGoFiles | An object to configure custom AL-Go files, that will be updated during "Update AL-Go System Files" workflow. The object can contain properties `filesToInclude` and `filesToExclude`. Read more at [Customizing AL-Go](CustomizingALGoForGitHub.md#Using-custom-template-files). | `{ "filesToInclude": [], "filesToExclude": [] }`
+| customALGoFiles | An object to configure custom AL-Go files, that will be updated during "Update AL-Go System Files" workflow. The object can contain properties `filesToInclude`, `filesToExclude`, and `filesToRemove`. Read more at [Customizing AL-Go](CustomizingALGoForGitHub.md#Using-custom-template-files). | `{ "filesToInclude": [], "filesToExclude": [], "filesToRemove": [] }`
## Overwrite settings
diff --git a/Tests/CheckForUpdates.Action.Test.ps1 b/Tests/CheckForUpdates.Action.Test.ps1
index be6cfa9b98..1777c840af 100644
--- a/Tests/CheckForUpdates.Action.Test.ps1
+++ b/Tests/CheckForUpdates.Action.Test.ps1
@@ -1808,6 +1808,181 @@ Describe "ResolveFilePaths" {
}
}
+Describe "ResolveFilePathsInDestinationFolder" {
+ BeforeAll {
+ $actionName = "CheckForUpdates"
+ $scriptRoot = Join-Path $PSScriptRoot "..\Actions\$actionName" -Resolve
+ . (Join-Path -Path $scriptRoot -ChildPath "CheckForUpdates.HelperFunctions.ps1")
+
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'destinationFolder', Justification = 'False positive.')]
+ $destinationFolder = Join-Path $PSScriptRoot "destResolveFolder"
+
+ New-Item -Path (Join-Path $destinationFolder "folder/File1.txt") -ItemType File -Force | Out-Null
+ New-Item -Path (Join-Path $destinationFolder "folder/File2.log") -ItemType File -Force | Out-Null
+ New-Item -Path (Join-Path $destinationFolder "folder/File3.txt") -ItemType File -Force | Out-Null
+ New-Item -Path (Join-Path $destinationFolder "folder/File4.md") -ItemType File -Force | Out-Null
+ New-Item -Path (Join-Path $destinationFolder "project1/folder/File1.txt") -ItemType File -Force | Out-Null
+ New-Item -Path (Join-Path $destinationFolder "project2/folder/File1.txt") -ItemType File -Force | Out-Null
+
+ # File tree:
+ # destResolveFolder/
+ # ├── folder/
+ # │ ├── File1.txt
+ # │ ├── File2.log
+ # │ ├── File3.txt
+ # │ └── File4.md
+ # └── project1/
+ # └── folder/
+ # └── File1.txt
+ # └── project2/
+ # └── folder/
+ # └── File1.txt
+ }
+
+ AfterAll {
+ if (Test-Path $destinationFolder) {
+ Remove-Item -Path $destinationFolder -Recurse -Force
+ }
+ }
+
+ It 'Returns files matching filter in sourceFolder relative to destinationFolder' {
+ $files = @(@{ sourceFolder = "folder"; filter = "*.txt" })
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files $files)
+
+ $result | Should -Not -BeNullOrEmpty
+ $result.Count | Should -Be 2
+ $result.sourceFullPath | Should -Contain (Join-Path $destinationFolder "folder/File1.txt")
+ $result.sourceFullPath | Should -Contain (Join-Path $destinationFolder "folder/File3.txt")
+ $result.destinationFullPath | Should -Contain (Join-Path $destinationFolder "folder/File1.txt")
+ $result.destinationFullPath | Should -Contain (Join-Path $destinationFolder "folder/File3.txt")
+ }
+
+ It 'destinationFolder key overrides sourceFolder for lookup' {
+ $files = @(@{ sourceFolder = "folder"; filter = "File1.txt"; destinationFolder = "project1/folder" })
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files $files)
+
+ $result | Should -Not -BeNullOrEmpty
+ $result.Count | Should -Be 1
+ $result[0].sourceFullPath | Should -Be (Join-Path $destinationFolder "project1/folder/File1.txt")
+ $result[0].destinationFullPath | Should -Be (Join-Path $destinationFolder "project1/folder/File1.txt")
+ }
+
+ It 'destinationName key overrides filter for filename lookup' {
+ $files = @(@{ sourceFolder = "folder"; filter = "File2.log"; destinationName = "File1.txt" })
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files $files)
+
+ $result | Should -Not -BeNullOrEmpty
+ $result.Count | Should -Be 1
+ $result[0].sourceFullPath | Should -Be (Join-Path $destinationFolder "folder/File1.txt")
+ $result[0].destinationFullPath | Should -Be (Join-Path $destinationFolder "folder/File1.txt")
+ }
+
+ It 'Both destinationFolder and destinationName combined resolve the correct file' {
+ $files = @(@{ sourceFolder = "folder"; filter = "File2.log"; destinationFolder = "project1/folder"; destinationName = "File1.txt" })
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files $files)
+
+ $result | Should -Not -BeNullOrEmpty
+ $result.Count | Should -Be 1
+ $result[0].sourceFullPath | Should -Be (Join-Path $destinationFolder "project1/folder/File1.txt")
+ $result[0].destinationFullPath | Should -Be (Join-Path $destinationFolder "project1/folder/File1.txt")
+ }
+
+ It 'perProject=true with a single project scopes results to that project subfolder' {
+ $files = @(@{ sourceFolder = "folder"; filter = "File1.txt"; perProject = $true })
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files $files -projects @('project1'))
+
+ $result | Should -Not -BeNullOrEmpty
+ $result.Count | Should -Be 1
+ $result[0].sourceFullPath | Should -Be (Join-Path $destinationFolder "project1/folder/File1.txt")
+ $result[0].destinationFullPath | Should -Be (Join-Path $destinationFolder "project1/folder/File1.txt")
+ }
+
+ It "perProject=true with project '.' maps to the root of destinationFolder" {
+ $files = @(@{ sourceFolder = "folder"; filter = "File1.txt"; perProject = $true })
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files $files -projects @('.'))
+
+ $result | Should -Not -BeNullOrEmpty
+ $result.Count | Should -Be 1
+ $result[0].sourceFullPath | Should -Be (Join-Path $destinationFolder "folder/File1.txt")
+ $result[0].destinationFullPath | Should -Be (Join-Path $destinationFolder "folder/File1.txt")
+ }
+
+ It 'perProject=true with empty projects array returns empty result' {
+ $files = @(@{ sourceFolder = "folder"; filter = "*.txt"; perProject = $true })
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files $files -projects @())
+
+ $result | Should -BeNullOrEmpty
+ }
+
+ It 'Mixed perProject=true and perProject=false files in same call returns correct results' {
+ $files = @(
+ @{ sourceFolder = "folder"; filter = "*.log" } # non-perProject
+ @{ sourceFolder = "folder"; filter = "File1.txt"; perProject = $true } # perProject
+ )
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files $files -projects @('project1'))
+
+ $result | Should -Not -BeNullOrEmpty
+ $result.Count | Should -Be 2
+ $result.sourceFullPath | Should -Contain (Join-Path $destinationFolder "folder/File2.log")
+ $result.sourceFullPath | Should -Contain (Join-Path $destinationFolder "project1/folder/File1.txt")
+ }
+
+ It 'perProject=true with multiple projects returns one entry per project' {
+ $files = @(@{ sourceFolder = "folder"; filter = "File1.txt"; perProject = $true })
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files $files -projects @('project1', 'project2'))
+
+ $result | Should -Not -BeNullOrEmpty
+ $result.Count | Should -Be 2
+ $result.sourceFullPath | Should -Contain (Join-Path $destinationFolder "project1/folder/File1.txt")
+ $result.sourceFullPath | Should -Contain (Join-Path $destinationFolder "project2/folder/File1.txt")
+ }
+
+ It 'originalSourceFullPath is always $null for all returned entries' {
+ $files = @(@{ sourceFolder = "folder" })
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files $files)
+
+ $result | Should -Not -BeNullOrEmpty
+ foreach ($entry in $result) {
+ $entry.originalSourceFullPath | Should -Be $null
+ }
+ }
+
+ It 'Returns empty array when files parameter is $null' {
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files $null)
+
+ $result | Should -BeNullOrEmpty
+ }
+
+ It 'Returns empty array when files parameter is an empty array' {
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files @())
+
+ $result | Should -BeNullOrEmpty
+ }
+
+ It 'Returns empty array when no files match the filter' {
+ $files = @(@{ sourceFolder = "folder"; filter = "*.nonexistent" })
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files $files)
+
+ $result | Should -BeNullOrEmpty
+ }
+
+ It 'Skips files outside the destinationFolder when sourceFolder traverses up' {
+ $externalFolder = Join-Path $PSScriptRoot "external"
+ New-Item -Path (Join-Path $externalFolder "external.txt") -ItemType File -Force | Out-Null
+
+ try {
+ $files = @(@{ sourceFolder = "../external"; filter = "*.txt" })
+ $result = @(ResolveFilePathsInDestinationFolder -destinationFolder $destinationFolder -files $files)
+
+ # Should return no results since ../external is outside destinationFolder
+ $result | Should -BeNullOrEmpty
+ }
+ finally {
+ if (Test-Path $externalFolder) { Remove-Item -Path $externalFolder -Recurse -Force }
+ }
+ }
+}
+
Describe "ReplaceOwnerRepoAndBranch" {
BeforeAll {
$actionName = "CheckForUpdates"
@@ -1877,15 +2052,84 @@ Describe "GetFilesToUpdate (general files to update logic)" {
# .
# ├── test.ps1
# ├── test.txt
- # └── test2.txt
+ # ├── test2.txt
+ # └── subfolder
+ # ├── testsub.txt
+ # └── testsub2.txt
+
+ $originalTemplateFolder = Join-Path $PSScriptRoot "originalTemplate"
+ Copy-Item -Path $templateFolder -Destination $originalTemplateFolder -Recurse -Force | Out-Null
+
+ $testOriginalTemplateTxtFile = Join-Path $originalTemplateFolder "test.original.txt"
+ Set-Content -Path $testOriginalTemplateTxtFile -Value "test original template txt file"
+
+ $testOriginalTemplatePSFile = Join-Path $originalTemplateFolder "test.original.ps1"
+ Set-Content -Path $testOriginalTemplatePSFile -Value "# test original template ps file"
+
+ # Display the created files structure for original template folder
+ # .
+ # ├── test.ps1
+ # ├── test.txt
+ # ├── test2.txt
+ # ├── test.original.ps1
+ # ├── test.original.txt
# └── subfolder
- # └── testsub.txt
+ # ├── testsub.txt
+ # └── testsub2.txt
+
+ $baseFolder = Join-Path $PSScriptRoot "base"
+ Copy-Item -Path $templateFolder -Destination $baseFolder -Recurse -Force | Out-Null
+
+ $testBaseTxtFile = Join-Path $baseFolder "test.base.txt"
+ Set-Content -Path $testBaseTxtFile -Value "test base txt file"
+
+ $testBasePSFile = Join-Path $baseFolder "test.base.ps1"
+ Set-Content -Path $testBasePSFile -Value "# test base ps file"
+
+ $baseProject1Folder = Join-Path $baseFolder "project1"
+ Copy-Item -Path $templateFolder -Destination $baseProject1Folder -Recurse -Force | Out-Null
+
+ $baseProject2Folder = Join-Path $baseFolder "project2"
+ Copy-Item -Path $templateFolder -Destination $baseProject2Folder -Recurse -Force | Out-Null
+
+ Remove-Item -Path (Join-Path $baseFolder 'test2.txt') -Recurse -Force | Out-Null
+
+ # Display the created files structure for base folder
+ # .
+ # ├── test.ps1
+ # ├── test.txt
+ # ├── test2.txt
+ # ├── test.base.ps1
+ # ├── test.base.txt
+ # ├── subfolder
+ # │ ├── testsub.txt
+ # │ └── testsub2.txt
+ # ├── project1
+ # │ ├── test.ps1
+ # │ ├── test.txt
+ # │ ├── test2.txt
+ # │ └── subfolder
+ # │ ├── testsub.txt
+ # │ └── testsub2.txt
+ # └── project2
+ # ├── test.ps1
+ # ├── test.txt
+ # ├── test2.txt
+ # └── subfolder
+ # ├── testsub.txt
+ # └── testsub2.txt
}
AfterAll {
if (Test-Path $templateFolder) {
Remove-Item -Path $templateFolder -Recurse -Force
}
+ if (Test-Path $originalTemplateFolder) {
+ Remove-Item -Path $originalTemplateFolder -Recurse -Force
+ }
+ if (Test-Path $baseFolder) {
+ Remove-Item -Path $baseFolder -Recurse -Force
+ }
}
It "Returns the correct files to update with filters" {
@@ -1895,18 +2139,20 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "*.ps1" })
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 1
$filesToInclude[0].sourceFullPath | Should -Be $testPSFile
- $filesToInclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'test.ps1')
+ $filesToInclude[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'test.ps1')
- # No files to remove
+ # No files to exclude or remove
$filesToExclude | Should -BeNullOrEmpty
+ $filesToRemove | Should -BeNullOrEmpty
$settings = @{
type = "NotPTE"
@@ -1914,19 +2160,21 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "*.txt" })
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 2
$filesToInclude[0].sourceFullPath | Should -Be $testTxtFile
- $filesToInclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'test.txt')
+ $filesToInclude[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'test.txt')
$filesToInclude[1].sourceFullPath | Should -Be $testTxtFile2
- $filesToInclude[1].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'test2.txt')
+ $filesToInclude[1].destinationFullPath | Should -Be (Join-Path $baseFolder 'test2.txt')
- # No files to remove
+ # No files to exclude or remove
$filesToExclude | Should -BeNullOrEmpty
+ $filesToRemove | Should -BeNullOrEmpty
}
It 'Returns the correct files with destinationFolder' {
@@ -1936,20 +2184,22 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "*.txt"; destinationFolder = "customFolder" })
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 2
$filesToInclude[0].sourceFullPath | Should -Be $testTxtFile
- $filesToInclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'customFolder/test.txt')
+ $filesToInclude[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'customFolder/test.txt')
$filesToInclude[1].sourceFullPath | Should -Be $testTxtFile2
- $filesToInclude[1].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'customFolder/test2.txt')
+ $filesToInclude[1].destinationFullPath | Should -Be (Join-Path $baseFolder 'customFolder/test2.txt')
- # No files to remove
+ # No files to exclude or remove
$filesToExclude | Should -BeNullOrEmpty
+ $filesToRemove | Should -BeNullOrEmpty
$settings = @{
type = "NotPTE"
@@ -1957,21 +2207,22 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "*.txt"; destinationFolder = "customFolder" })
filesToExclude = @(@{ filter = "test2.txt" })
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 1
$filesToInclude[0].sourceFullPath | Should -Be $testTxtFile
- $filesToInclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'customFolder/test.txt')
+ $filesToInclude[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'customFolder/test.txt')
# One file to remove
$filesToExclude | Should -Not -BeNullOrEmpty
$filesToExclude.Count | Should -Be 1
$filesToExclude[0].sourceFullPath | Should -Be $testTxtFile2
- $filesToExclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'test2.txt')
+ $filesToExclude[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'customFolder/test2.txt')
}
It 'Returns the correct files with destinationName' {
@@ -1981,18 +2232,20 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "test.ps1"; destinationName = "renamed.txt" })
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 1
$filesToInclude[0].sourceFullPath | Should -Be $testPSFile
- $filesToInclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'renamed.txt')
+ $filesToInclude[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'renamed.txt')
- # No files to remove
+ # No files to exclude or remove
$filesToExclude | Should -BeNullOrEmpty
+ $filesToRemove | Should -BeNullOrEmpty
$settings = @{
type = "NotPTE"
@@ -2000,15 +2253,16 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "test.ps1"; destinationFolder = 'dstPath'; destinationName = "renamed.txt" })
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 1
$filesToInclude[0].sourceFullPath | Should -Be $testPSFile
- $filesToInclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'dstPath/renamed.txt')
+ $filesToInclude[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'dstPath/renamed.txt')
}
It 'Return the correct files with types' {
@@ -2018,19 +2272,21 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "*.ps1"; type = "script" })
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 1
$filesToInclude[0].sourceFullPath | Should -Be $testPSFile
- $filesToInclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'test.ps1')
+ $filesToInclude[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'test.ps1')
$filesToInclude[0].type | Should -Be "script"
- # No files to remove
+ # No files to exclude or remove
$filesToExclude | Should -BeNullOrEmpty
+ $filesToRemove | Should -BeNullOrEmpty
$settings = @{
type = "NotPTE"
@@ -2038,22 +2294,23 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "*.txt"; type = "text" })
filesToExclude = @(@{ filter = "test.txt" })
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 1
$filesToInclude[0].sourceFullPath | Should -Be $testTxtFile2
- $filesToInclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'test2.txt')
+ $filesToInclude[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'test2.txt')
$filesToInclude[0].type | Should -Be "text"
# One file to remove
$filesToExclude | Should -Not -BeNullOrEmpty
$filesToExclude.Count | Should -Be 1
$filesToExclude[0].sourceFullPath | Should -Be $testTxtFile
- $filesToExclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'test.txt')
+ $filesToExclude[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'test.txt')
}
It 'Return the correct files when unusedALGoSystemFiles is specified' {
@@ -2063,23 +2320,24 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "*" })
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 2
$filesToInclude[0].sourceFullPath | Should -Be $testTxtFile
- $filesToInclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'test.txt')
+ $filesToInclude[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'test.txt')
$filesToInclude[1].sourceFullPath | Should -Be $testTxtFile2
- $filesToInclude[1].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'test2.txt')
+ $filesToInclude[1].destinationFullPath | Should -Be (Join-Path $baseFolder 'test2.txt')
# One file to remove
$filesToExclude | Should -Not -BeNullOrEmpty
$filesToExclude.Count | Should -Be 1
$filesToExclude[0].sourceFullPath | Should -Be $testPSFile
- $filesToExclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'test.ps1')
+ $filesToExclude[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'test.ps1')
}
It 'GetFilesToUpdate with perProject true and empty projects returns no per-project entries' {
@@ -2089,15 +2347,17 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "*.txt"; type = "text"; perProject = $true })
filesToExclude = @()
+ filesToRemove = @()
}
}
# Pass empty projects array
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder -projects @()
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder -projects @()
# Behavior: when projects is empty, no per-project entries should be created
$filesToInclude | Should -BeNullOrEmpty
$filesToExclude | Should -BeNullOrEmpty
+ $filesToRemove | Should -BeNullOrEmpty
}
It 'GetFilesToUpdate ignores filesToExclude patterns that do not match any file' {
@@ -2107,20 +2367,22 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "*.txt" })
filesToExclude = @(@{ filter = "no-match-*.none" })
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
# All txt files should be included, no files to exclude
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 2
$filesToInclude[0].sourceFullPath | Should -Be $testTxtFile
- $filesToInclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'test.txt')
+ $filesToInclude[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'test.txt')
$filesToInclude[1].sourceFullPath | Should -Be $testTxtFile2
- $filesToInclude[1].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'test2.txt')
+ $filesToInclude[1].destinationFullPath | Should -Be (Join-Path $baseFolder 'test2.txt')
$filesToExclude | Should -BeNullOrEmpty
+ $filesToRemove | Should -BeNullOrEmpty
}
It 'GetFilesToUpdate duplicates per-project includes for each project including the repository root' {
@@ -2134,22 +2396,24 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "perProjectFile.algo"; perProject = $true; destinationFolder = 'custom' })
filesToExclude = @()
+ filesToRemove = @()
}
}
$projects = @('.', 'ProjectOne')
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder -projects $projects
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder -projects $projects
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 2
- $rootDestination = Join-Path 'baseFolder' 'custom/perProjectFile.algo'
- $projectDestination = Join-Path 'baseFolder' 'ProjectOne/custom/perProjectFile.algo'
+ $rootDestination = Join-Path $baseFolder 'custom/perProjectFile.algo'
+ $projectDestination = Join-Path $baseFolder 'ProjectOne/custom/perProjectFile.algo'
$filesToInclude.destinationFullPath | Should -Contain $rootDestination
$filesToInclude.destinationFullPath | Should -Contain $projectDestination
$filesToExclude | Should -BeNullOrEmpty
+ $filesToRemove | Should -BeNullOrEmpty
}
finally {
if (Test-Path $perProjectFile) {
@@ -2182,13 +2446,13 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @()
filesToExclude = @()
+ filesToRemove = @()
}
}
- $baseFolder = 'baseFolder'
$projects = @('ProjectA')
- $filesWithoutOriginal, $excludesWithoutOriginal = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $customTemplateFolder -projects $projects
+ $filesWithoutOriginal, $excludesWithoutOriginal, $removesWithoutOriginal = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $customTemplateFolder -projects $projects
$filesWithoutOriginal | Should -Not -BeNullOrEmpty
@@ -2202,7 +2466,7 @@ Describe "GetFilesToUpdate (general files to update logic)" {
$filesWithoutOriginal.destinationFullPath | Should -Not -Contain (Join-Path $baseFolder (Join-Path '.github' $CustomTemplateRepoSettingsFileName))
$filesWithoutOriginal.destinationFullPath | Should -Not -Contain (Join-Path $baseFolder (Join-Path '.github' $CustomTemplateProjectSettingsFileName))
- $filesWithOriginal, $excludesWithOriginal = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $customTemplateFolder -originalTemplateFolder $originalTemplateFolder -projects $projects
+ $filesWithOriginal, $excludesWithOriginal, $removesWithOriginal = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $customTemplateFolder -originalTemplateFolder $originalTemplateFolder -projects $projects
$filesWithOriginal | Should -Not -BeNullOrEmpty
@@ -2212,7 +2476,10 @@ Describe "GetFilesToUpdate (general files to update logic)" {
$filesWithOriginal.destinationFullPath | Should -Contain (Join-Path $baseFolder (Join-Path '.github' $CustomTemplateProjectSettingsFileName))
$excludesWithoutOriginal | Should -BeNullOrEmpty
+ $removesWithoutOriginal | Should -BeNullOrEmpty
+
$excludesWithOriginal | Should -BeNullOrEmpty
+ $removesWithOriginal | Should -BeNullOrEmpty
}
finally {
if (Test-Path $customTemplateFolder) {
@@ -2231,10 +2498,11 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "*.txt" })
filesToExclude = @(@{ filter = "test.txt" })
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
# test.txt should not be in filesToInclude
$includedTestTxt = $filesToInclude | Where-Object { $_.sourceFullPath -eq (Join-Path $templateFolder "test.txt") }
@@ -2252,10 +2520,11 @@ Describe "GetFilesToUpdate (general files to update logic)" {
customALGoFiles = @{
filesToInclude = @(@{ filter = "*.txt" })
filesToExclude = @(@{ filter = "nonexistent.xyz" })
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
# All txt files should be included
$filesToInclude | Should -Not -BeNullOrEmpty
@@ -2267,6 +2536,29 @@ Describe "GetFilesToUpdate (general files to update logic)" {
$excludedNonExistent | Should -BeNullOrEmpty
}
+ It 'GetFilesToUpdate excludes files with different destinations that match both include and exclude patterns' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.txt" }, @{ filter = "test.txt"; destinationName = "test.renamed.txt" })
+ filesToExclude = @(@{ filter = "test.txt" })
+ filesToRemove = @()
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
+
+ # test.txt should not be in filesToInclude
+ $filesToInclude | Should -BeNullOrEmpty
+
+ # test.txt should be in filesToExclude two times with different destinations
+ $testTxtFiles = $filesToExclude | Where-Object { $_.sourceFullPath -eq (Join-Path $templateFolder "test.txt") }
+ $testTxtFiles.Count | Should -Be 2
+ $testTxtFiles[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'test.renamed.txt')
+ $testTxtFiles[1].destinationFullPath | Should -Be (Join-Path $baseFolder 'test.txt')
+ }
+
It 'GetFilesToUpdate handles overlapping include patterns with different destinations' {
$settings = @{
type = "NotPTE"
@@ -2277,16 +2569,435 @@ Describe "GetFilesToUpdate (general files to update logic)" {
@{ filter = "test.txt"; destinationFolder = "folder2" }
)
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $templateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
# Should have two entries for test.txt with different destinations
$testTxtFiles = $filesToInclude | Where-Object { $_.sourceFullPath -eq (Join-Path $templateFolder "test.txt") }
$testTxtFiles.Count | Should -Be 2
- $testTxtFiles[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'folder1/test.txt')
- $testTxtFiles[1].destinationFullPath | Should -Be (Join-Path 'baseFolder' 'folder2/test.txt')
+ $testTxtFiles[0].destinationFullPath | Should -Be (Join-Path $baseFolder 'folder1/test.txt')
+ $testTxtFiles[1].destinationFullPath | Should -Be (Join-Path $baseFolder 'folder2/test.txt')
+ }
+
+ It 'GetFilesToUpdate filesToRemove resolves files from base folder' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @()
+ filesToExclude = @()
+ filesToRemove = @(@{ filter = "test.txt" })
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
+
+ # filesToRemove should one entry resolved from the base folder
+ $filesToRemove | Should -Not -BeNullOrEmpty
+ $filesToRemove.Count | Should -Be 1
+ $filesToRemove[0].sourceFullPath | Should -Be (Join-Path $baseFolder "test.txt")
+ $filesToRemove[0].destinationFullPath | Should -Be (Join-Path $baseFolder "test.txt")
+ $filesToRemove[0].originalSourceFullPath | Should -Be $null
+
+ # filesToInclude should be empty
+ $filesToInclude | Should -BeNullOrEmpty
+ }
+
+ It 'GetFilesToUpdate filesToRemove resolves missing template files from base folder' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @()
+ filesToExclude = @()
+ filesToRemove = @(@{ filter = "test.base.txt" })
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
+
+ # filesToRemove should have one entry pointing to the base folder file
+ $filesToRemove | Should -Not -BeNullOrEmpty
+ $filesToRemove.Count | Should -Be 1
+ $filesToRemove[0].sourceFullPath | Should -Be $testBaseTxtFile
+ $filesToRemove[0].destinationFullPath | Should -Be $testBaseTxtFile
+ $filesToRemove[0].originalSourceFullPath | Should -Be $null
+ }
+
+ It 'GetFilesToUpdate filesToRemove resolves template files not in base folder' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test2.txt" })
+ filesToExclude = @()
+ filesToRemove = @(@{ filter = "test2.txt" })
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
+
+ # filesToRemove should have one entry resolved from the template folder
+ $filesToRemove | Should -Not -BeNullOrEmpty
+ $filesToRemove.Count | Should -Be 1
+ $filesToRemove[0].sourceFullPath | Should -Be (Join-Path $templateFolder "test2.txt")
+ $filesToRemove[0].destinationFullPath | Should -Be (Join-Path $baseFolder "test2.txt")
+ $filesToRemove[0].originalSourceFullPath | Should -Be $null
+
+ # filesToExclude should be empty
+ $filesToExclude | Should -BeNullOrEmpty
+ }
+
+ It 'GetFilesToUpdate filesToRemove excludes matching files from filesToInclude' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.txt" }, @{ filter = "test2.txt" })
+ filesToExclude = @()
+ filesToRemove = @(@{ filter = "test.txt" }, @{ filter = "test2.txt" })
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
+
+ # filesToInclude should be empty
+ $filesToInclude | Should -BeNullOrEmpty
+ }
+
+ It 'GetFilesToUpdate filesToRemove excludes matching files from filesToExclude' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.txt" })
+ filesToExclude = @(@{ filter = "test.txt" })
+ filesToRemove = @(@{ filter = "test.txt" })
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
+
+ # filesToExclude should be empty
+ $filesToExclude | Should -BeNullOrEmpty
+ }
+
+ It 'GetFilesToUpdate filesToRemove keeps not matching files in filesToInclude' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.txt" })
+ filesToExclude = @()
+ filesToRemove = @(@{ filter = "test.base.txt" })
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
+
+ # test.txt should still be in filesToInclude
+ $filesToInclude.SourceFullPath | Should -Contain ( Join-Path $templateFolder "test.txt" )
+ $filesToInclude.destinationFullPath | Should -Contain ( Join-Path $baseFolder "test.txt" )
+ }
+
+ It 'GetFilesToUpdate filesToRemove keeps not matching files in filesToExclude' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.txt" })
+ filesToExclude = @(@{ filter = "test.txt" })
+ filesToRemove = @(@{ filter = "test.base.txt" })
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
+
+ # test.txt should still be in filesToExclude
+ $filesToExclude.SourceFullPath | Should -Contain ( Join-Path $templateFolder "test.txt" )
+ $filesToExclude.destinationFullPath | Should -Contain ( Join-Path $baseFolder "test.txt" )
+ }
+
+ It 'GetFilesToUpdate filesToRemove uses destinationFolder and destinationName keys' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @()
+ filesToExclude = @()
+ filesToRemove = @(@{ filter = "test.txt"; destinationFolder = "subfolder"; destinationName = "testsub.txt" })
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
+
+ $filesToRemove | Should -Not -BeNullOrEmpty
+ $filesToRemove.Count | Should -Be 1
+ $filesToRemove[0].sourceFullPath | Should -Be (Join-Path $baseFolder "subfolder/testsub.txt")
+ $filesToRemove[0].destinationFullPath | Should -Be (Join-Path $baseFolder "subfolder/testsub.txt")
+ $filesToRemove[0].originalSourceFullPath | Should -Be $null
+ }
+
+ It 'GetFilesToUpdate filesToRemove with perProject expands per project' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @()
+ filesToExclude = @()
+ filesToRemove = @(@{ filter = "testsub.txt"; destinationFolder = "subfolder"; perProject = $true })
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder -projects @('.', 'project1', 'project2')
+
+ # filesToRemove should have 3 entries: one for root ('.'), one for project1, and one for project2, all pointing to the respective subfolder/testsub.txt
+ $filesToRemove | Should -Not -BeNullOrEmpty
+ $filesToRemove.Count | Should -Be 3
+ $filesToRemove.destinationFullPath | Should -Contain (Join-Path $baseFolder "subfolder/testsub.txt")
+ $filesToRemove.destinationFullPath | Should -Contain (Join-Path $baseFolder "project1/subfolder/testsub.txt")
+ $filesToRemove.destinationFullPath | Should -Contain (Join-Path $baseFolder "project2/subfolder/testsub.txt")
+ }
+
+ It 'GetFilesToUpdate empty filesToRemove returns empty third element' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @()
+ filesToExclude = @()
+ filesToRemove = @()
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
+
+ $filesToRemove | Should -BeNullOrEmpty
+ }
+
+ It 'GetFilesToUpdate unknown filesToRemove returns empty third element' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @()
+ filesToExclude = @()
+ filesToRemove = @(@{ filter = "*.unknown" })
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder
+
+ $filesToRemove | Should -BeNullOrEmpty
+ }
+
+ It 'GetFilesToUpdate template settings filesToInclude added to filesToInclude' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.txt" })
+ filesToExclude = @()
+ filesToRemove = @()
+ }
+ }
+
+ $templateSettings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.ps1" })
+ filesToExclude = @()
+ filesToRemove = @()
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder -templateSettings $templateSettings
+
+ # Both test.txt and test.ps1 should be in filesToInclude
+ $filesToInclude.sourceFullPath | Should -Contain $testTxtFile
+ $filesToInclude.sourceFullPath | Should -Contain $testPSFile
+ }
+
+ It 'GetFilesToUpdate template settings filesToExclude added to filesToExclude' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.txt" }, @{ filter = "test.ps1" })
+ filesToExclude = @(@{ filter = "test.txt" })
+ filesToRemove = @()
+ }
+ }
+
+ $templateSettings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @()
+ filesToExclude = @(@{ filter = "test.ps1" })
+ filesToRemove = @()
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder -templateSettings $templateSettings
+
+ # Both test.txt and test.ps1 should be in filesToExclude
+ $filesToExclude.sourceFullPath | Should -Contain $testTxtFile
+ $filesToExclude.sourceFullPath | Should -Contain $testPSFile
+ }
+
+ It 'GetFilesToUpdate template settings filesToRemove added to filesToRemove' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @()
+ filesToExclude = @()
+ filesToRemove = @(@{ filter = "test.base.txt" })
+ }
+ }
+
+ $templateSettings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @()
+ filesToExclude = @()
+ filesToRemove = @(@{ filter = "test.base.ps1" })
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder -templateSettings $templateSettings
+
+ # Both test.base.txt and test.base.ps1 should be in filesToRemove
+ $filesToRemove.destinationFullPath | Should -Contain $testBaseTxtFile
+ $filesToRemove.destinationFullPath | Should -Contain $testBasePSFile
+ }
+
+ It 'GetFilesToUpdate template settings unusedALGoSystemFiles added to unusedALGoSystemFiles' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @("test.txt")
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.txt" })
+ filesToExclude = @()
+ filesToRemove = @()
+ }
+ }
+
+ $templateSettings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @("test.ps1")
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.ps1" })
+ filesToExclude = @()
+ filesToRemove = @()
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder -templateSettings $templateSettings
+
+ # Both test.txt and test.ps1 should be in filesToExclude
+ $filesToExclude.sourceFullPath | Should -Contain $testTxtFile
+ $filesToExclude.sourceFullPath | Should -Contain $testPSFile
+ }
+
+ It 'GetFilesToUpdate filesToInclude includes original template files missing in template' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.original.txt" })
+ filesToExclude = @()
+ filesToRemove = @()
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder -originalTemplateFolder $originalTemplateFolder
+
+ # test.original.txt of original template should be in filesToInclude
+ $testOriginalTemplateTxtFiles = @($filesToInclude | Where-Object { $_.sourceFullPath -eq $testOriginalTemplateTxtFile })
+ $testOriginalTemplateTxtFiles | Should -Not -BeNullOrEmpty
+ $testOriginalTemplateTxtFiles.Count | Should -Be 1
+ $testOriginalTemplateTxtFiles[0].sourceFullPath | Should -Be $testOriginalTemplateTxtFile
+ $testOriginalTemplateTxtFiles[0].originalSourceFullPath | Should -Be $null
+ $testOriginalTemplateTxtFiles[0].destinationFullPath | Should -Be ( Join-Path $baseFolder "test.original.txt" )
+ }
+
+ It 'GetFilesToUpdate filesToExclude excludes original template files missing in template' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.original.txt" })
+ filesToExclude = @(@{ filter = "test.original.txt" })
+ filesToRemove = @()
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder -originalTemplateFolder $originalTemplateFolder
+
+ # test.original.txt of original template should be in filesToExclude
+ $testOriginalTemplateTxtFiles = @($filesToExclude | Where-Object { $_.sourceFullPath -eq $testOriginalTemplateTxtFile })
+ $testOriginalTemplateTxtFiles | Should -Not -BeNullOrEmpty
+ $testOriginalTemplateTxtFiles.Count | Should -Be 1
+ $testOriginalTemplateTxtFiles[0].sourceFullPath | Should -Be $testOriginalTemplateTxtFile
+ $testOriginalTemplateTxtFiles[0].originalSourceFullPath | Should -Be $null
+ $testOriginalTemplateTxtFiles[0].destinationFullPath | Should -Be ( Join-Path $baseFolder "test.original.txt" )
+ }
+
+ It 'GetFilesToUpdate filesToInclude not including original template files existing in template' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.txt" })
+ filesToExclude = @()
+ filesToRemove = @()
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder -originalTemplateFolder $originalTemplateFolder
+
+ # test.txt of template should be in filesToInclude
+ $testTxtFiles = @($filesToInclude | Where-Object { $_.sourceFullPath -eq (Join-Path $templateFolder "test.txt") })
+ $testTxtFiles | Should -Not -BeNullOrEmpty
+ $testTxtFiles.Count | Should -Be 1
+ $testTxtFiles[0].sourceFullPath | Should -Be (Join-Path $templateFolder "test.txt")
+ $testTxtFiles[0].originalSourceFullPath | Should -Be ( Join-Path $originalTemplateFolder "test.txt" )
+ $testTxtFiles[0].destinationFullPath | Should -Be (Join-Path $baseFolder "test.txt")
+
+ # test.txt of original template should not be in filesToInclude
+ $filesToInclude.SourceFullPath | Should -Not -Contain ( Join-Path $originalTemplateFolder "test.txt" )
+ }
+
+ It 'GetFilesToUpdate filesToExclude not excluding original template files existing in template' {
+ $settings = @{
+ type = "NotPTE"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @(@{ filter = "test.txt" })
+ filesToExclude = @(@{ filter = "test.txt" })
+ filesToRemove = @()
+ }
+ }
+
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder $baseFolder -templateFolder $templateFolder -originalTemplateFolder $originalTemplateFolder
+
+ # test.txt of template should be in filesToExclude
+ $testTxtFiles = @($filesToExclude | Where-Object { $_.sourceFullPath -eq (Join-Path $templateFolder "test.txt") })
+ $testTxtFiles | Should -Not -BeNullOrEmpty
+ $testTxtFiles.Count | Should -Be 1
+ $testTxtFiles[0].sourceFullPath | Should -Be (Join-Path $templateFolder "test.txt")
+ $testTxtFiles[0].originalSourceFullPath | Should -Be ( Join-Path $originalTemplateFolder "test.txt" )
+ $testTxtFiles[0].destinationFullPath | Should -Be (Join-Path $baseFolder "test.txt")
+
+ # test.txt of original template should not be in filesToExclude
+ $filesToExclude.SourceFullPath | Should -Not -Contain ( Join-Path $originalTemplateFolder "test.txt" )
}
}
@@ -2301,6 +3012,13 @@ Describe "GetFilesToUpdate (real template)" {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'realAppSourceAppTemplateFolder', Justification = 'False positive.')]
$realAppSourceAppTemplateFolder = Join-Path $PSScriptRoot "../Templates/AppSource App" -Resolve
+
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'powerPlatformFiles', Justification = 'False positive.')]
+ $powerPlatformFiles = @(
+ ".github/workflows/_BuildPowerPlatformSolution.yaml",
+ ".github/workflows/PullPowerPlatformChanges.yaml",
+ ".github/workflows/PushPowerPlatformChanges.yaml"
+ )
}
It 'Return the correct files to exclude when type is PTE and powerPlatformSolutionFolder is not empty' {
@@ -2311,19 +3029,21 @@ Describe "GetFilesToUpdate (real template)" {
customALGoFiles = @{
filesToInclude = @()
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 25
- $filesToInclude.sourceFullPath | Should -Contain (Join-Path $realPTETemplateFolder ".github/workflows/_BuildPowerPlatformSolution.yaml")
- $filesToInclude.sourceFullPath | Should -Contain (Join-Path $realPTETemplateFolder ".github/workflows/PullPowerPlatformChanges.yaml")
- $filesToInclude.sourceFullPath | Should -Contain (Join-Path $realPTETemplateFolder ".github/workflows/PushPowerPlatformChanges.yaml")
+ $filesToInclude.sourceFullPath | Should -Contain (Join-Path $realPTETemplateFolder $powerPlatformFiles[0])
+ $filesToInclude.sourceFullPath | Should -Contain (Join-Path $realPTETemplateFolder $powerPlatformFiles[1])
+ $filesToInclude.sourceFullPath | Should -Contain (Join-Path $realPTETemplateFolder $powerPlatformFiles[2])
- # No files to remove
+ # No files to exclude or remove
$filesToExclude | Should -BeNullOrEmpty
+ $filesToRemove | Should -BeNullOrEmpty
}
It 'Return PP files in filesToExclude when type is PTE but powerPlatformSolutionFolder is empty' {
@@ -2334,33 +3054,32 @@ Describe "GetFilesToUpdate (real template)" {
customALGoFiles = @{
filesToInclude = @()
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 22
$filesToInclude | ForEach-Object {
- $_.sourceFullPath | Should -Not -Be (Join-Path $realPTETemplateFolder ".github/workflows/_BuildPowerPlatformSolution.yaml")
- $_.sourceFullPath | Should -Not -Be (Join-Path $realPTETemplateFolder ".github/workflows/PullPowerPlatformChanges.yaml")
- $_.sourceFullPath | Should -Not -Be (Join-Path $realPTETemplateFolder ".github/workflows/PushPowerPlatformChanges.yaml")
+ $fileToInclude = $_
+ $powerPlatformFiles | ForEach-Object {
+ $fileToInclude.sourceFullPath | Should -Not -Be (Join-Path $realPTETemplateFolder $_)
+ }
}
# All PP files to remove
$filesToExclude | Should -Not -BeNullOrEmpty
- $filesToExclude.Count | Should -Be 3
-
- $filesToExclude[0].sourceFullPath | Should -Be (Join-Path $realPTETemplateFolder ".github/workflows/_BuildPowerPlatformSolution.yaml")
- $filesToExclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' ".github/workflows/_BuildPowerPlatformSolution.yaml")
-
- $filesToExclude[1].sourceFullPath | Should -Be (Join-Path $realPTETemplateFolder ".github/workflows/PushPowerPlatformChanges.yaml")
- $filesToExclude[1].destinationFullPath | Should -Be (Join-Path 'baseFolder' ".github/workflows/PushPowerPlatformChanges.yaml")
+ $filesToExclude.Count | Should -Be $powerPlatformFiles.Count
- $filesToExclude[2].sourceFullPath | Should -Be (Join-Path $realPTETemplateFolder ".github/workflows/PullPowerPlatformChanges.yaml")
- $filesToExclude[2].destinationFullPath | Should -Be (Join-Path 'baseFolder' ".github/workflows/PullPowerPlatformChanges.yaml")
+ for ($i = 0; $i -lt $powerPlatformFiles.Count; $i++) {
+ $filesToExclude[$i].sourceFullPath | Should -Be (Join-Path $realPTETemplateFolder $powerPlatformFiles[$i])
+ $filesToExclude[$i].destinationFullPath | Should -Be (Join-Path 'baseFolder' $powerPlatformFiles[$i])
+ }
+ $filesToRemove | Should -BeNullOrEmpty
}
It 'Return the correct files when unusedALGoSystemFiles is specified' {
@@ -2371,10 +3090,11 @@ Describe "GetFilesToUpdate (real template)" {
customALGoFiles = @{
filesToInclude = @()
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 24
@@ -2384,6 +3104,7 @@ Describe "GetFilesToUpdate (real template)" {
$filesToExclude.Count | Should -Be 1
$filesToExclude[0].sourceFullPath | Should -Be (Join-Path $realPTETemplateFolder ".github/Test Next Major.settings.json")
$filesToExclude[0].destinationFullPath | Should -Be (Join-Path 'baseFolder' '.github/Test Next Major.settings.json')
+ $filesToRemove | Should -BeNullOrEmpty
}
It 'Return the correct files when unusedALGoSystemFiles is specified and no PP solution is present' {
@@ -2394,10 +3115,11 @@ Describe "GetFilesToUpdate (real template)" {
customALGoFiles = @{
filesToInclude = @()
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder
$filesToInclude | Should -Not -BeNullOrEmpty
$filesToInclude.Count | Should -Be 21
@@ -2407,9 +3129,10 @@ Describe "GetFilesToUpdate (real template)" {
$filesToExclude.Count | Should -Be 4
$filesToExclude.sourceFullPath | Should -Contain (Join-Path $realPTETemplateFolder ".github/Test Next Major.settings.json")
- $filesToExclude.sourceFullPath | Should -Contain (Join-Path $realPTETemplateFolder ".github/workflows/_BuildPowerPlatformSolution.yaml")
- $filesToExclude.sourceFullPath | Should -Contain (Join-Path $realPTETemplateFolder ".github/workflows/PullPowerPlatformChanges.yaml")
- $filesToExclude.sourceFullPath | Should -Contain (Join-Path $realPTETemplateFolder ".github/workflows/PushPowerPlatformChanges.yaml")
+ $powerPlatformFiles | ForEach-Object {
+ $filesToExclude.sourceFullPath | Should -Contain (Join-Path $realPTETemplateFolder $_)
+ }
+ $filesToRemove | Should -BeNullOrEmpty
}
It 'Returns the custom template settings files when there is a custom template' {
@@ -2420,12 +3143,13 @@ Describe "GetFilesToUpdate (real template)" {
customALGoFiles = @{
filesToInclude = @()
filesToExclude = @()
+ filesToRemove = @()
}
}
$customTemplateFolder = $realPTETemplateFolder
$originalTemplateFolder = $realAppSourceAppTemplateFolder
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -projects @('.') -templateFolder $customTemplateFolder -originalTemplateFolder $originalTemplateFolder # Indicate custom template
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -projects @('.') -templateFolder $customTemplateFolder -originalTemplateFolder $originalTemplateFolder # Indicate custom template
$filesToInclude | Should -Not -BeNullOrEmpty
@@ -2457,8 +3181,70 @@ Describe "GetFilesToUpdate (real template)" {
$projectSettingsFilesFromCustomTemplate[1].destinationFullPath | Should -Be (Join-Path 'baseFolder' '.github/AL-Go-TemplateProjectSettings.doNotEdit.json')
$projectSettingsFilesFromCustomTemplate[1].type | Should -Be ''
- # No files to exclude
+ # No files to exclude or remove
+ $filesToExclude | Should -BeNullOrEmpty
+ $filesToRemove | Should -BeNullOrEmpty
+ }
+
+ It 'Returns the original template PP files in filesToInclude when there is a custom template without them and powerPlatformSolutionFolder is not empty' {
+ $settings = @{
+ type = "PTE"
+ powerPlatformSolutionFolder = "PowerPlatformSolution"
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @()
+ filesToExclude = @()
+ filesToRemove = @()
+ }
+ }
+
+ # AppSource App is used as custom template because it has no PP workflows, simulating a custom PTE fork that stripped them out
+ $customTemplateFolder = $realAppSourceAppTemplateFolder
+ $originalTemplateFolder = $realPTETemplateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -projects @('.') -templateFolder $customTemplateFolder -originalTemplateFolder $originalTemplateFolder # Indicate custom template
+
+ $filesToInclude | Should -Not -BeNullOrEmpty
+ $powerPlatformFiles | ForEach-Object {
+ $filesToInclude.sourceFullPath | Should -Not -Contain (Join-Path $customTemplateFolder $_)
+ $filesToInclude.sourceFullPath | Should -Contain (Join-Path $originalTemplateFolder $_)
+ }
+
+ # No files to exclude or remove
$filesToExclude | Should -BeNullOrEmpty
+ $filesToRemove | Should -BeNullOrEmpty
+ }
+
+ It 'Returns the original template PP files in filesToExclude when there is a custom template without them and powerPlatformSolutionFolder is empty' {
+ $settings = @{
+ type = "PTE"
+ powerPlatformSolutionFolder = ""
+ unusedALGoSystemFiles = @()
+ customALGoFiles = @{
+ filesToInclude = @()
+ filesToExclude = @()
+ filesToRemove = @()
+ }
+ }
+
+ # AppSource App is used as custom template because it has no PP workflows, simulating a custom PTE fork that stripped them out
+ $customTemplateFolder = $realAppSourceAppTemplateFolder
+ $originalTemplateFolder = $realPTETemplateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -projects @('.') -templateFolder $customTemplateFolder -originalTemplateFolder $originalTemplateFolder # Indicate custom template
+
+ $filesToInclude | Should -Not -BeNullOrEmpty
+ $powerPlatformFiles | ForEach-Object {
+ $filesToInclude.sourceFullPath | Should -Not -Contain (Join-Path $customTemplateFolder $_)
+ $filesToInclude.sourceFullPath | Should -Not -Contain (Join-Path $originalTemplateFolder $_)
+ }
+
+ $filesToExclude | Should -Not -BeNullOrEmpty
+ $powerPlatformFiles | ForEach-Object {
+ $filesToExclude.sourceFullPath | Should -Not -Contain (Join-Path $customTemplateFolder $_)
+ $filesToExclude.sourceFullPath | Should -Contain (Join-Path $originalTemplateFolder $_)
+ }
+
+ # No files to exclude
+ $filesToRemove | Should -BeNullOrEmpty
}
It 'GetFilesToUpdate handles AppSource template type correctly' {
@@ -2469,10 +3255,11 @@ Describe "GetFilesToUpdate (real template)" {
customALGoFiles = @{
filesToInclude = @()
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realAppSourceAppTemplateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realAppSourceAppTemplateFolder
# PowerPlatform files should be excluded for AppSource App too (same as PTE)
$filesToInclude | Should -Not -BeNullOrEmpty
@@ -2493,14 +3280,16 @@ Describe "GetFilesToUpdate (real template)" {
customALGoFiles = @{
filesToInclude = @()
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder
# No additional files should be excluded due to unusedALGoSystemFiles
$ppExcludes = $filesToExclude | Where-Object { $_.sourceFullPath -like "*_BuildPowerPlatformSolution.yaml" -or $_.sourceFullPath -like "*PullPowerPlatformChanges.yaml" -or $_.sourceFullPath -like "*PushPowerPlatformChanges.yaml" }
$ppExcludes.Count | Should -Be 3 # Only PP files should be excluded by default
+ $filesToRemove | Should -BeNullOrEmpty
}
It 'GetFilesToUpdate marks settings files with correct type' {
@@ -2511,10 +3300,11 @@ Describe "GetFilesToUpdate (real template)" {
customALGoFiles = @{
filesToInclude = @()
filesToExclude = @()
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder -projects @('Project1')
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder -projects @('Project1')
# Check that settings files have type = 'settings'
$repoSettingsFiles = @($filesToInclude | Where-Object { $_.sourceFullPath -like "*$RepoSettingsFileName" -and $_.destinationFullPath -like "*.github*$RepoSettingsFileName" })
@@ -2524,6 +3314,7 @@ Describe "GetFilesToUpdate (real template)" {
$projectSettingsFiles = @($filesToInclude | Where-Object { $_.sourceFullPath -like "*$ALGoSettingsFileName" -and $_.destinationFullPath -like "*Project1*.AL-Go*" })
$projectSettingsFiles | Should -Not -BeNullOrEmpty
$projectSettingsFiles[0].type | Should -Be 'settings'
+ $filesToRemove | Should -BeNullOrEmpty
}
It 'GetFilesToUpdate handles multiple projects correctly' {
@@ -2534,11 +3325,12 @@ Describe "GetFilesToUpdate (real template)" {
customALGoFiles = @{
filesToInclude = @()
filesToExclude = @()
+ filesToRemove = @()
}
}
$projects = @('ProjectA', 'ProjectB', 'ProjectC')
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder -projects $projects
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder -projects $projects
# Each project should have its own settings file
$projectASettings = $filesToInclude | Where-Object { $_.destinationFullPath -like "*ProjectA*.AL-Go*" }
@@ -2558,10 +3350,11 @@ Describe "GetFilesToUpdate (real template)" {
customALGoFiles = @{
filesToInclude = @()
filesToExclude = @(@{ filter = "Test Next Major.settings.json" })
+ filesToRemove = @()
}
}
- $filesToInclude, $filesToExclude = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder
+ $filesToInclude, $filesToExclude, $filesToRemove = GetFilesToUpdate -settings $settings -baseFolder 'baseFolder' -templateFolder $realPTETemplateFolder
# Test Next Major.settings.json should be excluded
$testNextMajor = $filesToInclude | Where-Object { $_.sourceFullPath -like "*Test Next Major.settings.json" }
diff --git a/e2eTests/scenarios/CustomTemplate/runtest.ps1 b/e2eTests/scenarios/CustomTemplate/runtest.ps1
index 5cdef971b3..b58278402e 100644
--- a/e2eTests/scenarios/CustomTemplate/runtest.ps1
+++ b/e2eTests/scenarios/CustomTemplate/runtest.ps1
@@ -31,8 +31,12 @@ Write-Host -ForegroundColor Yellow @'
# - Create a new repository based on the PTE template with 1 app, using compilerfolder and donotpublishapps (this will be the "final" template repository)
# - Run Update AL-Go System Files in final repo (using custom template repository as template)
# - Run Update AL-Go System files in custom template repository
+# - Validate that custom AL-Go files are applied in custom template repository
# - Validate that custom job is present in custom template repository
# - Run Update AL-Go System files in final repo
+# - Validate that custom AL-Go files of template repository are applied in final repository
+# - Run Update AL-Go System files in final repo
+# - Validate that custom AL-Go files of template repository and final repository are applied in final repository
# - Validate that custom job is present in final repo
#
'@
@@ -55,6 +59,8 @@ $template = "https://github.com/$pteTemplate"
# Login
SetTokenAndRepository -github:$github -githubOwner $githubOwner -appId $e2eAppId -appKey $e2eAppKey -repository $repository
+#region create repositories
+
# Create template repository
CreateAlGoRepository `
-github:$github `
@@ -64,6 +70,9 @@ CreateAlGoRepository `
-branch $branch
$templateRepoPath = (Get-Location).Path
+# Stop all currently running workflows on template repository
+CancelAllWorkflows -repository $templateRepository
+
Set-Location $prevLocation
$appName = 'MyApp'
@@ -76,15 +85,26 @@ CreateAlGoRepository `
-template $template `
-repository $repository `
-branch $branch `
+ -addRepoSettings @{ "useCompilerFolder" = $true; "doNotPublishApps" = $true } `
-contentScript {
Param([string] $path)
$null = CreateNewAppInFolder -folder $path -name $appName -publisher $publisherName
}
$finalRepoPath = (Get-Location).Path
+# Stop all currently running workflows on final repository
+CancelAllWorkflows -repository $repository
+
# Update AL-Go System Files to use template repository
RunUpdateAlGoSystemFiles -directCommit -wait -templateUrl $templateRepository -ghTokenWorkflow $algoauthapp -repository $repository -branch $branch | Out-Null
+# Stop all currently running workflows on final repository
+CancelAllWorkflows -repository $repository
+
+#endregion
+
+#region setup template repository customizations
+
Set-Location $templateRepoPath
Pull
@@ -154,6 +174,10 @@ on:
branches:
- main
+defaults:
+ run:
+ shell: powershell
+
jobs:
CustomJob:
runs-on: [ windows-latest ]
@@ -166,23 +190,101 @@ jobs:
"@
Set-Content -Path $customWorkflowFile -Value $customWorkflowContent
+$finalRepoCustomWorkflowContent = $customWorkflowContent
if($linux) {
- # Modify workflow to run on ubuntu-latest if the test is running on linux. AL-Go will not modify workflow files based on platform, so we need to do it here to ensure the test works correctly.
- $customWorkflowContent = $customWorkflowContent -replace 'windows-latest', 'ubuntu-latest'
+ $finalRepoCustomWorkflowContent = $finalRepoCustomWorkflowContent -replace 'windows-latest', 'ubuntu-latest'
+ $finalRepoCustomWorkflowContent = $finalRepoCustomWorkflowContent -replace 'shell: powershell', 'shell: pwsh'
}
-# Add another custom file in the template repository (to be ignored unless specifically added via the settings)
-$customFileName = 'CustomTemplateFile.txt'
-$customFile = Join-Path $templateRepoPath $customFileName
-$customFileContent = "This is a custom file in the template repository."
-Set-Content -Path $customFile -Value $customFileContent
+# Add custom files in the template repository
+$defaultCustomFileName = 'CustomTemplateFile.Default.txt'
+$defaultCustomFile = Join-Path $templateRepoPath $defaultCustomFileName
+$defaultCustomFileContent = "This is a default custom file in the template repository."
+Set-Content -Path $defaultCustomFile -Value $defaultCustomFileContent
+
+$optionalCustomFileName = 'CustomTemplateFile.Optional.txt'
+$optionalCustomFile = Join-Path $templateRepoPath $optionalCustomFileName
+$optionalCustomFileContent = "This is an optional custom file in the template repository."
+Set-Content -Path $optionalCustomFile -Value $optionalCustomFileContent
+
+$legacyCustomFileName = 'CustomTemplateFile.Legacy.txt'
+
+# Remove workflow files from template repository
+$excludedWorkflowFileName = 'DeployReferenceDocumentation.yaml'
+$excludedWorkflowFileRelativePath = Join-Path '.github/workflows' $excludedWorkflowFileName
+$excludedWorkflowFile = Join-Path $templateRepoPath $excludedWorkflowFileRelativePath
+Remove-Item -Path $excludedWorkflowFile -Force | Out-Null
+
+$missingWorkflowFileName = 'Troubleshooting.yaml'
+$missingWorkflowFileRelativePath = Join-Path '.github/workflows' $missingWorkflowFileName
+$missingWorkflowFile = Join-Path $templateRepoPath $missingWorkflowFileRelativePath
+Remove-Item -Path $missingWorkflowFile -Force | Out-Null
+
+# Add customALGoFiles settings to the template repository
+$null = Add-PropertiesToJsonFile -path '.github/AL-Go-Settings.json' -properties @{
+ "customALGoFiles" = @{
+ "filesToInclude" = @( @{ "filter" = $defaultCustomFileName } )
+ "filesToExclude" = @( @{ "sourceFolder" = ".github/workflows"; "filter" = $excludedWorkflowFileName } )
+ "filesToRemove" = @( @{ "filter" = $legacyCustomFileName } )
+ }
+}
# Push
-CommitAndPush -commitMessage 'Add template customizations'
+CommitAndPush -commitMessage 'Add template customizations [skip ci]'
+
+#endregion
-# Do not run workflows on template repository
+#region update template repository with template repository customizations
+
+# Update AL-Go System Files for template repository to update customizations from template repository
+RunUpdateAlGoSystemFiles -directCommit -wait -templateUrl $template -ghTokenWorkflow $algoauthapp -repository $templateRepository -branch $branch | Out-Null
+
+# Stop all currently running workflows on template repository
CancelAllWorkflows -repository $templateRepository
+# Pull changes
+Pull
+
+# Check that custom workflow file is present
+(Join-Path (Get-Location) $customWorkflowfileRelativePath) | Should -Exist
+Get-ContentLF -Path (Join-Path (Get-Location) $customWorkflowfileRelativePath) | Should -Be $customWorkflowContent.Replace("`r", "").TrimEnd("`n")
+
+# Check that default custom file is present
+(Join-Path (Get-Location) $defaultCustomFileName) | Should -Exist
+Get-ContentLF -Path (Join-Path (Get-Location) $defaultCustomFileName) | Should -Be $defaultCustomFileContent.Replace("`r", "").TrimEnd("`n")
+# Check that optional custom file is present
+(Join-Path (Get-Location) $optionalCustomFileName) | Should -Exist
+Get-ContentLF -Path (Join-Path (Get-Location) $optionalCustomFileName) | Should -Be $optionalCustomFileContent.Replace("`r", "").TrimEnd("`n")
+# Check that legacy custom file is NOT present
+(Join-Path (Get-Location) $legacyCustomFileName) | Should -Not -Exist
+
+# Check that excluded workflow file is NOT present (in template's filesToExclude)
+(Join-Path (Get-Location) $excludedWorkflowFileRelativePath) | Should -Not -Exist
+# Check that missing workflow file is present (in default filesToExclude)
+(Join-Path (Get-Location) $missingWorkflowFileRelativePath) | Should -Exist
+
+# Remove missing workflow files from template repository again
+Remove-Item -Path $missingWorkflowFile -Force | Out-Null
+
+# Push
+CommitAndPush -commitMessage 'Restore template customizations [skip ci]'
+
+#endregion
+
+#region validate template repository CI/CD workflow
+
+# Run CICD
+$run = RunCICD -repository $templateRepository -branch $branch -wait
+
+# Check Custom Jobs
+Test-LogContainsFromRun -repository $templateRepository -runid $run.id -jobName 'CustomJob-TemplateInit' -stepName 'Init' -expectedText 'CustomJob-TemplateInit was here!'
+Test-LogContainsFromRun -repository $templateRepository -runid $run.id -jobName 'CustomJob-TemplateDeploy' -stepName 'Deploy' -expectedText 'CustomJob-TemplateDeploy was here!'
+{ Test-LogContainsFromRun -repository $templateRepository -runid $run.id -jobName 'JustSomeTemplateJob' -stepName 'JustSomeTemplateStep' -expectedText 'JustSomeTemplateJob was here!' } | Should -Throw
+
+#endregion
+
+#region setup final repository customizations
+
# Add local customizations to the final repository
Set-Location $finalRepoPath
Pull
@@ -245,14 +347,39 @@ $cicdYaml.AddCustomJobsToYaml($customJobs, [CustomizationOrigin]::FinalRepositor
# save
$cicdYaml.Save($cicdWorkflow)
+# Add custom files in the final repository
+$legacyCustomFileContent = "This is a removed custom file that will be removed in the final repository."
+Set-Content -Path (Join-Path (Get-Location) $legacyCustomFileName) -Value $legacyCustomFileContent
+
+# Remove workflow files from final repository
+Remove-Item -Path (Join-Path (Get-Location) $missingWorkflowFileRelativePath) -Force | Out-Null
+
+# Check that custom workflow file is NOT present
+(Join-Path (Get-Location) $customWorkflowfileRelativePath) | Should -Not -Exist
+
+# Check that default custom file is NOT present in final repository
+(Join-Path (Get-Location) $defaultCustomFileName) | Should -Not -Exist
+# Check that optional custom file is NOT present in final repository
+(Join-Path (Get-Location) $optionalCustomFileName) | Should -Not -Exist
+# Check that legacy workflow file is present in final repository
+(Join-Path (Get-Location) $legacyCustomFileName) | Should -Exist
+
+# Check that excluded workflow file is present in final repository
+(Join-Path (Get-Location) $excludedWorkflowFileRelativePath) | Should -Exist
+# Check that missing workflow file is NOT present in final repository
+(Join-Path (Get-Location) $missingWorkflowFileRelativePath) | Should -Not -Exist
# Push
-CommitAndPush -commitMessage 'Add final repo customizations'
+CommitAndPush -commitMessage 'Add final repo customizations [skip ci]'
+
+#endregion
+
+#region update final repository with template repository customizations
-# Update AL-Go System Files to uptake UseProjectDependencies setting
+# Update AL-Go System Files for the final repository to uptake customizations from template repository
RunUpdateAlGoSystemFiles -directCommit -wait -templateUrl $templateRepository -ghTokenWorkflow $algoauthapp -repository $repository -branch $branch | Out-Null
-# Stop all currently running workflows and run a new CI/CD workflow
+# Stop all currently running workflows on final repository
CancelAllWorkflows -repository $repository
# Pull changes
@@ -263,39 +390,82 @@ Pull
# Check that custom workflow file is present
(Join-Path (Get-Location) $customWorkflowfileRelativePath) | Should -Exist
-Get-ContentLF -Path (Join-Path (Get-Location) $customWorkflowfileRelativePath) | Should -Be $customWorkflowContent.Replace("`r", "").TrimEnd("`n")
+Get-ContentLF -Path (Join-Path (Get-Location) $customWorkflowfileRelativePath) | Should -Be $finalRepoCustomWorkflowContent.Replace("`r", "").TrimEnd("`n")
+
+# Check that default custom file is present (in template's filesToInclude)
+(Join-Path (Get-Location) $defaultCustomFileName) | Should -Exist
+Get-ContentLF -Path (Join-Path (Get-Location) $defaultCustomFileName) | Should -Be $defaultCustomFileContent.Replace("`r", "").TrimEnd("`n")
+# Check that optional custom file is NOT present (not in default or template's filesToInclude)
+(Join-Path (Get-Location) $optionalCustomFileName) | Should -Not -Exist
+# Check that legacy custom file is NOT present (in template's filesToRemove)
+(Join-Path (Get-Location) $legacyCustomFileName) | Should -Not -Exist
+
+# Check that excluded workflow file is NOT present (in default filesToInclude and template's filesToExclude)
+(Join-Path (Get-Location) $excludedWorkflowFileRelativePath) | Should -Not -Exist
+# Check that missing workflow file is present (in default filesToInclude, propagated from PTE template)
+(Join-Path (Get-Location) $missingWorkflowFileRelativePath) | Should -Exist
+
+# Add customALGoFiles settings to the final repository
+$null = Add-PropertiesToJsonFile -path '.github/AL-Go-Settings.json' -properties @{
+ "customALGoFiles" = @{
+ "filesToInclude" = @( @{ "filter" = $optionalCustomFileName } )
+ "filesToExclude" = @( @{ "filter" = $defaultCustomFileName } )
+ }
+}
-# Check that custom file is NOT present
-(Join-Path (Get-Location) $customFileName) | Should -Not -Exist # Custom file should not be copied by default
+# Push
+CommitAndPush -commitMessage 'Add custom files to be updated when updating AL-Go system files [skip ci]'
-# Add custom file to be copied via settings
-$null = Add-PropertiesToJsonFile -path '.github/AL-Go-Settings.json' -properties @{ "customALGoFiles" = @{ "filesToInclude" = @( @{ "filter" = $customFileName } ) } }
+#endregion
-# Push
-CommitAndPush -commitMessage 'Add custom file to be updated when updating AL-Go system files [skip ci]'
+#region update final repository with template and final repository customizations
-# Update AL-Go System Files to uptake custom file
+# Update AL-Go System Files for final repository to uptake customizations from final repository
RunUpdateAlGoSystemFiles -directCommit -wait -templateUrl $templateRepository -ghTokenWorkflow $algoauthapp -repository $repository -branch $branch | Out-Null
+# Stop all currently running workflows on final repository
+CancelAllWorkflows -repository $repository
+
# Pull changes
Pull
-# Check that custom file is now present
-(Join-Path (Get-Location) $customFileName) | Should -Exist
-Get-ContentLF -Path (Join-Path (Get-Location) $customFileName)| Should -Be $customFileContent.Replace("`r", "").TrimEnd("`n")
+# Check that custom workflow file is present
+(Join-Path (Get-Location) $customWorkflowfileRelativePath) | Should -Exist
+Get-ContentLF -Path (Join-Path (Get-Location) $customWorkflowfileRelativePath) | Should -Be $finalRepoCustomWorkflowContent.Replace("`r", "").TrimEnd("`n")
+
+# Check that default custom file is NOT present (in repos's filesToExclude and template's filesToInclude)
+(Join-Path (Get-Location) $defaultCustomFileName) | Should -Not -Exist
+# Check that optional custom file is present (in repos's filesToInclude)
+(Join-Path (Get-Location) $optionalCustomFileName) | Should -Exist
+Get-ContentLF -Path (Join-Path (Get-Location) $optionalCustomFileName) | Should -Be $optionalCustomFileContent.Replace("`r", "").TrimEnd("`n")
+# Check that legacy custom file is NOT present (in template's filesToRemove)
+(Join-Path (Get-Location) $legacyCustomFileName) | Should -Not -Exist
+
+# Check that excluded workflow file is NOT present (in default filesToInclude and template's filesToExclude)
+(Join-Path (Get-Location) $excludedWorkflowFileRelativePath) | Should -Not -Exist
+# Check that missing workflow file is present (in default filesToInclude, propagated from PTE template)
+(Join-Path (Get-Location) $missingWorkflowFileRelativePath) | Should -Exist
+
+#endregion
+
+#region validate final repository CI/CD workflow
# Run CICD
$run = RunCICD -repository $repository -branch $branch -wait
# Check Custom Jobs
-Test-LogContainsFromRun -runid $run.id -jobName 'CustomJob-TemplateInit' -stepName 'Init' -expectedText 'CustomJob-TemplateInit was here!'
-Test-LogContainsFromRun -runid $run.id -jobName 'CustomJob-TemplateDeploy' -stepName 'Deploy' -expectedText 'CustomJob-TemplateDeploy was here!'
-Test-LogContainsFromRun -runid $run.id -jobName 'CustomJob-PreDeploy' -stepName 'PreDeploy' -expectedText 'CustomJob-PreDeploy was here!'
-Test-LogContainsFromRun -runid $run.id -jobName 'CustomJob-PostDeploy' -stepName 'PostDeploy' -expectedText 'CustomJob-PostDeploy was here!'
-{ Test-LogContainsFromRun -runid $run.id -jobName 'JustSomeJob' -stepName 'JustSomeStep' -expectedText 'JustSomeJob was here!' } | Should -Throw
-{ Test-LogContainsFromRun -runid $run.id -jobName 'JustSomeTemplateJob' -stepName 'JustSomeTemplateStep' -expectedText 'JustSomeTemplateJob was here!' } | Should -Throw
+Test-LogContainsFromRun -repository $repository -runid $run.id -jobName 'CustomJob-TemplateInit' -stepName 'Init' -expectedText 'CustomJob-TemplateInit was here!'
+Test-LogContainsFromRun -repository $repository -runid $run.id -jobName 'CustomJob-TemplateDeploy' -stepName 'Deploy' -expectedText 'CustomJob-TemplateDeploy was here!'
+Test-LogContainsFromRun -repository $repository -runid $run.id -jobName 'CustomJob-PreDeploy' -stepName 'PreDeploy' -expectedText 'CustomJob-PreDeploy was here!'
+Test-LogContainsFromRun -repository $repository -runid $run.id -jobName 'CustomJob-PostDeploy' -stepName 'PostDeploy' -expectedText 'CustomJob-PostDeploy was here!'
+{ Test-LogContainsFromRun -repository $repository -runid $run.id -jobName 'JustSomeJob' -stepName 'JustSomeStep' -expectedText 'JustSomeJob was here!' } | Should -Throw
+{ Test-LogContainsFromRun -repository $repository -runid $run.id -jobName 'JustSomeTemplateJob' -stepName 'JustSomeTemplateStep' -expectedText 'JustSomeTemplateJob was here!' } | Should -Throw
+
+#endregion
Set-Location $prevLocation
+RefreshToken -repository $repository
RemoveRepository -repository $repository -path $finalRepoPath
+RefreshToken -repository $templateRepository
RemoveRepository -repository $templateRepository -path $templateRepoPath