diff --git a/Actions/.Modules/ReadSettings.psm1 b/Actions/.Modules/ReadSettings.psm1
index ec6dc5588..38bd6a494 100644
--- a/Actions/.Modules/ReadSettings.psm1
+++ b/Actions/.Modules/ReadSettings.psm1
@@ -16,13 +16,19 @@ $CustomTemplateProjectSettingsFile = Join-Path '.github' $CustomTemplateProjectS
function MergeCustomObjectIntoOrderedDictionary {
Param(
[System.Collections.Specialized.OrderedDictionary] $dst,
- [PSCustomObject] $src
+ [PSCustomObject] $src,
+ [string[]] $srcImportantSettings = @(),
+ [string[]] $dstImportantSettings = @()
)
# If the src object contains property 'overwriteSettings' (list of settings), remove these settings from the dst object, so that they can be re-added with the new value later on
if ($src.PSObject.Properties.Name -contains "overwriteSettings") {
$src.overwriteSettings | ForEach-Object {
$prop = $_
+ if ($dstImportantSettings -contains $prop -and $srcImportantSettings -notcontains $prop) {
+ OutputDebug "Ignoring overwriteSettings for '$prop' because it is important in higher priority settings and not marked important in source"
+ return
+ }
if ($dst.Contains($prop) -and $src.PSObject.Properties.Name -contains $prop) {
# Remove the property from the destination object only if it also exists in the source object. The property will be re-added with the new value later on.
OutputDebug "Overwriting setting $prop"
@@ -37,7 +43,7 @@ function MergeCustomObjectIntoOrderedDictionary {
$src.PSObject.Properties.GetEnumerator() | ForEach-Object {
$prop = $_.Name
- # Skip overwriteSettings property as it's only used to remove settings from the destination object and is specific to the source object
+ # Skip overwriteSettings property as it's only used for configuration, not actual settings
if ($prop -eq "overwriteSettings") {
return
}
@@ -62,15 +68,23 @@ function MergeCustomObjectIntoOrderedDictionary {
# If the property exists in the source object, but is of a different type, throw an error
# If the property exists in the source object:
# If the property is an Object, call this function recursively to merge values
- # If the property is an Object[], merge the arrays
+ # If the property is an Object[], merge the arrays (even if important - arrays always merge)
# If the property is a simple type, replace the value in the destination object with the value from the source object
@($dst.Keys) | ForEach-Object {
$prop = $_
+
if ($src.PSObject.Properties.Name -eq $prop) {
$dstProp = $dst."$prop"
$srcProp = $src."$prop"
$dstPropType = $dstProp.GetType().Name
$srcPropType = $srcProp.GetType().Name
+
+ # For non-array properties: skip if this setting is marked as important from higher priority source,
+ # unless the lower-priority source also marks this property as important.
+ if ($dstImportantSettings -contains $prop -and $srcPropType -ne "Object[]" -and $srcImportantSettings -notcontains $prop) {
+ OutputDebug "Skipping important setting '$prop' marked from higher priority source (non-array type)"
+ return
+ }
if ($srcPropType -eq "PSCustomObject" -and $dstPropType -eq "OrderedDictionary") {
MergeCustomObjectIntoOrderedDictionary -dst $dst."$prop" -src $srcProp
}
@@ -109,8 +123,7 @@ function MergeCustomObjectIntoOrderedDictionary {
function GetDefaultSettings
(
[string] $repoName
-)
-{
+) {
return [ordered]@{
"type" = "PTE"
"unusedALGoSystemFiles" = @()
@@ -180,10 +193,10 @@ function GetDefaultSettings
"configPackages" = @()
"appSourceCopMandatoryAffixes" = @()
"deliverToAppSource" = [ordered]@{
- "mainAppFolder" = ""
- "productId" = ""
- "includeDependencies" = @()
- "continuousDelivery" = $false
+ "mainAppFolder" = ""
+ "productId" = ""
+ "includeDependencies" = @()
+ "continuousDelivery" = $false
}
"obsoleteTagMinAllowedMajorMinor" = ""
"memoryLimit" = ""
@@ -201,11 +214,11 @@ function GetDefaultSettings
"cacheKeepDays" = 3
"alwaysBuildAllProjects" = $false
"incrementalBuilds" = [ordered]@{
- "onPush" = $false
- "onPull_Request" = $true
- "onSchedule" = $false
- "retentionDays" = 30
- "mode" = "modifiedApps" # modifiedProjects, modifiedApps
+ "onPush" = $false
+ "onPull_Request" = $true
+ "onSchedule" = $false
+ "retentionDays" = 30
+ "mode" = "modifiedApps" # modifiedProjects, modifiedApps
}
"microsoftTelemetryConnectionString" = "InstrumentationKey=cd2cc63e-0f37-4968-b99a-532411a314b8;IngestionEndpoint=https://northeurope-2.in.applicationinsights.azure.com/"
"partnerTelemetryConnectionString" = ""
@@ -214,54 +227,55 @@ function GetDefaultSettings
"buildModes" = @()
"useCompilerFolder" = $false
"workspaceCompilation" = [ordered]@{
- "enabled" = $false
- "parallelism" = 1
+ "enabled" = $false
+ "parallelism" = 1
}
"pullRequestTrigger" = "pull_request"
"bcptThresholds" = [ordered]@{
- "DurationWarning" = 10
- "DurationError" = 25
- "NumberOfSqlStmtsWarning" = 5
- "NumberOfSqlStmtsError" = 10
+ "DurationWarning" = 10
+ "DurationError" = 25
+ "NumberOfSqlStmtsWarning" = 5
+ "NumberOfSqlStmtsError" = 10
}
"fullBuildPatterns" = @()
"excludeEnvironments" = @()
"alDoc" = [ordered]@{
- "continuousDeployment" = $false
- "deployToGitHubPages" = $true
- "maxReleases" = 3
- "groupByProject" = $true
- "includeProjects" = @()
- "excludeProjects" = @()
- "header" = "Documentation for {REPOSITORY} {VERSION}"
- "footer" = "Documentation for {REPOSITORY} made with AL-Go for GitHub, ALDoc and DocFx"
- "defaultIndexMD" = "## Reference documentation\n\nThis is the generated reference documentation for [{REPOSITORY}](https://github.com/{REPOSITORY}).\n\nYou can use the navigation bar at the top and the table of contents to the left to navigate your documentation.\n\nYou can change this content by creating/editing the **{INDEXTEMPLATERELATIVEPATH}** file in your repository or use the alDoc:defaultIndexMD setting in your repository settings file (.github/AL-Go-Settings.json)\n\n{RELEASENOTES}"
- "defaultReleaseMD" = "## Release reference documentation\n\nThis is the generated reference documentation for [{REPOSITORY}](https://github.com/{REPOSITORY}).\n\nYou can use the navigation bar at the top and the table of contents to the left to navigate your documentation.\n\nYou can change this content by creating/editing the **{INDEXTEMPLATERELATIVEPATH}** file in your repository or use the alDoc:defaultReleaseMD setting in your repository settings file (.github/AL-Go-Settings.json)\n\n{RELEASENOTES}"
+ "continuousDeployment" = $false
+ "deployToGitHubPages" = $true
+ "maxReleases" = 3
+ "groupByProject" = $true
+ "includeProjects" = @()
+ "excludeProjects" = @()
+ "header" = "Documentation for {REPOSITORY} {VERSION}"
+ "footer" = "Documentation for {REPOSITORY} made with AL-Go for GitHub, ALDoc and DocFx"
+ "defaultIndexMD" = "## Reference documentation\n\nThis is the generated reference documentation for [{REPOSITORY}](https://github.com/{REPOSITORY}).\n\nYou can use the navigation bar at the top and the table of contents to the left to navigate your documentation.\n\nYou can change this content by creating/editing the **{INDEXTEMPLATERELATIVEPATH}** file in your repository or use the alDoc:defaultIndexMD setting in your repository settings file (.github/AL-Go-Settings.json)\n\n{RELEASENOTES}"
+ "defaultReleaseMD" = "## Release reference documentation\n\nThis is the generated reference documentation for [{REPOSITORY}](https://github.com/{REPOSITORY}).\n\nYou can use the navigation bar at the top and the table of contents to the left to navigate your documentation.\n\nYou can change this content by creating/editing the **{INDEXTEMPLATERELATIVEPATH}** file in your repository or use the alDoc:defaultReleaseMD setting in your repository settings file (.github/AL-Go-Settings.json)\n\n{RELEASENOTES}"
}
"trustMicrosoftNuGetFeeds" = $true
"nuGetFeedSelectMode" = "LatestMatching"
"commitOptions" = [ordered]@{
- "messageSuffix" = ""
- "pullRequestAutoMerge" = $false
- "pullRequestMergeMethod" = "squash"
- "pullRequestLabels" = @()
- "createPullRequest" = $true
+ "messageSuffix" = ""
+ "pullRequestAutoMerge" = $false
+ "pullRequestMergeMethod" = "squash"
+ "pullRequestLabels" = @()
+ "createPullRequest" = $true
}
"trustedSigning" = [ordered]@{
- "Endpoint" = ""
- "Account" = ""
- "CertificateProfile" = ""
+ "Endpoint" = ""
+ "Account" = ""
+ "CertificateProfile" = ""
}
"useGitSubmodules" = "false"
"gitSubmodulesTokenSecretName" = "gitSubmodulesToken"
"shortLivedArtifactsRetentionDays" = 1 # 0 means use GitHub default
"reportSuppressedDiagnostics" = $false
"workflowDefaultInputs" = @()
- "customALGoFiles" = [ordered]@{
- "filesToInclude" = @()
- "filesToExclude" = @()
+ "customALGoFiles" = [ordered]@{
+ "filesToInclude" = @()
+ "filesToExclude" = @()
}
- "postponeProjectInBuildOrder" = $false
+ "postponeProjectInBuildOrder" = $false
+ "importantSettings" = @()
}
}
@@ -311,7 +325,7 @@ function GetDefaultSettings
JSON formatted string that will be applied last to override any other settings. These settings have the highest precedence.
#>
function ReadSettings {
- Param(
+ param(
[string] $baseFolder = "$ENV:GITHUB_WORKSPACE",
[string] $repoName = "$ENV:GITHUB_REPOSITORY",
[string] $project = '.',
@@ -333,7 +347,7 @@ function ReadSettings {
}
function GetSettingsObject {
- Param(
+ param(
[string] $path
)
@@ -366,8 +380,8 @@ function ReadSettings {
if ($orgSettingsVariableValue) {
$orgSettingsVariableObject = $orgSettingsVariableValue | ConvertFrom-Json
$settingsObjects += @{
- "Source" = "ALGoOrgSettings"
- "Type" = "Variable"
+ "Source" = "ALGoOrgSettings"
+ "Type" = "Variable"
"Settings" = $orgSettingsVariableObject
}
}
@@ -375,16 +389,16 @@ function ReadSettings {
# Read settings from the custom template repository settings file
$customTemplateRepoSettingsObject = GetSettingsObject -Path (Join-Path $baseFolder $CustomTemplateRepoSettingsFile)
$settingsObjects += @{
- "Source" = "$CustomTemplateRepoSettingsFile"
- "Type" = "File"
+ "Source" = "$CustomTemplateRepoSettingsFile"
+ "Type" = "File"
"Settings" = $customTemplateRepoSettingsObject
}
# Read settings from repository settings file
$repoSettingsObject = GetSettingsObject -Path (Join-Path $baseFolder $RepoSettingsFile)
$settingsObjects += @{
- "Source" = "$RepoSettingsFile"
- "Type" = "File"
+ "Source" = "$RepoSettingsFile"
+ "Type" = "File"
"Settings" = $repoSettingsObject
}
@@ -392,8 +406,8 @@ function ReadSettings {
if ($repoSettingsVariableValue) {
$repoSettingsVariableObject = $repoSettingsVariableValue | ConvertFrom-Json
$settingsObjects += @{
- "Source" = "ALGoRepoSettings"
- "Type" = "Variable"
+ "Source" = "ALGoRepoSettings"
+ "Type" = "Variable"
"Settings" = $repoSettingsVariableObject
}
}
@@ -401,8 +415,8 @@ function ReadSettings {
if ($project) {
$customTemplateProjectSettingsObject = GetSettingsObject -Path (Join-Path $baseFolder $CustomTemplateProjectSettingsFile)
$settingsObjects += @{
- "Source" = "$CustomTemplateProjectSettingsFile"
- "Type" = "File"
+ "Source" = "$CustomTemplateProjectSettingsFile"
+ "Type" = "File"
"Settings" = $customTemplateProjectSettingsObject
}
@@ -410,8 +424,8 @@ function ReadSettings {
$projectFolder = Join-Path $baseFolder $project -Resolve
$projectSettingsObject = GetSettingsObject -Path (Join-Path $projectFolder $ALGoSettingsFile)
$settingsObjects += @{
- "Source" = "$(Join-Path $project $ALGoSettingsFile)"
- "Type" = "File"
+ "Source" = "$(Join-Path $project $ALGoSettingsFile)"
+ "Type" = "File"
"Settings" = $projectSettingsObject
}
}
@@ -420,8 +434,8 @@ function ReadSettings {
# Read settings from workflow settings file
$workflowSettingsObject = GetSettingsObject -Path (Join-Path $githubFolder "$workflowName.settings.json")
$settingsObjects += @{
- "Source" = "$(Join-Path ".github" "$workflowName.settings.json")"
- "Type" = "File"
+ "Source" = "$(Join-Path ".github" "$workflowName.settings.json")"
+ "Type" = "File"
"Settings" = $workflowSettingsObject
}
@@ -429,16 +443,16 @@ function ReadSettings {
# Read settings from project workflow settings file
$projectWorkflowSettingsObject = GetSettingsObject -Path (Join-Path $projectFolder "$ALGoFolderName/$workflowName.settings.json")
$settingsObjects += @{
- "Source" = "$(Join-Path $project "$ALGoFolderName/$workflowName.settings.json")"
- "Type" = "File"
+ "Source" = "$(Join-Path $project "$ALGoFolderName/$workflowName.settings.json")"
+ "Type" = "File"
"Settings" = $projectWorkflowSettingsObject
}
# Read settings from user settings file
- $userSettingsObject = GetSettingsObject -Path (Join-Path $projectFolder "$ALGoFolderName/$userName.settings.json")
+ $userSettingsObject = GetSettingsObject -Path (Join-Path $projectFolder "$ALGoFolderName/$userName.settings.json")
$settingsObjects += @{
- "Source" = "$(Join-Path $project "$ALGoFolderName/$userName.settings.json")"
- "Type" = "File"
+ "Source" = "$(Join-Path $project "$ALGoFolderName/$userName.settings.json")"
+ "Type" = "File"
"Settings" = $userSettingsObject
}
}
@@ -461,8 +475,8 @@ function ReadSettings {
}
}
$settingsObjects += @{
- "Source" = "ALGoEnvSettings for $environmentName"
- "Type" = "Variable"
+ "Source" = "ALGoEnvSettings for $environmentName"
+ "Type" = "Variable"
"Settings" = $environmentVariableObject
}
}
@@ -476,19 +490,27 @@ function ReadSettings {
throw "Failed to parse customSettings JSON: $($_.Exception.Message)"
}
$settingsObjects += @{
- "Source" = "CustomSettings"
- "Type" = "Parameter"
+ "Source" = "CustomSettings"
+ "Type" = "Parameter"
"Settings" = $customSettingsObject
}
}
- foreach($settingsObject in $settingsObjects) {
+ $currentImportantSettings = @()
+ foreach ($settingsObject in $settingsObjects) {
$settingsJson = $settingsObject.Settings
if ($settingsJson) {
OutputDebug "Applying settings from $($settingsObject.Source) ($($settingsObject.Type))"
- MergeCustomObjectIntoOrderedDictionary -dst $settings -src $settingsJson
+ $srcImportantSettings = @()
+ if ($settingsJson.PSObject.Properties.Name -contains "importantSettings") {
+ $srcImportantSettings = @($settingsJson.importantSettings)
+ }
+ MergeCustomObjectIntoOrderedDictionary -dst $settings -src $settingsJson -srcImportantSettings $srcImportantSettings -dstImportantSettings $currentImportantSettings
+ if ($settingsJson.PSObject.Properties.Name -contains "importantSettings") {
+ $currentImportantSettings = @($currentImportantSettings + $srcImportantSettings | Select-Object -Unique)
+ }
if ($settingsJson.PSObject.Properties.Name -eq "ConditionalSettings") {
- foreach($conditionalSetting in $settingsJson.ConditionalSettings) {
+ foreach ($conditionalSetting in $settingsJson.ConditionalSettings) {
if ("$conditionalSetting" -ne "") {
$conditionMet = $true
$conditions = @()
@@ -498,7 +520,7 @@ function ReadSettings {
if ($conditionMet -and $conditionalSetting.PSObject.Properties.Name -eq $propName) {
# If the property name is workflows then we should sanitize the workflow name in the same way we sanitize the $workflowName variable
- if($propName -eq "workflows") {
+ if ($propName -eq "workflows") {
$conditionalSetting."$propName" = $conditionalSetting."$propName" | ForEach-Object { SanitizeWorkflowName -workflowName $_ }
}
@@ -508,7 +530,14 @@ function ReadSettings {
}
if ($conditionMet) {
OutputDebug "Applying conditional settings for $($conditions -join ", ")"
- MergeCustomObjectIntoOrderedDictionary -dst $settings -src $conditionalSetting.settings
+ $srcImportantSettings = @()
+ if ($conditionalSetting.settings.PSObject.Properties.Name -contains "importantSettings") {
+ $srcImportantSettings = @($conditionalSetting.settings.importantSettings)
+ }
+ MergeCustomObjectIntoOrderedDictionary -dst $settings -src $conditionalSetting.settings -srcImportantSettings $srcImportantSettings -dstImportantSettings $currentImportantSettings
+ if ($conditionalSetting.settings.PSObject.Properties.Name -contains "importantSettings") {
+ $currentImportantSettings = @($currentImportantSettings + $srcImportantSettings | Select-Object -Unique)
+ }
}
}
}
diff --git a/Actions/.Modules/settings.schema.json b/Actions/.Modules/settings.schema.json
index 8cf29ab0d..5667e10a5 100644
--- a/Actions/.Modules/settings.schema.json
+++ b/Actions/.Modules/settings.schema.json
@@ -395,7 +395,7 @@
"type": "string",
"enum": ["includeDependencies"]
},
- "description": "An array of settings to be overwritten by the current deliverToAppSource setting. See https://aka.ms/ALGoSettings#overwriteSettings"
+ "description": "An array of settings to be overwritten by the current deliverToAppSource setting. If a setting is marked as important in higher-priority settings, overwrite is ignored unless the current source also marks that same setting as important. See https://aka.ms/ALGoSettings#overwriteSettings"
}
},
"required": [
@@ -650,7 +650,7 @@
"type": "string",
"enum": ["includeProjects", "excludeProjects"]
},
- "description": "An array of settings to be overwritten by the current alDoc setting. See https://aka.ms/ALGoSettings#overwriteSettings"
+ "description": "An array of settings to be overwritten by the current alDoc setting. If a setting is marked as important in higher-priority settings, overwrite is ignored unless the current source also marks that same setting as important. See https://aka.ms/ALGoSettings#overwriteSettings"
}
},
"commitOptions": {
@@ -680,7 +680,7 @@
"type": "string",
"enum": ["pullRequestLabels"]
},
- "description": "An array of settings to be overwritten by the current commitOptions setting. See https://aka.ms/ALGoSettings#overwriteSettings"
+ "description": "An array of settings to be overwritten by the current commitOptions setting. If a setting is marked as important in higher-priority settings, overwrite is ignored unless the current source also marks that same setting as important. See https://aka.ms/ALGoSettings#overwriteSettings"
}
},
"required": [
@@ -728,7 +728,15 @@
"type": "string",
"enum": ["unusedALGoSystemFiles", "projects", "additionalCountries", "appDependencies", "appFolders", "testDependencies", "testFolders", "bcptTestFolders", "pageScriptingTests", "restoreDatabases", "installApps", "installTestApps", "customCodeCops", "configPackages", "appSourceCopMandatoryAffixes", "deliverToAppSource", "appDependencyProbingPaths", "incrementalBuilds", "environments", "buildModes", "bcptThresholds", "fullBuildPatterns", "excludeEnvironments", "alDoc", "commitOptions", "trustedSigning"]
},
- "description": "An array of settings to be overwritten by the current settings. See https://aka.ms/ALGoSettings#overwriteSettings"
+ "description": "An array of settings to be overwritten by the current settings. If a setting is marked as important in higher-priority settings, overwrite is ignored unless the current source also marks that same setting as important. See https://aka.ms/ALGoSettings#overwriteSettings"
+ },
+ "importantSettings": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "default": [],
+ "description": "An array of top-level setting names that are protected from non-important lower-priority overrides. If the same setting is marked as important in both source and destination, the source value is allowed to override. See https://aka.ms/ALGoSettings#importantSettings"
},
"reportSuppressedDiagnostics": {
"type": "boolean",
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index f55ae4740..14c874dec 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -1,3 +1,37 @@
+### Important settings protection
+
+A new `importantSettings` setting allows you to protect specific settings from being overridden by lower-priority sources in the settings hierarchy. When a setting is marked as important at a higher priority level, it cannot be overridden by non-important settings from lower priority sources.
+
+```json
+{
+ "importantSettings": ["country", "keyVaultName"],
+ "country": "de",
+ "keyVaultName": "orgVault"
+}
+```
+
+**Behavior:**
+- Settings marked as important in organization or repository settings cannot be overridden by non-important values from project, workflow, user, or environment settings
+- If a lower-priority source also marks the same setting as important, the lower-priority value is allowed to override
+- Important arrays are still merged by default
+- The `overwriteSettings` mechanism can replace an important setting only when the source also marks that same setting as important
+- `ConditionalSettings` respect importantSettings markings, allowing you to enforce conditional important settings based on buildMode, branch, trigger, or user
+
+**Example with ConditionalSettings:**
+```json
+{
+ "ConditionalSettings": [
+ {
+ "buildModes": ["Validate"],
+ "settings": {
+ "importantSettings": ["country"],
+ "country": "us"
+ }
+ }
+ ]
+}
+```
+
### Resilient Pull Request Status Check for large builds
The Pull Request Status Check action no longer fails on builds with more than one page of jobs (more than 100 jobs). The jobs API call now uses `--slurp` so multi-page responses are parsed as a single JSON array (previously `gh api --paginate | ConvertFrom-Json` failed with "Invalid JSON primitive" when more than one page was returned). The call is also retried, and requests a smaller page size, to tolerate the intermittent HTTP 502 responses that the GitHub jobs endpoint returns for large builds.
diff --git a/Scenarios/settings.md b/Scenarios/settings.md
index 6b58c1c37..1199bfd1f 100644
--- a/Scenarios/settings.md
+++ b/Scenarios/settings.md
@@ -316,6 +316,83 @@ then, after merging, the result settings object will contain the following value
> _**Note**_: `overwriteSettings` isn't a setting on its own and it isn't available in the output of `ReadSetting` action, for example. It's merely used to control the settings merging mechanism and allow overwriting complex settings types. The value of `overwriteSettings` should only contain settings of types _array_ or _object_ and all the settings in `overwriteSettings` should be present with the new value.
+## Important settings
+
+By default, AL-Go follows a standard settings hierarchy where settings from higher priority levels (closer to deployment) override settings from lower priority levels. However, you can mark specific settings as **important** to protect them from being overridden by lower priority settings using the `importantSettings` array.
+
+When a setting is marked as important at a higher level in the hierarchy, it cannot be overridden by non-important values from lower priority levels. If a lower-priority source also marks the same setting as important, then the lower-priority value is allowed to override.
+
+_Example_:
+Say, `ALGoOrgSettings` (organization level) contains the following values:
+
+```json
+{
+ "importantSettings": ["country", "keyVaultName"],
+ "country": "de",
+ "keyVaultName": "OrgVault"
+}
+```
+
+and `.AL-Go\settings.json` (project level, lower priority) contains the following values:
+
+```json
+{
+ "country": "us",
+ "keyVaultName": "ProjectVault"
+}
+```
+
+then, after merging, the result settings object will contain the following values:
+
+```json
+{
+ "importantSettings": ["country", "keyVaultName"],
+ "country": "de",
+ "keyVaultName": "OrgVault"
+}
+```
+
+The `country` and `keyVaultName` settings from the organization level are protected and cannot be overridden by the project level settings.
+
+_Example with ConditionalSettings_:
+Say, `ALGoOrgSettings` (organization level) contains conditional settings that set country based on buildMode:
+
+```json
+{
+ "ConditionalSettings": [
+ {
+ "buildModes": ["ValidateUS"],
+ "settings": {
+ "importantSettings": ["country"],
+ "country": "us"
+ }
+ }
+ ]
+}
+```
+
+and `.AL-Go\settings.json` (project level) contains:
+
+```json
+{
+ "country": "w1",
+ "buildModes": ["Default", "ValidateUS"]
+}
+```
+
+When reading settings for buildMode `ValidateUS`, the conditional setting from the organization level will apply. The result will be:
+
+```json
+{
+ "country": "us",
+ "buildModes": ["Default", "ValidateUS"]
+}
+```
+
+Even though the project specifies `country: "w1"`, the conditional setting from the organization level marked the country as important for the `ValidateUS` buildMode and the project value is not marked important, so the conditional value takes precedence.
+
+> _**Note**_: `importantSettings` is an array of setting names that should be protected from non-important overrides from lower priority settings. If the same setting is marked as important at both levels, the source (lower-priority) value is allowed to override the destination value. Only top-level setting names can be marked as important; nested properties within complex objects cannot be individually marked as important. Array settings marked as important are still merged with lower-priority arrays. `overwriteSettings` can force replacement for important settings only when the source also marks that same setting as important.
+
## Custom Delivery
diff --git a/Tests/ReadSettings.Test.ps1 b/Tests/ReadSettings.Test.ps1
index 569a9bcb6..ef8b8b450 100644
--- a/Tests/ReadSettings.Test.ps1
+++ b/Tests/ReadSettings.Test.ps1
@@ -536,6 +536,533 @@ InModuleScope ReadSettings { # Allows testing of private functions
$ENV:ALGoRepoSettings = ''
+ # Clean up
+ Pop-Location
+ Remove-Item -Path $tempName -Recurse -Force
+ }
+
+ It 'importantSettings from higher priority source prevents overwrite from lower priority' {
+ Mock Write-Host { }
+ Mock Out-Host { }
+
+ Push-Location
+ $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())
+ $githubFolder = Join-Path $tempName ".github"
+ $projectALGoFolder = Join-Path $tempName "Project/$ALGoFolderName"
+
+ New-Item $githubFolder -ItemType Directory | Out-Null
+ New-Item $projectALGoFolder -ItemType Directory | Out-Null
+
+ # Repo settings: important settings includes "country", set country = "de"
+ @{ "importantSettings" = @("country"); "country" = "de" } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $githubFolder "AL-Go-Settings.json") -Encoding utf8 -Force
+
+ # Project settings: try to override country = "ch"
+ @{ "country" = "ch" } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $projectALGoFolder "settings.json") -Encoding utf8 -Force
+
+ $ENV:ALGoOrgSettings = ''
+ $ENV:ALGoRepoSettings = ''
+
+ # Important setting from repo should prevent project from overwriting
+ $settings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -userName ''
+ $settings.country | Should -Be 'de' # Repo important value wins
+ $settings.importantSettings | Should -Contain 'country'
+
+ # Clean up
+ Pop-Location
+ Remove-Item -Path $tempName -Recurse -Force
+ }
+
+ It 'Multiple importantSettings are respected' {
+ Mock Write-Host { }
+ Mock Out-Host { }
+
+ Push-Location
+ $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())
+ $githubFolder = Join-Path $tempName ".github"
+ $projectALGoFolder = Join-Path $tempName "Project/$ALGoFolderName"
+
+ New-Item $githubFolder -ItemType Directory | Out-Null
+ New-Item $projectALGoFolder -ItemType Directory | Out-Null
+
+ # Repo settings: mark both country and keyVaultName as important
+ @{
+ "importantSettings" = @("country", "keyVaultName")
+ "country" = "de"
+ "keyVaultName" = "orgVault"
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $githubFolder "AL-Go-Settings.json") -Encoding utf8 -Force
+
+ # Project settings: try to override both
+ @{
+ "country" = "ch"
+ "keyVaultName" = "projectVault"
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $projectALGoFolder "settings.json") -Encoding utf8 -Force
+
+ $ENV:ALGoOrgSettings = ''
+ $ENV:ALGoRepoSettings = ''
+
+ $settings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -userName ''
+ $settings.country | Should -Be 'de'
+ $settings.keyVaultName | Should -Be 'orgVault'
+ $settings.importantSettings | Should -Contain 'country'
+ $settings.importantSettings | Should -Contain 'keyVaultName'
+
+ # Clean up
+ Pop-Location
+ Remove-Item -Path $tempName -Recurse -Force
+ }
+
+ It 'Lower-priority important settings can override higher-priority important settings' {
+ Mock Write-Host { }
+ Mock Out-Host { }
+
+ Push-Location
+ $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())
+ $githubFolder = Join-Path $tempName ".github"
+ $projectALGoFolder = Join-Path $tempName "Project/$ALGoFolderName"
+
+ New-Item $githubFolder -ItemType Directory | Out-Null
+ New-Item $projectALGoFolder -ItemType Directory | Out-Null
+
+ # Org settings (via variable): mark country as important and set to "de"
+ $ENV:ALGoOrgSettings = @{
+ "importantSettings" = @("country")
+ "country" = "de"
+ } | ConvertTo-Json -Depth 99
+
+ # Repo settings: try to override country with normal (non-important) setting = "us"
+ @{ "country" = "us" } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $githubFolder "AL-Go-Settings.json") -Encoding utf8 -Force
+
+ # Project settings: try to override with important setting = "ch"
+ @{
+ "importantSettings" = @("country")
+ "country" = "ch"
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $projectALGoFolder "settings.json") -Encoding utf8 -Force
+
+ # Project setting is also marked as important and should be allowed to override
+ # an important setting from a higher-priority source.
+ $settings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -userName ''
+ $settings.country | Should -Be 'ch' # Source important overrides destination important
+
+ $ENV:ALGoOrgSettings = ''
+
+ # Clean up
+ Pop-Location
+ Remove-Item -Path $tempName -Recurse -Force
+ }
+
+ It 'importantSettings marked arrays are still merged with lower priority arrays' {
+ Mock Write-Host { }
+ Mock Out-Host { }
+
+ Push-Location
+ $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())
+ $githubFolder = Join-Path $tempName ".github"
+ $projectALGoFolder = Join-Path $tempName "Project/$ALGoFolderName"
+
+ New-Item $githubFolder -ItemType Directory | Out-Null
+ New-Item $projectALGoFolder -ItemType Directory | Out-Null
+
+ # Repo settings: mark additionalCountries as important with specific values
+ @{
+ "importantSettings" = @("additionalCountries")
+ "additionalCountries" = @("de", "at")
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $githubFolder "AL-Go-Settings.json") -Encoding utf8 -Force
+
+ # Project settings: try to add more countries
+ @{
+ "additionalCountries" = @("ch", "be")
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $projectALGoFolder "settings.json") -Encoding utf8 -Force
+
+ $ENV:ALGoOrgSettings = ''
+ $ENV:ALGoRepoSettings = ''
+
+ # Important array settings are still merged with lower priority arrays (exception: if overwriteSettings is used)
+ $settings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -userName ''
+ $settings.additionalCountries | Should -Be @("de", "at", "ch", "be") # Org + Project values merged
+ $settings.importantSettings | Should -Contain "additionalCountries"
+
+ # Clean up
+ Pop-Location
+ Remove-Item -Path $tempName -Recurse -Force
+ }
+
+ It 'importantSettings are not overridden by overwriteSettings unless source also marks them important' {
+ Mock Write-Host { }
+ Mock Out-Host { }
+
+ Push-Location
+ $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())
+ $githubFolder = Join-Path $tempName ".github"
+ $projectALGoFolder = Join-Path $tempName "Project/$ALGoFolderName"
+
+ New-Item $githubFolder -ItemType Directory | Out-Null
+ New-Item $projectALGoFolder -ItemType Directory | Out-Null
+
+ # Repo settings: mark additionalCountries as important
+ @{
+ "importantSettings" = @("additionalCountries")
+ "additionalCountries" = @("de", "at")
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $githubFolder "AL-Go-Settings.json") -Encoding utf8 -Force
+
+ # Project settings: use overwriteSettings to override additionalCountries (force replacement instead of merge)
+ @{
+ "overwriteSettings" = @("additionalCountries")
+ "additionalCountries" = @("ch", "be")
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $projectALGoFolder "settings.json") -Encoding utf8 -Force
+
+ $ENV:ALGoOrgSettings = ''
+ $ENV:ALGoRepoSettings = ''
+
+ # overwriteSettings should be ignored because source does not mark the setting as important.
+ $settings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -userName ''
+ $settings.additionalCountries | Should -Be @("de", "at", "ch", "be")
+
+ # Clean up
+ Pop-Location
+ Remove-Item -Path $tempName -Recurse -Force
+ }
+
+ It 'importantSettings can be overridden with overwriteSettings when source also marks them important' {
+ Mock Write-Host { }
+ Mock Out-Host { }
+
+ Push-Location
+ $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())
+ $githubFolder = Join-Path $tempName ".github"
+ $projectALGoFolder = Join-Path $tempName "Project/$ALGoFolderName"
+
+ New-Item $githubFolder -ItemType Directory | Out-Null
+ New-Item $projectALGoFolder -ItemType Directory | Out-Null
+
+ @{
+ "importantSettings" = @("additionalCountries")
+ "additionalCountries" = @("de", "at")
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $githubFolder "AL-Go-Settings.json") -Encoding utf8 -Force
+
+ @{
+ "importantSettings" = @("additionalCountries")
+ "overwriteSettings" = @("additionalCountries")
+ "additionalCountries" = @("ch", "be")
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $projectALGoFolder "settings.json") -Encoding utf8 -Force
+
+ $ENV:ALGoOrgSettings = ''
+ $ENV:ALGoRepoSettings = ''
+
+ $settings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -userName ''
+ $settings.additionalCountries | Should -Be @("ch", "be")
+
+ Pop-Location
+ Remove-Item -Path $tempName -Recurse -Force
+ }
+
+ It 'Non-important array settings are merged normally (baseline)' {
+ Mock Write-Host { }
+ Mock Out-Host { }
+
+ Push-Location
+ $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())
+ $githubFolder = Join-Path $tempName ".github"
+ $projectALGoFolder = Join-Path $tempName "Project/$ALGoFolderName"
+
+ New-Item $githubFolder -ItemType Directory | Out-Null
+ New-Item $projectALGoFolder -ItemType Directory | Out-Null
+
+ # Repo settings: additionalCountries WITHOUT marking as important
+ @{
+ "additionalCountries" = @("de", "at")
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $githubFolder "AL-Go-Settings.json") -Encoding utf8 -Force
+
+ # Project settings: add more countries
+ @{
+ "additionalCountries" = @("ch", "be")
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $projectALGoFolder "settings.json") -Encoding utf8 -Force
+
+ $ENV:ALGoOrgSettings = ''
+ $ENV:ALGoRepoSettings = ''
+
+ # Without important marking, arrays should be merged
+ $settings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -userName ''
+ $settings.additionalCountries | Should -Be @("de", "at", "ch", "be") # All values merged
+
+ # Clean up
+ Pop-Location
+ Remove-Item -Path $tempName -Recurse -Force
+ }
+
+ It 'Empty importantSettings has no effect (backward compatibility)' {
+ Mock Write-Host { }
+ Mock Out-Host { }
+
+ Push-Location
+ $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())
+ $githubFolder = Join-Path $tempName ".github"
+ $projectALGoFolder = Join-Path $tempName "Project/$ALGoFolderName"
+
+ New-Item $githubFolder -ItemType Directory | Out-Null
+ New-Item $projectALGoFolder -ItemType Directory | Out-Null
+
+ # Repo settings: importantSettings is empty
+ @{
+ "importantSettings" = @()
+ "country" = "us"
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $githubFolder "AL-Go-Settings.json") -Encoding utf8 -Force
+
+ # Project settings: override country
+ @{
+ "country" = "ch"
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $projectALGoFolder "settings.json") -Encoding utf8 -Force
+
+ $ENV:ALGoOrgSettings = ''
+ $ENV:ALGoRepoSettings = ''
+
+ # Without important marking, normal hierarchy applies
+ $settings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -userName ''
+ $settings.country | Should -Be 'ch' # Project wins (normal behavior)
+ $settings.importantSettings | Should -Be @() # Empty array preserved
+
+ # Clean up
+ Pop-Location
+ Remove-Item -Path $tempName -Recurse -Force
+ }
+
+ It 'ConditionalSetting with importantSettings at repo level overrides project setting for specific buildMode' {
+ Mock Write-Host { }
+ Mock Out-Host { }
+
+ Push-Location
+ $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())
+ $githubFolder = Join-Path $tempName ".github"
+ $projectALGoFolder = Join-Path $tempName "Project/$ALGoFolderName"
+
+ New-Item $githubFolder -ItemType Directory | Out-Null
+ New-Item $projectALGoFolder -ItemType Directory | Out-Null
+
+ # Repo settings: ConditionalSetting for buildMode "ValidateUS" with important country marking
+ @{
+ "ConditionalSettings" = @(
+ @{
+ "buildModes" = @("ValidateUS")
+ "settings" = @{
+ "importantSettings" = @("country")
+ "country" = "us"
+ }
+ }
+ )
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $githubFolder "AL-Go-Settings.json") -Encoding utf8 -Force
+
+ # Project settings: country = "w1", buildModes include "ValidateUS"
+ @{
+ "country" = "w1"
+ "buildModes" = @("Default", "ValidateUS")
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $projectALGoFolder "settings.json") -Encoding utf8 -Force
+
+ $ENV:ALGoOrgSettings = ''
+ $ENV:ALGoRepoSettings = ''
+
+ # When reading for buildMode "Default", project country "w1" should be used
+ $settingsDefault = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -buildMode 'Default' -userName ''
+ $settingsDefault.country | Should -Be 'w1' # No org conditional applies for "Default"
+
+ # When reading for buildMode "ValidateUS", repo conditional with important marking should override project
+ $settingsValidateUS = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -buildMode 'ValidateUS' -userName ''
+ $settingsValidateUS.country | Should -Be 'us' # Repo conditional important setting wins
+ $settingsValidateUS.buildModes | Should -Contain 'ValidateUS'
+ $settingsValidateUS.importantSettings | Should -Contain 'country'
+
+ # Clean up
+ Pop-Location
+ Remove-Item -Path $tempName -Recurse -Force
+ }
+
+ It 'importantSettings are merged correctly' {
+ Mock Write-Host { }
+ Mock Out-Host { }
+
+ Push-Location
+ $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())
+ $githubFolder = Join-Path $tempName ".github"
+ $projectALGoFolder = Join-Path $tempName "Project/$ALGoFolderName"
+
+ New-Item $githubFolder -ItemType Directory | Out-Null
+ New-Item $projectALGoFolder -ItemType Directory | Out-Null
+
+ # Org settings: importantSettings is filled
+ $ENV:ALGoOrgSettings = @{
+ "importantSettings" = @("country")
+ "country" = "us"
+ } | ConvertTo-Json -Depth 99
+
+ # Repo settings: add another important setting
+ $ENV:ALGoRepoSettings = @{
+ "importantSettings" = @("companyName")
+ "country" = "de"
+ "companyName" = "MyCompany"
+ } | ConvertTo-Json -Depth 99
+
+ # Project settings: add another important setting
+ @{
+ "importantSettings" = @("keyVaultName")
+ "country" = "ch"
+ "keyVaultName" = "mykv"
+ "companyName" = "AnotherCompany"
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $projectALGoFolder "settings.json") -Encoding utf8 -Force
+
+ # Without important marking, normal hierarchy applies
+ $settings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -userName ''
+ $settings.importantSettings | Should -Contain 'country' # from repo settings
+ $settings.importantSettings | Should -Contain 'companyName' # from repo settings
+ $settings.importantSettings | Should -Contain 'keyVaultName' # from project settings
+
+ $settings.country | Should -Be 'us' # from org settings
+ $settings.companyName | Should -Be 'MyCompany' # from repo settings
+ $settings.keyVaultName | Should -Be 'mykv' # from project settings
+
+
+ # Clean up
+ Pop-Location
+ Remove-Item -Path $tempName -Recurse -Force
+ }
+ It 'conditional importantSettings are merged correctly' {
+ Mock Write-Host { }
+ Mock Out-Host { }
+
+ Push-Location
+ $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())
+ $githubFolder = Join-Path $tempName ".github"
+ $projectALGoFolder = Join-Path $tempName "Project/$ALGoFolderName"
+
+ New-Item $githubFolder -ItemType Directory | Out-Null
+ New-Item $projectALGoFolder -ItemType Directory | Out-Null
+
+ # Org settings: importantSettings is filled
+ $ENV:ALGoOrgSettings = @{
+ "ConditionalSettings" = @(
+ @{
+ "buildModes" = @("CustomBuildMode")
+ "settings" = @{
+ "importantSettings" = @("country")
+ "country" = "us"
+ }
+ })
+ } | ConvertTo-Json -Depth 99
+
+ # Repo settings: add another important setting
+ $ENV:ALGoRepoSettings = @{
+ "ConditionalSettings" = @(
+ @{
+ "buildModes" = @("CustomBuildMode")
+ "settings" = @{ "importantSettings" = @("companyName")
+ "country" = "de"
+ "companyName" = "MyCompany"
+ }
+ })
+ } | ConvertTo-Json -Depth 99
+
+ # Project settings: add another important setting
+ @{
+ "ConditionalSettings" = @(
+ @{
+ "buildModes" = @("CustomBuildMode")
+ "settings" = @{ "importantSettings" = @("keyVaultName")
+ "country" = "ch"
+ "keyVaultName" = "mykv"
+ "companyName" = "AnotherCompany"
+ }
+ })
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $projectALGoFolder "settings.json") -Encoding utf8 -Force
+
+ # Without important marking, normal hierarchy applies
+ $settings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -userName '' -buildMode 'CustomBuildMode'
+ $settings.importantSettings | Should -Contain 'country' # from repo settings
+ $settings.importantSettings | Should -Contain 'companyName' # from repo settings
+ $settings.importantSettings | Should -Contain 'keyVaultName' # from project settings
+
+ $settings.country | Should -Be 'us' # from org settings
+ $settings.companyName | Should -Be 'MyCompany' # from repo settings
+ $settings.keyVaultName | Should -Be 'mykv' # from project settings
+
+
+ # Clean up
+ Pop-Location
+ Remove-Item -Path $tempName -Recurse -Force
+ }
+ It 'mixed importantSettings are merged correctly' {
+ Mock Write-Host { }
+ Mock Out-Host { }
+
+ Push-Location
+ $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())
+ $githubFolder = Join-Path $tempName ".github"
+ $projectALGoFolder = Join-Path $tempName "Project/$ALGoFolderName"
+
+ New-Item $githubFolder -ItemType Directory | Out-Null
+ New-Item $projectALGoFolder -ItemType Directory | Out-Null
+
+ # Org settings: importantSettings is filled
+ $ENV:ALGoOrgSettings = @{
+ "ConditionalSettings" = @(
+ @{
+ "buildModes" = @("CustomBuildMode")
+ "settings" = @{
+ "importantSettings" = @("country")
+ "country" = "us"
+ }
+ })
+ } | ConvertTo-Json -Depth 99
+
+ # Repo settings: add another important setting
+ $ENV:ALGoRepoSettings = @{
+ "importantSettings" = @("companyName")
+ "country" = "de"
+ "companyName" = "MyCompany"
+
+ } | ConvertTo-Json -Depth 99
+
+ # Project settings: add another important setting
+ @{
+ "ConditionalSettings" = @(
+ @{
+ "buildModes" = @("CustomBuildMode")
+ "settings" = @{ "importantSettings" = @("keyVaultName")
+ "country" = "ch"
+ "keyVaultName" = "mykv"
+ "companyName" = "AnotherCompany"
+ }
+ })
+ } | ConvertTo-Json -Depth 99 |
+ Set-Content -Path (Join-Path $projectALGoFolder "settings.json") -Encoding utf8 -Force
+
+ # Without important marking, normal hierarchy applies
+ $settings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -userName '' -buildMode 'CustomBuildMode'
+ $settings.importantSettings | Should -Contain 'country' # from repo settings
+ $settings.importantSettings | Should -Contain 'companyName' # from repo settings
+ $settings.importantSettings | Should -Contain 'keyVaultName' # from project settings
+
+ $settings.country | Should -Be 'us' # from org settings
+ $settings.companyName | Should -Be 'MyCompany' # from repo settings
+ $settings.keyVaultName | Should -Be 'mykv' # from project settings
+
+
# Clean up
Pop-Location
Remove-Item -Path $tempName -Recurse -Force