Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions .github/scripts/Initialize-CI.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

[CmdletBinding()]
param(
[switch] $ForTest,
[switch] $UseAzAuth
)

$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'

$modulePath = Join-Path $env:RUNNER_TEMP 'TempModules'
$null = New-Item $modulePath -ItemType Directory -Force
Save-Module -Name Microsoft.PowerShell.PSResourceGet -MinimumVersion 0.9.0 -Path $modulePath -Force
$env:PSModulePath = $modulePath + [IO.Path]::PathSeparator + $env:PSModulePath
"PSModulePath=$env:PSModulePath" >> $env:GITHUB_ENV

if (-not $ForTest) {
return
}

"TEMP=$env:RUNNER_TEMP" >> $env:GITHUB_ENV
Save-Module -Name Pester -RequiredVersion 4.10.1 -Path $modulePath -Force
if (-not $UseAzAuth) {
Save-Module -Name Microsoft.PowerShell.SecretManagement, Microsoft.PowerShell.SecretStore -Path $modulePath -Force
}

$headers = @{
'User-Agent' = 'PSResourceGet-CI'
Accept = 'application/vnd.github+json'
Authorization = "Bearer $env:GH_TOKEN"
}
$releases = Invoke-RestMethod 'https://api.github.com/repos/PowerShell/DSC/releases' -Headers $headers -MaximumRetryCount 3
$release = $releases | Where-Object { -not $_.draft } | Sort-Object published_at -Descending | Select-Object -First 1
$architecture = switch ([Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()) {
'X64' { 'x86_64' }
'Arm64' { 'aarch64' }
default { throw "Unsupported DSC architecture: $_" }
}
$platform = if ($IsWindows) { 'pc-windows-msvc' } elseif ($IsMacOS) { 'apple-darwin' } else { 'linux' }
$extension = if ($IsWindows) { '.zip' } else { '.tar.gz' }
$assets = @($release.assets | Where-Object { $_.name.EndsWith("-$architecture-$platform$extension") })
if ($assets.Count -ne 1) {
throw "Expected one DSC asset for $architecture-$platform in release $($release.tag_name); found $($assets.Count)."
}
$dscPath = Join-Path $env:RUNNER_TEMP 'dsc'
$null = New-Item $dscPath -ItemType Directory -Force
$archive = Join-Path $env:RUNNER_TEMP $assets[0].name
Invoke-WebRequest $assets[0].browser_download_url -OutFile $archive
if ($IsWindows) {
Expand-Archive $archive -DestinationPath $dscPath -Force
}
else {
& tar -xzf $archive -C $dscPath
if ($LASTEXITCODE -ne 0) { throw 'Failed to extract DSC.' }
}
$executableName = if ($IsWindows) { 'dsc.exe' } else { 'dsc' }
$executables = @(Get-ChildItem $dscPath -Recurse -File -Filter $executableName)
if ($executables.Count -ne 1) {
throw "Expected one DSC executable; found $($executables.Count)."
}
& $executables[0].FullName --version
if ($LASTEXITCODE -ne 0) { throw 'DSC could not start.' }
"DSC_ROOT=$($executables[0].DirectoryName)" >> $env:GITHUB_ENV

if (-not $UseAzAuth) {
# Windows PSResourceGet discovers an .exe; Unix discovers the netcore .dll.
$assetName = if ($IsWindows) { 'Microsoft.NetFx48.NuGet.CredentialProvider.zip' } else { 'Microsoft.Net8.NuGet.CredentialProvider.tar.gz' }
$providerArchive = Join-Path $env:RUNNER_TEMP $assetName
$providerPath = Join-Path $env:RUNNER_TEMP 'credential-provider'
$null = New-Item $providerPath -ItemType Directory -Force
Invoke-WebRequest "https://github.com/microsoft/artifacts-credprovider/releases/download/v2.0.4/$assetName" -OutFile $providerArchive
if ($IsWindows) {
Expand-Archive $providerArchive -DestinationPath $providerPath -Force
}
else {
& tar -xzf $providerArchive -C $providerPath
if ($LASTEXITCODE -ne 0) { throw 'Failed to extract the credential provider.' }
}
$pluginsPath = Join-Path $HOME '.nuget/plugins'
$null = New-Item $pluginsPath -ItemType Directory -Force
Copy-Item (Join-Path $providerPath 'plugins/*') $pluginsPath -Recurse -Force
$providerFile = if ($IsWindows) { 'netfx/CredentialProvider.Microsoft/CredentialProvider.Microsoft.exe' } else { 'netcore/CredentialProvider.Microsoft/CredentialProvider.Microsoft.dll' }
if (-not (Test-Path (Join-Path $pluginsPath $providerFile))) {
throw 'The credential provider was not installed in the expected discovery location.'
}
}
85 changes: 85 additions & 0 deletions .github/scripts/Invoke-CITests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateSet('Full', 'AzAuth')]
[string] $Suite
)

