diff --git a/.cspell.json b/.cspell.json index fe5bc34..7a6da8b 100644 --- a/.cspell.json +++ b/.cspell.json @@ -20,6 +20,7 @@ "unshown" ], "ignoreWords": [ + "Deadlydog", "devcontainer", "pkgs", "pwsh" diff --git a/.github/workflows/build-and-test-powershell-module.yml b/.github/workflows/build-and-test-powershell-module.yml index df3ddd1..0c1572f 100644 --- a/.github/workflows/build-and-test-powershell-module.yml +++ b/.github/workflows/build-and-test-powershell-module.yml @@ -111,11 +111,19 @@ jobs: - name: Run Pester tests on Windows PowerShell to ensure backward compatibility shell: powershell + env: + AZURE_ARTIFACTS_TESTING_FEED_PAT: ${{ secrets.DEADLYDOG_OPENSOURCEPACKAGESFEED_AZDO_PAT }} run: | Write-Output "Pester version being used:" Import-Module -Name Pester Get-Module -Name Pester + Write-Output "PowerShell version being used:" + $PSVersionTable + + Write-Output "Host information:" + $host + Write-Output "Running all Pester tests in the repo:" $pesterConfig = New-PesterConfiguration @{ Output = @{ Verbosity = 'Detailed' } @@ -130,11 +138,19 @@ jobs: - name: Run Pester tests and generate code coverage report shell: pwsh + env: + AZURE_ARTIFACTS_TESTING_FEED_PAT: ${{ secrets.DEADLYDOG_OPENSOURCEPACKAGESFEED_AZDO_PAT }} run: | Write-Output "Pester version being used:" Import-Module -Name Pester Get-Module -Name Pester + Write-Output "PowerShell version being used:" + $PSVersionTable + + Write-Output "Host information:" + $host + Write-Output "Running all Pester tests in the repo:" $pesterConfig = New-PesterConfiguration @{ Output = @{ Verbosity = 'Detailed' } diff --git a/.github/workflows/build-test-and-deploy-powershell-module.yml b/.github/workflows/build-test-and-deploy-powershell-module.yml index 2ded4e4..017f5d1 100644 --- a/.github/workflows/build-test-and-deploy-powershell-module.yml +++ b/.github/workflows/build-test-and-deploy-powershell-module.yml @@ -82,6 +82,8 @@ jobs: - name: Run smoke tests shell: pwsh + env: + AZURE_ARTIFACTS_TESTING_FEED_PAT: ${{ secrets.DEADLYDOG_OPENSOURCEPACKAGESFEED_AZDO_PAT }} run: | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1" @@ -123,6 +125,8 @@ jobs: - name: Run smoke tests shell: powershell + env: + AZURE_ARTIFACTS_TESTING_FEED_PAT: ${{ secrets.DEADLYDOG_OPENSOURCEPACKAGESFEED_AZDO_PAT }} run: | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1" @@ -189,6 +193,8 @@ jobs: - name: Run smoke tests shell: pwsh + env: + AZURE_ARTIFACTS_TESTING_FEED_PAT: ${{ secrets.DEADLYDOG_OPENSOURCEPACKAGESFEED_AZDO_PAT }} run: | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1" @@ -230,6 +236,8 @@ jobs: - name: Run smoke tests shell: powershell + env: + AZURE_ARTIFACTS_TESTING_FEED_PAT: ${{ secrets.DEADLYDOG_OPENSOURCEPACKAGESFEED_AZDO_PAT }} run: | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index feab2e8..272ddc1 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -20,7 +20,7 @@ "isDefault": true }, "dependsOn": [ - "Run all Pester tests" + "Run Pester tests in Docker" ] }, { @@ -140,6 +140,51 @@ "problemMatcher": [ "$func-powershell-watch" ] + }, + { + "label": "Build Docker image for Pester tests", + "type": "shell", + "options": { + "shell": { + "executable": "pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + }, + "command": "docker build -t pester-tests:latest .", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated", + "clear": true + }, + "problemMatcher": [] + }, + { + "label": "Run Pester tests in Docker", + "type": "shell", + "options": { + "shell": { + "executable": "pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + }, + "command": "docker run --rm -e AZURE_ARTIFACTS_TESTING_FEED_PAT=$Env:AZURE_ARTIFACTS_TESTING_FEED_PAT pester-tests:latest", + "dependsOn": [ + "Build Docker image for Pester tests" + ], + "group": "test", + "presentation": { + "reveal": "always", + "panel": "dedicated", + "clear": true + }, + "problemMatcher": [] } ] } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8f87852 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +# Use .NET 10 runtime as base image +FROM mcr.microsoft.com/dotnet/sdk:10.0 + +# Install PowerShell +RUN apt-get update && \ + apt-get install -y curl gnupg apt-transport-https && \ + curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - && \ + echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-debian-bullseye-prod bullseye main" > /etc/apt/sources.list.d/microsoft.list && \ + apt-get update && \ + apt-get install -y powershell && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Create working directory +WORKDIR /workspace + +# Copy the module and tests into the container +COPY . . + +# Set PowerShell as the default shell +SHELL ["pwsh", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] + +# Install required PowerShell modules +RUN Install-Module -Name PowerShellGet -RequiredVersion 2.2.5 -Force && \ + Install-Module -Name Pester -Force + +# Run Pester tests +ENTRYPOINT ["pwsh", "-NoProfile", "-Command", "Invoke-Pester -Configuration (New-PesterConfiguration @{ Output = @{ Verbosity = 'Detailed' }})"] diff --git a/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 b/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 deleted file mode 100644 index 5d90e4b..0000000 --- a/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 +++ /dev/null @@ -1,321 +0,0 @@ -# These are Integration tests (not unit tests). -# This means that these tests will actually reach out to the specified $FeedUrl and connect/authenticate against it. -# In order for these tests to run successfully: -# - You need to use a real Azure Artifacts $FeedUrl and a real module to import from it. -# Ideally we would mock out any external/infrastructure dependencies; I just haven't had time to yet so for now hit the real dependencies. - -param -( - [Parameter(Mandatory = $false, HelpMessage = 'The Personal Access Token to use to connect to the Azure Artifacts feed.')] - [string] $AzureArtifactsPersonalAccessToken = 'YourPatGoesHereButDoNotCommitItToSourceControl' -) - -Set-StrictMode -Version Latest -[string] $RepositoryRootDirectoryPath = Split-Path -Path $PSScriptRoot -Parent -[string] $moduleFilePathToTest = (Join-Path $RepositoryRootDirectoryPath -ChildPath 'src\AzureArtifactsPowerShellModuleHelper\AzureArtifactsPowerShellModuleHelper.psm1') | Resolve-Path -Write-Verbose "Importing the module file '$moduleFilePathToTest' to run tests against it." -Verbose -Import-Module -Name $moduleFilePathToTest -Force -[string] $ModuleNameBeingTested = ((Split-Path -Path $moduleFilePathToTest -Leaf) -split '\.')[0] # Filename without the extension. - -########################################################### -# You will need to update the following variables with info to pull a real package down from a real feed. -########################################################### -# [string] $FeedUrl = 'https://pkgs.dev.azure.com/Organization/_packaging/Feed/nuget/v2' -[string] $FeedUrl = 'https://pkgs.dev.azure.com/iqmetrix/_packaging/iqmetrix/nuget/v2' -[string] $PowerShellModuleName = 'IQ.DataCenter.ServerConfiguration' -[string] $ValidOlderModuleVersionThatExists = '1.0.40' -[string] $InvalidModuleVersionThatDoesNotExist = '1.0.99999' -[string] $ValidModulePrereleaseVersionThatExists = '1.0.66-ci20191121T214736' -[System.Security.SecureString] $SecurePersonalAccessToken = ($AzureArtifactsPersonalAccessToken | ConvertTo-SecureString -AsPlainText -Force) -[PSCredential] $Credential = New-Object System.Management.Automation.PSCredential 'Username@DoesNotMatter.com', $SecurePersonalAccessToken -[System.Version] $MinimumRequiredPowerShellGetModuleVersion = [System.Version]::Parse('2.2.1') - -function Remove-PsRepository([string] $feedUrl) -{ - $repositories = Get-PSRepository - - # PowerShellGet v2 uses SourceLocation and v3 uses Uri for the feed URL of Get-PSRepository, so check both. - if ($repositories -and $repositories[0].PSObject.Properties.Name -contains 'SourceLocation') - { - $repositories | Where-Object { $_.SourceLocation -ieq $feedUrl } | Unregister-PSRepository - Get-PSRepository | Where-Object { $_.SourceLocation -ieq $feedUrl } | Should -BeNullOrEmpty - } - else - { - $repositories | Where-Object { $_.Uri -ieq $feedUrl } | Unregister-PSRepository - Get-PSRepository | Where-Object { $_.Uri -ieq $feedUrl } | Should -BeNullOrEmpty - } -} - -function Remove-PowerShellModule([string] $powerShellModuleName) -{ - Remove-Module -Name $powerShellModuleName -Force -ErrorAction SilentlyContinue - Get-Module -Name $powerShellModuleName | Should -BeNullOrEmpty -} - -function Uninstall-PowerShellModule([string] $powerShellModuleName) -{ - Remove-PowerShellModule -powerShellModuleName $powerShellModuleName - Uninstall-Module -Name $powerShellModuleName -AllVersions -Force - Get-Module -Name $powerShellModuleName -ListAvailable | Should -BeNullOrEmpty -} - -Describe 'Registering an Azure Artifacts PS Repository' { - Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { - Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested - - It 'Should register a new PS repository properly when relying in PAT from environmental variable' { - # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' - Remove-PsRepository -feedUrl $FeedUrl - - # Act. - [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository - - # Assert. - $repository | Should -Be $expectedRepository - Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty - } - - It 'Should return an existing PS repository properly when no Repository is specified' { - # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' - Remove-PsRepository -feedUrl $FeedUrl - Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository - - # Act. - [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl - - # Assert. - $repository | Should -Be $expectedRepository - Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty - } - - It 'Should return an existing PS repository properly when a different Repository is specified' { - # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' - Remove-PsRepository -feedUrl $FeedUrl - Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository - - # Act. - [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository 'NameThatShouldNotEndUpInThePSRepositories' - - # Assert. - $repository | Should -Be $expectedRepository - Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty - } - - It 'Should register a new PS repository properly when piping in the Feed URL' { - # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' - Remove-PsRepository -feedUrl $FeedUrl - - # Act. - [string] $repository = ($FeedUrl | Register-AzureArtifactsPSRepository -Repository $expectedRepository) - - # Assert. - $repository | Should -Be $expectedRepository - Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty - } - - It 'Should register a new PS repository properly when piping in the all of the parameters by property name' { - # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' - [PSCustomObject] $params = [PSCustomObject]@{ - FeedUrl = $FeedUrl - Repository = $expectedRepository - Credential = $Credential - Scope = 'CurrentUser' - } - Remove-PsRepository -feedUrl $FeedUrl - - # Act. - [string] $repository = ($params | Register-AzureArtifactsPSRepository) - - # Assert. - $repository | Should -Be $expectedRepository - Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty - } - - It 'Should import the PowerShellGet module properly when it is not imported yet' { - # Arrange. - Remove-PowerShellModule -powerShellModuleName PowerShellGet - - # Act. - Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl - - # Assert. - $powerShellGetModuleImported = Get-Module -Name PowerShellGet - $powerShellGetModuleImported | Should -Not -BeNullOrEmpty - $powerShellGetModuleImported.Version | Should -BeGreaterOrEqual $MinimumRequiredPowerShellGetModuleVersion - } - - It 'Should remove the existing too-low PowerShellGet module and import a newer version properly' { - # Arrange. - [System.Version] $notHighEnoughPowerShellGetModuleVersion = [System.Version]::Parse('2.0.4') - Remove-PowerShellModule -powerShellModuleName PowerShellGet - Install-Module -Name PowerShellGet -RequiredVersion $notHighEnoughPowerShellGetModuleVersion -Force - Import-Module -Name PowerShellGet -RequiredVersion $notHighEnoughPowerShellGetModuleVersion -Force - - # Act. - Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl - - # Assert. - $powerShellGetModuleImported = Get-Module -Name PowerShellGet - $powerShellGetModuleImported | Should -Not -BeNullOrEmpty - $powerShellGetModuleImported.Version | Should -BeGreaterOrEqual $MinimumRequiredPowerShellGetModuleVersion - } - - It 'Should import the PowerShellGet module properly when a high enough version is already imported' { - # Arrange. - Remove-PowerShellModule -powerShellModuleName PowerShellGet - Install-Module -Name PowerShellGet -MinimumVersion $MinimumRequiredPowerShellGetModuleVersion -Force - Import-Module -Name PowerShellGet -MinimumVersion $MinimumRequiredPowerShellGetModuleVersion - - # Act. - Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl - - # Assert. - $powerShellGetModuleImported = Get-Module -Name PowerShellGet - $powerShellGetModuleImported | Should -Not -BeNullOrEmpty - $powerShellGetModuleImported.Version | Should -BeGreaterOrEqual $MinimumRequiredPowerShellGetModuleVersion - } - } - - It 'Should register a new PS repository properly when passing in a valid Credential' { - # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' - Remove-PsRepository -feedUrl $FeedUrl - - # Act. - [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository -Credential $Credential - - # Assert. - $repository | Should -Be $expectedRepository - Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty - } - - Context 'When connecting to a feed without using a Credential' { - Mock Get-AzureArtifactsCredential { return $null } -ModuleName $ModuleNameBeingTested - - It 'Should not throw an error when credentials are not found. (Assumes the FeedUrl allows you to register it without a Credential)' { - # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' - Remove-PsRepository -feedUrl $FeedUrl - - # Act. - [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository - - # Assert. - $repository | Should -Be $expectedRepository - Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty - } - } -} - -Describe 'Finding a PowerShell module from Azure Artifacts' { - Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { - Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested - - It 'Should find the module properly' { - # Arrange. - [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl - [ScriptBlock] $action = { Find-AzureArtifactsModule -Name $PowerShellModuleName -Repository $repository } - - # Act and Assert. - $action | Should -Not -BeNullOrEmpty - } - } - - Context 'When connecting to a feed without using a Credential' { - Mock Get-AzureArtifactsCredential { return $null } -ModuleName $ModuleNameBeingTested - - It 'Should throw an exception' { - # Arrange. - [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl - [scriptblock] $action = { Find-AzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -ErrorAction Stop } - - # Act and Assert. - $action | Should -Throw "No match was found for the specified search criteria and module name '$PowerShellModuleName'. Try Get-PSRepository to see all available registered module repositories." - } - } - - It 'Should throw an exception when the Credential is invalid' { - # Arrange. - [System.Security.SecureString] $invalidPat = 'InvalidPat' | ConvertTo-SecureString -AsPlainText -Force - [PSCredential] $invalidCredential = New-Object System.Management.Automation.PSCredential 'Username@DoesNotMatter.com', $invalidPat - [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl - [scriptblock] $action = { Find-AzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -Credential $invalidCredential -ErrorAction Stop } - - # Act and Assert. - $action | Should -Throw "No match was found for the specified search criteria and module name '$PowerShellModuleName'. Try Get-PSRepository to see all available registered module repositories." - } -} - -Describe 'Installing a PowerShell module from Azure Artifacts' { - Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { - Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested - - It 'Should install the module properly' { - # Arrange. - [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl - [ScriptBlock] $action = { Install-AzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -ErrorAction Stop } - Uninstall-PowerShellModule -powerShellModuleName $PowerShellModuleName -ErrorAction 'SilentlyContinue' - - # Act and Assert. - $action | Should -Not -Throw - Get-Module -Name $PowerShellModuleName -ListAvailable | Should -Not -BeNullOrEmpty - } - } -} - -Describe 'Updating a PowerShell module from Azure Artifacts' { - Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { - Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested - - It 'Should update the module properly when already installed, resulting in 2 versions being installed' { - # Arrange. - [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl - [ScriptBlock] $action = { Update-AzureArtifactsModule -Name $PowerShellModuleName -ErrorAction Stop } - Uninstall-PowerShellModule -powerShellModuleName $PowerShellModuleName -ErrorAction 'SilentlyContinue' - Install-AzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -RequiredVersion $ValidOlderModuleVersionThatExists - - # Act and Assert. - $action | Should -Not -Throw - $modulesInstalled = Get-Module -Name $PowerShellModuleName -ListAvailable - $modulesInstalled | Should -Not -BeNullOrEmpty - $modulesInstalled.Count | Should -BeGreaterThan 1 - } - } -} - -Describe 'Installing-and-Updating a PowerShell module from Azure Artifacts' { - Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { - Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested - - It 'Should install the module properly' { - # Arrange. - [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl - [ScriptBlock] $action = { Install-AndUpdateAzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -ErrorAction Stop } - Uninstall-PowerShellModule -powerShellModuleName $PowerShellModuleName -ErrorAction 'SilentlyContinue' - - # Act and Assert. - $action | Should -Not -Throw - Get-Module -Name $PowerShellModuleName -ListAvailable | Should -Not -BeNullOrEmpty - } - - It 'Should update the module properly when already installed, resulting in 2 versions being installed' { - # Arrange. - [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl - [ScriptBlock] $action = { Install-AndUpdateAzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -ErrorAction Stop } - Uninstall-PowerShellModule -powerShellModuleName $PowerShellModuleName -ErrorAction 'SilentlyContinue' - Install-AzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -RequiredVersion $ValidOlderModuleVersionThatExists - - # Act and Assert. - $action | Should -Not -Throw - $modulesInstalled = Get-Module -Name $PowerShellModuleName -ListAvailable - $modulesInstalled | Should -Not -BeNullOrEmpty - $modulesInstalled.Count | Should -BeGreaterThan 1 - } - } -} diff --git a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 index ffdd9b0..61d7601 100644 --- a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 +++ b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 @@ -1,10 +1,366 @@ +# These are Integration tests (not unit tests). +# This means that these tests will actually reach out to the specified $FeedUrl and connect/authenticate against it. +# They will also interact with the local machines PowerShell PS Repositories and installed modules. +# In order for these tests to run successfully: +# - You need to use a real Azure Artifacts $FeedUrl and a real module to import from it. +# - You need to use a real Azure Artifacts Personal Access Token (PAT) with package read permissions, stored in an +# environment variable called AZURE_ARTIFACTS_TESTING_FEED_PAT, or you can hardcode it in the $AzureArtifactsPersonalAccessToken +# variable below, but do not commit that to source control. +# Ideally we would mock out any external/infrastructure dependencies; I just haven't had time to yet so for now hit the real dependencies. + using module '.\AzureArtifactsPowerShellModuleHelper.psm1' -# UPDATE ME: This is just example code. Replace the code below with your module's tests. -# Describe 'Get-HelloWorld' { -# It 'Should return "Hello, World!"' { -# $expected = 'Hello, World!' -# $result = Get-HelloWorld -# $result | Should -Be $expected -# } -# } +BeforeAll { + Set-StrictMode -Version Latest + + ########################################################### + # You will need to update the following variables with info to pull a real package down from a real feed. + ########################################################### + [string] $AzureArtifactsPersonalAccessToken = $Env:AZURE_ARTIFACTS_TESTING_FEED_PAT + [string] $FeedUrl = 'https://pkgs.dev.azure.com/deadlydog/2fdacc85-2f97-401e-bc68-69090c712dea/_packaging/AzureArtifactsPowerShellModuleHelper-Tests/nuget/v2' + [string] $PowerShellModuleName = 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests' + [string] $ValidOlderModuleVersionThatExists = '1.0.2' + [string] $InvalidModuleVersionThatDoesNotExist = '1.0.99999' + [string] $ValidModulePrereleaseVersionThatExists = '1.0.1-alpha' + + [string] $ModuleNameBeingTested = 'AzureArtifactsPowerShellModuleHelper' + [System.Security.SecureString] $SecurePersonalAccessToken = ($AzureArtifactsPersonalAccessToken | ConvertTo-SecureString -AsPlainText -Force) + [PSCredential] $Credential = New-Object System.Management.Automation.PSCredential 'Username@DoesNotMatter.com', $SecurePersonalAccessToken + [System.Version] $MinimumRequiredPowerShellGetModuleVersion = [System.Version]::Parse('2.2.1') + + function Remove-PsRepositoryAndPackageSource([string] $feedUrl) + { + function Get-RepositoriesForFeed([string] $feedUrlToFind) + { + # PowerShellGet v2 uses SourceLocation and v3 uses Uri for the feed URL of Get-PSRepository, so check both. + Get-PSRepository | Where-Object { + (($_.PSObject.Properties.Name -contains 'SourceLocation') -and ($_.SourceLocation -ieq $feedUrlToFind)) -or + (($_.PSObject.Properties.Name -contains 'Uri') -and ($_.Uri -ieq $feedUrlToFind)) + } + } + + function Get-PackageSourcesForFeed([string] $feedUrlToFind) + { + Get-PackageSource | Where-Object { $_.Location -ieq $feedUrlToFind } + } + + Get-RepositoriesForFeed -feedUrlToFind $feedUrl | + Select-Object -ExpandProperty Name -Unique | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | + ForEach-Object { + Unregister-PSRepository -Name $_ -ErrorAction SilentlyContinue -WarningAction SilentlyContinue + } + + Get-RepositoriesForFeed -feedUrlToFind $feedUrl | Should -BeNullOrEmpty + + Get-PackageSourcesForFeed -feedUrlToFind $feedUrl | + Select-Object -ExpandProperty Name -Unique | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | + ForEach-Object { + Unregister-PackageSource -Name $_ -ProviderName NuGet -ErrorAction SilentlyContinue -WarningAction SilentlyContinue > $null + } + + Get-PackageSourcesForFeed -feedUrlToFind $feedUrl | Should -BeNullOrEmpty + } + + function Remove-PowerShellModule([string] $powerShellModuleName) + { + Remove-Module -Name $powerShellModuleName -Force -ErrorAction SilentlyContinue + Get-Module -Name $powerShellModuleName | Should -BeNullOrEmpty + } + + function Uninstall-PowerShellModule([string] $powerShellModuleName) + { + Remove-PowerShellModule -powerShellModuleName $powerShellModuleName + Uninstall-Module -Name $powerShellModuleName -AllVersions -Force + Get-Module -Name $powerShellModuleName -ListAvailable | Should -BeNullOrEmpty + } +} + +Describe 'Tests setup' { + It 'Should have the Azure Artifacts Personal Access Token available' { + $AzureArtifactsPersonalAccessToken | Should -Not -BeNullOrEmpty + } + + It 'Should have the Feed URL available' { + $FeedUrl | Should -Not -BeNullOrEmpty + } + + It 'Should have the PowerShell module name to test with available' { + $PowerShellModuleName | Should -Not -BeNullOrEmpty + } +} + +Describe 'Registering an Azure Artifacts PS Repository' { + Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { + BeforeEach { + Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + } + + It 'Should register a new PS repository properly when relying on PAT from environmental variable' { + # Arrange. + [string] $expectedRepository = 'TempTestingFeed' + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl + + # Act. + [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository + + # Assert. + $repository | Should -Be $expectedRepository + Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty + } + + It 'Should return an existing PS repository properly when no Repository is specified' { + # Arrange. + [string] $expectedRepository = 'TempTestingFeed' + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl + Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository + + # Act. + [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl + + # Assert. + $repository | Should -Be $expectedRepository + Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty + } + + It 'Should return an existing PS repository properly when a different Repository is specified' { + # Arrange. + [string] $expectedRepository = 'TempTestingFeed' + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl + Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository + + # Act. + [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository 'NameThatShouldNotEndUpInThePSRepositories' + + # Assert. + $repository | Should -Be $expectedRepository + Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty + } + + It 'Should register a new PS repository properly when piping in the Feed URL' { + # Arrange. + [string] $expectedRepository = 'TempTestingFeed' + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl + + # Act. + [string] $repository = ($FeedUrl | Register-AzureArtifactsPSRepository -Repository $expectedRepository) + + # Assert. + $repository | Should -Be $expectedRepository + Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty + } + + It 'Should register a new PS repository properly when piping in the all of the parameters by property name' { + # Arrange. + [string] $expectedRepository = 'TempTestingFeed' + [PSCustomObject] $params = [PSCustomObject]@{ + FeedUrl = $FeedUrl + Repository = $expectedRepository + Credential = $Credential + Scope = 'CurrentUser' + } + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl + + # Act. + [string] $repository = ($params | Register-AzureArtifactsPSRepository) + + # Assert. + $repository | Should -Be $expectedRepository + Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty + } + + It 'Should import the PowerShellGet module properly when it is not imported yet' { + # Arrange. + Remove-PowerShellModule -powerShellModuleName PowerShellGet + + # Act. + Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl + + # Assert. + $powerShellGetModuleImported = Get-Module -Name PowerShellGet + $powerShellGetModuleImported | Should -Not -BeNullOrEmpty + $powerShellGetModuleImported.Version | Should -BeGreaterOrEqual $MinimumRequiredPowerShellGetModuleVersion + } + + It 'Should remove the existing too-low PowerShellGet module and import a newer version properly' { + # Arrange. + [System.Version] $notHighEnoughPowerShellGetModuleVersion = [System.Version]::Parse('2.0.4') + Remove-PowerShellModule -powerShellModuleName PowerShellGet + Install-Module -Name PowerShellGet -RequiredVersion $notHighEnoughPowerShellGetModuleVersion -Force + Import-Module -Name PowerShellGet -RequiredVersion $notHighEnoughPowerShellGetModuleVersion -Force + + # Act. + Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl + + # Assert. + $powerShellGetModuleImported = Get-Module -Name PowerShellGet + $powerShellGetModuleImported | Should -Not -BeNullOrEmpty + $powerShellGetModuleImported.Version | Should -BeGreaterOrEqual $MinimumRequiredPowerShellGetModuleVersion + } + + It 'Should import the PowerShellGet module properly when a high enough version is already imported' { + # Arrange. + Remove-PowerShellModule -powerShellModuleName PowerShellGet + Install-Module -Name PowerShellGet -MinimumVersion $MinimumRequiredPowerShellGetModuleVersion -Force + Import-Module -Name PowerShellGet -MinimumVersion $MinimumRequiredPowerShellGetModuleVersion + + # Act. + Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl + + # Assert. + $powerShellGetModuleImported = Get-Module -Name PowerShellGet + $powerShellGetModuleImported | Should -Not -BeNullOrEmpty + $powerShellGetModuleImported.Version | Should -BeGreaterOrEqual $MinimumRequiredPowerShellGetModuleVersion + } + } + + It 'Should register a new PS repository properly when passing in a valid Credential' { + # Arrange. + [string] $expectedRepository = 'TempTestingFeed' + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl + + # Act. + [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository -Credential $Credential + + # Assert. + $repository | Should -Be $expectedRepository + Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty + } + + Context 'When connecting to a feed without using a Credential' { + BeforeEach { + Mock Get-AzureArtifactsCredential { return $null } -ModuleName $ModuleNameBeingTested + } + + It 'Should not throw an error when credentials are not found. (Assumes the FeedUrl allows you to register it without a Credential)' { + # Arrange. + [string] $expectedRepository = 'TempTestingFeed' + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl + + # Act. + [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository + + # Assert. + $repository | Should -Be $expectedRepository + Get-PSRepository -Name $repository | Should -Not -BeNullOrEmpty + } + } +} + +Describe 'Finding a PowerShell module from Azure Artifacts' { + Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { + BeforeEach { + Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + } + + It 'Should find the module properly' { + # Arrange. + [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl + [ScriptBlock] $action = { Find-AzureArtifactsModule -Name $PowerShellModuleName -Repository $repository } + + # Act and Assert. + $action | Should -Not -BeNullOrEmpty + } + } + + Context 'When connecting to a feed without using a Credential' { + BeforeEach { + Mock Get-AzureArtifactsCredential { return $null } -ModuleName $ModuleNameBeingTested + } + + It 'Should throw an exception' { + # Arrange. + [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl + [scriptblock] $action = { Find-AzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -ErrorAction Stop } + + # Act and Assert. + $action | Should -Throw "Unable to find repository '$repository'. Use Get-PSRepository to see all available repositories." + } + } + + It 'Should throw an exception when the Credential is invalid' { + # Arrange. + [System.Security.SecureString] $invalidPat = 'InvalidPat' | ConvertTo-SecureString -AsPlainText -Force + [PSCredential] $invalidCredential = New-Object System.Management.Automation.PSCredential 'Username@DoesNotMatter.com', $invalidPat + [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl + [scriptblock] $action = { Find-AzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -Credential $invalidCredential -ErrorAction Stop } + + # Act and Assert. + $action | Should -Throw "No match was found for the specified search criteria and module name '$PowerShellModuleName'. Try Get-PSRepository to see all available registered module repositories." + } +} + +Describe 'Installing a PowerShell module from Azure Artifacts' { + Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { + BeforeEach { + Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + } + + It 'Should install the module properly' { + # Arrange. + [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl + [ScriptBlock] $action = { Install-AzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -ErrorAction Stop } + Uninstall-PowerShellModule -powerShellModuleName $PowerShellModuleName -ErrorAction 'SilentlyContinue' + + # Act and Assert. + $action | Should -Not -Throw + Get-Module -Name $PowerShellModuleName -ListAvailable | Should -Not -BeNullOrEmpty + } + } +} + +Describe 'Updating a PowerShell module from Azure Artifacts' { + Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { + BeforeEach { + Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + } + + It 'Should update the module properly when already installed, resulting in 2 versions being installed' { + # Arrange. + [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl + [ScriptBlock] $action = { Update-AzureArtifactsModule -Name $PowerShellModuleName -ErrorAction Stop } + Uninstall-PowerShellModule -powerShellModuleName $PowerShellModuleName -ErrorAction 'SilentlyContinue' + Install-AzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -RequiredVersion $ValidOlderModuleVersionThatExists + + # Act and Assert. + $action | Should -Not -Throw + $modulesInstalled = Get-Module -Name $PowerShellModuleName -ListAvailable + $modulesInstalled | Should -Not -BeNullOrEmpty + $modulesInstalled.Count | Should -BeGreaterThan 1 + } + } +} + +Describe 'Installing-and-Updating a PowerShell module from Azure Artifacts' { + Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { + BeforeEach { + Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + } + + It 'Should install the module properly' { + # Arrange. + [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl + [ScriptBlock] $action = { Install-AndUpdateAzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -ErrorAction Stop } + Uninstall-PowerShellModule -powerShellModuleName $PowerShellModuleName -ErrorAction 'SilentlyContinue' + + # Act and Assert. + $action | Should -Not -Throw + Get-Module -Name $PowerShellModuleName -ListAvailable | Should -Not -BeNullOrEmpty + } + + It 'Should update the module properly when already installed, resulting in 2 versions being installed' { + # Arrange. + [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl + [ScriptBlock] $action = { Install-AndUpdateAzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -ErrorAction Stop } + Uninstall-PowerShellModule -powerShellModuleName $PowerShellModuleName -ErrorAction 'SilentlyContinue' + Install-AzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -RequiredVersion $ValidOlderModuleVersionThatExists + + # Act and Assert. + $action | Should -Not -Throw + $modulesInstalled = Get-Module -Name $PowerShellModuleName -ListAvailable + $modulesInstalled | Should -Not -BeNullOrEmpty + $modulesInstalled.Count | Should -BeGreaterThan 1 + } + } +} diff --git a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.psm1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.psm1 index 42b4d6f..71133e6 100644 --- a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.psm1 +++ b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.psm1 @@ -91,16 +91,12 @@ function Register-AzureArtifactsPSRepository $psRepositories = Get-PSRepository # PowerShellGet v2 uses SourceLocation and v3 uses Uri for the feed URL of Get-PSRepository, so check both. - [PSCustomObject] $existingPsRepositoryForFeed = $null - if ($psRepositories -and $psRepositories[0].PSObject.Properties.Name -contains 'SourceLocation') { - $existingPsRepositoryForFeed = $psRepositories | - Where-Object { $_.SourceLocation -ieq $feedUrl } | - Select-Object -First 1 - } else { - $existingPsRepositoryForFeed = $psRepositories | - Where-Object { $_.Uri -ieq $feedUrl } | - Select-Object -First 1 - } + [PSCustomObject] $existingPsRepositoryForFeed = $psRepositories | + Where-Object { + (($_.PSObject.Properties.Name -contains 'SourceLocation') -and ($_.SourceLocation -ieq $feedUrl)) -or + (($_.PSObject.Properties.Name -contains 'Uri') -and ($_.Uri -ieq $feedUrl)) + } | + Select-Object -First 1 [bool] $psRepositoryIsAlreadyRegistered = ($null -ne $existingPsRepositoryForFeed) if ($psRepositoryIsAlreadyRegistered) diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 new file mode 100644 index 0000000..89502b6 --- /dev/null +++ b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 @@ -0,0 +1,131 @@ +# +# Module manifest for module 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests' +# +# Generated by: Dan.Schroeder +# +# Generated on: 4/24/2026 +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1' + +# Version number of this module. +ModuleVersion = '1.3.0' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = '26dbeb89-35a3-4aeb-a7cd-2372cac0f246' + +# Author of this module +Author = 'Dan.Schroeder' + +# Company or vendor of this module +CompanyName = 'Dan.Schroeder' + +# Copyright statement for this module +Copyright = '(c) Dan.Schroeder. All rights reserved.' + +# Description of the functionality provided by this module +Description = 'This is a dummy module used for testing that the AzureArtifactsPowerShellModuleHelper module can install packages from an Azure DevOps Artifacts feed.' + +# Minimum version of the PowerShell engine required by this module +# PowerShellVersion = '' + +# Name of the PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# DotNetFrameworkVersion = '' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# ClrVersion = '' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +# RequiredModules = @() + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = 'Get-HelloWorld' + +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = @() + +# Variables to export from this module +VariablesToExport = '*' + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = @() + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/deadlydog/AzureArtifactsPowerShellModuleHelper' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # Prerelease string of this module + Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + + } # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1 new file mode 100644 index 0000000..c6037e8 --- /dev/null +++ b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1 @@ -0,0 +1,10 @@ +# This is a fake dummy module that will be published to an Azure DevOps Artifacts feed. +# This is used when testing the AzureArtifactsPowerShellModuleHelper module to ensure that +# it can successfully install packages from an Azure DevOps Artifacts feed. + +function Get-HelloWorld { + [CmdletBinding()] + Param () + + Write-Output "Hello, World!" +} diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 new file mode 100644 index 0000000..46607e2 --- /dev/null +++ b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 @@ -0,0 +1,32 @@ +# Use this script to publish versions of the FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests module. + +[string] $versionToPublish = '1.3.0' # Update this value to the version you want to publish. +[string] $prereleaseVersionLabel = '' # Leave blank for no prerelease label. + +[string] $azureArtifactsPersonalAccessToken = $Env:AZURE_ARTIFACTS_TESTING_FEED_PAT +[string] $feedUrl = 'https://pkgs.dev.azure.com/deadlydog/2fdacc85-2f97-401e-bc68-69090c712dea/_packaging/AzureArtifactsPowerShellModuleHelper-Tests/nuget/v3/index.json' +[string] $moduleName = 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests' +[string] $moduleDirectoryPath = $PSScriptRoot +[string] $moduleManifestFilePath = Join-Path -Path $moduleDirectoryPath -ChildPath "$moduleName.psd1" +[PSCredential] $credential = New-Object System.Management.Automation.PSCredential('AzureArtifacts', (ConvertTo-SecureString -String $azureArtifactsPersonalAccessToken -AsPlainText -Force)) + +if (-not $azureArtifactsPersonalAccessToken) { + throw "The environment variable 'AZURE_ARTIFACTS_TESTING_FEED_PAT' is not set. Please set this variable to your Azure Artifacts Personal Access Token and try again." +} + +Write-Output "Updating the module manifest version number to '$versionToPublish' with prerelease label '$prereleaseVersionLabel'." +[string] $manifestContents = Get-Content -Path $moduleManifestFilePath -Raw +$versionReplacedContents = [regex]::Replace($manifestContents, "ModuleVersion = '.*?'", "ModuleVersion = '$versionToPublish'") +$prereleaseLabelReplacedContents = [regex]::Replace($versionReplacedContents, "Prerelease = '.*?'", "Prerelease = '$prereleaseVersionLabel'") +Set-Content -Path $moduleManifestFilePath -Value $prereleaseLabelReplacedContents -Encoding UTF8 -NoNewline +Test-ModuleManifest -Path $moduleManifestFilePath + +# Get the PSRepository if it exists, otherwise register it. +$psRepository = Get-PSResourceRepository | Where-Object { $_.Uri -eq $feedUrl } | Select-Object -First 1 +if (-not $psRepository) { + Write-Output "Registering the PSRepository for the feed URL '$feedUrl'." + $psRepository = Register-PSResourceRepository -Name DeadlydogTestingFeed -Uri $feedUrl -Trusted -PassThru +} + +Write-Output "Publishing new version of the module to '$($psRepository.Uri)'." +Publish-PSResource -Path $moduleDirectoryPath -Repository $psRepository.Name -ApiKey AzureDevOps -Credential $credential