diff --git a/Actions/.Modules/ReadSettings.psm1 b/Actions/.Modules/ReadSettings.psm1 index c0a3bb0406..bfd3964b12 100644 --- a/Actions/.Modules/ReadSettings.psm1 +++ b/Actions/.Modules/ReadSettings.psm1 @@ -300,7 +300,8 @@ function ReadSettings { [string] $repoSettingsVariableValue = "$ENV:ALGoRepoSettings", [string] $environmentSettingsVariableValue = "$ENV:ALGoEnvSettings", [string] $environmentName = "$ENV:ALGoEnvName", - [string] $customSettings = "" + [string] $customSettings = "", + [string] $githubEnvironmentsJson = "" ) # If the build is triggered by a pull request the refname will be the merge branch. To apply conditional settings we need to use the base branch @@ -549,6 +550,74 @@ function ReadSettings { $settings.projectName = $project # Default to project path as project name } + # Environments can come from three places: GitHub repo environments, settings.environments and the environmentName input of the PublishToEnvironment action + if ($githubEnvironmentsJson) { + $ghEnvironments = $githubEnvironmentsJson | ConvertFrom-Json + } else { + $ghEnvironments = @() + } + + # $ghEnvironments = @(GetGitHubEnvironments) + $environments = @($ghEnvironments | ForEach-Object { $_.name }) + @($settings.environments) + @($environmentName) | Select-Object -unique | Where-Object { $_ -ne "" } + + if ($environments) { + OutputDebug "$($environments.Count) Environment(s) defined: $($environments -join ', ')" + foreach ($environmentName in $environments) { + $envName = $environmentName.Split(' ')[0] + # Default deployment settings + $deploymentSettings = @{ + "EnvironmentType" = "SaaS" + "EnvironmentName" = $envName + "Branches" = @() + "Projects" = @('*') + "DependencyInstallMode" = "install" # ignore, install, upgrade or forceUpgrade + "includeTestAppsInSandboxEnvironment" = $false + "excludeAppIds" = @() + "Scope" = $null + "SyncMode" = $null + "buildMode" = $null + "continuousDeployment" = $null + "runs-on" = $settings."runs-on" + "shell" = $settings."shell" + "companyId" = '' + "ppEnvironmentUrl" = '' + } + $settingsName = "DeployTo$envName" + if ($settings.Contains($settingsName)) { + # If a DeployTo setting exists - use values from this (over the defaults) + Write-Host "Using custom settings for environment $environmentName" + $deployTo = $settings."$settingsName" + $keys = @($deployTo.Keys) + foreach($key in $keys) { + if ($deploymentSettings.Contains($key)) { + if ($null -ne $deploymentSettings."$key" -and $null -ne $deployTo."$key" -and $deploymentSettings."$key".GetType().Name -ne $deployTo."$key".GetType().Name) { + if ($key -eq "runs-on" -and $deployTo."$key" -is [Object[]]) { + # Support setting runs-on as an array in settings to not break old settings + # See https://github.com/microsoft/AL-Go/issues/1182 + $deployTo."$key" = $deployTo."$key" -join ',' + } + else { + OutputWarning -message "The property $key in $settingsName is expected to be of type $($deploymentSettings."$key".GetType().Name)" + } + } + $deploymentSettings."$key" = $deployTo."$key" + } + } + if ($deploymentSettings."shell" -ne 'pwsh' -and $deploymentSettings."shell" -ne 'powershell') { + throw "The shell setting in $settingsName must be either 'pwsh' or 'powershell'" + } + if ($deploymentSettings."runs-on" -like "*ubuntu-*" -and $deploymentSettings."shell" -eq "powershell") { + Write-Host "Switching deployment shell to pwsh for ubuntu" + $deploymentSettings."shell" = "pwsh" + } + } + OutputDebug -message "Deployment settings for environment $($envName): $($deploymentSettings | ConvertTo-Json -Depth 5 -Compress)" + $settings."$settingsName" = $deploymentSettings + } + } else { + OutputDebug "No environments defined" + } + $settings | ValidateSettings $settings @@ -608,5 +677,21 @@ function SanitizeWorkflowName { return $workflowName.Trim().Split([System.IO.Path]::getInvalidFileNameChars()) -join "" } +function GetGitHubEnvironments() { + OutputDebugFunctionCall + $headers = GetHeaders -token $env:GITHUB_TOKEN + $url = "$($ENV:GITHUB_API_URL)/repos/$($ENV:GITHUB_REPOSITORY)/environments" + OutputDebug "Url: $url" + try { + Write-Host "Requesting environments from GitHub" + $ghEnvironments = @(((InvokeWebRequest -Headers $headers -Uri $url).Content | ConvertFrom-Json).environments) + } + catch { + $ghEnvironments = @() + Write-Host "Failed to get environments from GitHub API - Environments are not supported in this repository" + } + $ghEnvironments +} + Export-ModuleMember -Function ReadSettings Export-ModuleMember -Variable ALGoFolderName, ALGoSettingsFile, RepoSettingsFile, CustomTemplateRepoSettingsFile, CustomTemplateProjectSettingsFile diff --git a/Actions/BuildReferenceDocumentation/BuildReferenceDocumentation.ps1 b/Actions/BuildReferenceDocumentation/BuildReferenceDocumentation.ps1 index cfbbce96d5..98265c2598 100644 --- a/Actions/BuildReferenceDocumentation/BuildReferenceDocumentation.ps1 +++ b/Actions/BuildReferenceDocumentation/BuildReferenceDocumentation.ps1 @@ -17,13 +17,33 @@ $excludeProjects = $settings.alDoc.excludeProjects $maxReleases = $settings.alDoc.maxReleases $artifactsFolder = Join-Path $ENV:GITHUB_WORKSPACE ".artifacts" $artifactsFolderCreated = $false +$buildModePrefixes = @() +if ($settings.PSObject.Properties.Name -contains "buildModes") { + $buildModes = $settings.buildModes + foreach($buildMode in $buildModes) { + if ($buildMode -eq 'default') { + $buildMode = '' + } + if (-not ($buildModePrefixes -contains $buildMode)) { + $buildModePrefixes += $buildMode + } + } +} +if ($buildModePrefixes.Count -eq 0) { + $buildModePrefixes += '' +} +Write-Host $buildModePrefixes if (!(Test-Path $artifactsFolder)) { New-Item $artifactsFolder -ItemType Directory | Out-Null $artifactsFolderCreated = $true } if ($artifacts -ne ".artifacts") { Write-Host "::group::Downloading artifacts" - $allArtifacts = @(GetArtifacts -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -mask "Apps" -projects '*' -Version $artifacts -branch $ENV:GITHUB_REF_NAME) + $allArtifacts = @() + $buildModePrefixes | ForEach-Object { + $allArtifacts += @(GetArtifacts -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -mask "$($_)Apps" -projects '*' -Version $artifacts -branch $ENV:GITHUB_REF_NAME) + } + # $allArtifacts = @(GetArtifacts -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -mask "Apps" -projects '*' -Version $artifacts -branch $ENV:GITHUB_REF_NAME) if ($allArtifacts) { $allArtifacts | ForEach-Object { $filename = DownloadArtifact -token $token -artifact $_ -path $artifactsFolder diff --git a/Actions/CheckForUpdates/CheckForUpdates.HelperFunctions.ps1 b/Actions/CheckForUpdates/CheckForUpdates.HelperFunctions.ps1 index 40f770bb38..838c2491f4 100644 --- a/Actions/CheckForUpdates/CheckForUpdates.HelperFunctions.ps1 +++ b/Actions/CheckForUpdates/CheckForUpdates.HelperFunctions.ps1 @@ -244,7 +244,7 @@ function ModifyBuildWorkflows { # Modify Deliver and Deploy steps depending on build jobs if ($deploy) { $deploy.Replace('needs:', "needs: [ $($needs -join ', ') ]") - $deploy.Replace('if:', "if: (!cancelled())$ifpart && needs.Initialization.outputs.environmentCount > 0") + $deploy.Replace('if:', "if: (!cancelled())$ifpart && fromJson(needs.Initialization.outputs.deploymentEnvironmentsJson).environmentCount > 0") $yaml.Replace('jobs:/Deploy:/', $deploy.content) $postProcessNeeds += @('Deploy') } diff --git a/Actions/Deploy/Deploy.ps1 b/Actions/Deploy/Deploy.ps1 index 7851dcd5c0..9f019d7668 100644 --- a/Actions/Deploy/Deploy.ps1 +++ b/Actions/Deploy/Deploy.ps1 @@ -8,8 +8,6 @@ Param( [Parameter(HelpMessage = "Type of deployment (CD or Publish)", Mandatory = $false)] [ValidateSet('CD','Publish')] [string] $type = "CD", - [Parameter(HelpMessage = "The settings for all Deployment Environments", Mandatory = $true)] - [string] $deploymentEnvironmentsJson, [Parameter(HelpMessage = "Artifacts version. Used to check if this is a deployment from a PR", Mandatory = $false)] [string] $artifactsVersion = '' ) @@ -18,41 +16,17 @@ Import-Module (Join-Path -Path $PSScriptRoot "Deploy.psm1") . (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve) DownloadAndImportBcContainerHelper -$deploymentEnvironments = $deploymentEnvironmentsJson | ConvertFrom-Json | ConvertTo-HashTable -recurse -$deploymentSettings = $deploymentEnvironments."$environmentName" - $envName = $environmentName.Split(' ')[0] + $secrets = $env:Secrets | ConvertFrom-Json $settings = $env:Settings | ConvertFrom-Json | ConvertTo-HashTable -recurse -# Check DeployTo setting (it could have been added in the environment-specific settings) -$settingsName = "DeployTo$envName" -if ($settings.ContainsKey($settingsName)) { - # If a DeployTo setting exists - use values from this (over the defaults) - Write-Host "Setting $settingsName" - $deployTo = $settings."$settingsName" - $keys = @($deployTo.Keys) - foreach($key in $keys) { - if ($deploymentSettings.ContainsKey($key)) { - if ($null -ne $deploymentSettings."$key" -and $null -ne $deployTo."$key" -and $deploymentSettings."$key".GetType().Name -ne $deployTo."$key".GetType().Name) { - if ($key -eq "runs-on" -and $deployTo."$key" -is [Object[]]) { - # Support setting runs-on as an array in settings to not break old settings - # See https://github.com/microsoft/AL-Go/issues/1182 - $deployTo."$key" = $deployTo."$key" -join ',' - } - else { - Write-Host "::WARNING::The property $key in $settingsName is expected to be of type $($deploymentSettings."$key".GetType().Name)" - } - } - Write-Host "Property $key = $($deployTo."$key")" - $deploymentSettings."$key" = $deployTo."$key" - } - else { - $deploymentSettings += @{ - "$key" = $deployTo."$key" - } - } - } +$settingsName = "deployTo$($envName)" +if($settings.Keys -contains $settingsName) { + $deploymentSettings = ($settings."$settingsName") | ConvertTo-HashTable -recurse + OutputDebug -message "Using deployment settings: $($deploymentSettings | ConvertTo-Json -Depth 10)" +} else { + OutputError -message "No deployment settings found for environment $envName" } $authContext = $null diff --git a/Actions/Deploy/README.md b/Actions/Deploy/README.md index c0fe51d2bc..a0a323180b 100644 --- a/Actions/Deploy/README.md +++ b/Actions/Deploy/README.md @@ -20,7 +20,6 @@ Deploy Apps to online environment | environmentName | Yes | Name of environment to deploy to | | artifactsFolder | Yes | Path to the downloaded artifacts to deploy | | | type | | Type of delivery (CD or Release) | CD | -| deploymentEnvironmentsJson | Yes | The settings for all Deployment Environments | | ## OUTPUT diff --git a/Actions/Deploy/action.yaml b/Actions/Deploy/action.yaml index 4e594444d3..0c5a5df7c6 100644 --- a/Actions/Deploy/action.yaml +++ b/Actions/Deploy/action.yaml @@ -19,9 +19,6 @@ inputs: description: Type of deployment (CD or Publish) required: false default: 'CD' - deploymentEnvironmentsJson: - description: The settings for all Deployment Environments - required: true artifactsVersion: description: Artifacts version. Used to check if this is a deployment from a PR required: false @@ -41,11 +38,10 @@ runs: _environmentName: ${{ inputs.environmentName }} _artifactsFolder: ${{ inputs.artifactsFolder }} _type: ${{ inputs.type }} - _deploymentEnvironmentsJson: ${{ inputs.deploymentEnvironmentsJson }} _artifactsVersion: ${{ inputs.artifactsVersion }} run: | ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "Deploy" -Action { - ${{ github.action_path }}/Deploy.ps1 -token $ENV:_token -environmentName $ENV:_environmentName -artifactsFolder $ENV:_artifactsFolder -type $ENV:_type -deploymentEnvironmentsJson $ENV:_deploymentEnvironmentsJson -artifactsVersion $ENV:_artifactsVersion + ${{ github.action_path }}/Deploy.ps1 -token $ENV:_token -environmentName $ENV:_environmentName -artifactsFolder $ENV:_artifactsFolder -type $ENV:_type -artifactsVersion $ENV:_artifactsVersion } branding: icon: terminal diff --git a/Actions/DeployPowerPlatform/README.md b/Actions/DeployPowerPlatform/README.md index 25ed241547..a3f974d74d 100644 --- a/Actions/DeployPowerPlatform/README.md +++ b/Actions/DeployPowerPlatform/README.md @@ -19,7 +19,6 @@ Deploy the Power Platform solution from the artifacts folder | environmentName | Yes | Name of environment to deploy to | | artifactsFolder | | Path to the downloaded artifacts to deploy (when deploying from a build) | | | solutionFolder | | Path to the unpacked solutions to deploy (when deploying from branch) | | -| deploymentEnvironmentsJson | Yes | The settings for all Deployment Environments | | Either artifactsFolder or solutionFolder needs to be specified diff --git a/Actions/DeployPowerPlatform/action.yaml b/Actions/DeployPowerPlatform/action.yaml index 68fc07ced4..d476e69148 100644 --- a/Actions/DeployPowerPlatform/action.yaml +++ b/Actions/DeployPowerPlatform/action.yaml @@ -16,9 +16,6 @@ inputs: description: Path to the unpacked solution to deploy (when deploying from branch) required: false default: '' - deploymentEnvironmentsJson: - description: The settings for all Deployment Environments - required: true runs: using: composite steps: @@ -42,12 +39,16 @@ runs: ref: ${{ env.actionsRef }} path: ${{ env.actionsPath }} + - name: Read settings + uses: ./_AL-Go/Actions/ReadSettings@main + with: + shell: ${{ matrix.shell }} + - name: Parse DeployToSettings and AuthContext id: ReadPowerPlatformSettings uses: ./_AL-Go/Actions/ReadPowerPlatformSettings with: shell: ${{ inputs.shell }} - deploymentEnvironmentsJson: ${{ inputs.deploymentEnvironmentsJson }} environmentName: ${{ inputs.environmentName }} - name: Determine Power Platform solution location diff --git a/Actions/DetermineDeploymentEnvironments/DetermineDeploymentEnvironments.ps1 b/Actions/DetermineDeploymentEnvironments/DetermineDeploymentEnvironments.ps1 index 0a27f9e422..38c862b4be 100644 --- a/Actions/DetermineDeploymentEnvironments/DetermineDeploymentEnvironments.ps1 +++ b/Actions/DetermineDeploymentEnvironments/DetermineDeploymentEnvironments.ps1 @@ -1,4 +1,6 @@ Param( + [Parameter(HelpMessage = "GitHub repo environments in Json format", Mandatory = $false)] + [string] $githubEnvironmentsJson = '', [Parameter(HelpMessage = "Specifies the pattern of the environments you want to retreive (* for all)", Mandatory = $true)] [string] $getEnvironments, [Parameter(HelpMessage = "Type of deployment (CD, Publish or All)", Mandatory = $true)] @@ -22,21 +24,21 @@ function IsGitHubPagesAvailable() { } } -function GetGitHubEnvironments() { - OutputDebugFunctionCall - $headers = GetHeaders -token $env:GITHUB_TOKEN - $url = "$($ENV:GITHUB_API_URL)/repos/$($ENV:GITHUB_REPOSITORY)/environments" - OutputDebug "Url: $url" - try { - Write-Host "Requesting environments from GitHub" - $ghEnvironments = @(((InvokeWebRequest -Headers $headers -Uri $url).Content | ConvertFrom-Json).environments) - } - catch { - $ghEnvironments = @() - Write-Host "Failed to get environments from GitHub API - Environments are not supported in this repository" - } - $ghEnvironments -} +# function GetGitHubEnvironments() { +# OutputDebugFunctionCall +# $headers = GetHeaders -token $env:GITHUB_TOKEN +# $url = "$($ENV:GITHUB_API_URL)/repos/$($ENV:GITHUB_REPOSITORY)/environments" +# OutputDebug "Url: $url" +# try { +# Write-Host "Requesting environments from GitHub" +# $ghEnvironments = @(((InvokeWebRequest -Headers $headers -Uri $url).Content | ConvertFrom-Json).environments) +# } +# catch { +# $ghEnvironments = @() +# Write-Host "Failed to get environments from GitHub API - Environments are not supported in this repository" +# } +# $ghEnvironments +# } function Get-BranchesFromPolicy($ghEnvironment) { OutputDebugFunctionCall @@ -87,7 +89,15 @@ if ($getEnvironments -eq 'github-pages') { } Write-Host "Environment pattern to use: $getEnvironments" -$ghEnvironments = @(GetGitHubEnvironments) +try { + if ($githubEnvironmentsJson -eq '') { + $ghEnvironments = @() + } else { + $ghEnvironments = $githubEnvironmentsJson | ConvertFrom-Json + } +} catch { + $ghEnvironments = @() +} Write-Host "Reading environments from settings" $settings.excludeEnvironments += @('github-pages') @@ -98,40 +108,28 @@ $environments = @($ghEnvironments | ForEach-Object { $_.name }) + @($settings.en Write-Host "Environments found: $($environments -join ', ')" -$deploymentEnvironments = @{} +$deploymentEnvironments = @{ + "environments" = @(); + "environmentCount" = 0 +} $unknownEnvironment = 0 if (!($environments)) { # If no environments are defined and the user specified a single environment, use that environment # This allows the user to specify a single environment without having to define it in the settings - if ($getenvironments -notcontains '*' -and $getenvironments -notcontains '?' -and $getenvironments -notcontains ',') { - $envName = $getEnvironments.Split(' ')[0] - $deploymentEnvironments += @{ - "$getEnvironments" = @{ - "EnvironmentType" = "SaaS" - "EnvironmentName" = $envName - "Branches" = $null - "BranchesFromPolicy" = @() - "Projects" = '*' - "DependencyInstallMode" = "install" # ignore, install, upgrade or forceUpgrade - "SyncMode" = $null - "Scope" = $null - "buildMode" = $null - "continuousDeployment" = !($getEnvironments -like '* (PROD)' -or $getEnvironments -like '* (Production)' -or $getEnvironments -like '* (FAT)' -or $getEnvironments -like '* (Final Acceptance Test)') + if ($getEnvironments -notcontains '*' -and $getEnvironments -notcontains '?' -and $getEnvironments -notcontains ',') { + $deploymentEnvironments.environments += @{ + "environmentName" = $getEnvironments "runs-on" = $settings."runs-on" "shell" = $settings."shell" - "companyId" = '' - "ppEnvironmentUrl" = '' - "includeTestAppsInSandboxEnvironment" = $false - "excludeAppIds" = @() - } } + $unknownEnvironment = 1 } } else { foreach($environmentName in $environments) { - Write-Host "Environment: $environmentName" + Write-Host "Evaluating Environment for deployment: $environmentName" $envName = $environmentName.Split(' ')[0] # Check Obsolete Settings @@ -142,74 +140,30 @@ else { } # Default Deployment settings are: - # - environment name: same - # - branches: main - # - projects: all + # - branches: empty (means all branches) # - continuous deployment: only for environments not tagged with PROD or FAT # - runs-on: same as settings."runs-on" # - shell: same as settings."shell" - # - no companyId - # - no ppEnvironmentUrl - $deploymentSettings = @{ - "EnvironmentType" = "SaaS" - "EnvironmentName" = $envName - "Branches" = @() - "BranchesFromPolicy" = @() - "Projects" = '*' - "DependencyInstallMode" = "install" # ignore, install, upgrade or forceUpgrade - "SyncMode" = $null - "Scope" = $null - "buildMode" = $null - "continuousDeployment" = $null - "runs-on" = $settings."runs-on" - "shell" = $settings."shell" - "companyId" = '' - "ppEnvironmentUrl" = '' - "includeTestAppsInSandboxEnvironment" = $false - "excludeAppIds" = @() - } + # $deploymentSettings = @{ + # "branches" = @() + # "branchesFromPolicy" = @() + # "continuousDeployment" = $null + # "runs-on" = $settings."runs-on" + # "shell" = $settings."shell" + # } # Check DeployTo setting $settingsName = "DeployTo$envName" - if ($settings.ContainsKey($settingsName)) { - # If a DeployTo setting exists - use values from this (over the defaults) - Write-Host "Setting $settingsName" - $deployTo = $settings."$settingsName" - $keys = @($deployTo.Keys) - foreach($key in $keys) { - if ($deploymentSettings.ContainsKey($key)) { - if ($null -ne $deploymentSettings."$key" -and $null -ne $deployTo."$key" -and $deploymentSettings."$key".GetType().Name -ne $deployTo."$key".GetType().Name) { - if ($key -eq "runs-on" -and $deployTo."$key" -is [Object[]]) { - # Support setting runs-on as an array in settings to not break old settings - # See https://github.com/microsoft/AL-Go/issues/1182 - $deployTo."$key" = $deployTo."$key" -join ',' - } - else { - Write-Host "::WARNING::The property $key in $settingsName is expected to be of type $($deploymentSettings."$key".GetType().Name)" - } - } - Write-Host "Property $key = $($deployTo."$key")" - $deploymentSettings."$key" = $deployTo."$key" - } - else { - $deploymentSettings += @{ - "$key" = $deployTo."$key" - } - } - - } - if ($deploymentSettings."shell" -ne 'pwsh' -and $deploymentSettings."shell" -ne 'powershell') { - throw "The shell setting in $settingsName must be either 'pwsh' or 'powershell'" - } - if ($deploymentSettings."runs-on" -like "*ubuntu-*" -and $deploymentSettings."shell" -eq "powershell") { - Write-Host "Switching deployment shell to pwsh for ubuntu" - $deploymentSettings."shell" = "pwsh" - } + if($settings.Keys -contains $settingsName) { + $deploymentSettings = $settings."$settingsName" + OutputDebug -message "Using deployment settings: $($deploymentSettings | ConvertTo-Json -Depth 10)" + } else { + OutputError -message "No deployment settings found for environment $envName" } # Get Branch policies on GitHub Environment $ghEnvironment = $ghEnvironments | Where-Object { $_.name -eq $environmentName } - $deploymentSettings.BranchesFromPolicy = @(Get-BranchesFromPolicy -ghEnvironment $ghEnvironment) + $branchesFromPolicy = @(Get-BranchesFromPolicy -ghEnvironment $ghEnvironment) # Include Environment if: # - Type is not Continous Deployment @@ -244,18 +198,19 @@ else { } elseif ($type -ne 'All') { # Check whether any GitHub policy disallows this branch to deploy to this environment - if ($deploymentSettings.BranchesFromPolicy) { + if ($branchesFromPolicy) { # Check whether GITHUB_REF_NAME is allowed to deploy to this environment - $includeEnvironment = $deploymentSettings.BranchesFromPolicy | Where-Object { $ENV:GITHUB_REF_NAME -like $_ } - if ($deploymentSettings.Branches -and $includeEnvironment) { + # $includeEnvironment = $deploymentSettings.branchesFromPolicy | Where-Object { $ENV:GITHUB_REF_NAME -like $_ } + $includeEnvironment = $branchesFromPolicy | Where-Object { $ENV:GITHUB_REF_NAME -like $_ } + if ($deploymentSettings.branches -and $includeEnvironment) { # Branches are also defined in settings for this environment - only include branches that also exists in settings - $includeEnvironment = $deploymentSettings.Branches | Where-Object { $ENV:GITHUB_REF_NAME -like $_ } + $includeEnvironment = $deploymentSettings.branches | Where-Object { $ENV:GITHUB_REF_NAME -like $_ } } } else { - if ($deploymentSettings.Branches) { + if ($deploymentSettings.branches) { # Branches are defined in settings for this environment - only include branches that exists in settings - $includeEnvironment = $deploymentSettings.Branches | Where-Object { $ENV:GITHUB_REF_NAME -like $_ } + $includeEnvironment = $deploymentSettings.branches | Where-Object { $ENV:GITHUB_REF_NAME -like $_ } } else { # If no branch policies are defined in GitHub nor in settings - only allow main branch to deploy @@ -266,30 +221,22 @@ else { Write-Host "Environment $environmentName is not setup for deployments from branch $ENV:GITHUB_REF_NAME" } } + if ($includeEnvironment) { - $deploymentEnvironments += @{ "$environmentName" = $deploymentSettings } - # Dump Deployment settings for included environments - $deploymentSettings | ConvertTo-Json -Depth 99 | Out-Host + $deploymentEnvironments.environments += @{ + "environmentName" = $environmentName + "runs-on" = "$(ConvertTo-Json -InputObject @($deploymentSettings."runs-on".Split(',').Trim()) -compress)" + "shell" = $deploymentSettings."shell" + } } } } -# Calculate deployment matrix -$json = @{"matrix" = @{ "include" = @() }; "fail-fast" = $false } -$deploymentEnvironments.Keys | Sort-Object | ForEach-Object { - $deploymentEnvironment = $deploymentEnvironments."$_" - $json.matrix.include += @{ "environment" = $_; "os" = "$(ConvertTo-Json -InputObject @($deploymentEnvironment."runs-on".Split(',').Trim()) -compress)"; "shell" = $deploymentEnvironment."shell" } -} -$environmentsMatrixJson = $json | ConvertTo-Json -Depth 99 -compress -Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "EnvironmentsMatrixJson=$environmentsMatrixJson" -Write-Host "EnvironmentsMatrixJson=$environmentsMatrixJson" +$deploymentEnvironments.environmentCount = $deploymentEnvironments.environments.Count $deploymentEnvironmentsJson = ConvertTo-Json -InputObject $deploymentEnvironments -Depth 99 -Compress Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "DeploymentEnvironmentsJson=$deploymentEnvironmentsJson" Write-Host "DeploymentEnvironmentsJson=$deploymentEnvironmentsJson" -Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "EnvironmentCount=$($deploymentEnvironments.Keys.Count)" -Write-Host "EnvironmentCount=$($deploymentEnvironments.Keys.Count)" - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "UnknownEnvironment=$unknownEnvironment" Write-Host "UnknownEnvironment=$unknownEnvironment" diff --git a/Actions/DetermineDeploymentEnvironments/README.md b/Actions/DetermineDeploymentEnvironments/README.md index 2a34e96c93..797436a60c 100644 --- a/Actions/DetermineDeploymentEnvironments/README.md +++ b/Actions/DetermineDeploymentEnvironments/README.md @@ -16,14 +16,13 @@ Determines the environments to be used for a build or a publish | Name | Required | Description | Default value | | :-- | :-: | :-- | :-- | | shell | | The shell (powershell or pwsh) in which the PowerShell script in this action should run | powershell | +| githubEnvironmentsJson | | GitHub repo environments in Json format | | | getEnvironments | Yes | Specifies the pattern of the environments you want to retrieve (\* for all) | | | type | Yes | Type of deployment to get environments for (CD, Publish or All) | | ## OUTPUT -| EnvironmentsMatrixJson | The Environment matrix to use for the Deploy step in compressed JSON format | -| DeploymentEnvironmentsJson | Deployment Environments with settings in compressed JSON format | -| EnvironmentCount | Number of Deployment Environments | +| DeploymentEnvironmentsJson | The JSON representation of the environment that are suitable for deployment | | UnknownEnvironment | Flag determining whether we try to publish to an unknown environment (invoke device code flow) | | GenerateALDocArtifact | Flag determining whether to generate the ALDoc artifact | | DeployALDocArtifact | Flag determining whether to deploy the ALDoc artifact to GitHub Pages | diff --git a/Actions/DetermineDeploymentEnvironments/action.yaml b/Actions/DetermineDeploymentEnvironments/action.yaml index d913710cb2..1802f6ea79 100644 --- a/Actions/DetermineDeploymentEnvironments/action.yaml +++ b/Actions/DetermineDeploymentEnvironments/action.yaml @@ -5,6 +5,10 @@ inputs: description: Shell in which you want to run the action (powershell or pwsh) required: false default: powershell + githubEnvironmentsJson: + description: GitHub repo environments in Json format + required: false + default: '' getEnvironments: description: Specifies the pattern of the environments you want to retreive (* for all) required: true @@ -12,15 +16,9 @@ inputs: description: Type of deployment (CD, Publish or All) required: true outputs: - EnvironmentsMatrixJson: - description: The Environment matrix to use for the Deploy step in compressed JSON format - value: ${{ steps.determineDeploymentEnvironments.outputs.EnvironmentsMatrixJson }} DeploymentEnvironmentsJson: - description: Deployment Environments with settings in compressed JSON format + description: The JSON representation of the environment that are suitable for deployment value: ${{ steps.determineDeploymentEnvironments.outputs.DeploymentEnvironmentsJson }} - EnvironmentCount: - description: Number of Deployment Environments - value: ${{ steps.determineDeploymentEnvironments.outputs.EnvironmentCount }} UnknownEnvironment: description: Flag determining whether the environment is unknown value: ${{ steps.determineDeploymentEnvironments.outputs.UnknownEnvironment }} @@ -37,11 +35,12 @@ runs: shell: ${{ inputs.shell }} id: determineDeploymentEnvironments env: + _githubEnvironmentsJson: ${{ inputs.githubEnvironmentsJson }} _getEnvironments: ${{ inputs.getEnvironments }} _type: ${{ inputs.type }} run: | ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "DetermineDeploymentEnvironments" -Action { - ${{ github.action_path }}/DetermineDeploymentEnvironments.ps1 -getEnvironments $ENV:_getEnvironments -type $ENV:_type + ${{ github.action_path }}/DetermineDeploymentEnvironments.ps1 -githubEnvironmentsJson $ENV:_githubEnvironmentsJson -getEnvironments $ENV:_getEnvironments -type $ENV:_type } branding: icon: terminal diff --git a/Actions/GetArtifactsForDeployment/GetArtifactsForDeployment.ps1 b/Actions/GetArtifactsForDeployment/GetArtifactsForDeployment.ps1 index 30c2f5e7c4..c8ea2cc289 100644 --- a/Actions/GetArtifactsForDeployment/GetArtifactsForDeployment.ps1 +++ b/Actions/GetArtifactsForDeployment/GetArtifactsForDeployment.ps1 @@ -1,123 +1,149 @@ -Param( - [Parameter(HelpMessage = "The GitHub token running the action", Mandatory = $false)] - [string] $token, - [Parameter(HelpMessage = "Artifacts version to download (current, prerelease, draft, latest or version number)", Mandatory = $true)] - [string] $artifactsVersion, - [Parameter(HelpMessage = "Folder in which the artifacts will be downloaded", Mandatory = $true)] - [string] $artifactsFolder -) - -Import-Module (Join-Path -Path $PSScriptRoot "GetArtifactsForDeployment.psm1") -. (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve) -DownloadAndImportBcContainerHelper - -# Get artifacts for all projects -$projects = "*" -$artifactsToDownload = @("Apps","TestApps","Dependencies","PowerPlatformSolution") - -Write-Host "Get artifacts for version: '$artifactsVersion' for these projects: '$projects' to folder: '$artifactsFolder'" - -$artifactsFolder = Join-Path $ENV:GITHUB_WORKSPACE $artifactsFolder -if (!(Test-Path $artifactsFolder)) { - New-Item $artifactsFolder -ItemType Directory | Out-Null -} -$searchArtifacts = $false -if ($artifactsVersion -eq "current" -or $artifactsVersion -eq "prerelease" -or $artifactsVersion -eq "draft") { - Write-Host "Getting $artifactsVersion release artifacts" - $releases = GetReleases -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY - if ($releases) { - if ($artifactsVersion -eq "current") { - $release = $releases | Where-Object { -not ($_.prerelease -or $_.draft) } | Select-Object -First 1 - } - elseif ($artifactsVersion -eq "prerelease") { - $release = $releases | Where-Object { -not ($_.draft) } | Select-Object -First 1 - } - elseif ($artifactsVersion -eq "draft") { - $release = $releases | Select-Object -First 1 - } - if (!($release)) { - throw "Unable to locate $artifactsVersion release" - } - $artifactsToDownload | ForEach-Object { - DownloadRelease -token $token -projects $projects -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -release $release -path $artifactsFolder -mask $_ -unpack - } - } - else { - if ($artifactsVersion -eq "current") { - Write-Host "::Warning::Current release was specified, but no releases were found. Searching for latest build artifacts instead." - $artifactsVersion = "latest" - $searchArtifacts = $true - } - else { - throw "Artifact $artifactsVersion was not found on any release." - } - } -} -elseif ($artifactsVersion -like "PR_*") { - $prId = $artifactsVersion.Substring(3) - if (!($prId -as [int])) { - throw "Invalid PR id: $prId" - } - $prLatestCommitSha = GetLatestCommitShaFromPRId -repository $ENV:GITHUB_REPOSITORY -prId $prId -token $token - if (!($prLatestCommitSha)) { - throw "Unable to locate commit sha for PR $prId" - } - $latestPRBuildId, $lastKnownGoodBuildId = FindLatestPRRun -repository $ENV:GITHUB_REPOSITORY -commitSha $prLatestCommitSha -token $token - if ($latestPRBuildId -eq 0) { - $prLink = "https://github.com/$($ENV:GITHUB_REPOSITORY)/pull/$prId" - throw "Latest PR build for PR $prId not found, not completed or not successful - Please re-run this workflow when you have a successful build on PR_$prId ($prLink)" - } - - $prArtifacts = @() - $expiredArtifacts = @() - $lastKnownGoodBuildArtifacts = @() - # Get PR artifacts - $artifactsToDownload | ForEach-Object { - $prArtifacts += GetArtifactsFromWorkflowRun -workflowRun $latestPRBuildId -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -mask $_ -projects $projects -expiredArtifacts ([ref]$expiredArtifacts) - } - # Get last known good build artifacts referenced from PR - if ($lastKnownGoodBuildId -ne 0) { - Write-Host "Last known good build id: $lastKnownGoodBuildId" - $artifactsToDownload | ForEach-Object { - $lastKnownGoodBuildArtifacts += GetArtifactsFromWorkflowRun -workflowRun $lastKnownGoodBuildId -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -mask $_ -projects $projects -expiredArtifacts ([ref]$expiredArtifacts) - } - } - else { - Write-Host "No last known good build found." - } - - if ($expiredArtifacts) { - $prBuildLink = "https://github.com/$($ENV:GITHUB_REPOSITORY)/actions/runs/$latestPRBuildId" - $shortLivedRetentionSettingLink = "https://aka.ms/algosettings#shortLivedArtifactsRetentionDays" - throw "Build artifacts are expired, please re-run the pull request build ($prBuildLink). Note that you can control the retention days of short-lived artifacts using the AL-Go setting ShortLivedArtifactsRetentionDays. See $shortLivedRetentionSettingLink" - } - - if ($prArtifacts) { - OutputMessageAndArray -message "Artifacts from pr build" -arrayOfStrings $prArtifacts.Name - } - if ($lastKnownGoodBuildArtifacts) { - OutputMessageAndArray -message "Artifacts from last known good build" -arrayOfStrings $lastKnownGoodBuildArtifacts.Name - } - DownloadPRArtifacts -token $token -path $artifactsFolder -prArtifacts $prArtifacts -lastKnownGoodBuildArtifacts $lastKnownGoodBuildArtifacts -prRunId $latestPRBuildId -lastKnownGoodBuildRunId $lastKnownGoodBuildId -} -else { - $searchArtifacts = $true -} - -if ($searchArtifacts) { - $allArtifacts = @() - $artifactsToDownload | ForEach-Object { - $allArtifacts += @(GetArtifacts -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -mask $_ -projects $projects -version $artifactsVersion -branch $ENV:GITHUB_REF_NAME) - } - if ($allArtifacts) { - $allArtifacts | ForEach-Object { - $folder = DownloadArtifact -token $token -artifact $_ -path $artifactsFolder -unpack - if (!(Test-Path $folder)) { - throw "Unable to download artifact $($_.name)" - } - } - } - else { - throw "Could not find any artifacts for projects: '$projects', version: '$artifactsVersion'" - } -} +Param( + [Parameter(HelpMessage = "The GitHub token running the action", Mandatory = $false)] + [string] $token, + [Parameter(HelpMessage = "Artifacts version to download (current, prerelease, draft, latest or version number)", Mandatory = $true)] + [string] $artifactsVersion, + [Parameter(HelpMessage = "Folder in which the artifacts will be downloaded", Mandatory = $true)] + [string] $artifactsFolder, + [Parameter(HelpMessage = "Name of environment to deploy to", Mandatory = $true)] + [string] $environmentName +) + +Import-Module (Join-Path -Path $PSScriptRoot "GetArtifactsForDeployment.psm1") +. (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve) +DownloadAndImportBcContainerHelper + +$envName = $environmentName.Split(' ')[0] +$settings = $env:Settings | ConvertFrom-Json +$settingsName = "deployTo$($envName)" +if($settings.PSObject.Properties.Name -contains $settingsName) { + $deploymentSettings = $settings."$settingsName" + OutputDebug -message "Using deployment settings: $($deploymentSettings | ConvertTo-Json -Depth 10)" +} else { + OutputError -message "No deployment settings found for environment $envName" +} + +# Determine buildMode prefix for artifact names based on settings +$buildModePrefix = 'default' +$buildModeLabel = 'default' +if ($deploymentSettings.PSObject.Properties.Name -contains "buildMode") { + $buildModePrefix = $deploymentSettings.buildMode +} + +# If buildMode is not defined or is 'default', set it to empty string +if ($null -eq $buildModePrefix -or $buildModePrefix -eq 'default' -or $buildModePrefix -eq '') { + $buildModePrefix = '' +} else { + $buildModeLabel = $buildModePrefix +} + +# Get artifacts for all projects +$projects = "*" +$artifactsToDownload = @("$($buildModePrefix)Apps","$($buildModePrefix)TestApps","$($buildModePrefix)Dependencies","$($buildModePrefix)PowerPlatformSolution") + +Write-Host "Get artifacts for version: '$artifactsVersion' with buildMode: '$buildModeLabel' for these projects: '$projects' to folder: '$artifactsFolder'" + +$artifactsFolder = Join-Path $ENV:GITHUB_WORKSPACE $artifactsFolder +if (!(Test-Path $artifactsFolder)) { + New-Item $artifactsFolder -ItemType Directory | Out-Null +} +$searchArtifacts = $false +if ($artifactsVersion -eq "current" -or $artifactsVersion -eq "prerelease" -or $artifactsVersion -eq "draft") { + Write-Host "Getting $artifactsVersion release artifacts" + $releases = GetReleases -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY + if ($releases) { + if ($artifactsVersion -eq "current") { + $release = $releases | Where-Object { -not ($_.prerelease -or $_.draft) } | Select-Object -First 1 + } + elseif ($artifactsVersion -eq "prerelease") { + $release = $releases | Where-Object { -not ($_.draft) } | Select-Object -First 1 + } + elseif ($artifactsVersion -eq "draft") { + $release = $releases | Select-Object -First 1 + } + if (!($release)) { + throw "Unable to locate $artifactsVersion release" + } + $artifactsToDownload | ForEach-Object { + DownloadRelease -token $token -projects $projects -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -release $release -path $artifactsFolder -mask $_ -unpack + } + } + else { + if ($artifactsVersion -eq "current") { + Write-Host "::Warning::Current release was specified, but no releases were found. Searching for latest build artifacts instead." + $artifactsVersion = "latest" + $searchArtifacts = $true + } + else { + throw "Artifact $artifactsVersion was not found on any release." + } + } +} +elseif ($artifactsVersion -like "PR_*") { + $prId = $artifactsVersion.Substring(3) + if (!($prId -as [int])) { + throw "Invalid PR id: $prId" + } + $prLatestCommitSha = GetLatestCommitShaFromPRId -repository $ENV:GITHUB_REPOSITORY -prId $prId -token $token + if (!($prLatestCommitSha)) { + throw "Unable to locate commit sha for PR $prId" + } + $latestPRBuildId, $lastKnownGoodBuildId = FindLatestPRRun -repository $ENV:GITHUB_REPOSITORY -commitSha $prLatestCommitSha -token $token + if ($latestPRBuildId -eq 0) { + $prLink = "https://github.com/$($ENV:GITHUB_REPOSITORY)/pull/$prId" + throw "Latest PR build for PR $prId not found, not completed or not successful - Please re-run this workflow when you have a successful build on PR_$prId ($prLink)" + } + + $prArtifacts = @() + $expiredArtifacts = @() + $lastKnownGoodBuildArtifacts = @() + # Get PR artifacts + $artifactsToDownload | ForEach-Object { + $prArtifacts += GetArtifactsFromWorkflowRun -workflowRun $latestPRBuildId -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -mask $_ -projects $projects -expiredArtifacts ([ref]$expiredArtifacts) + } + # Get last known good build artifacts referenced from PR + if ($lastKnownGoodBuildId -ne 0) { + Write-Host "Last known good build id: $lastKnownGoodBuildId" + $artifactsToDownload | ForEach-Object { + $lastKnownGoodBuildArtifacts += GetArtifactsFromWorkflowRun -workflowRun $lastKnownGoodBuildId -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -mask $_ -projects $projects -expiredArtifacts ([ref]$expiredArtifacts) + } + } + else { + Write-Host "No last known good build found." + } + + if ($expiredArtifacts) { + $prBuildLink = "https://github.com/$($ENV:GITHUB_REPOSITORY)/actions/runs/$latestPRBuildId" + $shortLivedRetentionSettingLink = "https://aka.ms/algosettings#shortLivedArtifactsRetentionDays" + throw "Build artifacts are expired, please re-run the pull request build ($prBuildLink). Note that you can control the retention days of short-lived artifacts using the AL-Go setting ShortLivedArtifactsRetentionDays. See $shortLivedRetentionSettingLink" + } + + if ($prArtifacts) { + OutputMessageAndArray -message "Artifacts from pr build" -arrayOfStrings $prArtifacts.Name + } + if ($lastKnownGoodBuildArtifacts) { + OutputMessageAndArray -message "Artifacts from last known good build" -arrayOfStrings $lastKnownGoodBuildArtifacts.Name + } + DownloadPRArtifacts -token $token -path $artifactsFolder -prArtifacts $prArtifacts -lastKnownGoodBuildArtifacts $lastKnownGoodBuildArtifacts -prRunId $latestPRBuildId -lastKnownGoodBuildRunId $lastKnownGoodBuildId +} +else { + $searchArtifacts = $true +} + +if ($searchArtifacts) { + $allArtifacts = @() + $artifactsToDownload | ForEach-Object { + $allArtifacts += @(GetArtifacts -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -mask $_ -projects $projects -version $artifactsVersion -branch $ENV:GITHUB_REF_NAME) + } + if ($allArtifacts) { + $allArtifacts | ForEach-Object { + $folder = DownloadArtifact -token $token -artifact $_ -path $artifactsFolder -unpack + if (!(Test-Path $folder)) { + throw "Unable to download artifact $($_.name)" + } + } + } + else { + throw "Could not find any artifacts for projects: '$projects', version: '$artifactsVersion'" + } +} diff --git a/Actions/GetArtifactsForDeployment/README.md b/Actions/GetArtifactsForDeployment/README.md index 40d153eeee..92bf62ec61 100644 --- a/Actions/GetArtifactsForDeployment/README.md +++ b/Actions/GetArtifactsForDeployment/README.md @@ -1,30 +1,31 @@ -# Get artifacts for deployment - -Download artifacts for deployment - -## INPUT - -### ENV variables - -none - -### Parameters - -| Name | Required | Description | Default value | -| :-- | :-: | :-- | :-- | -| shell | | The shell (powershell or pwsh) in which the PowerShell script in this action should run | powershell | -| token | | The GitHub token running the action | github.token | -| artifactsVersion | Yes | Artifacts version to download (current, prerelease, draft, latest or version number) | | -| artifactsFolder | Yes | Folder in which the artifacts will be downloaded | | - -## OUTPUT - -none - -### ENV variables - -none - -### OUTPUT variables - -none +# Get artifacts for deployment + +Download artifacts for deployment + +## INPUT + +### ENV variables + +none + +### Parameters + +| Name | Required | Description | Default value | +| :-- | :-: | :-- | :-- | +| shell | | The shell (powershell or pwsh) in which the PowerShell script in this action should run | powershell | +| token | | The GitHub token running the action | github.token | +| artifactsVersion | Yes | Artifacts version to download (current, prerelease, draft, latest or version number) | | +| artifactsFolder | Yes | Folder in which the artifacts will be downloaded | | +| environmentName | Yes | Name of environment to deploy to | | + +## OUTPUT + +none + +### ENV variables + +none + +### OUTPUT variables + +none diff --git a/Actions/GetArtifactsForDeployment/action.yaml b/Actions/GetArtifactsForDeployment/action.yaml index 67e1578128..0cd4b2b602 100644 --- a/Actions/GetArtifactsForDeployment/action.yaml +++ b/Actions/GetArtifactsForDeployment/action.yaml @@ -1,33 +1,37 @@ -name: Get Artifacts for deployment -author: Microsoft Corporation -inputs: - shell: - description: Shell in which you want to run the action (powershell or pwsh) - required: false - default: powershell - token: - description: The GitHub token running the action - required: false - default: ${{ github.token }} - artifactsVersion: - description: Artifacts version to download (current, prerelease, draft, latest or version number) - required: true - artifactsFolder: - description: Folder in which the artifacts will be downloaded - required: true -runs: - using: composite - steps: - - name: run - shell: ${{ inputs.shell }} - env: - _token: ${{ inputs.token }} - _artifactsVersion: ${{ inputs.artifactsVersion }} - _artifactsFolder: ${{ inputs.artifactsFolder }} - run: | - ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "GetArtifactsForDeployment" -Action { - ${{ github.action_path }}/GetArtifactsForDeployment.ps1 -token $ENV:_token -artifactsVersion $ENV:_artifactsVersion -artifactsFolder $ENV:_artifactsFolder - } -branding: - icon: terminal - color: blue +name: Get Artifacts for deployment +author: Microsoft Corporation +inputs: + shell: + description: Shell in which you want to run the action (powershell or pwsh) + required: false + default: powershell + token: + description: The GitHub token running the action + required: false + default: ${{ github.token }} + artifactsVersion: + description: Artifacts version to download (current, prerelease, draft, latest or version number) + required: true + artifactsFolder: + description: Folder in which the artifacts will be downloaded + required: true + environmentName: + description: Name of environment to deploy to + required: true +runs: + using: composite + steps: + - name: run + shell: ${{ inputs.shell }} + env: + _token: ${{ inputs.token }} + _artifactsVersion: ${{ inputs.artifactsVersion }} + _artifactsFolder: ${{ inputs.artifactsFolder }} + _environmentName: ${{ inputs.environmentName }} + run: | + ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "GetArtifactsForDeployment" -Action { + ${{ github.action_path }}/GetArtifactsForDeployment.ps1 -token $ENV:_token -artifactsVersion $ENV:_artifactsVersion -artifactsFolder $ENV:_artifactsFolder -environmentName $ENV:_environmentName + } +branding: + icon: terminal + color: blue diff --git a/Actions/GetGitHubEnvironments/GetGitHubEnvironments.ps1 b/Actions/GetGitHubEnvironments/GetGitHubEnvironments.ps1 new file mode 100644 index 0000000000..5b78133720 --- /dev/null +++ b/Actions/GetGitHubEnvironments/GetGitHubEnvironments.ps1 @@ -0,0 +1,26 @@ +. (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve) + +$headers = GetHeaders -token $env:GITHUB_TOKEN +$url = "$($ENV:GITHUB_API_URL)/repos/$($ENV:GITHUB_REPOSITORY)/environments" + +OutputDebug "Url: $url" + +try { + Write-Host "Requesting environments from GitHub" + $ghEnvironments = @(((InvokeWebRequest -Headers $headers -Uri $url).Content | ConvertFrom-Json).environments) +} +catch { + $ghEnvironments = @() + Write-Host "Failed to get environments from GitHub API - Environments are not supported in this repository" +} + +Write-Host "Found $($ghEnvironments.Count) environment(s) in GitHub repository:" +if ($ghEnvironments.Count -gt 0) { + $ghEnvironments | ForEach-Object { + Write-Host "- $($_.name)" + } +} else { + Write-Host "- None" +} +Add-Content -Encoding UTF8 -Path $env:GITHUB_ENV -Value "GitHubEnvironments=$($ghEnvironments | ConvertTo-Json -Depth 99 -Compress)" +Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "GitHubEnvironments=$($ghEnvironments | ConvertTo-Json -Depth 99 -Compress)" diff --git a/Actions/GetGitHubEnvironments/README.md b/Actions/GetGitHubEnvironments/README.md new file mode 100644 index 0000000000..e29f33e49e --- /dev/null +++ b/Actions/GetGitHubEnvironments/README.md @@ -0,0 +1,23 @@ +# Determine Deployment Environments + +Determines the environments to be used for a build or a publish + +## INPUT + +### ENV variables + +| Name | Description | +| :-- | :-- | +| GITHUB_TOKEN | GITHUB_TOKEN must be set as an environment variable when calling this action | + +### Parameters + +| Name | Required | Description | Default value | +| :-- | :-: | :-- | :-- | +| shell | | The shell (powershell or pwsh) in which the PowerShell script in this action should run | powershell | + +## OUTPUT + +| GitHubEnvironments | GitHub Environments in compressed Json format | + +### ENV variables diff --git a/Actions/GetGitHubEnvironments/action.yaml b/Actions/GetGitHubEnvironments/action.yaml new file mode 100644 index 0000000000..12bd312a2f --- /dev/null +++ b/Actions/GetGitHubEnvironments/action.yaml @@ -0,0 +1,24 @@ +name: Get GitHub Environments +author: Microsoft Corporation +inputs: + shell: + description: Shell in which you want to run the action (powershell or pwsh) + required: false + default: powershell +outputs: + GitHubEnvironments: + description: GitHub Environments in compressed Json format + value: ${{ steps.getgithubenvironments.outputs.GitHubEnvironments }} +runs: + using: composite + steps: + - name: run + shell: ${{ inputs.shell }} + id: getgithubenvironments + run: | + ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "GetGitHubEnvironments" -Action { + ${{ github.action_path }}/GetGitHubEnvironments.ps1 + } +branding: + icon: terminal + color: blue diff --git a/Actions/PullPowerPlatformChanges/action.yaml b/Actions/PullPowerPlatformChanges/action.yaml index 46cd654a75..f51ea6883a 100644 --- a/Actions/PullPowerPlatformChanges/action.yaml +++ b/Actions/PullPowerPlatformChanges/action.yaml @@ -20,9 +20,6 @@ inputs: description: Name of the solution to download and folder in which to download the solution required: false default: '' - deploymentEnvironmentsJson: - description: The settings for all Deployment Environments - required: true updateBranch: description: Set the branch to update required: false @@ -54,12 +51,16 @@ runs: ref: ${{ env.actionsRef }} path: ${{ env.actionsPath }} + - name: Read settings + uses: ./_AL-Go/Actions/ReadSettings@main + with: + shell: ${{ matrix.shell }} + - name: Parse DeployToSettings and AuthContext id: ReadPowerPlatformSettings uses: ./_AL-Go/Actions/ReadPowerPlatformSettings with: shell: ${{ inputs.shell }} - deploymentEnvironmentsJson: ${{ inputs.deploymentEnvironmentsJson }} environmentName: ${{ inputs.environmentName }} - name: Set up new branch for changes diff --git a/Actions/ReadPowerPlatformSettings/README.md b/Actions/ReadPowerPlatformSettings/README.md index c5f7324be6..c9ff8bb400 100644 --- a/Actions/ReadPowerPlatformSettings/README.md +++ b/Actions/ReadPowerPlatformSettings/README.md @@ -16,7 +16,6 @@ Read settings for Power Platform deployment from settings and secrets | Name | Required | Description | Default value | | :-- | :-: | :-- | :-- | | shell | | The shell (powershell or pwsh) in which the PowerShell script in this action should run | powershell | -| deploymentEnvironmentsJson | Yes | The settings for all Deployment Environments | | | environmentName | Yes | Name of environment to deploy to | | ## OUTPUT diff --git a/Actions/ReadPowerPlatformSettings/ReadPowerPlatformSettings.ps1 b/Actions/ReadPowerPlatformSettings/ReadPowerPlatformSettings.ps1 index dccab375ef..8b9f6133bd 100644 --- a/Actions/ReadPowerPlatformSettings/ReadPowerPlatformSettings.ps1 +++ b/Actions/ReadPowerPlatformSettings/ReadPowerPlatformSettings.ps1 @@ -1,6 +1,4 @@ param( - [Parameter(Mandatory = $true)] - [string] $deploymentEnvironmentsJson, [Parameter(Mandatory = $true)] [string] $environmentName ) @@ -8,17 +6,31 @@ $ErrorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-S $envName = $environmentName.Split(' ')[0] -# Read the deployment settings -$deploymentEnvironments = $deploymentEnvironmentsJson | ConvertFrom-Json -$deploymentSettings = $deploymentEnvironments."$environmentName" +$settings = $env:Settings | ConvertFrom-Json + +# If there is a deployTo settings, overwrite the default settings +$settingsName = "deployTo$($envName)" +if($settings.PSObject.Properties.Name -contains $settingsName) { + $deploymentSettings = $settings."$settingsName" + OutputDebug -message "Using deployment settings: $($deploymentSettings | ConvertTo-Json -Depth 10)" +} else { + OutputError -message "No deployment settings found for environment $envName" +} foreach($property in 'ppEnvironmentUrl','companyId','environmentName') { - if ($deploymentSettings | Get-Member -MemberType Properties -name $property) { + if ($deploymentSettings.PSObject.Properties.Name -contains $property) { Write-Host "Setting $property" Add-Content -Encoding utf8 -Path $env:GITHUB_OUTPUT -Value "$property=$($deploymentSettings."$property")" } else { - throw "$envName setting must contain '$property' property" + throw "DeployTo$envName setting must contain '$property' property" # Defensive check + } +} + +# Make sure required settings are not empty +foreach($property in 'ppEnvironmentUrl','companyId','environmentName') { + if ([string]::IsNullOrWhiteSpace($deploymentSettings."$property")) { + throw "DeployTo$envName setting must contain '$property' property" } } @@ -27,7 +39,7 @@ $secrets = $env:Secrets | ConvertFrom-Json # Read the authentication context from secrets $authContext = $null foreach($secretName in "$($envName)-AuthContext","$($envName)_AuthContext","AuthContext") { - if ($secrets."$secretName") { + if ($secrets.PSObject.Properties.Name -contains "$secretName") { Write-Host "Setting authentication context from secret $secretName" $authContext = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($secrets."$secretName")) | ConvertFrom-Json 'ppTenantId','ppApplicationId','ppClientSecret','ppUserName','ppPassword' | ForEach-Object { diff --git a/Actions/ReadPowerPlatformSettings/action.yaml b/Actions/ReadPowerPlatformSettings/action.yaml index bbfabe9236..d13753408c 100644 --- a/Actions/ReadPowerPlatformSettings/action.yaml +++ b/Actions/ReadPowerPlatformSettings/action.yaml @@ -5,9 +5,6 @@ inputs: description: Shell in which you want to run the action (powershell or pwsh) required: false default: powershell - deploymentEnvironmentsJson: - description: The settings for all Deployment Environments - required: true environmentName: description: Name of environment to deploy to required: true @@ -43,11 +40,10 @@ runs: shell: ${{ inputs.shell }} id: ReadPowerPlatformSettings env: - _deploymentEnvironmentsJson: ${{ inputs.deploymentEnvironmentsJson }} _environmentName: ${{ inputs.environmentName }} run: | ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "ReadPowerPlatformSettings" -Action { - ${{ github.action_path }}/ReadPowerPlatformSettings.ps1 -deploymentEnvironmentsJson $ENV:_deploymentEnvironmentsJson -environmentName $ENV:_environmentName + ${{ github.action_path }}/ReadPowerPlatformSettings.ps1 -environmentName $ENV:_environmentName } branding: icon: terminal diff --git a/Actions/ReadSettings/ReadSettings.ps1 b/Actions/ReadSettings/ReadSettings.ps1 index c19bf94e8a..6dd5208695 100644 --- a/Actions/ReadSettings/ReadSettings.ps1 +++ b/Actions/ReadSettings/ReadSettings.ps1 @@ -6,12 +6,14 @@ [Parameter(HelpMessage = "Workflow name for which the settings are to be read", Mandatory = $false)] [string] $workflowName = "$ENV:GITHUB_WORKFLOW", [Parameter(HelpMessage = "Specifies which properties to get from the settings file, default is all", Mandatory = $false)] - [string] $get = "" + [string] $get = "", + [Parameter(HelpMessage = "GitHub Environments in compressed Json format", Mandatory = $false)] + [string] $githubEnvironmentsJson = "" ) . (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve) -$settings = ReadSettings -project $project -buildMode $buildMode -workflowName $workflowName +$settings = ReadSettings -project $project -buildMode $buildMode -workflowName $workflowName -githubEnvironmentsJson $githubEnvironmentsJson if ($get) { $getSettings = $get.Split(',').Trim() } diff --git a/Actions/ReadSettings/action.yaml b/Actions/ReadSettings/action.yaml index 3709208eb0..a2b676a7e5 100644 --- a/Actions/ReadSettings/action.yaml +++ b/Actions/ReadSettings/action.yaml @@ -1,48 +1,53 @@ -name: Read Settings -author: Microsoft Corporation -inputs: - shell: - description: Shell in which you want to run the action (powershell or pwsh) - required: false - default: powershell - project: - description: Project folder - required: false - default: '.' - buildMode: - description: Build mode - required: false - default: 'Default' - workflowName: - description: Workflow name for which the settings are to be read - required: false - default: ${{ github.workflow }} - get: - description: Specifies which properties to get from the settings file, default is all - required: false - default: '' -outputs: - GitHubRunnerJson: - description: GitHubRunner in compressed Json format - value: ${{ steps.readsettings.outputs.GitHubRunnerJson }} - GitHubRunnerShell: - description: Shell for GitHubRunner jobs - value: ${{ steps.readsettings.outputs.GitHubRunnerShell }} -runs: - using: composite - steps: - - name: run - shell: ${{ inputs.shell }} - id: readsettings - env: - _project: ${{ inputs.project }} - _buildMode: ${{ inputs.buildMode }} - _workflowName: ${{ inputs.workflowName }} - _get: ${{ inputs.get }} - run: | - ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "ReadSettings" -Action { - ${{ github.action_path }}/ReadSettings.ps1 -project $ENV:_project -buildMode $ENV:_buildMode -workflowName $ENV:_workflowName -get $ENV:_get - } -branding: - icon: terminal - color: blue +name: Read Settings +author: Microsoft Corporation +inputs: + shell: + description: Shell in which you want to run the action (powershell or pwsh) + required: false + default: powershell + project: + description: Project folder + required: false + default: '.' + buildMode: + description: Build mode + required: false + default: 'Default' + workflowName: + description: Workflow name for which the settings are to be read + required: false + default: ${{ github.workflow }} + get: + description: Specifies which properties to get from the settings file, default is all + required: false + default: '' + githubEnvironmentsJson: + description: GitHub Environments in compressed Json format + required: false + default: '' +outputs: + GitHubRunnerJson: + description: GitHubRunner in compressed Json format + value: ${{ steps.readsettings.outputs.GitHubRunnerJson }} + GitHubRunnerShell: + description: Shell for GitHubRunner jobs + value: ${{ steps.readsettings.outputs.GitHubRunnerShell }} +runs: + using: composite + steps: + - name: run + shell: ${{ inputs.shell }} + id: readsettings + env: + _project: ${{ inputs.project }} + _buildMode: ${{ inputs.buildMode }} + _workflowName: ${{ inputs.workflowName }} + _get: ${{ inputs.get }} + _githubEnvironmentsJson: ${{ inputs.githubEnvironmentsJson }} + run: | + ${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "ReadSettings" -Action { + ${{ github.action_path }}/ReadSettings.ps1 -project $ENV:_project -buildMode $ENV:_buildMode -workflowName $ENV:_workflowName -get $ENV:_get -githubEnvironmentsJson $ENV:_githubEnvironmentsJson + } +branding: + icon: terminal + color: blue diff --git a/Scenarios/settings.md b/Scenarios/settings.md index 48ea6ab0cf..82e1cefa82 100644 --- a/Scenarios/settings.md +++ b/Scenarios/settings.md @@ -473,8 +473,8 @@ In the `needs` property, you specify which jobs should be complete before this j ``` Deploy: needs: [ Initialization, Build, CustomJob-PrepareDeploy ] - if: always() && needs.Build.result == 'Success' && needs.Initialization.outputs.environmentCount > 0 - strategy: ${{ fromJson(needs.Initialization.outputs.environmentsMatrixJson) }} + if: always() && needs.Build.result == 'Success' && fromJson(needs.Initialization.outputs.deploymentEnvironmentsJson).environmentCount > 0 + strategy: ... ``` Custom jobs will be preserved when running Update AL-Go System Files. diff --git a/Templates/AppSource App/.github/workflows/CICD.yaml b/Templates/AppSource App/.github/workflows/CICD.yaml index 742e7c8747..c7ac944038 100644 --- a/Templates/AppSource App/.github/workflows/CICD.yaml +++ b/Templates/AppSource App/.github/workflows/CICD.yaml @@ -31,9 +31,7 @@ jobs: runs-on: [ windows-latest ] outputs: telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }} - environmentsMatrixJson: ${{ steps.DetermineDeploymentEnvironments.outputs.EnvironmentsMatrixJson }} - environmentCount: ${{ steps.DetermineDeploymentEnvironments.outputs.EnvironmentCount }} - deploymentEnvironmentsJson: ${{ steps.DetermineDeploymentEnvironments.outputs.DeploymentEnvironmentsJson }} + DeploymentEnvironmentsJson: ${{ steps.DetermineDeploymentEnvironments.outputs.DeploymentEnvironmentsJson }} generateALDocArtifact: ${{ steps.DetermineDeploymentEnvironments.outputs.GenerateALDocArtifact }} deployALDocArtifact: ${{ steps.DetermineDeploymentEnvironments.outputs.DeployALDocArtifact }} deliveryTargetsJson: ${{ steps.DetermineDeliveryTargets.outputs.DeliveryTargetsJson }} @@ -48,6 +46,7 @@ jobs: workflowDepth: ${{ steps.DetermineWorkflowDepth.outputs.WorkflowDepth }} powerPlatformSolutionFolder: ${{ steps.DeterminePowerPlatformSolutionFolder.outputs.powerPlatformSolutionFolder }} trackALAlertsInGitHub: ${{ steps.SetALCodeAnalysisVar.outputs.trackALAlertsInGitHub }} + githubEnvironmentsJson: ${{ steps.GetGitHubEnvironments.outputs.GitHubEnvironments }} steps: - name: Dump Workflow Information uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@main @@ -65,12 +64,21 @@ jobs: with: shell: powershell + - name: Get GitHub Environments + id: GetGitHubEnvironments + uses: microsoft/AL-Go-Actions/GetGitHubEnvironments@main + env: + GITHUB_TOKEN: ${{ github.token }} + with: + shell: powershell + - name: Read settings id: ReadSettings uses: microsoft/AL-Go-Actions/ReadSettings@main with: shell: powershell get: type,powerPlatformSolutionFolder,useGitSubmodules,trackALAlertsInGitHub + githubEnvironmentsJson: ${{ env.GithubEnvironments }} - name: Set AL Code Analysis Var output id: SetALCodeAnalysisVar @@ -146,6 +154,7 @@ jobs: GITHUB_TOKEN: ${{ github.token }} with: shell: powershell + githubEnvironmentsJson: ${{ env.GithubEnvironments }} getEnvironments: '*' type: 'CD' @@ -292,19 +301,22 @@ jobs: Deploy: needs: [ Initialization, Build ] - if: (!cancelled()) && (needs.Build.result == 'success' || needs.Build.result == 'skipped') && needs.Initialization.outputs.environmentCount > 0 - strategy: ${{ fromJson(needs.Initialization.outputs.environmentsMatrixJson) }} - runs-on: ${{ fromJson(matrix.os) }} - name: Deploy to ${{ matrix.environment }} + if: (!cancelled()) && (needs.Build.result == 'success' || needs.Build.result == 'skipped') && fromJson(needs.Initialization.outputs.deploymentEnvironmentsJson).environmentCount > 0 + strategy: + matrix: + include: ${{ fromJson(needs.Initialization.outputs.deploymentEnvironmentsJson).environments }} + fail-fast: false + runs-on: ${{ fromJson(matrix.runs-on) }} + name: Deploy to ${{ matrix.environmentName }} defaults: run: shell: ${{ matrix.shell }} environment: - name: ${{ matrix.environment }} + name: ${{ matrix.environmentName }} url: ${{ steps.Deploy.outputs.environmentUrl }} env: ALGoEnvSettings: ${{ vars.ALGoEnvironmentSettings }} - ALGoEnvName: ${{ matrix.environment }} + ALGoEnvName: ${{ matrix.environmentName }} steps: - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -319,12 +331,13 @@ jobs: with: shell: ${{ matrix.shell }} get: type,powerPlatformSolutionFolder + githubEnvironmentsJson: ${{ needs.Initialization.outputs.githubEnvironmentsJson }} - name: EnvName id: envName run: | $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - $envName = '${{ matrix.environment }}'.split(' ')[0] + $envName = '${{ matrix.environmentName }}'.split(' ')[0] Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "envName=$envName" - name: Read secrets @@ -342,10 +355,9 @@ jobs: Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}' with: shell: ${{ matrix.shell }} - environmentName: ${{ matrix.environment }} + environmentName: ${{ matrix.environmentName }} artifactsFolder: '.artifacts' type: 'CD' - deploymentEnvironmentsJson: ${{ needs.Initialization.outputs.deploymentEnvironmentsJson }} - name: Deploy to Power Platform if: env.type == 'PTE' && env.powerPlatformSolutionFolder != '' @@ -354,9 +366,8 @@ jobs: Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}' with: shell: powershell - environmentName: ${{ matrix.environment }} + environmentName: ${{ matrix.environmentName }} artifactsFolder: '.artifacts' - deploymentEnvironmentsJson: ${{ needs.Initialization.outputs.deploymentEnvironmentsJson }} Deliver: needs: [ Initialization, Build ] diff --git a/Templates/AppSource App/.github/workflows/PublishToEnvironment.yaml b/Templates/AppSource App/.github/workflows/PublishToEnvironment.yaml index 0f1021c8ac..b01e2afb6b 100644 --- a/Templates/AppSource App/.github/workflows/PublishToEnvironment.yaml +++ b/Templates/AppSource App/.github/workflows/PublishToEnvironment.yaml @@ -31,11 +31,10 @@ jobs: needs: [ ] runs-on: [ windows-latest ] outputs: - environmentsMatrixJson: ${{ steps.DetermineDeploymentEnvironments.outputs.EnvironmentsMatrixJson }} - environmentCount: ${{ steps.DetermineDeploymentEnvironments.outputs.EnvironmentCount }} deploymentEnvironmentsJson: ${{ steps.DetermineDeploymentEnvironments.outputs.DeploymentEnvironmentsJson }} deviceCode: ${{ steps.Authenticate.outputs.deviceCode }} telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }} + githubEnvironmentsJson: ${{ steps.GetGitHubEnvironments.outputs.GitHubEnvironments }} steps: - name: Dump Workflow Information uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@main @@ -51,11 +50,20 @@ jobs: with: shell: powershell + - name: Get GitHub Environments + id: GetGitHubEnvironments + uses: microsoft/AL-Go-Actions/GetGitHubEnvironments@main + env: + GITHUB_TOKEN: ${{ github.token }} + with: + shell: powershell + - name: Read settings id: ReadSettings uses: microsoft/AL-Go-Actions/ReadSettings@main with: shell: powershell + githubEnvironmentsJson: ${{ env.GithubEnvironments }} - name: Determine Deployment Environments id: DetermineDeploymentEnvironments @@ -64,6 +72,7 @@ jobs: GITHUB_TOKEN: ${{ github.token }} with: shell: powershell + githubEnvironmentsJson: ${{ env.GithubEnvironments }} getEnvironments: ${{ github.event.inputs.environmentName }} type: 'Publish' @@ -72,7 +81,7 @@ jobs: if: steps.DetermineDeploymentEnvironments.outputs.UnknownEnvironment == 1 run: | $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - $envName = '${{ fromJson(steps.DetermineDeploymentEnvironments.outputs.environmentsMatrixJson).matrix.include[0].environment }}'.split(' ')[0] + $envName = '${{ fromJson(steps.DetermineDeploymentEnvironments.outputs.DeploymentEnvironmentsJson).environments[0].name }}'.split(' ')[0] Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "envName=$envName" - name: Read secrets @@ -119,20 +128,23 @@ jobs: Deploy: needs: [ Initialization ] - if: needs.Initialization.outputs.environmentCount > 0 - strategy: ${{ fromJson(needs.Initialization.outputs.environmentsMatrixJson) }} - runs-on: ${{ fromJson(matrix.os) }} - name: Deploy to ${{ matrix.environment }} + if: fromJson(needs.Initialization.outputs.deploymentEnvironmentsJson).environmentCount > 0 + strategy: + matrix: + include: ${{ fromJson(needs.Initialization.outputs.deploymentEnvironmentsJson).environments }} + fail-fast: false + runs-on: ${{ fromJson(matrix.runs-on) }} + name: Deploy to ${{ matrix.environmentName }} defaults: run: shell: ${{ matrix.shell }} environment: - name: ${{ matrix.environment }} + name: ${{ matrix.environmentName }} url: ${{ steps.Deploy.outputs.environmentUrl }} env: deviceCode: ${{ needs.Initialization.outputs.deviceCode }} ALGoEnvSettings: ${{ vars.ALGoEnvironmentSettings }} - ALGoEnvName: ${{ matrix.environment }} + ALGoEnvName: ${{ matrix.environmentName }} steps: - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -141,7 +153,7 @@ jobs: id: envName run: | $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - $envName = '${{ matrix.environment }}'.split(' ')[0] + $envName = '${{ matrix.environmentName }}'.split(' ')[0] Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "envName=$envName" - name: Read settings @@ -149,6 +161,7 @@ jobs: with: shell: ${{ matrix.shell }} get: type,powerPlatformSolutionFolder + githubEnvironmentsJson: ${{ needs.Initialization.outputs.githubEnvironmentsJson }} - name: Read secrets id: ReadSecrets @@ -164,6 +177,7 @@ jobs: shell: ${{ matrix.shell }} artifactsVersion: ${{ github.event.inputs.appVersion }} artifactsFolder: '.artifacts' + environmentName: ${{ matrix.environmentName }} - name: Deploy to Business Central id: Deploy @@ -172,10 +186,9 @@ jobs: Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}' with: shell: ${{ matrix.shell }} - environmentName: ${{ matrix.environment }} + environmentName: ${{ matrix.environmentName }} artifactsFolder: '.artifacts' type: 'Publish' - deploymentEnvironmentsJson: ${{ needs.Initialization.outputs.deploymentEnvironmentsJson }} artifactsVersion: ${{ github.event.inputs.appVersion }} - name: Deploy to Power Platform @@ -185,9 +198,8 @@ jobs: Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}' with: shell: ${{ matrix.shell }} - environmentName: ${{ matrix.environment }} + environmentName: ${{ matrix.environmentName }} artifactsFolder: '.artifacts' - deploymentEnvironmentsJson: ${{ needs.Initialization.outputs.deploymentEnvironmentsJson }} PostProcess: needs: [ Initialization, Deploy ] diff --git a/Templates/Per Tenant Extension/.github/workflows/CICD.yaml b/Templates/Per Tenant Extension/.github/workflows/CICD.yaml index 48df20bd79..19b0a69294 100644 --- a/Templates/Per Tenant Extension/.github/workflows/CICD.yaml +++ b/Templates/Per Tenant Extension/.github/workflows/CICD.yaml @@ -31,8 +31,6 @@ jobs: runs-on: [ windows-latest ] outputs: telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }} - environmentsMatrixJson: ${{ steps.DetermineDeploymentEnvironments.outputs.EnvironmentsMatrixJson }} - environmentCount: ${{ steps.DetermineDeploymentEnvironments.outputs.EnvironmentCount }} deploymentEnvironmentsJson: ${{ steps.DetermineDeploymentEnvironments.outputs.DeploymentEnvironmentsJson }} generateALDocArtifact: ${{ steps.DetermineDeploymentEnvironments.outputs.GenerateALDocArtifact }} deployALDocArtifact: ${{ steps.DetermineDeploymentEnvironments.outputs.DeployALDocArtifact }} @@ -48,6 +46,7 @@ jobs: workflowDepth: ${{ steps.DetermineWorkflowDepth.outputs.WorkflowDepth }} powerPlatformSolutionFolder: ${{ steps.DeterminePowerPlatformSolutionFolder.outputs.powerPlatformSolutionFolder }} trackALAlertsInGitHub: ${{ steps.SetALCodeAnalysisVar.outputs.trackALAlertsInGitHub }} + githubEnvironmentsJson: ${{ steps.GetGitHubEnvironments.outputs.GitHubEnvironments }} steps: - name: Dump Workflow Information uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@main @@ -65,12 +64,21 @@ jobs: with: shell: powershell + - name: Get GitHub Environments + id: GetGitHubEnvironments + uses: microsoft/AL-Go-Actions/GetGitHubEnvironments@main + env: + GITHUB_TOKEN: ${{ github.token }} + with: + shell: powershell + - name: Read settings id: ReadSettings uses: microsoft/AL-Go-Actions/ReadSettings@main with: shell: powershell get: type,powerPlatformSolutionFolder,useGitSubmodules,trackALAlertsInGitHub + githubEnvironmentsJson: ${{ env.GithubEnvironments }} - name: Set AL Code Analysis Var output id: SetALCodeAnalysisVar @@ -146,6 +154,7 @@ jobs: GITHUB_TOKEN: ${{ github.token }} with: shell: powershell + githubEnvironmentsJson: ${{ env.GithubEnvironments }} getEnvironments: '*' type: 'CD' @@ -217,7 +226,7 @@ jobs: parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }} project: ${{ needs.Initialization.outputs.powerPlatformSolutionFolder }} projectName: ${{ needs.Initialization.outputs.powerPlatformSolutionFolder }} - publishArtifacts: ${{ github.ref_name == 'main' || startswith(github.ref_name, 'release/') || startswith(github.ref_name, 'releases/') || needs.Initialization.outputs.deliveryTargetsJson != '[]' || needs.Initialization.outputs.environmentCount > 0 }} + publishArtifacts: ${{ github.ref_name == 'main' || startswith(github.ref_name, 'release/') || startswith(github.ref_name, 'releases/') || needs.Initialization.outputs.deliveryTargetsJson != '[]' || fromJson(needs.Initialization.outputs.deploymentEnvironmentsJson).environmentCount > 0 }} CodeAnalysisUpload: needs: [ Initialization, Build ] @@ -306,19 +315,22 @@ jobs: Deploy: needs: [ Initialization, Build, BuildPP ] - if: (!cancelled()) && (needs.Build.result == 'success' || needs.Build.result == 'skipped') && (needs.BuildPP.result == 'success' || needs.BuildPP.result == 'skipped') && needs.Initialization.outputs.environmentCount > 0 - strategy: ${{ fromJson(needs.Initialization.outputs.environmentsMatrixJson) }} - runs-on: ${{ fromJson(matrix.os) }} - name: Deploy to ${{ matrix.environment }} + if: (!cancelled()) && (needs.Build.result == 'success' || needs.Build.result == 'skipped') && (needs.BuildPP.result == 'success' || needs.BuildPP.result == 'skipped') && fromJson(needs.Initialization.outputs.deploymentEnvironmentsJson).environmentCount > 0 + strategy: + matrix: + include: ${{ fromJson(needs.Initialization.outputs.deploymentEnvironmentsJson).environments }} + fail-fast: false + runs-on: ${{ fromJson(matrix.runs-on) }} + name: Deploy to ${{ matrix.environmentName }} defaults: run: shell: ${{ matrix.shell }} environment: - name: ${{ matrix.environment }} + name: ${{ matrix.environmentName }} url: ${{ steps.Deploy.outputs.environmentUrl }} env: ALGoEnvSettings: ${{ vars.ALGoEnvironmentSettings }} - ALGoEnvName: ${{ matrix.environment }} + ALGoEnvName: ${{ matrix.environmentName }} steps: - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -333,12 +345,13 @@ jobs: with: shell: ${{ matrix.shell }} get: type,powerPlatformSolutionFolder + githubEnvironmentsJson: ${{ needs.Initialization.outputs.githubEnvironmentsJson }} - name: EnvName id: envName run: | $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - $envName = '${{ matrix.environment }}'.split(' ')[0] + $envName = '${{ matrix.environmentName }}'.split(' ')[0] Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "envName=$envName" - name: Read secrets @@ -356,10 +369,9 @@ jobs: Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}' with: shell: ${{ matrix.shell }} - environmentName: ${{ matrix.environment }} + environmentName: ${{ matrix.environmentName }} artifactsFolder: '.artifacts' type: 'CD' - deploymentEnvironmentsJson: ${{ needs.Initialization.outputs.deploymentEnvironmentsJson }} - name: Deploy to Power Platform if: env.type == 'PTE' && env.powerPlatformSolutionFolder != '' @@ -368,9 +380,8 @@ jobs: Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}' with: shell: powershell - environmentName: ${{ matrix.environment }} + environmentName: ${{ matrix.environmentName }} artifactsFolder: '.artifacts' - deploymentEnvironmentsJson: ${{ needs.Initialization.outputs.deploymentEnvironmentsJson }} Deliver: needs: [ Initialization, Build, BuildPP ] diff --git a/Templates/Per Tenant Extension/.github/workflows/PublishToEnvironment.yaml b/Templates/Per Tenant Extension/.github/workflows/PublishToEnvironment.yaml index 0f1021c8ac..b01e2afb6b 100644 --- a/Templates/Per Tenant Extension/.github/workflows/PublishToEnvironment.yaml +++ b/Templates/Per Tenant Extension/.github/workflows/PublishToEnvironment.yaml @@ -31,11 +31,10 @@ jobs: needs: [ ] runs-on: [ windows-latest ] outputs: - environmentsMatrixJson: ${{ steps.DetermineDeploymentEnvironments.outputs.EnvironmentsMatrixJson }} - environmentCount: ${{ steps.DetermineDeploymentEnvironments.outputs.EnvironmentCount }} deploymentEnvironmentsJson: ${{ steps.DetermineDeploymentEnvironments.outputs.DeploymentEnvironmentsJson }} deviceCode: ${{ steps.Authenticate.outputs.deviceCode }} telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }} + githubEnvironmentsJson: ${{ steps.GetGitHubEnvironments.outputs.GitHubEnvironments }} steps: - name: Dump Workflow Information uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@main @@ -51,11 +50,20 @@ jobs: with: shell: powershell + - name: Get GitHub Environments + id: GetGitHubEnvironments + uses: microsoft/AL-Go-Actions/GetGitHubEnvironments@main + env: + GITHUB_TOKEN: ${{ github.token }} + with: + shell: powershell + - name: Read settings id: ReadSettings uses: microsoft/AL-Go-Actions/ReadSettings@main with: shell: powershell + githubEnvironmentsJson: ${{ env.GithubEnvironments }} - name: Determine Deployment Environments id: DetermineDeploymentEnvironments @@ -64,6 +72,7 @@ jobs: GITHUB_TOKEN: ${{ github.token }} with: shell: powershell + githubEnvironmentsJson: ${{ env.GithubEnvironments }} getEnvironments: ${{ github.event.inputs.environmentName }} type: 'Publish' @@ -72,7 +81,7 @@ jobs: if: steps.DetermineDeploymentEnvironments.outputs.UnknownEnvironment == 1 run: | $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - $envName = '${{ fromJson(steps.DetermineDeploymentEnvironments.outputs.environmentsMatrixJson).matrix.include[0].environment }}'.split(' ')[0] + $envName = '${{ fromJson(steps.DetermineDeploymentEnvironments.outputs.DeploymentEnvironmentsJson).environments[0].name }}'.split(' ')[0] Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "envName=$envName" - name: Read secrets @@ -119,20 +128,23 @@ jobs: Deploy: needs: [ Initialization ] - if: needs.Initialization.outputs.environmentCount > 0 - strategy: ${{ fromJson(needs.Initialization.outputs.environmentsMatrixJson) }} - runs-on: ${{ fromJson(matrix.os) }} - name: Deploy to ${{ matrix.environment }} + if: fromJson(needs.Initialization.outputs.deploymentEnvironmentsJson).environmentCount > 0 + strategy: + matrix: + include: ${{ fromJson(needs.Initialization.outputs.deploymentEnvironmentsJson).environments }} + fail-fast: false + runs-on: ${{ fromJson(matrix.runs-on) }} + name: Deploy to ${{ matrix.environmentName }} defaults: run: shell: ${{ matrix.shell }} environment: - name: ${{ matrix.environment }} + name: ${{ matrix.environmentName }} url: ${{ steps.Deploy.outputs.environmentUrl }} env: deviceCode: ${{ needs.Initialization.outputs.deviceCode }} ALGoEnvSettings: ${{ vars.ALGoEnvironmentSettings }} - ALGoEnvName: ${{ matrix.environment }} + ALGoEnvName: ${{ matrix.environmentName }} steps: - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -141,7 +153,7 @@ jobs: id: envName run: | $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - $envName = '${{ matrix.environment }}'.split(' ')[0] + $envName = '${{ matrix.environmentName }}'.split(' ')[0] Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "envName=$envName" - name: Read settings @@ -149,6 +161,7 @@ jobs: with: shell: ${{ matrix.shell }} get: type,powerPlatformSolutionFolder + githubEnvironmentsJson: ${{ needs.Initialization.outputs.githubEnvironmentsJson }} - name: Read secrets id: ReadSecrets @@ -164,6 +177,7 @@ jobs: shell: ${{ matrix.shell }} artifactsVersion: ${{ github.event.inputs.appVersion }} artifactsFolder: '.artifacts' + environmentName: ${{ matrix.environmentName }} - name: Deploy to Business Central id: Deploy @@ -172,10 +186,9 @@ jobs: Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}' with: shell: ${{ matrix.shell }} - environmentName: ${{ matrix.environment }} + environmentName: ${{ matrix.environmentName }} artifactsFolder: '.artifacts' type: 'Publish' - deploymentEnvironmentsJson: ${{ needs.Initialization.outputs.deploymentEnvironmentsJson }} artifactsVersion: ${{ github.event.inputs.appVersion }} - name: Deploy to Power Platform @@ -185,9 +198,8 @@ jobs: Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}' with: shell: ${{ matrix.shell }} - environmentName: ${{ matrix.environment }} + environmentName: ${{ matrix.environmentName }} artifactsFolder: '.artifacts' - deploymentEnvironmentsJson: ${{ needs.Initialization.outputs.deploymentEnvironmentsJson }} PostProcess: needs: [ Initialization, Deploy ] diff --git a/Templates/Per Tenant Extension/.github/workflows/PullPowerPlatformChanges.yaml b/Templates/Per Tenant Extension/.github/workflows/PullPowerPlatformChanges.yaml index df85744d34..783c16dd53 100644 --- a/Templates/Per Tenant Extension/.github/workflows/PullPowerPlatformChanges.yaml +++ b/Templates/Per Tenant Extension/.github/workflows/PullPowerPlatformChanges.yaml @@ -98,7 +98,6 @@ jobs: directCommit: ${{ inputs.directCommit }} environmentName: ${{ inputs.environment }} solutionFolder: ${{ env.solutionFolder }} - deploymentEnvironmentsJson: ${{ steps.DetermineDeploymentEnvironments.outputs.deploymentEnvironmentsJson }} - name: Finalize the workflow if: always() diff --git a/Templates/Per Tenant Extension/.github/workflows/PushPowerPlatformChanges.yaml b/Templates/Per Tenant Extension/.github/workflows/PushPowerPlatformChanges.yaml index 51ccd5c6b9..a2616d9576 100644 --- a/Templates/Per Tenant Extension/.github/workflows/PushPowerPlatformChanges.yaml +++ b/Templates/Per Tenant Extension/.github/workflows/PushPowerPlatformChanges.yaml @@ -87,7 +87,6 @@ jobs: shell: powershell environmentName: ${{ inputs.environment }} solutionFolder: ${{ env.solutionFolder }} - deploymentEnvironmentsJson: ${{ steps.DetermineDeploymentEnvironments.outputs.deploymentEnvironmentsJson }} - name: Finalize the workflow if: always() diff --git a/Tests/BuildReferenceDocumentation.Action.Test.ps1 b/Tests/BuildReferenceDocumentation.Action.Test.ps1 index d48796abba..0281ca24fd 100644 --- a/Tests/BuildReferenceDocumentation.Action.Test.ps1 +++ b/Tests/BuildReferenceDocumentation.Action.Test.ps1 @@ -104,4 +104,28 @@ Describe "BuildReferenceDocumentation Action Tests" { Assert-MockCalled -CommandName Get-BCArtifactUrl -Exactly 0 -Scope It Assert-MockCalled -CommandName Download-Artifacts -ParameterFilter { $artifactUrl -eq "https://example.com/sandbox/core" } } + + It 'Build modes handles correctly' { + . (Join-Path $scriptRoot 'BuildReferenceDocumentation.HelperFunctions.ps1') + . (Join-Path -Path $scriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve) + + $settings = @{ "buildModes" = @('default','custom1','custom2','default','custom1'); "alDoc" = @{ "includeProjects" = @(); "excludeProjects" = @(); "maxReleases" = 2; "header" = ""; "footer" = ""; "defaultIndexMD" = ""; "defaultReleaseMD" = ""; "groupByProject" = $true } } + $env:Settings = $settings | ConvertTo-Json -Compress + $ENV:GITHUB_WORKSPACE = ([System.IO.Path]::GetTempPath()) + + Mock GetArtifacts { return $null } + Mock DownloadArtifact { return ([System.IO.Path]::GetTempPath()) } + Mock Expand-Archive { } + Mock Remove-Item { } + Mock GetReleases { return @() } + Mock New-Item { } + Mock Get-ChildItem { } + Mock CalculateProjectsAndApps { return @(), @() } + + . (Join-Path $scriptRoot $scriptName) -token 'token' -artifacts 'placeholder' + + Assert-MockCalled -CommandName GetArtifacts -Exactly 1 -Scope It -ParameterFilter { $mask -eq 'Apps'} + Assert-MockCalled -CommandName GetArtifacts -Exactly 1 -Scope It -ParameterFilter { $mask -eq 'custom1Apps'} + Assert-MockCalled -CommandName GetArtifacts -Exactly 1 -Scope It -ParameterFilter { $mask -eq 'custom2Apps'} + } } diff --git a/Tests/DetermineDeploymentEnvironments.Test.ps1 b/Tests/DetermineDeploymentEnvironments.Test.ps1 index dd28dd7757..5260376175 100644 --- a/Tests/DetermineDeploymentEnvironments.Test.ps1 +++ b/Tests/DetermineDeploymentEnvironments.Test.ps1 @@ -38,9 +38,7 @@ Describe "DetermineDeploymentEnvironments Action Test" { It 'Test action.yaml matches script' { $outputs = [ordered]@{ - "EnvironmentsMatrixJson" = "The Environment matrix to use for the Deploy step in compressed JSON format" - "DeploymentEnvironmentsJson" = "Deployment Environments with settings in compressed JSON format" - "EnvironmentCount" = "Number of Deployment Environments" + "DeploymentEnvironmentsJson" = "The JSON representation of the environment that are suitable for deployment" "UnknownEnvironment" = "Flag determining whether the environment is unknown" "GenerateALDocArtifact" = "Flag determining whether to generate the ALDoc artifact" "DeployALDocArtifact" = "Flag determining whether to deploy the ALDoc artifact to GitHub Pages" @@ -50,44 +48,79 @@ Describe "DetermineDeploymentEnvironments Action Test" { # 2 environments defined in GitHub - no branch policy It 'Test calling action directly - 2 environments defined in GitHub - no branch policy' { - Mock InvokeWebRequest -ParameterFilter { $uri -like '*/environments' } -MockWith { - return @{"Content" = (ConvertTo-Json -Compress -Depth 99 -InputObject @{ "environments" = @( @{ "name" = "test"; "protection_rules" = @() }, @{ "name" = "another"; "protection_rules" = @() } ) })} - } + # Mock InvokeWebRequest -ParameterFilter { $uri -like '*/environments' } -MockWith { + # return @{"Content" = (ConvertTo-Json -Compress -Depth 99 -InputObject @{ "environments" = @( @{ "name" = "test"; "protection_rules" = @() }, @{ "name" = "another"; "protection_rules" = @() } ) })} + # } + $githubEnvironmentsJson = ConvertTo-Json -Compress -Depth 99 -InputObject @( @{ "name" = "test"; "protection_rules" = @() }, @{ "name" = "another"; "protection_rules" = @() } ) - $env:Settings = @{ "type" = "PTE"; "runs-on" = "ubuntu-latest"; "shell" = "pwsh"; "environments" = @(); "excludeEnvironments" = @( 'github-pages' ); "alDoc" = @{ "continuousDeployment" = $false; "deployToGitHubPages" = $false } } | ConvertTo-Json -Compress - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + $deployToTestSettings = @{ "branches" = @(); "continuousDeployment" = $null; "runs-on" = "ubuntu-latest"; "shell" = "pwsh" } + $deployToAnotherSettings = @{ "branches" = @(); "continuousDeployment" = $null; "runs-on" = "ubuntu-latest"; "shell" = "pwsh" } + $env:Settings = @{ "type" = "PTE"; "runs-on" = "ubuntu-latest"; "shell" = "pwsh"; "environments" = @(); "excludeEnvironments" = @( 'github-pages' ); "alDoc" = @{ "continuousDeployment" = $false; "deployToGitHubPages" = $false }; "DeployTotest" = $deployToTestSettings; "DeployToAnother" = $deployToAnotherSettings } | ConvertTo-Json -Compress + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse | Should -MatchHashtable @{"matrix"=@{"include"=@(@{"environment"="another";"os"="[""ubuntu-latest""]";"shell"="pwsh"};@{"environment"="test";"os"="[""ubuntu-latest""]";"shell"="pwsh"})};"fail-fast"=$false} - $DeploymentEnvironmentsJson | ConvertFrom-Json | ConvertTo-HashTable -recurse | Should -MatchHashtable @{"test"=@{"EnvironmentType"="SaaS";"EnvironmentName"="test";"Branches"=@();"BranchesFromPolicy"=@();"Projects"="*";"DependencyInstallMode"="install";"Scope"=$null;"syncMode"=$null;"buildMode"=$null;"continuousDeployment"=$null;"runs-on"=@("ubuntu-latest");"shell"="pwsh";"ppEnvironmentUrl"="";"companyId"="";"includeTestAppsInSandboxEnvironment"=$false;"excludeAppIds"=@()};"another"=@{"EnvironmentType"="SaaS";"EnvironmentName"="another";"Branches"=@();"BranchesFromPolicy"=@();"Projects"="*";"DependencyInstallMode"="install";"Scope"=$null;"syncMode"=$null;"buildMode"=$null;"continuousDeployment"=$null;"runs-on"=@("ubuntu-latest");"shell"="pwsh";"ppEnvironmentUrl"="";"companyId"="";"includeTestAppsInSandboxEnvironment"=$false;"excludeAppIds"=@()}} - $EnvironmentCount | Should -Be 2 - . (Join-Path $scriptRoot $scriptName) -getEnvironments 'test' -type 'CD' + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 2 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'test' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' + $deploymentEnvironments.environments[1].environmentName | Should -Be 'another' + $deploymentEnvironments.environments[1].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[1].shell | Should -Be 'pwsh' + + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments 'test' -type 'CD' PassGeneratedOutput - $EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse | Should -MatchHashtable @{"matrix"=@{"include"=@(@{"environment"="test";"os"="[""ubuntu-latest""]";"shell"="pwsh"})};"fail-fast"=$false} - $DeploymentEnvironmentsJson | ConvertFrom-Json | ConvertTo-HashTable -recurse | Should -MatchHashtable @{"test"=@{"EnvironmentType"="SaaS";"EnvironmentName"="test";"Branches"=@();"BranchesFromPolicy"=@();"Projects"="*";"DependencyInstallMode"="install";"Scope"=$null;"syncMode"=$null;"buildMode"=$null;"continuousDeployment"=$null;"runs-on"=@("ubuntu-latest");"shell"="pwsh";"ppEnvironmentUrl"="";"companyId"="";"includeTestAppsInSandboxEnvironment"=$false;"excludeAppIds"=@()}} - $EnvironmentCount | Should -Be 1 + + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 1 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 1 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'test' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' } # 2 environments defined in GitHub - one with branch policy = protected branches It 'Test calling action directly - 2 environments defined in GitHub - one with branch policy = protected branches' { - Mock InvokeWebRequest -ParameterFilter { $uri -like '*/environments' } -MockWith { - return @{"Content" = (ConvertTo-Json -Compress -Depth 99 -InputObject @{ "environments" = @( @{ "name" = "test"; "protection_rules" = @( @{ "type" = "branch_policy"}); "deployment_branch_policy" = @{ "protected_branches" = $true; "custom_branch_policies" = $false } }, @{ "name" = "another"; "protection_rules" = @() } ) })} - } + # Mock InvokeWebRequest -ParameterFilter { $uri -like '*/environments' } -MockWith { + # return @{"Content" = (ConvertTo-Json -Compress -Depth 99 -InputObject @{ "environments" = @( @{ "name" = "test"; "protection_rules" = @( @{ "type" = "branch_policy"}); "deployment_branch_policy" = @{ "protected_branches" = $true; "custom_branch_policies" = $false } }, @{ "name" = "another"; "protection_rules" = @() } ) })} + # } Mock InvokeWebRequest -ParameterFilter { $uri -like '*/branches' } -MockWith { return @{"Content" = (ConvertTo-Json -Compress -Depth 99 -InputObject @( @{ "name" = "branch"; "protected" = $true }, @{ "name" = "main"; "protected" = $false } ))} } - $env:Settings = @{ "type" = "PTE"; "runs-on" = "ubuntu-latest"; "shell" = "pwsh"; "environments" = @(); "excludeEnvironments" = @( 'github-pages' ); "alDoc" = @{ "continuousDeployment" = $false; "deployToGitHubPages" = $false } } | ConvertTo-Json -Compress - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + $githubEnvironmentsJson = ConvertTo-Json -Compress -Depth 99 -InputObject @( @{ "name" = "test"; "protection_rules" = @( @{ "type" = "branch_policy"}); "deployment_branch_policy" = @{ "protected_branches" = $true; "custom_branch_policies" = $false } }, @{ "name" = "another"; "protection_rules" = @() } ) + $deployToTestSettings = @{ "branches" = @(); "continuousDeployment" = $null; "runs-on" = "ubuntu-latest"; "shell" = "pwsh" } + $deployToAnotherSettings = @{ "branches" = @(); "continuousDeployment" = $null; "runs-on" = "ubuntu-latest"; "shell" = "pwsh" } + $env:Settings = @{ "type" = "PTE"; "runs-on" = "ubuntu-latest"; "shell" = "pwsh"; "environments" = @(); "excludeEnvironments" = @( 'github-pages' ); "alDoc" = @{ "continuousDeployment" = $false; "deployToGitHubPages" = $false }; "DeployTotest" = $deployToTestSettings; "DeployToAnother" = $deployToAnotherSettings } | ConvertTo-Json -Compress + + $env:GITHUB_REF_NAME = 'main' # This is not a protected branch, so the _test_ environment should not be included, while _another_ environment should be included (no branch policy) + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse | Should -MatchHashtable @{"matrix"=@{"include"=@(@{"environment"="another";"os"="[""ubuntu-latest""]";"shell"="pwsh"})};"fail-fast"=$false} - $DeploymentEnvironmentsJson | ConvertFrom-Json | ConvertTo-HashTable -recurse | Should -MatchHashtable @{"another"=@{"EnvironmentType"="SaaS";"EnvironmentName"="another";"Branches"=@();"BranchesFromPolicy"=@();"Projects"="*";"DependencyInstallMode"="install";"Scope"=$null;"syncMode"=$null;"buildMode"=$null;"continuousDeployment"=$null;"runs-on"=@("ubuntu-latest");"shell"="pwsh";"ppEnvironmentUrl"="";"companyId"="";"includeTestAppsInSandboxEnvironment"=$false;"excludeAppIds"=@()}} - $EnvironmentCount | Should -Be 1 + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 1 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 1 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'another' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' + Set-Variable -Name deploymentEnvironments -Value $null # clean up variable - $env:GITHUB_REF_NAME = 'branch' - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + $env:GITHUB_REF_NAME = 'branch' # This is a protected branch, so the _test_ environment should be included. _another_ environment should not be included as it has no branch policy (in that case deployment is only allowed from _main_ branch) + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentCount | Should -Be 1 + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 1 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 1 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'test' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' + Set-Variable -Name deploymentEnvironments -Value $null # clean up variable } # 2 environments defined in GitHub - one with branch policy = branch. the other with no branch policy @@ -102,46 +135,77 @@ Describe "DetermineDeploymentEnvironments Action Test" { return @{"Content" = (@{ "branch_policies" = @( @{ "name" = "branch" }, @{ "name" = "branch2" } ) } | ConvertTo-Json -Depth 99 -Compress)} } - $env:Settings = @{ "type" = "PTE"; "runs-on" = "ubuntu-latest"; "shell" = "pwsh"; "environments" = @(); "excludeEnvironments" = @( 'github-pages' ); "alDoc" = @{ "continuousDeployment" = $false; "deployToGitHubPages" = $false } } | ConvertTo-Json -Compress + $githubEnvironmentsJson = ConvertTo-Json -Compress -Depth 99 -InputObject @( @{ "name" = "test"; "protection_rules" = @( @{ "type" = "branch_policy"}); "deployment_branch_policy" = @{ "protected_branches" = $false; "custom_branch_policies" = $true } }, @{ "name" = "another"; "protection_rules" = @() } ) + $deployToTestSettings = @{ "branches" = @(); "continuousDeployment" = $null; "runs-on" = "ubuntu-latest"; "shell" = "pwsh" } + $deployToAnotherSettings = @{ "branches" = @(); "continuousDeployment" = $null; "runs-on" = "ubuntu-latest"; "shell" = "pwsh" } + $env:Settings = @{ "type" = "PTE"; "runs-on" = "ubuntu-latest"; "shell" = "pwsh"; "environments" = @(); "excludeEnvironments" = @( 'github-pages' ); "alDoc" = @{ "continuousDeployment" = $false; "deployToGitHubPages" = $false }; "DeployTotest" = $deployToTestSettings; "DeployToAnother" = $deployToAnotherSettings } | ConvertTo-Json -Compress # Only another environment should be included when deploying from main - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + + $env:GITHUB_REF_NAME = 'main' # This is not a protected branch, so the _test_ environment should not be included, while _another_ environment should be included (no branch policy) + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse | Should -MatchHashtable @{"matrix"=@{"include"=@(@{"environment"="another";"os"="[""ubuntu-latest""]";"shell"="pwsh"})};"fail-fast"=$false} - $DeploymentEnvironmentsJson | ConvertFrom-Json | ConvertTo-HashTable -recurse | Should -MatchHashtable @{"another"=@{"EnvironmentType"="SaaS";"EnvironmentName"="another";"Branches"=@();"BranchesFromPolicy"=@();"Projects"="*";"DependencyInstallMode"="install";"Scope"=$null;"syncMode"=$null;"buildMode"=$null;"continuousDeployment"=$null;"runs-on"=@("ubuntu-latest");"shell"="pwsh";"ppEnvironmentUrl"="";"companyId"="";"includeTestAppsInSandboxEnvironment"=$false;"excludeAppIds"=@()}} - $EnvironmentCount | Should -Be 1 - ($EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse).matrix.include.environment | Should -Contain "another" + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 1 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 1 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'another' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' + Set-Variable -Name deploymentEnvironments -Value $null # clean up variable - # Change branch to branch - now only test environment should be included (due to branch policy) - $env:GITHUB_REF_NAME = 'branch' - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + + $env:GITHUB_REF_NAME = 'branch' # Change branch to _branch_ - now only test environment should be included (due to branch policy) + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentCount | Should -Be 1 - ($EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse).matrix.include.environment | Should -Contain "test" + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 1 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 1 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'test' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' + Set-Variable -Name deploymentEnvironments -Value $null # clean up variable + - # Change branch to branch2 - test environment should still be included (due to branch policy) - $env:GITHUB_REF_NAME = 'branch2' - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + $env:GITHUB_REF_NAME = 'branch2' # Change branch to branch2 - test environment should still be included (due to branch policy) + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentCount | Should -Be 1 - ($EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse).matrix.include.environment | Should -Contain "test" + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 1 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 1 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'test' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' + Set-Variable -Name deploymentEnvironments -Value $null # clean up variable # Add Branch policy to settings to only allow branch to deploy to test environment - now no environments should be included - $settings += @{ - "DeployToTest" = @{ - "Branches" = @("branch") - } - } + $settings.DeployToTest.branches = @('branch') + $env:Settings = $settings | ConvertTo-Json -Compress - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentCount | Should -Be 0 + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 0 + $deploymentEnvironments.environments | Should -BeNullOrEmpty + Set-Variable -Name deploymentEnvironments -Value $null # clean up variable # Change branch to branch - test environment should still be included (due to branch policy) $env:GITHUB_REF_NAME = 'branch' - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentCount | Should -Be 1 - ($EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse).matrix.include.environment | Should -Contain "test" + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 1 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 1 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'test' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' } # 2 environments defined in GitHub, 1 in settings - exclude another environment @@ -150,38 +214,84 @@ Describe "DetermineDeploymentEnvironments Action Test" { return @{"Content" = (ConvertTo-Json -Compress -Depth 99 -InputObject @{ "environments" = @( @{ "name" = "test"; "protection_rules" = @() }; @{ "name" = "another"; "protection_rules" = @() } ) })} } - $settings = @{ "type" = "PTE"; "runs-on" = "ubuntu-latest"; "shell" = "pwsh"; "environments" = @("settingsenv"); "excludeEnvironments" = @( 'github-pages' ); "alDoc" = @{ "continuousDeployment" = $false; "deployToGitHubPages" = $false } } + $githubEnvironmentsJson = ConvertTo-Json -Compress -Depth 99 -InputObject @( @{ "name" = "test"; "protection_rules" = @() }; @{ "name" = "another"; "protection_rules" = @() } ) + $deployToTestSettings = @{ "branches" = @(); "continuousDeployment" = $null; "runs-on" = "ubuntu-latest"; "shell" = "pwsh" } + $deployToAnotherSettings = @{ "branches" = @(); "continuousDeployment" = $null; "runs-on" = "ubuntu-latest"; "shell" = "pwsh" } + $deployTosettingsenvSettings = @{ "branches" = @(); "continuousDeployment" = $null; "runs-on" = "ubuntu-latest"; "shell" = "pwsh" } + $settings = @{ "type" = "PTE"; "runs-on" = "ubuntu-latest"; "shell" = "pwsh"; "environments" = @("settingsenv"); "excludeEnvironments" = @( 'github-pages' ); "alDoc" = @{ "continuousDeployment" = $false; "deployToGitHubPages" = $false }; "DeployTotest" = $deployToTestSettings; "DeployToAnother" = $deployToAnotherSettings; "DeployTosettingsenv" = $deployTosettingsenvSettings } $env:Settings = $settings | ConvertTo-Json -Compress - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + + # All 3 environments should be included + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentCount | Should -Be 3 - # Exclude another environment + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 3 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 3 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'test' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' + $deploymentEnvironments.environments[1].environmentName | Should -Be 'another' + $deploymentEnvironments.environments[1].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[1].shell | Should -Be 'pwsh' + $deploymentEnvironments.environments[2].environmentName | Should -Be 'settingsenv' + $deploymentEnvironments.environments[2].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[2].shell | Should -Be 'pwsh' + Set-Variable -Name deploymentEnvironments -Value $null # clean up variable + + + # Exclude _another_ environment $settings.excludeEnvironments += @('another') $env:Settings = $settings | ConvertTo-Json -Compress - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentCount | Should -Be 2 - ($EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse).matrix.include.environment | Should -Not -Contain "another" - # Add Branch policy to settings to only allow branch to deploy to test environment - $settings += @{ - "DeployToTest" = @{ - "Branches" = @("branch") - } - } + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 2 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 2 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'test' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' + $deploymentEnvironments.environments[1].environmentName | Should -Be 'settingsenv' + $deploymentEnvironments.environments[1].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[1].shell | Should -Be 'pwsh' + + # Add Branch policy to settings to only allow branch to deploy to _test_ environment + $settings.DeployToTest.branches = @('branch') + $settings.excludeEnvironments = @() # Clear exclude environments + $env:GITHUB_REF_NAME = 'main' $env:Settings = $settings | ConvertTo-Json -Compress - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentCount | Should -Be 1 - ($EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse).matrix.include.environment | Should -Contain "settingsenv" + + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 2 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 2 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'another' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' + $deploymentEnvironments.environments[1].environmentName | Should -Be 'settingsenv' + $deploymentEnvironments.environments[1].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[1].shell | Should -Be 'pwsh' # Changing branch to branch - now only test environment should be included (due to settings branch policy) $env:GITHUB_REF_NAME = 'branch' - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentCount | Should -Be 1 - ($EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse).matrix.include.environment | Should -Contain "test" + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 1 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 1 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'test' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' } # 2 environments defined in Settings - one PROD and one non-PROD (name based) @@ -189,52 +299,75 @@ Describe "DetermineDeploymentEnvironments Action Test" { Mock InvokeWebRequest -ParameterFilter { $uri -like '*/environments' } -MockWith { throw "Not supported" } + $githubEnvironmentsJson = '' # One PROD environment and one non-PROD environment - only non-PROD environment is selected for CD - $settings = @{ "type" = "PTE"; "runs-on" = "ubuntu-latest"; "shell" = "pwsh"; "environments" = @("test (PROD)","another"); "excludeEnvironments" = @( 'github-pages' ); "alDoc" = @{ "continuousDeployment" = $false; "deployToGitHubPages" = $false } } + $deployToTestSettings = @{ "branches" = @(); "continuousDeployment" = $null; "runs-on" = "ubuntu-latest"; "shell" = "pwsh" } + $deployToAnotherSettings = @{ "branches" = @(); "continuousDeployment" = $null; "runs-on" = "ubuntu-latest"; "shell" = "pwsh" } + $settings = @{ "type" = "PTE"; "runs-on" = "ubuntu-latest"; "shell" = "pwsh"; "environments" = @("test (PROD)","another"); "excludeEnvironments" = @( 'github-pages' ); "alDoc" = @{ "continuousDeployment" = $false; "deployToGitHubPages" = $false }; "DeployTotest" = $deployToTestSettings; "DeployToAnother" = $deployToAnotherSettings } $env:Settings = $settings | ConvertTo-Json -Compress - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentCount | Should -Be 1 - ($EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse).matrix.include.environment | Should -Contain "another" + + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 1 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 1 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'another' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' # Publish to test environment - test is included $env:Settings = $settings | ConvertTo-Json -Compress - . (Join-Path $scriptRoot $scriptName) -getEnvironments 'test' -type 'Publish' + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments 'test' -type 'Publish' PassGeneratedOutput - $EnvironmentCount | Should -Be 1 - ($EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse).matrix.include.environment | Should -Contain "test (PROD)" + + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 1 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 1 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'test (PROD)' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' } # 2 environments defined in Settings - one PROD and one non-PROD (settings based) It 'Test calling action directly - 2 environments defined in Settings - one PROD and one non-PROD (settings based)' { - $settings = @{ "type" = "PTE"; "runs-on" = "ubuntu-latest"; "shell" = "pwsh"; "environments" = @("test (PROD)","another"); "excludeEnvironments" = @( 'github-pages' ); "alDoc" = @{ "continuousDeployment" = $false; "deployToGitHubPages" = $false } } - + $deployToTestSettings = @{ "branches" = @(); "continuousDeployment" = $false; "runs-on" = "ubuntu-latest"; "shell" = "pwsh" } + $deployToAnotherSettings = @{ "branches" = @(); "continuousDeployment" = $true; "runs-on" = "ubuntu-latest"; "shell" = "pwsh" } + $settings = @{ "type" = "PTE"; "runs-on" = "ubuntu-latest"; "shell" = "pwsh"; "environments" = @("test (PROD)","another"); "excludeEnvironments" = @( 'github-pages' ); "alDoc" = @{ "continuousDeployment" = $false; "deployToGitHubPages" = $false }; "DeployTotest" = $deployToTestSettings; "DeployToAnother" = $deployToAnotherSettings } + $githubEnvironmentsJson = '' Mock InvokeWebRequest -ParameterFilter { $uri -like '*/environments' } -MockWith { throw "Not supported" } - $settings += @{ - "DeployToTest" = @{ - "continuousDeployment" = $false - } - "DeployToAnother" = @{ - "continuousDeployment" = $true - } - } - # One PROD environment and one non-PROD environment - only non-PROD environment is selected for CD $env:Settings = $settings | ConvertTo-Json -Compress - . (Join-Path $scriptRoot $scriptName) -getEnvironments '*' -type 'CD' + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments '*' -type 'CD' PassGeneratedOutput - $EnvironmentCount | Should -Be 1 - ($EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse).matrix.include.environment | Should -Contain "another" + + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 1 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 1 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'another' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' # Publish to test environment - test is included $env:Settings = $settings | ConvertTo-Json -Compress - . (Join-Path $scriptRoot $scriptName) -getEnvironments 'test' -type 'Publish' + . (Join-Path $scriptRoot $scriptName) -githubEnvironmentsJson $githubEnvironmentsJson -getEnvironments 'test' -type 'Publish' PassGeneratedOutput - $EnvironmentCount | Should -Be 1 - ($EnvironmentsMatrixJson | ConvertFrom-Json | ConvertTo-HashTable -recurse).matrix.include.environment | Should -Contain "test (PROD)" + $deploymentEnvironments = $DeploymentEnvironmentsJson | ConvertFrom-Json + $deploymentEnvironments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environmentCount | Should -Be 1 + $deploymentEnvironments.environments | Should -Not -BeNullOrEmpty + $deploymentEnvironments.environments.Count | Should -Be 1 + $deploymentEnvironments.environments[0].environmentName | Should -Be 'test (PROD)' + $deploymentEnvironments.environments[0].'runs-on' | Should -Be '["ubuntu-latest"]' + $deploymentEnvironments.environments[0].shell | Should -Be 'pwsh' } } diff --git a/Tests/GetArtifactsForDeployment.Test.ps1 b/Tests/GetArtifactsForDeployment.Test.ps1 new file mode 100644 index 0000000000..ea488cf6ce --- /dev/null +++ b/Tests/GetArtifactsForDeployment.Test.ps1 @@ -0,0 +1,64 @@ +Get-Module TestActionsHelper | Remove-Module -Force +Import-Module (Join-Path $PSScriptRoot '../Actions/Github-Helper.psm1') -Force +Import-Module (Join-Path $PSScriptRoot 'TestActionsHelper.psm1') +$errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 + +Describe "GetArtifactsForDeployment Action Tests" { + BeforeAll { + . (Join-Path -Path $PSScriptRoot -ChildPath "../Actions/AL-Go-Helper.ps1" -Resolve) + + $actionName = "GetArtifactsForDeployment" + $scriptRoot = Join-Path $PSScriptRoot "..\Actions\$actionName" -Resolve + $scriptName = "$actionName.ps1" + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'scriptPath', Justification = 'False positive.')] + $scriptPath = Join-Path $scriptRoot $scriptName + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'actionScript', Justification = 'False positive.')] + $actionScript = GetActionScript -scriptRoot $scriptRoot -scriptName $scriptName + + $ENV:GITHUB_API_URL = "apiForGitHub" + + Mock GetHeaders { return @{} } + Mock GetReleases { + return @( + @{ "prerelease" = $false; "draft" = $false; "tag_name" = "v1.0.0"; "id" = 1 } + ) + } + Mock DownloadRelease { } + Mock GetArtifacts { return @{} } + Mock DownloadArtifact { return ([System.IO.Path]::GetTempPath()) } + } + + It 'Compile Action' { + Invoke-Expression $actionScript + } + + It 'Test action.yaml matches script' { + YamlTest -scriptRoot $scriptRoot -actionName $actionName -actionScript $actionScript + } + + It 'Version is current, releases exist' { + $settings = @{ "DeployToenv1" = @{ "buildMode" = '' } } + $env:Settings = $settings | ConvertTo-Json -Compress + + . (Join-Path $scriptRoot $scriptName) -artifactsVersion 'current' -artifactsFolder '.artifacts' -environmentName 'env1' + + # DownloadRelease called 4 times (Apps, TestApps, Dependencies, PowerPlatformSolution) + Assert-MockCalled -CommandName DownloadRelease -Exactly 4 + } + + It 'Version is current, releases does not exist' { + $settings = @{ "DeployToenv1" = @{ "buildMode" = '' } } + $env:Settings = $settings | ConvertTo-Json -Compress + $ENV:GITHUB_REPOSITORY = "repo" + + Mock GetReleases { + return @() + } + + . (Join-Path $scriptRoot $scriptName) -token 'token' -artifactsVersion 'current' -artifactsFolder '.artifacts' -environmentName 'env1' + + Assert-MockCalled -CommandName DownloadRelease -Exactly 0 + Assert-MockCalled -CommandName GetArtifacts -Exactly 4 + Assert-MockCalled -CommandName DownloadArtifact -Exactly 4 + } +} diff --git a/Tests/GetGitHubEnvironments.Test.ps1 b/Tests/GetGitHubEnvironments.Test.ps1 new file mode 100644 index 0000000000..108123a332 --- /dev/null +++ b/Tests/GetGitHubEnvironments.Test.ps1 @@ -0,0 +1,42 @@ +Get-Module TestActionsHelper | Remove-Module -Force +Import-Module (Join-Path $PSScriptRoot '../Actions/Github-Helper.psm1') -Force +Import-Module (Join-Path $PSScriptRoot 'TestActionsHelper.psm1') +$errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 + +Describe "GetGitHubEnvironments Action Tests" { + BeforeAll { + . (Join-Path -Path $PSScriptRoot -ChildPath "../Actions/AL-Go-Helper.ps1" -Resolve) + + $actionName = "GetGitHubEnvironments" + $scriptRoot = Join-Path $PSScriptRoot "..\Actions\$actionName" -Resolve + $scriptName = "$actionName.ps1" + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'scriptPath', Justification = 'False positive.')] + $scriptPath = Join-Path $scriptRoot $scriptName + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'actionScript', Justification = 'False positive.')] + $actionScript = GetActionScript -scriptRoot $scriptRoot -scriptName $scriptName + + $ENV:GITHUB_API_URL = "apiForGitHub" + + Mock GetHeaders { return @{} } + Mock GetReleases { + return @( + @{ "prerelease" = $false; "draft" = $false; "tag_name" = "v1.0.0"; "id" = 1 } + ) + } + Mock DownloadRelease { } + Mock GetArtifacts { return @{} } + Mock DownloadArtifact { return ([System.IO.Path]::GetTempPath()) } + } + + It 'Compile Action' { + Invoke-Expression $actionScript + } + + It 'Test action.yaml matches script' { + $outputs = [ordered]@{ + "GitHubEnvironments" = "GitHub Environments in compressed Json format" + } + + YamlTest -scriptRoot $scriptRoot -actionName $actionName -actionScript $actionScript -outputs $outputs + } +} diff --git a/Tests/ReadPowerPlatformSettings.Action.Test.ps1 b/Tests/ReadPowerPlatformSettings.Action.Test.ps1 index 4dae598dc1..cacb1101c0 100644 --- a/Tests/ReadPowerPlatformSettings.Action.Test.ps1 +++ b/Tests/ReadPowerPlatformSettings.Action.Test.ps1 @@ -17,7 +17,7 @@ Describe "Read Power Platform Settings Action Tests" { [Parameter(Mandatory = $true)] [hashtable] $deployToDevProperties ) - return @{ + $env:Settings = @{ type = "PTE" powerPlatformSolutionFolder = "CoffeMR" DeployToDev = $deployToDevProperties @@ -30,12 +30,11 @@ Describe "Read Power Platform Settings Action Tests" { [hashtable] $secretProperties ) $testSecret = $secretProperties | ConvertTo-Json - $env:Secrets = '{"DeployToDev-AuthContext": "' + [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($testSecret)) + '"}' + $env:Secrets = '{"Dev-AuthContext": "' + [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($testSecret)) + '"}' } } BeforeEach { - Write-Host "Before test" $env:GITHUB_OUTPUT = [System.IO.Path]::GetTempFileName() $env:GITHUB_ENV = [System.IO.Path]::GetTempFileName() @@ -46,7 +45,6 @@ Describe "Read Power Platform Settings Action Tests" { } AfterEach { - Write-Host "After test" Remove-Item -Path $env:GITHUB_OUTPUT -Force Remove-Item -Path $env:GITHUB_ENV -Force @@ -60,7 +58,7 @@ Describe "Read Power Platform Settings Action Tests" { companyId = "11111111-1111-1111-1111-111111111111" ppEnvironmentUrl = "https://TestUrL.crm.dynamics.com" } - $jsonInput = ConvertToDeployToSettings -deployToDevProperties $deployToDevProperties + ConvertToDeployToSettings -deployToDevProperties $deployToDevProperties # Setup secrets as GitHub environment variable $secretProperties = @{ @@ -71,7 +69,7 @@ Describe "Read Power Platform Settings Action Tests" { SetSecretsEnvVariable -secretProperties $secretProperties # Run the action - ReadPowerPlatformSettings -deploymentEnvironmentsJson $jsonInput -environmentName "DeployToDev" + . (Join-Path $scriptRoot $scriptName) -environmentName "Dev" # Assert the GitHub environment variables are set correctly $gitHubEnvPlaceholder = Get-Content -Path $env:GITHUB_OUTPUT @@ -90,7 +88,7 @@ Describe "Read Power Platform Settings Action Tests" { companyId = "11111111-1111-1111-1111-111111111111" ppEnvironmentUrl = "https://TestUrL.crm.dynamics.com" } - $jsonInput = ConvertToDeployToSettings -deployToDevProperties $deployToDevProperties + ConvertToDeployToSettings -deployToDevProperties $deployToDevProperties # Setup secrets as GitHub environment variable $secretProperties = @{ @@ -100,7 +98,7 @@ Describe "Read Power Platform Settings Action Tests" { SetSecretsEnvVariable -secretProperties $secretProperties # Run the action - ReadPowerPlatformSettings -deploymentEnvironmentsJson $jsonInput -environmentName "DeployToDev" + . (Join-Path $scriptRoot $scriptName) -environmentName "Dev" # Assert the GitHub environment variables are set correctly $gitHubEnvPlaceholder = Get-Content -Path $env:GITHUB_OUTPUT @@ -117,14 +115,13 @@ Describe "Read Power Platform Settings Action Tests" { param ( [hashtable] $deployToDevProperties ) - # Convert hashtables to JSON strings - $jsonInput = ConvertToDeployToSettings -deployToDevProperties $deployToDevProperties + ConvertToDeployToSettings -deployToDevProperties $deployToDevProperties $errorObject = $null $HasThrownException = $false # Run the action try { - ReadPowerPlatformSettings -deploymentEnvironmentsJson $jsonInput -environmentName "DeployToDev" + . (Join-Path $scriptRoot $scriptName) -environmentName "Dev" } catch { $errorObject = $_ @@ -141,6 +138,7 @@ Describe "Read Power Platform Settings Action Tests" { companyId = "11111111-1111-1111-1111-111111111111" } $errorMessage = runMissingSettingsTest -deployToDevProperties $deployToDevProperties + Write-Host "Settings are: $($env:Settings)" $errorMessage | Should -Be "DeployToDev setting must contain 'ppEnvironmentUrl' property" # Test missing companyId @@ -150,15 +148,6 @@ Describe "Read Power Platform Settings Action Tests" { } $errorMessage = runMissingSettingsTest -deployToDevProperties $deployToDevProperties $errorMessage | Should -Be "DeployToDev setting must contain 'companyId' property" - - # Test missing environmentName - $deployToDevProperties = @{ - companyId = "11111111-1111-1111-1111-111111111111" - ppEnvironmentUrl = "https://TestUrL.crm.dynamics.com" - } - $errorMessage = runMissingSettingsTest -deployToDevProperties $deployToDevProperties - $errorMessage | Should -Be "DeployToDev setting must contain 'environmentName' property" - } It 'Fails if required secret settings are missing' { @@ -169,13 +158,13 @@ Describe "Read Power Platform Settings Action Tests" { companyId = "11111111-1111-1111-1111-111111111111" ppEnvironmentUrl = "https://TestUrL.crm.dynamics.com" } - # Convert hashtables to JSON strings - $jsonInput = ConvertToDeployToSettings -deployToDevProperties $deployToDevProperties + + ConvertToDeployToSettings -deployToDevProperties $deployToDevProperties $errorObject = $null # Run the action try { - ReadPowerPlatformSettings -deploymentEnvironmentsJson $jsonInput -environmentName "DeployToDev" + . (Join-Path $scriptRoot $scriptName) -environmentName "Dev" } catch { $errorObject = $_ @@ -193,7 +182,7 @@ Describe "Read Power Platform Settings Action Tests" { } SetSecretsEnvVariable -secretProperties $secretProperties $errorMessage = runMissingSecretsTest - $errorMessage | Should -Be "Secret DeployToDev-AuthContext must contain either 'ppUserName' and 'ppPassword' properties or 'ppApplicationId', 'ppClientSecret' and 'ppTenantId' properties" + $errorMessage | Should -Be "Secret Dev-AuthContext must contain either 'ppUserName' and 'ppPassword' properties or 'ppApplicationId', 'ppClientSecret' and 'ppTenantId' properties" # Test secret missing username @@ -202,7 +191,7 @@ Describe "Read Power Platform Settings Action Tests" { } SetSecretsEnvVariable -secretProperties $secretProperties $errorMessage = runMissingSecretsTest - $errorMessage | Should -Be "Secret DeployToDev-AuthContext must contain either 'ppUserName' and 'ppPassword' properties or 'ppApplicationId', 'ppClientSecret' and 'ppTenantId' properties" + $errorMessage | Should -Be "Secret Dev-AuthContext must contain either 'ppUserName' and 'ppPassword' properties or 'ppApplicationId', 'ppClientSecret' and 'ppTenantId' properties" } diff --git a/Tests/ReadSettings.Test.ps1 b/Tests/ReadSettings.Test.ps1 index 18dc52beb4..53d2cc08d8 100644 --- a/Tests/ReadSettings.Test.ps1 +++ b/Tests/ReadSettings.Test.ps1 @@ -5,6 +5,7 @@ InModuleScope ReadSettings { # Allows testing of private functions BeforeAll { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'scriptPath', Justification = 'False positive.')] $schema = Get-Content -Path (Join-Path $PSScriptRoot '../Actions/.Modules/settings.schema.json') -Raw + Mock GetGitHubEnvironments { } } It 'Reads settings from all settings locations' { @@ -459,5 +460,65 @@ InModuleScope ReadSettings { # Allows testing of private functions # overwriteSettings should never be added to the destination object $dst.PSObject.Properties.Name | Should -Not -Contain 'overwriteSettings' } + + It 'DeployTo default settings are created for all environments' { + Mock Write-Host { } + Mock Out-Host { } + + $githubEnvironmentsJson = @( @{ "name" = "env1" }, @{ "name" = "env2" } ) | ConvertTo-Json -Depth 99 + + Push-Location + $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString()) + $githubFolder = Join-Path $tempName ".github" + $ALGoFolder = Join-Path $tempName $ALGoFolderName + + New-Item $githubFolder -ItemType Directory | Out-Null + New-Item $ALGoFolder -ItemType Directory | Out-Null + + # Create settings files + @{ "environments" = @( "settingsEnv1", "settingsEnv2" ) } | 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 + + # No settings variables + $ENV:ALGoOrgSettings = '' + $ENV:ALGoRepoSettings = '' + + # Repo only + $repoSettings = ReadSettings -baseFolder $tempName -project '' -repoName 'repo' -workflowName '' -branchName '' -userName '' -environmentName 'inputEnv1' -githubEnvironmentsJson $githubEnvironmentsJson + $deployToCount = @($repoSettings.Keys | Where-Object { $_ -like 'deployTo*' }).Count + $deployToCount | Should -Be 5 + } + + It 'DeployTo settings are not duplicated' { + Mock Write-Host { } + Mock Out-Host { } + + $githubEnvironmentsJson = @( @{ "name" = "env1" }, @{ "name" = "env2" } ) | ConvertTo-Json -Depth 99 + + Push-Location + $tempName = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString()) + $githubFolder = Join-Path $tempName ".github" + $ALGoFolder = Join-Path $tempName $ALGoFolderName + + New-Item $githubFolder -ItemType Directory | Out-Null + New-Item $ALGoFolder -ItemType Directory | Out-Null + + # Create settings files + @{ "environments" = @( "env1", "env3" ) } | 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 + + # No settings variables + $ENV:ALGoOrgSettings = '' + $ENV:ALGoRepoSettings = '' + + # Repo only + $repoSettings = ReadSettings -baseFolder $tempName -project '' -repoName 'repo' -workflowName '' -branchName '' -userName '' -environmentName 'env3' -githubEnvironmentsJson $githubEnvironmentsJson + $deployToCount = @($repoSettings.Keys | Where-Object { $_ -like 'deployTo*' }).Count + $deployToCount | Should -Be 3 + } } }