$ErrorActionPreference = 'Stop'
$root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
$config = Get-Content (Join-Path $root 'package.config.json') -Raw | ConvertFrom-Json
$testPath = Join-Path $root $config.TestPath
$modulePath = Join-Path (Join-Path $root $config.BuildOutputPath) $config.ModuleName
$acrFiles = @(
'FindPSResourceContainerRegistryServer.Tests.ps1'
'InstallPSResourceContainerRegistryServer.Tests.ps1'
'PublishPSResourceContainerRegistryServer.Tests.ps1'
)
$files = @(Get-ChildItem $testPath -Recurse -Filter '*.Tests.ps1' -File | Where-Object {
$Suite -eq 'Full' -or $_.Name -in $acrFiles
})
if ($files.Count -eq 0) { throw "No test files selected for suite $Suite." }

$env:USINGAZAUTH = ($Suite -eq 'AzAuth').ToString().ToLowerInvariant()
$repositoryNamesFolder = Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'TempModules'
$null = New-Item $repositoryNamesFolder -ItemType Directory -Force
$null = New-Item (Join-Path $repositoryNamesFolder 'ACRTestRepositoryNames.txt') -ItemType File -Force

if ($Suite -eq 'Full') {
foreach ($name in 'TENANTID', 'GITHUB_USERNAME', 'ADO_USERNAME', 'MAPPED_GITHUB_PAT', 'MAPPED_ADO_PUBLIC_PAT', 'MAPPED_ADO_PRIVATE_PAT', 'MAPPED_ADO_PRIVATE_REPO_URL') {
if ([string]::IsNullOrWhiteSpace([Environment]::GetEnvironmentVariable($name))) {
throw "Missing $name. Configure the ci-integration environment as described in README.md."
}
}
Import-Module Microsoft.PowerShell.SecretManagement
Import-Module Microsoft.PowerShell.SecretStore
Set-SecretStoreConfiguration -Authentication None -Interaction None -Confirm:$false
Register-SecretVault -Name SecretStore -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault

$acrToken = & az account get-access-token --resource 'https://management.azure.com/' --query accessToken --output tsv --only-show-errors
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($acrToken)) {
throw 'Could not acquire an ARM token for the ACR SecretStore tests.'
}
Write-Host "::add-mask::$acrToken"
Set-Secret -Name $env:TENANTID -Secret (ConvertTo-SecureString $acrToken -AsPlainText -Force) -Vault SecretStore

# Azure DevOps needs its own token audience, not an ARM access token.
$adoToken = & az account get-access-token --resource '499b84ac-1321-427f-aa17-267ca6975798' --query accessToken --output tsv --only-show-errors
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($adoToken)) {
throw 'Could not acquire an Azure DevOps token for the credential provider tests.'
}
Write-Host "::add-mask::$adoToken"
$env:VSS_NUGET_EXTERNAL_FEED_ENDPOINTS = @{
endpointCredentials = @(@{
endpoint = 'https://pkgs.dev.azure.com/powershell-rel/PSResourceGet/_packaging/psrg-credprovidertest/nuget/v2'
username = 'ci'
password = $adoToken
})
} | ConvertTo-Json -Compress -Depth 4
}

# Run in this fresh shell, never the process that imported the bootstrap module.
Import-Module Pester -RequiredVersion 4.10.1 -Force
Import-Module $modulePath -Force
Set-Location $testPath
$resultFile = Join-Path $testPath 'result.pester.xml'
try {
# Preserve the existing suite's handling of expected non-terminating errors.
$ErrorActionPreference = 'Continue'
$result = Invoke-Pester -Script $files.FullName -Tag CI -ExcludeTag ManualValidationOnly `
-OutputFormat NUnitXml -OutputFile $resultFile -PassThru
}
finally {
$ErrorActionPreference = 'Stop'
}
if (-not (Test-Path $resultFile) -or $null -eq $result -or $result.TotalCount -eq 0) {
throw "Suite $Suite did not produce test results."
}
if ($result.FailedCount -gt 0) {
throw "$($result.FailedCount) Pester tests failed in suite $Suite."
}
# Expected native failures (for example DSC negative tests) must not fail the shell.
$global:LASTEXITCODE = 0
27 changes: 27 additions & 0 deletions .github/scripts/Remove-CITestRepositories.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

$ErrorActionPreference = 'Stop'
$repositoryNamesFolder = Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'TempModules'
$repositoryNamesFile = Join-Path $repositoryNamesFolder 'ACRTestRepositoryNames.txt'
if (-not (Test-Path $repositoryNamesFile)) {
Write-Warning 'ACR tests did not initialize their cleanup file; no repositories will be deleted.'
return
}
$names = @(Get-Content $repositoryNamesFile | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Sort-Object -Unique)
if ($names.Count -eq 0) {
return
}
$repositories = & az acr repository list --name psresourcegettest --output json --only-show-errors
if ($LASTEXITCODE -ne 0) { throw 'Could not list ACR repositories for cleanup.' }
$repositories = $repositories | ConvertFrom-Json
foreach ($name in $names) {
# Only delete GUID-suffixed packages produced by the publish tests, never fixtures.
if ($name -notmatch '^temp-(testmodule|testmodulewithoutrequiredmodule-|testscript|testscriptwithexternaldeps|scriptwithoutemptylinesinmetadata|scriptwithoutemptylinesbetweencommentblocks)[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') {
throw "Refusing to delete unexpected ACR repository name: $name"
}
if ($name -in $repositories) {
& az acr repository delete --name psresourcegettest --repository $name --yes --only-show-errors
if ($LASTEXITCODE -ne 0) { throw "Failed to delete test repository $name." }
}
}
Loading
Loading