diff --git a/.github/actions/E2EAnalyze/E2EAnalyze.ps1 b/.github/actions/E2EAnalyze/E2EAnalyze.ps1 new file mode 100644 index 0000000000..0d0ed7d2e3 --- /dev/null +++ b/.github/actions/E2EAnalyze/E2EAnalyze.ps1 @@ -0,0 +1,151 @@ +Param( + [Parameter(HelpMessage = "Maximum parallel jobs", Mandatory = $true)] + [int] $maxParallel, + [Parameter(HelpMessage = "Test upgrades from version", Mandatory = $false)] + [string] $testUpgradesFromVersion = 'v5.0', + [Parameter(HelpMessage = "Filter to run specific scenarios (separated by comma, supports wildcards)", Mandatory = $false)] + [string] $scenariosFilter = '*' +) + +$ErrorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 + +<# +.SYNOPSIS +Filters the available E2E scenarios based on a comma-separated wildcard filter and a list of disabled scenarios. +.DESCRIPTION +Returns the scenarios that match at least one of the (comma separated, wildcard enabled) filter patterns and +that are not listed as disabled. The function is pure (no side effects other than host logging) so it can be +unit tested in isolation. +.PARAMETER allScenarios +The full list of available scenario names. +.PARAMETER scenariosFilter +Comma separated list of wildcard patterns. A scenario is included if it matches at least one pattern. Default is '*' (all). +.PARAMETER disabledScenariosConfig +Array of objects (as read from disabled-scenarios.json) each having a 'scenario' and an optional 'reason' property. +.EXAMPLE +Get-E2EScenariosToRun -allScenarios @('a','b','c') -scenariosFilter 'a*,b*' +#> +function Get-E2EScenariosToRun { + Param( + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [string[]] $allScenarios, + [Parameter(Mandatory = $false)] + [string] $scenariosFilter = '*', + [Parameter(Mandatory = $false)] + [AllowEmptyCollection()] + [array] $disabledScenariosConfig = @() + ) + + $scenariosFilterArr = @($scenariosFilter -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' }) + if ($scenariosFilterArr.Count -eq 0) { + $scenariosFilterArr = @('*') + } + # A scenario matches if it is -like at least one of the filter patterns + $filteredScenarios = @($allScenarios | Where-Object { $scenario = $_; @($scenariosFilterArr | Where-Object { $scenario -like $_ }).Count -gt 0 }) + + # Determine disabled scenarios from config (optional) + $disabledScenarios = @() + if ($disabledScenariosConfig -and @($disabledScenariosConfig).Count -gt 0) { + $disabledScenarios = @($disabledScenariosConfig | ForEach-Object { $_.scenario }) + } + Write-Host "Disabled scenarios from config: $($disabledScenarios -join ', ')" + + # Filter out disabled scenarios + $scenariosBeforeDisabledFilter = $filteredScenarios + $beforeFilter = $filteredScenarios.Count + $filteredScenarios = @($filteredScenarios | Where-Object { $disabledScenarios -notcontains $_ }) + $afterFilter = $filteredScenarios.Count + if ($beforeFilter -ne $afterFilter) { + Write-Host "Filtered out $($beforeFilter - $afterFilter) disabled scenario(s)" + $disabledScenariosConfig | Where-Object { ($scenariosBeforeDisabledFilter -contains $_.scenario) -and ($filteredScenarios -notcontains $_.scenario) } | ForEach-Object { + Write-Host " - $($_.scenario): $($_.reason)" + } + } + + return $filteredScenarios +} + +if ($MyInvocation.InvocationName -ne '.') { + $modulePath = Join-Path "." "e2eTests/e2eTestHelper.psm1" -resolve + Import-Module $modulePath -DisableNameChecking + + $publicTestruns = @{ + "max-parallel" = $maxParallel + "fail-fast" = $false + "matrix" = @{ + "include" = @() + } + } + $privateTestruns = @{ + "max-parallel" = $maxParallel + "fail-fast" = $false + "matrix" = @{ + "include" = @() + } + } + @('appSourceApp','PTE') | ForEach-Object { + $type = $_ + @('linux','windows') | ForEach-Object { + $os = $_ + @('multiProject','singleProject') | ForEach-Object { + $style = $_ + $publicTestruns.matrix.include += @{ "type" = $type; "os" = $os; "style" = $style; "Compiler" = "Container" } + $privateTestruns.matrix.include += @{ "type" = $type; "os" = $os; "style" = $style; "Compiler" = "Container" } + if ($type -eq "PTE") { + # Run end 2 end tests using CompilerFolder with Windows+Linux and single/multiproject + $publicTestruns.matrix.include += @{ "type" = $type; "os" = $os; "style" = $style; "Compiler" = "CompilerFolder" } + } + } + } + } + $publicTestrunsJson = $publicTestruns | ConvertTo-Json -depth 99 -compress + $privateTestrunsJson = $privateTestruns | ConvertTo-Json -depth 99 -compress + Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "publictestruns=$publicTestrunsJson" + Write-Host "publictestruns=$publicTestrunsJson" + Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "privatetestruns=$privateTestrunsJson" + Write-Host "privatetestruns=$privateTestrunsJson" + + $releaseList = @(gh release list --repo microsoft/AL-Go) + if ($LASTEXITCODE -ne 0) { + throw "Failed to retrieve releases from microsoft/AL-Go. 'gh release list' exited with code $LASTEXITCODE." + } + $releases = @($releaseList | ForEach-Object { $_.split("`t")[0] }) | Where-Object { [Version]($_.trimStart('v')) -ge [Version]($testUpgradesFromVersion.TrimStart('v')) } + $releasesJson = @{ + "matrix" = @{ + "include" = @($releases | ForEach-Object { @{ "Release" = $_; "type" = 'appSourceApp' }; @{ "Release" = $_; "type" = 'PTE' } } ) + }; + "max-parallel" = $maxParallel + "fail-fast" = $false + } | ConvertTo-Json -depth 99 -compress + Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "releases=$releasesJson" + Write-Host "releases=$releasesJson" + + $allScenarios = @(Get-ChildItem -Path (Join-Path $ENV:GITHUB_WORKSPACE "e2eTests/scenarios/*/runtest.ps1") | ForEach-Object { $_.Directory.Name }) + + # Load disabled scenarios from config file (optional) + $disabledScenariosConfigPath = Join-Path $ENV:GITHUB_WORKSPACE "e2eTests/disabled-scenarios.json" + $disabledScenariosConfig = @() + if (Test-Path -Path $disabledScenariosConfigPath) { + $disabledScenariosContent = Get-Content -Path $disabledScenariosConfigPath -Encoding UTF8 -Raw + if (-not [string]::IsNullOrWhiteSpace($disabledScenariosContent)) { + $disabledScenariosConfig = @($disabledScenariosContent | ConvertFrom-Json | ConvertTo-HashTable -recurse) + } + } + else { + Write-Host "No disabled-scenarios.json found; proceeding with all scenarios enabled." + } + + $filteredScenarios = @(Get-E2EScenariosToRun -allScenarios $allScenarios -scenariosFilter $scenariosFilter -disabledScenariosConfig $disabledScenariosConfig) + Write-Host "Scenarios to run: $($filteredScenarios -join ', ')" + + $scenariosJson = @{ + "matrix" = @{ + "include" = @($filteredScenarios | ForEach-Object { @{ "Scenario" = $_ } }) + }; + "max-parallel" = $maxParallel + "fail-fast" = $false + } | ConvertTo-Json -depth 99 -compress + Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "scenarios=$scenariosJson" + Write-Host "scenarios=$scenariosJson" +} diff --git a/.github/actions/E2EAnalyze/README.md b/.github/actions/E2EAnalyze/README.md new file mode 100644 index 0000000000..31ca272e37 --- /dev/null +++ b/.github/actions/E2EAnalyze/README.md @@ -0,0 +1,17 @@ +# E2E Analyze + +Analyzes and generates test matrices for E2E testing including public/private test runs, releases, and scenarios. + +## Inputs + +- `maxParallel`: Maximum parallel jobs +- `testUpgradesFromVersion`: Test upgrades from version (default: 'v5.0') +- `scenariosFilter`: Filter to run specific scenarios, separated by comma, supports wildcards (default: '\*') +- `token`: GitHub token with permissions to read releases + +## Outputs + +- `publictestruns`: Public test runs matrix +- `privatetestruns`: Private test runs matrix +- `releases`: Releases matrix +- `scenarios`: Scenarios matrix diff --git a/.github/actions/E2EAnalyze/action.yaml b/.github/actions/E2EAnalyze/action.yaml new file mode 100644 index 0000000000..f05bd6da60 --- /dev/null +++ b/.github/actions/E2EAnalyze/action.yaml @@ -0,0 +1,50 @@ +name: E2E Analyze +author: Microsoft Corporation +inputs: + shell: + description: Shell in which you want to run the action (powershell or pwsh) + required: false + default: pwsh + maxParallel: + description: Maximum parallel jobs + required: true + testUpgradesFromVersion: + description: Test upgrades from version + required: false + default: 'v5.0' + scenariosFilter: + description: Filter to run specific scenarios (separated by comma, supports wildcards) + required: false + default: '*' + token: + description: GitHub token with permissions to read releases + required: true +outputs: + publictestruns: + description: Public test runs matrix + value: ${{ steps.run.outputs.publictestruns }} + privatetestruns: + description: Private test runs matrix + value: ${{ steps.run.outputs.privatetestruns }} + releases: + description: Releases matrix + value: ${{ steps.run.outputs.releases }} + scenarios: + description: Scenarios matrix + value: ${{ steps.run.outputs.scenarios }} +runs: + using: composite + steps: + - name: run + id: run + shell: ${{ inputs.shell }} + env: + _maxParallel: ${{ inputs.maxParallel }} + _testUpgradesFromVersion: ${{ inputs.testUpgradesFromVersion }} + _scenariosFilter: ${{ inputs.scenariosFilter }} + GH_TOKEN: ${{ inputs.token }} + run: | + ${{ github.action_path }}/E2EAnalyze.ps1 -maxParallel ([int]$ENV:_maxParallel) -testUpgradesFromVersion $ENV:_testUpgradesFromVersion -scenariosFilter $ENV:_scenariosFilter +branding: + icon: activity + color: blue diff --git a/.github/actions/E2ECalculateRepoName/E2ECalculateRepoName.ps1 b/.github/actions/E2ECalculateRepoName/E2ECalculateRepoName.ps1 new file mode 100644 index 0000000000..9c5c7bfa8d --- /dev/null +++ b/.github/actions/E2ECalculateRepoName/E2ECalculateRepoName.ps1 @@ -0,0 +1,12 @@ +Param( + [Parameter(HelpMessage = "GitHub owner for test repositories", Mandatory = $false)] + [string] $githubOwner = '' +) + +$ErrorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 +$reponame = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetTempFileName()) +Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "repoName=$repoName" +Write-Host "repoName=$repoName" +if ($githubOwner) { + Write-Host "Repo URL: https://github.com/$githubOwner/$repoName" +} diff --git a/.github/actions/E2ECalculateRepoName/README.md b/.github/actions/E2ECalculateRepoName/README.md new file mode 100644 index 0000000000..28be7d2702 --- /dev/null +++ b/.github/actions/E2ECalculateRepoName/README.md @@ -0,0 +1,11 @@ +# E2E Calculate Repo Name + +Generates a random repository name for E2E testing. + +## Inputs + +- `githubOwner`: GitHub owner for test repositories (optional, for logging purposes) + +## Outputs + +- `repoName`: Generated repository name diff --git a/.github/actions/E2ECalculateRepoName/action.yaml b/.github/actions/E2ECalculateRepoName/action.yaml new file mode 100644 index 0000000000..fd94339b81 --- /dev/null +++ b/.github/actions/E2ECalculateRepoName/action.yaml @@ -0,0 +1,28 @@ +name: E2E Calculate Repo Name +author: Microsoft Corporation +inputs: + shell: + description: Shell in which you want to run the action (powershell or pwsh) + required: false + default: pwsh + githubOwner: + description: GitHub owner for test repositories (optional, for logging purposes) + required: false + default: '' +outputs: + repoName: + description: Generated repository name + value: ${{ steps.run.outputs.repoName }} +runs: + using: composite + steps: + - name: run + id: run + shell: ${{ inputs.shell }} + env: + _githubOwner: ${{ inputs.githubOwner }} + run: | + ${{ github.action_path }}/E2ECalculateRepoName.ps1 -githubOwner $ENV:_githubOwner +branding: + icon: hash + color: blue diff --git a/.github/actions/E2ECalculateTestParams/E2ECalculateTestParams.ps1 b/.github/actions/E2ECalculateTestParams/E2ECalculateTestParams.ps1 new file mode 100644 index 0000000000..da1c7dc38c --- /dev/null +++ b/.github/actions/E2ECalculateTestParams/E2ECalculateTestParams.ps1 @@ -0,0 +1,130 @@ +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '', Justification = 'GitHub Secrets are transferred as plain text')] +Param( + [Parameter(HelpMessage = "GitHub owner for test repositories", Mandatory = $true)] + [string] $githubOwner, + [Parameter(HelpMessage = "Matrix type (PTE or appSourceApp)", Mandatory = $false)] + [string] $matrixType = '', + [Parameter(HelpMessage = "Matrix style (singleProject or multiProject)", Mandatory = $false)] + [string] $matrixStyle = '', + [Parameter(HelpMessage = "Matrix OS (windows or linux)", Mandatory = $false)] + [string] $matrixOs = '', + [Parameter(HelpMessage = "Admin center API credentials secret", Mandatory = $false)] + [string] $adminCenterApiCredentialsSecret = '', + [Parameter(HelpMessage = "AppSource app repository template", Mandatory = $true)] + [string] $appSourceAppRepo, + [Parameter(HelpMessage = "Per-tenant extension repository template", Mandatory = $true)] + [string] $perTenantExtensionRepo, + [Parameter(HelpMessage = "Content path (for upgrade tests)", Mandatory = $false)] + [string] $contentPath = '' +) + +$ErrorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 + +<# +.SYNOPSIS +Calculates the template, contentPath and adminCenterApiCredentials for an E2E test run based on the matrix cell. +.DESCRIPTION +Given the matrix coordinates (type/style/os) and the repository templates, this function returns the parameters +used to run a single E2E test. It is pure (no side effects) so it can be unit tested in isolation: +- adminCenterApiCredentials is only forwarded for the PTE / singleProject / windows cell. +- template is derived from the matrix type and the corresponding repository template. +- contentPath defaults to 'appsourceapp' or 'pte' when not explicitly provided (used by upgrade tests). +.PARAMETER githubOwner +The GitHub owner that hosts the temporary test repositories. +.PARAMETER matrixType +The matrix type, either 'appSourceApp' or 'PTE'. +.PARAMETER matrixStyle +The matrix style, either 'singleProject' or 'multiProject'. +.PARAMETER matrixOs +The matrix operating system, either 'windows' or 'linux'. +.PARAMETER adminCenterApiCredentialsSecret +The admin center API credentials secret, only forwarded for the PTE / singleProject / windows cell. +.PARAMETER appSourceAppRepo +The AppSource app repository template name. +.PARAMETER perTenantExtensionRepo +The per-tenant extension (PTE) repository template name. +.PARAMETER contentPath +Optional explicit content path (for upgrade tests). Defaulted based on matrixType when empty. +.EXAMPLE +Get-E2ECalculatedTestParams -githubOwner 'contoso' -matrixType 'PTE' -matrixStyle 'singleProject' -matrixOs 'windows' -appSourceAppRepo 'appsource' -perTenantExtensionRepo 'pte' +#> +function Get-E2ECalculatedTestParams { + Param( + [Parameter(Mandatory = $false)] + [string] $githubOwner = '', + [Parameter(Mandatory = $false)] + [string] $matrixType = '', + [Parameter(Mandatory = $false)] + [string] $matrixStyle = '', + [Parameter(Mandatory = $false)] + [string] $matrixOs = '', + [Parameter(Mandatory = $false)] + [string] $adminCenterApiCredentialsSecret = '', + [Parameter(Mandatory = $false)] + [string] $appSourceAppRepo = '', + [Parameter(Mandatory = $false)] + [string] $perTenantExtensionRepo = '', + [Parameter(Mandatory = $false)] + [string] $contentPath = '' + ) + + # Calculate adminCenterApiCredentials (only used for the PTE / singleProject / windows cell) + $adminCenterApiCredentials = '' + if ($matrixType -eq 'PTE' -and $matrixStyle -eq 'singleProject' -and $matrixOs -eq 'windows') { + $adminCenterApiCredentials = $adminCenterApiCredentialsSecret + } + + # Calculate template + $template = '' + if ($matrixType -eq 'appSourceApp') { + $template = "$githubOwner/$appSourceAppRepo" + } + elseif ($matrixType -eq 'PTE') { + $template = "$githubOwner/$perTenantExtensionRepo" + } + + # Calculate contentPath if not provided + if (-not $contentPath -and $matrixType) { + if ($matrixType -eq 'appSourceApp') { + $contentPath = 'appsourceapp' + } + else { + $contentPath = 'pte' + } + } + + return @{ + adminCenterApiCredentials = $adminCenterApiCredentials + template = $template + contentPath = $contentPath + } +} + +if ($MyInvocation.InvocationName -ne '.') { + $testParams = Get-E2ECalculatedTestParams ` + -githubOwner $githubOwner ` + -matrixType $matrixType ` + -matrixStyle $matrixStyle ` + -matrixOs $matrixOs ` + -adminCenterApiCredentialsSecret $adminCenterApiCredentialsSecret ` + -appSourceAppRepo $appSourceAppRepo ` + -perTenantExtensionRepo $perTenantExtensionRepo ` + -contentPath $contentPath + + # Add outputs + Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "adminCenterApiCredentials=$($testParams.adminCenterApiCredentials)" + + if ($testParams.template) { + Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "template=$($testParams.template)" + } + + if ($testParams.contentPath) { + Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "contentPath=$($testParams.contentPath)" + } + + # Generate repo name + $repoName = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetTempFileName()) + Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "repoName=$repoName" + Write-Host "repoName=$repoName" + Write-Host "Repo URL: https://github.com/$githubOwner/$repoName" +} diff --git a/.github/actions/E2ECalculateTestParams/README.md b/.github/actions/E2ECalculateTestParams/README.md new file mode 100644 index 0000000000..e9a5aadf30 --- /dev/null +++ b/.github/actions/E2ECalculateTestParams/README.md @@ -0,0 +1,21 @@ +# E2E Calculate Test Parameters + +Calculates test parameters including template repository, admin center credentials, and repository name based on matrix configuration. + +## Inputs + +- `githubOwner`: GitHub owner for test repositories +- `matrixType`: Matrix type (PTE or appSourceApp) +- `matrixStyle`: Matrix style (singleProject or multiProject) +- `matrixOs`: Matrix OS (windows or linux) +- `adminCenterApiCredentialsSecret`: Admin center API credentials secret +- `appSourceAppRepo`: AppSource app repository template +- `perTenantExtensionRepo`: Per-tenant extension repository template +- `contentPath`: Content path (for upgrade tests) + +## Outputs + +- `adminCenterApiCredentials`: Calculated admin center API credentials +- `template`: Calculated template repository +- `repoName`: Generated repository name +- `contentPath`: Content path (for upgrade tests) diff --git a/.github/actions/E2ECalculateTestParams/action.yaml b/.github/actions/E2ECalculateTestParams/action.yaml new file mode 100644 index 0000000000..9305f7b49b --- /dev/null +++ b/.github/actions/E2ECalculateTestParams/action.yaml @@ -0,0 +1,69 @@ +name: E2E Calculate Test Parameters +author: Microsoft Corporation +inputs: + shell: + description: Shell in which you want to run the action (powershell or pwsh) + required: false + default: pwsh + githubOwner: + description: GitHub owner for test repositories + required: true + matrixType: + description: Matrix type (PTE or appSourceApp) + required: false + default: '' + matrixStyle: + description: Matrix style (singleProject or multiProject) + required: false + default: '' + matrixOs: + description: Matrix OS (windows or linux) + required: false + default: '' + adminCenterApiCredentialsSecret: + description: Admin center API credentials secret + required: false + default: '' + appSourceAppRepo: + description: AppSource app repository template + required: true + perTenantExtensionRepo: + description: Per-tenant extension repository template + required: true + contentPath: + description: Content path (for upgrade tests) + required: false + default: '' +outputs: + adminCenterApiCredentials: + description: Calculated admin center API credentials + value: ${{ steps.run.outputs.adminCenterApiCredentials }} + template: + description: Calculated template repository + value: ${{ steps.run.outputs.template }} + repoName: + description: Generated repository name + value: ${{ steps.run.outputs.repoName }} + contentPath: + description: Content path (for upgrade tests) + value: ${{ steps.run.outputs.contentPath }} +runs: + using: composite + steps: + - name: run + id: run + shell: ${{ inputs.shell }} + env: + _githubOwner: ${{ inputs.githubOwner }} + _matrixType: ${{ inputs.matrixType }} + _matrixStyle: ${{ inputs.matrixStyle }} + _matrixOs: ${{ inputs.matrixOs }} + _adminCenterApiCredentialsSecret: ${{ inputs.adminCenterApiCredentialsSecret }} + _appSourceAppRepo: ${{ inputs.appSourceAppRepo }} + _perTenantExtensionRepo: ${{ inputs.perTenantExtensionRepo }} + _contentPath: ${{ inputs.contentPath }} + run: | + ${{ github.action_path }}/E2ECalculateTestParams.ps1 -githubOwner $ENV:_githubOwner -matrixType $ENV:_matrixType -matrixStyle $ENV:_matrixStyle -matrixOs $ENV:_matrixOs -adminCenterApiCredentialsSecret $ENV:_adminCenterApiCredentialsSecret -appSourceAppRepo $ENV:_appSourceAppRepo -perTenantExtensionRepo $ENV:_perTenantExtensionRepo -contentPath $ENV:_contentPath +branding: + icon: settings + color: blue diff --git a/.github/actions/E2ECheckSecrets/E2ECheckSecrets.ps1 b/.github/actions/E2ECheckSecrets/E2ECheckSecrets.ps1 new file mode 100644 index 0000000000..6cf46c997a --- /dev/null +++ b/.github/actions/E2ECheckSecrets/E2ECheckSecrets.ps1 @@ -0,0 +1,61 @@ +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '', Justification = 'GitHub Secrets are transferred as plain text')] +Param( + [Parameter(HelpMessage = "GitHub owner (defaults to current repository owner)", Mandatory = $false)] + [string] $githubOwner = '', + [Parameter(HelpMessage = "E2E_APP_ID variable value", Mandatory = $false)] + [string] $e2eAppId = '', + [Parameter(HelpMessage = "E2E_PRIVATE_KEY secret value", Mandatory = $false)] + [string] $e2ePrivateKey = '', + [Parameter(HelpMessage = "ALGOAUTHAPP secret value", Mandatory = $false)] + [string] $algoAuthApp = '', + [Parameter(HelpMessage = "adminCenterApiCredentials secret value", Mandatory = $false)] + [string] $adminCenterApiCredentials = '', + [Parameter(HelpMessage = "E2E_GHPackagesPAT secret value", Mandatory = $false)] + [string] $e2eGHPackagesPAT = '', + [Parameter(HelpMessage = "E2EAZURECREDENTIALS secret value", Mandatory = $false)] + [string] $e2eAzureCredentials = '' +) + +$ErrorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 + +. (Join-Path $PSScriptRoot "../../../Actions/AL-Go-Helper.ps1" -Resolve) + +$err = $false +if (($e2eAppId -eq '') -or ($e2ePrivateKey -eq '')){ + Write-Host "::Error::In order to run end to end tests, you need a Secret called E2E_PRIVATE_KEY and a variable called E2E_APP_ID." + $err = $true +} +if ($algoAuthApp -eq '') { + Write-Host "::Error::In order to run end to end tests, you need a Secret called ALGOAUTHAPP" + $err = $true +} +if ($adminCenterApiCredentials -eq '') { + Write-Host "::Error::In order to run end to end tests, you need a Secret called adminCenterApiCredentials" + $err = $true +} +if ($e2eGHPackagesPAT -eq '') { + Write-Host "::Error::In order to run end to end tests, you need a secret called E2E_GHPackagesPAT" + $err = $true +} +if ($e2eAzureCredentials -eq '') { + Write-Host "::Error::In order to run end to end tests, you need a secret called E2EAZURECREDENTIALS" + $err = $true +} +if ($err) { + exit 1 +} +$maxParallel = 99 +if (!($githubOwner)) { + $githubOwner = $ENV:GITHUB_REPOSITORY_OWNER +} +$orgmap = Get-Content -path (Join-Path "." "e2eTests/orgmap.json") -encoding UTF8 -raw | ConvertFrom-Json | ConvertTo-HashTable -recurse +if ($orgmap.Keys -contains $githubOwner) { + $githubOwner = $orgmap[$githubOwner] +} +if ($githubOwner -eq $ENV:GITHUB_REPOSITORY_OWNER) { + $maxParallel = 8 +} +Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "maxParallel=$maxParallel" +Write-Host "maxParallel=$maxParallel" +Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "githubOwner=$githubOwner" +Write-Host "githubOwner=$githubOwner" diff --git a/.github/actions/E2ECheckSecrets/README.md b/.github/actions/E2ECheckSecrets/README.md new file mode 100644 index 0000000000..23111251c8 --- /dev/null +++ b/.github/actions/E2ECheckSecrets/README.md @@ -0,0 +1,18 @@ +# E2E Check Secrets + +Validates that all required secrets and variables are configured for E2E testing. + +## Inputs + +- `githubOwner`: GitHub owner (defaults to current repository owner) +- `e2eAppId`: E2E_APP_ID variable value +- `e2ePrivateKey`: E2E_PRIVATE_KEY secret value +- `algoAuthApp`: ALGOAUTHAPP secret value +- `adminCenterApiCredentials`: adminCenterApiCredentials secret value +- `e2eGHPackagesPAT`: E2E_GHPackagesPAT secret value +- `e2eAzureCredentials`: E2EAZURECREDENTIALS secret value + +## Outputs + +- `maxParallel`: Maximum number of parallel jobs +- `githubOwner`: GitHub owner for test repositories diff --git a/.github/actions/E2ECheckSecrets/action.yaml b/.github/actions/E2ECheckSecrets/action.yaml new file mode 100644 index 0000000000..806a1da559 --- /dev/null +++ b/.github/actions/E2ECheckSecrets/action.yaml @@ -0,0 +1,61 @@ +name: E2E Check Secrets +author: Microsoft Corporation +inputs: + shell: + description: Shell in which you want to run the action (powershell or pwsh) + required: false + default: pwsh + githubOwner: + description: GitHub owner (defaults to current repository owner) + required: false + default: '' + e2eAppId: + description: E2E_APP_ID variable value + required: false + default: '' + e2ePrivateKey: + description: E2E_PRIVATE_KEY secret value + required: false + default: '' + algoAuthApp: + description: ALGOAUTHAPP secret value + required: false + default: '' + adminCenterApiCredentials: + description: adminCenterApiCredentials secret value + required: false + default: '' + e2eGHPackagesPAT: + description: E2E_GHPackagesPAT secret value + required: false + default: '' + e2eAzureCredentials: + description: E2EAZURECREDENTIALS secret value + required: false + default: '' +outputs: + maxParallel: + description: Maximum number of parallel jobs + value: ${{ steps.run.outputs.maxParallel }} + githubOwner: + description: GitHub owner for test repositories + value: ${{ steps.run.outputs.githubOwner }} +runs: + using: composite + steps: + - name: run + id: run + shell: ${{ inputs.shell }} + env: + _githubOwner: ${{ inputs.githubOwner }} + _e2eAppId: ${{ inputs.e2eAppId }} + _e2ePrivateKey: ${{ inputs.e2ePrivateKey }} + _algoAuthApp: ${{ inputs.algoAuthApp }} + _adminCenterApiCredentials: ${{ inputs.adminCenterApiCredentials }} + _e2eGHPackagesPAT: ${{ inputs.e2eGHPackagesPAT }} + _e2eAzureCredentials: ${{ inputs.e2eAzureCredentials }} + run: | + ${{ github.action_path }}/E2ECheckSecrets.ps1 -githubOwner $ENV:_githubOwner -e2eAppId $ENV:_e2eAppId -e2ePrivateKey $ENV:_e2ePrivateKey -algoAuthApp $ENV:_algoAuthApp -adminCenterApiCredentials $ENV:_adminCenterApiCredentials -e2eGHPackagesPAT $ENV:_e2eGHPackagesPAT -e2eAzureCredentials $ENV:_e2eAzureCredentials +branding: + icon: check-circle + color: blue diff --git a/.github/actions/E2ERunScenario/E2ERunScenario.ps1 b/.github/actions/E2ERunScenario/E2ERunScenario.ps1 new file mode 100644 index 0000000000..c4c9cc2d6d --- /dev/null +++ b/.github/actions/E2ERunScenario/E2ERunScenario.ps1 @@ -0,0 +1,57 @@ +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '', Justification = 'GitHub Secrets are transferred as plain text')] +Param( + [Parameter(HelpMessage = "Scenario name", Mandatory = $true)] + [string] $scenario, + [Parameter(HelpMessage = "Run on Linux", Mandatory = $false)] + [bool] $linux = $false, + [Parameter(HelpMessage = "GitHub owner", Mandatory = $true)] + [string] $githubOwner, + [Parameter(HelpMessage = "Repository name", Mandatory = $true)] + [string] $repoName, + [Parameter(HelpMessage = "E2E App ID", Mandatory = $true)] + [string] $e2eAppId, + [Parameter(HelpMessage = "E2E App Key", Mandatory = $true)] + [string] $e2eAppKey, + [Parameter(HelpMessage = "ALGO Auth App", Mandatory = $true)] + [string] $algoAuthApp, + [Parameter(HelpMessage = "PTE template", Mandatory = $true)] + [string] $pteTemplate, + [Parameter(HelpMessage = "AppSource template", Mandatory = $true)] + [string] $appSourceTemplate, + [Parameter(HelpMessage = "Admin center API credentials", Mandatory = $true)] + [string] $adminCenterApiCredentials, + [Parameter(HelpMessage = "Azure credentials", Mandatory = $true)] + [string] $azureCredentials, + [Parameter(HelpMessage = "GitHub packages token", Mandatory = $true)] + [string] $githubPackagesToken +) + +$ErrorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 + +try { + $params = @{ + 'github' = $true + 'githubOwner' = $githubOwner + 'repoName' = $repoName + 'e2eAppId' = $e2eAppId + 'e2eAppKey' = $e2eAppKey + 'algoauthapp' = $algoAuthApp + 'pteTemplate' = $pteTemplate + 'appSourceTemplate' = $appSourceTemplate + 'adminCenterApiCredentials' = $adminCenterApiCredentials + 'azureCredentials' = $azureCredentials + 'githubPackagesToken' = $githubPackagesToken + } + + if ($linux) { + $params['linux'] = $true + } + + . (Join-Path "." "e2eTests/scenarios/$scenario/runtest.ps1") @params +} +catch { + Write-Host $_.Exception.Message + Write-Host $_.ScriptStackTrace + Write-Host "::Error::$($_.Exception.Message)" + $host.SetShouldExit(1) +} diff --git a/.github/actions/E2ERunScenario/README.md b/.github/actions/E2ERunScenario/README.md new file mode 100644 index 0000000000..16b474c434 --- /dev/null +++ b/.github/actions/E2ERunScenario/README.md @@ -0,0 +1,18 @@ +# E2E Run Scenario + +Runs E2E scenario tests by executing scenario-specific test scripts. + +## Inputs + +- `scenario`: Scenario name +- `linux`: Run on Linux (default: false) +- `githubOwner`: GitHub owner +- `repoName`: Repository name +- `e2eAppId`: E2E App ID +- `e2eAppKey`: E2E App Key +- `algoAuthApp`: ALGO Auth App +- `pteTemplate`: PTE template +- `appSourceTemplate`: AppSource template +- `adminCenterApiCredentials`: Admin center API credentials +- `azureCredentials`: Azure credentials +- `githubPackagesToken`: GitHub packages token diff --git a/.github/actions/E2ERunScenario/action.yaml b/.github/actions/E2ERunScenario/action.yaml new file mode 100644 index 0000000000..ffe937c907 --- /dev/null +++ b/.github/actions/E2ERunScenario/action.yaml @@ -0,0 +1,68 @@ +name: E2E Run Scenario +author: Microsoft Corporation +inputs: + shell: + description: Shell in which you want to run the action (powershell or pwsh) + required: false + default: pwsh + scenario: + description: Scenario name + required: true + linux: + description: Run on Linux + required: false + default: 'false' + githubOwner: + description: GitHub owner + required: true + repoName: + description: Repository name + required: true + e2eAppId: + description: E2E App ID + required: true + e2eAppKey: + description: E2E App Key + required: true + algoAuthApp: + description: ALGO Auth App + required: true + pteTemplate: + description: PTE template + required: true + appSourceTemplate: + description: AppSource template + required: true + adminCenterApiCredentials: + description: Admin center API credentials + required: true + azureCredentials: + description: Azure credentials + required: true + githubPackagesToken: + description: GitHub packages token + required: true +runs: + using: composite + steps: + - name: run + id: run + shell: ${{ inputs.shell }} + env: + _scenario: ${{ inputs.scenario }} + _linux: ${{ inputs.linux }} + _githubOwner: ${{ inputs.githubOwner }} + _repoName: ${{ inputs.repoName }} + _e2eAppId: ${{ inputs.e2eAppId }} + _e2eAppKey: ${{ inputs.e2eAppKey }} + _algoAuthApp: ${{ inputs.algoAuthApp }} + _pteTemplate: ${{ inputs.pteTemplate }} + _appSourceTemplate: ${{ inputs.appSourceTemplate }} + _adminCenterApiCredentials: ${{ inputs.adminCenterApiCredentials }} + _azureCredentials: ${{ inputs.azureCredentials }} + _githubPackagesToken: ${{ inputs.githubPackagesToken }} + run: | + ${{ github.action_path }}/E2ERunScenario.ps1 -scenario $ENV:_scenario -linux ($ENV:_linux -eq 'true') -githubOwner $ENV:_githubOwner -repoName $ENV:_repoName -e2eAppId $ENV:_e2eAppId -e2eAppKey $ENV:_e2eAppKey -algoAuthApp $ENV:_algoAuthApp -pteTemplate $ENV:_pteTemplate -appSourceTemplate $ENV:_appSourceTemplate -adminCenterApiCredentials $ENV:_adminCenterApiCredentials -azureCredentials $ENV:_azureCredentials -githubPackagesToken $ENV:_githubPackagesToken +branding: + icon: play + color: blue diff --git a/.github/actions/E2ERunTest/E2ERunTest.ps1 b/.github/actions/E2ERunTest/E2ERunTest.ps1 new file mode 100644 index 0000000000..599ac37236 --- /dev/null +++ b/.github/actions/E2ERunTest/E2ERunTest.ps1 @@ -0,0 +1,87 @@ +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '', Justification = 'GitHub Secrets are transferred as plain text')] +Param( + [Parameter(HelpMessage = "Test type (test or upgrade)", Mandatory = $false)] + [ValidateSet('test', 'upgrade')] + [string] $testType = 'test', + [Parameter(HelpMessage = "Private repository", Mandatory = $false)] + [bool] $private = $false, + [Parameter(HelpMessage = "GitHub owner", Mandatory = $true)] + [string] $githubOwner, + [Parameter(HelpMessage = "Repository name", Mandatory = $true)] + [string] $repoName, + [Parameter(HelpMessage = "E2E App ID", Mandatory = $true)] + [string] $e2eAppId, + [Parameter(HelpMessage = "E2E App Key", Mandatory = $true)] + [string] $e2eAppKey, + [Parameter(HelpMessage = "ALGO Auth App", Mandatory = $true)] + [string] $algoAuthApp, + [Parameter(HelpMessage = "Template", Mandatory = $true)] + [string] $template, + [Parameter(HelpMessage = "Admin center API credentials", Mandatory = $false)] + [string] $adminCenterApiCredentials = '', + [Parameter(HelpMessage = "Multi-project", Mandatory = $false)] + [bool] $multiProject = $false, + [Parameter(HelpMessage = "AppSource app", Mandatory = $false)] + [bool] $appSource = $false, + [Parameter(HelpMessage = "Linux", Mandatory = $false)] + [bool] $linux = $false, + [Parameter(HelpMessage = "Use compiler folder", Mandatory = $false)] + [bool] $useCompilerFolder = $false, + [Parameter(HelpMessage = "Release (for upgrade tests)", Mandatory = $false)] + [string] $release = '', + [Parameter(HelpMessage = "Content path (for upgrade tests)", Mandatory = $false)] + [string] $contentPath = '' +) + +$ErrorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 + +try { + if ($testType -eq 'upgrade') { + $params = @{ + 'github' = $true + 'githubOwner' = $githubOwner + 'repoName' = $repoName + 'e2eAppId' = $e2eAppId + 'e2eAppKey' = $e2eAppKey + 'algoauthapp' = $algoAuthApp + 'template' = $template + 'appSourceApp' = $appSource + 'release' = $release + 'contentPath' = $contentPath + } + + if ($private) { + $params['private'] = $true + } + + . (Join-Path "." "e2eTests/Test-AL-Go-Upgrade.ps1") @params + } + else { + $params = @{ + 'github' = $true + 'githubOwner' = $githubOwner + 'repoName' = $repoName + 'e2eAppId' = $e2eAppId + 'e2eAppKey' = $e2eAppKey + 'algoauthapp' = $algoAuthApp + 'template' = $template + 'adminCenterApiCredentials' = $adminCenterApiCredentials + 'multiProject' = $multiProject + 'appSourceApp' = $appSource + 'linux' = $linux + 'useCompilerFolder' = $useCompilerFolder + } + + if ($private) { + $params['private'] = $true + } + + . (Join-Path "." "e2eTests/Test-AL-Go.ps1") @params + } +} +catch { + Write-Host $_.Exception.Message + Write-Host $_.ScriptStackTrace + Write-Host "::Error::$($_.Exception.Message)" + $host.SetShouldExit(1) +} diff --git a/.github/actions/E2ERunTest/README.md b/.github/actions/E2ERunTest/README.md new file mode 100644 index 0000000000..6fe298599c --- /dev/null +++ b/.github/actions/E2ERunTest/README.md @@ -0,0 +1,21 @@ +# E2E Run Test + +Runs E2E tests by executing Test-AL-Go.ps1 or Test-AL-Go-Upgrade.ps1 scripts. + +## Inputs + +- `testType`: Test type (test or upgrade, default: test) +- `private`: Private repository (default: false) +- `githubOwner`: GitHub owner +- `repoName`: Repository name +- `e2eAppId`: E2E App ID +- `e2eAppKey`: E2E App Key +- `algoAuthApp`: ALGO Auth App +- `template`: Template +- `adminCenterApiCredentials`: Admin center API credentials +- `multiProject`: Multi-project (default: false) +- `appSource`: AppSource app (default: false) +- `linux`: Linux (default: false) +- `useCompilerFolder`: Use compiler folder (default: false) +- `release`: Release (for upgrade tests) +- `contentPath`: Content path (for upgrade tests) diff --git a/.github/actions/E2ERunTest/action.yaml b/.github/actions/E2ERunTest/action.yaml new file mode 100644 index 0000000000..99aa277cce --- /dev/null +++ b/.github/actions/E2ERunTest/action.yaml @@ -0,0 +1,88 @@ +name: E2E Run Test +author: Microsoft Corporation +inputs: + shell: + description: Shell in which you want to run the action (powershell or pwsh) + required: false + default: pwsh + testType: + description: Test type (test or upgrade) + required: false + default: 'test' + private: + description: Private repository + required: false + default: 'false' + githubOwner: + description: GitHub owner + required: true + repoName: + description: Repository name + required: true + e2eAppId: + description: E2E App ID + required: true + e2eAppKey: + description: E2E App Key + required: true + algoAuthApp: + description: ALGO Auth App + required: true + template: + description: Template + required: true + adminCenterApiCredentials: + description: Admin center API credentials + required: false + default: '' + multiProject: + description: Multi-project + required: false + default: 'false' + appSource: + description: AppSource app + required: false + default: 'false' + linux: + description: Linux + required: false + default: 'false' + useCompilerFolder: + description: Use compiler folder + required: false + default: 'false' + release: + description: Release (for upgrade tests) + required: false + default: '' + contentPath: + description: Content path (for upgrade tests) + required: false + default: '' +runs: + using: composite + steps: + - name: run + id: run + shell: ${{ inputs.shell }} + env: + _testType: ${{ inputs.testType }} + _private: ${{ inputs.private }} + _githubOwner: ${{ inputs.githubOwner }} + _repoName: ${{ inputs.repoName }} + _e2eAppId: ${{ inputs.e2eAppId }} + _e2eAppKey: ${{ inputs.e2eAppKey }} + _algoAuthApp: ${{ inputs.algoAuthApp }} + _template: ${{ inputs.template }} + _adminCenterApiCredentials: ${{ inputs.adminCenterApiCredentials }} + _multiProject: ${{ inputs.multiProject }} + _appSource: ${{ inputs.appSource }} + _linux: ${{ inputs.linux }} + _useCompilerFolder: ${{ inputs.useCompilerFolder }} + _release: ${{ inputs.release }} + _contentPath: ${{ inputs.contentPath }} + run: | + ${{ github.action_path }}/E2ERunTest.ps1 -testType $ENV:_testType -private ($ENV:_private -eq 'true') -githubOwner $ENV:_githubOwner -repoName $ENV:_repoName -e2eAppId $ENV:_e2eAppId -e2eAppKey $ENV:_e2eAppKey -algoAuthApp $ENV:_algoAuthApp -template $ENV:_template -adminCenterApiCredentials $ENV:_adminCenterApiCredentials -multiProject ($ENV:_multiProject -eq 'true') -appSource ($ENV:_appSource -eq 'true') -linux ($ENV:_linux -eq 'true') -useCompilerFolder ($ENV:_useCompilerFolder -eq 'true') -release $ENV:_release -contentPath $ENV:_contentPath +branding: + icon: check-square + color: blue diff --git a/.github/actions/E2ESetupRepositories/E2ESetupRepositories.ps1 b/.github/actions/E2ESetupRepositories/E2ESetupRepositories.ps1 new file mode 100644 index 0000000000..9ccdd5b496 --- /dev/null +++ b/.github/actions/E2ESetupRepositories/E2ESetupRepositories.ps1 @@ -0,0 +1,9 @@ +Param( + [Parameter(HelpMessage = "GitHub owner for test repositories", Mandatory = $true)] + [string] $githubOwner, + [Parameter(HelpMessage = "BcContainerHelper version", Mandatory = $false)] + [string] $bcContainerHelperVersion = '' +) + +$ErrorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 +. (Join-Path "." "e2eTests/SetupRepositories.ps1") -githubOwner $githubOwner -bcContainerHelperVersion $bcContainerHelperVersion diff --git a/.github/actions/E2ESetupRepositories/README.md b/.github/actions/E2ESetupRepositories/README.md new file mode 100644 index 0000000000..9cc433495a --- /dev/null +++ b/.github/actions/E2ESetupRepositories/README.md @@ -0,0 +1,15 @@ +# E2E Setup Repositories + +Sets up test repositories for E2E testing by calling the SetupRepositories.ps1 script. + +## Inputs + +- `githubOwner`: GitHub owner for test repositories +- `bcContainerHelperVersion`: BcContainerHelper version +- `token`: GitHub token with permissions to create repositories + +## Outputs + +- `actionsRepo`: Actions repository name +- `perTenantExtensionRepo`: Per-tenant extension repository name +- `appSourceAppRepo`: AppSource app repository name diff --git a/.github/actions/E2ESetupRepositories/action.yaml b/.github/actions/E2ESetupRepositories/action.yaml new file mode 100644 index 0000000000..d0e8f601db --- /dev/null +++ b/.github/actions/E2ESetupRepositories/action.yaml @@ -0,0 +1,42 @@ +name: E2E Setup Repositories +author: Microsoft Corporation +inputs: + shell: + description: Shell in which you want to run the action (powershell or pwsh) + required: false + default: pwsh + githubOwner: + description: GitHub owner for test repositories + required: true + bcContainerHelperVersion: + description: BcContainerHelper version + required: false + default: '' + token: + description: GitHub token with permissions to create repositories + required: true +outputs: + actionsRepo: + description: Actions repository name + value: ${{ steps.run.outputs.actionsRepo }} + perTenantExtensionRepo: + description: Per-tenant extension repository name + value: ${{ steps.run.outputs.perTenantExtensionRepo }} + appSourceAppRepo: + description: AppSource app repository name + value: ${{ steps.run.outputs.appSourceAppRepo }} +runs: + using: composite + steps: + - name: run + id: run + shell: ${{ inputs.shell }} + env: + _githubOwner: ${{ inputs.githubOwner }} + _bcContainerHelperVersion: ${{ inputs.bcContainerHelperVersion }} + GH_TOKEN: ${{ inputs.token }} + run: | + ${{ github.action_path }}/E2ESetupRepositories.ps1 -githubOwner $ENV:_githubOwner -bcContainerHelperVersion $ENV:_bcContainerHelperVersion +branding: + icon: git-branch + color: blue diff --git a/.github/workflows/E2E.yaml b/.github/workflows/E2E.yaml index 5429010bd6..0d2cbef328 100644 --- a/.github/workflows/E2E.yaml +++ b/.github/workflows/E2E.yaml @@ -66,49 +66,15 @@ jobs: - name: Check secrets id: check - env: + uses: ./.github/actions/E2ECheckSecrets + with: githubOwner: ${{ github.event.inputs.githubOwner }} - run: | - $err = $false - if (('${{ vars.E2E_APP_ID }}' -eq '') -or ('${{ secrets.E2E_PRIVATE_KEY }}' -eq '')){ - Write-Host "::Error::In order to run end to end tests, you need a Secret called E2E_PRIVATE_KEY and a variable called E2E_APP_ID." - $err = $true - } - if ('${{ Secrets.ALGOAUTHAPP }}' -eq '') { - Write-Host "::Error::In order to run end to end tests, you need a Secret called ALGOAUTHAPP" - $err = $true - } - if ('${{ Secrets.adminCenterApiCredentials }}' -eq '') { - Write-Host "::Error::In order to run end to end tests, you need a Secret called adminCenterApiCredentials" - $err = $true - } - if ('${{ Secrets.E2E_GHPackagesPAT }}' -eq '') { - Write-Host "::Error::In order to run end to end tests, you need a secret called E2E_GHPackagesPAT" - $err = $true - } - if ('${{ Secrets.E2EAZURECREDENTIALS }}' -eq '') { - Write-Host "::Error::In order to run end to end tests, you need a secret called E2EAZURECREDENTIALS" - $err = $true - } - if ($err) { - exit 1 - } - $githubOwner = $ENV:githubOwner - $maxParallel = 99 - if (!($githubOwner)) { - $githubOwner = "$ENV:GITHUB_REPOSITORY_OWNER" - } - $orgmap = Get-Content -path (Join-Path "." "e2eTests\orgmap.json") -encoding UTF8 -raw | ConvertFrom-Json - if ($orgmap.PSObject.Properties.Name -eq $githubOwner) { - $githubOwner = $orgmap."$githubOwner" - } - if ($githubOwner -eq $ENV:GITHUB_REPOSITORY_OWNER) { - $maxParallel = 8 - } - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "maxParallel=$maxParallel" - Write-Host "maxParallel=$maxParallel" - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "githubOwner=$githubOwner" - Write-Host "githubOwner=$githubOwner" + e2eAppId: ${{ vars.E2E_APP_ID }} + e2ePrivateKey: ${{ secrets.E2E_PRIVATE_KEY }} + algoAuthApp: ${{ secrets.ALGOAUTHAPP }} + adminCenterApiCredentials: ${{ secrets.adminCenterApiCredentials }} + e2eGHPackagesPAT: ${{ secrets.E2E_GHPackagesPAT }} + e2eAzureCredentials: ${{ secrets.E2EAZURECREDENTIALS }} SetupRepositories: runs-on: [ ubuntu-latest ] @@ -137,12 +103,11 @@ jobs: - name: Setup Repositories id: setup - env: - _bcContainerHelperVersion: ${{ github.event.inputs.bcContainerHelperVersion }} - GH_TOKEN: ${{ steps.app-token.outputs.token }} - run: | - $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - . (Join-Path "." "e2eTests/SetupRepositories.ps1") -githubOwner '${{ needs.Check.outputs.githubowner }}' -bcContainerHelperVersion $ENV:_bcContainerHelperVersion + uses: ./.github/actions/E2ESetupRepositories + with: + githubOwner: ${{ needs.Check.outputs.githubowner }} + bcContainerHelperVersion: ${{ github.event.inputs.bcContainerHelperVersion }} + token: ${{ steps.app-token.outputs.token }} Analyze: runs-on: [ ubuntu-latest ] @@ -172,106 +137,12 @@ jobs: - name: Analyze id: Analyze - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - _scenariosFilter: ${{ github.event.inputs.scenariosFilter }} - run: | - $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - $modulePath = Join-Path "." "e2eTests\e2eTestHelper.psm1" -resolve - Import-Module $modulePath -DisableNameChecking - $maxParallel = [int]'${{ needs.Check.outputs.maxParallel }}' - - $publicTestruns = @{ - "max-parallel" = $maxParallel - "fail-fast" = $false - "matrix" = @{ - "include" = @() - } - } - $privateTestruns = @{ - "max-parallel" = $maxParallel - "fail-fast" = $false - "matrix" = @{ - "include" = @() - } - } - @('appSourceApp','PTE') | ForEach-Object { - $type = $_ - @('linux','windows') | ForEach-Object { - $os = $_ - @('multiProject','singleProject') | ForEach-Object { - $style = $_ - $publicTestruns.matrix.include += @{ "type" = $type; "os" = $os; "style" = $style; "Compiler" = "Container" } - $privateTestruns.matrix.include += @{ "type" = $type; "os" = $os; "style" = $style; "Compiler" = "Container" } - if ($type -eq "PTE") { - # Run end 2 end tests using CompilerFolder with Windows+Linux and single/multiproject - $publicTestruns.matrix.include += @{ "type" = $type; "os" = $os; "style" = $style; "Compiler" = "CompilerFolder" } - } - } - } - } - $publicTestrunsJson = $publicTestruns | ConvertTo-Json -depth 99 -compress - $privateTestrunsJson = $privateTestruns | ConvertTo-Json -depth 99 -compress - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "publictestruns=$publicTestrunsJson" - Write-Host "publictestruns=$publicTestrunsJson" - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "privatetestruns=$privateTestrunsJson" - Write-Host "privatetestruns=$privateTestrunsJson" - - $releases = @(gh release list --repo microsoft/AL-Go | ForEach-Object { $_.split("`t")[0] }) | Where-Object { [Version]($_.trimStart('v')) -ge [Version]("$env:TestUpgradesFromVersion".TrimStart('v')) } - $releasesJson = @{ - "matrix" = @{ - "include" = @($releases | ForEach-Object { @{ "Release" = $_; "type" = 'appSourceApp' }; @{ "Release" = $_; "type" = 'PTE' } } ) - }; - "max-parallel" = $maxParallel - "fail-fast" = $false - } | ConvertTo-Json -depth 99 -compress - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "releases=$releasesJson" - Write-Host "releases=$releasesJson" - - $scenariosFilter = "$($env:_scenariosFilter)" -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' } - $allScenarios = @(Get-ChildItem -Path (Join-Path $ENV:GITHUB_WORKSPACE "e2eTests/scenarios/*/runtest.ps1") | ForEach-Object { $_.Directory.Name }) - $filteredScenarios = @($allScenarios | Where-Object { $scenario = $_; @($scenariosFilter | Where-Object { $scenario -like $_ }).Count -gt 0 }) - - # Load disabled scenarios from config file (optional) - $disabledScenariosConfigPath = Join-Path $ENV:GITHUB_WORKSPACE "e2eTests/disabled-scenarios.json" - $disabledScenariosConfig = @() - if (Test-Path -Path $disabledScenariosConfigPath) { - $disabledScenariosContent = Get-Content -Path $disabledScenariosConfigPath -Encoding UTF8 -Raw - if (-not [string]::IsNullOrWhiteSpace($disabledScenariosContent)) { - $disabledScenariosConfig = $disabledScenariosContent | ConvertFrom-Json - } - } - else { - Write-Host "No disabled-scenarios.json found; proceeding with all scenarios enabled." - } - $disabledScenarios = @() - if ($disabledScenariosConfig -and $disabledScenariosConfig.Count -gt 0) { - $disabledScenarios = @($disabledScenariosConfig | ForEach-Object { $_.scenario }) - } - Write-Host "Disabled scenarios from config: $($disabledScenarios -join ', ')" - - # Filter out disabled scenarios - $scenariosBeforeDisabledFilter = $filteredScenarios - $beforeFilter = $filteredScenarios.Count - $filteredScenarios = @($filteredScenarios | Where-Object { $disabledScenarios -notcontains $_ }) - $afterFilter = $filteredScenarios.Count - if ($beforeFilter -ne $afterFilter) { - Write-Host "Filtered out $($beforeFilter - $afterFilter) disabled scenario(s)" - $disabledScenariosConfig | Where-Object { ($scenariosBeforeDisabledFilter -contains $_.scenario) -and ($filteredScenarios -notcontains $_.scenario) } | ForEach-Object { - Write-Host " - $($_.scenario): $($_.reason)" - } - } - Write-Host "Scenarios to run: $($filteredScenarios -join ', ')" - - $scenariosJson = @{ - "matrix" = @{ - "include" = @($filteredScenarios | ForEach-Object { @{ "Scenario" = $_ } }) - }; - "max-parallel" = $maxParallel - "fail-fast" = $false - } | ConvertTo-Json -depth 99 -compress - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "scenarios=$scenariosJson" - Write-Host "scenarios=$scenariosJson" + uses: ./.github/actions/E2EAnalyze + with: + maxParallel: ${{ needs.Check.outputs.maxParallel }} + testUpgradesFromVersion: ${{ env.TestUpgradesFromVersion }} + scenariosFilter: ${{ github.event.inputs.scenariosFilter }} + token: ${{ steps.app-token.outputs.token }} ScenariosOnWindows: runs-on: [ windows-latest ] @@ -291,24 +162,25 @@ jobs: - name: Calculate parameters id: calculateParams - run: | - $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - $reponame = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetTempFileName()) - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "repoName=$repoName" - Write-Host "repoName=$repoName" - Write-Host "Repo URL: https://github.com/${{ needs.Check.outputs.githubowner }}/$repoName" + uses: ./.github/actions/E2ECalculateRepoName + with: + githubOwner: ${{ needs.Check.outputs.githubowner }} - name: Run test on Windows - run: | - try { - . (Join-Path "." "e2eTests/scenarios/${{ matrix.scenario }}/runtest.ps1") -github -githubOwner '${{ needs.Check.outputs.githubowner }}' -repoName '${{ steps.calculateParams.outputs.repoName }}' -e2eAppId '${{ vars.E2E_APP_ID }}' -e2eAppKey '${{ secrets.E2E_PRIVATE_KEY }}' -algoauthapp '${{ Secrets.ALGOAUTHAPP }}' -pteTemplate '${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.perTenantExtensionRepo }}' -appSourceTemplate '${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.appSourceAppRepo }}' -adminCenterApiCredentials '${{ Secrets.adminCenterApiCredentials }}' -azureCredentials '${{ Secrets.E2EAzureCredentials }}' -githubPackagesToken '${{ Secrets.E2E_GHPackagesPAT }}' - } - catch { - Write-Host $_.Exception.Message - Write-Host $_.ScriptStackTrace - Write-Host "::Error::$($_.Exception.Message)" - $host.SetShouldExit(1) - } + uses: ./.github/actions/E2ERunScenario + with: + scenario: ${{ matrix.scenario }} + linux: false + githubOwner: ${{ needs.Check.outputs.githubowner }} + repoName: ${{ steps.calculateParams.outputs.repoName }} + e2eAppId: ${{ vars.E2E_APP_ID }} + e2eAppKey: ${{ secrets.E2E_PRIVATE_KEY }} + algoAuthApp: ${{ secrets.ALGOAUTHAPP }} + pteTemplate: ${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.perTenantExtensionRepo }} + appSourceTemplate: ${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.appSourceAppRepo }} + adminCenterApiCredentials: ${{ secrets.adminCenterApiCredentials }} + azureCredentials: ${{ secrets.E2EAzureCredentials }} + githubPackagesToken: ${{ secrets.E2E_GHPackagesPAT }} ScenariosOnLinux: runs-on: [ windows-latest ] @@ -328,24 +200,25 @@ jobs: - name: Calculate parameters id: calculateParams - run: | - $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - $reponame = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetTempFileName()) - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "repoName=$repoName" - Write-Host "repoName=$repoName" - Write-Host "Repo URL: https://github.com/${{ needs.Check.outputs.githubowner }}/$repoName" + uses: ./.github/actions/E2ECalculateRepoName + with: + githubOwner: ${{ needs.Check.outputs.githubowner }} - name: Run tests - run: | - try { - . (Join-Path "." "e2eTests/scenarios/${{ matrix.scenario }}/runtest.ps1") -github -linux -githubOwner '${{ needs.Check.outputs.githubowner }}' -repoName '${{ steps.calculateParams.outputs.repoName }}' -e2eAppId '${{ vars.E2E_APP_ID }}' -e2eAppKey '${{ secrets.E2E_PRIVATE_KEY }}' -algoauthapp '${{ Secrets.ALGOAUTHAPP }}' -pteTemplate '${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.perTenantExtensionRepo }}' -appSourceTemplate '${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.appSourceAppRepo }}' -adminCenterApiCredentials '${{ Secrets.adminCenterApiCredentials }}' -azureCredentials '${{ Secrets.E2EAzureCredentials }}' -githubPackagesToken '${{ Secrets.E2E_GHPackagesPAT }}' - } - catch { - Write-Host $_.Exception.Message - Write-Host $_.ScriptStackTrace - Write-Host "::Error::$($_.Exception.Message)" - $host.SetShouldExit(1) - } + uses: ./.github/actions/E2ERunScenario + with: + scenario: ${{ matrix.scenario }} + linux: true + githubOwner: ${{ needs.Check.outputs.githubowner }} + repoName: ${{ steps.calculateParams.outputs.repoName }} + e2eAppId: ${{ vars.E2E_APP_ID }} + e2eAppKey: ${{ secrets.E2E_PRIVATE_KEY }} + algoAuthApp: ${{ secrets.ALGOAUTHAPP }} + pteTemplate: ${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.perTenantExtensionRepo }} + appSourceTemplate: ${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.appSourceAppRepo }} + adminCenterApiCredentials: ${{ secrets.adminCenterApiCredentials }} + azureCredentials: ${{ secrets.E2EAzureCredentials }} + githubPackagesToken: ${{ secrets.E2E_GHPackagesPAT }} TestAlGoPublic: runs-on: [ ubuntu-latest ] @@ -365,36 +238,32 @@ jobs: - name: Calculate parameters id: calculateParams - run: | - $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - $adminCenterApiCredentials = '' - if ('${{ matrix.type }}' -eq 'PTE' -and '${{ matrix.style }}' -eq 'singleProject' -and '${{ matrix.os }}' -eq 'windows') { - $adminCenterApiCredentials = '${{ Secrets.adminCenterApiCredentials }}' - } - if ('${{ matrix.type }}' -eq 'appSourceApp') { - $template = '${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.appSourceAppRepo }}' - } - else { - $template = '${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.perTenantExtensionRepo }}' - } - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "adminCenterApiCredentials='$adminCenterApiCredentials'" - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "template='$template'" - $reponame = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetTempFileName()) - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "repoName='$repoName'" - Write-Host "repoName='$repoName'" - Write-Host "Repo URL: https://github.com/${{ needs.Check.outputs.githubowner }}/$repoName" + uses: ./.github/actions/E2ECalculateTestParams + with: + githubOwner: ${{ needs.Check.outputs.githubowner }} + matrixType: ${{ matrix.type }} + matrixStyle: ${{ matrix.style }} + matrixOs: ${{ matrix.os }} + adminCenterApiCredentialsSecret: ${{ secrets.adminCenterApiCredentials }} + appSourceAppRepo: ${{ needs.SetupRepositories.outputs.appSourceAppRepo }} + perTenantExtensionRepo: ${{ needs.SetupRepositories.outputs.perTenantExtensionRepo }} - name: Run tests - run: | - try { - . (Join-Path "." "e2eTests/Test-AL-Go.ps1") -github -githubOwner '${{ needs.Check.outputs.githubowner }}' -repoName ${{ steps.calculateParams.outputs.repoName }} -e2eAppId '${{ vars.E2E_APP_ID }}' -e2eAppKey '${{ secrets.E2E_PRIVATE_KEY }}' -algoauthapp '${{ Secrets.ALGOAUTHAPP }}' -template ${{ steps.calculateParams.outputs.template }} -adminCenterApiCredentials ${{ steps.calculateParams.outputs.adminCenterApiCredentials }} -multiProject:('${{ matrix.style }}' -eq 'multiProject') -appSource:('${{ matrix.type }}' -eq 'appSourceApp') -linux:('${{ matrix.os }}' -eq 'linux') -useCompilerFolder:('${{ matrix.Compiler }}' -eq 'CompilerFolder') - } - catch { - Write-Host $_.Exception.Message - Write-Host $_.ScriptStackTrace - Write-Host "::Error::$($_.Exception.Message)" - $host.SetShouldExit(1) - } + uses: ./.github/actions/E2ERunTest + with: + testType: test + private: false + githubOwner: ${{ needs.Check.outputs.githubowner }} + repoName: ${{ steps.calculateParams.outputs.repoName }} + e2eAppId: ${{ vars.E2E_APP_ID }} + e2eAppKey: ${{ secrets.E2E_PRIVATE_KEY }} + algoAuthApp: ${{ secrets.ALGOAUTHAPP }} + template: ${{ steps.calculateParams.outputs.template }} + adminCenterApiCredentials: ${{ steps.calculateParams.outputs.adminCenterApiCredentials }} + multiProject: ${{ matrix.style == 'multiProject' }} + appSource: ${{ matrix.type == 'appSourceApp' }} + linux: ${{ matrix.os == 'linux' }} + useCompilerFolder: ${{ matrix.Compiler == 'CompilerFolder' }} TestAlGoPrivate: runs-on: [ ubuntu-latest ] @@ -414,36 +283,32 @@ jobs: - name: Calculate parameters id: calculateParams - run: | - $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - $adminCenterApiCredentials = '' - if ('${{ matrix.type }}' -eq 'PTE' -and '${{ matrix.style }}' -eq 'singleProject' -and '${{ matrix.os }}' -eq 'windows') { - $adminCenterApiCredentials = '${{ Secrets.adminCenterApiCredentials }}' - } - if ('${{ matrix.type }}' -eq 'appSourceApp') { - $template = '${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.appSourceAppRepo }}' - } - else { - $template = '${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.perTenantExtensionRepo }}' - } - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "adminCenterApiCredentials='$adminCenterApiCredentials'" - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "template='$template'" - $reponame = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetTempFileName()) - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "repoName='$repoName'" - Write-Host "repoName='$repoName'" - Write-Host "Repo URL: https://github.com/${{ needs.Check.outputs.githubowner }}/$repoName" + uses: ./.github/actions/E2ECalculateTestParams + with: + githubOwner: ${{ needs.Check.outputs.githubowner }} + matrixType: ${{ matrix.type }} + matrixStyle: ${{ matrix.style }} + matrixOs: ${{ matrix.os }} + adminCenterApiCredentialsSecret: ${{ secrets.adminCenterApiCredentials }} + appSourceAppRepo: ${{ needs.SetupRepositories.outputs.appSourceAppRepo }} + perTenantExtensionRepo: ${{ needs.SetupRepositories.outputs.perTenantExtensionRepo }} - name: Run tests - run: | - try { - . (Join-Path "." "e2eTests/Test-AL-Go.ps1") -private -github -githubOwner '${{ needs.Check.outputs.githubowner }}' -repoName ${{ steps.calculateParams.outputs.repoName }} -e2eAppId '${{ vars.E2E_APP_ID }}' -e2eAppKey '${{ secrets.E2E_PRIVATE_KEY }}' -algoauthapp '${{ Secrets.ALGOAUTHAPP }}' -template ${{ steps.calculateParams.outputs.template }} -adminCenterApiCredentials ${{ steps.calculateParams.outputs.adminCenterApiCredentials }} -multiProject:('${{ matrix.style }}' -eq 'multiProject') -appSource:('${{ matrix.type }}' -eq 'appSourceApp') -linux:('${{ matrix.os }}' -eq 'linux') -useCompilerFolder:('${{ matrix.Compiler }}' -eq 'CompilerFolder') - } - catch { - Write-Host $_.Exception.Message - Write-Host $_.ScriptStackTrace - Write-Host "::Error::$($_.Exception.Message)" - $host.SetShouldExit(1) - } + uses: ./.github/actions/E2ERunTest + with: + testType: test + private: true + githubOwner: ${{ needs.Check.outputs.githubowner }} + repoName: ${{ steps.calculateParams.outputs.repoName }} + e2eAppId: ${{ vars.E2E_APP_ID }} + e2eAppKey: ${{ secrets.E2E_PRIVATE_KEY }} + algoAuthApp: ${{ secrets.ALGOAUTHAPP }} + template: ${{ steps.calculateParams.outputs.template }} + adminCenterApiCredentials: ${{ steps.calculateParams.outputs.adminCenterApiCredentials }} + multiProject: ${{ matrix.style == 'multiProject' }} + appSource: ${{ matrix.type == 'appSourceApp' }} + linux: ${{ matrix.os == 'linux' }} + useCompilerFolder: ${{ matrix.Compiler == 'CompilerFolder' }} TestAlGoUpgrade: runs-on: [ ubuntu-latest ] @@ -463,31 +328,23 @@ jobs: - name: Calculate parameters id: calculateParams - run: | - $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0 - if ('${{ matrix.type }}' -eq 'appSourceApp') { - $template = '${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.appSourceAppRepo }}' - $contentPath = 'appsourceapp' - } - else { - $template = '${{ needs.Check.outputs.githubowner }}/${{ needs.SetupRepositories.outputs.perTenantExtensionRepo }}' - $contentPath = 'pte' - } - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "template='$template'" - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "contentPath='$contentPath'" - $reponame = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetTempFileName()) - Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "repoName='$repoName'" - Write-Host "repoName='$repoName'" - Write-Host "Repo URL: https://github.com/${{ needs.Check.outputs.githubowner }}/$repoName" + uses: ./.github/actions/E2ECalculateTestParams + with: + githubOwner: ${{ needs.Check.outputs.githubowner }} + matrixType: ${{ matrix.type }} + appSourceAppRepo: ${{ needs.SetupRepositories.outputs.appSourceAppRepo }} + perTenantExtensionRepo: ${{ needs.SetupRepositories.outputs.perTenantExtensionRepo }} - name: Run tests - run: | - try { - . (Join-Path "." "e2eTests/Test-AL-Go-Upgrade.ps1") -github -githubOwner '${{ needs.Check.outputs.githubowner }}' -repoName ${{ steps.calculateParams.outputs.repoName }} -e2eAppId '${{ vars.E2E_APP_ID }}' -e2eAppKey '${{ secrets.E2E_PRIVATE_KEY }}' -algoauthapp '${{ Secrets.ALGOAUTHAPP }}' -template ${{ steps.calculateParams.outputs.template }} -appSource:('${{ matrix.type }}' -eq 'appSourceApp') -release '${{ matrix.release }}' -contentPath ${{ steps.calculateParams.outputs.contentPath }} - } - catch { - Write-Host $_.Exception.Message - Write-Host $_.ScriptStackTrace - Write-Host "::Error::$($_.Exception.Message)" - $host.SetShouldExit(1) - } + uses: ./.github/actions/E2ERunTest + with: + testType: upgrade + githubOwner: ${{ needs.Check.outputs.githubowner }} + repoName: ${{ steps.calculateParams.outputs.repoName }} + e2eAppId: ${{ vars.E2E_APP_ID }} + e2eAppKey: ${{ secrets.E2E_PRIVATE_KEY }} + algoAuthApp: ${{ secrets.ALGOAUTHAPP }} + template: ${{ steps.calculateParams.outputs.template }} + appSource: ${{ matrix.type == 'appSourceApp' }} + release: ${{ matrix.release }} + contentPath: ${{ steps.calculateParams.outputs.contentPath }} diff --git a/Internal/Deploy.ps1 b/Internal/Deploy.ps1 index 563db98200..b102944366 100644 --- a/Internal/Deploy.ps1 +++ b/Internal/Deploy.ps1 @@ -175,7 +175,7 @@ try { catch { Write-Host "gh repo create $($config.githubOwner)/$repo --public --clone" $ownerRepo = "$($config.githubOwner)/$repo" - invoke-gh repo create $ownerRepo --public --clone + invoke-gh repo create "$ownerRepo" --public --clone Start-Sleep -Seconds 10 Set-Location $repo invoke-git checkout -b $branch diff --git a/Tests/E2EAnalyze.Test.ps1 b/Tests/E2EAnalyze.Test.ps1 new file mode 100644 index 0000000000..4a0fc89a25 --- /dev/null +++ b/Tests/E2EAnalyze.Test.ps1 @@ -0,0 +1,84 @@ +Get-Module TestActionsHelper | Remove-Module -Force +Import-Module (Join-Path $PSScriptRoot 'TestActionsHelper.psm1') + +Describe "E2EAnalyze Action Tests" { + BeforeAll { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'scriptPath', Justification = 'False positive.')] + $scriptPath = Join-Path $PSScriptRoot "../.github/actions/E2EAnalyze/E2EAnalyze.ps1" -Resolve + # Dot-source the script to load Get-E2EScenariosToRun without executing the main block. + # -maxParallel satisfies the mandatory parameter; the main block is guarded to only run when the + # script is invoked directly (InvocationName -ne '.'). + . $scriptPath -maxParallel 1 + + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'allScenarios', Justification = 'False positive.')] + $allScenarios = @('alpha', 'beta', 'gamma') + } + + It 'Get-E2EScenariosToRun is defined' { + Get-Command Get-E2EScenariosToRun -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + } + + It 'Default filter (*) returns all scenarios' { + $result = @(Get-E2EScenariosToRun -allScenarios $allScenarios) + $result.Count | Should -Be 3 + $result | Should -Contain 'alpha' + $result | Should -Contain 'beta' + $result | Should -Contain 'gamma' + } + + It 'Zero-match filter returns no scenarios' { + $result = @(Get-E2EScenariosToRun -allScenarios $allScenarios -scenariosFilter 'zzz*') + $result.Count | Should -Be 0 + } + + It 'One-match exact filter returns a single scenario' { + $result = @(Get-E2EScenariosToRun -allScenarios $allScenarios -scenariosFilter 'alpha') + $result.Count | Should -Be 1 + $result | Should -Contain 'alpha' + } + + It 'One-match wildcard filter returns a single scenario' { + $result = @(Get-E2EScenariosToRun -allScenarios $allScenarios -scenariosFilter 'gam*') + $result.Count | Should -Be 1 + $result | Should -Contain 'gamma' + } + + It 'Multi-match comma separated filter returns only matching scenarios' { + # Regression test: multiple filter entries must not cause every scenario to match + $result = @(Get-E2EScenariosToRun -allScenarios $allScenarios -scenariosFilter 'alpha,beta') + $result.Count | Should -Be 2 + $result | Should -Contain 'alpha' + $result | Should -Contain 'beta' + $result | Should -Not -Contain 'gamma' + } + + It 'Multi-match comma separated wildcard filter returns only matching scenarios' { + $result = @(Get-E2EScenariosToRun -allScenarios $allScenarios -scenariosFilter 'al*, be*') + $result.Count | Should -Be 2 + $result | Should -Contain 'alpha' + $result | Should -Contain 'beta' + $result | Should -Not -Contain 'gamma' + } + + It 'Disabled scenarios are filtered out' { + $disabled = @([PSCustomObject]@{ scenario = 'beta'; reason = 'Flaky' }) + $result = @(Get-E2EScenariosToRun -allScenarios $allScenarios -disabledScenariosConfig $disabled) + $result.Count | Should -Be 2 + $result | Should -Contain 'alpha' + $result | Should -Contain 'gamma' + $result | Should -Not -Contain 'beta' + } + + It 'Filter and disabled scenarios combine' { + $disabled = @([PSCustomObject]@{ scenario = 'alpha'; reason = 'Disabled' }) + $result = @(Get-E2EScenariosToRun -allScenarios $allScenarios -scenariosFilter 'alpha,beta' -disabledScenariosConfig $disabled) + $result.Count | Should -Be 1 + $result | Should -Contain 'beta' + $result | Should -Not -Contain 'alpha' + } + + It 'Empty scenario list returns nothing' { + $result = @(Get-E2EScenariosToRun -allScenarios @() -scenariosFilter '*') + $result.Count | Should -Be 0 + } +} diff --git a/Tests/E2ECalculateTestParams.Test.ps1 b/Tests/E2ECalculateTestParams.Test.ps1 new file mode 100644 index 0000000000..d6aa1fe39a --- /dev/null +++ b/Tests/E2ECalculateTestParams.Test.ps1 @@ -0,0 +1,123 @@ +Get-Module TestActionsHelper | Remove-Module -Force +Import-Module (Join-Path $PSScriptRoot 'TestActionsHelper.psm1') + +Describe "E2ECalculateTestParams Action Tests" { + BeforeAll { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'scriptPath', Justification = 'False positive.')] + $scriptPath = Join-Path $PSScriptRoot "../.github/actions/E2ECalculateTestParams/E2ECalculateTestParams.ps1" -Resolve + # Dot-source the script to load Get-E2ECalculatedTestParams without executing the main block. + # The mandatory parameters are satisfied with placeholders; the main block is guarded to only run + # when the script is invoked directly (InvocationName -ne '.'). + . $scriptPath -githubOwner 'placeholder' -appSourceAppRepo 'placeholder' -perTenantExtensionRepo 'placeholder' + } + + It 'Get-E2ECalculatedTestParams is defined' { + Get-Command Get-E2ECalculatedTestParams -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + } + + Context 'Template calculation' { + It 'appSourceApp uses the AppSource app repository template' { + $result = Get-E2ECalculatedTestParams -githubOwner 'contoso' -matrixType 'appSourceApp' -appSourceAppRepo 'appSourceRepo' -perTenantExtensionRepo 'pteRepo' + $result.template | Should -Be 'contoso/appSourceRepo' + } + + It 'PTE uses the per-tenant extension repository template' { + $result = Get-E2ECalculatedTestParams -githubOwner 'contoso' -matrixType 'PTE' -appSourceAppRepo 'appSourceRepo' -perTenantExtensionRepo 'pteRepo' + $result.template | Should -Be 'contoso/pteRepo' + } + + It 'Empty matrixType yields an empty template' { + $result = Get-E2ECalculatedTestParams -githubOwner 'contoso' -appSourceAppRepo 'appSourceRepo' -perTenantExtensionRepo 'pteRepo' + $result.template | Should -Be '' + } + } + + Context 'adminCenterApiCredentials forwarding' { + # Credentials must only be forwarded for the PTE / singleProject / windows cell + $forwardCases = @( + @{ type = 'PTE'; style = 'singleProject'; os = 'windows'; expected = 'the-secret' } + @{ type = 'PTE'; style = 'multiProject'; os = 'windows'; expected = '' } + @{ type = 'PTE'; style = 'singleProject'; os = 'linux'; expected = '' } + @{ type = 'appSourceApp'; style = 'singleProject'; os = 'windows'; expected = '' } + @{ type = 'appSourceApp'; style = 'multiProject'; os = 'linux'; expected = '' } + ) + + It 'Forwards credentials only for PTE/singleProject/windows (type= style=