Skip to content

ci-azure [pool:andreasjordan] Tests - Case sensitive lookups and backup cleanup in seven test files #609

ci-azure [pool:andreasjordan] Tests - Case sensitive lookups and backup cleanup in seven test files

ci-azure [pool:andreasjordan] Tests - Case sensitive lookups and backup cleanup in seven test files #609

Workflow file for this run

# 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.