From bfe872fc91d440d74a1368bd6bd03add979c5549 Mon Sep 17 00:00:00 2001 From: Freddy Kristiansen Date: Thu, 19 Jan 2023 10:08:56 +0100 Subject: [PATCH] Refactor ReadSettings and add tests (#357) Issue #171 create a workspace file when creating a project Issue #356 Publish to AppSource fails in multi project repo ReadSettings has been refactored to allow organization wide settings to be added as well. CI Tests have been added to cover ReadSettings. The primary change is to ensure that $baseFolder is always the $baseFolder and $projectFolder is always a project folder (they are the same in a single project repo). ReadSettings and more now have two parameters: baseFolder and project - which clearly specifies when to read project settings files and when to read repo settings files only Comments have been added to ReadSettings and MergeCustomObjectIntoOrderedDictionary Co-authored-by: freddydk --- Actions/AL-Go-Helper.ps1 | 204 +++++++++++------ Actions/AL-Go-TestRepoHelper.ps1 | 4 +- Actions/AddExistingApp/AddExistingApp.ps1 | 18 +- Actions/CheckForUpdates/CheckForUpdates.ps1 | 24 +- Actions/CreateApp/AppHelper.psm1 | 4 +- Actions/CreateApp/CreateApp.ps1 | 24 +- Actions/Deliver/Deliver.ps1 | 77 +++---- Actions/Deploy/Deploy.ps1 | 24 +- Actions/ReadSecrets/ReadSecrets.ps1 | 6 +- Actions/ReadSettings/ReadSettings.ps1 | 27 +-- Actions/RunPipeline/RunPipeline.ps1 | 20 +- .../WorkflowInitialize/WorkflowInitialize.ps1 | 4 +- README.md | 6 +- RELEASENOTES.md | 8 + .../AppSource App/.AL-Go/cloudDevEnv.ps1 | 12 +- .../AppSource App/.AL-Go/localDevEnv.ps1 | 6 +- .../AppSource App/.github/workflows/CICD.yaml | 4 +- .../workflows/PublishToEnvironment.yaml | 4 +- .../.AL-Go/cloudDevEnv.ps1 | 10 +- .../.AL-Go/localDevEnv.ps1 | 6 +- .../.github/workflows/CICD.yaml | 4 +- .../workflows/PublishToEnvironment.yaml | 4 +- Tests/AL-Go-Helper.Test.ps1 | 206 ++++++++++++++++++ 23 files changed, 497 insertions(+), 209 deletions(-) create mode 100644 Tests/AL-Go-Helper.Test.ps1 diff --git a/Actions/AL-Go-Helper.ps1 b/Actions/AL-Go-Helper.ps1 index 2ed1ac8aa..604a247b2 100644 --- a/Actions/AL-Go-Helper.ps1 +++ b/Actions/AL-Go-Helper.ps1 @@ -10,13 +10,14 @@ if (Test-Path $gitHubHelperPath) { $ErrorActionPreference = "stop" Set-StrictMode -Version 2.0 -$ALGoFolder = Join-Path '.AL-Go' '' +$ALGoFolderName = '.AL-Go' $ALGoSettingsFile = Join-Path '.AL-Go' 'settings.json' $RepoSettingsFile = Join-Path '.github' 'AL-Go-Settings.json' $defaultCICDPushBranches = @( 'main', 'release/*', 'feature/*' ) $defaultCICDPullRequestBranches = @( 'main' ) $runningLocal = $local.IsPresent $defaultBcContainerHelperVersion = "" # Must be double quotes. Will be replaced by BcContainerHelperVersion if necessary in the deploy step +$microsoftTelemetryConnectionString = "InstrumentationKey=84bd9223-67d4-4378-8590-9e4a46023be2;IngestionEndpoint=https://westeurope-1.in.applicationinsights.azure.com/" $runAlPipelineOverrides = @( "DockerPull" @@ -52,8 +53,6 @@ $testLibrariesApps = @($systemApplicationTestLibraryAppId, $TestsTestLibrariesAp $testFrameworkApps = @($anyAppId, $libraryAssertAppId, $libraryVariableStorageAppId) + $testLibrariesApps $testRunnerApps = @($permissionsMockAppId, $testRunnerAppId) + $performanceToolkitApps + $testLibrariesApps + $testFrameworkApps -$microsoftTelemetryConnectionString = "InstrumentationKey=84bd9223-67d4-4378-8590-9e4a46023be2;IngestionEndpoint=https://westeurope-1.in.applicationinsights.azure.com/" - $isPsCore = $PSVersionTable.PSVersion -ge "6.0.0" if ($isPsCore) { $byteEncodingParam = @{ "asByteStream" = $true } @@ -217,7 +216,7 @@ function DownloadAndImportBcContainerHelper { if (Test-Path $repoSettingsPath) { $repoSettings = Get-Content $repoSettingsPath -Encoding UTF8 | ConvertFrom-Json | ConvertTo-HashTable if ($bcContainerHelperVersion -eq "") { - if ($repoSettings.ContainsKey("BcContainerHelperVersion")) { + if ($repoSettings.Keys -contains "BcContainerHelperVersion") { $bcContainerHelperVersion = $repoSettings.BcContainerHelperVersion if ($bcContainerHelperVersion -like "https://*") { throw "Setting BcContainerHelperVersion to a URL is not allowed." @@ -299,8 +298,9 @@ function MergeCustomObjectIntoOrderedDictionary { [PSCustomObject] $src ) - # Add missing properties in OrderedDictionary - + # Loop through all properties in the source object + # If the property does not exist in the destination object, add it with the right type, but no value + # Types supported: PSCustomObject, Object[] and simple types $src.PSObject.Properties.GetEnumerator() | ForEach-Object { $prop = $_.Name $srcProp = $src."$prop" @@ -318,6 +318,13 @@ function MergeCustomObjectIntoOrderedDictionary { } } + # Loop through all properties in the destination object + # If the property does not exist in the source object, do nothing + # 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 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) { @@ -329,10 +336,8 @@ function MergeCustomObjectIntoOrderedDictionary { MergeCustomObjectIntoOrderedDictionary -dst $dst."$prop" -src $srcProp } elseif ($dstPropType -ne $srcPropType -and !($srcPropType -eq "Int64" -and $dstPropType -eq "Int32")) { - # Under Linux, the Int fields read from the .json file will be Int64, while the settings defaults will be Int32 # This is not seen as an error and will not throw an error - throw "property $prop should be of type $dstPropType, is $srcPropType." } else { @@ -341,12 +346,16 @@ function MergeCustomObjectIntoOrderedDictionary { $srcElm = $_ $srcElmType = $srcElm.GetType().Name if ($srcElmType -eq "PSCustomObject") { + # Array of objects are not checked for uniqueness $ht = [ordered]@{} - $srcElm.PSObject.Properties | Sort-Object -Property Name -Culture "iv-iv" | ForEach-Object { $ht[$_.Name] = $_.Value } + $srcElm.PSObject.Properties | Sort-Object -Property Name -Culture "iv-iv" | ForEach-Object { + $ht[$_.Name] = $_.Value + } $dst."$prop" += @($ht) } else { - $dst."$prop" += $srcElm + # Add source element to destination array, but only if it does not already exist + $dst."$prop" = @($dst."$prop" + $srcElm | Select-Object -Unique) } } } @@ -358,17 +367,28 @@ function MergeCustomObjectIntoOrderedDictionary { } } +# Read settings from the settings files +# Settings are read from the following files: +# - .github/AL-Go-Settings.json +# - /.AL-Go/settings.json +# - .github/.settings.json +# - /.AL-Go/.settings.json +# - /.AL-Go/.settings.json function ReadSettings { Param( - [string] $baseFolder, - [string] $repoName = "$env:GITHUB_REPOSITORY", - [string] $workflowName = "", - [string] $userName = "" + [string] $baseFolder = "$ENV:GITHUB_WORKSPACE", + [string] $repoName = "$ENV:GITHUB_REPOSITORY", + [string] $project = '.', + [string] $workflowName = "$ENV:GITHUB_WORKFLOW", + [string] $userName = "$ENV:GITHUB_ACTOR", + [string] $branchName = "$ENV:GITHUB_REF_NAME" ) $repoName = $repoName.SubString("$repoName".LastIndexOf('/') + 1) - - # Read Settings file + $githubFolder = Join-Path $baseFolder ".github" + $workflowName = $workflowName.Trim().Split([System.IO.Path]::getInvalidFileNameChars()) -join "" + + # Start with default settings $settings = [ordered]@{ "type" = "PTE" "unusedALGoSystemFiles" = @() @@ -443,42 +463,39 @@ function ReadSettings { "environments" = @() "buildModes" = @() } - $gitHubFolder = ".github" - $repoSettingsPath = $RepoSettingsFile - if (-not (Test-Path (Join-Path $baseFolder $repoSettingsPath) -PathType Leaf)) { - $RepoSettingsPath = "..\$RepoSettingsPath" - $gitHubFolder = "..\$gitHubFolder" - if (-not (Test-Path (Join-Path $baseFolder $RepoSettingsPath) -PathType Leaf)) { - $RepoSettingsPath = "..\$RepoSettingsPath" - $gitHubFolder = "..\$gitHubFolder" + + # Read settings from files and merge them into the settings object + $settingsFiles = @((Join-Path $baseFolder $RepoSettingsFile)) + if ($project) { + $projectFolder = Join-Path $baseFolder $project -Resolve + $settingsFiles += @((Join-Path $projectFolder $ALGoSettingsFile)) + } + if ($workflowName) { + $settingsFiles += @((Join-Path $gitHubFolder "$workflowName.settings.json")) + if ($project) { + $settingsFiles += @((Join-Path $projectFolder "$ALGoFolderName/$workflowName.settings.json"), (Join-Path $projectFolder "$ALGoFolderName/$userName.settings.json")) } } - - $workflowName = $workflowName.Trim().Split([System.IO.Path]::getInvalidFileNameChars()) -join "" - $RepoSettingsPath, $ALGoSettingsFile, (Join-Path $gitHubFolder "$workflowName.settings.json"), (Join-Path $ALGoFolder "$workflowName.settings.json"), (Join-Path $ALGoFolder "$userName.settings.json") | ForEach-Object { + $settingsFiles | ForEach-Object { $settingsFile = $_ - $settingsPath = Join-Path $baseFolder $settingsFile - if (Test-Path $settingsPath) { + if (Test-Path $settingsFile) { try { Write-Host "Reading $settingsFile" - $settingsJson = Get-Content $settingsPath -Encoding UTF8 | ConvertFrom-Json - - # check settingsJson.version and do modifications if needed - + $settingsJson = Get-Content $settingsFile -Encoding UTF8 | ConvertFrom-Json MergeCustomObjectIntoOrderedDictionary -dst $settings -src $settingsJson if ($settingsJson.PSObject.Properties.Name -eq "ConditionalSettings") { $settingsJson.ConditionalSettings | ForEach-Object { $conditionalSetting = $_ - if ($conditionalSetting.branches | Where-Object { $ENV:GITHUB_REF_NAME -like $_ }) { - Write-Host "Applying conditional settings for $ENV:GITHUB_REF_NAME" + if ($conditionalSetting.branches | Where-Object { $branchName -like $_ }) { + Write-Host "Applying conditional settings for $branchName" MergeCustomObjectIntoOrderedDictionary -dst $settings -src $conditionalSetting.settings } } } } catch { - throw "Settings file $settingsFile, is wrongly formatted. Error is $($_.Exception.Message).`n$($_.ScriptStackTrace)" + throw "Error reading $settingsFile. Error was $($_.Exception.Message).`n$($_.ScriptStackTrace)" } } } @@ -936,9 +953,7 @@ function AnalyzeRepo { Write-Host "Identified dependency to project $depProject in the same repository" $dependencyIds = @( @($settings.appDependencies + $settings.testDependencies) | ForEach-Object { $_.id }) - $depProjectPath = Join-Path $baseFolder $depProject - $depSettings = ReadSettings -baseFolder $depProjectPath -workflowName "CI/CD" - + $depSettings = ReadSettings -baseFolder $baseFolder -project $depProject -workflowName "CI/CD" $depSettings = AnalyzeRepo -settings $depSettings -token $token -baseFolder $baseFolder -project $depProject -includeOnlyAppIds @($dependencyIds + $includeOnlyAppIds + $dependency.alwaysIncludeApps) -doNotIssueWarnings -doNotCheckArtifactSetting -server_url $server_url -repository $repository Set-Location $projectPath @@ -947,7 +962,7 @@ function AnalyzeRepo { Write-Host "Adding folders from $depProject to $_" $found = $true $depSettings."$propertyName" | ForEach-Object { - $folder = (Resolve-Path -Path (Join-Path $depProjectPath $_) -Relative).ToLowerInvariant() + $folder = (Resolve-Path -Path (Join-Path $baseFolder "$depProject/$_") -Relative).ToLowerInvariant() if (!$settings."$propertyName".Contains($folder)) { $settings."$propertyName" += @($folder) $found = $true @@ -997,14 +1012,13 @@ function Get-ProjectFolders { } $projectFolders = @() - $projectPath = Join-Path $baseFolder $project - $settings = ReadSettings -baseFolder $projectPath -workflowName "CI/CD" + $settings = ReadSettings -baseFolder $baseFolder -project $project -workflowName "CI/CD" $settings = AnalyzeRepo -settings $settings -token $token -baseFolder $baseFolder -project $project -includeOnlyAppIds $includeOnlyAppIds -doNotIssueWarnings -doNotCheckArtifactSetting -server_url $server_url -repository $repository $AlGoFolderArr = @() - if ($includeALGoFolder) { $AlGoFolderArr = @(".AL-Go") } + if ($includeALGoFolder) { $AlGoFolderArr = @($ALGoFolderName) } Set-Location $baseFolder @($settings.appFolders + $settings.testFolders + $settings.bcptTestFolders + $AlGoFolderArr) | ForEach-Object { - $fullPath = Join-Path $projectPath $_ -Resolve + $fullPath = Join-Path $baseFolder "$project/$_" -Resolve $relativePath = Resolve-Path -Path $fullPath -Relative $folder = $relativePath.Substring(2).Replace('\','/').ToLowerInvariant() if ($includeOnlyAppIds) { @@ -1272,6 +1286,7 @@ function CreateDevEnv { [string] $caller = 'local', [Parameter(Mandatory=$true)] [string] $baseFolder, + [string] $project, [string] $userName = $env:Username, [string] $bcContainerHelperPath = "", @@ -1299,7 +1314,8 @@ function CreateDevEnv { throw "Specified parameters doesn't match kind=$kind" } - $dependenciesFolder = Join-Path $baseFolder ".dependencies" + $projectFolder = Join-Path $baseFolder $project -Resolve + $dependenciesFolder = Join-Path $projectFolder ".dependencies" $runAlPipelineParams = @{} $loadBcContainerHelper = ($bcContainerHelperPath -eq "") if ($loadBcContainerHelper) { @@ -1316,7 +1332,7 @@ function CreateDevEnv { $workflowName = "$($kind)DevEnv" $params = @{ "baseFolder" = $baseFolder - "project" = "." + "project" = $project "workflowName" = $workflowName } if ($caller -eq "local") { $params += @{ "userName" = $userName } } @@ -1410,7 +1426,7 @@ function CreateDevEnv { throw "$_ is an illegal property in adminCenterApiCredentials setting" } } - if ($adminCenterApiCredentials.ContainsKey('ClientSecret')) { + if ($adminCenterApiCredentials.Keys -contains 'ClientSecret') { $adminCenterApiCredentials.ClientSecret = ConvertTo-SecureString -String $adminCenterApiCredentials.ClientSecret -AsPlainText -Force } } @@ -1419,7 +1435,7 @@ function CreateDevEnv { $params = @{ "settings" = $settings - "baseFolder" = $baseFolder + "baseFolder" = $projectFolder } if ($kind -eq "local") { $params += @{ @@ -1490,16 +1506,16 @@ function CreateDevEnv { } $allTestResults = "testresults*.xml" - $testResultsFile = Join-Path $baseFolder "TestResults.xml" - $testResultsFiles = Join-Path $baseFolder $allTestResults + $testResultsFile = Join-Path $projectFolder "TestResults.xml" + $testResultsFiles = Join-Path $projectFolder $allTestResults if (Test-Path $testResultsFiles) { Remove-Item $testResultsFiles -Force } - Set-Location $baseFolder + Set-Location $projectFolder $runAlPipelineOverrides | ForEach-Object { $scriptName = $_ - $scriptPath = Join-Path $ALGoFolder "$ScriptName.ps1" + $scriptPath = Join-Path $ALGoFolderName "$ScriptName.ps1" if (Test-Path -Path $scriptPath -Type Leaf) { Write-Host "Add override for $scriptName" $runAlPipelineParams += @{ @@ -1527,7 +1543,7 @@ function CreateDevEnv { } } elseif ($kind -eq "cloud") { - if ($runAlPipelineParams.ContainsKey('NewBcContainer')) { + if ($runAlPipelineParams.Keys -contains 'NewBcContainer') { throw "Overriding NewBcContainer is not allowed when running cloud DevEnv" } @@ -1599,7 +1615,7 @@ function CreateDevEnv { -pipelinename $workflowName ` -imageName "" ` -memoryLimit $repo.memoryLimit ` - -baseFolder $baseFolder ` + -baseFolder $projectFolder ` -licenseFile $licenseFileUrl ` -installApps $installApps ` -installTestApps $installTestApps ` @@ -1632,21 +1648,31 @@ function CreateDevEnv { } } +# This function will check and create the project folder if needed +# If project is not specified (or '.'), the root folder is used and the repository is single project +# If project is specified, check whether project folder exists and create it if it doesn't +# If no apps has been added to the repository, move the .AL-Go folder to the project folder (Convert to multi-project repository) function CheckAndCreateProjectFolder { Param( [string] $project ) - if (-not $project) { $project -eq "." } - if ($project -ne ".") { + if (-not $project) { $project = "." } + if ($project -eq ".") { + if (!(Test-Path $ALGoSettingsFile)) { + throw "Repository is setup as a multi-project repository, but no project has been specified." + } + } + else { + $createCodeWorkspace = $false if (Test-Path $ALGoSettingsFile) { - Write-Host "Reading $ALGoSettingsFile" - $settingsJson = Get-Content $ALGoSettingsFile -Encoding UTF8 | ConvertFrom-Json - if ($settingsJson.appFolders.Count -eq 0 -and $settingsJson.testFolders.Count -eq 0) { + $appCount = @(Get-ChildItem -Path '.' -Filter 'app.json' -Recurse -File).Count + if ($appCount -eq 0) { OutputWarning "Converting the repository to a multi-project repository as no other apps have been added previously." New-Item $project -ItemType Directory | Out-Null - Move-Item -path $ALGoFolder -Destination $project + Move-Item -path $ALGoFolderName -Destination $project Set-Location $project + $createCodeWorkspace = $true } else { throw "Repository is setup for a single project, cannot add a project. Move all appFolders, testFolders and the .AL-Go folder to a subdirectory in order to convert to a multi-project repository." @@ -1654,7 +1680,7 @@ function CheckAndCreateProjectFolder { } else { if (!(Test-Path $project)) { - New-Item -Path (Join-Path $project $ALGoFolder) -ItemType Directory | Out-Null + New-Item -Path (Join-Path $project $ALGoFolderName) -ItemType Directory | Out-Null Set-Location $project OutputWarning "Project folder doesn't exist, creating a new project folder and a default settings file with country us. Please modify if needed." [ordered]@{ @@ -1662,17 +1688,24 @@ function CheckAndCreateProjectFolder { "appFolders" = @() "testFolders" = @() } | Set-JsonContentLF -path $ALGoSettingsFile + $createCodeWorkspace = $true } else { Set-Location $project } } + if ($createCodeWorkspace) { + [ordered]@{ + "folders" = @( @{ "path" = ".AL-Go" } ) + "settings" = @{} + } | Set-JsonContentLF -path "$project.code-workspace" + } } } Function AnalyzeProjectDependencies { Param( - [string] $basePath, + [string] $baseFolder, [string[]] $projects, [ref] $buildOrder, [ref] $buildAlso, @@ -1688,10 +1721,10 @@ Function AnalyzeProjectDependencies { $project = $_ Write-Host "- $project" $apps = @() - $folders = @(Get-ChildItem -Path (Join-Path $basePath $project) -Recurse | Where-Object { $_.PSIsContainer -and (Test-Path (Join-Path $_.FullName 'app.json')) } | ForEach-Object { $_.FullName.Substring($basePath.Length+1) } ) + $folders = @(Get-ChildItem -Path (Join-Path $baseFolder $project) -Recurse | Where-Object { $_.PSIsContainer -and (Test-Path (Join-Path $_.FullName 'app.json')) } | ForEach-Object { $_.FullName.Substring($baseFolder.Length+1) } ) $unknownDependencies = @() $apps = @() - $sortedFolders = Sort-AppFoldersByDependencies -appFolders $folders -baseFolder $basePath -WarningAction SilentlyContinue -unknownDependencies ([ref]$unknownDependencies) -knownApps ([ref]$apps) + $sortedFolders = Sort-AppFoldersByDependencies -appFolders $folders -baseFolder $baseFolder -WarningAction SilentlyContinue -unknownDependencies ([ref]$unknownDependencies) -knownApps ([ref]$apps) $appDependencies."$project" = @{ "apps" = $apps "dependencies" = @($unknownDependencies | ForEach-Object { $_.Split(':')[0] }) @@ -1729,14 +1762,14 @@ Function AnalyzeProjectDependencies { # Add this project and all projects on which that project has a dependency to the list of dependencies for the current project $depProject | ForEach-Object { $_ - if ($projectDependencies.Value.ContainsKey($_)) { + if ($projectDependencies.Value.Keys -contains $_) { $projectDependencies.value."$_" } } } | Select-Object -Unique) # foundDependencies now contains all projects that the current project has a dependency on # Update ref variable projectDependencies for this project - if (!$projectDependencies.Value.ContainsKey($project)) { + if ($projectDependencies.Value.Keys -notcontains $project) { # Loop through the list of projects for which we already built a dependency list # Update the dependency list for that project if it contains the current project, which might lead to a changed dependency list # This is needed because we are looping through the projects in a any order @@ -1757,7 +1790,7 @@ Function AnalyzeProjectDependencies { Write-Host "Found dependencies to projects: $($foundDependencies -join ", ")" # Add project to buildAlso for this dependency to ensure that this project also gets build when the dependency is built $foundDependencies | ForEach-Object { - if ($buildAlso.value.ContainsKey($_)) { + if ($buildAlso.value.Keys -contains $_) { if ($buildAlso.value."$_" -notcontains $project) { $buildAlso.value."$_" += @( $project ) } @@ -1781,3 +1814,40 @@ Function AnalyzeProjectDependencies { $no++ } } + +function GetBaseFolder { + Param( + [string] $folder + ) + + if (!(Test-Path (Join-Path $folder '.github') -PathType Container)) { + $folder = (Get-Item -Path $folder).Parent.FullName + if (!(Test-Path (Join-Path $folder '.github') -PathType Container)) { + $folder = (Get-Item -Path $folder).Parent.FullName + if (!(Test-Path (Join-Path $folder '.github') -PathType Container)) { + throw "Cannot determine base folder from folder $folder." + } + } + } + $folder +} + +function GetProject { + Param( + [string] $baseFolder, + [string] $projectALGoFolder + ) + + $projectFolder = Join-Path $projectALGoFolder ".." -Resolve + if ($projectFolder -eq $baseFolder) { + $project = '.' + } + else { + Push-Location + Set-Location $baseFolder + $project = (Resolve-Path -Path $projectFolder -Relative).Substring(2) + Pop-Location + } + $project +} + diff --git a/Actions/AL-Go-TestRepoHelper.ps1 b/Actions/AL-Go-TestRepoHelper.ps1 index 88a59a152..f86fa90b2 100644 --- a/Actions/AL-Go-TestRepoHelper.ps1 +++ b/Actions/AL-Go-TestRepoHelper.ps1 @@ -8,7 +8,7 @@ function Test-Property { [switch] $shouldnot ) - $exists = $json.ContainsKey($key) + $exists = $json.Keys -contains $key if ($exists) { if ($maynot) { Write-Host "::Error::Property '$key' may not exist in $settingsFile" @@ -208,7 +208,7 @@ $chars = @{ 0..5 | ForEach-Object { $line = $_ $str.ToCharArray() | ForEach-Object { - if ($chars.ContainsKey($_)) { + if ($chars.Keys -contains $_) { $ch = $chars."$_" Write-Host -noNewline $ch[$line] } diff --git a/Actions/AddExistingApp/AddExistingApp.ps1 b/Actions/AddExistingApp/AddExistingApp.ps1 index c8e530fa8..ab360b46f 100644 --- a/Actions/AddExistingApp/AddExistingApp.ps1 +++ b/Actions/AddExistingApp/AddExistingApp.ps1 @@ -101,13 +101,7 @@ try { } CheckAndCreateProjectFolder -project $project - $baseFolder = (Get-Location).path - - Write-Host "Reading $ALGoSettingsFile" - $settingsJson = Get-Content $ALGoSettingsFile -Encoding UTF8 | ConvertFrom-Json - if ($settingsJson.PSObject.Properties.Name -eq "type") { - $type = $settingsJson.type - } + $projectFolder = (Get-Location).path $appNames = @() getfiles -url $url | ForEach-Object { @@ -170,14 +164,14 @@ try { } $orgfolderName = $appJson.name.Split([System.IO.Path]::getInvalidFileNameChars()) -join "" - $folderName = GetUniqueFolderName -baseFolder $baseFolder -folderName $orgfolderName + $folderName = GetUniqueFolderName -baseFolder $projectFolder -folderName $orgfolderName if ($folderName -ne $orgfolderName) { OutputWarning -message "$orgFolderName already exists as a folder in the repo, using $folderName instead" } - Move-Item -Path $appFolder -Destination $baseFolder -Force + Move-Item -Path $appFolder -Destination $projectFolder -Force Rename-Item -Path ([System.IO.Path]::GetFileName($appFolder)) -NewName $folderName - $appFolder = Join-Path $baseFolder $folderName + $appFolder = Join-Path $projectFolder $folderName Get-ChildItem $appFolder -Filter '*.*' -Recurse | ForEach-Object { if ($_.Name.Contains('%20')) { @@ -188,7 +182,7 @@ try { $appFolders | ForEach-Object { # Modify .AL-Go\settings.json try { - $settingsJsonFile = Join-Path $baseFolder $ALGoSettingsFile + $settingsJsonFile = Join-Path $projectFolder $ALGoSettingsFile $SettingsJson = Get-Content $settingsJsonFile -Encoding UTF8 | ConvertFrom-Json if (@($settingsJson.appFolders)+@($settingsJson.testFolders)) { if ($ttype -eq "Test App") { @@ -209,7 +203,7 @@ try { } # Modify workspace - Get-ChildItem -Path $baseFolder -Filter "*.code-workspace" | ForEach-Object { + Get-ChildItem -Path $projectFolder -Filter "*.code-workspace" | ForEach-Object { try { $workspaceFileName = $_.Name $workspaceFile = $_.FullName diff --git a/Actions/CheckForUpdates/CheckForUpdates.ps1 b/Actions/CheckForUpdates/CheckForUpdates.ps1 index bea67b467..54a2dc477 100644 --- a/Actions/CheckForUpdates/CheckForUpdates.ps1 +++ b/Actions/CheckForUpdates/CheckForUpdates.ps1 @@ -72,13 +72,13 @@ try { $repoSettings = @{} } $unusedALGoSystemFiles = @() - if ($repoSettings.ContainsKey("unusedALGoSystemFiles")) { + if ($repoSettings.Keys -contains "unusedALGoSystemFiles") { $unusedALGoSystemFiles = $repoSettings.unusedALGoSystemFiles } # if UpdateSettings is true, we need to update the settings file with the new template url (i.e. there are changes to your AL-Go System files) $updateSettings = $true - if ($repoSettings.ContainsKey("templateUrl")) { + if ($repoSettings.Keys -contains "templateUrl") { if ($templateUrl.StartsWith('@')) { $templateUrl = "$($repoSettings.templateUrl.Split('@')[0])$templateUrl" } @@ -123,7 +123,7 @@ try { @{ "dstPath" = ".github"; "srcPath" = ".github"; "pattern" = "*.copy.md"; "type" = "releasenotes" } ) # Get the list of projects in the current repository - if ($repoSettings.ContainsKey('projects')) { + if ($repoSettings.Keys -contains 'projects') { $projects = $repoSettings.projects } else { @@ -146,11 +146,11 @@ try { # Dependency depth determines how many build jobs we need to run sequentially # Every build job might spin up multiple jobs in parallel to build the projects without unresolved deependencies $depth = 1 - if ($repoSettings.ContainsKey('useProjectDependencies') -and $repoSettings.useProjectDependencies -and $projects.Count -gt 1) { + if ($repoSettings.Keys -contains 'useProjectDependencies' -and $repoSettings.useProjectDependencies -and $projects.Count -gt 1) { $buildAlso = @{} $buildOrder = @{} $projectDependencies = @{} - AnalyzeProjectDependencies -basePath $baseFolder -projects $projects -buildOrder ([ref]$buildOrder) -buildAlso ([ref]$buildAlso) -projectDependencies ([ref]$projectDependencies) + AnalyzeProjectDependencies -baseFolder $baseFolder -projects $projects -buildOrder ([ref]$buildOrder) -buildAlso ([ref]$buildAlso) -projectDependencies ([ref]$projectDependencies) $depth = $buildOrder.Count Write-Host "Calculated dependency depth to be $depth" } @@ -181,7 +181,7 @@ try { # Any workflow (except for the PullRequestHandler) can have a RepoSetting called Schedule, which will be used to set the schedule for the workflow if ($baseName -ne "PullRequestHandler") { - if ($repoSettings.ContainsKey($workflowScheduleKey)) { + if ($repoSettings.Keys -contains $workflowScheduleKey) { # Read the section under the on: key and add the schedule section $yamlOn = $yaml.Get('on:/') $yaml.Replace('on:/', $yamlOn.content+@('schedule:', " - cron: '$($repoSettings."$workflowScheduleKey")'")) @@ -191,10 +191,10 @@ try { # The CICD workflow can have a RepoSetting called CICDPushBranches, which will be used to set the branches for the workflow # Setting the CICDSchedule will disable the push trigger for the CI/CD workflow (unless CICDPushBranches is set) if ($baseName -eq "CICD") { - if ($repoSettings.ContainsKey('CICDPushBranches')) { + if ($repoSettings.Keys -contains 'CICDPushBranches') { $CICDPushBranches = $repoSettings.CICDPushBranches } - elseif ($repoSettings.ContainsKey($workflowScheduleKey)) { + elseif ($repoSettings.Keys -contains $workflowScheduleKey) { $CICDPushBranches = '' } else { @@ -211,7 +211,7 @@ try { # The PullRequestHandler workflow can have a RepoSetting called CICDPullRequestBranches, which will be used to set the branches for the workflow if ($baseName -eq "PullRequestHandler") { - if ($repoSettings.ContainsKey('CICDPullRequestBranches')) { + if ($repoSettings.Keys -contains 'CICDPullRequestBranches') { $CICDPullRequestBranches = $repoSettings.CICDPullRequestBranches } else { @@ -230,15 +230,15 @@ try { # - Update AL-Go System files is needed for changing runs-on - by having non-functioning runners, you might dead-lock yourself # - Pull Request Handler workflow for security reasons if ($baseName -ne "UpdateGitHubGoSystemFiles" -and $baseName -ne "PullRequestHandler") { - if ($repoSettings.ContainsKey("runs-on")) { + if ($repoSettings.Keys -contains "runs-on") { $runson = $repoSettings."runs-on" $yaml.ReplaceAll('runs-on: [ windows-latest ]', "runs-on: [ $runson ]") - if ($runson -like 'ubuntu-*' -and !$repoSettings.ContainsKey("shell")) { + if ($runson -like 'ubuntu-*' -and $repoSettings.Keys -notcontains "shell") { # Default shell for Ubuntu (Linux) is pwsh $repoSettings.shell = "pwsh" } } - if ($repoSettings.ContainsKey("shell")) { + if ($repoSettings.Keys -contains "shell") { $yaml.ReplaceAll('shell: powershell', "shell: $($repoSettings."shell")") } } diff --git a/Actions/CreateApp/AppHelper.psm1 b/Actions/CreateApp/AppHelper.psm1 index 2d91ce290..bf40e5135 100644 --- a/Actions/CreateApp/AppHelper.psm1 +++ b/Actions/CreateApp/AppHelper.psm1 @@ -184,11 +184,11 @@ function New-SamplePerformanceTestApp function Update-WorkSpaces ( - [string] $baseFolder, + [string] $projectFolder, [string] $appName ) { - Get-ChildItem -Path $baseFolder -Filter "*.code-workspace" | + Get-ChildItem -Path $projectFolder -Filter "*.code-workspace" | ForEach-Object { try { $workspaceFileName = $_.Name diff --git a/Actions/CreateApp/CreateApp.ps1 b/Actions/CreateApp/CreateApp.ps1 index 293a60281..eafce5170 100644 --- a/Actions/CreateApp/CreateApp.ps1 +++ b/Actions/CreateApp/CreateApp.ps1 @@ -36,8 +36,8 @@ try { . (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve) $branch = "$(if (!$directCommit) { [System.IO.Path]::GetRandomFileName() })" $serverUrl = CloneIntoNewFolder -actor $actor -token $token -branch $branch - $repoBaseFolder = (Get-Location).Path - $BcContainerHelperPath = DownloadAndImportBcContainerHelper -baseFolder $repoBaseFolder + $baseFolder = (Get-Location).Path + $BcContainerHelperPath = DownloadAndImportBcContainerHelper -baseFolder $baseFolder import-module (Join-Path -path $PSScriptRoot -ChildPath "..\TelemetryHelper.psm1" -Resolve) $telemetryScope = CreateScope -eventId 'DO0072' -parentTelemetryScopeJson $parentTelemetryScopeJson @@ -57,12 +57,12 @@ try { $ids = Confirm-IdRanges -templateType $type -idrange $idrange CheckAndCreateProjectFolder -project $project - $baseFolder = (Get-Location).Path + $projectFolder = (Get-Location).Path if ($type -eq "Performance Test App") { try { - $settings = ReadSettings -baseFolder $baseFolder -repoName $env:GITHUB_REPOSITORY -workflowName $env:GITHUB_WORKFLOW - $settings = AnalyzeRepo -settings $settings -token $token -baseFolder $repoBaseFolder -project $project -doNotIssueWarnings + $settings = ReadSettings -baseFolder $baseFolder -project $project + $settings = AnalyzeRepo -settings $settings -token $token -baseFolder $baseFolder -project $project -doNotIssueWarnings $folders = Download-Artifacts -artifactUrl $settings.artifact -includePlatform $sampleApp = Join-Path $folders[0] "Applications.*\Microsoft_Performance Toolkit Samples_*.app" if (Test-Path $sampleApp) { @@ -82,14 +82,14 @@ try { } $orgfolderName = $name.Split([System.IO.Path]::getInvalidFileNameChars()) -join "" - $folderName = GetUniqueFolderName -baseFolder $baseFolder -folderName $orgfolderName + $folderName = GetUniqueFolderName -baseFolder $projectFolder -folderName $orgfolderName if ($folderName -ne $orgfolderName) { OutputWarning -message "Folder $orgFolderName already exists in the repo, folder name $folderName will be used instead." } # Modify .AL-Go\settings.json try { - $settingsJsonFile = Join-Path $baseFolder $ALGoSettingsFile + $settingsJsonFile = Join-Path $projectFolder $ALGoSettingsFile $SettingsJson = Get-Content $settingsJsonFile -Encoding UTF8 | ConvertFrom-Json if (@($settingsJson.appFolders)+@($settingsJson.testFolders)) { if ($type -eq "Performance Test App") { @@ -120,18 +120,18 @@ try { } if ($type -eq "Performance Test App") { - New-SamplePerformanceTestApp -destinationPath (Join-Path $baseFolder $folderName) -name $name -publisher $publisher -version $appVersion -sampleCode $sampleCode -sampleSuite $sampleSuite -idrange $ids -appSourceFolder $tmpFolder + New-SamplePerformanceTestApp -destinationPath (Join-Path $projectFolder $folderName) -name $name -publisher $publisher -version $appVersion -sampleCode $sampleCode -sampleSuite $sampleSuite -idrange $ids -appSourceFolder $tmpFolder } elseif ($type -eq "Test App") { - New-SampleTestApp -destinationPath (Join-Path $baseFolder $folderName) -name $name -publisher $publisher -version $appVersion -sampleCode $sampleCode -idrange $ids + New-SampleTestApp -destinationPath (Join-Path $projectFolder $folderName) -name $name -publisher $publisher -version $appVersion -sampleCode $sampleCode -idrange $ids } else { - New-SampleApp -destinationPath (Join-Path $baseFolder $folderName) -name $name -publisher $publisher -version $appVersion -sampleCode $sampleCode -idrange $ids + New-SampleApp -destinationPath (Join-Path $projectFolder $folderName) -name $name -publisher $publisher -version $appVersion -sampleCode $sampleCode -idrange $ids } - Update-WorkSpaces -baseFolder $baseFolder -appName $folderName + Update-WorkSpaces -projectFolder $projectFolder -appName $folderName - Set-Location $repoBaseFolder + Set-Location $baseFolder CommitFromNewFolder -serverUrl $serverUrl -commitMessage "New $type ($Name)" -branch $branch TrackTrace -telemetryScope $telemetryScope diff --git a/Actions/Deliver/Deliver.ps1 b/Actions/Deliver/Deliver.ps1 index b902776d8..7efed74f7 100644 --- a/Actions/Deliver/Deliver.ps1 +++ b/Actions/Deliver/Deliver.ps1 @@ -50,8 +50,9 @@ function EnsureAzStorageModule() { # IMPORTANT: No code that can fail should be outside the try/catch try { + $baseFolder = $ENV:GITHUB_WORKSPACE . (Join-Path -Path $PSScriptRoot -ChildPath "../AL-Go-Helper.ps1" -Resolve) - $BcContainerHelperPath = DownloadAndImportBcContainerHelper -baseFolder $ENV:GITHUB_WORKSPACE + $BcContainerHelperPath = DownloadAndImportBcContainerHelper -baseFolder $baseFolder import-module (Join-Path -path $PSScriptRoot -ChildPath "../TelemetryHelper.psm1" -Resolve) $telemetryScope = CreateScope -eventId 'DO0081' -parentTelemetryScopeJson $parentTelemetryScopeJson @@ -65,13 +66,13 @@ try { $artifacts = $artifacts.Replace('/',([System.IO.Path]::DirectorySeparatorChar)).Replace('\',([System.IO.Path]::DirectorySeparatorChar)) - $settings = ReadSettings -baseFolder $ENV:GITHUB_WORKSPACE -workflowName $env:GITHUB_WORKFLOW + $settings = ReadSettings -baseFolder $baseFolder if ($settings.projects) { $projectList = $settings.projects | Where-Object { $_ -like $projects } } else { - $projectList = @(Get-ChildItem -Path $ENV:GITHUB_WORKSPACE -Recurse -Depth 2 | Where-Object { $_.PSIsContainer -and (Test-Path (Join-Path $_.FullName ".AL-Go/settings.json") -PathType Leaf) } | ForEach-Object { $_.FullName.Substring("$ENV:GITHUB_WORKSPACE".length+1) }) - if (Test-Path (Join-Path $ENV:GITHUB_WORKSPACE ".AL-Go") -PathType Container) { + $projectList = @(Get-ChildItem -Path $baseFolder -Recurse -Depth 2 | Where-Object { $_.PSIsContainer -and (Test-Path (Join-Path $_.FullName ".AL-Go/settings.json") -PathType Leaf) } | ForEach-Object { $_.FullName.Substring($baseFolder.length+1) }) + if (Test-Path (Join-Path $baseFolder ".AL-Go") -PathType Container) { $projectList += @(".") } } @@ -107,14 +108,14 @@ try { # projectName is the project name stripped for special characters $projectName = $project -replace "[^a-z0-9]", "-" Write-Host "Project '$project'" - $baseFolder = Join-Path $ENV:GITHUB_WORKSPACE ".artifacts" - $baseFolderCreated = $false + $artifactsFolder = Join-Path $baseFolder ".artifacts" + $artifactsFolderCreated = $false if ($artifacts -eq '.artifacts') { # Base folder is set } - elseif ($artifacts -like "$($ENV:GITHUB_WORKSPACE)*") { - $baseFolder = $artifacts + elseif ($artifacts -like "$($baseFolder)*") { + $artifactsFolder = $artifacts } elseif ($artifacts -eq "current" -or $artifacts -eq "prerelease" -or $artifacts -eq "draft") { # latest released version @@ -131,9 +132,9 @@ try { if (!($release)) { throw "Unable to locate $artifacts release" } - New-Item $baseFolder -ItemType Directory | Out-Null - $baseFolderCreated = $true - $artifactFile = DownloadRelease -token $token -projects $project -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -release $release -path $baseFolder -mask "Apps" + New-Item $artifactsFolder -ItemType Directory | Out-Null + $artifactsFolderCreated = $true + $artifactFile = DownloadRelease -token $token -projects $project -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -release $release -path $artifactsFolder -mask "Apps" Write-Host "'$artifactFile'" if (!$artifactFile -or !(Test-Path $artifactFile)) { throw "Artifact $artifacts was not found on any release. Make sure that the artifact files exist and files are not corrupted." @@ -145,14 +146,14 @@ try { Remove-Item $artifactFile -Force } else { - New-Item $baseFolder -ItemType Directory | Out-Null - $baseFolderCreated = $true + New-Item $artifactsFolder -ItemType Directory | Out-Null + $artifactsFolderCreated = $true $atypes.Split(',') | ForEach-Object { $atype = $_ $allArtifacts = GetArtifacts -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -mask $atype -projects $project -Version $artifacts -branch $refname if ($allArtifacts) { $allArtifacts | ForEach-Object { - $artifactFile = DownloadArtifact -token $token -artifact $_ -path $baseFolder + $artifactFile = DownloadArtifact -token $token -artifact $_ -path $artifactsFolder Write-Host $artifactFile if (!(Test-Path $artifactFile)) { throw "Unable to download artifact $($_.name)" @@ -176,17 +177,18 @@ try { } Write-Host "Artifacts:" - Get-ChildItem -Path $baseFolder | ForEach-Object { + Get-ChildItem -Path $artifactsFolder | ForEach-Object { Write-Host "- $($_.Name)" } # Check if there is a custom script to run for the delivery target - $customScript = Join-Path $ENV:GITHUB_WORKSPACE ".github/DeliverTo$deliveryTarget.ps1" + $customScript = Join-Path $baseFolder ".github/DeliverTo$deliveryTarget.ps1" if (Test-Path $customScript -PathType Leaf) { Write-Host "Found custom script $customScript for delivery target $deliveryTarget" - $projectSettings = AnalyzeRepo -settings $settings -baseFolder $ENV:GITHUB_WORKSPACE -project $thisProject -doNotCheckArtifactSetting -doNotIssueWarnings + $projectSettings = ReadSettings -baseFolder $baseFolder -project $thisProject + $projectSettings = AnalyzeRepo -settings $projectSettings -baseFolder $baseFolder -project $thisProject -doNotCheckArtifactSetting -doNotIssueWarnings $parameters = @{ "Project" = $thisProject "ProjectName" = $projectName @@ -203,7 +205,7 @@ try { $singleArtifactFilter = "$project-$refname-$artifactType-*.*.*.*"; # Get the folder holding the artifacts from the standard build - $artifactFolder = @(Get-ChildItem -Path (Join-Path $baseFolder $singleArtifactFilter) -Directory) + $artifactFolder = @(Get-ChildItem -Path (Join-Path $artifactsFolder $singleArtifactFilter) -Directory) # Verify that there is an apps folder if ($artifactFolder.Count -eq 0 -and $artifactType -eq "Apps") { @@ -223,7 +225,7 @@ try { # Get the folders holding the artifacts from all build modes $multipleArtifactFilter = "$project-$refname-*$artifactType-*.*.*.*"; - $artifactFolders = @(Get-ChildItem -Path (Join-Path $baseFolder $multipleArtifactFilter) -Directory) + $artifactFolders = @(Get-ChildItem -Path (Join-Path $artifactsFolder $multipleArtifactFilter) -Directory) if ($artifactFolders.Count -gt 0) { $parameters[$artifactType.ToLowerInvariant() + "Folders"] = $artifactFolders.FullName } @@ -235,7 +237,7 @@ try { elseif ($deliveryTarget -eq "GitHubPackages") { $githubPackagesCredential = $githubPackagesContext | ConvertFrom-Json 'Apps' | ForEach-Object { - $folder = @(Get-ChildItem -Path (Join-Path $baseFolder "$project-$refname-$($_)-*.*.*.*") | Where-Object { $_.PSIsContainer }) + $folder = @(Get-ChildItem -Path (Join-Path $artifactsFolder "$project-$refname-$($_)-*.*.*.*") | Where-Object { $_.PSIsContainer }) if ($folder.Count -gt 1) { $folder | Out-Host throw "Internal error - multiple $_ folders located" @@ -265,7 +267,7 @@ try { catch { throw "NuGetContext secret is malformed. Needs to be formatted as Json, containing serverUrl and token as a minimum." } - $appsfolder = @(Get-ChildItem -Path (Join-Path $baseFolder "$project-$refname-Apps-*.*.*.*") | Where-Object { $_.PSIsContainer }) + $appsfolder = @(Get-ChildItem -Path (Join-Path $artifactsFolder "$project-$refname-Apps-*.*.*.*") | Where-Object { $_.PSIsContainer }) if ($appsFolder.Count -eq 0) { throw "Internal error - unable to locate apps folder" } @@ -273,12 +275,12 @@ try { $appsFolder | Out-Host throw "Internal error - multiple apps folders located" } - $testAppsFolder = @(Get-ChildItem -Path (Join-Path $baseFolder "$project-$refname-TestApps-*.*.*.*") | Where-Object { $_.PSIsContainer }) + $testAppsFolder = @(Get-ChildItem -Path (Join-Path $artifactsFolder "$project-$refname-TestApps-*.*.*.*") | Where-Object { $_.PSIsContainer }) if ($testAppsFolder.Count -gt 1) { $testAppsFolder | Out-Host throw "Internal error - multiple testApps folders located" } - $dependenciesFolder = @(Get-ChildItem -Path (Join-Path $baseFolder "$project-$refname-Dependencies-*.*.*.*") | Where-Object { $_.PSIsContainer }) + $dependenciesFolder = @(Get-ChildItem -Path (Join-Path $artifactsFolder "$project-$refname-Dependencies-*.*.*.*") | Where-Object { $_.PSIsContainer }) if ($dependenciesFolder.Count -gt 1) { $dependenciesFolder | Out-Host throw "Internal error - multiple dependencies folders located" @@ -294,7 +296,7 @@ try { if ($dependenciesFolder.Count -gt 0) { $parameters.dependencyAppFiles = @(Get-Item -Path (Join-Path $dependenciesFolder[0] "*.app") | ForEach-Object { $_.FullName }) } - if ($nuGetAccount.ContainsKey('PackageName')) { + if ($nuGetAccount.Keys -contains 'PackageName') { $parameters.packageId = $nuGetAccount.PackageName.replace('{project}',$projectName).replace('{owner}',$ENV:GITHUB_REPOSITORY_OWNER).replace('{repo}',$env:repoName) } else { @@ -309,19 +311,19 @@ try { $parameters.packageId += "-preview" } $parameters.packageVersion = [System.Version]$appsFolder[0].Name.SubString($appsFolder[0].Name.IndexOf("-Apps-")+6) - if ($nuGetAccount.ContainsKey('PackageTitle')) { + if ($nuGetAccount.Keys -contains 'PackageTitle') { $parameters.packageTitle = $nuGetAccount.PackageTitle } else { $parameters.packageTitle = $parameters.packageId } - if ($nuGetAccount.ContainsKey('PackageDescription')) { + if ($nuGetAccount.Keys -contains 'PackageDescription') { $parameters.packageDescription = $nuGetAccount.PackageDescription } else { $parameters.packageDescription = $parameters.packageTitle } - if ($nuGetAccount.ContainsKey('PackageAuthors')) { + if ($nuGetAccount.Keys -contains 'PackageAuthors') { $parameters.packageAuthors = $nuGetAccount.PackageAuthors } else { @@ -335,7 +337,7 @@ try { try { $storageAccount = $storageContext | ConvertFrom-Json | ConvertTo-HashTable Write-Host "Json OK" - if ($storageAccount.ContainsKey('sastoken')) { + if ($storageAccount.Keys -contains 'sastoken') { $azStorageContext = New-AzStorageContext -StorageAccountName $storageAccount.StorageAccountName -SasToken $storageAccount.sastoken } else { @@ -356,7 +358,7 @@ try { $atypes.Split(',') | ForEach-Object { $atype = $_ Write-Host "Looking for: $project-$refname-$atype-*.*.*.*" - $artfolder = @(Get-ChildItem -Path (Join-Path $baseFolder "$project-$refname-$atype-*.*.*.*") | Where-Object { $_.PSIsContainer }) + $artfolder = @(Get-ChildItem -Path (Join-Path $artifactsFolder "$project-$refname-$atype-*.*.*.*") | Where-Object { $_.PSIsContainer }) if ($artFolder.Count -eq 0) { if ($atype -eq "Apps") { throw "Error - unable to locate apps" @@ -395,15 +397,16 @@ try { } } elseif ($deliveryTarget -eq "AppSource") { - $projectSettings = AnalyzeRepo -settings $settings -baseFolder $ENV:GITHUB_WORKSPACE -project $thisProject -doNotCheckArtifactSetting -doNotIssueWarnings + $projectSettings = ReadSettings -baseFolder $baseFolder -project $thisProject + $projectSettings = AnalyzeRepo -settings $projectSettings -baseFolder $baseFolder -project $thisProject -doNotCheckArtifactSetting -doNotIssueWarnings # if type is Release, we only get here with the projects that needs to be delivered to AppSource # if type is CD, we get here for all projects, but should only deliver to AppSource if AppSourceContinuousDelivery is set to true - if ($type -eq 'Release' -or ($projectSettings.ContainsKey('AppSourceContinuousDelivery') -and $projectSettings.AppSourceContinuousDelivery)) { + if ($type -eq 'Release' -or ($projectSettings.Keys -contains 'AppSourceContinuousDelivery' -and $projectSettings.AppSourceContinuousDelivery)) { EnsureAzStorageModule $appSourceContextHt = $appSourceContext | ConvertFrom-Json | ConvertTo-HashTable $authContext = New-BcAuthContext @appSourceContextHt - if ($projectSettings.ContainsKey("AppSourceMainAppFolder")) { + if ($projectSettings.Keys -contains "AppSourceMainAppFolder") { $AppSourceMainAppFolder = $projectSettings.AppSourceMainAppFolder } else { @@ -414,15 +417,15 @@ try { throw "Unable to determine main App folder" } } - if (-not $projectSettings.ContainsKey('AppSourceProductId')) { + if ($projectSettings.Keys -notcontains 'AppSourceProductId') { throw "AppSourceProductId needs to be specified in $thisProject/.AL-Go/settings.json in order to deliver to AppSource" } Write-Host "AppSource MainAppFolder $AppSourceMainAppFolder" - $mainAppJson = Get-Content -Path (Join-Path $ENV:GITHUB_WORKSPACE "$thisProject/$AppSourceMainAppFolder/app.json") | ConvertFrom-Json + $mainAppJson = Get-Content -Path (Join-Path $baseFolder "$thisProject/$AppSourceMainAppFolder/app.json") | ConvertFrom-Json $mainAppVersion = [Version]$mainAppJson.Version $mainAppFileName = ("$($mainAppJson.Publisher)_$($mainAppJson.Name)_".Split([System.IO.Path]::GetInvalidFileNameChars()) -join '') + "*.*.*.*.app" - $artfolder = @(Get-ChildItem -Path (Join-Path $baseFolder "$project-$refname-Apps-*.*.*.*") | Where-Object { $_.PSIsContainer }) + $artfolder = @(Get-ChildItem -Path (Join-Path $artifactsFolder "$project-$refname-Apps-*.*.*.*") | Where-Object { $_.PSIsContainer }) if ($artFolder.Count -eq 0) { throw "Internal error - unable to locate apps" } @@ -453,8 +456,8 @@ try { throw "Internal error, no handler for $deliveryTarget" } - if ($baseFolderCreated) { - Remove-Item $baseFolder -Recurse -Force + if ($artifactsFolderCreated) { + Remove-Item $artifactsFolder -Recurse -Force } } diff --git a/Actions/Deploy/Deploy.ps1 b/Actions/Deploy/Deploy.ps1 index 6c9407017..47c3d2c51 100644 --- a/Actions/Deploy/Deploy.ps1 +++ b/Actions/Deploy/Deploy.ps1 @@ -40,10 +40,10 @@ try { $artifacts = $artifacts.Replace('/',([System.IO.Path]::DirectorySeparatorChar)).Replace('\',([System.IO.Path]::DirectorySeparatorChar)) $apps = @() - $baseFolder = Join-Path $ENV:GITHUB_WORKSPACE ".artifacts" - $baseFolderCreated = $false + $artifactsFolder = Join-Path $ENV:GITHUB_WORKSPACE ".artifacts" + $artifactsFolderCreated = $false if ($artifacts -eq ".artifacts") { - $artifacts = $baseFolder + $artifacts = $artifactsFolder } if ($artifacts -like "$($ENV:GITHUB_WORKSPACE)*") { @@ -81,23 +81,23 @@ try { if (!($release)) { throw "Unable to locate $artifacts release" } - New-Item $baseFolder -ItemType Directory | Out-Null - $baseFolderCreated = $true - DownloadRelease -token $token -projects $projects -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -release $release -path $baseFolder -mask "Apps" - DownloadRelease -token $token -projects $projects -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -release $release -path $baseFolder -mask "Dependencies" - $apps = @((Get-ChildItem -Path $baseFolder) | ForEach-Object { $_.FullName }) + New-Item $artifactsFolder -ItemType Directory | Out-Null + $artifactsFolderCreated = $true + DownloadRelease -token $token -projects $projects -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -release $release -path $artifactsFolder -mask "Apps" + DownloadRelease -token $token -projects $projects -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -release $release -path $artifactsFolder -mask "Dependencies" + $apps = @((Get-ChildItem -Path $artifactsFolder) | ForEach-Object { $_.FullName }) if (!$apps) { throw "Artifact $artifacts was not found on any release. Make sure that the artifact files exist and files are not corrupted." } } else { - New-Item $baseFolder -ItemType Directory | Out-Null + New-Item $artifactsFolder -ItemType Directory | Out-Null $baseFolderCreated = $true $allArtifacts = @(GetArtifacts -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -mask "Apps" -projects $projects -Version $artifacts -branch "main") $allArtifacts += @(GetArtifacts -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -mask "Dependencies" -projects $projects -Version $artifacts -branch "main") if ($allArtifacts) { $allArtifacts | ForEach-Object { - $appFile = DownloadArtifact -token $token -artifact $_ -path $baseFolder + $appFile = DownloadArtifact -token $token -artifact $_ -path $artifactsFolder if (!(Test-Path $appFile)) { throw "Unable to download artifact $($_.name)" } @@ -164,8 +164,8 @@ try { exit } - if ($baseFolderCreated) { - Remove-Item $baseFolder -Recurse -Force + if ($artifactsFolderCreated) { + Remove-Item $artifactsFolder -Recurse -Force } TrackTrace -telemetryScope $telemetryScope diff --git a/Actions/ReadSecrets/ReadSecrets.ps1 b/Actions/ReadSecrets/ReadSecrets.ps1 index ce388aa21..ea589b117 100644 --- a/Actions/ReadSecrets/ReadSecrets.ps1 +++ b/Actions/ReadSecrets/ReadSecrets.ps1 @@ -42,7 +42,7 @@ try { $keyVaultName = $settings.keyVaultName if ([string]::IsNullOrEmpty($keyVaultName) -and (IsKeyVaultSet)) { $credentialsJson = Get-KeyVaultCredentials -dontmask | ConvertTo-HashTable - if ($credentialsJson.ContainsKey("keyVaultName")) { + if ($credentialsJson.Keys -contains "keyVaultName") { $keyVaultName = $credentialsJson.keyVaultName } } @@ -50,7 +50,7 @@ try { $secrets.Split(',') | ForEach-Object { $secret = $_ $secretNameProperty = "$($secret)SecretName" - if ($settings.containsKey($secretNameProperty)) { + if ($settings.Keys -contains $secretNameProperty) { $secret = "$($secret)=$($settings."$secretNameProperty")" } $secretsCollection += $secret @@ -90,7 +90,7 @@ try { } } - if ($outSettings.ContainsKey('appDependencyProbingPaths')) { + if ($outSettings.Keys -contains 'appDependencyProbingPaths') { $outSettings.appDependencyProbingPaths | ForEach-Object { if ($_.PsObject.Properties.name -eq "AuthTokenSecret") { $_.authTokenSecret = GetSecret -secret $_.authTokenSecret -keyVaultName $keyVaultName diff --git a/Actions/ReadSettings/ReadSettings.ps1 b/Actions/ReadSettings/ReadSettings.ps1 index 8c6cb86b8..cccc7bf61 100644 --- a/Actions/ReadSettings/ReadSettings.ps1 +++ b/Actions/ReadSettings/ReadSettings.ps1 @@ -27,17 +27,14 @@ $bcContainerHelperPath = $null # IMPORTANT: No code that can fail should be outside the try/catch try { + $baseFolder = $ENV:GITHUB_WORKSPACE . (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve) - $BcContainerHelperPath = DownloadAndImportBcContainerHelper -baseFolder $ENV:GITHUB_WORKSPACE + $BcContainerHelperPath = DownloadAndImportBcContainerHelper -baseFolder $baseFolder import-module (Join-Path -path $PSScriptRoot -ChildPath "..\TelemetryHelper.psm1" -Resolve) $telemetryScope = CreateScope -eventId 'DO0079' -parentTelemetryScopeJson $parentTelemetryScopeJson - if ($project -eq ".") { $project = "" } - - $baseFolder = Join-Path $ENV:GITHUB_WORKSPACE $project - - $settings = ReadSettings -baseFolder $baseFolder -workflowName $env:GITHUB_WORKFLOW + $settings = ReadSettings -baseFolder $baseFolder -project $project if ($get) { $getSettings = $get.Split(',').Trim() } @@ -127,14 +124,14 @@ try { $projects = $settings.projects } else { - $projects = @(Get-ChildItem -Path $ENV:GITHUB_WORKSPACE -Recurse -Depth 2 | Where-Object { $_.PSIsContainer -and (Test-Path (Join-Path $_.FullName ".AL-Go/settings.json") -PathType Leaf) } | ForEach-Object { $_.FullName.Substring("$ENV:GITHUB_WORKSPACE".length+1) }) + $projects = @(Get-ChildItem -Path $baseFolder -Recurse -Depth 2 | Where-Object { $_.PSIsContainer -and (Test-Path (Join-Path $_.FullName ".AL-Go/settings.json") -PathType Leaf) } | ForEach-Object { $_.FullName.Substring($baseFolder.length+1) }) } if ($projects) { AddTelemetryProperty -telemetryScope $telemetryScope -key "projects" -value "$($projects -join ', ')" Write-Host "All Projects: $($projects -join ', ')" - if (!$settings.alwaysBuildAllProjects -and ($ENV:GITHUB_EVENT_NAME -eq "pull_request" -or $ENV:GITHUB_EVENT_NAME -eq "push" -or ($ENV:GITHUB_EVENT_NAME -eq "workflow_run" -and (Test-Path (Join-Path $ENV:GITHUB_WORKSPACE '.PullRequestFilesChanged'))))) { - if ($ENV:GITHUB_EVENT_NAME -eq "workflow_run" -and (Test-Path (Join-Path $ENV:GITHUB_WORKSPACE '.PullRequestFilesChanged'))) { - $filesChanged = @(Get-Content (Join-Path $ENV:GITHUB_WORKSPACE '.PullRequestFilesChanged') -Encoding UTF8) + if (!$settings.alwaysBuildAllProjects -and ($ENV:GITHUB_EVENT_NAME -eq "pull_request" -or $ENV:GITHUB_EVENT_NAME -eq "push" -or ($ENV:GITHUB_EVENT_NAME -eq "workflow_run" -and (Test-Path (Join-Path $baseFolder '.PullRequestFilesChanged'))))) { + if ($ENV:GITHUB_EVENT_NAME -eq "workflow_run" -and (Test-Path (Join-Path $baseFolder '.PullRequestFilesChanged'))) { + $filesChanged = @(Get-Content (Join-Path $baseFolder '.PullRequestFilesChanged') -Encoding UTF8) } else { $headers = @{ @@ -172,10 +169,10 @@ try { Write-Host "Modified files:" $filesChanged | Out-Host $buildProjects = @($projects | Where-Object { - $project = $_ + $checkProject = $_ $buildProject = $false - if (Test-Path -path (Join-Path $ENV:GITHUB_WORKSPACE "$project/.AL-Go/settings.json")) { - $projectFolders = Get-ProjectFolders -baseFolder $ENV:GITHUB_WORKSPACE -project $project -token $token -includeAlGoFolder -includeApps -includeTestApps + if (Test-Path -path (Join-Path $baseFolder "$checkProject/.AL-Go/settings.json")) { + $projectFolders = Get-ProjectFolders -baseFolder $baseFolder -project $checkProject -token $token -includeAlGoFolder -includeApps -includeTestApps $projectFolders | ForEach-Object { if ($filesChanged -like "$_/*") { $buildProject = $true } } @@ -192,8 +189,8 @@ try { $buildAlso = @{} $buildOrder = @{} $projectDependencies = @{} - AnalyzeProjectDependencies -basePath $ENV:GITHUB_WORKSPACE -projects $projects -buildOrder ([ref]$buildOrder) -buildAlso ([ref]$buildAlso) -projectDependencies ([ref]$projectDependencies) - $buildProjects = @($buildProjects | ForEach-Object { $_; if ($buildAlso.ContainsKey("$_")) { $buildAlso."$_" } } | Select-Object -Unique) + AnalyzeProjectDependencies -baseFolder $baseFolder -projects $projects -buildOrder ([ref]$buildOrder) -buildAlso ([ref]$buildAlso) -projectDependencies ([ref]$projectDependencies) + $buildProjects = @($buildProjects | ForEach-Object { $_; if ($buildAlso.Keys -contains $_) { $buildAlso."$_" } } | Select-Object -Unique) Write-Host "Building projects: $($buildProjects -join ', ')" $projectDependenciesJson = $projectDependencies | ConvertTo-Json -Compress $buildOrderJson = $buildOrder | ConvertTo-Json -Compress diff --git a/Actions/RunPipeline/RunPipeline.ps1 b/Actions/RunPipeline/RunPipeline.ps1 index dffc22e50..a3557a8d9 100644 --- a/Actions/RunPipeline/RunPipeline.ps1 +++ b/Actions/RunPipeline/RunPipeline.ps1 @@ -70,7 +70,7 @@ try { $appBuild = $settings.appBuild $appRevision = $settings.appRevision 'licenseFileUrl','insiderSasToken','codeSignCertificateUrl','codeSignCertificatePassword','keyVaultCertificateUrl','keyVaultCertificatePassword','keyVaultClientId','storageContext','gitHubPackagesContext','applicationInsightsConnectionString' | ForEach-Object { - if ($secrets.ContainsKey($_)) { + if ($secrets.Keys -contains $_) { $value = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($secrets."$_")) } else { @@ -99,7 +99,7 @@ try { Write-Host "Project: $project" if ($project -and $repo.useProjectDependencies -and $projectDependenciesJson -ne "") { $projectDependencies = $projectDependenciesJson | ConvertFrom-Json | ConvertTo-HashTable - if ($projectDependencies.ContainsKey($project)) { + if ($projectDependencies.Keys -contains $project) { $projects = @($projectDependencies."$project") -join "," } else { @@ -248,7 +248,7 @@ try { Set-Location $projectPath $runAlPipelineOverrides | ForEach-Object { $scriptName = $_ - $scriptPath = Join-Path $ALGoFolder "$ScriptName.ps1" + $scriptPath = Join-Path $ALGoFolderName "$ScriptName.ps1" if (Test-Path -Path $scriptPath -Type Leaf) { Write-Host "Add override for $scriptName" $runAlPipelineParams += @{ @@ -257,7 +257,7 @@ try { } } - if (-not $runAlPipelineParams.ContainsKey('RemoveBcContainer')) { + if ($runAlPipelineParams.Keys -notcontains 'RemoveBcContainer') { $runAlPipelineParams += @{ "RemoveBcContainer" = { Param([Hashtable]$parameters) @@ -267,7 +267,7 @@ try { } } - if (-not $runAlPipelineParams.ContainsKey('ImportTestDataInBcContainer')) { + if ($runAlPipelineParams.Keys -notcontains 'ImportTestDataInBcContainer') { if (($repo.configPackages) -or ($repo.Keys | Where-Object { $_ -like 'configPackages.*' })) { Write-Host "Adding Import Test Data override" Write-Host "Configured config packages:" @@ -282,7 +282,7 @@ try { Param([Hashtable]$parameters) $country = Get-BcContainerCountry -containerOrImageName $parameters.containerName $prop = "configPackages.$country" - if (-not $repo.ContainsKey($prop)) { + if ($repo.Keys -notcontains $prop) { $prop = "configPackages" } if ($repo."$prop") { @@ -303,7 +303,7 @@ try { } } - if ($gitHubPackagesContext -and (-not $runAlPipelineParams.ContainsKey('InstallMissingDependencies'))) { + if ($gitHubPackagesContext -and ($runAlPipelineParams.Keys -notcontains 'InstallMissingDependencies')) { $gitHubPackagesCredential = $gitHubPackagesContext | ConvertFrom-Json $runAlPipelineParams += @{ "InstallMissingDependencies" = { @@ -317,7 +317,7 @@ try { $appName = $_.Split(':')[1] $version = $appName.SubString($appName.LastIndexOf('_')+1) $version = [System.Version]$version.SubString(0,$version.Length-4) - if ($parameters.ContainsKey('CopyInstalledAppsToFolder')) { + if ($parameters.Keys -contains 'CopyInstalledAppsToFolder') { $publishParams += @{ "CopyInstalledAppsToFolder" = $parameters.CopyInstalledAppsToFolder } @@ -352,7 +352,7 @@ try { throw "No cleanModePreprocessorSymbols defined in settings.json for this project. Please add the preprocessor symbols to use when building in clean mode or disable CLEAN mode." } - if (!$runAlPipelineParams.ContainsKey('preprocessorsymbols')) { + if ($runAlPipelineParams.Keys -notcontains 'preprocessorsymbols') { $runAlPipelineParams["preprocessorsymbols"] = @() } @@ -360,7 +360,7 @@ try { $runAlPipelineParams["preprocessorsymbols"] += $preprocessorsymbols } 'Translated' { - if (!$runAlPipelineParams.ContainsKey('features')) { + if ($runAlPipelineParams.Keys -notcontains 'features') { $runAlPipelineParams["features"] = @() } $runAlPipelineParams["features"] += "translationfile" diff --git a/Actions/WorkflowInitialize/WorkflowInitialize.ps1 b/Actions/WorkflowInitialize/WorkflowInitialize.ps1 index 1d4c63198..c8c28b815 100644 --- a/Actions/WorkflowInitialize/WorkflowInitialize.ps1 +++ b/Actions/WorkflowInitialize/WorkflowInitialize.ps1 @@ -38,11 +38,11 @@ try { if ($telemetryScope) { $repoSettings = Get-Content -Path (Join-Path $ENV:GITHUB_WORKSPACE '.github/AL-Go-Settings.json') -Raw -Encoding UTF8 | ConvertFrom-Json | ConvertTo-HashTable $type = 'PTE' - if ($repoSettings.ContainsKey('type')) { + if ($repoSettings.Keys -contains 'type') { $type = $repoSettings.type } $templateUrl = 'Not set' - if ($repoSettings.ContainsKey('templateUrl')) { + if ($repoSettings.Keys -contains 'templateUrl') { $templateUrl = $repoSettings.templateUrl } if ($verstr -eq "d") { diff --git a/README.md b/README.md index 3635e968a..467442341 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,11 @@ AL-Go for GitHub is a set of GitHub templates and actions, which can be used to The goal is that people who have created their GitHub repositories based on the AL-Go templates, can maintain these repositories and stay current just by running a workflow, which updates their repositories. This includes necessary changes to scripts and workflows to cope with new features and functions in Business Central. +The roadmap for AL-Go for GitHub can be found here: [https://aka.ms/ALGoRoadmap](https://aka.ms/ALGoRoadmap) + The template repositories to use as starting point are: -- [https://github.com/microsoft/AL-Go-PTE](https://github.com/microsoft/AL-Go-PTE) is the GitHub repository template for Per Tenant Extenstions. For creating a Per Tenant Extensions, this is your starting point. -- [https://github.com/microsoft/AL-Go-AppSource](https://github.com/microsoft/AL-Go-AppSource) is the GitHub repository template for AppSource apps. For creating an AppSource App, this is your starting point. +- [https://github.com/microsoft/AL-Go-PTE](https://github.com/microsoft/AL-Go-PTE) or [https://aka.ms/algopte](https://aka.ms/algopte) is the GitHub repository template for Per Tenant Extenstions. When creating a Per Tenant Extensions, this is your starting point. +- [https://github.com/microsoft/AL-Go-AppSource](https://github.com/microsoft/AL-Go-AppSource) or [https://aka.ms/algoappsource](https://aka.ms/algoappsource) is the GitHub repository template for AppSource apps. When creating an AppSource App, this is your starting point. The below usage scenarios takes you through how to get started and how to perform the most common tasks. diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 085f1d803..7d9011c75 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,14 @@ Note that when using the preview version of AL-Go for GitHub, you need to Update your AL-Go system files, as soon as possible when told to do so. +### Issues +- Issue #171 create a workspace file when creating a project +- Issue #356 Publish to AppSource fails in multi project repo +- Issue #358 Publish To Environment Action stopped working in v2.3 + +### Refactoring and tests +ReadSettings has been refactored to allow organization wide settings to be added as well. CI Tests have been added to cover ReadSettings. + ## v2.3 ### Issues diff --git a/Templates/AppSource App/.AL-Go/cloudDevEnv.ps1 b/Templates/AppSource App/.AL-Go/cloudDevEnv.ps1 index 17ac02c43..84b0cb0de 100644 --- a/Templates/AppSource App/.AL-Go/cloudDevEnv.ps1 +++ b/Templates/AppSource App/.AL-Go/cloudDevEnv.ps1 @@ -25,8 +25,9 @@ $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actio Import-Module $GitHubHelperPath . $ALGoHelperPath -local - -$baseFolder = Join-Path $PSScriptRoot ".." -Resolve + +$baseFolder = GetBaseFolder -folder $PSScriptRoot +$project = GetProject -folder $baseFolder -ALGoFolder $PSScriptRoot Clear-Host Write-Host @@ -51,7 +52,7 @@ if (Test-Path (Join-Path $PSScriptRoot "NewBcContainer.ps1")) { Write-Host -ForegroundColor Red "WARNING: The project has a NewBcContainer override defined. Typically, this means that you cannot run a cloud development environment" } -$settings = ReadSettings -baseFolder $baseFolder -userName $env:USERNAME +$settings = ReadSettings -baseFolder $baseFolder -project $project -userName $env:USERNAME Write-Host @@ -62,7 +63,7 @@ if (-not $environmentName) { -default "$($env:USERNAME)-sandbox" } -if (-not $PSBoundParameters.ContainsKey('reuseExistingEnvironment')) { +if ($PSBoundParameters.Keys -notcontains 'reuseExistingEnvironment') { $reuseExistingEnvironment = (Select-Value ` -title "What if the environment already exists?" ` -options @{ "Yes" = "Reuse existing environment"; "No" = "Recreate environment" } ` @@ -75,7 +76,8 @@ CreateDevEnv ` -caller local ` -environmentName $environmentName ` -reuseExistingEnvironment:$reuseExistingEnvironment ` - -baseFolder $baseFolder + -baseFolder $baseFolder ` + -project $project } catch { Write-Host -ForegroundColor Red "Error: $($_.Exception.Message)`nStacktrace: $($_.scriptStackTrace)" diff --git a/Templates/AppSource App/.AL-Go/localDevEnv.ps1 b/Templates/AppSource App/.AL-Go/localDevEnv.ps1 index fd41bcd43..0e9c17ba7 100644 --- a/Templates/AppSource App/.AL-Go/localDevEnv.ps1 +++ b/Templates/AppSource App/.AL-Go/localDevEnv.ps1 @@ -29,7 +29,8 @@ $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actio Import-Module $GitHubHelperPath . $ALGoHelperPath -local -$baseFolder = Join-Path $PSScriptRoot ".." -Resolve +$baseFolder = GetBaseFolder -folder $PSScriptRoot +$project = GetProject -folder $baseFolder -ALGoFolder $PSScriptRoot Clear-Host Write-Host @@ -54,7 +55,7 @@ The script will also modify launch.json to have a Local Sandbox configuration po '@ -$settings = ReadSettings -baseFolder $baseFolder -userName $env:USERNAME +$settings = ReadSettings -baseFolder $baseFolder -project $project -userName $env:USERNAME Write-Host "Checking System Requirements" $dockerProcess = (Get-Process "dockerd" -ErrorAction Ignore) @@ -125,6 +126,7 @@ CreateDevEnv ` -caller local ` -containerName $containerName ` -baseFolder $baseFolder ` + -project $project -auth $auth ` -credential $credential ` -licenseFileUrl $licenseFileUrl ` diff --git a/Templates/AppSource App/.github/workflows/CICD.yaml b/Templates/AppSource App/.github/workflows/CICD.yaml index 3c2ba50c4..f88b5e3aa 100644 --- a/Templates/AppSource App/.github/workflows/CICD.yaml +++ b/Templates/AppSource App/.github/workflows/CICD.yaml @@ -686,7 +686,7 @@ jobs: Write-Host "::Error::No AuthContext provided" exit 1 } - if ($deployToSetting.PSObject.Properties.name -eq "EnvironmentName") { + if (("$deployToSetting" -ne "") -and $deployToSetting.PSObject.Properties.name -eq "EnvironmentName") { $environmentName = $deployToSetting.EnvironmentName } else { @@ -706,7 +706,7 @@ jobs: } $environmentName = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($environmentName + '${{ matrix.environment }}'.SubString($envName.Length)).ToUpperInvariant())) - if ($deployToSetting.PSObject.Properties.name -eq "projects") { + if (("$deployToSetting" -ne "") -and $deployToSetting.PSObject.Properties.name -eq "projects") { $projects = $deployToSetting.projects } else { diff --git a/Templates/AppSource App/.github/workflows/PublishToEnvironment.yaml b/Templates/AppSource App/.github/workflows/PublishToEnvironment.yaml index 949b08cdf..04c65a64b 100644 --- a/Templates/AppSource App/.github/workflows/PublishToEnvironment.yaml +++ b/Templates/AppSource App/.github/workflows/PublishToEnvironment.yaml @@ -107,7 +107,7 @@ jobs: Write-Host "::Error::No AuthContext provided" exit 1 } - if ($deployToSetting.PSObject.Properties.name -eq "EnvironmentName") { + if (("$deployToSetting" -ne "") -and $deployToSetting.PSObject.Properties.name -eq "EnvironmentName") { $environmentName = $deployToSetting.EnvironmentName } else { @@ -126,7 +126,7 @@ jobs: $environmentName = '${{ steps.envName.outputs.envName }}' } $environmentName = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($environmentName + '${{ matrix.environment }}'.SubString($envName.Length)).ToUpperInvariant())) - if ($deployToSetting.PSObject.Properties.name -eq "projects") { + if (("$deployToSetting" -ne "") -and $deployToSetting.PSObject.Properties.name -eq "projects") { $projects = $deployToSetting.projects } else { diff --git a/Templates/Per Tenant Extension/.AL-Go/cloudDevEnv.ps1 b/Templates/Per Tenant Extension/.AL-Go/cloudDevEnv.ps1 index 17ac02c43..ccdbf1560 100644 --- a/Templates/Per Tenant Extension/.AL-Go/cloudDevEnv.ps1 +++ b/Templates/Per Tenant Extension/.AL-Go/cloudDevEnv.ps1 @@ -26,7 +26,8 @@ $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actio Import-Module $GitHubHelperPath . $ALGoHelperPath -local -$baseFolder = Join-Path $PSScriptRoot ".." -Resolve +$baseFolder = GetBaseFolder -folder $PSScriptRoot +$project = GetProject -folder $baseFolder -ALGoFolder $PSScriptRoot Clear-Host Write-Host @@ -51,7 +52,7 @@ if (Test-Path (Join-Path $PSScriptRoot "NewBcContainer.ps1")) { Write-Host -ForegroundColor Red "WARNING: The project has a NewBcContainer override defined. Typically, this means that you cannot run a cloud development environment" } -$settings = ReadSettings -baseFolder $baseFolder -userName $env:USERNAME +$settings = ReadSettings -baseFolder $baseFolder -project $project -userName $env:USERNAME Write-Host @@ -62,7 +63,7 @@ if (-not $environmentName) { -default "$($env:USERNAME)-sandbox" } -if (-not $PSBoundParameters.ContainsKey('reuseExistingEnvironment')) { +if ($PSBoundParameters.Keys -notcontains 'reuseExistingEnvironment') { $reuseExistingEnvironment = (Select-Value ` -title "What if the environment already exists?" ` -options @{ "Yes" = "Reuse existing environment"; "No" = "Recreate environment" } ` @@ -75,7 +76,8 @@ CreateDevEnv ` -caller local ` -environmentName $environmentName ` -reuseExistingEnvironment:$reuseExistingEnvironment ` - -baseFolder $baseFolder + -baseFolder $baseFolder ` + -project $project } catch { Write-Host -ForegroundColor Red "Error: $($_.Exception.Message)`nStacktrace: $($_.scriptStackTrace)" diff --git a/Templates/Per Tenant Extension/.AL-Go/localDevEnv.ps1 b/Templates/Per Tenant Extension/.AL-Go/localDevEnv.ps1 index fd41bcd43..b9e1bc941 100644 --- a/Templates/Per Tenant Extension/.AL-Go/localDevEnv.ps1 +++ b/Templates/Per Tenant Extension/.AL-Go/localDevEnv.ps1 @@ -29,7 +29,8 @@ $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actio Import-Module $GitHubHelperPath . $ALGoHelperPath -local -$baseFolder = Join-Path $PSScriptRoot ".." -Resolve +$baseFolder = GetBaseFolder -folder $PSScriptRoot +$project = GetProject -folder $baseFolder -ALGoFolder $PSScriptRoot Clear-Host Write-Host @@ -54,7 +55,7 @@ The script will also modify launch.json to have a Local Sandbox configuration po '@ -$settings = ReadSettings -baseFolder $baseFolder -userName $env:USERNAME +$settings = ReadSettings -baseFolder $baseFolder -project $project -userName $env:USERNAME Write-Host "Checking System Requirements" $dockerProcess = (Get-Process "dockerd" -ErrorAction Ignore) @@ -125,6 +126,7 @@ CreateDevEnv ` -caller local ` -containerName $containerName ` -baseFolder $baseFolder ` + -project $project ` -auth $auth ` -credential $credential ` -licenseFileUrl $licenseFileUrl ` diff --git a/Templates/Per Tenant Extension/.github/workflows/CICD.yaml b/Templates/Per Tenant Extension/.github/workflows/CICD.yaml index 3c2ba50c4..f88b5e3aa 100644 --- a/Templates/Per Tenant Extension/.github/workflows/CICD.yaml +++ b/Templates/Per Tenant Extension/.github/workflows/CICD.yaml @@ -686,7 +686,7 @@ jobs: Write-Host "::Error::No AuthContext provided" exit 1 } - if ($deployToSetting.PSObject.Properties.name -eq "EnvironmentName") { + if (("$deployToSetting" -ne "") -and $deployToSetting.PSObject.Properties.name -eq "EnvironmentName") { $environmentName = $deployToSetting.EnvironmentName } else { @@ -706,7 +706,7 @@ jobs: } $environmentName = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($environmentName + '${{ matrix.environment }}'.SubString($envName.Length)).ToUpperInvariant())) - if ($deployToSetting.PSObject.Properties.name -eq "projects") { + if (("$deployToSetting" -ne "") -and $deployToSetting.PSObject.Properties.name -eq "projects") { $projects = $deployToSetting.projects } else { diff --git a/Templates/Per Tenant Extension/.github/workflows/PublishToEnvironment.yaml b/Templates/Per Tenant Extension/.github/workflows/PublishToEnvironment.yaml index 1e2904cfd..6f061f1e8 100644 --- a/Templates/Per Tenant Extension/.github/workflows/PublishToEnvironment.yaml +++ b/Templates/Per Tenant Extension/.github/workflows/PublishToEnvironment.yaml @@ -107,7 +107,7 @@ jobs: Write-Host "::Error::No AuthContext provided" exit 1 } - if ($deployToSetting.PSObject.Properties.name -eq "EnvironmentName") { + if (("$deployToSetting" -ne "") -and $deployToSetting.PSObject.Properties.name -eq "EnvironmentName") { $environmentName = $deployToSetting.EnvironmentName } else { @@ -126,7 +126,7 @@ jobs: $environmentName = '${{ steps.envName.outputs.envName }}' } $environmentName = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($environmentName + '${{ matrix.environment }}'.SubString($envName.Length)).ToUpperInvariant())) - if ($deployToSetting.PSObject.Properties.name -eq "projects") { + if (("$deployToSetting" -ne "") -and $deployToSetting.PSObject.Properties.name -eq "projects") { $projects = $deployToSetting.projects } else { diff --git a/Tests/AL-Go-Helper.Test.ps1 b/Tests/AL-Go-Helper.Test.ps1 new file mode 100644 index 000000000..c482b06c0 --- /dev/null +++ b/Tests/AL-Go-Helper.Test.ps1 @@ -0,0 +1,206 @@ +Describe "RunPipeline Action Tests" { + BeforeAll { + . (Join-Path $PSScriptRoot '../Actions/AL-Go-Helper.ps1') + } + + It 'MergeCustomObjectIntoOrderedDictionary' { + # This function is used to merge settings files into the settings object + # dest is the default settings object + $dest = [ordered]@{ + 'int0' = 0 + 'int1' = 1 + 'int2' = 2 + 'str1' = 'str1' + 'str2' = 'str2' + 'arr1' = @('a', 'b', 'c') + 'arr2' = @('a', 'b', 'c') + 'obj1' = [ordered]@{ + 'a' = 'a' + 'b' = 'b' + 'c' = 'c' + } + 'obj2' = [ordered]@{ + 'a' = 'a' + 'b' = 'b' + 'c' = 'c' + } + 'objarr1' = @([ordered]@{'a' = 'a'; 'b' = 'b'; 'c' = 'c'}, [ordered]@{'d' = 'd'; 'e' = 'e'; 'f' = 'f'}) + 'objarr2' = @([ordered]@{'a' = 'a'; 'b' = 'b'; 'c' = 'c'}, [ordered]@{'d' = 'd'; 'e' = 'e'; 'f' = 'f'}) + } + + $dest.Count | Should -Be 11 + + # source is the settings read from a file + $src = @{ + 'int1' = [Int64]::MaxValue + 'int2' = 3 + 'int3' = 4 + 'objarr2' = @([ordered]@{'g' = 'g'; 'h' = 'h'; 'i' = 'i'}, [ordered]@{'d' = 'd'; 'e' = 'e'; 'f' = 'f'}) + 'objarr3' = @([ordered]@{'g' = 'g'; 'h' = 'h'; 'i' = 'i'}, [ordered]@{'j' = 'j'; 'k' = 'k'; 'l' = 'l'}) + } | ConvertTo-Json | ConvertFrom-Json + $src.int2 = [Int32]3 + $src.int3 = [Int32]4 + + # Merge the settings + MergeCustomObjectIntoOrderedDictionary -dst $dest -src $src + $dest.Count | Should -Be 13 + $dest['int0'] | Should -Be 0 + $dest['int1'] | Should -Be ([Int64]::MaxValue) + $dest['int2'] | Should -Be 3 + $dest['int3'] | Should -Be 4 + $dest['objarr2'] | ConvertTo-Json | Should -Be (@([ordered]@{'a' = 'a'; 'b' = 'b'; 'c' = 'c'}, [ordered]@{'d' = 'd'; 'e' = 'e'; 'f' = 'f'}, [ordered]@{'g' = 'g'; 'h' = 'h'; 'i' = 'i'}, [ordered]@{'d' = 'd'; 'e' = 'e'; 'f' = 'f'}) | ConvertTo-Json) + $dest['objarr3'] | ConvertTo-Json | Should -Be (@([ordered]@{'g' = 'g'; 'h' = 'h'; 'i' = 'i'}, [ordered]@{'j' = 'j'; 'k' = 'k'; 'l' = 'l'}) | ConvertTo-Json) + + # source is the settings read from a file + # Check that multiple settings files are merged correctly one after the other + $src = @{ + 'str2' = 'str3' + 'str3' = 'str4' + 'arr2' = @('c', 'd', 'e') + 'arr3' = @('c', 'd', 'e') + 'obj2' = [ordered]@{'c' = 'c'; 'd' = 'd'; 'e' = 'e'} + 'obj3' = [ordered]@{'d' = 'd'; 'e' = 'e'; 'f' = 'f'} + } | ConvertTo-Json | ConvertFrom-Json + + # Check that applying the same settings twice doesn't change the result + 1..2 | ForEach-Object { + MergeCustomObjectIntoOrderedDictionary -dst $dest -src $src + $dest.Count | Should -Be 16 + $dest['int0'] | Should -Be 0 + $dest['int1'] | Should -Be ([Int64]::MaxValue) + $dest['int2'] | Should -Be 3 + $dest['int3'] | Should -Be 4 + $dest['str2'] | Should -Be 'str3' + $dest['str3'] | Should -Be 'str4' + $dest['arr2'] | Should -Be @('a', 'b', 'c', 'd', 'e') + $dest['arr3'] | Should -Be @('c', 'd', 'e') + $dest['obj2'] | ConvertTo-Json | Should -Be ([ordered]@{'a' = 'a'; 'b' = 'b'; 'c' = 'c'; 'd' = 'd'; 'e' = 'e'} | ConvertTo-Json) + $dest['obj3'] | ConvertTo-Json | Should -Be ([ordered]@{'d' = 'd'; 'e' = 'e'; 'f' = 'f'} | ConvertTo-Json) + } + } + + It 'ReadSettings' { + Mock Write-Host { } + Mock Out-Host { } + + Push-Location + $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString()) + $githubFolder = Join-Path $tempName ".github" + $ALGoFolder = Join-Path $tempName $ALGoFolderName + $projectALGoFolder = Join-Path $tempName "Project/$ALGoFolderName" + + New-Item $githubFolder -ItemType Directory | Out-Null + New-Item $ALGoFolder -ItemType Directory | Out-Null + New-Item $projectALGoFolder -ItemType Directory | Out-Null + + @{ "property1" = "repo1"; "property2" = "repo2"; "property3" = "repo3" } | ConvertTo-Json -Depth 99 | + Set-Content -Path (Join-Path $githubFolder "AL-Go-Settings.json") -encoding utf8 -Force + @{ "property1" = "single1"; "property4" = "single4" } | ConvertTo-Json -Depth 99 | + Set-Content -Path (Join-Path $ALGoFolder "settings.json") -encoding utf8 -Force + @{ "property1" = "multi1"; "property5" = "multi5" } | ConvertTo-Json -Depth 99 | + Set-Content -Path (Join-Path $projectALGoFolder "settings.json") -encoding utf8 -Force + @{ "property2" = "workflow2"; "conditionalSettings" = @{ "branches" = @( 'dev' ); "settings" = @{ "property1" = "branch1"; "property4" = "branch4" } } } | ConvertTo-Json -Depth 99 | + Set-Content -Path (Join-Path $githubFolder "Workflow.settings.json") -encoding utf8 -Force + @{ "property1" = "user1"; "property6" = "user6" } | ConvertTo-Json -Depth 99 | + Set-Content -Path (Join-Path $projectALGoFolder "user.settings.json") -encoding utf8 -Force + + $repoSettings = ReadSettings -baseFolder $tempName -project '' -repoName 'repo' -workflowName '' -branchName '' -userName '' + $repoSettings.property1 | Should -Be 'repo1' + $repoSettings.property2 | Should -Be 'repo2' + $repoSettings.property3 | Should -Be 'repo3' + + $singleProjectSettings = ReadSettings -baseFolder $tempName -project '.' -repoName 'repo' -workflowName '' -branchName '' -userName '' + $singleProjectSettings.property1 | Should -Be 'single1' + $singleProjectSettings.property2 | Should -Be 'repo2' + $singleProjectSettings.property4 | Should -Be 'single4' + + $multiProjectSettings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName '' -branchName '' -userName '' + $multiProjectSettings.property1 | Should -Be 'multi1' + $multiProjectSettings.property2 | Should -Be 'repo2' + $multiProjectSettings.property5 | Should -Be 'multi5' + + $workflowRepoSettings = ReadSettings -baseFolder $tempName -project '' -repoName 'repo' -workflowName 'Workflow' -branchName '' -userName '' + $workflowRepoSettings.property1 | Should -Be 'repo1' + $workflowRepoSettings.property2 | Should -Be 'workflow2' + + $workflowSingleSettings = ReadSettings -baseFolder $tempName -project '.' -repoName 'repo' -workflowName 'Workflow' -branchName '' -userName '' + $workflowSingleSettings.property1 | Should -Be 'single1' + $workflowSingleSettings.property2 | Should -Be 'workflow2' + $workflowSingleSettings.property4 | Should -Be 'single4' + $workflowSingleSettings.property3 | Should -Be 'repo3' + + $workflowMultiSettings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName 'Workflow' -branchName 'dev' -userName '' + $workflowMultiSettings.property1 | Should -Be 'branch1' + $workflowMultiSettings.property2 | Should -Be 'workflow2' + $workflowMultiSettings.property3 | Should -Be 'repo3' + $workflowMultiSettings.property4 | Should -Be 'branch4' + $workflowMultiSettings.property5 | Should -Be 'multi5' + { $workflowMultiSettings.property6 } | Should -Throw + + $userWorkflowMultiSettings = ReadSettings -baseFolder $tempName -project 'Project' -repoName 'repo' -workflowName 'Workflow' -branchName 'dev' -userName 'user' + $userWorkflowMultiSettings.property1 | Should -Be 'user1' + $userWorkflowMultiSettings.property2 | Should -Be 'workflow2' + $userWorkflowMultiSettings.property3 | Should -Be 'repo3' + $userWorkflowMultiSettings.property4 | Should -Be 'branch4' + $userWorkflowMultiSettings.property5 | Should -Be 'multi5' + $userWorkflowMultiSettings.property6 | Should -Be 'user6' + + # Clean up + Pop-Location + Remove-Item -Path $tempName -Recurse -Force + } + + It 'CheckAndCreateProjectFolder' { + Mock Write-Host { } + + Push-Location + + # Create a temp folder with the PTE template files + $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString()) + New-Item $tempName -ItemType Directory | Out-Null + $repoName = "Per Tenant Extension" + $pteTemplateFiles = Join-Path $PSScriptRoot "../Templates/$repoName" -Resolve + Copy-Item -Path $pteTemplateFiles -Destination $tempName -Recurse -Force + $repoFolder = Join-Path $tempName $repoName + $repoFolder | Should -Exist + Join-Path $repoFolder '.AL-Go/settings.json' | Should -Exist + Set-Location $repoFolder + + # Test without project name - should not change the repo + CheckAndCreateProjectFolder -project '.' + Join-Path $repoFolder '.AL-Go/settings.json' | Should -Exist + CheckAndCreateProjectFolder -project '' + Join-Path $repoFolder '.AL-Go/settings.json' | Should -Exist + + # Create an app in an empty repo + New-Item -Path 'App' -ItemType Directory | Out-Null + Set-Content -Path 'App/app.json' -Value '{"id": "123"}' + + # Creating a project in a single project repo with apps should fail + { CheckAndCreateProjectFolder -project 'project1' } | Should -Throw + + # Remove app folder and try again + Remove-Item -Path 'App' -Recurse -Force + + # Creating a project in a single project repo without apps should succeed + { CheckAndCreateProjectFolder -project 'project1' } | Should -Not -Throw + + # .AL-Go folder should be moved to the project folder + Join-Path $repoFolder '.AL-Go/settings.json' | Should -Not -Exist + Join-Path '.' '.AL-Go/settings.json' | Should -Exist + 'project1.code-workspace' | Should -Exist + + # If repo is setup for multiple projects, using an empty project name should fail + Set-Location $repoFolder + { CheckAndCreateProjectFolder -project '' } | Should -Throw + + # Creating a second project should not fail + { CheckAndCreateProjectFolder -project 'project2' } | Should -Not -Throw + Join-Path $repoFolder 'project2/.AL-Go/settings.json' | Should -Exist + Join-Path $repoFolder 'project2/project2.code-workspace' | Should -Exist + + # Clean up + Pop-Location + Remove-Item -Path $tempName -Recurse -Force + } +}