ci-azure [pool:andreasjordan] Tests - Case sensitive lookups and backup cleanup in seven test files #609
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # The AppVeyor matrix, ported to self-hosted Azure VMSS runners. The appveyor.*.ps1 | |
| # harness runs UNTOUCHED via tests/gha.shim.ps1; the AppVeyor webhook is disabled, so | |
| # this workflow is the test suite of record. Commit-message controls carry over: | |
| # (do Command) scopes tests, [skip ci] skips entirely, part: n/m splitting comes from | |
| # the matrix. HADR and RESTART run on sql2019 (the image also carries sql2017 and | |
| # sql2022) so the suite exercises three engine versions instead of one. | |
| # | |
| # Runner rules (also apply to the future legacy 2012 pool): | |
| # - zero node-based actions on self-hosted jobs (git via run: steps only) | |
| # - every job lands on a factory-fresh ephemeral VM; runner-reconcile deletes it after | |
| name: ci-azure | |
| run-name: "ci-azure [pool:${{ inputs.pool_user || github.actor }}] ${{ github.event.pull_request.title || github.event.head_commit.message || inputs.message }}" | |
| on: | |
| push: | |
| branches: | |
| - development | |
| paths-ignore: | |
| - "**/*.md" | |
| - ".github/FUNDING.yml" | |
| - ".github/ISSUE_TEMPLATE/**" | |
| - ".github/PULL_REQUEST_TEMPLATE.md" | |
| - ".aider/**" | |
| - ".devcontainer/**" | |
| - ".vscode/**" | |
| - "en-us/**" | |
| pull_request: | |
| branches: | |
| - development | |
| paths-ignore: | |
| - "**/*.md" | |
| - ".github/FUNDING.yml" | |
| - ".github/ISSUE_TEMPLATE/**" | |
| - ".github/PULL_REQUEST_TEMPLATE.md" | |
| - ".aider/**" | |
| - ".devcontainer/**" | |
| - ".vscode/**" | |
| - "en-us/**" | |
| workflow_dispatch: | |
| inputs: | |
| message: | |
| description: "Commit message the harness sees; the default (no (do ...) scope) runs the full suite" | |
| required: false | |
| default: "Full-suite validation run" | |
| pool_user: | |
| description: "Runner pool owner for controller-dispatched CI" | |
| required: false | |
| default: "" | |
| permissions: | |
| contents: read | |
| actions: write | |
| pull-requests: read | |
| # AppVeyor-style build lanes: one matrix build consumes its lane's worker limit | |
| # while later builds wait in FIFO order. Pool-specific runner labels below prevent | |
| # an active maintainer or community lane from stealing another lane's workers. | |
| concurrency: | |
| group: ci-azure-${{ (inputs.pool_user || github.actor) == 'potatoqualitee' && 'potatoqualitee' || ((inputs.pool_user || github.actor) == 'andreasjordan' && 'andreasjordan' || ((inputs.pool_user || github.actor) == 'niphlod' && 'niphlod' || 'community')) }} | |
| queue: max | |
| env: | |
| APPVEYOR: "True" | |
| APPVEYOR_BUILD_FOLDER: C:\github\dbatools | |
| APPVEYOR_REPO_BRANCH: ${{ github.base_ref || github.ref_name }} | |
| APPVEYOR_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} | |
| APPVEYOR_BUILD_VERSION: 2.1.${{ github.run_number }} | |
| DBATOOLS_COMMIT_MESSAGE: ${{ github.event.head_commit.message || github.event.pull_request.title || inputs.message }} | |
| DBATOOLS_BUILD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| DBATOOLS_HEAD_REF: ${{ github.event.pull_request.head.ref || github.ref_name }} | |
| DBATOOLS_HEAD_REPO: ${{ github.event.pull_request.head.repo.clone_url }} | |
| environment: development | |
| version: 2.1.${{ github.run_number }} | |
| jobs: | |
| authorize: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| outputs: | |
| run-ci: ${{ steps.policy.outputs.run-ci }} | |
| steps: | |
| - name: Apply push CI policy | |
| id: policy | |
| env: | |
| CI_ACTOR: ${{ inputs.pool_user || github.actor }} | |
| CI_EVENT: ${{ github.event_name }} | |
| CI_POOL_USER: ${{ inputs.pool_user }} | |
| CI_MESSAGE: ${{ github.event.head_commit.message || github.event.pull_request.title || inputs.message }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| run_ci=true | |
| # The marker exists so a maintainer's ordinary feature-branch push does not | |
| # acquire runners. Those pushes only ever reach this workflow as a controller | |
| # dispatch, which sets pool_user -- so that is the only path still checked. | |
| # | |
| # The old condition was "not a pull_request", which also caught push. But push | |
| # here only fires on development, so every merge to development was silently | |
| # skipped: authorize green, test skipped, run green in ten seconds. Merges are | |
| # the one thing that must always run the whole suite. A hand-started dispatch | |
| # (no pool_user) is a deliberate act and runs too. | |
| if [ "$CI_ACTOR" = "potatoqualitee" ] && [ "$CI_EVENT" = "workflow_dispatch" ] && [ -n "$CI_POOL_USER" ]; then | |
| message_lower=$(printf '%s' "$CI_MESSAGE" | tr '[:upper:]' '[:lower:]') | |
| if [[ "$message_lower" != *"[do ci]"* ]]; then | |
| run_ci=false | |
| echo "::notice::Unmarked potato push is not authorized to acquire Azure runners." | |
| fi | |
| fi | |
| echo "run-ci=$run_ci" >> "$GITHUB_OUTPUT" | |
| test: | |
| needs: authorize | |
| if: needs.authorize.outputs.run-ci == 'true' | |
| runs-on: | |
| - self-hosted | |
| - dbatools-modern | |
| - dbatools-pool-${{ (inputs.pool_user || github.actor) == 'potatoqualitee' && 'potatoqualitee' || ((inputs.pool_user || github.actor) == 'andreasjordan' && 'andreasjordan' || ((inputs.pool_user || github.actor) == 'niphlod' && 'niphlod' || 'community')) }} | |
| timeout-minutes: 90 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - scenario: SINGLE | |
| part: 1/5 | |
| InstanceSingle: sql2022 | |
| - scenario: SINGLE | |
| part: 2/5 | |
| InstanceSingle: sql2022 | |
| - scenario: SINGLE | |
| part: 3/5 | |
| InstanceSingle: sql2022 | |
| - scenario: SINGLE | |
| part: 4/5 | |
| InstanceSingle: sql2022 | |
| - scenario: SINGLE | |
| part: 5/5 | |
| InstanceSingle: sql2022 | |
| - scenario: MULTI | |
| InstanceMulti1: sql2022 | |
| InstanceMulti2: sql2017 | |
| - scenario: COPY | |
| InstanceCopy1: sql2017 | |
| InstanceCopy2: sql2022 | |
| - scenario: HADR | |
| InstanceHadr: sql2019 | |
| - scenario: RESTART | |
| InstanceRestart: sql2019 | |
| - scenario: default | |
| env: | |
| scenario: ${{ matrix.scenario }} | |
| part: ${{ matrix.part }} | |
| InstanceSingle: ${{ matrix.InstanceSingle }} | |
| InstanceMulti1: ${{ matrix.InstanceMulti1 }} | |
| InstanceMulti2: ${{ matrix.InstanceMulti2 }} | |
| InstanceCopy1: ${{ matrix.InstanceCopy1 }} | |
| InstanceCopy2: ${{ matrix.InstanceCopy2 }} | |
| InstanceHadr: ${{ matrix.InstanceHadr }} | |
| InstanceRestart: ${{ matrix.InstanceRestart }} | |
| defaults: | |
| run: | |
| working-directory: C:\github\dbatools | |
| steps: | |
| - name: Sync repo to build commit | |
| shell: powershell | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| git config --global --add safe.directory C:/github/dbatools | |
| if ($LASTEXITCODE -ne 0) { throw "Failed to configure the Git safe directory." } | |
| git fetch --force origin development | |
| if ($LASTEXITCODE -ne 0) { throw "Failed to fetch development." } | |
| $checkoutRef = $env:DBATOOLS_BUILD_SHA | |
| if ($env:APPVEYOR_PULL_REQUEST_NUMBER) { | |
| $pullRequest = $null | |
| $splatPullRequest = @{ | |
| Headers = @{ | |
| Accept = "application/vnd.github+json" | |
| Authorization = "Bearer $env:GH_TOKEN" | |
| "X-GitHub-Api-Version" = "2022-11-28" | |
| } | |
| TimeoutSec = 30 | |
| Uri = "https://api.github.com/repos/$env:GITHUB_REPOSITORY/pulls/$env:APPVEYOR_PULL_REQUEST_NUMBER" | |
| } | |
| foreach ($attempt in 1..3) { | |
| try { | |
| $pullRequest = Invoke-RestMethod @splatPullRequest | |
| break | |
| } catch { | |
| Write-Warning "PR state lookup attempt $attempt of 3 failed. $($PSItem.Exception.Message)" | |
| if ($attempt -lt 3) { Start-Sleep -Seconds (3 * $attempt) } | |
| } | |
| } | |
| if (-not $pullRequest) { throw "Unable to determine the current PR state after three attempts." } | |
| if ($pullRequest.merged) { | |
| Write-Host "PR #$env:APPVEYOR_PULL_REQUEST_NUMBER has merged; testing current development." | |
| $checkoutRef = "origin/development" | |
| } else { | |
| git fetch --force $env:DBATOOLS_HEAD_REPO $env:DBATOOLS_HEAD_REF | |
| if ($LASTEXITCODE -ne 0) { throw "Failed to fetch the open PR head." } | |
| } | |
| } else { | |
| git fetch --force origin $env:DBATOOLS_HEAD_REF | |
| if ($LASTEXITCODE -ne 0) { throw "Failed to fetch the push ref." } | |
| } | |
| git checkout --force $checkoutRef | |
| if ($LASTEXITCODE -ne 0) { throw "Failed to check out $checkoutRef." } | |
| git clean -xdff | |
| if ($LASTEXITCODE -ne 0) { throw "Failed to clean the worktree." } | |
| git log -1 --oneline | |
| if ($LASTEXITCODE -ne 0) { throw "Failed to read the checked-out commit." } | |
| - name: Convert tests to CRLF (unix2dos equivalent) | |
| shell: powershell | |
| run: | | |
| Get-ChildItem -Path C:\github\dbatools\tests -Filter *.ps1 | ForEach-Object { | |
| $content = [System.IO.File]::ReadAllText($_.FullName) | |
| $converted = $content -replace "(?<!\r)\n", "`r`n" | |
| if ($converted -ne $content) { | |
| [System.IO.File]::WriteAllText($_.FullName, $converted) | |
| } | |
| } | |
| - name: Run CI pipeline (prep, instances, pester, post) | |
| shell: powershell | |
| run: | | |
| # GitHub's powershell shell injects ErrorActionPreference=Stop; AppVeyor runs | |
| # its build script lines under Continue, so restore AppVeyor semantics | |
| $ErrorActionPreference = "Continue" | |
| . C:\github\dbatools\tests\gha.shim.ps1 | |
| Invoke-GhaStage -Script "tests\appveyor.prep.ps1" | |
| Assert-GhaDbatoolsLoaded | |
| Invoke-GhaStage -Script "tests\appveyor.sqlserver.ps1" | |
| Repair-GhaSqlServerName | |
| Invoke-GhaStage -Script "tests\appveyor.pester.ps1" | |
| Invoke-GhaStage -Script "tests\appveyor.pester.ps1" -Arguments @{ Finalize = $true } | |
| Invoke-GhaStage -Script "tests\appveyor.post.ps1" | |
| # The SSPI provider is .NET 8 only, so this test cannot run under the Windows | |
| # PowerShell 5.1 pipeline and needs a portable pwsh 7 alongside it. Gated on the | |
| # test file rather than a branch name so it keeps running after this branch is gone. | |
| # When the scoped test selection leaves this partition empty, appveyor.prep exits | |
| # early without installing the pinned dbatools.library, so the SSPI test would load | |
| # whatever stale library the reused runner has - skip on the shim's early-exit flag. | |
| - name: Install PowerShell 7 for Windows SSPI integration test | |
| if: matrix.scenario == 'SINGLE' && matrix.part == '1/5' | |
| shell: powershell | |
| run: | | |
| if (-not (Test-Path C:\github\dbatools\tests\Connect-DbaInstance.WindowsSspi.Tests.ps1)) { | |
| Write-Host "Connect-DbaInstance.WindowsSspi.Tests.ps1 is not present, skipping." | |
| exit 0 | |
| } | |
| if (Test-Path C:\Temp\gha-exit-build.flag) { | |
| Write-Host "CI pipeline exited early without provisioning, skipping." | |
| exit 0 | |
| } | |
| $assetUrl = "https://github.com/PowerShell/PowerShell/releases/download/v7.4.18/PowerShell-7.4.18-win-x64.zip" | |
| $pwshRoot = "C:\Temp\powershell-7.4.18" | |
| if (Test-Path $pwshRoot) { Remove-Item $pwshRoot -Recurse -Force } | |
| $null = New-Item -Path $pwshRoot -ItemType Directory -Force | |
| $zipPath = Join-Path $env:TEMP "PowerShell-7.4.18-win-x64.zip" | |
| $ProgressPreference = "SilentlyContinue" | |
| Invoke-WebRequest -Uri $assetUrl -OutFile $zipPath -UseBasicParsing | |
| Expand-Archive -Path $zipPath -DestinationPath $pwshRoot -Force | |
| & "$pwshRoot\pwsh.exe" -NoProfile -Command '$PSVersionTable.PSVersion.ToString()' | |
| if ($LASTEXITCODE -ne 0) { throw "The portable PowerShell 7 runtime did not start successfully." } | |
| - name: Run PowerShell 7 Windows SSPI integration test | |
| if: matrix.scenario == 'SINGLE' && matrix.part == '1/5' | |
| shell: powershell | |
| run: | | |
| if (-not (Test-Path C:\github\dbatools\tests\Connect-DbaInstance.WindowsSspi.Tests.ps1)) { | |
| Write-Host "Connect-DbaInstance.WindowsSspi.Tests.ps1 is not present, skipping." | |
| exit 0 | |
| } | |
| if (Test-Path C:\Temp\gha-exit-build.flag) { | |
| Write-Host "CI pipeline exited early without provisioning, skipping." | |
| exit 0 | |
| } | |
| $scriptPath = Join-Path $env:RUNNER_TEMP "Invoke-WindowsSspiTest.ps1" | |
| @' | |
| . C:\github\dbatools\private\testing\Invoke-ManualPester.ps1 | |
| $splatPester = @{ | |
| Path = "C:\github\dbatools\tests\Connect-DbaInstance.WindowsSspi.Tests.ps1" | |
| TestIntegration = $true | |
| PassThru = $true | |
| Show = "Detailed" | |
| } | |
| $result = @(Invoke-ManualPester @splatPester) | | |
| Where-Object { $null -ne $PSItem.PSObject.Properties["FailedCount"] } | | |
| Select-Object -Last 1 | |
| if ($null -eq $result) { | |
| throw "The Windows SSPI integration test returned no Pester result." | |
| } | |
| # Count-agnostic so tests can be added, but still refuses a silent skip: | |
| # everything discovered has to actually run and pass. | |
| if ($result.TotalCount -lt 1 -or $result.PassedCount -ne $result.TotalCount -or $result.FailedCount -ne 0 -or $result.SkippedCount -ne 0 -or $result.NotRunCount -ne 0) { | |
| throw "Unexpected Windows SSPI result: Total=$($result.TotalCount), Passed=$($result.PassedCount), Failed=$($result.FailedCount), Skipped=$($result.SkippedCount), NotRun=$($result.NotRunCount)." | |
| } | |
| '@ | Set-Content -Path $scriptPath -Encoding UTF8 | |
| & "C:\Temp\powershell-7.4.18\pwsh.exe" -NoProfile -File $scriptPath | |
| if ($LASTEXITCODE -ne 0) { throw "The Windows SSPI integration test failed under PowerShell 7." } | |
| - name: Stage artifacts summary | |
| if: always() | |
| shell: powershell | |
| run: | | |
| if (Test-Path C:\Temp\gha-artifacts) { | |
| "## Artifacts (on-runner, forensic)" >> $env:GITHUB_STEP_SUMMARY | |
| Get-ChildItem C:\Temp\gha-artifacts | ForEach-Object { | |
| "- $($_.Name) ($([math]::Round($_.Length / 1KB, 1)) KB)" >> $env:GITHUB_STEP_SUMMARY | |
| } | |
| } | |
| # The replacement nudge that used to live here is gone. Its job was to tell | |
| # runner-reconcile.yml a worker slot had freed up, the way AppVeyor replenishes | |
| # one as soon as a matrix job ends. The controller learns the same thing from the | |
| # workflow_job.completed webhook, without a step in the critical path. |