From 74a6ff6a9f98d0fa7c755b2979537d29f093ad4f Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sat, 6 Sep 2025 21:19:17 -0500 Subject: [PATCH 01/18] Updating test cases to run properly on windows and linux and started the bulk of homebrew integration. --- .../ConvertFrom-3rdPartyInstall.Tests.ps1 | 2 + .../3rdParty/ConvertFrom-3rdPartyInstall.ps1 | 20 +-- .../Commands/Export-DevSetupEnv.Tests.ps1 | 72 +++++++++-- .../Private/Commands/Export-DevSetupEnv.ps1 | 8 +- .../Commands/Initialize-DevSetup.Tests.ps1 | 14 ++- .../Private/Commands/Initialize-DevSetup.ps1 | 35 +++--- .../Commands/Install-DevSetupEnv.Tests.ps1 | 30 +++-- .../Private/Commands/Install-DevSetupEnv.ps1 | 49 ++++---- .../Commands/Show-DevSetupEnvList.Tests.ps1 | 15 ++- .../Commands/Uninstall-DevSetupEnv.Tests.ps1 | 50 +++++--- .../Commands/Uninstall-DevSetupEnv.ps1 | 55 +++++---- .../Get-ChocolateyCacheFile.Tests.ps1 | 33 +++-- ...et-ChocolateyPackageDependencies.Tests.ps1 | 81 ++++++++++--- .../Get-ChocolateyPackageDependencies.ps1 | 7 +- .../Core/Install-CoreDependencies.Tests.ps1 | 4 +- .../Core/Install-CoreDependencies.ps1 | 16 +-- .../Private/Providers/Core/Install-Nuget.ps1 | 9 +- .../Homebrew/Find-Homebrew.Tests.ps1 | 114 ++++++++++++++++++ .../Providers/Homebrew/Find-Homebrew.ps1 | 25 ++++ .../Homebrew/Get-HomebrewCacheFile.ps1 | 7 ++ .../Homebrew/Get-HomebrewVersion.ps1 | 31 +++++ .../Providers/Homebrew/Install-Homebrew.ps1 | 39 +++++- .../Homebrew/Install-HomebrewPackage.ps1 | 42 +++++++ .../Invoke-HomebrewComponentsExport.ps1 | 80 ++++++++++++ .../Invoke-HomebrewComponentsInstall.ps1 | 31 +++++ .../Invoke-HomebrewComponentsUninstall.ps1 | 28 +++++ .../Providers/Homebrew/Read-HomebrewCache.ps1 | 14 +++ .../Homebrew/Test-HomebewInstalled.ps1 | 25 ++++ .../Test-HomebrewPackageInstalled.ps1 | 38 ++++++ .../Homebrew/Uninstall-HomebrewPackage.ps1 | 42 +++++++ .../Homebrew/Write-HomebrewCache.ps1 | 30 +++++ .../Test-PowershellModuleInstalled.Tests.ps1 | 78 ++++++++++-- .../Test-PowershellModuleInstalled.ps1 | 7 +- .../Providers/Scoop/Find-Scoop.Tests.ps1 | 101 +++++++++++++--- .../Scoop/Get-ScoopCacheFile.Tests.ps1 | 16 ++- .../Scoop/Test-ScoopInstalled.Tests.ps1 | 8 ++ .../Providers/Scoop/Test-ScoopInstalled.ps1 | 6 +- .../Utils/ConvertFrom-Base64.Tests.ps1 | 2 +- .../Private/Utils/ConvertTo-Base64.Tests.ps1 | 2 +- .../Utils/Get-DevSetupCachePath.Tests.ps1 | 48 +++++++- .../Utils/Get-DevSetupEnvPath.Tests.ps1 | 49 +++++++- .../Private/Utils/Get-DevSetupLogPath.ps1 | 13 ++ .../Private/Utils/Get-DevSetupPath.Tests.ps1 | 52 +++++++- .../Private/Utils/Invoke-ExternalCommand.ps1 | 19 +++ .../Utils/Optimize-DevSetupEnvs.Tests.ps1 | 27 +++-- .../Private/Utils/Optimize-DevSetupEnvs.ps1 | 20 +-- .../Utils/Start-DevSetupSelfUpdate.ps1 | 22 ++++ DevSetup/Private/Utils/Test-HasSudoAccess.ps1 | 13 ++ .../Utils/Test-OperatingSystem.Tests.ps1 | 79 +++++++++--- .../Private/Utils/Test-OperatingSystem.ps1 | 15 +-- .../Utils/Test-RunningAsAdmin.Tests.ps1 | 48 ++++---- DevSetup/Private/Utils/Write-NewConfig.ps1 | 32 +++-- .../Utils/Write-StatusMessage.Tests.ps1 | 2 + .../Private/Utils/Write-StatusMessage.ps1 | 10 ++ DevSetup/Public/Use-DevSetup.ps1 | 72 ++++++----- Remove-File.ps1 | 11 ++ runTests.ps1 | 2 +- 57 files changed, 1487 insertions(+), 313 deletions(-) create mode 100644 DevSetup/Private/Providers/Homebrew/Find-Homebrew.Tests.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Find-Homebrew.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Get-HomebrewCacheFile.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Get-HomebrewVersion.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Install-HomebrewPackage.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsExport.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsInstall.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsUninstall.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Read-HomebrewCache.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Test-HomebewInstalled.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Test-HomebrewPackageInstalled.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Uninstall-HomebrewPackage.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Write-HomebrewCache.ps1 create mode 100644 DevSetup/Private/Utils/Get-DevSetupLogPath.ps1 create mode 100644 DevSetup/Private/Utils/Invoke-ExternalCommand.ps1 create mode 100644 DevSetup/Private/Utils/Start-DevSetupSelfUpdate.ps1 create mode 100644 DevSetup/Private/Utils/Test-HasSudoAccess.ps1 create mode 100644 Remove-File.ps1 diff --git a/DevSetup/Private/3rdParty/ConvertFrom-3rdPartyInstall.Tests.ps1 b/DevSetup/Private/3rdParty/ConvertFrom-3rdPartyInstall.Tests.ps1 index e1383d0..c1e13c8 100644 --- a/DevSetup/Private/3rdParty/ConvertFrom-3rdPartyInstall.Tests.ps1 +++ b/DevSetup/Private/3rdParty/ConvertFrom-3rdPartyInstall.Tests.ps1 @@ -2,10 +2,12 @@ BeforeAll { . $PSScriptRoot\ConvertFrom-3rdPartyInstall.ps1 . $PSScriptRoot\..\..\..\DevSetup\Private\3rdParty\VisualStudio\ConvertFrom-VisualStudioInstall.ps1 . $PSScriptRoot\..\..\..\DevSetup\Private\3rdParty\VisualStudioCode\ConvertFrom-VisualStudioCodeInstall.ps1 + . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Test-OperatingSystem.ps1 Mock Write-Host { } Mock Write-Warning { } Mock ConvertFrom-VisualStudioInstall { $true } Mock ConvertFrom-VisualStudioCodeInstall { $true } + Mock Test-OperatingSystem { $true } } Describe "ConvertFrom-3rdPartyInstall" { diff --git a/DevSetup/Private/3rdParty/ConvertFrom-3rdPartyInstall.ps1 b/DevSetup/Private/3rdParty/ConvertFrom-3rdPartyInstall.ps1 index 540a0f6..8425ad3 100644 --- a/DevSetup/Private/3rdParty/ConvertFrom-3rdPartyInstall.ps1 +++ b/DevSetup/Private/3rdParty/ConvertFrom-3rdPartyInstall.ps1 @@ -3,15 +3,17 @@ Function ConvertFrom-3rdPartyInstall { [string]$Config ) - # Convert from Visual Studio installations - Write-Host "`nScanning Visual Studio installations..." -ForegroundColor Cyan - if (-not (ConvertFrom-VisualStudioInstall -Config $Config)) { - Write-Warning "Failed to convert Visual Studio installations, but continuing..." - } + if((Test-OperatingSystem -Windows)) { + # Convert from Visual Studio installations + Write-Host "`nScanning Visual Studio installations..." -ForegroundColor Cyan + if (-not (ConvertFrom-VisualStudioInstall -Config $Config)) { + Write-Warning "Failed to convert Visual Studio installations, but continuing..." + } - # Convert from Visual Studio Code installations - Write-Host "`nScanning Visual Studio Code installation..." -ForegroundColor Cyan - if (-not (ConvertFrom-VisualStudioCodeInstall -Config $Config)) { - Write-Warning "Failed to convert Visual Studio Code installation, but continuing..." + # Convert from Visual Studio Code installations + Write-Host "`nScanning Visual Studio Code installation..." -ForegroundColor Cyan + if (-not (ConvertFrom-VisualStudioCodeInstall -Config $Config)) { + Write-Warning "Failed to convert Visual Studio Code installation, but continuing..." + } } } \ No newline at end of file diff --git a/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1 b/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1 index a494767..3cbd499 100644 --- a/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1 +++ b/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1 @@ -4,9 +4,27 @@ BeforeAll { . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupLocalEnvPath.ps1 . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupCommunityEnvPath.ps1 . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Write-NewConfig.ps1 - Mock Get-DevSetupEnvPath { "TestDrive:\DevSetupEnvs" } - Mock Get-DevSetupLocalEnvPath { "TestDrive:\DevSetupEnvs\local"} - Mock Get-DevSetupCommunityEnvPath { "TestDrive:\DevSetupEnvs\community"} + if ($PSVersionTable.PSVersion.Major -eq 5) { + Mock Get-DevSetupEnvPath { "$TestDrive\DevSetup\DevSetupEnvs" } + Mock Get-DevSetupLocalEnvPath { "$TestDrive\DevSetup\DevSetupEnvs\local" } + Mock Get-DevSetupCommunityEnvPath { "$TestDrive\DevSetup\DevSetupEnvs\community" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6) { + if ($IsWindows) { + Mock Get-DevSetupEnvPath { "$TestDrive\DevSetup\DevSetupEnvs" } + Mock Get-DevSetupLocalEnvPath { "$TestDrive\DevSetup\DevSetupEnvs\local" } + Mock Get-DevSetupCommunityEnvPath { "$TestDrive\DevSetup\DevSetupEnvs\community" } + } + if ($IsLinux) { + Mock Get-DevSetupEnvPath { "$TestDrive/home/testuser/DevSetup/DevSetupEnvs" } + Mock Get-DevSetupLocalEnvPath { "$TestDrive/home/testuser/DevSetup/DevSetupEnvs/local" } + Mock Get-DevSetupCommunityEnvPath { "$TestDrive/home/testuser/DevSetup/DevSetupEnvs/community" } + } + if ($IsMacOS) { + Mock Get-DevSetupEnvPath { "$TestDrive/Users/TestUser/DevSetup/DevSetupEnvs" } + Mock Get-DevSetupLocalEnvPath { "$TestDrive/Users/TestUser/DevSetup/DevSetupEnvs/local" } + Mock Get-DevSetupCommunityEnvPath { "$TestDrive/Users/TestUser/DevSetup/DevSetupEnvs/community" } + } + } Mock Write-NewConfig { param($OutFile) $OutFile } Mock Write-Host { } Mock Write-Error { } @@ -17,17 +35,33 @@ Describe "Export-DevSetupEnv" { Context "When called with a valid name" { It "Should create the config file and return its path" { $result = Export-DevSetupEnv -Name "MyEnv" - $result | Should -Be "TestDrive:\DevSetupEnvs\local\MyEnv.devsetup" - Assert-MockCalled Write-NewConfig -Exactly 1 -Scope It -ParameterFilter { $OutFile -eq "TestDrive:\DevSetupEnvs\local\MyEnv.devsetup" } + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + $expectedPath = "$TestDrive\DevSetup\DevSetupEnvs\local\MyEnv.devsetup" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + $expectedPath = "$TestDrive/home/testuser/DevSetup/DevSetupEnvs/local/MyEnv.devsetup" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + $expectedPath = "$TestDrive/Users/TestUser/DevSetup/DevSetupEnvs/local/MyEnv.devsetup" + } + $result | Should -Be $expectedPath + Assert-MockCalled Write-NewConfig -Exactly 1 -Scope It -ParameterFilter { $OutFile -eq $expectedPath } Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "exported to" -and $ForegroundColor -eq "Green" } } } Context "When called with a valid path" { It "Should create the config file and return its path" { - $result = Export-DevSetupEnv -Path "TestDrive:\MyCustomPath\MyEnv.devsetup" - $result | Should -Be "TestDrive:\MyCustomPath\MyEnv.devsetup" - Assert-MockCalled Write-NewConfig -Exactly 1 -Scope It -ParameterFilter { $OutFile -eq "TestDrive:\MyCustomPath\MyEnv.devsetup" } + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + $result = Export-DevSetupEnv -Path "$TestDrive\MyCustomPath\MyEnv.devsetup" + $expectedPath = "$TestDrive\MyCustomPath\MyEnv.devsetup" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + $result = Export-DevSetupEnv -Path "$TestDrive/MyCustomPath/MyEnv.devsetup" + $expectedPath = "$TestDrive/MyCustomPath/MyEnv.devsetup" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + $result = Export-DevSetupEnv -Path "$TestDrive/MyCustomPath/MyEnv.devsetup" + $expectedPath = "$TestDrive/MyCustomPath/MyEnv.devsetup" + } + $result | Should -Be $expectedPath + Assert-MockCalled Write-NewConfig -Exactly 1 -Scope It -ParameterFilter { $OutFile -eq $expectedPath } Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "exported to" -and $ForegroundColor -eq "Green" } } } @@ -35,15 +69,31 @@ Describe "Export-DevSetupEnv" { Context "When called with a name that needs sanitization" { It "Should sanitize the name and warn" { $result = Export-DevSetupEnv -Name "Data Science Environment!" - $result | Should -Be "TestDrive:\DevSetupEnvs\local\DataScienceEnvironment.devsetup" + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + $expectedPath = "$TestDrive\DevSetup\DevSetupEnvs\local\DataScienceEnvironment.devsetup" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + $expectedPath = "$TestDrive/home/testuser/DevSetup/DevSetupEnvs/local/DataScienceEnvironment.devsetup" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + $expectedPath = "$TestDrive/Users/TestUser/DevSetup/DevSetupEnvs/local/DataScienceEnvironment.devsetup" + } + $result | Should -Be $expectedPath Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "sanitized" -and $ForegroundColor -eq "Yellow" } } } Context "When called with a path that needs sanitization" { It "Should sanitize the path and warn" { - $result = Export-DevSetupEnv -Path "TestDrive:\MyCustomPath\MyEnv!.devsetup" - $result | Should -Be "TestDrive:\MyCustomPath\MyEnv.devsetup" + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + $result = Export-DevSetupEnv -Path "$TestDrive\MyCustomPath\MyEnv!.devsetup" + $expectedPath = "$TestDrive\MyCustomPath\MyEnv.devsetup" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + $result = Export-DevSetupEnv -Path "$TestDrive/MyCustomPath/MyEnv!.devsetup" + $expectedPath = "$TestDrive/MyCustomPath/MyEnv.devsetup" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + $result = Export-DevSetupEnv -Path "$TestDrive/MyCustomPath/MyEnv!.devsetup" + $expectedPath = "$TestDrive/MyCustomPath/MyEnv.devsetup" + } + $result | Should -Be $expectedPath Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "sanitized" -and $ForegroundColor -eq "Yellow" } } } diff --git a/DevSetup/Private/Commands/Export-DevSetupEnv.ps1 b/DevSetup/Private/Commands/Export-DevSetupEnv.ps1 index b30fa3b..3de017d 100644 --- a/DevSetup/Private/Commands/Export-DevSetupEnv.ps1 +++ b/DevSetup/Private/Commands/Export-DevSetupEnv.ps1 @@ -65,7 +65,9 @@ Function Export-DevSetupEnv { [Parameter(Mandatory=$true, ParameterSetName="Export")] [string]$Name, [Parameter(Mandatory=$true, ParameterSetName="ExportPath")] - [string]$Path + [string]$Path, + [Parameter(Mandatory=$false)] + [switch]$DryRun ) $OutFile = $null if($PSBoundParameters.ContainsKey('Name')) { @@ -110,11 +112,11 @@ Function Export-DevSetupEnv { return $null } - $config = Write-NewConfig -OutFile $OutFile + $config = Write-NewConfig -OutFile $OutFile -DryRun:$DryRun if (-not $config) { Write-Error "Failed to create configuration file" return $null - } + } Write-Host "Configuration file exported to: $OutFile" -ForegroundColor Green return $OutFile } \ No newline at end of file diff --git a/DevSetup/Private/Commands/Initialize-DevSetup.Tests.ps1 b/DevSetup/Private/Commands/Initialize-DevSetup.Tests.ps1 index 2906d50..1bc3cb6 100644 --- a/DevSetup/Private/Commands/Initialize-DevSetup.Tests.ps1 +++ b/DevSetup/Private/Commands/Initialize-DevSetup.Tests.ps1 @@ -6,6 +6,9 @@ BeforeAll { . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupCommunityEnvPath.ps1 . $PSScriptRoot\..\..\..\DevSetup\Private\Providers\Core\Install-CoreDependencies.ps1 . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Initialize-DevSetupEnvs.ps1 + . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1 + . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupCachePath.ps1 + . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupLogPath.ps1 Mock Write-Host { } Mock Write-Error { } Mock Write-Verbose { } @@ -17,6 +20,9 @@ BeforeAll { Mock Test-Path { $false } Mock New-Item { } Mock Initialize-DevSetupEnvs { "TestDrive:\Users\Test\devsetup\envs" } + Mock Write-StatusMessage { } + Mock Get-DevSetupCachePath { "TestDrive:\Users\Test\devsetup\cache" } + Mock Get-DevSetupLogPath { "TestDrive:\Users\Test\devsetup\logs" } } Describe "Initialize-DevSetup" { @@ -37,7 +43,7 @@ Describe "Initialize-DevSetup" { Mock Install-CoreDependencies { $false } $result = Initialize-DevSetup $result | Should -Be $null - Assert-MockCalled Write-Error -Scope It -ParameterFilter { $Message -match "Failed to install core dependencies" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Failed to install core dependencies" -and $Verbosity -eq "Error" } } } @@ -47,7 +53,7 @@ Describe "Initialize-DevSetup" { $result = Initialize-DevSetup $result | Should -Be $true Assert-MockCalled New-Item -Exactly 0 -Scope It - Assert-MockCalled Write-Verbose -Scope It -ParameterFilter { $Message -match "already exists" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "already exists" -and $Verbosity -eq "Verbose" } } } @@ -56,7 +62,7 @@ Describe "Initialize-DevSetup" { Mock Initialize-DevSetupEnvs { $null } $result = Initialize-DevSetup $result | Should -Be $false - Assert-MockCalled Write-Error -Scope It -ParameterFilter { $Message -match "Failed to initialize DevSetup environment path" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Failed to initialize DevSetup environment path" -and $Verbosity -eq "Error" } } } @@ -65,7 +71,7 @@ Describe "Initialize-DevSetup" { Mock Install-CoreDependencies { throw "Unexpected error" } $result = Initialize-DevSetup $result | Should -Be $false - Assert-MockCalled Write-Error -Scope It -ParameterFilter { $Message -match "Failed to initialize DevSetup environment" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Failed to initialize DevSetup environment" -and $Verbosity -eq "Error" } } } } \ No newline at end of file diff --git a/DevSetup/Private/Commands/Initialize-DevSetup.ps1 b/DevSetup/Private/Commands/Initialize-DevSetup.ps1 index bd50b5f..e4c8cdb 100644 --- a/DevSetup/Private/Commands/Initialize-DevSetup.ps1 +++ b/DevSetup/Private/Commands/Initialize-DevSetup.ps1 @@ -64,12 +64,12 @@ Function Initialize-DevSetup { try { # Install core dependencies first - Write-Host "- Installing core dependencies..." -ForegroundColor Cyan + Write-StatusMessage "- Installing core dependencies..." -ForegroundColor Cyan if (-not (Install-CoreDependencies)) { - Write-Error "Failed to install core dependencies" + Write-StatusMessage "Failed to install core dependencies" -Verbosity Error return } - Write-Host "- Core dependencies installed successfully" -ForegroundColor Green + Write-StatusMessage "- Core dependencies installed successfully" -ForegroundColor Green # Define .devsetup folder path $devSetupPath = Get-DevSetupPath @@ -80,35 +80,40 @@ Function Initialize-DevSetup { New-Item -Path $devSetupPath -ItemType Directory -Force | Out-Null #Write-Host ".devsetup directory created successfully" -ForegroundColor Green } else { - Write-Verbose ".devsetup directory already exists at: $devSetupPath" + Write-StatusMessage ".devsetup directory already exists at: $devSetupPath" -Verbosity Verbose } Write-Host "" - Write-Host "- Installing community environments..." -ForegroundColor Cyan + Write-StatusMessage "- Installing community environments..." -ForegroundColor Cyan # Initialize DevSetup environments path $envSetupPath = Initialize-DevSetupEnvs if (-not $envSetupPath) { - Write-Error "Failed to initialize DevSetup environment path" + Write-StatusMessage "Failed to initialize DevSetup environment path" -Verbosity Error return $false } else { - Write-Host "- Community environments installed successfully" -ForegroundColor Green + Write-StatusMessage "- Community environments installed successfully" -ForegroundColor Green } Write-Host "" - Write-Host "Path Information: " -ForegroundColor Yellow - Write-Host "- DevSetup:" -ForegroundColor Cyan - Write-Host " - $devSetupPath" -ForegroundColor Gray - Write-Host "- Local Environments: " -ForegroundColor Cyan - Write-Host " - $($envSetupPath.Local)" -ForegroundColor Gray - Write-Host "- Community Environments: " -ForegroundColor Cyan - Write-Host " - $($envSetupPath.Community)" -ForegroundColor Gray + Write-StatusMessage "Path Information: " -ForegroundColor Yellow + Write-StatusMessage "- DevSetup:" -ForegroundColor Cyan + Write-StatusMessage " - $devSetupPath" -ForegroundColor Gray + Write-StatusMessage "- Local Environments: " -ForegroundColor Cyan + Write-StatusMessage " - $($envSetupPath.Local)" -ForegroundColor Gray + Write-StatusMessage "- Community Environments: " -ForegroundColor Cyan + Write-StatusMessage " - $($envSetupPath.Community)" -ForegroundColor Gray + Write-StatusMessage "- Logs:" -ForegroundColor Cyan + Write-StatusMessage " - $(Get-DevSetupLogPath)" -ForegroundColor Gray + Write-StatusMessage "- Cache:" -ForegroundColor Cyan + Write-StatusMessage " - $(Get-DevSetupCachePath)" -ForegroundColor Gray Write-Host "" # Return the path for use by other functions return $true } catch { - Write-Error "Failed to initialize DevSetup environment: $_" + Write-StatusMessage "Failed to initialize DevSetup environment: $_" -Verbosity Error + Write-StatusMessage $_.ScriptStackTrace -Verbosity Error return $false } } \ No newline at end of file diff --git a/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1 b/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1 index 05a5b21..fa540f2 100644 --- a/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1 +++ b/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1 @@ -5,7 +5,21 @@ BeforeAll { . $PSScriptRoot\..\..\..\DevSetup\Private\Providers\Scoop\Install-ScoopComponents.ps1 . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Read-ConfigurationFile.ps1 . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupEnvPath.ps1 - Mock Get-DevSetupEnvPath { "C:\DevSetupEnvs" } + . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1 + . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Test-OperatingSystem.ps1 + if ($PSVersionTable.PSVersion.Major -eq 5) { + Mock Get-DevSetupEnvPath { "C:\DevSetup" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6) { + if ($IsWindows) { + Mock Get-DevSetupEnvPath { "C:\DevSetup" } + } + if ($IsLinux) { + Mock Get-DevSetupEnvPath { "/home/testuser/devsetup" } + } + if ($IsMacOS) { + Mock Get-DevSetupEnvPath { "/Users/TestUser/devsetup" } + } + } Mock Test-Path { $true } Mock Read-ConfigurationFile { } Mock Install-PowershellModules { } @@ -16,6 +30,8 @@ BeforeAll { Mock Write-Warning { } Mock Invoke-Command { } Mock Invoke-Expression { } + Mock Write-StatusMessage { } + Mock Test-OperatingSystem { $true } } Describe "Install-DevSetupEnv" { @@ -25,7 +41,7 @@ Describe "Install-DevSetupEnv" { Mock Test-Path { $false } $result = Install-DevSetupEnv -Name "missing-env" $result | Should -Be $null - Assert-MockCalled Write-Error -Exactly 1 -Scope It -ParameterFilter { $Message -match "Environment file not found" } + Assert-MockCalled Write-StatusMessage -Exactly 1 -Scope It -ParameterFilter { $Message -match "Environment file not found" -and $Verbosity -eq "Error" } } } @@ -35,7 +51,7 @@ Describe "Install-DevSetupEnv" { Mock Read-ConfigurationFile { $null } $result = Install-DevSetupEnv -Name "bad-yaml" $result | Should -Be $null - Assert-MockCalled Write-Error -Exactly 1 -Scope It -ParameterFilter { $Message -match "Failed to parse YAML" } + Assert-MockCalled Write-StatusMessage -Exactly 1 -Scope It -ParameterFilter { $Message -match "Failed to parse YAML" -and $Verbosity -eq "Error" } } } @@ -48,7 +64,7 @@ Describe "Install-DevSetupEnv" { Assert-MockCalled Install-PowershellModules -Exactly 1 -Scope It Assert-MockCalled Install-ChocolateyPackages -Exactly 1 -Scope It Assert-MockCalled Install-ScoopComponents -Exactly 1 -Scope It - Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "No commands found" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "No commands found" } } } @@ -63,8 +79,8 @@ Describe "Install-DevSetupEnv" { $result = Install-DevSetupEnv -Name "cmd-env" $result | Should -Be $null Assert-MockCalled Invoke-Expression -Exactly 2 -Scope It - Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "Executing command for: git" } - Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "Executing command for: nodejs" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Executing command for: git" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Executing command for: nodejs" } } } @@ -78,7 +94,7 @@ Describe "Install-DevSetupEnv" { Mock Read-ConfigurationFile { @{ devsetup = @{ commands = $commands } } } $result = Install-DevSetupEnv -Name "missing-cmd" $result | Should -Be $null - Assert-MockCalled Write-Warning -Exactly 1 -Scope It -ParameterFilter { $Message -match "missing command property" } + Assert-MockCalled Write-StatusMessage -Exactly 1 -Scope It -ParameterFilter { $Message -match "missing command property" -and $Verbosity -eq "Warning" } Assert-MockCalled Invoke-Expression -Exactly 1 -Scope It } } diff --git a/DevSetup/Private/Commands/Install-DevSetupEnv.ps1 b/DevSetup/Private/Commands/Install-DevSetupEnv.ps1 index 5382ed1..c9cd4f2 100644 --- a/DevSetup/Private/Commands/Install-DevSetupEnv.ps1 +++ b/DevSetup/Private/Commands/Install-DevSetupEnv.ps1 @@ -62,7 +62,9 @@ Function Install-DevSetupEnv { [Parameter(Mandatory=$true, Position=0, ParameterSetName = "InstallPath")] [string]$Path, [Parameter(Mandatory=$true, Position=0, ParameterSetName = "InstallUrl")] - [string]$Url + [string]$Url, + [Parameter(Mandatory=$false)] + [switch]$DryRun = $false ) $YamlFile = $null @@ -79,17 +81,17 @@ Function Install-DevSetupEnv { $YamlFile = Join-Path -Path (Join-Path -Path (Get-DevSetupEnvPath) -ChildPath $Provider) -ChildPath "$Name.devsetup" } elseif($PSBoundParameters.ContainsKey('Path')) { if(-not (Test-Path -Path $Path)) { - Write-Error "Invalid Path provided" + Write-StatusMessage "Invalid Path provided" -Verbosity Error return } $YamlFile = $Path } elseif($PSBoundParameters.ContainsKey('Url')) { $FileName = Split-Path $Url -Leaf - Write-Host "Downloading DevSetup environment from:" -ForegroundColor Cyan - Write-Host "- $Url" -ForegroundColor Gray + Write-StatusMessage "Downloading DevSetup environment from:" -ForegroundColor Cyan + Write-StatusMessage "- $Url" -Indent 2 -ForegroundColor Gray $YamlFile = Join-Path -Path (Get-DevSetupLocalEnvPath) -ChildPath $FileName - Write-Host "Saving Devsetup environment file to:" -ForegroundColor Cyan - Write-Host "- $YamlFile" -ForegroundColor Gray + Write-StatusMessage "Saving Devsetup environment file to:" -ForegroundColor Cyan + Write-StatusMessage "- $YamlFile" -Indent 2 -ForegroundColor Gray if((Test-Path -Path $YamlFile)) { Write-Warning "File $YamlFile already exists" do { @@ -102,51 +104,54 @@ Function Install-DevSetupEnv { try { Invoke-WebRequest -Uri $Url -OutFile $YamlFile | Out-Null } catch { - Write-Error "Failed to download devsetup env file" + Write-StatusMessage "Failed to download devsetup env file" -Verbosity Error return } } if (-not (Test-Path $YamlFile)) { - Write-Error "Environment file not found: $YamlFile" + Write-StatusMessage "Environment file not found: $YamlFile" -Verbosity Error return } - Write-Host "Installing DevSetup environment from:" -ForegroundColor Cyan - Write-Host "- $YamlFile" -ForegroundColor Gray - Write-Host "" + Write-StatusMessage "Installing DevSetup environment from:" -ForegroundColor Cyan + Write-StatusMessage "- $YamlFile`n" -Indent 2 -ForegroundColor Gray # Read the configuration from the YAML file $YamlData = Read-ConfigurationFile -Config $YamlFile # Check if YAML data was successfully parsed if ($null -eq $YamlData) { - Write-Error "Failed to parse YAML configuration from: $YamlFile" + Write-StatusMessage "Failed to parse YAML configuration from: $YamlFile" -Verbosity Error return } # Install PowerShell module dependencies Install-PowershellModules -YamlData $YamlData | Out-Null - # Install Chocolatey package dependencies - Install-ChocolateyPackages -YamlData $YamlData | Out-Null - - # Install Scoop package dependencies - Install-ScoopComponents -YamlData $YamlData | Out-Null + if ((Test-OperatingSystem -Windows)) { + # Install Chocolatey package dependencies + Install-ChocolateyPackages -YamlData $YamlData | Out-Null + # Install Scoop package dependencies + Install-ScoopComponents -YamlData $YamlData | Out-Null + } else { + # Install Homebrew package dependencies + Invoke-HomebrewComponentsInstall -YamlData $YamlData -DryRun:$DryRun | Out-Null + } # Execute any commands defined in the configuration if ($YamlData.devsetup.commands -and $YamlData.devsetup.commands.Count -gt 0) { - Write-Host "Executing configuration commands..." -ForegroundColor Cyan + Write-StatusMessage "Executing configuration commands..." -ForegroundColor Cyan foreach ($commandEntry in $YamlData.devsetup.commands) { if ($commandEntry.command) { - Write-Host " - Executing command for: $($commandEntry.packageName)" -ForegroundColor Gray + Write-StatusMessage "- Executing command for: $($commandEntry.packageName)" -Indent 2 -ForegroundColor Gray Invoke-Expression -Command $commandEntry.command *> $null } else { - Write-Warning "Skipping command entry with missing command property" + Write-StatusMessage "Skipping command entry with missing command property" -Verbosity Warning } } } else { - Write-Host "No commands found in configuration to execute." -ForegroundColor Gray - } + Write-StatusMessage "No commands found in configuration to execute." -ForegroundColor Gray + } } \ No newline at end of file diff --git a/DevSetup/Private/Commands/Show-DevSetupEnvList.Tests.ps1 b/DevSetup/Private/Commands/Show-DevSetupEnvList.Tests.ps1 index 2880232..250549a 100644 --- a/DevSetup/Private/Commands/Show-DevSetupEnvList.Tests.ps1 +++ b/DevSetup/Private/Commands/Show-DevSetupEnvList.Tests.ps1 @@ -7,7 +7,20 @@ BeforeAll { . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Test-OperatingSystem.ps1 . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Format-PrettyTable.ps1 . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1 - Mock Get-DevSetupPath { "C:\DevSetup" } + if ($PSVersionTable.PSVersion.Major -eq 5) { + Mock Get-DevSetupPath { "C:\DevSetup" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6) { + if ($IsWindows) { + Mock Get-DevSetupPath { "C:\DevSetup" } + } + if ($IsLinux) { + Mock Get-DevSetupPath { "/home/testuser/devsetup" } + } + if ($IsMacOS) { + Mock Get-DevSetupPath { "/Users/TestUser/devsetup" } + } + } + Mock Optimize-DevSetupEnvs { } Mock Write-Host { } Mock Write-Warning { } diff --git a/DevSetup/Private/Commands/Uninstall-DevSetupEnv.Tests.ps1 b/DevSetup/Private/Commands/Uninstall-DevSetupEnv.Tests.ps1 index 3dbcbbf..82fb247 100644 --- a/DevSetup/Private/Commands/Uninstall-DevSetupEnv.Tests.ps1 +++ b/DevSetup/Private/Commands/Uninstall-DevSetupEnv.Tests.ps1 @@ -1,20 +1,26 @@ BeforeAll { - . $PSScriptRoot\Uninstall-DevSetupEnv.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Read-ConfigurationFile.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupEnvPath.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Test-OperatingSystem.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Providers\Scoop\Uninstall-ScoopComponents.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Providers\Chocolatey\Uninstall-ChocolateyPackages.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Providers\PowerShell\Uninstall-PowershellModules.ps1 - Mock Get-DevSetupEnvPath { "TestDrive:\DevSetupEnvs" } + Function Write-EZLog { } + . (Join-Path $PSScriptRoot "Uninstall-DevSetupEnv.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Read-ConfigurationFile.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Get-DevSetupEnvPath.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Test-OperatingSystem.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\Scoop\Uninstall-ScoopComponents.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\Chocolatey\Uninstall-ChocolateyPackages.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\PowerShell\Uninstall-PowershellModules.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\Homebrew\Invoke-HomebrewComponentsUninstall.ps1") + Mock Get-DevSetupEnvPath { "$TestDrive\DevSetup\DevSetupEnvs" } Mock Test-Path { $true } Mock Read-ConfigurationFile { } - Mock Uninstall-PowershellModules { $true } - Mock Uninstall-ChocolateyPackages { $true } - Mock Uninstall-ScoopComponents { $true } + Mock Uninstall-PowershellModules { Param($YamlData, $DryRun) $true } + Mock Uninstall-ChocolateyPackages { Param($YamlData, $DryRun) $true } + Mock Uninstall-ScoopComponents { Param($YamlData, $DryRun) $true } Mock Test-OperatingSystem { $true } Mock Write-Host { } Mock Write-Error { } + Mock Write-StatusMessage { } + Mock Write-EZLog { } + Mock Invoke-HomebrewComponentsUninstall { $true } } Describe "Uninstall-DevSetupEnv" { @@ -22,9 +28,9 @@ Describe "Uninstall-DevSetupEnv" { Context "When environment file does not exist" { It "Should write error and return" { Mock Test-Path { $false } - $result = Uninstall-DevSetupEnv -Name "missing-env" + $result = Uninstall-DevSetupEnv -Name "missing-env" -DryRun:$false $result | Should -Be $null - Assert-MockCalled Write-Error -Exactly 1 -Scope It -ParameterFilter { $Message -match "Environment file not found" } + Assert-MockCalled Write-StatusMessage -Exactly 1 -Scope It -ParameterFilter { $Message -match "Environment file not found" -and $Verbosity -eq "Error" } } } @@ -34,7 +40,7 @@ Describe "Uninstall-DevSetupEnv" { Mock Read-ConfigurationFile { $null } $result = Uninstall-DevSetupEnv -Name "bad-yaml" $result | Should -Be $null - Assert-MockCalled Write-Error -Exactly 1 -Scope It -ParameterFilter { $Message -match "Failed to parse YAML" } + Assert-MockCalled Write-StatusMessage -Exactly 1 -Scope It -ParameterFilter { $Message -match "Failed to parse YAML" -and $Verbosity -eq "Error" } } } @@ -42,26 +48,38 @@ Describe "Uninstall-DevSetupEnv" { It "Should call all uninstallers and write status" { Mock Test-Path { $true } Mock Read-ConfigurationFile { @{ devsetup = @{ } } } + Mock Test-OperatingSystem { Param($Windows, $Linux, $MacOS) { return $true } } $result = Uninstall-DevSetupEnv -Name "basic-env" $result | Should -Be $null + Assert-MockCalled Test-OperatingSystem -Exactly 1 -Scope It Assert-MockCalled Uninstall-PowershellModules -Exactly 1 -Scope It Assert-MockCalled Uninstall-ChocolateyPackages -Exactly 1 -Scope It Assert-MockCalled Uninstall-ScoopComponents -Exactly 1 -Scope It - Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "Uninstalling DevSetup environment from:" } + Assert-MockCalled Invoke-HomebrewComponentsUninstall -Exactly 0 -Scope It + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Uninstalling DevSetup environment from:" } } } Context "When a component uninstaller fails" { It "Should continue calling other uninstallers" { + Mock Test-OperatingSystem { return $true } $script:callCount = 0 Mock Uninstall-PowershellModules { $script:callCount++; $false } Mock Uninstall-ChocolateyPackages { $script:callCount++; $true } Mock Uninstall-ScoopComponents { $script:callCount++; $true } + Mock Invoke-HomebrewComponentsUninstall { $script:callCount++; $true } Mock Test-Path { $true } Mock Read-ConfigurationFile { @{ devsetup = @{ } } } + $result = Uninstall-DevSetupEnv -Name "partial-fail" + Assert-MockCalled Test-OperatingSystem -Exactly 1 -Scope It -ParameterFilter { $Windows -eq $true } + Assert-MockCalled Uninstall-PowershellModules -Exactly 1 -Scope It + Assert-MockCalled Uninstall-ChocolateyPackages -Exactly 1 -Scope It + Assert-MockCalled Uninstall-ScoopComponents -Exactly 1 -Scope It + Assert-MockCalled Invoke-HomebrewComponentsUninstall -Exactly 0 -Scope It $result | Should -Be $null $script:callCount | Should -Be 3 + } } @@ -71,7 +89,7 @@ Describe "Uninstall-DevSetupEnv" { Mock Read-ConfigurationFile { throw "Unexpected error" } $result = Uninstall-DevSetupEnv -Name "exception-env" $result | Should -Be $null - Assert-MockCalled Write-Error -Scope It + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Verbosity -eq "Error" } } } } \ No newline at end of file diff --git a/DevSetup/Private/Commands/Uninstall-DevSetupEnv.ps1 b/DevSetup/Private/Commands/Uninstall-DevSetupEnv.ps1 index b2b596d..696833b 100644 --- a/DevSetup/Private/Commands/Uninstall-DevSetupEnv.ps1 +++ b/DevSetup/Private/Commands/Uninstall-DevSetupEnv.ps1 @@ -20,18 +20,18 @@ .EXAMPLE Uninstall-DevSetupEnv -Name "WebDev" - + Uninstalls all packages and components from the "WebDev" environment configuration. .EXAMPLE Uninstall-DevSetupEnv "DataScience" - + Removes the complete "DataScience" development environment using positional parameter. .EXAMPLE $envName = "GameDev" Uninstall-DevSetupEnv -Name $envName - + Demonstrates using a variable to specify the environment name for uninstallation. .NOTES @@ -40,7 +40,7 @@ - Validates YAML file existence before attempting to parse configuration - Processes uninstallation in specific order: 1. PowerShell modules via Uninstall-PowershellModules - 2. Chocolatey packages via Uninstall-ChocolateyPackages + 2. Chocolatey packages via Uninstall-ChocolateyPackages 3. Scoop packages via Uninstall-ScoopComponents - Each uninstaller function handles its own error reporting and validation - Does not remove the YAML configuration file itself after uninstallation @@ -58,16 +58,22 @@ Function Uninstall-DevSetupEnv { [CmdletBinding()] + [OutputType([void])] Param( [Parameter(Mandatory=$true, Position=0, ParameterSetName = "Uninstall")] + [Parameter(Mandatory=$true, Position=0, ParameterSetName = "UninstallDry")] [string]$Name, [Parameter(Mandatory=$true, Position=0, ParameterSetName = "UninstallPath")] - [string]$Path + [Parameter(Mandatory=$true, Position=0, ParameterSetName = "UninstallPathDry")] + [string]$Path, + [Parameter(Mandatory=$true, Position=1, ParameterSetName = "UninstallDry")] + [Parameter(Mandatory=$true, Position=1, ParameterSetName = "UninstallPathDry")] + [switch]$DryRun ) try { $YamlFile = $null - + if($PSBoundParameters.ContainsKey('Name')) { $Provider = "local" @@ -83,38 +89,45 @@ Function Uninstall-DevSetupEnv { Write-Error "Invalid Path provided" return } - $YamlFile = $Path + $YamlFile = $Path } #$YamlFile = Join-Path -Path (Get-DevSetupEnvPath) -ChildPath "$Name.yaml" if (-not (Test-Path $YamlFile)) { - Write-Error "Environment file not found: $YamlFile" + Write-StatusMessage "Environment file not found: $YamlFile" -Verbosity Error return } - Write-Host "Uninstalling DevSetup environment from: $YamlFile" -ForegroundColor Cyan + Write-StatusMessage "Uninstalling DevSetup environment from:" -ForegroundColor Cyan + Write-StatusMessage "- $YamlFile`n" -Indent 2 -ForegroundColor Gray # Read the configuration from the YAML file $YamlData = Read-ConfigurationFile -Config $YamlFile - + # Check if YAML data was successfully parsed if ($null -eq $YamlData) { - Write-Error "Failed to parse YAML configuration from: $YamlFile" + Write-StatusMessage "Failed to parse YAML configuration from: $YamlFile" -Verbosity Error return } - - # Install PowerShell module dependencies - $status = Uninstall-PowershellModules -YamlData $YamlData - if ((Test-OperatingSystem -Windows)) { - # Install Chocolatey package dependencies - $status = Uninstall-ChocolateyPackages -YamlData $YamlData + # Uninstall PowerShell module dependencies + Uninstall-PowershellModules -YamlData $YamlData | Out-Null + + $windows = Test-OperatingSystem -Windows + + if ($windows) { + # Uninstall Chocolatey package dependencies + Uninstall-ChocolateyPackages -YamlData $YamlData | Out-Null - # Install Scoop package dependencies - $status = Uninstall-ScoopComponents -YamlData $YamlData + # Uninstall Scoop package dependencies + Uninstall-ScoopComponents -YamlData $YamlData | Out-Null + } else { + # Uninstall Homebrew package dependencies + Invoke-HomebrewComponentsUninstall -YamlData $YamlData -DryRun:$DryRun | Out-Null } } catch { - Write-Error "An error occurred during uninstallation: $_" + Write-StatusMessage "An error occurred during uninstallation: $_" -Verbosity Error + Write-StatusMessage $_.ScriptStackTrace -Verbosity Error return - } + } } \ No newline at end of file diff --git a/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyCacheFile.Tests.ps1 b/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyCacheFile.Tests.ps1 index bf6cb94..d0a1feb 100644 --- a/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyCacheFile.Tests.ps1 +++ b/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyCacheFile.Tests.ps1 @@ -5,20 +5,39 @@ BeforeAll { } Describe "Get-ChocolateyCacheFile" { - Context "When Get-DevSetupCachePath returns a valid path" { It "Should return the correct cache file path" { - Mock Get-DevSetupCachePath { return "C:\Users\Test\.devsetup\.cache" } - $result = Get-ChocolateyCacheFile - $result | Should -Be "C:\Users\Test\.devsetup\.cache\chocolatey.cache" + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-DevSetupCachePath { return "$TestDrive\Users\Test\devsetup\.cache" } + $result = Get-ChocolateyCacheFile + $result | Should -Be "$TestDrive\Users\Test\devsetup\.cache\chocolatey.cache" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-DevSetupCachePath { return "$TestDrive/home/testuser/devsetup/.cache" } + $result = Get-ChocolateyCacheFile + $result | Should -Be "$TestDrive/home/testuser/devsetup/.cache/chocolatey.cache" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-DevSetupCachePath { return "$TestDrive/Users/TestUser/devsetup/.cache" } + $result = Get-ChocolateyCacheFile + $result | Should -Be "$TestDrive/Users/TestUser/devsetup/.cache/chocolatey.cache" + } } } Context "When Get-DevSetupCachePath returns a different path" { It "Should append chocolatey.cache to the returned path" { - Mock Get-DevSetupCachePath { return "D:\DevSetupCache" } - $result = Get-ChocolateyCacheFile - $result | Should -Be "D:\DevSetupCache\chocolatey.cache" + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-DevSetupCachePath { return "$TestDrive\DevSetupCache" } + $result = Get-ChocolateyCacheFile + $result | Should -Be "$TestDrive\DevSetupCache\chocolatey.cache" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-DevSetupCachePath { return "$TestDrive/home/testuser/devsetupcache/.cache" } + $result = Get-ChocolateyCacheFile + $result | Should -Be "$TestDrive/home/testuser/devsetupcache/.cache/chocolatey.cache" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-DevSetupCachePath { return "$TestDrive/Users/TestUser/devsetupcache/.cache" } + $result = Get-ChocolateyCacheFile + $result | Should -Be "$TestDrive/Users/TestUser/devsetupcache/.cache/chocolatey.cache" + } } } diff --git a/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.Tests.ps1 b/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.Tests.ps1 index 100e8d1..e5712e4 100644 --- a/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.Tests.ps1 +++ b/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.Tests.ps1 @@ -1,7 +1,14 @@ BeforeAll { . $PSScriptRoot\Get-ChocolateyPackageDependencies.ps1 + . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Get-EnvironmentVariable.ps1 Mock Write-Debug { } - $isPS5 = $PSVersionTable.PSVersion.Major -eq 5 + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-EnvironmentVariable { return "C:\choco" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-EnvironmentVariable { return "/opt/choco" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-EnvironmentVariable { return "/opt/choco" } + } } Describe "Get-ChocolateyPackageDependencies" { @@ -26,10 +33,24 @@ Describe "Get-ChocolateyPackageDependencies" { Context "When nuspec files have no dependencies" { It "Should return $null in PS5, empty array in PS6+" { Mock Test-Path { return $true } - Mock Get-ChildItem { - @( - [PSCustomObject]@{ FullName = "C:\choco\lib\foo\foo.nuspec" } - ) + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-ChildItem { + @( + [PSCustomObject]@{ FullName = "C:\choco\lib\foo\foo.nuspec" } + ) + } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-ChildItem { + @( + [PSCustomObject]@{ FullName = "/opt/choco/lib/foo/foo.nuspec" } + ) + } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-ChildItem { + @( + [PSCustomObject]@{ FullName = "/opt/choco/lib/foo/foo.nuspec" } + ) + } } Mock Get-Content { '' @@ -42,11 +63,25 @@ Describe "Get-ChocolateyPackageDependencies" { Context "When nuspec files have dependencies including chocolatey system packages" { It "Should return only non-chocolatey dependencies" { Mock Test-Path { return $true } - Mock Get-ChildItem { - @( - [PSCustomObject]@{ FullName = "C:\choco\lib\foo\foo.nuspec" } - ) - } + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-ChildItem { + @( + [PSCustomObject]@{ FullName = "C:\choco\lib\foo\foo.nuspec" } + ) + } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-ChildItem { + @( + [PSCustomObject]@{ FullName = "/opt/choco/lib/foo/foo.nuspec" } + ) + } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-ChildItem { + @( + [PSCustomObject]@{ FullName = "/opt/choco/lib/foo/foo.nuspec" } + ) + } + } Mock Get-Content { ' @@ -65,11 +100,27 @@ Describe "Get-ChocolateyPackageDependencies" { Context "When multiple nuspec files have overlapping dependencies" { It "Should return all dependencies including duplicates" { Mock Test-Path { return $true } - Mock Get-ChildItem { - @( - [PSCustomObject]@{ FullName = "C:\choco\lib\foo\foo.nuspec" }, - [PSCustomObject]@{ FullName = "C:\choco\lib\bar\bar.nuspec" } - ) + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-ChildItem { + @( + [PSCustomObject]@{ FullName = "C:\choco\lib\foo\foo.nuspec" }, + [PSCustomObject]@{ FullName = "C:\choco\lib\bar\bar.nuspec" } + ) + } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-ChildItem { + @( + [PSCustomObject]@{ FullName = "/opt/choco/lib/foo/foo.nuspec" }, + [PSCustomObject]@{ FullName = "/opt/choco/lib/bar/bar.nuspec" } + ) + } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-ChildItem { + @( + [PSCustomObject]@{ FullName = "/opt/choco/lib/foo/foo.nuspec" }, + [PSCustomObject]@{ FullName = "/opt/choco/lib/bar/bar.nuspec" } + ) + } } $nuspecs = @( ' diff --git a/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.ps1 b/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.ps1 index 0c4e424..5262306 100644 --- a/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.ps1 +++ b/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.ps1 @@ -56,19 +56,20 @@ Function Get-ChocolateyPackageDependencies { [CmdletBinding()] + [OutputType([array])] Param() write-Debug "Retrieving Chocolatey package dependencies..." $packageDependencies = @() - $chocolateyInstallPath = Join-Path $Env:ChocolateyInstall lib + $chocolateyInstallPath = Join-Path (Get-EnvironmentVariable ChocolateyInstall) lib if (-not (Test-Path $chocolateyInstallPath)) { Write-Debug "Chocolatey installation path not found: $chocolateyInstallPath" return $packageDependencies } - Get-ChildItem $chocolateyInstallPath -Recurse "*.nuspec" | % { - $dependencies = ([xml](Get-Content $_.FullName)).package.metadata.dependencies.dependency | Foreach-Object { + Get-ChildItem $chocolateyInstallPath -Recurse "*.nuspec" | ForEach-Object { + $dependencies = ([xml](Get-Content $_.FullName)).package.metadata.dependencies.dependency | ForEach-Object { if (-not ($_.id -like "chocolatey*")) { $_.id } diff --git a/DevSetup/Private/Providers/Core/Install-CoreDependencies.Tests.ps1 b/DevSetup/Private/Providers/Core/Install-CoreDependencies.Tests.ps1 index 8673921..3631d34 100644 --- a/DevSetup/Private/Providers/Core/Install-CoreDependencies.Tests.ps1 +++ b/DevSetup/Private/Providers/Core/Install-CoreDependencies.Tests.ps1 @@ -23,7 +23,7 @@ Describe "Install-CoreDependencies" { Context "When NuGet installation fails" { It "Should return false and write error" { Mock Install-NuGet { return $false } - Mock Test-OperatingSystem { param($os) return $false } + Mock Test-OperatingSystem { param($os) return $true } $result = Install-CoreDependencies $result | Should -Be $false } @@ -33,7 +33,7 @@ Describe "Install-CoreDependencies" { It "Should return true and write warning" { Mock Install-NuGet { return $true } Mock Get-DevSetupManifest { return $null } - Mock Test-OperatingSystem { param($os) return $false } + Mock Test-OperatingSystem { param($os) return $true } $result = Install-CoreDependencies $result | Should -Be $true diff --git a/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1 b/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1 index 008c6bc..dbd333d 100644 --- a/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1 +++ b/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1 @@ -74,7 +74,7 @@ Function Install-CoreDependencies { # Install NuGet PackageProvider if ((Test-OperatingSystem -Windows)) { if (-not (Install-NuGet)) { - Write-Error "Failed to install NuGet PackageProvider" + Write-StatusMessage "Failed to install NuGet PackageProvider" -Verbosity Error return $false } } @@ -82,21 +82,21 @@ Function Install-CoreDependencies { # Get required modules from DevSetup manifest $manifest = Get-DevSetupManifest if (-not $manifest -or -not $manifest.RequiredModules) { - Write-Warning "No required modules found in DevSetup manifest" + Write-StatusMessage "No required modules found in DevSetup manifest" -Verbosity Warning return $true } # Install each required PowerShell module foreach ($moduleName in $manifest.RequiredModules) { if (-not $moduleName -or [string]::IsNullOrEmpty($moduleName)) { - Write-Warning "Skipping empty module name" + Write-StatusMessage "Skipping empty module name" -Verbosity Warning continue } Write-StatusMessage "- Installing powershell module: $moduleName" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline if (-not (Install-PowerShellModule -ModuleName $moduleName -Force -AllowClobber -Scope 'CurrentUser')) { Write-StatusMessage "[FAILED]" -ForegroundColor Red - Write-Error "Failed to install required PowerShell module: $moduleName" + Write-StatusMessage "Failed to install required PowerShell module: $moduleName" -Verbosity Error return $false } Write-StatusMessage "[OK]" -ForegroundColor Green @@ -105,7 +105,7 @@ Function Install-CoreDependencies { if ((Test-OperatingSystem -Windows)) { # Install Chocolatey first if (-not (Install-Chocolatey)) { - Write-Error "Cannot proceed without Chocolatey" + Write-StatusMessage "Cannot proceed without Chocolatey" -Verbosity Error return $false } @@ -113,7 +113,7 @@ Function Install-CoreDependencies { Write-StatusMessage "- Installing Git package via Chocolatey" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline if (-not (Install-ChocolateyPackage -PackageName "git" -Version 2.50.1)) { Write-StatusMessage "[FAILED]" -ForegroundColor Red - Write-Error "Failed to install Git package" + Write-StatusMessage "Failed to install Git package" -Verbosity Error return $false } else { Write-StatusMessage "[OK]" -ForegroundColor Green @@ -123,12 +123,12 @@ Function Install-CoreDependencies { # Install Scoop PackageProvider if (-not (Install-Scoop)) { - Write-Error "Failed to install Scoop PackageProvider" + Write-StatusMessage "Failed to install Scoop PackageProvider" -Verbosity Error return $false } } else { if (-not (Install-Homebrew)) { - Write-Error "Failed to install Homebrew" + Write-StatusMessage "Failed to install Homebrew" -Verbosity Error return $false } } diff --git a/DevSetup/Private/Providers/Core/Install-Nuget.ps1 b/DevSetup/Private/Providers/Core/Install-Nuget.ps1 index e2e52af..68365b4 100644 --- a/DevSetup/Private/Providers/Core/Install-Nuget.ps1 +++ b/DevSetup/Private/Providers/Core/Install-Nuget.ps1 @@ -66,7 +66,7 @@ Function Install-Nuget { try { # Check if we're on Windows - NuGet PackageProvider is Windows-only if (-not (Test-OperatingSystem -Windows)) { - Write-Host "NuGet PackageProvider is not available on this platform. Skipping installation." -ForegroundColor Yellow + Write-StatusMessage "NuGet PackageProvider is not available on this platform. Skipping installation." -ForegroundColor Yellow return $true } @@ -79,7 +79,7 @@ Function Install-Nuget { $nugetProvider = Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue Write-StatusMessage "- Installing NuGet PackageProvider" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline if ($nugetProvider) { - #Write-Host "NuGet PackageProvider is already installed (version: $($nugetProvider.Version))" -ForegroundColor Green + Write-StatusMessage "NuGet PackageProvider is already installed (version: $($nugetProvider.Version))" -ForegroundColor Green -Verbosity Verbose Write-StatusMessage "[OK]" -ForegroundColor Green } else { #Write-Host "Installing NuGet PackageProvider..." -ForegroundColor Cyan @@ -88,7 +88,7 @@ Function Install-Nuget { # Verify installation $nugetProvider = Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue if ($nugetProvider) { - #Write-Host "NuGet PackageProvider successfully installed (version: $($nugetProvider.Version))" -ForegroundColor Green + Write-StatusMessage "NuGet PackageProvider successfully installed (version: $($nugetProvider.Version))" -ForegroundColor Green -Verbosity Verbose Write-StatusMessage "[OK]" -ForegroundColor Green } else { throw "Failed to install NuGet PackageProvider" @@ -112,7 +112,8 @@ Function Install-Nuget { return $true } catch { - Write-Error "Error checking/installing NuGet: $_" + Write-StatusMessage "Error checking/installing NuGet: $_" -Verbosity Error + Write-StatusMessage $_.ScriptStackTrace -Verbosity Error return $false } } \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Find-Homebrew.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Find-Homebrew.Tests.ps1 new file mode 100644 index 0000000..2304f07 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Find-Homebrew.Tests.ps1 @@ -0,0 +1,114 @@ +BeforeAll { + . $PSScriptRoot\Find-Homebrew.ps1 +} + +Describe "Find-Homebrew" { + Context "When brew is found in PATH" { + It "should return the path from Get-Command" { + Mock Get-Command { [PSCustomObject]@{ Path = "/usr/local/bin/brew" } } + Mock Test-Path { $false } # Not needed since Get-Command succeeds + + $result = Find-Homebrew + $result | Should -Be "/usr/local/bin/brew" + Assert-MockCalled Get-Command -Exactly 1 -Scope It + Assert-MockCalled Test-Path -Exactly 0 -Scope It # Test-Path not called if Get-Command succeeds + } + } + + Context "When brew is not in PATH but found in test paths" { + It "should return the first matching test path" { + Mock Get-Command { $null } + Mock Test-Path { + Param($Path) + switch ($Path) { + "/usr/local/bin/brew" { $true } + default { $false } + } + } + + $result = Find-Homebrew + $result | Should -Be "/usr/local/bin/brew" + Assert-MockCalled Get-Command -Exactly 1 -Scope It + Assert-MockCalled Test-Path -Exactly 1 -Scope It -ParameterFilter { $Path -eq "/usr/local/bin/brew" } + } + + It "should return the second matching test path if first fails" { + Mock Get-Command { $null } + Mock Test-Path { + Param($Path) + switch ($Path) { + "/usr/local/bin/brew" { $false } + "/opt/homebrew/bin/brew" { $true } + default { $false } + } + } + + $result = Find-Homebrew + $result | Should -Be "/opt/homebrew/bin/brew" + Assert-MockCalled Get-Command -Exactly 1 -Scope It + Assert-MockCalled Test-Path -Exactly 2 -Scope It # Checks first two paths + } + + It "should return the third matching test path if first two fail" { + Mock Get-Command { $null } + Mock Test-Path { + Param($Path) + switch ($Path) { + "/usr/local/bin/brew" { $false } + "/opt/homebrew/bin/brew" { $false } + "/home/linuxbrew/.linuxbrew/bin/brew" { $true } + default { $false } + } + } + + $result = Find-Homebrew + $result | Should -Be "/home/linuxbrew/.linuxbrew/bin/brew" + Assert-MockCalled Get-Command -Exactly 1 -Scope It + Assert-MockCalled Test-Path -Exactly 3 -Scope It # Checks all three paths + } + } + + Context "When brew is not found anywhere" { + It "should return null" { + Mock Get-Command { $null } + Mock Test-Path { $false } + + $result = Find-Homebrew + $result | Should -Be $null + Assert-MockCalled Get-Command -Exactly 1 -Scope It + Assert-MockCalled Test-Path -Exactly 3 -Scope It # Checks all three paths + } + } + + Context "Cross-platform compatibility" { + It "should handle Windows (where brew is unlikely to be found)" { + Mock Get-Command { $null } + Mock Test-Path { $false } + + $result = Find-Homebrew + $result | Should -Be $null + } + + It "should handle Linux paths" { + Mock Get-Command { $null } + Mock Test-Path { + Param($Path) + $Path -eq "/home/linuxbrew/.linuxbrew/bin/brew" + } + + $result = Find-Homebrew + $result | Should -Be "/home/linuxbrew/.linuxbrew/bin/brew" + } + + It "should handle macOS paths" { + Mock Get-Command { $null } + Mock Test-Path { + Param($Path) + $Path -eq "/opt/homebrew/bin/brew" + } + + $result = Find-Homebrew + $result | Should -Be "/opt/homebrew/bin/brew" + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Find-Homebrew.ps1 b/DevSetup/Private/Providers/Homebrew/Find-Homebrew.ps1 new file mode 100644 index 0000000..1fa028a --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Find-Homebrew.ps1 @@ -0,0 +1,25 @@ +Function Find-Homebrew { + [CmdletBinding()] + [OutputType([string])] + Param( + ) + + $TestPaths = @( + '/usr/local/bin/brew', + '/opt/homebrew/bin/brew', + '/home/linuxbrew/.linuxbrew/bin/brew' + ) + + # Check if Homebrew is installed + $Path = (Get-Command "brew" -ErrorAction SilentlyContinue).Path + if ([string]::IsNullOrEmpty($Path)) { + foreach ($p in $TestPaths) { + if (Test-Path $p) { + $Path = $p + break + } + } + } + + return $Path +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Get-HomebrewCacheFile.ps1 b/DevSetup/Private/Providers/Homebrew/Get-HomebrewCacheFile.ps1 new file mode 100644 index 0000000..1cebfa7 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Get-HomebrewCacheFile.ps1 @@ -0,0 +1,7 @@ +Function Get-HomebrewCacheFile { + [CmdletBinding()] + Param() + + $CacheFile = Join-Path -Path (Get-DevSetupCachePath) -ChildPath "homebrew.cache" + return $CacheFile +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Get-HomebrewVersion.ps1 b/DevSetup/Private/Providers/Homebrew/Get-HomebrewVersion.ps1 new file mode 100644 index 0000000..6ab2547 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Get-HomebrewVersion.ps1 @@ -0,0 +1,31 @@ +Function Get-HomebrewVersion { + [CmdletBinding()] + [OutputType([string])] + Param( + ) + + # Get Homebrew version + if( -not (Test-HomebrewInstalled) ) { + Write-StatusMessage "Homebrew is not installed" -Verbosity Verbose + return $null + } + + $HomebrewPath = Find-Homebrew + if (-not $HomebrewPath) { + Write-StatusMessage "Homebrew installation not found" -Verbosity Verbose + return $null + } + + $BrewArgs = @{ + Command = $HomebrewPath + Arguments = @("--version") + } + + $HomeBrewVersion = (Invoke-ExternalCommand @BrewArgs) -match "([0-9]+\.[0-9]+\.[0-9]+)" + if ($HomeBrewVersion) { + $version = $matches[1] + return $version + } else { + return $null + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1 b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1 index b11664a..3ef338f 100644 --- a/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1 +++ b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1 @@ -1,19 +1,52 @@ Function Install-Homebrew { [CmdletBinding()] + [OutputType([bool])] Param( ) + Write-StatusMessage "- Checking for sudo access" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline + if ((Test-HasSudoAccess)) { + Write-StatusMessage "[OK]" -ForegroundColor Green + } else { + Write-StatusMessage "[FAILED]" -ForegroundColor Red + Write-StatusMessage "Sudo access is required to install Homebrew. Please run this script with a user that has sudo privileges." -Verbosity Warning + return $false + } + # Install Homebrew Write-StatusMessage "- Installing Homebrew package manager" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline - if (-not (Get-Command "brew" -ErrorAction SilentlyContinue)) { + if (-not (Find-Homebrew)) { # Installation command for Homebrew (Linux/Mac) $installCmd = 'NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"' - (bash -c "$installCmd") *> $null - if (-not (Get-Command "brew" -ErrorAction SilentlyContinue)) { + $BrewArgs = @{ + Command = "bash" + Arguments = @("-c", "$installCmd") + } + Invoke-ExternalCommand @BrewArgs + $HomebrewPath = Find-Homebrew + if ([string]::IsNullOrEmpty($HomebrewPath)) { Write-StatusMessage "[FAILED]" -ForegroundColor Red return $false } else { Write-StatusMessage "[OK]" -ForegroundColor Green + switch ($env:SHELL) { + { $_ -like "*zsh*" } { + Write-StatusMessage "- Adding Homebrew path to $HOME/.zshrc" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline + Add-Content -Path "$HOME/.zshrc" -Value "" + Add-Content -Path "$HOME/.zshrc" -Value ([string]::Format('eval "$({0} shellenv)"', $HomebrewPath)) + Write-StatusMessage "[OK]" -ForegroundColor Green + } + { $_ -like "*bash*" } { + Write-StatusMessage "- Adding Homebrew path to $HOME/.bashrc" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline + Add-Content -Path "$HOME/.bashrc" -Value "" + Add-Content -Path "$HOME/.bashrc" -Value ([string]::Format('eval "$({0} shellenv)"', $HomebrewPath)) + Write-StatusMessage "[OK]" -ForegroundColor Green + } + default { + Write-StatusMessage "Unknown shell: $($env:SHELL). You may need to manually add Homebrew to your PATH." -Verbosity Warning + } + } + return $true } } else { diff --git a/DevSetup/Private/Providers/Homebrew/Install-HomebrewPackage.ps1 b/DevSetup/Private/Providers/Homebrew/Install-HomebrewPackage.ps1 new file mode 100644 index 0000000..8e98515 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Install-HomebrewPackage.ps1 @@ -0,0 +1,42 @@ +Function Install-HomebrewPackage { + [CmdletBinding(SupportsShouldProcess=$true)] + [OutputType([bool])] + Param( + [Parameter(Mandatory=$true, Position=0, ParameterSetName="Install")] + [string]$PackageName + ) + + if ($PSCmdlet.ShouldProcess($PackageName, "brew install")) { + Write-StatusMessage "- Installing Homebrew package '$PackageName'" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline + + if(-not (Test-HasSudoAccess)) { + Write-StatusMessage "Sudo Access is required to install Homebrew packages." -ForegroundColor Red -Verbosity Verbose + Write-StatusMessage "[FAILED]" -ForegroundColor Red + return $false + } + + if (-not (Find-Homebrew)) { + Write-StatusMessage "Homebrew is not installed. Please install Homebrew first." -ForegroundColor Red -Verbosity Verbose + Write-StatusMessage "[FAILED]" -ForegroundColor Red + return $false + } + + if((Test-HomebrewPackageInstalled -PackageName $PackageName).HasFlag([InstalledState]::Pass)) { + Write-StatusMessage "[OK]" -ForegroundColor Green + return $true + } + # Install Homebrew package + $BrewArgs = @{ + Command = (Find-Homebrew) + Arguments = @("install", $PackageName) + } + Invoke-ExternalCommand @BrewArgs *> $null + if ($LASTEXITCODE -eq 0) { + Write-StatusMessage "[OK]" -ForegroundColor Green + return $true + } else { + Write-StatusMessage "[FAILED]" -ForegroundColor Red + return $false + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsExport.ps1 b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsExport.ps1 new file mode 100644 index 0000000..39869ca --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsExport.ps1 @@ -0,0 +1,80 @@ +Function Invoke-HomebrewComponentExport { + [CmdletBinding()] + [OutputType([bool])] + Param( + [Parameter(Mandatory = $true)] + [string]$Config, + [Parameter(Mandatory = $false)] + [string]$OutFile, + [Parameter(Mandatory = $false)] + [switch]$DryRun + ) + + $YamlData = Read-ConfigurationFile -Config $Config + + # Ensure scoopPackages and scoopBuckets sections exist + if (-not $YamlData.devsetup) { $YamlData.devsetup = @{} } + if (-not $YamlData.devsetup.dependencies) { $YamlData.devsetup.dependencies = @{} } + if (-not $YamlData.devsetup.dependencies.homebrew) { $YamlData.devsetup.dependencies.homebrew = @() } + + if(-not (Find-Homebrew)) { + Write-StatusMessage "Homebrew is not installed. Please install Homebrew first." -ForegroundColor Red -Verbosity Verbose + return $false + } + + Write-StatusMessage "- Getting list of installed Homebrew packages..." -ForegroundColor Gray + $AvailablePackages = @{ + } + $BrewArgs = @{ + Command = "bash" + Arguments = @("-c", "$(Find-Homebrew) list --versions") + } + (Invoke-ExternalCommand @BrewArgs) | foreach-object { $Parts = $_ -split " "; $AvailablePackages[$Parts[0]] = $Parts[1]} | Out-Null + + $InstalledPackages = @() + $BrewArgs = @{ + Command = "bash" + Arguments = @("-c", "$(Find-Homebrew) list --installed-on-request") + } + (Invoke-ExternalCommand @BrewArgs) | Foreach-Object { $InstalledPackages += $_} | Out-Null + + Foreach($Package in $InstalledPackages) { + $PackageVersion = $AvailablePackages[$Package] + $existing = $YamlData.devsetup.dependencies.homebrew | Where-Object { $_.name -eq $Package } + if ($existing) { + # Update the version if package exists + Write-StatusMessage " - Updating package: $Package" -ForegroundColor Gray + $index = ($YamlData.devsetup.dependencies.homebrew).IndexOf($existing) + $YamlData.devsetup.dependencies.homebrew[$index].minimumVersion = $PackageVersion + } else { + Write-StatusMessage " - Adding package: $Package" -ForegroundColor Gray + # Add new package entry + $YamlData.devsetup.dependencies.homebrew += @{ name = $Package; minimumVersion = $PackageVersion } + } + } + + try { + $yamlOutput = $YamlData | ConvertTo-Yaml + } + catch { + Write-StatusMessage "Could not convert to YAML format. Showing PowerShell object instead:" -Verbosity Warning + $yamlOutput = $YamlData | ConvertTo-Json -Depth 10 + } + + # Determine output file + $outputFile = if ($OutFile) { $OutFile } else { $Config } + + try { + Write-StatusMessage "Saving configuration to: $outputFile" -Verbosity Verbose + $yamlOutput | Out-File -FilePath $outputFile + Write-StatusMessage "Configuration saved successfully!" -Verbosity Verbose + } + catch { + Write-StatusMessage "Failed to save configuration to $outputFile`: $_" -Verbosity Error + Write-StatusMessage $_.ScriptStackTrace + return $false + } + + Write-StatusMessage "Homebrew packages conversion completed!" -ForegroundColor Green + return $true +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsInstall.ps1 b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsInstall.ps1 new file mode 100644 index 0000000..8445efe --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsInstall.ps1 @@ -0,0 +1,31 @@ +Function Invoke-HomebrewComponentsInstall { + [CmdletBinding()] + [OutputType([bool])] + Param( + [Parameter(Mandatory=$true)] + [PSCustomObject]$YamlData, + [Parameter(Mandatory=$false)] + [switch]$DryRun = $false + ) + + $packageCount = 0 + Write-HomebrewCache | Out-Null + Write-StatusMessage "- Installing Homebrew packages from configuration:" -ForegroundColor Cyan + foreach($package in $YamlData.devsetup.dependencies.homebrew) { + $Params = @{ + PackageName = $package.name + WhatIf = $DryRun + } + if ($package.PSObject.Properties.Name -contains "minimumVersion") { + $Params.MinimumVersion = $package.minimumVersion + } + + $status = Install-HomebrewPackage @Params + + if ($status) { + $packageCount++ + Write-HomebrewCache | Out-Null + } + } + Write-StatusMessage "- Homebrew packages installation completed! Processed $packageCount packages.`n" -ForegroundColor Green +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsUninstall.ps1 b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsUninstall.ps1 new file mode 100644 index 0000000..88065b1 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsUninstall.ps1 @@ -0,0 +1,28 @@ +Function Invoke-HomebrewComponentsUninstall { + [CmdletBinding()] + [OutputType([bool])] + Param( + [Parameter(Mandatory=$true)] + [PSCustomObject]$YamlData, + [Parameter(Mandatory=$false)] + [switch]$DryRun = $false + ) + + $packageCount = 0 + Write-HomebrewCache | Out-Null + Write-StatusMessage "- Uninstalling Homebrew packages from configuration:" -ForegroundColor Cyan + foreach($package in $YamlData.devsetup.dependencies.homebrew) { + $Params = @{ + PackageName = $package.name + WhatIf = $DryRun + } + + $status = Uninstall-HomebrewPackage @Params + + if ($status) { + $packageCount++ + Write-HomebrewCache | Out-Null + } + } + Write-StatusMessage "- Homebrew packages uninstallation completed! Processed $packageCount packages.`n" -ForegroundColor Green +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Read-HomebrewCache.ps1 b/DevSetup/Private/Providers/Homebrew/Read-HomebrewCache.ps1 new file mode 100644 index 0000000..40d0b1a --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Read-HomebrewCache.ps1 @@ -0,0 +1,14 @@ +Function Read-HomebrewCache { + [CmdletBinding()] + [OutputType([hashtable])] + Param() + + $cacheFile = Get-HomebrewCacheFile + + if (Test-Path $cacheFile) { + $cacheData = Get-Content -Path $cacheFile | ConvertFrom-Json -AsHashtable + return $cacheData + } + + return @{} +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Test-HomebewInstalled.ps1 b/DevSetup/Private/Providers/Homebrew/Test-HomebewInstalled.ps1 new file mode 100644 index 0000000..14617a8 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Test-HomebewInstalled.ps1 @@ -0,0 +1,25 @@ +Function Test-HomebrewInstalled { + [CmdletBinding()] + [OutputType([bool])] + Param( + ) + + $TestPaths = @( + '/usr/local/bin/brew', + '/opt/homebrew/bin/brew', + '/home/linuxbrew/.linuxbrew/bin/brew' + ) + + # Check if Homebrew is installed + $Path = (Get-Command "brew" -ErrorAction SilentlyContinue).Path + if ([string]::IsNullOrEmpty($Path)) { + foreach ($p in $TestPaths) { + if (Test-Path $p) { + $Path = $p + break + } + } + } + + return (-not [string]::IsNullOrEmpty($Path)) +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Test-HomebrewPackageInstalled.ps1 b/DevSetup/Private/Providers/Homebrew/Test-HomebrewPackageInstalled.ps1 new file mode 100644 index 0000000..396447b --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Test-HomebrewPackageInstalled.ps1 @@ -0,0 +1,38 @@ +Function Test-HomebrewPackageInstalled { + [CmdletBinding()] + [OutputType([InstalledState])] + Param( + [Parameter(Mandatory=$true, Position=0)] + [string]$PackageName, + [Parameter(Mandatory=$false, Position=1)] + [Version]$MinimumVersion + ) + + $PackageStatus = [InstalledState]::NotInstalled + + if(-not (Test-HomebrewInstalled)) { + Write-StatusMessage "Homebrew is not installed" -Verbosity Verbose + return $PackageStatus + } + + $InstalledPackages = Read-HomebrewCache + + if ($InstalledPackages.ContainsKey($PackageName)) { + $PackageStatus += [InstalledState]::Installed + + if($PSBoundParameters.ContainsKey('MinimumVersion')) { + $MinimumVersion = [Version]::Parse($MinimumVersion) + $InstalledVersion = [Version]::Parse($InstalledPackages[$PackageName]) + if ($InstalledVersion -ge $MinimumVersion) { + $PackageStatus += [InstalledState]::MinimumVersionMet + } + } else { + $PackageStatus += [InstalledState]::MinimumVersionMet + } + + $PackageStatus += [InstalledState]::RequiredVersionMet + $PackageStatus += [InstalledState]::GlobalVersionMet + } + + return $PackageStatus +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Uninstall-HomebrewPackage.ps1 b/DevSetup/Private/Providers/Homebrew/Uninstall-HomebrewPackage.ps1 new file mode 100644 index 0000000..003f3bb --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Uninstall-HomebrewPackage.ps1 @@ -0,0 +1,42 @@ +Function Uninstall-HomebrewPackage { + [CmdletBinding(SupportsShouldProcess=$true)] + [OutputType([bool])] + Param( + [string]$PackageName + ) + + if ($PSCmdlet.ShouldProcess($PackageName, "brew uninstall")) { + Write-StatusMessage "- Uninstalling Homebrew package '$PackageName'" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline + if(-not (Test-HasSudoAccess)) { + Write-StatusMessage "Sudo Access is required to install Homebrew packages." -ForegroundColor Red -Verbosity Verbose + Write-StatusMessage "[FAILED]" -ForegroundColor Red + return $false + } + + if (-not (Find-Homebrew)) { + Write-StatusMessage "Homebrew is not installed. Please install Homebrew first." -ForegroundColor Red -Verbosity Verbose + Write-StatusMessage "[FAILED]" -ForegroundColor Red + return $false + } + + if(-not (Test-HomebrewPackageInstalled -PackageName $PackageName).HasFlag([InstalledState]::Installed)) { + Write-StatusMessage "Homebrew package '$PackageName' is not installed." -Verbosity Verbose + Write-StatusMessage "[OK]" -ForegroundColor Green + return $true + } + # Uninstall Homebrew package + $BrewArgs = @{ + Command = "bash" + Arguments = @("-c", [string]::Format("{0} uninstall {1}", (Find-Homebrew), $PackageName)) + } + (Invoke-ExternalCommand @BrewArgs) *> $null + if ($LASTEXITCODE -eq 0) { + Write-StatusMessage "[OK]" -ForegroundColor Green + return $true + } else { + Write-StatusMessage "Failed to uninstall Homebrew package '$PackageName'." -Verbosity Verbose + Write-StatusMessage "[FAILED]" -ForegroundColor Red + return $false + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Write-HomebrewCache.ps1 b/DevSetup/Private/Providers/Homebrew/Write-HomebrewCache.ps1 new file mode 100644 index 0000000..ea6b2a3 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Write-HomebrewCache.ps1 @@ -0,0 +1,30 @@ +Function Write-HomebrewCache { + [CmdletBinding()] + Param() + + $cacheFile = Get-HomebrewCacheFile + $cacheData = @{} + + try { + if (Test-HomebrewInstalled) { + $BrewArgs = @{ + Command = "bash" + Arguments = @("-c", "$(Find-Homebrew) list --versions") + } + + (Invoke-ExternalCommand @BrewArgs) | ForEach-Object { + $parts = $_ -split ' ' + if ($parts.Length -ge 2) { + $packageName = $parts[0] + $packageVersion = $parts[1] + $cacheData[$packageName] = $packageVersion + } + } | Out-Null + } + + $cacheData | ConvertTo-Json | Set-Content -Path $cacheFile + } catch { + Write-StatusMessage "Failed to write Homebrew cache: $_" -Verbosity Error + Write-StatusMessage $_.ScriptStackTrace -Verbosity Error + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Powershell/Test-PowershellModuleInstalled.Tests.ps1 b/DevSetup/Private/Providers/Powershell/Test-PowershellModuleInstalled.Tests.ps1 index 9a2dab5..37910d3 100644 --- a/DevSetup/Private/Providers/Powershell/Test-PowershellModuleInstalled.Tests.ps1 +++ b/DevSetup/Private/Providers/Powershell/Test-PowershellModuleInstalled.Tests.ps1 @@ -1,8 +1,72 @@ BeforeAll { . $PSScriptRoot\Test-PowershellModuleInstalled.ps1 . $PSScriptRoot\..\..\..\..\DevSetup\Private\Enums\InstalledState.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Test-OperatingSystem.ps1 - Mock Test-OperatingSystem { $true } + . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Test-OperatingSystem.ps1 + . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Get-EnvironmentVariable.ps1 + if($PSVersionTable.PSVersion.Major -eq 5) { + $script:LocalModulePath = "$env:USERPROFILE\Documents\WindowsPowerShell\Modules\" + $script:AllUsersModulePath = "$env:ProgramFiles\WindowsPowerShell\Modules\" + Mock Get-EnvironmentVariable { + Param( + [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + $Name + ) + switch ($Name) { + "USERPROFILE" { Write-Output $env:USERPROFILE } + "PSModulePath" { Write-Output "$env:USERPROFILE\Documents\WindowsPowerShell\Modules;$env:ProgramFiles\WindowsPowerShell\Modules" } + } + } + Mock Test-OperatingSystem { $true } + } else { + if($IsWindows) { + $script:LocalModulePath = "$env:USERPROFILE\Documents\PowerShell\Modules\" + $script:AllUsersModulePath = "$env:ProgramFiles\PowerShell\Modules\" + Mock Get-EnvironmentVariable { + Param( + [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + $Name + ) + switch ($Name) { + "USERPROFILE" { Write-Output $env:USERPROFILE } + "HOME" { Write-Output $env:HOME } + "PSModulePath" { Write-Output "$env:USERPROFILE\Documents\PowerShell\Modules;$env:ProgramFiles\PowerShell\Modules" } + } + } + Mock Test-OperatingSystem { $true } + } + if($IsLinux) { + $script:LocalModulePath = "$env:HOME/.local/share/powershell/Modules/" + $script:AllUsersModulePath = "/usr/local/share/powershell/Modules/" + Mock Get-EnvironmentVariable { + Param( + [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + $Name + ) + switch ($Name) { + "USERPROFILE" { Write-Output $env:USERPROFILE } + "HOME" { Write-Output $env:HOME } + "PSModulePath" { Write-Output "$env:HOME/.local/share/powershell/Modules:/usr/local/share/powershell/Modules:/opt/microsoft/powershell/7/Modules" } + } + } + Mock Test-OperatingSystem { $false } + } + if($IsMacOS) { + $script:LocalModulePath = "$env:HOME/.local/share/powershell/Modules/" + $script:AllUsersModulePath = "/usr/local/share/powershell/Modules/" + Mock Get-EnvironmentVariable { + Param( + [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + $Name + ) + switch ($Name) { + "USERPROFILE" { Write-Output $env:USERPROFILE } + "HOME" { Write-Output $env:HOME } + "PSModulePath" { Write-Output "$env:HOME/.local/share/powershell/Modules:/usr/local/share/powershell/Modules:/opt/microsoft/powershell/7/Modules" } + } + } + Mock Test-OperatingSystem { $false } + } + } } Describe "Test-PowershellModuleInstalled" { @@ -21,7 +85,7 @@ Describe "Test-PowershellModuleInstalled" { [PSCustomObject]@{ Name = "posh-git" Version = "1.0.0" - Path = "$env:USERPROFILE\Documents\WindowsPowerShell\Modules\posh-git" + Path = "$($script:LocalModulePath)posh-git" } } $result = Test-PowershellModuleInstalled -ModuleName "posh-git" @@ -36,7 +100,7 @@ Describe "Test-PowershellModuleInstalled" { [PSCustomObject]@{ Name = "PSReadLine" Version = "2.2.6" - Path = "$env:USERPROFILE\Documents\PowerShell\Modules\PSReadLine" + Path = "$($script:LocalModulePath)PSReadLine" } } $result = Test-PowershellModuleInstalled -ModuleName "PSReadLine" -Version "2.2.6" @@ -51,7 +115,7 @@ Describe "Test-PowershellModuleInstalled" { [PSCustomObject]@{ Name = "PSReadLine" Version = "2.2.5" - Path = "$env:USERPROFILE\Documents\PowerShell\Modules\PSReadLine" + Path = "$($script:LocalModulePath)PSReadLine" } } $result = Test-PowershellModuleInstalled -ModuleName "PSReadLine" -Version "2.2.6" @@ -66,7 +130,7 @@ Describe "Test-PowershellModuleInstalled" { [PSCustomObject]@{ Name = "PowerShellGet" Version = "2.2.5" - Path = "$env:ProgramFiles\PowerShell\Modules\PowerShellGet" + Path = "$($script:AllUsersModulePath)PowerShellGet" } } $result = Test-PowershellModuleInstalled -ModuleName "PowerShellGet" -Scope "AllUsers" @@ -81,7 +145,7 @@ Describe "Test-PowershellModuleInstalled" { [PSCustomObject]@{ Name = "Az" Version = "9.0.1" - Path = "$env:USERPROFILE\Documents\PowerShell\Modules\Az" + Path = "$($script:LocalModulePath)Az" } } $result = Test-PowershellModuleInstalled -ModuleName "Az" -Scope "CurrentUser" diff --git a/DevSetup/Private/Providers/Powershell/Test-PowershellModuleInstalled.ps1 b/DevSetup/Private/Providers/Powershell/Test-PowershellModuleInstalled.ps1 index f5e1614..ab8f0d7 100644 --- a/DevSetup/Private/Providers/Powershell/Test-PowershellModuleInstalled.ps1 +++ b/DevSetup/Private/Providers/Powershell/Test-PowershellModuleInstalled.ps1 @@ -104,12 +104,13 @@ Function Test-PowershellModuleInstalled { # AllUsers ps7 (linux/macos) # $env:HOME/.local/share/powershell/Modules if((Test-OperatingSystem -Windows)) { - $SearchPath = $env:USERPROFILE + $SearchPath = (Get-EnvironmentVariable USERPROFILE) } else { - $SearchPath = $env:HOME + $SearchPath = (Get-EnvironmentVariable HOME) } + $InstallPaths = @( - $env:PSModulePath -split ([System.IO.Path]::PathSeparator) | ForEach-Object { + (Get-EnvironmentVariable PSModulePath) -split ([System.IO.Path]::PathSeparator) | ForEach-Object { if($_ -match [regex]::Escape("$SearchPath")) { @{ Path = $_; Scope = "CurrentUser" } } else { diff --git a/DevSetup/Private/Providers/Scoop/Find-Scoop.Tests.ps1 b/DevSetup/Private/Providers/Scoop/Find-Scoop.Tests.ps1 index 8b5c4b7..ccd0fe1 100644 --- a/DevSetup/Private/Providers/Scoop/Find-Scoop.Tests.ps1 +++ b/DevSetup/Private/Providers/Scoop/Find-Scoop.Tests.ps1 @@ -1,12 +1,25 @@ BeforeAll { . $PSScriptRoot\Find-Scoop.ps1 . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Get-EnvironmentVariable.ps1 + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-EnvironmentVariable { return "$TestDrive\Users\Test User" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-EnvironmentVariable { return "$TestDrive/home/testuser" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-EnvironmentVariable { return "$TestDrive/Users/TestUser" } + } } Describe "Find-Scoop" { Context "When scoop is found by Get-Command" { BeforeEach { - Mock Get-Command { return 'TestDrive:\Users\Test User\scoop\shims\scoop.ps1' } + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-Command { return "$TestDrive\Users\Test User\scoop\shims\scoop.ps1" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-Command { return "$TestDrive/home/testuser/scoop/shims/scoop" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-Command { return "$TestDrive/Users/TestUser/scoop/shims/scoop" } + } } It "should return scoop" { $scoop = Find-Scoop @@ -17,7 +30,13 @@ Describe "Find-Scoop" { Context "When scoop is not found by Get-Command or any other option it should return null" { BeforeEach { Mock Get-Command { return $null } - Mock Get-EnvironmentVariable { return 'TestDrive:\Users\Test User' } + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-EnvironmentVariable { return "$TestDrive\Users\Test User" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-EnvironmentVariable { return "$TestDrive/home/testuser" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-EnvironmentVariable { return "$TestDrive/Users/TestUser" } + } } It "should return null" { $scoop = Find-Scoop @@ -28,39 +47,87 @@ Describe "Find-Scoop" { Context "When scoop is not found by Get-Command but scoop.ps1 is found" { BeforeEach { Mock Get-Command { return $null } - Mock Get-EnvironmentVariable { return 'TestDrive:\Users\Test User' } - New-Item -Path 'TestDrive:\Users\Test User\scoop\shims' -ItemType Directory -Force | Out-Null - Set-Content 'TestDrive:\Users\Test User\scoop\shims\scoop.ps1' -Value 'Scoop PowerShell Script' + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-EnvironmentVariable { return "$TestDrive\Users\Test User" } + New-Item -Path "$TestDrive\Users\Test User\scoop\shims" -ItemType Directory -Force | Out-Null + Set-Content "$TestDrive\Users\Test User\scoop\shims\scoop.ps1" -Value 'Scoop PowerShell Script' + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-EnvironmentVariable { return "$TestDrive/home/testuser" } + New-Item -Path "$TestDrive/home/testuser/scoop/shims" -ItemType Directory -Force | Out-Null + Set-Content "$TestDrive/home/testuser/scoop/shims/scoop.ps1" -Value 'Scoop Command Script' + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-EnvironmentVariable { return "$TestDrive/Users/TestUser" } + New-Item -Path "$TestDrive/Users/TestUser/scoop/shims" -ItemType Directory -Force | Out-Null + Set-Content "$TestDrive/Users/TestUser/scoop/shims/scoop.ps1" -Value 'Scoop Command Script' + } } - It "should return TestDrive:\Users\Test User\scoop\shims\scoop.ps1" { + It "should return scoop.ps1" { $scoop = Find-Scoop - $scoop | Should -Be "TestDrive:\Users\Test User\scoop\shims\scoop.ps1" + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + $scoop | Should -Be "$TestDrive\Users\Test User\scoop\shims\scoop.ps1" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + $scoop | Should -Be "$TestDrive/home/testuser/scoop/shims/scoop.ps1" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + $scoop | Should -Be "$TestDrive/Users/TestUser/scoop/shims/scoop.ps1" + } } } Context "When scoop is not found by Get-Command but scoop.cmd is found" { BeforeEach { Mock Get-Command { return $null } - Mock Get-EnvironmentVariable { return 'TestDrive:\Users\Test User' } - New-Item -Path 'TestDrive:\Users\Test User\scoop\shims' -ItemType Directory -Force | Out-Null - Set-Content 'TestDrive:\Users\Test User\scoop\shims\scoop.cmd' -Value 'Scoop Command Script' + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-EnvironmentVariable { return "$TestDrive\Users\Test User" } + New-Item -Path "$TestDrive\Users\Test User\scoop\shims" -ItemType Directory -Force | Out-Null + Set-Content "$TestDrive\Users\Test User\scoop\shims\scoop.cmd" -Value 'Scoop Command Script' + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-EnvironmentVariable { return "$TestDrive/home/testuser" } + New-Item -Path "$TestDrive/home/testuser/scoop/shims" -ItemType Directory -Force | Out-Null + Set-Content "$TestDrive/home/testuser/scoop/shims/scoop.cmd" -Value 'Scoop Command Script' + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-EnvironmentVariable { return "$TestDrive/Users/TestUser" } + New-Item -Path "$TestDrive/Users/TestUser/scoop/shims" -ItemType Directory -Force | Out-Null + Set-Content "$TestDrive/Users/TestUser/scoop/shims/scoop.cmd" -Value 'Scoop Command Script' + } } - It "should return TestDrive:\Users\Test User\scoop\shims\scoop.cmd" { + It "should return scoop.cmd" { $scoop = Find-Scoop - $scoop | Should -Be "TestDrive:\Users\Test User\scoop\shims\scoop.cmd" + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + $scoop | Should -Be "$TestDrive\Users\Test User\scoop\shims\scoop.cmd" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + $scoop | Should -Be "$TestDrive/home/testuser/scoop/shims/scoop.cmd" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + $scoop | Should -Be "$TestDrive/Users/TestUser/scoop/shims/scoop.cmd" + } } } Context "When scoop is not found by Get-Command but scoop is found" { BeforeEach { Mock Get-Command { return $null } - Mock Get-EnvironmentVariable { return 'TestDrive:\Users\Test User' } - New-Item -Path 'TestDrive:\Users\Test User\scoop\shims' -ItemType Directory -Force | Out-Null - Set-Content 'TestDrive:\Users\Test User\scoop\shims\scoop' -Value 'Scoop Command Script' + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-EnvironmentVariable { return "$TestDrive\Users\Test User" } + New-Item -Path "$TestDrive\Users\Test User\scoop\shims" -ItemType Directory -Force | Out-Null + Set-Content "$TestDrive\Users\Test User\scoop\shims\scoop" -Value 'Scoop Command Script' + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-EnvironmentVariable { return "$TestDrive/home/testuser" } + New-Item -Path "$TestDrive/home/testuser/scoop/shims" -ItemType Directory -Force | Out-Null + Set-Content "$TestDrive/home/testuser/scoop/shims/scoop" -Value 'Scoop Command Script' + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-EnvironmentVariable { return "$TestDrive/Users/TestUser" } + New-Item -Path "$TestDrive/Users/TestUser/scoop/shims" -ItemType Directory -Force | Out-Null + Set-Content "$TestDrive/Users/TestUser/scoop/shims/scoop" -Value 'Scoop Command Script' + } } - It "should return TestDrive:\Users\Test User\scoop\shims\scoop" { + It "should return scoop" { $scoop = Find-Scoop - $scoop | Should -Be "TestDrive:\Users\Test User\scoop\shims\scoop" + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + $scoop | Should -Be "$TestDrive\Users\Test User\scoop\shims\scoop" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + $scoop | Should -Be "$TestDrive/home/testuser/scoop/shims/scoop" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + $scoop | Should -Be "$TestDrive/Users/TestUser/scoop/shims/scoop" + } } } } \ No newline at end of file diff --git a/DevSetup/Private/Providers/Scoop/Get-ScoopCacheFile.Tests.ps1 b/DevSetup/Private/Providers/Scoop/Get-ScoopCacheFile.Tests.ps1 index 617a9d7..ac92999 100644 --- a/DevSetup/Private/Providers/Scoop/Get-ScoopCacheFile.Tests.ps1 +++ b/DevSetup/Private/Providers/Scoop/Get-ScoopCacheFile.Tests.ps1 @@ -6,11 +6,23 @@ BeforeAll { Describe "Get-ScoopCacheFile" { Context "When scoop is found by Get-Command" { BeforeEach { - Mock Get-DevSetupCachePath { return 'TestDrive:\Users\Test User\.devsetup\.cache' } + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-DevSetupCachePath { return "$TestDrive\Users\Test User\devsetup\.cache" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-DevSetupCachePath { return "$TestDrive/home/testuser/devsetup/.cache" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-DevSetupCachePath { return "$TestDrive/Users/TestUser/devsetup/.cache" } + } } It "should return the correct scoop cache file path" { $scoopCacheFile = Get-ScoopCacheFile - $scoopCacheFile | Should -Be "TestDrive:\Users\Test User\.devsetup\.cache\scoop.cache" + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + $scoopCacheFile | Should -Be "$TestDrive\Users\Test User\devsetup\.cache\scoop.cache" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + $scoopCacheFile | Should -Be "$TestDrive/home/testuser/devsetup/.cache/scoop.cache" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + $scoopCacheFile | Should -Be "$TestDrive/Users/TestUser/devsetup/.cache/scoop.cache" + } } } } \ No newline at end of file diff --git a/DevSetup/Private/Providers/Scoop/Test-ScoopInstalled.Tests.ps1 b/DevSetup/Private/Providers/Scoop/Test-ScoopInstalled.Tests.ps1 index 2cd4774..c0810d3 100644 --- a/DevSetup/Private/Providers/Scoop/Test-ScoopInstalled.Tests.ps1 +++ b/DevSetup/Private/Providers/Scoop/Test-ScoopInstalled.Tests.ps1 @@ -1,5 +1,13 @@ BeforeAll { . $PSScriptRoot\Test-ScoopInstalled.ps1 + . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Get-EnvironmentVariable.ps1 + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-EnvironmentVariable { return "$TestDrive\Users\TestUser" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-EnvironmentVariable { return "$TestDrive/home/testuser" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-EnvironmentVariable { return "$TestDrive/Users/TestUser" } + } } Describe "Test-ScoopInstalled" { diff --git a/DevSetup/Private/Providers/Scoop/Test-ScoopInstalled.ps1 b/DevSetup/Private/Providers/Scoop/Test-ScoopInstalled.ps1 index 047594e..304f39b 100644 --- a/DevSetup/Private/Providers/Scoop/Test-ScoopInstalled.ps1 +++ b/DevSetup/Private/Providers/Scoop/Test-ScoopInstalled.ps1 @@ -63,17 +63,17 @@ Function Test-ScoopInstalled { return $true } else { # Check for Scoop in user profile directory - $scoopPath = Join-Path $env:USERPROFILE "scoop\shims\scoop.ps1" + $scoopPath = Join-Path (Get-EnvironmentVariable USERPROFILE) "scoop\shims\scoop.ps1" if (Test-Path $scoopPath) { return $true } - $scoopPath = Join-Path $env:USERPROFILE "scoop\shims\scoop.cmd" + $scoopPath = Join-Path (Get-EnvironmentVariable USERPROFILE) "scoop\shims\scoop.cmd" if (Test-Path $scoopPath) { return $true } - $scoopPath = Join-Path $env:USERPROFILE "scoop\shims\scoop" + $scoopPath = Join-Path (Get-EnvironmentVariable USERPROFILE) "scoop\shims\scoop" if (Test-Path $scoopPath) { return $true } diff --git a/DevSetup/Private/Utils/ConvertFrom-Base64.Tests.ps1 b/DevSetup/Private/Utils/ConvertFrom-Base64.Tests.ps1 index 5554228..b9bd915 100644 --- a/DevSetup/Private/Utils/ConvertFrom-Base64.Tests.ps1 +++ b/DevSetup/Private/Utils/ConvertFrom-Base64.Tests.ps1 @@ -25,7 +25,7 @@ Describe "ConvertFrom-Base64" { It "Should decode and write to file, returning true" { $plainText = "Test file output" $base64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($plainText)) - $testFile = "$PSScriptRoot\test_output.txt" + $testFile = New-TemporaryFile if (Test-Path $testFile) { Remove-Item $testFile } $result = ConvertFrom-Base64 -EncodedString $base64 -OutputFile $testFile $result | Should -Be $true diff --git a/DevSetup/Private/Utils/ConvertTo-Base64.Tests.ps1 b/DevSetup/Private/Utils/ConvertTo-Base64.Tests.ps1 index 0f457b2..9499789 100644 --- a/DevSetup/Private/Utils/ConvertTo-Base64.Tests.ps1 +++ b/DevSetup/Private/Utils/ConvertTo-Base64.Tests.ps1 @@ -18,7 +18,7 @@ Describe "ConvertTo-Base64" { Context "When converting a file to Base64" { It "Should return the correct Base64 string" { $inputString = "File content" - $testFile = "${TestDrive}\test_input.txt" + $testFile = New-TemporaryFile Set-Content -Path $testFile -Value $inputString $stringBytes = [System.IO.File]::ReadAllBytes($testFile) $expected = [System.Convert]::ToBase64String($stringBytes) diff --git a/DevSetup/Private/Utils/Get-DevSetupCachePath.Tests.ps1 b/DevSetup/Private/Utils/Get-DevSetupCachePath.Tests.ps1 index 16f617e..47a7d02 100644 --- a/DevSetup/Private/Utils/Get-DevSetupCachePath.Tests.ps1 +++ b/DevSetup/Private/Utils/Get-DevSetupCachePath.Tests.ps1 @@ -4,11 +4,47 @@ BeforeAll { } Describe "Get-DevSetupCachePath" { - BeforeEach { - Mock Get-DevSetupPath { return 'TestDrive:\Users\Test User\.devsetup' } - } - It "should return the correct cache path for a valid user" { - $cachePath = Get-DevSetupCachePath - $cachePath | Should -Be "TestDrive:\Users\Test User\.devsetup\.cache" + if ($PSVersionTable.PSVersion.Major -eq 5) { + Context "When running on Pwsh 5.1" { + BeforeEach { + Mock Get-DevSetupPath { return "$TestDrive\Users\Test User\devsetup" } + } + It "should return the correct cache path for a valid user" { + $cachePath = Get-DevSetupCachePath + $cachePath | Should -Be "$TestDrive\Users\Test User\devsetup\.cache" + } + } + } elseif ($PSVersionTable.PSVersion.Major -ge 6) { + if ($IsWindows) { + Context "When running on Pwsh 6+ on Windows" { + BeforeEach { + Mock Get-DevSetupPath { return "$TestDrive\Users\Test User\devsetup" } + } + It "should return the correct cache path for a valid user" { + $cachePath = Get-DevSetupCachePath + $cachePath | Should -Be "$TestDrive\Users\Test User\devsetup\.cache" + } + } + } elseif ($IsLinux) { + Context "When running on Pwsh 6+ on Linux" { + BeforeEach { + Mock Get-DevSetupPath { return "$TestDrive/home/testuser/devsetup" } + } + It "should return the correct cache path for a valid user" { + $cachePath = Get-DevSetupCachePath + $cachePath | Should -Be "$TestDrive/home/testuser/devsetup/.cache" + } + } + } elseif ($IsMacOS) { + Context "When running on Pwsh 6+ on MacOS" { + BeforeEach { + Mock Get-DevSetupPath { return "$TestDrive/Users/TestUser/devsetup" } + } + It "should return the correct cache path for a valid user" { + $cachePath = Get-DevSetupCachePath + $cachePath | Should -Be "$TestDrive/Users/TestUser/devsetup/.cache" + } + } + } } } \ No newline at end of file diff --git a/DevSetup/Private/Utils/Get-DevSetupEnvPath.Tests.ps1 b/DevSetup/Private/Utils/Get-DevSetupEnvPath.Tests.ps1 index f6a238d..39b924e 100644 --- a/DevSetup/Private/Utils/Get-DevSetupEnvPath.Tests.ps1 +++ b/DevSetup/Private/Utils/Get-DevSetupEnvPath.Tests.ps1 @@ -1,14 +1,51 @@ BeforeAll { . $PSScriptRoot\Get-DevSetupEnvPath.ps1 . $PSScriptRoot\Get-DevSetupPath.ps1 + . $PSScriptRoot\Test-OperatingSystem.ps1 } Describe "Get-DevSetupEnvPath" { - BeforeEach { - Mock Get-DevSetupPath { return 'TestDrive:\Users\Test User\.devsetup' } - } - It "should return the correct environment path for a valid user" { - $envPath = Get-DevSetupEnvPath - $envPath | Should -Be "TestDrive:\Users\Test User\.devsetup\environments" + if ($PSVersionTable.PSVersion.Major -eq 5) { + Context "When running on Pwsh 5.1" { + BeforeEach { + Mock Get-DevSetupPath { return "$TestDrive\Users\Test User\devsetup" } + } + It "should return the correct environment path for a valid user" { + $envPath = Get-DevSetupEnvPath + $envPath | Should -Be "$TestDrive\Users\Test User\devsetup\environments" + } + } + } elseif ($PSVersionTable.PSVersion.Major -ge 6) { + if($IsWindows) { + Context "When running on Pwsh 6+" { + BeforeEach { + Mock Get-DevSetupPath { return "$TestDrive\Users\Test User\devsetup" } + } + It "should return the correct environment path for a valid user" { + $envPath = Get-DevSetupEnvPath + $envPath | Should -Be "$TestDrive\Users\Test User\devsetup\environments" + } + } + } elseif ($IsLinux) { + Context "When running on Linux" { + BeforeEach { + Mock Get-DevSetupPath { return "$TestDrive/home/testuser/devsetup" } + } + It "should return the correct environment path for a valid user" { + $envPath = Get-DevSetupEnvPath + $envPath | Should -Be "$TestDrive/home/testuser/devsetup/environments" + } + } + } elseif ($IsMacOS) { + Context "When running on MacOS" { + BeforeEach { + Mock Get-DevSetupPath { return "$TestDrive/Users/TestUser/devsetup" } + } + It "should return the correct environment path for a valid user" { + $envPath = Get-DevSetupEnvPath + $envPath | Should -Be "$TestDrive/Users/TestUser/devsetup/environments" + } + } + } } } \ No newline at end of file diff --git a/DevSetup/Private/Utils/Get-DevSetupLogPath.ps1 b/DevSetup/Private/Utils/Get-DevSetupLogPath.ps1 new file mode 100644 index 0000000..7e03414 --- /dev/null +++ b/DevSetup/Private/Utils/Get-DevSetupLogPath.ps1 @@ -0,0 +1,13 @@ +Function Get-DevSetupLogPath { + [CmdletBinding()] + param ( + ) + + $LogPath = Join-Path -Path (Get-DevSetupPath) -ChildPath "logs" + + if (-not (Test-Path -Path $LogPath)) { + New-Item -ItemType Directory -Path $LogPath | Out-Null + } + + return $LogPath +} \ No newline at end of file diff --git a/DevSetup/Private/Utils/Get-DevSetupPath.Tests.ps1 b/DevSetup/Private/Utils/Get-DevSetupPath.Tests.ps1 index f8a8fc0..0f2bf30 100644 --- a/DevSetup/Private/Utils/Get-DevSetupPath.Tests.ps1 +++ b/DevSetup/Private/Utils/Get-DevSetupPath.Tests.ps1 @@ -6,11 +6,51 @@ BeforeAll { } Describe "Get-DevSetupPath" { - BeforeEach { - Mock Get-EnvironmentVariable { return 'TestDrive:\Users\Test User' } - } - It "should return the correct devsetup for the current user" { - $envPath = Get-DevSetupPath - $envPath | Should -Be "TestDrive:\Users\Test User\devsetup" + if ($PSVersionTable.PSVersion.Major -eq 5) { + Context "When running on Pwsh 5.1" { + BeforeEach { + Mock Get-EnvironmentVariable { return "$TestDrive\Users\Test User" } + } + It "should return the correct devsetup for the current user" { + $envPath = Get-DevSetupPath + $envPath | Should -Be "$TestDrive\Users\Test User\devsetup" + } + } + } elseif ($PSVersionTable.PSVersion.Major -ge 6) { + Context "When running on Pwsh 6+" { + BeforeEach { + if ($IsWindows) { + Mock Get-EnvironmentVariable { return (Join-Path $TestDrive "Users" "Test User") } + } elseif( $IsLinux) { + Mock Get-EnvironmentVariable { return (Join-Path $TestDrive "home" "testuser") } + } elseif ($IsMacOS) { + Mock Get-EnvironmentVariable { return (Join-Path $TestDrive "Users" "TestUser") } + } + Mock Test-OperatingSystem { $true } + } + + if($IsLinux) { + It "should return the correct devsetup for the current user on Linux" { + $envPath = Get-DevSetupPath + $envPath | Should -Be (Join-Path $TestDrive "home" "testuser" "devsetup") + } + } + + if($IsMacOS) { + It "should return the correct devsetup for the current user on MacOS" { + Mock Test-OperatingSystem { Param($Windows, $Linux, $MacOS) { return $MacOS } } + $envPath = Get-DevSetupPath + $envPath | Should -Be (Join-Path $TestDrive "Users" "TestUser" "devsetup") + } + } + + if($IsWindows) { + It "should return the correct devsetup for the current user on Windows" { + Mock Test-OperatingSystem { Param($Windows, $Linux, $MacOS) { return $Windows } } + $envPath = Get-DevSetupPath + $envPath | Should -Be (Join-Path $TestDrive "Users" "Test User" "devsetup") + } + } + } } } \ No newline at end of file diff --git a/DevSetup/Private/Utils/Invoke-ExternalCommand.ps1 b/DevSetup/Private/Utils/Invoke-ExternalCommand.ps1 new file mode 100644 index 0000000..12f5155 --- /dev/null +++ b/DevSetup/Private/Utils/Invoke-ExternalCommand.ps1 @@ -0,0 +1,19 @@ +Function Invoke-ExternalCommand { + [CmdletBinding()] + Param( + [Parameter(Mandatory)] + [string]$Command, + [Parameter()] + [string[]]$Arguments + ) + + # Build the full command line + $cmdLine = $Command + if ($Arguments) { + $cmdLine += " " + ($Arguments -join " ") + } + + # Invoke the command and capture output + $output = & $Command @Arguments 2>&1 + return $output +} \ No newline at end of file diff --git a/DevSetup/Private/Utils/Optimize-DevSetupEnvs.Tests.ps1 b/DevSetup/Private/Utils/Optimize-DevSetupEnvs.Tests.ps1 index 9818354..e531397 100644 --- a/DevSetup/Private/Utils/Optimize-DevSetupEnvs.Tests.ps1 +++ b/DevSetup/Private/Utils/Optimize-DevSetupEnvs.Tests.ps1 @@ -1,4 +1,5 @@ BeforeAll { + Function Write-EZLog { } Function ConvertFrom-Yaml { } . $PSScriptRoot\Optimize-DevSetupEnvs.ps1 . $PSScriptRoot\Get-DevSetupEnvPath.ps1 @@ -8,7 +9,7 @@ BeforeAll { Mock Get-DevSetupEnvPath { "$TestDrive\DevSetupEnvs" } Mock Get-DevSetupPath { "$TestDrive\DevSetup" } Mock Join-Path { Param($Path, $ChildPath) "$Path\$ChildPath" } - Mock Write-StatusMessage { } + Mock Write-StatusMessage { Write-Error $Message } Mock Write-Warning { } Mock Write-Error { } Mock Write-Debug { } @@ -36,7 +37,7 @@ Describe "Optimize-DevSetupEnvs" { Mock Get-DevSetupEnvPath { $null } $result = Optimize-DevSetupEnvs $result | Should -Be $false - Assert-MockCalled Write-Warning -Scope It -ParameterFilter { $Message -match "DevSetup environments path not found" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "DevSetup environments path not found" -and $Verbosity -eq "Warning" } } It "Should warn and return null if path does not exist" { @@ -44,7 +45,7 @@ Describe "Optimize-DevSetupEnvs" { Mock Test-Path { $false } $result = Optimize-DevSetupEnvs $result | Should -Be $false - Assert-MockCalled Write-Warning -Scope It -ParameterFilter { $Message -match "DevSetup environments path not found" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "DevSetup environments path not found" -and $Verbosity -eq "Warning" } } } @@ -56,7 +57,7 @@ Describe "Optimize-DevSetupEnvs" { $result | Should -BeOfType 'bool' $result | Should -Be $true Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Indexing 0 environment files" } - Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -eq "[OK]" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "[OK]" } } } @@ -70,11 +71,11 @@ Describe "Optimize-DevSetupEnvs" { ) } $result = Optimize-DevSetupEnvs - Assert-MockCalled Write-Error -Scope It -Exactly 0 - #Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Object -match "Indexing 2 environment files" } - #Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -eq "[OK]" } + Assert-MockCalled Write-StatusMessage -Scope It -Exactly 0 -ParameterFilter { $Verbosity -eq "Error" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Indexing 2 environment files" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "[OK]" } $result | Should -BeOfType 'bool' - $result | Should -Be $false + $result | Should -Be $true } } @@ -93,10 +94,10 @@ Describe "Optimize-DevSetupEnvs" { @{ devsetup = @{ configuration = @{ os = @{ name = "Windows" }; version = "1.0.0" } } } } $result = Optimize-DevSetupEnvs - Assert-MockCalled Write-Error -Scope It -Exactly 0 - Assert-MockCalled Write-Warning -Scope It -ParameterFilter { $Message -match "Failed to process bad.yaml" } + Assert-MockCalled Write-StatusMessage -Scope It -Exactly 0 -ParameterFilter { $Verbosity -eq "Error" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Failed to process bad.yaml" -and $Verbosity -eq "Warning" } $result | Should -BeOfType 'bool' - $result | Should -Be $false + $result | Should -Be $true } } @@ -112,7 +113,7 @@ Describe "Optimize-DevSetupEnvs" { Mock Out-File { throw "File error" } $result = Optimize-DevSetupEnvs $result | Should -Be $false - Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -eq "[Failed]" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Failed to optimize DevSetup environments" } } } @@ -121,7 +122,7 @@ Describe "Optimize-DevSetupEnvs" { Mock Get-DevSetupEnvPath { throw "Unexpected error" } $result = Optimize-DevSetupEnvs $result | Should -Be $false - Assert-MockCalled Write-Error -Scope It -ParameterFilter { $Message -match "Failed to optimize DevSetup environments" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Failed to optimize DevSetup environments" -and $Verbosity -eq "Error" } } } } \ No newline at end of file diff --git a/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1 b/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1 index d262fea..a876fdf 100644 --- a/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1 +++ b/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1 @@ -1,9 +1,12 @@ Function Optimize-DevSetupEnvs { + [CmdletBinding()] + [OutputType([bool])] + Param() try { # Get the DevSetup environments path $envPath = Get-DevSetupEnvPath if (-not $envPath -or -not (Test-Path $envPath)) { - Write-Warning "DevSetup environments path not found or doesn't exist: $envPath" + Write-StatusMessage "DevSetup environments path not found or doesn't exist: $envPath" -Verbosity Warning return $false } @@ -60,10 +63,10 @@ Function Optimize-DevSetupEnvs { $environments += $envEntry $platformDisplay = if ($platform) { $platform } else { 'Not specified' } - Write-Debug " - Name: $envName, Version: $version, Platform: $platformDisplay" + Write-StatusMessage " - Name: $envName, Version: $version, Platform: $platformDisplay" -Verbosity Debug } catch { - Write-Warning "Failed to process $($devsetupEnvFile.Name): $_" + Write-StatusMessage "Failed to process $($devsetupEnvFile.Name): $_" -Verbosity Warning continue } } @@ -74,20 +77,21 @@ Function Optimize-DevSetupEnvs { try { $jsonOutput = $environments | ConvertTo-Json -Depth 10 - $jsonOutput | Out-File -FilePath $environmentsJsonPath -Encoding UTF8 - Write-Debug "Environment index written to: $environmentsJsonPath" - #Write-Host "Processed $($environments.Count) environment(s) successfully" -ForegroundColor Green + $jsonOutput | Out-File -FilePath $environmentsJsonPath + Write-StatusMessage "Environment index written to: $environmentsJsonPath" -Verbosity Debug Write-StatusMessage "[OK]" -ForegroundColor Green } catch { - Write-StatusMessage "[Failed]" -ForegroundColor Red + Write-StatusMessage "Failed to optimize DevSetup environments: $_" -ForegroundColor Red -Verbosity Error + Write-StatusMessage $_.ScriptStackTrace -Verbosity Error return $false } return $true } catch { - Write-Error "Failed to optimize DevSetup environments: $_" + Write-StatusMessage "Failed to optimize DevSetup environments: $_" -Verbosity Error + Write-StatusMessage $_.ScriptStackTrace -Verbosity Error return $false } } \ No newline at end of file diff --git a/DevSetup/Private/Utils/Start-DevSetupSelfUpdate.ps1 b/DevSetup/Private/Utils/Start-DevSetupSelfUpdate.ps1 new file mode 100644 index 0000000..dfff6e6 --- /dev/null +++ b/DevSetup/Private/Utils/Start-DevSetupSelfUpdate.ps1 @@ -0,0 +1,22 @@ +Function Start-DevSetupSelfUpdate { + [CmdletBinding()] + Param() + + $manifest = Get-DevSetupManifest + if($null -eq $manifest) { + throw "Failed to load manifest file" + } + + $communityEnvironmentsProjectUri = $manifest.PrivateData.PSData.EnvironmentsProjectUri + $devsetupProjectUri = $manifest.PrivateData.PSData.ProjectUri + + $currentVersion = Get-DevSetupVersion + + $devsetupCurrentReleaseInfo = Get-GitHubRelease -Uri $devsetupProjectUri | Select-Object -First 1 + $communityEnvironmentsCurrentReleaseInfo = Get-GitHubRelease -Uri $communityEnvironmentsProjectUri | Select-Object -First 1 + + $devsetupCurrentReleaseVersion = [Version]::new(($devsetupCurrentReleaseInfo.name -Replace "v")) + if($currentVersion -lt $devsetupCurrentReleaseVersion) { + + } +} \ No newline at end of file diff --git a/DevSetup/Private/Utils/Test-HasSudoAccess.ps1 b/DevSetup/Private/Utils/Test-HasSudoAccess.ps1 new file mode 100644 index 0000000..97d832e --- /dev/null +++ b/DevSetup/Private/Utils/Test-HasSudoAccess.ps1 @@ -0,0 +1,13 @@ +Function Test-HasSudoAccess { + [CmdletBinding()] + Param( + ) + + # Try running a harmless command with sudo + (bash -c "sudo -n true") *>$null + if ($LASTEXITCODE -eq 0) { + return $true + } else { + return $false + } +} \ No newline at end of file diff --git a/DevSetup/Private/Utils/Test-OperatingSystem.Tests.ps1 b/DevSetup/Private/Utils/Test-OperatingSystem.Tests.ps1 index 9c3d61b..3c6c187 100644 --- a/DevSetup/Private/Utils/Test-OperatingSystem.Tests.ps1 +++ b/DevSetup/Private/Utils/Test-OperatingSystem.Tests.ps1 @@ -30,33 +30,76 @@ Describe "Test-OperatingSystem" { } Context "When called with no parameters on PowerShell 5.1" { - It "Should return $null" { + It "Should return $false" { $result = Test-OperatingSystem - $result | Should -Be $null + $result | Should -Be $false } } } if ($PSVersionTable.PSVersion.Major -ge 6) { BeforeAll { Mock Get-PwshVersion { [PSCustomObject]@{ Major = $PSVersionTable.PSVersion.Major } } } - - Context "When called with -Windows on PowerShell 7+" { - It "Should return value of `$IsWindows (default: $true)" { - $result = Test-OperatingSystem -Windows - $result | Should -Be $true - } - It "Should return value of `$IsLinux (default: $false)" { - $result = Test-OperatingSystem -Linux - $result | Should -Be $false + if($IsWindows) { + Context "When called in PowerShell 7+ (Windows)" { + It "Should return value of `$IsWindows (default: $true)" { + $result = Test-OperatingSystem -Windows + $result | Should -Be $true + } + It "Should return value of `$IsLinux (default: $false)" { + $result = Test-OperatingSystem -Linux + $result | Should -Be $false + } + It "Should return value of `$IsMacOS (default: $false)" { + $result = Test-OperatingSystem -MacOS + $result | Should -Be $false + } + It "Should return $false if no parameter is specified" { + $result = Test-OperatingSystem + $result | Should -Be $false + } } - It "Should return value of `$IsMacOS (default: $false)" { - $result = Test-OperatingSystem -MacOS - $result | Should -Be $false + } + + if($IsLinux) { + Context "When called in PowerShell 7+ (Linux)" { + It "Should return value of `$IsWindows (default: $false)" { + $result = Test-OperatingSystem -Windows + $result | Should -Be $false + } + It "Should return value of `$IsLinux (default: $true)" { + $result = Test-OperatingSystem -Linux + $result | Should -Be $true + } + It "Should return value of `$IsMacOS (default: $false)" { + $result = Test-OperatingSystem -MacOS + $result | Should -Be $false + } + It "Should return $false if no parameter is specified" { + $result = Test-OperatingSystem + $result | Should -Be $false + } } - It "Should return $null if no parameter is specified" { - $result = Test-OperatingSystem - $result | Should -Be $null + } + + if($IsMacOS) { + Context "When called in PowerShell 7+ (MacOS)" { + It "Should return value of `$IsWindows (default: $false)" { + $result = Test-OperatingSystem -Windows + $result | Should -Be $false + } + It "Should return value of `$IsLinux (default: $false)" { + $result = Test-OperatingSystem -Linux + $result | Should -Be $false + } + It "Should return value of `$IsMacOS (default: $true)" { + $result = Test-OperatingSystem -MacOS + $result | Should -Be $true + } + It "Should return $false if no parameter is specified" { + $result = Test-OperatingSystem + $result | Should -Be $false + } } - } + } } } \ No newline at end of file diff --git a/DevSetup/Private/Utils/Test-OperatingSystem.ps1 b/DevSetup/Private/Utils/Test-OperatingSystem.ps1 index ebb1f27..b12392c 100644 --- a/DevSetup/Private/Utils/Test-OperatingSystem.ps1 +++ b/DevSetup/Private/Utils/Test-OperatingSystem.ps1 @@ -1,5 +1,6 @@ Function Test-OperatingSystem { [CmdletBinding()] + [OutputType([bool])] Param( [Parameter(Mandatory=$false)] [switch]$Windows, @@ -12,19 +13,19 @@ Function Test-OperatingSystem { ) if((Get-PwshVersion).Major -lt 6) { - $IsWindows = $true - $IsLinux = $false - $IsMacOS = $false + $IsPS5Windows = $true + $IsPS5Linux = $false + $IsPS5MacOS = $false } if($Windows) { - return $IsWindows + return ($IsPS5Windows -or $IsWindows) } if($Linux) { - return $IsLinux + return ($IsPS5Linux -or $IsLinux) } if($MacOS) { - return $IsMacOS + return ($IsPS5MacOS -or $IsMacOS) } - return $null + return $false } \ No newline at end of file diff --git a/DevSetup/Private/Utils/Test-RunningAsAdmin.Tests.ps1 b/DevSetup/Private/Utils/Test-RunningAsAdmin.Tests.ps1 index 6865457..d1cd82f 100644 --- a/DevSetup/Private/Utils/Test-RunningAsAdmin.Tests.ps1 +++ b/DevSetup/Private/Utils/Test-RunningAsAdmin.Tests.ps1 @@ -14,33 +14,35 @@ Describe "Test-RunningAsAdmin" { } } - Context "When running on Windows as administrator" { - It "Should return true" { - Mock Test-OperatingSystem { param($Windows) $true } - class MockPrincipal { - [bool] IsInRole([object]$role) { return $true } + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -eq 6 -and $IsWindows)) { + Context "When running on Windows as administrator" { + It "Should return true" { + Mock Test-OperatingSystem { param($Windows) $true } + class MockPrincipal { + [bool] IsInRole([object]$role) { return $true } + } + Mock 'New-Object' -MockWith { + param($type) + return [MockPrincipal]::new() + } + $result = Test-RunningAsAdmin + $result | Should -Be $true } - Mock 'New-Object' -MockWith { - param($type) - return [MockPrincipal]::new() - } - $result = Test-RunningAsAdmin - $result | Should -Be $true } - } - Context "When running on Windows but not as administrator" { - It "Should return false" { - Mock Test-OperatingSystem { param($Windows) $true } - class MockPrincipal { - [bool] IsInRole([object]$role) { return $false } - } - Mock 'New-Object' -MockWith { - param($type) - return [MockPrincipal]::new() + Context "When running on Windows but not as administrator" { + It "Should return false" { + Mock Test-OperatingSystem { param($Windows) $true } + class MockPrincipal { + [bool] IsInRole([object]$role) { return $false } + } + Mock 'New-Object' -MockWith { + param($type) + return [MockPrincipal]::new() + } + $result = Test-RunningAsAdmin + $result | Should -Be $false } - $result = Test-RunningAsAdmin - $result | Should -Be $false } } } \ No newline at end of file diff --git a/DevSetup/Private/Utils/Write-NewConfig.ps1 b/DevSetup/Private/Utils/Write-NewConfig.ps1 index df37b3e..bdb3f93 100644 --- a/DevSetup/Private/Utils/Write-NewConfig.ps1 +++ b/DevSetup/Private/Utils/Write-NewConfig.ps1 @@ -1,7 +1,8 @@ Function Write-NewConfig { Param( [Parameter(Mandatory = $true)] - [string]$OutFile + [string]$OutFile, + [switch]$DryRun = $false ) try { @@ -89,6 +90,7 @@ Function Write-NewConfig { } } + $username = if ($env:USERNAME) { $env:USERNAME } elseif ($env:USER) { $env:USER } else { "Unknown" } # Handle versioning and preserve existing config $currentVersion = "1.0.0" # Default version for new files $baseConfig = @{ @@ -111,7 +113,7 @@ Function Write-NewConfig { description = "Auto-generated development environment configuration" version = $currentVersion createdDate = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") - createdBy = $env:USERNAME + createdBy = $username os = @{ name = $friendlyPlatform version = $friendlyOsVersion @@ -189,16 +191,24 @@ Function Write-NewConfig { return $false } - # Convert from installed Chocolatey packages - Write-Host "`nScanning installed Chocolatey packages..." -ForegroundColor Cyan - if (-not (Export-InstalledChocolateyPackages -Config $OutFile)) { - Write-Warning "Failed to convert Chocolatey packages, but continuing..." - } + if((Test-OperatingSystem -Windows)) { + # Convert from installed Chocolatey packages + Write-Host "`nScanning installed Chocolatey packages..." -ForegroundColor Cyan + if (-not (Export-InstalledChocolateyPackages -Config $OutFile)) { + Write-Warning "Failed to convert Chocolatey packages, but continuing..." + } - # Convert from installed Scoop packages - Write-Host "`nScanning installed Scoop packages..." -ForegroundColor Cyan - if (-not (Export-InstalledScoopPackages -Config $OutFile)) { - Write-Warning "Failed to convert Scoop packages, but continuing..." + # Convert from installed Scoop packages + Write-Host "`nScanning installed Scoop packages..." -ForegroundColor Cyan + if (-not (Export-InstalledScoopPackages -Config $OutFile)) { + Write-Warning "Failed to convert Scoop packages, but continuing..." + } + } else { + # Convert from installed Homebrew packages + Write-Host "`nScanning installed Homebrew packages..." -ForegroundColor Cyan + if (-not (Invoke-HomebrewComponentExport -Config $OutFile -DryRun:$DryRun)) { + Write-Warning "Failed to convert Homebrew packages, but continuing..." + } } # Convert from installed PowerShell modules diff --git a/DevSetup/Private/Utils/Write-StatusMessage.Tests.ps1 b/DevSetup/Private/Utils/Write-StatusMessage.Tests.ps1 index c9cc458..60f4b91 100644 --- a/DevSetup/Private/Utils/Write-StatusMessage.Tests.ps1 +++ b/DevSetup/Private/Utils/Write-StatusMessage.Tests.ps1 @@ -1,10 +1,12 @@ BeforeAll { + Function Write-EZLog {} . $PSScriptRoot\Write-StatusMessage.ps1 Mock Write-Host { param($Message) } Mock Write-Verbose { param($Object) } Mock Write-Debug { param($Object) } Mock Write-Warning { param($Object) } Mock Write-Error { param($Object) } + Mock Write-EZLog { } } Describe "Write-StatusMessage" { diff --git a/DevSetup/Private/Utils/Write-StatusMessage.ps1 b/DevSetup/Private/Utils/Write-StatusMessage.ps1 index 35d6223..82bcec4 100644 --- a/DevSetup/Private/Utils/Write-StatusMessage.ps1 +++ b/DevSetup/Private/Utils/Write-StatusMessage.ps1 @@ -16,6 +16,11 @@ Function Write-StatusMessage { [switch]$NoNewLine ) + if([string]::IsNullOrWhiteSpace($Message)) { + #Write-StatusMessage "Message cannot be empty or whitespace." -Verbosity Error + return + } + if ($Indent -gt 0) { $Message = "$(' ' * $Indent)$Message" } @@ -41,18 +46,23 @@ Function Write-StatusMessage { switch($Verbosity) { "Verbose" { + Write-EZLog -Category INF -Message $Message Write-Verbose @messageParams } "Debug" { + Write-EZLog -Category INF -Message $Message Write-Debug @messageParams } "Warning" { + Write-EZLog -Category WAR -Message $Message Write-Warning @messageParams } "Error" { + Write-EZLog -Category ERR -Message $Message Write-Error @messageParams } "Default" { + #Write-EZLog -Category INF -Message $Message Write-Host @messageParams } } diff --git a/DevSetup/Public/Use-DevSetup.ps1 b/DevSetup/Public/Use-DevSetup.ps1 index df02c7e..99b0d28 100644 --- a/DevSetup/Public/Use-DevSetup.ps1 +++ b/DevSetup/Public/Use-DevSetup.ps1 @@ -113,7 +113,7 @@ Function Use-DevSetup { .FUNCTIONALITY Environment Management, Configuration Installation, System Setup #> - + [CmdletBinding()] Param( [Parameter(Mandatory = $true, ParameterSetName = "Install")] [Parameter(Mandatory = $true, ParameterSetName = "InstallUrl")] @@ -165,7 +165,10 @@ Function Use-DevSetup { [Parameter(Mandatory = $true, ParameterSetName = "InstallPath")] [Parameter(Mandatory = $true, ParameterSetName = "ExportPath")] - [string]$Path + [string]$Path, + + [Parameter(Mandatory = $false)] + [switch]$DryRun = $false ) try { @@ -173,7 +176,7 @@ Function Use-DevSetup { $selectedAction = $PSCmdlet.ParameterSetName.ToLower() - function Repeat-Char($char, $count) { -join (1..$count | ForEach-Object { $char }) } + function Format-RepeatChar($char, $count) { -join (1..$count | ForEach-Object { $char }) } # Display fancy action header # Define box drawing characters using [char] codes @@ -187,15 +190,15 @@ Function Use-DevSetup { $ml = [char]0x2560 # $mr = [char]0x2563 - $tb = "$tl" + (Repeat-Char $h 118) + "$tr" - $bm = "$ml" + (Repeat-Char $h 118) + "$mr" - $bb = "$bl" + (Repeat-Char $h 118) + "$br" - $sp = "$v" + (Repeat-Char " " 118) + "$v" + $tb = "$tl" + (Format-RepeatChar $h 118) + "$tr" + $bm = "$ml" + (Format-RepeatChar $h 118) + "$mr" + $bb = "$bl" + (Format-RepeatChar $h 118) + "$br" + $sp = "$v" + (Format-RepeatChar " " 118) + "$v" Write-Host "" Write-Host "$tb" -ForegroundColor Cyan Write-Host "$sp" -ForegroundColor Cyan - Write-Host "$v" (Repeat-Char " " 25) -ForegroundColor Cyan -NoNewLine + Write-Host "$v" (Format-RepeatChar " " 25) -ForegroundColor Cyan -NoNewLine Write-Host "$b$b$b$b$b$b" -ForegroundColor White -NoNewLine Write-Host "$tr " -ForegroundColor Cyan -NoNewLine Write-Host "$b$b$b$b$b$b$b" -ForegroundColor White -NoNewLine @@ -215,9 +218,9 @@ Function Use-DevSetup { Write-Host "$b$b" -ForegroundColor White -NoNewLine Write-Host "$tr" -ForegroundColor Cyan -NoNewLine Write-Host "$b$b$b$b$b$b" -ForegroundColor White -NoNewLine - Write-Host "$tr" (Repeat-Char " " 24) "$v" -ForegroundColor Cyan + Write-Host "$tr" (Format-RepeatChar " " 24) "$v" -ForegroundColor Cyan - Write-Host "$v" (Repeat-Char " " 25) -ForegroundColor Cyan -NoNewLine + Write-Host "$v" (Format-RepeatChar " " 25) -ForegroundColor Cyan -NoNewLine Write-Host "$b$b" -ForegroundColor White -NoNewLine Write-Host "$tl$h$h" -ForegroundColor Cyan -NoNewLine Write-Host "$b$b" -ForegroundColor White -NoNewLine @@ -241,9 +244,9 @@ Function Use-DevSetup { Write-Host "$b$b" -ForegroundColor White -NoNewLine Write-Host "$tl$h$h" -ForegroundColor Cyan -NoNewLine Write-Host "$b$b" -ForegroundColor White -NoNewLine - Write-Host "$tr" (Repeat-Char " " 23) "$v" -ForegroundColor Cyan + Write-Host "$tr" (Format-RepeatChar " " 23) "$v" -ForegroundColor Cyan - Write-Host "$v" (Repeat-Char " " 25) -ForegroundColor Cyan -NoNewLine + Write-Host "$v" (Format-RepeatChar " " 25) -ForegroundColor Cyan -NoNewLine Write-Host "$b$b" -ForegroundColor White -NoNewLine Write-Host "$v " -ForegroundColor Cyan -NoNewLine Write-Host "$b$b" -ForegroundColor White -NoNewLine @@ -265,9 +268,9 @@ Function Use-DevSetup { Write-Host "$b$b" -ForegroundColor White -NoNewLine Write-Host "$v" -ForegroundColor Cyan -NoNewLine Write-Host "$b$b$b$b$b$b" -ForegroundColor White -NoNewLine - Write-Host "$tl$br" (Repeat-Char " " 23) "$v" -ForegroundColor Cyan + Write-Host "$tl$br" (Format-RepeatChar " " 23) "$v" -ForegroundColor Cyan - Write-Host "$v" (Repeat-Char " " 25) -ForegroundColor Cyan -NoNewLine + Write-Host "$v" (Format-RepeatChar " " 25) -ForegroundColor Cyan -NoNewLine Write-Host "$b$b" -ForegroundColor White -NoNewLine Write-Host "$v " -ForegroundColor Cyan -NoNewLine Write-Host "$b$b" -ForegroundColor White -NoNewLine @@ -289,9 +292,9 @@ Function Use-DevSetup { Write-Host "$b$b" -ForegroundColor White -NoNewLine Write-Host "$v" -ForegroundColor Cyan -NoNewLine Write-Host "$b$b" -ForegroundColor White -NoNewLine - Write-Host "$tl$h$h$h$br" (Repeat-Char " " 24) "$v" -ForegroundColor Cyan + Write-Host "$tl$h$h$h$br" (Format-RepeatChar " " 24) "$v" -ForegroundColor Cyan - Write-Host "$v" (Repeat-Char " " 25) -ForegroundColor Cyan -NoNewLine + Write-Host "$v" (Format-RepeatChar " " 25) -ForegroundColor Cyan -NoNewLine Write-Host "$b$b$b$b$b$b" -ForegroundColor White -NoNewLine Write-Host "$tl$br" -ForegroundColor Cyan -NoNewLine Write-Host "$b$b$b$b$b$b$b" -ForegroundColor White -NoNewLine @@ -307,9 +310,9 @@ Function Use-DevSetup { Write-Host "$b$b$b$b$b$b" -ForegroundColor White -NoNewLine Write-Host "$tl$br" -ForegroundColor Cyan -NoNewLine Write-Host "$b$b" -ForegroundColor White -NoNewLine - Write-Host "$v" (Repeat-Char " " 28) "$v" -ForegroundColor Cyan + Write-Host "$v" (Format-RepeatChar " " 28) "$v" -ForegroundColor Cyan - Write-Host "$v" (Repeat-Char " " 24) "$bl$h$h$h$h$h$br $bl$h$h$h$h$h$h$br $bl$h$h$h$br $bl$h$h$h$h$h$h$br$bl$h$h$h$h$h$h$br $bl$h$br $bl$h$h$h$h$h$br $bl$h$br" (Repeat-Char " " 28) "$v" -ForegroundColor Cyan + Write-Host "$v" (Format-RepeatChar " " 24) "$bl$h$h$h$h$h$br $bl$h$h$h$h$h$h$br $bl$h$h$h$br $bl$h$h$h$h$h$h$br$bl$h$h$h$h$h$h$br $bl$h$br $bl$h$h$h$h$h$br $bl$h$br" (Format-RepeatChar " " 28) "$v" -ForegroundColor Cyan Write-Host "$v" -ForegroundColor Cyan -NoNewline $version = Get-DevSetupVersion -Local @@ -345,42 +348,49 @@ Function Use-DevSetup { Write-Host "$v" -ForegroundColor Cyan Write-Host "$bb" -ForegroundColor Cyan Write-Host "" - + + $RunDate = Get-Date -Format "yyyy-MM-dd_HH-mm-ss" + $LogFile = Join-Path -Path (Get-DevSetupLogPath) -ChildPath ([string]::Format("devsetup_{0}_{1}.log", $selectedAction, $RunDate)) + $PSDefaultParameterValues = @{ + 'Write-EZLog:LogFile' = $LogFile ; + } + + Write-EZLog -Header switch ($selectedAction) { {$_ -eq 'install' -or $_ -eq 'installpath' -or $_ -eq 'installurl'} { - Write-Host "Installing development environment..." -ForegroundColor Yellow + Write-StatusMessage "Installing development environment..." -ForegroundColor Yellow $ParameterCopy = [hashtable]$PSBoundParameters $ParameterCopy.Remove('Install') Install-DevSetupEnv @ParameterCopy } {$_ -eq 'update' -or $_ -eq 'updatemain' -or $_ -eq 'updatedevelop' -or $_ -eq 'updateversion'} { - Write-Host "Updating devsetup system..." -ForegroundColor Yellow + Write-StatusMessage "Updating devsetup system..." -ForegroundColor Yellow $ParameterCopy = [hashtable]$PSBoundParameters $ParameterCopy.Remove('Update') if($_ -eq 'update') { $ParameterCopy['Latest'] = $true - } + } Update-DevSetup @ParameterCopy | Out-Null } 'init' { - Write-Host "Initializing DevSetup system..." -ForegroundColor Yellow + Write-StatusMessage "Initializing DevSetup system..." -ForegroundColor Yellow Initialize-DevSetup | Out-Null } { $_ -eq 'export' -or $_ -eq 'exportpath' } { - Write-Host "Exporting current development environment..." -ForegroundColor Yellow + Write-StatusMessage "Exporting current development environment..." -ForegroundColor Yellow $ParameterCopy = [hashtable]$PSBoundParameters - $ParameterCopy.Remove('Export') + $ParameterCopy.Remove('Export') Export-DevSetupEnv @ParameterCopy } { $_ -eq 'list' -or $_ -eq 'listplatform' -or $_ -eq 'listprovider' -or $_ -eq 'listproviderplatform' } { $ParameterCopy = [hashtable]$PSBoundParameters - $ParameterCopy.Remove('List') + $ParameterCopy.Remove('List') Show-DevSetupEnvList @ParameterCopy } 'uninstall' { - Write-Host "Uninstalling development environment..." -ForegroundColor Yellow + Write-StatusMessage "Uninstalling development environment..." -ForegroundColor Yellow $ParameterCopy = [hashtable]$PSBoundParameters - $ParameterCopy.Remove('Uninstall') + $ParameterCopy.Remove('Uninstall') Uninstall-DevSetupEnv @ParameterCopy } } @@ -388,6 +398,10 @@ Function Use-DevSetup { #Write-Host "DevSetup action '$selectedAction' completed successfully!" -ForegroundColor Green } catch { - Write-Error "Error executing DevSetup action '$selectedAction': $_" + Write-StatusMessage "Error executing DevSetup action '$selectedAction': $_" -Verbosity Error + Write-StatusMessage $_.ScriptStackTrace -Verbosity Error } + Write-StatusMessage "Log file: $LogFile" -ForegroundColor DarkGray + Write-EZLog -Footer + Write-Host "" } \ No newline at end of file diff --git a/Remove-File.ps1 b/Remove-File.ps1 new file mode 100644 index 0000000..2cb2731 --- /dev/null +++ b/Remove-File.ps1 @@ -0,0 +1,11 @@ +Function Remove-File { + [CmdletBinding(SupportsShouldProcess=$true)] + Param([string]$Path) + + if ($PSCmdlet.ShouldProcess("git", "brew install")) { + Remove-Item $Path + } +} + +Remove-File -Path ./converage.xml -WhatIf +Remove-File -Path ./converage.xml -Confirm \ No newline at end of file diff --git a/runTests.ps1 b/runTests.ps1 index 545a806..19cf388 100644 --- a/runTests.ps1 +++ b/runTests.ps1 @@ -2,5 +2,5 @@ $config = New-PesterConfiguration #$config.Run.PassThru = $true $config.CodeCoverage.Enabled = $true $config.TestResult.Enabled = $true -#$config.Output.Verbosity = "Detailed" +#$config.Output.Verbosity = "GithubActions" Invoke-Pester -Configuration $config \ No newline at end of file From 753d0ffd130a5b8eef15fb1cf2170fa08494823b Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sat, 6 Sep 2025 23:36:26 -0500 Subject: [PATCH 02/18] Adding unit tests for homebrew related tasks. --- .../Homebrew/Get-HomebrewCacheFile.Tests.ps1 | 41 +++++ .../Homebrew/Get-HomebrewCacheFile.ps1 | 6 + .../Homebrew/Get-HomebrewVersion.Tests.ps1 | 111 +++++++++++ .../Homebrew/Get-HomebrewVersion.ps1 | 44 +++-- .../Homebrew/Install-Homebrew.Tests.ps1 | 172 ++++++++++++++++++ .../Providers/Homebrew/Install-Homebrew.ps1 | 90 ++++----- .../Install-HomebrewPackage.Tests.ps1 | 141 ++++++++++++++ .../Homebrew/Install-HomebrewPackage.ps1 | 5 +- .../Invoke-HomebrewComponentsExport.Tests.ps1 | 169 +++++++++++++++++ .../Invoke-HomebrewComponentsExport.ps1 | 12 +- ...Invoke-HomebrewComponentsInstall.Tests.ps1 | 106 +++++++++++ ...voke-HomebrewComponentsUninstall.Tests.ps1 | 94 ++++++++++ .../Homebrew/Read-HomebrewCache.Tests.ps1 | 78 ++++++++ .../Homebrew/Test-HomebrewInstalled.Tests.ps1 | 114 ++++++++++++ ...stalled.ps1 => Test-HomebrewInstalled.ps1} | 0 .../Test-HomebrewPackageInstalled.Tests.ps1 | 102 +++++++++++ .../Test-HomebrewPackageInstalled.ps1 | 9 +- .../Uninstall-HomebrewPackage.Tests.ps1 | 141 ++++++++++++++ .../Homebrew/Write-HomebrewCache.Tests.ps1 | 122 +++++++++++++ .../Homebrew/Write-HomebrewCache.ps1 | 4 +- 20 files changed, 1487 insertions(+), 74 deletions(-) create mode 100644 DevSetup/Private/Providers/Homebrew/Get-HomebrewCacheFile.Tests.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Get-HomebrewVersion.Tests.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Install-Homebrew.Tests.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Install-HomebrewPackage.Tests.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsExport.Tests.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsInstall.Tests.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsUninstall.Tests.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Read-HomebrewCache.Tests.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Test-HomebrewInstalled.Tests.ps1 rename DevSetup/Private/Providers/Homebrew/{Test-HomebewInstalled.ps1 => Test-HomebrewInstalled.ps1} (100%) create mode 100644 DevSetup/Private/Providers/Homebrew/Test-HomebrewPackageInstalled.Tests.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Uninstall-HomebrewPackage.Tests.ps1 create mode 100644 DevSetup/Private/Providers/Homebrew/Write-HomebrewCache.Tests.ps1 diff --git a/DevSetup/Private/Providers/Homebrew/Get-HomebrewCacheFile.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Get-HomebrewCacheFile.Tests.ps1 new file mode 100644 index 0000000..e83d4c2 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Get-HomebrewCacheFile.Tests.ps1 @@ -0,0 +1,41 @@ +BeforeAll { + . (Join-Path $PSScriptRoot Get-HomebrewCacheFile.ps1) + . (Join-Path $PSScriptRoot ..\..\..\..\DevSetup\Private\Utils\Get-DevSetupCachePath.ps1) + . (Join-Path $PSScriptRoot ..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1) + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + Mock Get-DevSetupCachePath { return "$TestDrive\Users\TestUser\devsetup\.cache" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + Mock Get-DevSetupCachePath { return "$TestDrive/home/testuser/devsetup/.cache" } + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + Mock Get-DevSetupCachePath { return "$TestDrive/Users/testuser/devsetup/.cache" } + } + Mock Write-StatusMessage { } +} + +Describe "Get-HomebrewCacheFile" { + Context "Windows" { + It "should return the correct cache file path on Windows" { + $result = Get-HomebrewCacheFile + if ($PSVersionTable.PSVersion.Major -eq 5 -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)) { + $result | Should -Be "$TestDrive\Users\TestUser\devsetup\.cache\homebrew.cache" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsLinux) { + $result | Should -Be "$TestDrive/home/testuser/devsetup/.cache/homebrew.cache" + } elseif ($PSVersionTable.PSVersion.Major -ge 6 -and $IsMacOS) { + $result | Should -Be "$TestDrive/Users/testuser/devsetup/.cache/homebrew.cache" + } + + Assert-MockCalled Get-DevSetupCachePath -Exactly 1 -Scope It + } + } + + Context "When Get-DevSetupCachePath returns null" { + It "should return null" { + Mock Get-DevSetupCachePath { return $null } + + $result = Get-HomebrewCacheFile + $result | Should -Be $null + Assert-MockCalled Get-DevSetupCachePath -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 2 -Scope It -ParameterFilter { $Verbosity -eq "Error" } + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Get-HomebrewCacheFile.ps1 b/DevSetup/Private/Providers/Homebrew/Get-HomebrewCacheFile.ps1 index 1cebfa7..8124e9f 100644 --- a/DevSetup/Private/Providers/Homebrew/Get-HomebrewCacheFile.ps1 +++ b/DevSetup/Private/Providers/Homebrew/Get-HomebrewCacheFile.ps1 @@ -2,6 +2,12 @@ Function Get-HomebrewCacheFile { [CmdletBinding()] Param() + try { $CacheFile = Join-Path -Path (Get-DevSetupCachePath) -ChildPath "homebrew.cache" + } catch { + Write-StatusMessage "Failed to get Homebrew cache file path: $_" -Verbosity Error + Write-StatusMessage $_.ScriptStackTrace -Verbosity Error + return $null + } return $CacheFile } \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Get-HomebrewVersion.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Get-HomebrewVersion.Tests.ps1 new file mode 100644 index 0000000..40ead2f --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Get-HomebrewVersion.Tests.ps1 @@ -0,0 +1,111 @@ +BeforeAll { + . (Join-Path $PSScriptRoot Get-HomebrewVersion.ps1) + . (Join-Path $PSScriptRoot ..\..\..\..\DevSetup\Private\Providers\Homebrew\Test-HomebrewInstalled.ps1) + . (Join-Path $PSScriptRoot ..\..\..\..\DevSetup\Private\Providers\Homebrew\Find-Homebrew.ps1) + . (Join-Path $PSScriptRoot ..\..\..\..\DevSetup\Private\Utils\Invoke-ExternalCommand.ps1) + . (Join-Path $PSScriptRoot ..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1) +} + +Describe "Get-HomebrewVersion" { + Context "When Homebrew is not installed" { + It "should return null" { + Mock Test-HomebrewInstalled { $false } + Mock Write-StatusMessage { } + + $result = Get-HomebrewVersion + $result | Should -Be $null + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 1 -Scope It -ParameterFilter { $Message -match "Homebrew is not installed" } + } + } + + Context "When Homebrew path is not found" { + It "should return null" { + Mock Test-HomebrewInstalled { $true } + Mock Find-Homebrew { $null } + Mock Write-StatusMessage { } + + $result = Get-HomebrewVersion + $result | Should -Be $null + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 1 -Scope It -ParameterFilter { $Message -match "Homebrew installation not found" } + } + } + + Context "When version is successfully retrieved" { + It "should return the version string" { + Mock Test-HomebrewInstalled { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Invoke-ExternalCommand { "Homebrew 3.5.10" } + Mock Write-StatusMessage { } + + $result = Get-HomebrewVersion + $result | Should -Be "3.5.10" + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 1 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It -ParameterFilter { $Command -eq "/usr/local/bin/brew" -and $Arguments -contains "--version" } + } + } + + Context "When output does not contain a version" { + It "should return null" { + Mock Test-HomebrewInstalled { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Invoke-ExternalCommand { "Homebrew version not available" } + Mock Write-StatusMessage { } + + $result = Get-HomebrewVersion + $result | Should -Be $null + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 1 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + } + } + + Context "When Invoke-ExternalCommand throws an exception" { + It "should return null" { + Mock Test-HomebrewInstalled { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Invoke-ExternalCommand { throw "Command failed" } + Mock Write-StatusMessage { } + + $result = Get-HomebrewVersion + $result | Should -Be $null + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 1 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 1 -Scope It -ParameterFilter { $Message -match "Failed to get Homebrew version" } + } + } + + Context "Cross-platform compatibility" { + It "should work on Windows (where Homebrew is unlikely)" { + Mock Test-HomebrewInstalled { $false } + Mock Write-StatusMessage { } + + $result = Get-HomebrewVersion + $result | Should -Be $null + } + + It "should work on Linux" { + Mock Test-HomebrewInstalled { $true } + Mock Find-Homebrew { "/home/linuxbrew/.linuxbrew/bin/brew" } + Mock Invoke-ExternalCommand { "Homebrew 3.5.10" } + Mock Write-StatusMessage { } + + $result = Get-HomebrewVersion + $result | Should -Be "3.5.10" + } + + It "should work on macOS" { + Mock Test-HomebrewInstalled { $true } + Mock Find-Homebrew { "/opt/homebrew/bin/brew" } + Mock Invoke-ExternalCommand { "Homebrew 3.5.10" } + Mock Write-StatusMessage { } + + $result = Get-HomebrewVersion + $result | Should -Be "3.5.10" + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Get-HomebrewVersion.ps1 b/DevSetup/Private/Providers/Homebrew/Get-HomebrewVersion.ps1 index 6ab2547..6596d9c 100644 --- a/DevSetup/Private/Providers/Homebrew/Get-HomebrewVersion.ps1 +++ b/DevSetup/Private/Providers/Homebrew/Get-HomebrewVersion.ps1 @@ -4,28 +4,34 @@ Function Get-HomebrewVersion { Param( ) - # Get Homebrew version - if( -not (Test-HomebrewInstalled) ) { - Write-StatusMessage "Homebrew is not installed" -Verbosity Verbose - return $null - } + try { + # Get Homebrew version + if( -not (Test-HomebrewInstalled) ) { + Write-StatusMessage "Homebrew is not installed" -Verbosity Verbose + return $null + } - $HomebrewPath = Find-Homebrew - if (-not $HomebrewPath) { - Write-StatusMessage "Homebrew installation not found" -Verbosity Verbose - return $null - } + $HomebrewPath = Find-Homebrew + if (-not $HomebrewPath) { + Write-StatusMessage "Homebrew installation not found" -Verbosity Verbose + return $null + } - $BrewArgs = @{ - Command = $HomebrewPath - Arguments = @("--version") - } + $BrewArgs = @{ + Command = $HomebrewPath + Arguments = @("--version") + } - $HomeBrewVersion = (Invoke-ExternalCommand @BrewArgs) -match "([0-9]+\.[0-9]+\.[0-9]+)" - if ($HomeBrewVersion) { - $version = $matches[1] - return $version - } else { + $HomeBrewVersion = (Invoke-ExternalCommand @BrewArgs) -match "([0-9]+\.[0-9]+\.[0-9]+)" + if ($HomeBrewVersion) { + $version = $matches[1] + return $version + } else { + return $null + } + } catch { + Write-StatusMessage "Failed to get Homebrew version: $_" -Verbosity Error + Write-StatusMessage $_.ScriptStackTrace -Verbosity Error return $null } } \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Install-Homebrew.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.Tests.ps1 new file mode 100644 index 0000000..59f039a --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.Tests.ps1 @@ -0,0 +1,172 @@ +BeforeAll { + . (Join-Path $PSScriptRoot "Install-Homebrew.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Test-HasSudoAccess.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Find-Homebrew.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Invoke-ExternalCommand.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Get-EnvironmentVariable.ps1") +} + +Describe "Install-Homebrew" { + Context "When sudo access is not available" { + It "should return false" { + Mock Test-HasSudoAccess { $false } + Mock Write-StatusMessage { } + + $result = Install-Homebrew + $result | Should -Be $false + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 3 -Scope It # One for checking sudo, one for failure + } + } + + Context "When Homebrew is already installed" { + It "should return true without installing" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Write-StatusMessage { } + Mock Invoke-ExternalCommand { return $true } + + $result = Install-Homebrew + $result | Should -Be $true + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 1 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 0 -Scope It # No installation needed + } + } + + Context "When installation succeeds and shell is bash" { + It "should install Homebrew, add to .bashrc, and return true" { + $script:callCount = 0 + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { + $script:callCount++ + if ($script:callCount -eq 1) { return $null } # First call returns null + else { return "/usr/local/bin/brew" } # Second call returns path + } + Mock Invoke-ExternalCommand { $true } + Mock Write-StatusMessage { } + Mock Get-EnvironmentVariable { "/bin/bash" } + Mock Add-Content { } + + $result = Install-Homebrew + $result | Should -Be $true + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 2 -Scope It # Once before install, once after + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + Assert-MockCalled Add-Content -Exactly 2 -Scope It # Blank line and shellenv line + } + } + + Context "When installation succeeds and shell is zsh" { + It "should install Homebrew, add to .zshrc, and return true" { + $script:callCount = 0 + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { + $script:callCount++ + if ($script:callCount -eq 1) { return $null } + else { return "/usr/local/bin/brew" } + } + Mock Invoke-ExternalCommand { "Installation successful" } + Mock Write-StatusMessage { } + Mock Get-EnvironmentVariable { "/bin/zsh" } + Mock Add-Content { } + + $result = Install-Homebrew + $result | Should -Be $true + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 2 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + Assert-MockCalled Add-Content -Exactly 2 -Scope It + } + } + + Context "When installation succeeds and shell is unknown" { + It "should install Homebrew, warn about shell, and return true" { + $script:callCount = 0 + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { + $script:callCount++ + if ($script:callCount -eq 1) { return $null } + else { return "/usr/local/bin/brew" } + } + Mock Invoke-ExternalCommand { "Installation successful" } + Mock Write-StatusMessage { } + Mock Get-EnvironmentVariable { "/bin/fish" } + Mock Add-Content { } + + $result = Install-Homebrew + $result | Should -Be $true + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 2 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + Assert-MockCalled Add-Content -Exactly 0 -Scope It # No shell config added + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Unknown shell" } + } + } + + Context "When installation fails" { + It "should return false" { + $script:callCount = 0 + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { + $script:callCount++ + return $null # Always return null + } + Mock Invoke-ExternalCommand { "Installation failed" } + Mock Write-StatusMessage { } + + $result = Install-Homebrew + $result | Should -Be $false + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 2 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + } + } + + Context "Cross-platform compatibility" { + It "should handle Windows (where Homebrew installation is not supported)" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { $null } + Mock Invoke-ExternalCommand { throw "Not supported on Windows" } + Mock Write-StatusMessage { } + + $result = Install-Homebrew + $result | Should -Be $false + } + + It "should work on Linux" { + $script:callCount = 0 + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { + $script:callCount++ + if ($script:callCount -eq 1) { return $null } + else { return "/home/linuxbrew/.linuxbrew/bin/brew" } + } + Mock Invoke-ExternalCommand { $true } + Mock Write-StatusMessage { } + Mock Get-EnvironmentVariable { "/bin/bash" } + Mock Add-Content { } + + $result = Install-Homebrew + $result | Should -Be $true + } + + It "should work on macOS" { + $script:callCount = 0 + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { + $script:callCount++ + if ($script:callCount -eq 1) { return $null } + else { return "/opt/homebrew/bin/brew" } + } + Mock Invoke-ExternalCommand { $true } + Mock Write-StatusMessage { } + Mock Get-EnvironmentVariable { "/bin/zsh" } + Mock Add-Content { } + + $result = Install-Homebrew + $result | Should -Be $true + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1 b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1 index 3ef338f..133d083 100644 --- a/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1 +++ b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1 @@ -4,53 +4,59 @@ Function Install-Homebrew { Param( ) - Write-StatusMessage "- Checking for sudo access" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline - if ((Test-HasSudoAccess)) { - Write-StatusMessage "[OK]" -ForegroundColor Green - } else { - Write-StatusMessage "[FAILED]" -ForegroundColor Red - Write-StatusMessage "Sudo access is required to install Homebrew. Please run this script with a user that has sudo privileges." -Verbosity Warning - return $false - } - - # Install Homebrew - Write-StatusMessage "- Installing Homebrew package manager" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline - if (-not (Find-Homebrew)) { - # Installation command for Homebrew (Linux/Mac) - $installCmd = 'NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"' - $BrewArgs = @{ - Command = "bash" - Arguments = @("-c", "$installCmd") - } - Invoke-ExternalCommand @BrewArgs - $HomebrewPath = Find-Homebrew - if ([string]::IsNullOrEmpty($HomebrewPath)) { + try { + Write-StatusMessage "- Checking for sudo access" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline + if ((Test-HasSudoAccess)) { + Write-StatusMessage "[OK]" -ForegroundColor Green + } else { Write-StatusMessage "[FAILED]" -ForegroundColor Red + Write-StatusMessage "Sudo access is required to install Homebrew. Please run this script with a user that has sudo privileges." -Verbosity Warning return $false - } else { - Write-StatusMessage "[OK]" -ForegroundColor Green - switch ($env:SHELL) { - { $_ -like "*zsh*" } { - Write-StatusMessage "- Adding Homebrew path to $HOME/.zshrc" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline - Add-Content -Path "$HOME/.zshrc" -Value "" - Add-Content -Path "$HOME/.zshrc" -Value ([string]::Format('eval "$({0} shellenv)"', $HomebrewPath)) - Write-StatusMessage "[OK]" -ForegroundColor Green - } - { $_ -like "*bash*" } { - Write-StatusMessage "- Adding Homebrew path to $HOME/.bashrc" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline - Add-Content -Path "$HOME/.bashrc" -Value "" - Add-Content -Path "$HOME/.bashrc" -Value ([string]::Format('eval "$({0} shellenv)"', $HomebrewPath)) - Write-StatusMessage "[OK]" -ForegroundColor Green - } - default { - Write-StatusMessage "Unknown shell: $($env:SHELL). You may need to manually add Homebrew to your PATH." -Verbosity Warning - } + } + + # Install Homebrew + Write-StatusMessage "- Installing Homebrew package manager" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline + if (-not (Find-Homebrew)) { + # Installation command for Homebrew (Linux/Mac) + $installCmd = 'NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"' + $BrewArgs = @{ + Command = "bash" + Arguments = @("-c", "$installCmd") } + Invoke-ExternalCommand @BrewArgs *> $null + $HomebrewPath = Find-Homebrew + if ([string]::IsNullOrEmpty($HomebrewPath)) { + Write-StatusMessage "[FAILED]" -ForegroundColor Red + return $false + } else { + Write-StatusMessage "[OK]" -ForegroundColor Green + switch ((Get-EnvironmentVariable SHELL)) { + { $_ -like "*zsh*" } { + Write-StatusMessage "- Adding Homebrew path to $HOME/.zshrc" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline + Add-Content -Path "$HOME/.zshrc" -Value "" + Add-Content -Path "$HOME/.zshrc" -Value ([string]::Format('eval "$({0} shellenv)"', $HomebrewPath)) + Write-StatusMessage "[OK]" -ForegroundColor Green + } + { $_ -like "*bash*" } { + Write-StatusMessage "- Adding Homebrew path to $HOME/.bashrc" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline + Add-Content -Path "$HOME/.bashrc" -Value "" + Add-Content -Path "$HOME/.bashrc" -Value ([string]::Format('eval "$({0} shellenv)"', $HomebrewPath)) + Write-StatusMessage "[OK]" -ForegroundColor Green + } + default { + Write-StatusMessage "Unknown shell: $($env:SHELL). You may need to manually add Homebrew to your PATH." -Verbosity Warning + } + } + return $true + } + } else { + Write-StatusMessage "[OK]" -ForegroundColor Green return $true } - } else { - Write-StatusMessage "[OK]" -ForegroundColor Green - return $true + } catch { + Write-StatusMessage "An error occurred while installing Homebrew: $_" -Verbosity Error + Write-StatusMessage $_.ScriptStackTrace -Verbosity Error + return $false } } \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Install-HomebrewPackage.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Install-HomebrewPackage.Tests.ps1 new file mode 100644 index 0000000..c956814 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Install-HomebrewPackage.Tests.ps1 @@ -0,0 +1,141 @@ +BeforeAll { + . (Join-Path $PSScriptRoot "Install-HomebrewPackage.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Test-HasSudoAccess.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Find-Homebrew.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Test-HomebrewPackageInstalled.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Invoke-ExternalCommand.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") +} + +Describe "Install-HomebrewPackage" { + Context "When sudo access is not available" { + It "should return false" { + Mock Test-HasSudoAccess { $false } + Mock Write-StatusMessage { } + + $result = Install-HomebrewPackage -PackageName "git" + $result | Should -Be $false + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 3 -Scope It # One for status, one for failure + } + } + + Context "When Homebrew is not installed" { + It "should return false" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { $null } + Mock Write-StatusMessage { } + + $result = Install-HomebrewPackage -PackageName "git" + $result | Should -Be $false + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 3 -Scope It + } + } + + Context "When package is already installed" { + It "should return true without installing" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Test-HomebrewPackageInstalled { [InstalledState]::Pass } + Mock Write-StatusMessage { } + Mock Invoke-ExternalCommand { $true } + + $result = Install-HomebrewPackage -PackageName "git" + $result | Should -Be $true + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 1 -Scope It + Assert-MockCalled Test-HomebrewPackageInstalled -Exactly 1 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 0 -Scope It # No installation needed + } + } + + Context "When installation succeeds" { + It "should install the package and return true" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Test-HomebrewPackageInstalled { [InstalledState]::NotInstalled } + Mock Invoke-ExternalCommand { $true } + Mock Write-StatusMessage { } + $global:LASTEXITCODE = 0 + + $result = Install-HomebrewPackage -PackageName "git" + $result | Should -Be $true + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 2 -Scope It + Assert-MockCalled Test-HomebrewPackageInstalled -Exactly 1 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + } + } + + Context "When installation fails" { + It "should return false" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Test-HomebrewPackageInstalled { [InstalledState]::NotInstalled } + Mock Invoke-ExternalCommand { $false } + Mock Write-StatusMessage { } + $global:LASTEXITCODE = 1 + + $result = Install-HomebrewPackage -PackageName "git" + $result | Should -Be $false + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 2 -Scope It + Assert-MockCalled Test-HomebrewPackageInstalled -Exactly 1 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + } + } + + Context "When -WhatIf is used" { + It "should not perform the installation" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Test-HomebrewPackageInstalled { [InstalledState]::Fail } + Mock Write-StatusMessage { } + Mock Invoke-ExternalCommand { $true } + + ($result = Install-HomebrewPackage -PackageName "git" -WhatIf) *> $null + $result | Should -Be $null # ShouldProcess returns null when WhatIf is used + Assert-MockCalled Test-HasSudoAccess -Exactly 0 -Scope It # Should not proceed + Assert-MockCalled Find-Homebrew -Exactly 0 -Scope It + Assert-MockCalled Test-HomebrewPackageInstalled -Exactly 0 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 0 -Scope It + } + } + + Context "Cross-platform compatibility" { + It "should handle Windows (where Homebrew is unlikely)" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { $null } + Mock Write-StatusMessage { } + + $result = Install-HomebrewPackage -PackageName "git" + $result | Should -Be $false + } + + It "should work on Linux" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { "/home/linuxbrew/.linuxbrew/bin/brew" } + Mock Test-HomebrewPackageInstalled { [InstalledState]::NotInstalled } + Mock Invoke-ExternalCommand { $true } + Mock Write-StatusMessage { } + $global:LASTEXITCODE = 0 + + $result = Install-HomebrewPackage -PackageName "git" + $result | Should -Be $true + } + + It "should work on macOS" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { "/opt/homebrew/bin/brew" } + Mock Test-HomebrewPackageInstalled { [InstalledState]::NotInstalled } + Mock Invoke-ExternalCommand { $true } + Mock Write-StatusMessage { } + $global:LASTEXITCODE = 0 + + $result = Install-HomebrewPackage -PackageName "git" + $result | Should -Be $true + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Install-HomebrewPackage.ps1 b/DevSetup/Private/Providers/Homebrew/Install-HomebrewPackage.ps1 index 8e98515..03f2f4f 100644 --- a/DevSetup/Private/Providers/Homebrew/Install-HomebrewPackage.ps1 +++ b/DevSetup/Private/Providers/Homebrew/Install-HomebrewPackage.ps1 @@ -3,7 +3,10 @@ Function Install-HomebrewPackage { [OutputType([bool])] Param( [Parameter(Mandatory=$true, Position=0, ParameterSetName="Install")] - [string]$PackageName + [Parameter(Mandatory=$true, Position=0, ParameterSetName="InstallMinimumVersion")] + [string]$PackageName, + [Parameter(Mandatory=$true, Position=1, ParameterSetName="InstallMinimumVersion")] + [string]$MinimumVersion ) if ($PSCmdlet.ShouldProcess($PackageName, "brew install")) { diff --git a/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsExport.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsExport.Tests.ps1 new file mode 100644 index 0000000..2b0222c --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsExport.Tests.ps1 @@ -0,0 +1,169 @@ +BeforeAll { + Function ConvertTo-Yaml { } + . (Join-Path $PSScriptRoot "Invoke-HomebrewComponentsExport.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Read-ConfigurationFile.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Find-Homebrew.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Invoke-ExternalCommand.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") +} + +Describe "Invoke-HomebrewComponentsExport" { + Context "When Homebrew is not installed" { + It "should return false" { + Mock Read-ConfigurationFile { @{ devsetup = @{ dependencies = @{ } } } } + Mock Find-Homebrew { $null } + Mock Write-StatusMessage { } + + $result = Invoke-HomebrewComponentsExport -Config "test.yaml" + $result | Should -Be $false + Assert-MockCalled Find-Homebrew -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 1 -Scope It -ParameterFilter { $Message -match "Homebrew is not installed" } + } + } + + Context "When export succeeds" { + It "should update YAML data and save the file" { + Mock Read-ConfigurationFile { @{ devsetup = @{ dependencies = @{ homebrew = @() } } } } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Invoke-ExternalCommand { + Param($Command, $Arguments) + if ($Arguments -contains "list --versions") { + return "git 2.30.1`nnode 14.17.0" + } elseif ($Arguments -contains "list --installed-on-request") { + return "git`nnode" + } + } + Mock ConvertTo-Yaml { "mock yaml output" } + Mock Out-File { } + Mock Write-StatusMessage { } + + $result = Invoke-HomebrewComponentsExport -Config "test.yaml" + $result | Should -Be $true + Assert-MockCalled Read-ConfigurationFile -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 3 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 2 -Scope It + Assert-MockCalled ConvertTo-Yaml -Exactly 1 -Scope It + Assert-MockCalled Out-File -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Configuration saved successfully" } + } + } + + Context "When YAML conversion fails" { + It "should fall back to JSON and save" { + Mock Read-ConfigurationFile { @{ devsetup = @{ dependencies = @{ homebrew = @() } } } } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Invoke-ExternalCommand { + Param($Command, $Arguments) + if ($Arguments -contains "list --versions") { + return "git 2.30.1" + } elseif ($Arguments -contains "list --installed-on-request") { + return "git" + } + } + Mock ConvertTo-Yaml { throw "YAML conversion failed" } + Mock ConvertTo-Json { "mock json output" } + Mock Out-File { } + Mock Write-StatusMessage { } + + $result = Invoke-HomebrewComponentsExport -Config "test.yaml" + $result | Should -Be $true + Assert-MockCalled ConvertTo-Yaml -Exactly 1 -Scope It + Assert-MockCalled ConvertTo-Json -Exactly 1 -Scope It + Assert-MockCalled Out-File -Exactly 1 -Scope It + } + } + + Context "When saving fails" { + It "should return false" { + Mock Read-ConfigurationFile { @{ devsetup = @{ dependencies = @{ homebrew = @() } } } } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Invoke-ExternalCommand { + Param($Command, $Arguments) + if ($Arguments -contains "list --versions") { + return "git 2.30.1" + } elseif ($Arguments -contains "list --installed-on-request") { + return "git" + } + } + Mock ConvertTo-Yaml { "mock yaml output" } + Mock Out-File { throw "Save failed" } + Mock Write-StatusMessage { } + + $result = Invoke-HomebrewComponentsExport -Config "test.yaml" + $result | Should -Be $false + Assert-MockCalled Out-File -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "Failed to save configuration" } + } + } + + Context "When WhatIf is specified" { + It "should not save the file" { + Mock Read-ConfigurationFile { @{ devsetup = @{ dependencies = @{ homebrew = @() } } } } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Invoke-ExternalCommand { + Param($Command, $Arguments) + if ($Arguments -contains "list --versions") { + return "git 2.30.1" + } elseif ($Arguments -contains "list --installed-on-request") { + return "git" + } + } + Mock ConvertTo-Yaml { "mock yaml output" } + Mock Out-File { } + Mock Write-StatusMessage { } + + ($result = Invoke-HomebrewComponentsExport -Config "test.yaml" -WhatIf:$true) *> $null + $result | Should -Be $true + Assert-MockCalled Out-File -Exactly 0 -Scope It # Should not save + } + } + + Context "Cross-platform compatibility" { + It "should handle Windows (where Homebrew is unlikely)" { + Mock Read-ConfigurationFile { @{ devsetup = @{ dependencies = @{ } } } } + Mock Find-Homebrew { $null } + Mock Write-StatusMessage { } + + $result = Invoke-HomebrewComponentsExport -Config "test.yaml" + $result | Should -Be $false + } + + It "should work on Linux" { + Mock Read-ConfigurationFile { @{ devsetup = @{ dependencies = @{ homebrew = @() } } } } + Mock Find-Homebrew { "/home/linuxbrew/.linuxbrew/bin/brew" } + Mock Invoke-ExternalCommand { + Param($Command, $Arguments) + if ($Arguments -contains "list --versions") { + return "git 2.30.1" + } elseif ($Arguments -contains "list --installed-on-request") { + return "git" + } + } + Mock ConvertTo-Yaml { "mock yaml output" } + Mock Out-File { } + Mock Write-StatusMessage { } + + $result = Invoke-HomebrewComponentsExport -Config "test.yaml" + $result | Should -Be $true + } + + It "should work on macOS" { + Mock Read-ConfigurationFile { @{ devsetup = @{ dependencies = @{ homebrew = @() } } } } + Mock Find-Homebrew { "/opt/homebrew/bin/brew" } + Mock Invoke-ExternalCommand { + Param($Command, $Arguments) + if ($Arguments -contains "list --versions") { + return "git 2.30.1" + } elseif ($Arguments -contains "list --installed-on-request") { + return "git" + } + } + Mock ConvertTo-Yaml { "mock yaml output" } + Mock Out-File { } + Mock Write-StatusMessage { } + + $result = Invoke-HomebrewComponentsExport -Config "test.yaml" + $result | Should -Be $true + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsExport.ps1 b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsExport.ps1 index 39869ca..b3579c4 100644 --- a/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsExport.ps1 +++ b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsExport.ps1 @@ -1,13 +1,11 @@ -Function Invoke-HomebrewComponentExport { - [CmdletBinding()] +Function Invoke-HomebrewComponentsExport { + [CmdletBinding(SupportsShouldProcess = $true)] [OutputType([bool])] Param( [Parameter(Mandatory = $true)] [string]$Config, [Parameter(Mandatory = $false)] - [string]$OutFile, - [Parameter(Mandatory = $false)] - [switch]$DryRun + [string]$OutFile ) $YamlData = Read-ConfigurationFile -Config $Config @@ -66,7 +64,9 @@ Function Invoke-HomebrewComponentExport { try { Write-StatusMessage "Saving configuration to: $outputFile" -Verbosity Verbose - $yamlOutput | Out-File -FilePath $outputFile + if ($PSCmdlet.ShouldProcess($outputFile, "Out-File")) { + $yamlOutput | Out-File -FilePath $outputFile + } Write-StatusMessage "Configuration saved successfully!" -Verbosity Verbose } catch { diff --git a/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsInstall.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsInstall.Tests.ps1 new file mode 100644 index 0000000..76dcec0 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsInstall.Tests.ps1 @@ -0,0 +1,106 @@ +BeforeAll { + . (Join-Path $PSScriptRoot "Invoke-HomebrewComponentsInstall.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Install-HomebrewPackage.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Write-HomebrewCache.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") +} + +Describe "Invoke-HomebrewComponentsInstall" { + Context "When there are no packages in YAML" { + It "should process 0 packages" { + $YamlData = @{ devsetup = @{ dependencies = @{ homebrew = @() } } } + Mock Write-HomebrewCache { } + Mock Write-StatusMessage { } + Mock Install-HomebrewPackage { $false } + + Invoke-HomebrewComponentsInstall -YamlData $YamlData + Assert-MockCalled Write-HomebrewCache -Exactly 1 -Scope It # Initial call + Assert-MockCalled Install-HomebrewPackage -Exactly 0 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 2 -Scope It # Start and completion + } + } + + Context "When all packages install successfully" { + It "should increment package count and write cache for each" { + $YamlData = @{ devsetup = @{ dependencies = @{ homebrew = @(@{ name = "git" }, @{ name = "node" }) } } } + Mock Write-HomebrewCache { } + Mock Install-HomebrewPackage { $true } + Mock Write-StatusMessage { } + + Invoke-HomebrewComponentsInstall -YamlData $YamlData + Assert-MockCalled Write-HomebrewCache -Exactly 3 -Scope It # Initial + 2 successful + Assert-MockCalled Install-HomebrewPackage -Exactly 2 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 2 -Scope It + } + } + + Context "When some packages fail" { + It "should only increment for successful packages" { + $YamlData = @{ devsetup = @{ dependencies = @{ homebrew = @(@{ name = "git" }, @{ name = "node" }) } } } + Mock Write-HomebrewCache { } + Mock Install-HomebrewPackage { Param($PackageName) if ($PackageName -eq "git") { $true } else { $false } } + Mock Write-StatusMessage { } + + Invoke-HomebrewComponentsInstall -YamlData $YamlData + Assert-MockCalled Write-HomebrewCache -Exactly 2 -Scope It # Initial + 1 successful + Assert-MockCalled Install-HomebrewPackage -Exactly 2 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 2 -Scope It + } + } + + Context "When DryRun is specified" { + It "should pass WhatIf to Install-HomebrewPackage" { + $YamlData = @{ devsetup = @{ dependencies = @{ homebrew = @(@{ name = "git" }) } } } + Mock Write-HomebrewCache { } + Mock Install-HomebrewPackage { $true } + Mock Write-StatusMessage { } + + Invoke-HomebrewComponentsInstall -YamlData $YamlData -DryRun + Assert-MockCalled Install-HomebrewPackage -Exactly 1 -Scope It -ParameterFilter { $WhatIf -eq $true } + } + } + + Context "When package has minimumVersion" { + It "should pass MinimumVersion to Install-HomebrewPackage" { + $YamlData = [PSCustomObject]@{ devsetup = [PSCustomObject]@{ dependencies = @{ homebrew = @([PSCustomObject]@{ name = "node"; minimumVersion = "14.0" }) } } } + Mock Write-HomebrewCache { } + Mock Install-HomebrewPackage { $true } + Mock Write-StatusMessage { } + + Invoke-HomebrewComponentsInstall -YamlData $YamlData + Assert-MockCalled Install-HomebrewPackage -Exactly 1 -Scope It -ParameterFilter { $MinimumVersion -eq "14.0" } + } + } + + Context "Cross-platform compatibility" { + It "should work on Windows (no specific Homebrew logic)" { + $YamlData = @{ devsetup = @{ dependencies = @{ homebrew = @(@{ name = "git" }) } } } + Mock Write-HomebrewCache { } + Mock Install-HomebrewPackage { $true } + Mock Write-StatusMessage { } + + Invoke-HomebrewComponentsInstall -YamlData $YamlData + Assert-MockCalled Install-HomebrewPackage -Exactly 1 -Scope It + } + + It "should work on Linux" { + $YamlData = @{ devsetup = @{ dependencies = @{ homebrew = @(@{ name = "git" }) } } } + Mock Write-HomebrewCache { } + Mock Install-HomebrewPackage { $true } + Mock Write-StatusMessage { } + + Invoke-HomebrewComponentsInstall -YamlData $YamlData + Assert-MockCalled Install-HomebrewPackage -Exactly 1 -Scope It + } + + It "should work on macOS" { + $YamlData = @{ devsetup = @{ dependencies = @{ homebrew = @(@{ name = "git" }) } } } + Mock Write-HomebrewCache { } + Mock Install-HomebrewPackage { $true } + Mock Write-StatusMessage { } + + Invoke-HomebrewComponentsInstall -YamlData $YamlData + Assert-MockCalled Install-HomebrewPackage -Exactly 1 -Scope It + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsUninstall.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsUninstall.Tests.ps1 new file mode 100644 index 0000000..0c88a1b --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsUninstall.Tests.ps1 @@ -0,0 +1,94 @@ +BeforeAll { + . (Join-Path $PSScriptRoot "Invoke-HomebrewComponentsUninstall.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Uninstall-HomebrewPackage.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Write-HomebrewCache.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") +} + +Describe "Invoke-HomebrewComponentsUninstall" { + Context "When there are no packages in YAML" { + It "should process 0 packages" { + $YamlData = [PSCustomObject]@{ devsetup = [PSCustomObject]@{ dependencies = [PSCustomObject]@{ homebrew = @() } } } + Mock Write-HomebrewCache { } + Mock Write-StatusMessage { } + Mock Uninstall-HomebrewPackage { $false } + + Invoke-HomebrewComponentsUninstall -YamlData $YamlData + Assert-MockCalled Write-HomebrewCache -Exactly 1 -Scope It # Initial call + Assert-MockCalled Uninstall-HomebrewPackage -Exactly 0 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 2 -Scope It # Start and completion + } + } + + Context "When all packages uninstall successfully" { + It "should increment package count and write cache for each" { + $YamlData = [PSCustomObject]@{ devsetup = [PSCustomObject]@{ dependencies = [PSCustomObject]@{ homebrew = @([PSCustomObject]@{ name = "git" }, [PSCustomObject]@{ name = "node" }) } } } + Mock Write-HomebrewCache { } + Mock Uninstall-HomebrewPackage { $true } + Mock Write-StatusMessage { } + + Invoke-HomebrewComponentsUninstall -YamlData $YamlData + Assert-MockCalled Write-HomebrewCache -Exactly 3 -Scope It # Initial + 2 successful + Assert-MockCalled Uninstall-HomebrewPackage -Exactly 2 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 2 -Scope It + } + } + + Context "When some packages fail" { + It "should only increment for successful packages" { + $YamlData = [PSCustomObject]@{ devsetup = [PSCustomObject]@{ dependencies = [PSCustomObject]@{ homebrew = @([PSCustomObject]@{ name = "git" }, [PSCustomObject]@{ name = "node" }) } } } + Mock Write-HomebrewCache { } + Mock Uninstall-HomebrewPackage { Param($PackageName) if ($PackageName -eq "git") { $true } else { $false } } + Mock Write-StatusMessage { } + + Invoke-HomebrewComponentsUninstall -YamlData $YamlData + Assert-MockCalled Write-HomebrewCache -Exactly 2 -Scope It # Initial + 1 successful + Assert-MockCalled Uninstall-HomebrewPackage -Exactly 2 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 2 -Scope It + } + } + + Context "When DryRun is specified" { + It "should pass WhatIf to Uninstall-HomebrewPackage" { + $YamlData = [PSCustomObject]@{ devsetup = [PSCustomObject]@{ dependencies = [PSCustomObject]@{ homebrew = @([PSCustomObject]@{ name = "git" }) } } } + Mock Write-HomebrewCache { } + Mock Uninstall-HomebrewPackage { $true } + Mock Write-StatusMessage { } + + Invoke-HomebrewComponentsUninstall -YamlData $YamlData -DryRun + Assert-MockCalled Uninstall-HomebrewPackage -Exactly 1 -Scope It -ParameterFilter { $WhatIf -eq $true } + } + } + + Context "Cross-platform compatibility" { + It "should work on Windows" { + $YamlData = [PSCustomObject]@{ devsetup = [PSCustomObject]@{ dependencies = [PSCustomObject]@{ homebrew = @([PSCustomObject]@{ name = "git" }) } } } + Mock Write-HomebrewCache { } + Mock Uninstall-HomebrewPackage { $true } + Mock Write-StatusMessage { } + + Invoke-HomebrewComponentsUninstall -YamlData $YamlData + Assert-MockCalled Uninstall-HomebrewPackage -Exactly 1 -Scope It + } + + It "should work on Linux" { + $YamlData = [PSCustomObject]@{ devsetup = [PSCustomObject]@{ dependencies = [PSCustomObject]@{ homebrew = @([PSCustomObject]@{ name = "git" }) } } } + Mock Write-HomebrewCache { } + Mock Uninstall-HomebrewPackage { $true } + Mock Write-StatusMessage { } + + Invoke-HomebrewComponentsUninstall -YamlData $YamlData + Assert-MockCalled Uninstall-HomebrewPackage -Exactly 1 -Scope It + } + + It "should work on macOS" { + $YamlData = [PSCustomObject]@{ devsetup = [PSCustomObject]@{ dependencies = [PSCustomObject]@{ homebrew = @([PSCustomObject]@{ name = "git" }) } } } + Mock Write-HomebrewCache { } + Mock Uninstall-HomebrewPackage { $true } + Mock Write-StatusMessage { } + + Invoke-HomebrewComponentsUninstall -YamlData $YamlData + Assert-MockCalled Uninstall-HomebrewPackage -Exactly 1 -Scope It + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Read-HomebrewCache.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Read-HomebrewCache.Tests.ps1 new file mode 100644 index 0000000..dd68137 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Read-HomebrewCache.Tests.ps1 @@ -0,0 +1,78 @@ +BeforeAll { + . (Join-Path $PSScriptRoot "Read-HomebrewCache.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Get-HomebrewCacheFile.ps1") +} + +Describe "Read-HomebrewCache" { + Context "When cache file exists" { + It "should read and return the cache data as hashtable" { + $mockCachePath = Join-Path $TestDrive "homebrew.cache" + Mock Get-HomebrewCacheFile { $mockCachePath } + Mock Test-Path { $true } + Mock Get-Content { '{"package1": "version1", "package2": "version2"}' } + Mock ConvertFrom-Json { @{ package1 = "version1"; package2 = "version2" } } + + $result = Read-HomebrewCache + $result | Should -BeOfType [hashtable] + $result["package1"] | Should -Be "version1" + $result["package2"] | Should -Be "version2" + Assert-MockCalled Get-HomebrewCacheFile -Exactly 1 -Scope It + Assert-MockCalled Test-Path -Exactly 1 -Scope It + Assert-MockCalled Get-Content -Exactly 1 -Scope It + Assert-MockCalled ConvertFrom-Json -Exactly 1 -Scope It + } + } + + Context "When cache file does not exist" { + It "should return an empty hashtable" { + $mockCachePath = Join-Path $TestDrive "homebrew.cache" + Mock Get-HomebrewCacheFile { $mockCachePath } + Mock Test-Path { $false } + Mock Get-Content { } + Mock ConvertFrom-Json { } + + $result = Read-HomebrewCache + $result | Should -BeOfType [hashtable] + $result.Count | Should -Be 0 + Assert-MockCalled Get-HomebrewCacheFile -Exactly 1 -Scope It + Assert-MockCalled Test-Path -Exactly 1 -Scope It + Assert-MockCalled Get-Content -Exactly 0 -Scope It + Assert-MockCalled ConvertFrom-Json -Exactly 0 -Scope It + } + } + + Context "Cross-platform compatibility" { + It "should work on Windows" { + $mockCachePath = Join-Path $TestDrive "homebrew.cache" + Mock Get-HomebrewCacheFile { $mockCachePath } + Mock Test-Path { $true } + Mock Get-Content { '{"git": "2.30.1"}' } + Mock ConvertFrom-Json { @{ git = "2.30.1" } } + + $result = Read-HomebrewCache + $result["git"] | Should -Be "2.30.1" + } + + It "should work on Linux" { + $mockCachePath = Join-Path $TestDrive "homebrew.cache" + Mock Get-HomebrewCacheFile { $mockCachePath } + Mock Test-Path { $true } + Mock Get-Content { '{"git": "2.30.1"}' } + Mock ConvertFrom-Json { @{ git = "2.30.1" } } + + $result = Read-HomebrewCache + $result["git"] | Should -Be "2.30.1" + } + + It "should work on macOS" { + $mockCachePath = Join-Path $TestDrive "homebrew.cache" + Mock Get-HomebrewCacheFile { $mockCachePath } + Mock Test-Path { $true } + Mock Get-Content { '{"git": "2.30.1"}' } + Mock ConvertFrom-Json { @{ git = "2.30.1" } } + + $result = Read-HomebrewCache + $result["git"] | Should -Be "2.30.1" + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Test-HomebrewInstalled.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Test-HomebrewInstalled.Tests.ps1 new file mode 100644 index 0000000..ccd3e94 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Test-HomebrewInstalled.Tests.ps1 @@ -0,0 +1,114 @@ +BeforeAll { + . (Join-Path $PSScriptRoot "Test-HomebrewInstalled.ps1") +} + +Describe "Test-HomebrewInstalled" { + Context "When brew is found in PATH" { + It "should return true" { + Mock Get-Command { [PSCustomObject]@{ Path = "/usr/local/bin/brew" } } + Mock Test-Path { $false } # Not needed since Get-Command succeeds + + $result = Test-HomebrewInstalled + $result | Should -Be $true + Assert-MockCalled Get-Command -Exactly 1 -Scope It + Assert-MockCalled Test-Path -Exactly 0 -Scope It # Test-Path not called if Get-Command succeeds + } + } + + Context "When brew is not in PATH but found in test paths" { + It "should return true if found in first test path" { + Mock Get-Command { $null } + Mock Test-Path { + Param($Path) + switch ($Path) { + "/usr/local/bin/brew" { $true } + default { $false } + } + } + + $result = Test-HomebrewInstalled + $result | Should -Be $true + Assert-MockCalled Get-Command -Exactly 1 -Scope It + Assert-MockCalled Test-Path -Exactly 1 -Scope It -ParameterFilter { $Path -eq "/usr/local/bin/brew" } + } + + It "should return true if found in second test path" { + Mock Get-Command { $null } + Mock Test-Path { + Param($Path) + switch ($Path) { + "/usr/local/bin/brew" { $false } + "/opt/homebrew/bin/brew" { $true } + default { $false } + } + } + + $result = Test-HomebrewInstalled + $result | Should -Be $true + Assert-MockCalled Get-Command -Exactly 1 -Scope It + Assert-MockCalled Test-Path -Exactly 2 -Scope It # Checks first two paths + } + + It "should return true if found in third test path" { + Mock Get-Command { $null } + Mock Test-Path { + Param($Path) + switch ($Path) { + "/usr/local/bin/brew" { $false } + "/opt/homebrew/bin/brew" { $false } + "/home/linuxbrew/.linuxbrew/bin/brew" { $true } + default { $false } + } + } + + $result = Test-HomebrewInstalled + $result | Should -Be $true + Assert-MockCalled Get-Command -Exactly 1 -Scope It + Assert-MockCalled Test-Path -Exactly 3 -Scope It # Checks all three paths + } + } + + Context "When brew is not found anywhere" { + It "should return false" { + Mock Get-Command { $null } + Mock Test-Path { $false } + + $result = Test-HomebrewInstalled + $result | Should -Be $false + Assert-MockCalled Get-Command -Exactly 1 -Scope It + Assert-MockCalled Test-Path -Exactly 3 -Scope It # Checks all three paths + } + } + + Context "Cross-platform compatibility" { + It "should return false on Windows (where Homebrew is unlikely)" { + Mock Get-Command { $null } + Mock Test-Path { $false } + + $result = Test-HomebrewInstalled + $result | Should -Be $false + } + + It "should work on Linux" { + Mock Get-Command { $null } + Mock Test-Path { + Param($Path) + $Path -eq "/home/linuxbrew/.linuxbrew/bin/brew" + } + + $result = Test-HomebrewInstalled + $result | Should -Be $true + } + + It "should work on macOS" { + Mock Get-Command { $null } + Mock Test-Path { + Param($Path) + $Path -eq "/opt/homebrew/bin/brew" + } + + $result = Test-HomebrewInstalled + $result | Should -Be $true + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Test-HomebewInstalled.ps1 b/DevSetup/Private/Providers/Homebrew/Test-HomebrewInstalled.ps1 similarity index 100% rename from DevSetup/Private/Providers/Homebrew/Test-HomebewInstalled.ps1 rename to DevSetup/Private/Providers/Homebrew/Test-HomebrewInstalled.ps1 diff --git a/DevSetup/Private/Providers/Homebrew/Test-HomebrewPackageInstalled.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Test-HomebrewPackageInstalled.Tests.ps1 new file mode 100644 index 0000000..b8977c9 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Test-HomebrewPackageInstalled.Tests.ps1 @@ -0,0 +1,102 @@ +BeforeAll { + . (Join-Path $PSScriptRoot "Test-HomebrewPackageInstalled.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Test-HomebrewInstalled.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Read-HomebrewCache.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Enums\InstalledState.ps1") +} + +Describe "Test-HomebrewPackageInstalled" { + Context "When Homebrew is not installed" { + It "should return NotInstalled" { + Mock Test-HomebrewInstalled { $false } + Mock Write-StatusMessage { } + + $result = Test-HomebrewPackageInstalled -PackageName "git" + $result | Should -Be ([InstalledState]::NotInstalled) + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 1 -Scope It + } + } + + Context "When package is not in cache" { + It "should return NotInstalled" { + Mock Test-HomebrewInstalled { $true } + Mock Read-HomebrewCache { @{ "node" = "14.17.0" } } # Package not in cache + Mock Write-StatusMessage { } + + $result = Test-HomebrewPackageInstalled -PackageName "git" + $result | Should -Be ([InstalledState]::NotInstalled) + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Read-HomebrewCache -Exactly 1 -Scope It + } + } + + Context "When package is in cache and no minimum version specified" { + It "should return full installed status" { + Mock Test-HomebrewInstalled { $true } + Mock Read-HomebrewCache { @{ "git" = "2.30.1" } } + Mock Write-StatusMessage { } + + $result = Test-HomebrewPackageInstalled -PackageName "git" + $result | Should -Be ([InstalledState]::Installed + [InstalledState]::MinimumVersionMet + [InstalledState]::RequiredVersionMet + [InstalledState]::GlobalVersionMet) + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Read-HomebrewCache -Exactly 1 -Scope It + } + } + + Context "When package is in cache and minimum version is met" { + It "should return full installed status" { + Mock Test-HomebrewInstalled { $true } + Mock Read-HomebrewCache { @{ "git" = "2.30.1" } } + Mock Write-StatusMessage { } + + $result = Test-HomebrewPackageInstalled -PackageName "git" -MinimumVersion "2.0.0" + $result | Should -Be ([InstalledState]::Installed + [InstalledState]::MinimumVersionMet + [InstalledState]::RequiredVersionMet + [InstalledState]::GlobalVersionMet) + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Read-HomebrewCache -Exactly 1 -Scope It + } + } + + Context "When package is in cache but minimum version is not met" { + It "should return Installed but not version flags" { + Mock Test-HomebrewInstalled { $true } + Mock Read-HomebrewCache { @{ "git" = "1.0.0" } } + Mock Write-StatusMessage { } + + $result = Test-HomebrewPackageInstalled -PackageName "git" -MinimumVersion "2.0.0" + $result | Should -Be ([InstalledState]::Installed) # Only Installed, no version flags + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Read-HomebrewCache -Exactly 1 -Scope It + } + } + + Context "Cross-platform compatibility" { + It "should work on Windows" { + Mock Test-HomebrewInstalled { $true } + Mock Read-HomebrewCache { @{ "git" = "2.30.1" } } + Mock Write-StatusMessage { } + + $result = Test-HomebrewPackageInstalled -PackageName "git" + $result | Should -Be ([InstalledState]::Installed + [InstalledState]::MinimumVersionMet + [InstalledState]::RequiredVersionMet + [InstalledState]::GlobalVersionMet) + } + + It "should work on Linux" { + Mock Test-HomebrewInstalled { $true } + Mock Read-HomebrewCache { @{ "git" = "2.30.1" } } + Mock Write-StatusMessage { } + + $result = Test-HomebrewPackageInstalled -PackageName "git" + $result | Should -Be ([InstalledState]::Installed + [InstalledState]::MinimumVersionMet + [InstalledState]::RequiredVersionMet + [InstalledState]::GlobalVersionMet) + } + + It "should work on macOS" { + Mock Test-HomebrewInstalled { $true } + Mock Read-HomebrewCache { @{ "git" = "2.30.1" } } + Mock Write-StatusMessage { } + + $result = Test-HomebrewPackageInstalled -PackageName "git" + $result | Should -Be ([InstalledState]::Installed + [InstalledState]::MinimumVersionMet + [InstalledState]::RequiredVersionMet + [InstalledState]::GlobalVersionMet) + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Test-HomebrewPackageInstalled.ps1 b/DevSetup/Private/Providers/Homebrew/Test-HomebrewPackageInstalled.ps1 index 396447b..abfafd5 100644 --- a/DevSetup/Private/Providers/Homebrew/Test-HomebrewPackageInstalled.ps1 +++ b/DevSetup/Private/Providers/Homebrew/Test-HomebrewPackageInstalled.ps1 @@ -8,7 +8,7 @@ Function Test-HomebrewPackageInstalled { [Version]$MinimumVersion ) - $PackageStatus = [InstalledState]::NotInstalled + [InstalledState]$PackageStatus = [InstalledState]::NotInstalled if(-not (Test-HomebrewInstalled)) { Write-StatusMessage "Homebrew is not installed" -Verbosity Verbose @@ -25,13 +25,14 @@ Function Test-HomebrewPackageInstalled { $InstalledVersion = [Version]::Parse($InstalledPackages[$PackageName]) if ($InstalledVersion -ge $MinimumVersion) { $PackageStatus += [InstalledState]::MinimumVersionMet + $PackageStatus += [InstalledState]::RequiredVersionMet + $PackageStatus += [InstalledState]::GlobalVersionMet } } else { $PackageStatus += [InstalledState]::MinimumVersionMet + $PackageStatus += [InstalledState]::RequiredVersionMet + $PackageStatus += [InstalledState]::GlobalVersionMet } - - $PackageStatus += [InstalledState]::RequiredVersionMet - $PackageStatus += [InstalledState]::GlobalVersionMet } return $PackageStatus diff --git a/DevSetup/Private/Providers/Homebrew/Uninstall-HomebrewPackage.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Uninstall-HomebrewPackage.Tests.ps1 new file mode 100644 index 0000000..ce1aa53 --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Uninstall-HomebrewPackage.Tests.ps1 @@ -0,0 +1,141 @@ +BeforeAll { + . (Join-Path $PSScriptRoot "Uninstall-HomebrewPackage.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Test-HasSudoAccess.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Find-Homebrew.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Test-HomebrewPackageInstalled.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Invoke-ExternalCommand.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") +} + +Describe "Uninstall-HomebrewPackage" { + Context "When sudo access is not available" { + It "should return false" { + Mock Test-HasSudoAccess { $false } + Mock Write-StatusMessage { } + + $result = Uninstall-HomebrewPackage -PackageName "git" + $result | Should -Be $false + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 3 -Scope It # Status, sudo message, failed + } + } + + Context "When Homebrew is not installed" { + It "should return false" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { $null } + Mock Write-StatusMessage { } + + $result = Uninstall-HomebrewPackage -PackageName "git" + $result | Should -Be $false + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 3 -Scope It # Status, homebrew message, failed + } + } + + Context "When package is not installed" { + It "should return true" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Test-HomebrewPackageInstalled { [InstalledState]::NotInstalled } + Mock Write-StatusMessage { } + Mock Invoke-ExternalCommand { $false } + + $result = Uninstall-HomebrewPackage -PackageName "git" + $result | Should -Be $true + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 1 -Scope It + Assert-MockCalled Test-HomebrewPackageInstalled -Exactly 1 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 0 -Scope It # No uninstall needed + } + } + + Context "When uninstallation succeeds" { + It "should return true" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Test-HomebrewPackageInstalled { [InstalledState]::Installed } + Mock Invoke-ExternalCommand { $true } + Mock Write-StatusMessage { } + $global:LASTEXITCODE = 0 + + $result = Uninstall-HomebrewPackage -PackageName "git" + $result | Should -Be $true + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 2 -Scope It + Assert-MockCalled Test-HomebrewPackageInstalled -Exactly 1 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + } + } + + Context "When uninstallation fails" { + It "should return false" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Test-HomebrewPackageInstalled { [InstalledState]::Installed } + Mock Invoke-ExternalCommand { $false } + Mock Write-StatusMessage { } + $global:LASTEXITCODE = 1 + + $result = Uninstall-HomebrewPackage -PackageName "git" + $result | Should -Be $false + Assert-MockCalled Test-HasSudoAccess -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 2 -Scope It + Assert-MockCalled Test-HomebrewPackageInstalled -Exactly 1 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + } + } + + Context "When -WhatIf is used" { + It "should not perform the uninstallation" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Test-HomebrewPackageInstalled { [InstalledState]::Installed } + Mock Write-StatusMessage { } + Mock Invoke-ExternalCommand { $false } + + $result = Uninstall-HomebrewPackage -PackageName "git" -WhatIf + $result | Should -Be $null # ShouldProcess returns null when WhatIf is used + Assert-MockCalled Test-HasSudoAccess -Exactly 0 -Scope It # Should not proceed + Assert-MockCalled Find-Homebrew -Exactly 0 -Scope It + Assert-MockCalled Test-HomebrewPackageInstalled -Exactly 0 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 0 -Scope It + } + } + + Context "Cross-platform compatibility" { + It "should handle Windows (where Homebrew is unlikely)" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { $null } + Mock Write-StatusMessage { } + + $result = Uninstall-HomebrewPackage -PackageName "git" + $result | Should -Be $false + } + + It "should work on Linux" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { "/home/linuxbrew/.linuxbrew/bin/brew" } + Mock Test-HomebrewPackageInstalled { [InstalledState]::Installed } + Mock Invoke-ExternalCommand { $true } + Mock Write-StatusMessage { } + $global:LASTEXITCODE = 0 + + $result = Uninstall-HomebrewPackage -PackageName "git" + $result | Should -Be $true + } + + It "should work on macOS" { + Mock Test-HasSudoAccess { $true } + Mock Find-Homebrew { "/opt/homebrew/bin/brew" } + Mock Test-HomebrewPackageInstalled { [InstalledState]::Installed } + Mock Invoke-ExternalCommand { $true } + Mock Write-StatusMessage { } + $global:LASTEXITCODE = 0 + + $result = Uninstall-HomebrewPackage -PackageName "git" + $result | Should -Be $true + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Write-HomebrewCache.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Write-HomebrewCache.Tests.ps1 new file mode 100644 index 0000000..8e1f04f --- /dev/null +++ b/DevSetup/Private/Providers/Homebrew/Write-HomebrewCache.Tests.ps1 @@ -0,0 +1,122 @@ +BeforeAll { + . (Join-Path $PSScriptRoot "Write-HomebrewCache.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Get-HomebrewCacheFile.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Test-HomebrewInstalled.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Find-Homebrew.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Invoke-ExternalCommand.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") +} + +Describe "Write-HomebrewCache" { + Context "When Homebrew is not installed" { + It "should not write anything" { + Mock Get-HomebrewCacheFile { Join-Path $TestDrive "homebrew.cache" } + Mock Test-HomebrewInstalled { $false } + Mock Set-Content { } + Mock Find-Homebrew { $null } + Mock Invoke-ExternalCommand { $null } + + Write-HomebrewCache + Assert-MockCalled Get-HomebrewCacheFile -Exactly 1 -Scope It + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 0 -Scope It # Not called if not installed + Assert-MockCalled Invoke-ExternalCommand -Exactly 0 -Scope It + Assert-MockCalled Set-Content -Exactly 0 -Scope It # No data to write + } + } + + Context "When Homebrew is installed and write succeeds" { + It "should parse output and write JSON to cache file" { + Mock Get-HomebrewCacheFile { Join-Path $TestDrive "homebrew.cache" } + Mock Test-HomebrewInstalled { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Invoke-ExternalCommand { "git 2.30.1`nnode 14.17.0" } + Mock Set-Content { } + Mock Write-StatusMessage { } + + Write-HomebrewCache + Assert-MockCalled Get-HomebrewCacheFile -Exactly 1 -Scope It + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 1 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + Assert-MockCalled Set-Content -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 0 -Scope It # No errors + } + } + + Context "When Invoke-ExternalCommand throws an exception" { + It "should handle the exception and write error message" { + Mock Get-HomebrewCacheFile { Join-Path $TestDrive "homebrew.cache" } + Mock Test-HomebrewInstalled { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Invoke-ExternalCommand { throw "Command failed" } + Mock Set-Content { } + Mock Write-StatusMessage { } + + Write-HomebrewCache + Assert-MockCalled Get-HomebrewCacheFile -Exactly 1 -Scope It + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 1 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + Assert-MockCalled Set-Content -Exactly 0 -Scope It # Exception prevents writing + Assert-MockCalled Write-StatusMessage -Exactly 2 -Scope It # Error and stack trace + } + } + + Context "When Set-Content throws an exception" { + It "should handle the exception and write error message" { + Mock Get-HomebrewCacheFile { Join-Path $TestDrive "homebrew.cache" } + Mock Test-HomebrewInstalled { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Invoke-ExternalCommand { "git 2.30.1" } + Mock Set-Content { throw "Write failed" } + Mock Write-StatusMessage { } + + Write-HomebrewCache + Assert-MockCalled Get-HomebrewCacheFile -Exactly 1 -Scope It + Assert-MockCalled Test-HomebrewInstalled -Exactly 1 -Scope It + Assert-MockCalled Find-Homebrew -Exactly 1 -Scope It + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + Assert-MockCalled Set-Content -Exactly 1 -Scope It + Assert-MockCalled Write-StatusMessage -Exactly 2 -Scope It # Error and stack trace + } + } + + Context "Cross-platform compatibility" { + It "should work on Windows" { + Mock Get-HomebrewCacheFile { Join-Path $TestDrive "homebrew.cache" } + Mock Test-HomebrewInstalled { $true } + Mock Find-Homebrew { "/usr/local/bin/brew" } + Mock Invoke-ExternalCommand { "git 2.30.1" } + Mock Set-Content { } + Mock Write-StatusMessage { } + + Write-HomebrewCache + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + } + + It "should work on Linux" { + Mock Get-HomebrewCacheFile { Join-Path $TestDrive "homebrew.cache" } + Mock Test-HomebrewInstalled { $true } + Mock Find-Homebrew { "/home/linuxbrew/.linuxbrew/bin/brew" } + Mock Invoke-ExternalCommand { "git 2.30.1" } + Mock Set-Content { } + Mock Write-StatusMessage { } + + Write-HomebrewCache + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + } + + It "should work on macOS" { + Mock Get-HomebrewCacheFile { Join-Path $TestDrive "homebrew.cache" } + Mock Test-HomebrewInstalled { $true } + Mock Find-Homebrew { "/opt/homebrew/bin/brew" } + Mock Invoke-ExternalCommand { "git 2.30.1" } + Mock Set-Content { } + Mock Write-StatusMessage { } + + Write-HomebrewCache + Assert-MockCalled Invoke-ExternalCommand -Exactly 1 -Scope It + } + } +} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Write-HomebrewCache.ps1 b/DevSetup/Private/Providers/Homebrew/Write-HomebrewCache.ps1 index ea6b2a3..9f1852f 100644 --- a/DevSetup/Private/Providers/Homebrew/Write-HomebrewCache.ps1 +++ b/DevSetup/Private/Providers/Homebrew/Write-HomebrewCache.ps1 @@ -1,5 +1,6 @@ Function Write-HomebrewCache { [CmdletBinding()] + [OutputType([void])] Param() $cacheFile = Get-HomebrewCacheFile @@ -20,9 +21,8 @@ Function Write-HomebrewCache { $cacheData[$packageName] = $packageVersion } } | Out-Null + $cacheData | ConvertTo-Json | Set-Content -Path $cacheFile } - - $cacheData | ConvertTo-Json | Set-Content -Path $cacheFile } catch { Write-StatusMessage "Failed to write Homebrew cache: $_" -Verbosity Error Write-StatusMessage $_.ScriptStackTrace -Verbosity Error From 3c064a14ca72fc6ed1ba4034b071deb643a84eda Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 05:49:06 -0500 Subject: [PATCH 03/18] Fixing some linting issues. --- .github/workflows/run-lint.yml | 28 +++++++ .github/workflows/run-unit-tests.yml | 78 +++++++++++++++++++ .../Commands/Export-DevSetupEnv.Tests.ps1 | 12 +-- .../Private/Commands/Export-DevSetupEnv.ps1 | 20 ++--- .../Private/Commands/Initialize-DevSetup.ps1 | 23 +++--- .../Private/Commands/Install-DevSetupEnv.ps1 | 14 ++-- .../Private/Commands/Show-DevSetupEnvList.ps1 | 20 ++--- .../Export-InstalledChocolateyPackages.ps1 | 4 +- .../Get-ChocolateyPackageDependencies.ps1 | 4 +- .../Chocolatey/Install-ChocolateyPackages.ps1 | 4 +- .../Uninstall-ChocolateyPackages.ps1 | 4 +- .../Core/Install-CoreDependencies.ps1 | 4 +- .../Export-InstalledPowershellModules.ps1 | 4 +- .../Powershell/Install-PowershellModules.ps1 | 4 +- .../Uninstall-PowershellModules.ps1 | 4 +- .../Scoop/Export-InstalledScoopPackages.ps1 | 4 +- .../Scoop/Install-ScoopComponents.ps1 | 4 +- .../Scoop/Uninstall-ScoopComponents.ps1 | 4 +- .../Private/Utils/Find-GitRepositories.ps1 | 4 +- .../Private/Utils/Initialize-DevSetupEnvs.ps1 | 2 +- .../Private/Utils/Optimize-DevSetupEnvs.ps1 | 2 +- Remove-File.ps1 | 11 --- runSecurity.ps1 | 8 ++ 23 files changed, 184 insertions(+), 82 deletions(-) create mode 100644 .github/workflows/run-lint.yml create mode 100644 .github/workflows/run-unit-tests.yml delete mode 100644 Remove-File.ps1 create mode 100644 runSecurity.ps1 diff --git a/.github/workflows/run-lint.yml b/.github/workflows/run-lint.yml new file mode 100644 index 0000000..c96cfce --- /dev/null +++ b/.github/workflows/run-lint.yml @@ -0,0 +1,28 @@ +name: Run PSScriptAnalyzer Tests +on: + pull_request: + branches: + - develop + +jobs: + psscriptanalyzer: + name: PSScriptAnalyzer + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + checks: write + pull-requests: write + security-events: write + steps: + - uses: actions/checkout@v4 + - name: Lint with PSScriptAnalyzer + shell: pwsh + run: | + .\runSecurity.ps1 + + - name: Upload SARIF results file + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml new file mode 100644 index 0000000..ef45772 --- /dev/null +++ b/.github/workflows/run-unit-tests.yml @@ -0,0 +1,78 @@ +name: Run Pester Tests +on: + pull_request: + branches: + - develop + +jobs: + pester-test-linux: + name: Pester test (On Linux) + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + permissions: + contents: read + issues: read + checks: write + pull-requests: write + steps: + - name: Check out repository code + uses: actions/checkout@v4 + - name: Execute runTests.ps1 + shell: pwsh + run: | + .\runTests.ps1 + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action/linux@v2 + if: (!cancelled()) + with: + check_name: Pester test (On Linux) Results + files: testResults.xml + + pester-test-windows: + name: Pester test (On Windows) + runs-on: windows-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + permissions: + contents: read + issues: read + checks: write + pull-requests: write + steps: + - name: Check out repository code + uses: actions/checkout@v4 + - name: Execute runTests.ps1 + shell: pwsh + run: | + .\runTests.ps1 + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action/windows@v2 + if: (!cancelled()) + with: + check_name: Pester test (On Windows) Results + files: testResults.xml + + pester-test-macos: + name: Pester test (On macOS) + runs-on: macos-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + permissions: + contents: read + issues: read + checks: write + pull-requests: write + steps: + - name: Check out repository code + uses: actions/checkout@v4 + - name: Execute runTests.ps1 + shell: pwsh + run: | + .\runTests.ps1 + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action/macos@v2 + if: (!cancelled()) + with: + check_name: Pester test (On macOS) Results + files: testResults.xml \ No newline at end of file diff --git a/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1 b/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1 index 3cbd499..4d98a6b 100644 --- a/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1 +++ b/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1 @@ -4,6 +4,7 @@ BeforeAll { . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupLocalEnvPath.ps1 . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupCommunityEnvPath.ps1 . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Write-NewConfig.ps1 + . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1 if ($PSVersionTable.PSVersion.Major -eq 5) { Mock Get-DevSetupEnvPath { "$TestDrive\DevSetup\DevSetupEnvs" } Mock Get-DevSetupLocalEnvPath { "$TestDrive\DevSetup\DevSetupEnvs\local" } @@ -28,6 +29,7 @@ BeforeAll { Mock Write-NewConfig { param($OutFile) $OutFile } Mock Write-Host { } Mock Write-Error { } + Mock Write-StatusMessage { } } Describe "Export-DevSetupEnv" { @@ -44,7 +46,7 @@ Describe "Export-DevSetupEnv" { } $result | Should -Be $expectedPath Assert-MockCalled Write-NewConfig -Exactly 1 -Scope It -ParameterFilter { $OutFile -eq $expectedPath } - Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "exported to" -and $ForegroundColor -eq "Green" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "exported to" -and $ForegroundColor -eq "Green" } } } @@ -62,7 +64,7 @@ Describe "Export-DevSetupEnv" { } $result | Should -Be $expectedPath Assert-MockCalled Write-NewConfig -Exactly 1 -Scope It -ParameterFilter { $OutFile -eq $expectedPath } - Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "exported to" -and $ForegroundColor -eq "Green" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "exported to" -and $ForegroundColor -eq "Green" } } } @@ -77,7 +79,7 @@ Describe "Export-DevSetupEnv" { $expectedPath = "$TestDrive/Users/TestUser/DevSetup/DevSetupEnvs/local/DataScienceEnvironment.devsetup" } $result | Should -Be $expectedPath - Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "sanitized" -and $ForegroundColor -eq "Yellow" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "sanitized" -and $ForegroundColor -eq "Yellow" } } } @@ -94,7 +96,7 @@ Describe "Export-DevSetupEnv" { $expectedPath = "$TestDrive/MyCustomPath/MyEnv.devsetup" } $result | Should -Be $expectedPath - Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "sanitized" -and $ForegroundColor -eq "Yellow" } + Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -match "sanitized" -and $ForegroundColor -eq "Yellow" } } } @@ -103,7 +105,7 @@ Describe "Export-DevSetupEnv" { Mock Write-NewConfig { param($OutFile) $null } $result = Export-DevSetupEnv -Name "FailEnv" $result | Should -Be $null - Assert-MockCalled Write-Error -Exactly 1 -Scope It -ParameterFilter { $Message -match "Failed to create configuration file" } + Assert-MockCalled Write-StatusMessage -Exactly 1 -Scope It -ParameterFilter { $Message -match "Failed to create configuration file" -and $Verbosity -eq "Error" } } } } diff --git a/DevSetup/Private/Commands/Export-DevSetupEnv.ps1 b/DevSetup/Private/Commands/Export-DevSetupEnv.ps1 index 3de017d..286ca53 100644 --- a/DevSetup/Private/Commands/Export-DevSetupEnv.ps1 +++ b/DevSetup/Private/Commands/Export-DevSetupEnv.ps1 @@ -21,7 +21,7 @@ .EXAMPLE Export-DevSetupEnv -Name "MyCurrentSetup" - + Exports the current system state to a configuration file named "MyCurrentSetup.yaml". .EXAMPLE @@ -31,12 +31,12 @@ } else { Write-Host "Export failed" } - + Demonstrates capturing the return value to verify export success. .EXAMPLE Export-DevSetupEnv -Name "Data Science Environment!" - + The exclamation mark will be removed, resulting in "DataScienceEnvironment.yaml". A warning message will indicate the sanitization that occurred. @@ -80,7 +80,7 @@ Function Export-DevSetupEnv { } $SanitizedEnvName = $Name -replace '[^a-zA-Z0-9\-\.]', '' if ($SanitizedEnvName -ne $Name) { - Write-Host "EnvName sanitized from '$Name' to '$SanitizedEnvName' (removed non-alphanumeric characters)" -ForegroundColor Yellow + Write-StatusMessage "EnvName sanitized from '$Name' to '$SanitizedEnvName' (removed non-alphanumeric characters)" -ForegroundColor Yellow } $BasePath = Join-Path -Path (Get-DevSetupEnvPath) -ChildPath $Provider @@ -98,25 +98,25 @@ Function Export-DevSetupEnv { if($Name -notlike "*.devsetup") { $Name = "$Name.devsetup" } - + $SanitizedEnvName = $Name -replace '[^a-zA-Z0-9\-\.]', '' if ($SanitizedEnvName -ne $Name) { - Write-Host "EnvName sanitized from '$Name' to '$SanitizedEnvName' (removed non-alphanumeric characters)" -ForegroundColor Yellow + Write-StatusMessage "EnvName sanitized from '$Name' to '$SanitizedEnvName' (removed non-alphanumeric characters)" -ForegroundColor Yellow } $OutFile = Join-Path -Path $BasePath -ChildPath $SanitizedEnvName } if(-not $OutFile) { - Write-Error "Failed to determine output file path" + Write-StatusMessage "Failed to determine output file path" -Verbosity Error return $null } $config = Write-NewConfig -OutFile $OutFile -DryRun:$DryRun if (-not $config) { - Write-Error "Failed to create configuration file" + Write-StatusMessage "Failed to create configuration file" -Verbosity Error return $null } - Write-Host "Configuration file exported to: $OutFile" -ForegroundColor Green - return $OutFile + Write-StatusMessage "Configuration file exported to: $OutFile" -ForegroundColor Green + return $OutFile } \ No newline at end of file diff --git a/DevSetup/Private/Commands/Initialize-DevSetup.ps1 b/DevSetup/Private/Commands/Initialize-DevSetup.ps1 index e4c8cdb..2ce6751 100644 --- a/DevSetup/Private/Commands/Initialize-DevSetup.ps1 +++ b/DevSetup/Private/Commands/Initialize-DevSetup.ps1 @@ -15,7 +15,7 @@ .EXAMPLE Initialize-DevSetup - + Initializes the complete DevSetup environment with default settings. .EXAMPLE @@ -26,7 +26,7 @@ Write-Host "DevSetup initialization failed" # Handle initialization failure } - + Demonstrates conditional logic based on initialization success. .EXAMPLE @@ -34,7 +34,7 @@ if ($setupReady) { Use-DevSetup -List } - + Shows using the function result to proceed with DevSetup operations. .NOTES @@ -62,18 +62,18 @@ #> Function Initialize-DevSetup { - try { + try { # Install core dependencies first Write-StatusMessage "- Installing core dependencies..." -ForegroundColor Cyan if (-not (Install-CoreDependencies)) { Write-StatusMessage "Failed to install core dependencies" -Verbosity Error return } - Write-StatusMessage "- Core dependencies installed successfully" -ForegroundColor Green - + Write-StatusMessage "- Core dependencies installed successfully" -ForegroundColor Green + # Define .devsetup folder path $devSetupPath = Get-DevSetupPath - + # Check if .devsetup folder exists if (-not (Test-Path -Path $devSetupPath)) { #Write-Host "Creating .devsetup directory at: $devSetupPath" -ForegroundColor Cyan @@ -83,8 +83,7 @@ Function Initialize-DevSetup { Write-StatusMessage ".devsetup directory already exists at: $devSetupPath" -Verbosity Verbose } - Write-Host "" - Write-StatusMessage "- Installing community environments..." -ForegroundColor Cyan + Write-StatusMessage "`n- Installing community environments..." -ForegroundColor Cyan # Initialize DevSetup environments path $envSetupPath = Initialize-DevSetupEnvs if (-not $envSetupPath) { @@ -94,8 +93,7 @@ Function Initialize-DevSetup { Write-StatusMessage "- Community environments installed successfully" -ForegroundColor Green } - Write-Host "" - Write-StatusMessage "Path Information: " -ForegroundColor Yellow + Write-StatusMessage "`nPath Information: " -ForegroundColor Yellow Write-StatusMessage "- DevSetup:" -ForegroundColor Cyan Write-StatusMessage " - $devSetupPath" -ForegroundColor Gray Write-StatusMessage "- Local Environments: " -ForegroundColor Cyan @@ -105,8 +103,7 @@ Function Initialize-DevSetup { Write-StatusMessage "- Logs:" -ForegroundColor Cyan Write-StatusMessage " - $(Get-DevSetupLogPath)" -ForegroundColor Gray Write-StatusMessage "- Cache:" -ForegroundColor Cyan - Write-StatusMessage " - $(Get-DevSetupCachePath)" -ForegroundColor Gray - Write-Host "" + Write-StatusMessage " - $(Get-DevSetupCachePath)`n" -ForegroundColor Gray # Return the path for use by other functions return $true diff --git a/DevSetup/Private/Commands/Install-DevSetupEnv.ps1 b/DevSetup/Private/Commands/Install-DevSetupEnv.ps1 index c9cd4f2..d281f5b 100644 --- a/DevSetup/Private/Commands/Install-DevSetupEnv.ps1 +++ b/DevSetup/Private/Commands/Install-DevSetupEnv.ps1 @@ -18,17 +18,17 @@ .EXAMPLE Install-DevSetupEnv -Name "development" - + Installs the development environment from the "development.yaml" configuration file. .EXAMPLE Install-DevSetupEnv "web-dev" - + Installs the web development environment using positional parameter syntax. .EXAMPLE Install-DevSetupEnv -Name "my-environment" - + Demonstrates the PSCustomObject structure that would be parsed from the YAML file. .NOTES @@ -94,7 +94,7 @@ Function Install-DevSetupEnv { Write-StatusMessage "- $YamlFile" -Indent 2 -ForegroundColor Gray if((Test-Path -Path $YamlFile)) { Write-Warning "File $YamlFile already exists" - do { + do { if(($sAnswer = Read-Host "Overwrite existing file and continue? [Y/N]") -eq '') { $sAnswer = 'N' } } until ($sAnswer.ToUpper()[0] -match '[yYnN]') if(-not ($sAnswer.ToUpper()[0] -match '[Y]')) { @@ -119,13 +119,13 @@ Function Install-DevSetupEnv { # Read the configuration from the YAML file $YamlData = Read-ConfigurationFile -Config $YamlFile - + # Check if YAML data was successfully parsed if ($null -eq $YamlData) { Write-StatusMessage "Failed to parse YAML configuration from: $YamlFile" -Verbosity Error return } - + # Install PowerShell module dependencies Install-PowershellModules -YamlData $YamlData | Out-Null @@ -142,7 +142,7 @@ Function Install-DevSetupEnv { # Execute any commands defined in the configuration if ($YamlData.devsetup.commands -and $YamlData.devsetup.commands.Count -gt 0) { Write-StatusMessage "Executing configuration commands..." -ForegroundColor Cyan - + foreach ($commandEntry in $YamlData.devsetup.commands) { if ($commandEntry.command) { Write-StatusMessage "- Executing command for: $($commandEntry.packageName)" -Indent 2 -ForegroundColor Gray diff --git a/DevSetup/Private/Commands/Show-DevSetupEnvList.ps1 b/DevSetup/Private/Commands/Show-DevSetupEnvList.ps1 index df7c7a9..463d3c5 100644 --- a/DevSetup/Private/Commands/Show-DevSetupEnvList.ps1 +++ b/DevSetup/Private/Commands/Show-DevSetupEnvList.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Lists available development environment configurations with platform filtering. @@ -21,22 +21,22 @@ .EXAMPLE Show-DevSetupEnvList - + Lists development environments compatible with the current platform. .EXAMPLE Show-DevSetupEnvList -Platform "all" - + Displays all available development environments regardless of platform. .EXAMPLE Show-DevSetupEnvList -Platform "linux" - + Shows only environments specifically designed for Linux systems. .EXAMPLE Show-DevSetupEnvList -Platform "windows" - + Lists environments compatible with Windows systems. .NOTES @@ -107,7 +107,7 @@ Function Show-DevSetupEnvList { # Get the environments.json file path $devSetupPath = Get-DevSetupPath $environmentsJsonPath = Join-Path -Path $devSetupPath -ChildPath "environments.json" - + if (-not (Test-Path $environmentsJsonPath)) { Write-Host "No environments index found. Running optimization to create it..." -ForegroundColor Cyan Optimize-DevSetupEnvs | Out-Null @@ -122,7 +122,7 @@ Function Show-DevSetupEnvList { Optimize-DevSetupEnvs | Out-Null } } - + if ($Provider) { $environments = $environments | Where-Object { $_.provider -and ($_.provider.ToLower() -eq $Provider.ToLower()) } } @@ -134,7 +134,7 @@ Function Show-DevSetupEnvList { if ($platformFilter -ne "all") { $environments = $environments | Where-Object { ($_.platform -and ($_.platform.ToLower() -eq $platformFilter)) } } - + if ($environments.Count -eq 0) { if ($platformFilter -eq "all") { Write-Host "No development environments found." -ForegroundColor Yellow @@ -144,7 +144,7 @@ Function Show-DevSetupEnvList { } return $true } - + # Create a formatted table $tableData = @() foreach ($env in $environments) { @@ -159,7 +159,7 @@ Function Show-DevSetupEnvList { Color = "DarkGray" } } - + $columnDefinitions = [ordered]@{ Name = @{ Name = "Name"; Width = 32; Alignment = "Left"; Color = "White"; Key = "Name" } Version = @{ Name = "Version"; Width = 10; Alignment = "Center"; Color = "White"; Key = "Version" } diff --git a/DevSetup/Private/Providers/Chocolatey/Export-InstalledChocolateyPackages.ps1 b/DevSetup/Private/Providers/Chocolatey/Export-InstalledChocolateyPackages.ps1 index c259a60..3950284 100644 --- a/DevSetup/Private/Providers/Chocolatey/Export-InstalledChocolateyPackages.ps1 +++ b/DevSetup/Private/Providers/Chocolatey/Export-InstalledChocolateyPackages.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Exports installed Chocolatey packages to a YAML configuration file. @@ -64,7 +64,7 @@ Configuration Export, Package Discovery, YAML Generation #> -Function Export-InstalledChocolateyPackages { +Function Export-InstalledChocolateyPackage { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] diff --git a/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.ps1 b/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.ps1 index 5262306..f057182 100644 --- a/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.ps1 +++ b/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Retrieves all package dependencies from installed Chocolatey packages. @@ -54,7 +54,7 @@ Dependency Analysis, Package Management, Metadata Extraction #> -Function Get-ChocolateyPackageDependencies { +Function Get-ChocolateyPackageDependency { [CmdletBinding()] [OutputType([array])] Param() diff --git a/DevSetup/Private/Providers/Chocolatey/Install-ChocolateyPackages.ps1 b/DevSetup/Private/Providers/Chocolatey/Install-ChocolateyPackages.ps1 index 3583f32..cf78829 100644 --- a/DevSetup/Private/Providers/Chocolatey/Install-ChocolateyPackages.ps1 +++ b/DevSetup/Private/Providers/Chocolatey/Install-ChocolateyPackages.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Installs Chocolatey packages from YAML configuration data. @@ -79,7 +79,7 @@ Bulk Installation, Configuration Processing, Package Management #> -Function Install-ChocolateyPackages { +Function Install-ChocolateyPackage { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] diff --git a/DevSetup/Private/Providers/Chocolatey/Uninstall-ChocolateyPackages.ps1 b/DevSetup/Private/Providers/Chocolatey/Uninstall-ChocolateyPackages.ps1 index 35943c1..45a7e83 100644 --- a/DevSetup/Private/Providers/Chocolatey/Uninstall-ChocolateyPackages.ps1 +++ b/DevSetup/Private/Providers/Chocolatey/Uninstall-ChocolateyPackages.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Uninstalls multiple Chocolatey packages from the system based on YAML configuration. @@ -74,7 +74,7 @@ Package Management, Batch Uninstallation, Configuration Processing, System Cleanup #> -Function Uninstall-ChocolateyPackages { +Function Uninstall-ChocolateyPackage { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] diff --git a/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1 b/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1 index dbd333d..f58f617 100644 --- a/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1 +++ b/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Installs core dependencies required for the DevSetup module to function properly. @@ -67,7 +67,7 @@ System Setup, Dependency Management, Package Manager Installation #> -Function Install-CoreDependencies { +Function Install-CoreDependency { [CmdletBinding()] Param() diff --git a/DevSetup/Private/Providers/Powershell/Export-InstalledPowershellModules.ps1 b/DevSetup/Private/Providers/Powershell/Export-InstalledPowershellModules.ps1 index 65b1f91..5d307dd 100644 --- a/DevSetup/Private/Providers/Powershell/Export-InstalledPowershellModules.ps1 +++ b/DevSetup/Private/Providers/Powershell/Export-InstalledPowershellModules.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Exports installed PowerShell modules to a YAML configuration file. @@ -65,7 +65,7 @@ Configuration Export, Module Discovery, YAML Generation #> -Function Export-InstalledPowershellModules { +Function Export-InstalledPowershellModule { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] diff --git a/DevSetup/Private/Providers/Powershell/Install-PowershellModules.ps1 b/DevSetup/Private/Providers/Powershell/Install-PowershellModules.ps1 index 2b18c4e..a05618b 100644 --- a/DevSetup/Private/Providers/Powershell/Install-PowershellModules.ps1 +++ b/DevSetup/Private/Providers/Powershell/Install-PowershellModules.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Installs PowerShell modules from YAML configuration data. @@ -76,7 +76,7 @@ Bulk Installation, Configuration Processing, Module Management #> -Function Install-PowershellModules { +Function Install-PowershellModule { Param( [Parameter(Mandatory=$true, Position=0)] [ValidateNotNullOrEmpty()] diff --git a/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.ps1 b/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.ps1 index 50b4563..700e95e 100644 --- a/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.ps1 +++ b/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Uninstalls multiple PowerShell modules from the system based on YAML configuration. @@ -76,7 +76,7 @@ Package Management, Batch Uninstallation, Configuration Processing, Module Management #> -Function Uninstall-PowershellModules { +Function Uninstall-PowershellModule { Param( [Parameter(Mandatory=$true, Position=0)] [ValidateNotNullOrEmpty()] diff --git a/DevSetup/Private/Providers/Scoop/Export-InstalledScoopPackages.ps1 b/DevSetup/Private/Providers/Scoop/Export-InstalledScoopPackages.ps1 index db2107d..48b0619 100644 --- a/DevSetup/Private/Providers/Scoop/Export-InstalledScoopPackages.ps1 +++ b/DevSetup/Private/Providers/Scoop/Export-InstalledScoopPackages.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Exports installed Scoop packages and buckets to a YAML configuration file. @@ -66,7 +66,7 @@ Configuration Export, Package Discovery, YAML Generation #> -Function Export-InstalledScoopPackages { +Function Export-InstalledScoopPackage { Param( [Parameter(Mandatory = $true)] [string]$Config, diff --git a/DevSetup/Private/Providers/Scoop/Install-ScoopComponents.ps1 b/DevSetup/Private/Providers/Scoop/Install-ScoopComponents.ps1 index cf5aa56..eaf6f60 100644 --- a/DevSetup/Private/Providers/Scoop/Install-ScoopComponents.ps1 +++ b/DevSetup/Private/Providers/Scoop/Install-ScoopComponents.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Installs Scoop buckets and packages from YAML configuration data. @@ -102,7 +102,7 @@ .FUNCTIONALITY Bulk Installation, Configuration Processing, Package Management #> -Function Install-ScoopComponents { +Function Install-ScoopComponent { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] diff --git a/DevSetup/Private/Providers/Scoop/Uninstall-ScoopComponents.ps1 b/DevSetup/Private/Providers/Scoop/Uninstall-ScoopComponents.ps1 index d2b2f6e..7fd4b4f 100644 --- a/DevSetup/Private/Providers/Scoop/Uninstall-ScoopComponents.ps1 +++ b/DevSetup/Private/Providers/Scoop/Uninstall-ScoopComponents.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Uninstalls multiple Scoop components (buckets and packages) from the system based on YAML configuration. @@ -79,7 +79,7 @@ Package Management, Batch Uninstallation, Configuration Processing, Component Management #> -Function Uninstall-ScoopComponents { +Function Uninstall-ScoopComponent { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] diff --git a/DevSetup/Private/Utils/Find-GitRepositories.ps1 b/DevSetup/Private/Utils/Find-GitRepositories.ps1 index dd7aa66..f7af8b1 100644 --- a/DevSetup/Private/Utils/Find-GitRepositories.ps1 +++ b/DevSetup/Private/Utils/Find-GitRepositories.ps1 @@ -1,4 +1,4 @@ -Function Find-GitRepositories { +Function Find-GitRepository { [CmdletBinding()] Param( [Parameter( @@ -25,7 +25,7 @@ Function Find-GitRepositories { Write-Verbose "[PROCESS] Excluding system folders: $($ExcludeFolders -join ', ')" # Use a more efficient search strategy - function Search-GitRepos { + function Search-GitRepo { param([string]$SearchPath, [string[]]$ExcludeFolders) try { diff --git a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1 b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1 index d8e29ee..5534210 100644 --- a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1 +++ b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1 @@ -1,4 +1,4 @@ -Function Initialize-DevSetupEnvs { +Function Initialize-DevSetupEnv { try { # Define environments repository path $environmentsPath = Get-DevSetupEnvPath diff --git a/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1 b/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1 index a876fdf..a720888 100644 --- a/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1 +++ b/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1 @@ -1,4 +1,4 @@ -Function Optimize-DevSetupEnvs { +Function Optimize-DevSetupEnv { [CmdletBinding()] [OutputType([bool])] Param() diff --git a/Remove-File.ps1 b/Remove-File.ps1 deleted file mode 100644 index 2cb2731..0000000 --- a/Remove-File.ps1 +++ /dev/null @@ -1,11 +0,0 @@ -Function Remove-File { - [CmdletBinding(SupportsShouldProcess=$true)] - Param([string]$Path) - - if ($PSCmdlet.ShouldProcess("git", "brew install")) { - Remove-Item $Path - } -} - -Remove-File -Path ./converage.xml -WhatIf -Remove-File -Path ./converage.xml -Confirm \ No newline at end of file diff --git a/runSecurity.ps1 b/runSecurity.ps1 new file mode 100644 index 0000000..5250d22 --- /dev/null +++ b/runSecurity.ps1 @@ -0,0 +1,8 @@ +#-IncludeRule @("PSAvoidUsingInvokeExpression", "PSAvoidUsingConvertToSecureStringWithPlainText") ` +import-module ConvertToSarif +Invoke-ScriptAnalyzer ` + -Path . ` + -ExcludeRule @("PSAvoidLongLines", "PSAlignAssignmentStatement") ` + -IncludeDefaultRules ` + -Severity @("Error", "Warning") ` + -Recurse | ConvertTo-SARIF -FilePath results.sarif -IgnorePattern 'Tests' \ No newline at end of file From f60e3069d9e8b92c12b2a308f455f434bb05a9e2 Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 05:51:20 -0500 Subject: [PATCH 04/18] Adding install-modules to the runSecurity.ps1 file. --- runSecurity.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runSecurity.ps1 b/runSecurity.ps1 index 5250d22..f40f775 100644 --- a/runSecurity.ps1 +++ b/runSecurity.ps1 @@ -1,5 +1,8 @@ #-IncludeRule @("PSAvoidUsingInvokeExpression", "PSAvoidUsingConvertToSecureStringWithPlainText") ` -import-module ConvertToSarif +Set-PSRepository PSGallery -InstallationPolicy Trusted +Install-Module PSScriptAnalyzer -ErrorAction Stop +Install-Module ConvertToSARIF -ErrorAction Stop +Import-Module ConvertToSarif Invoke-ScriptAnalyzer ` -Path . ` -ExcludeRule @("PSAvoidLongLines", "PSAlignAssignmentStatement") ` From 0686ca954348048f32ec45eb4a3a7e2e63d0010e Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 15:00:36 -0500 Subject: [PATCH 05/18] Fixing changes made my psscriptanalyzer when it was trying to 'help' --- .../Commands/Export-DevSetupEnv.Tests.ps1 | 12 +++++------ .../Commands/Initialize-DevSetup.Tests.ps1 | 20 +++++++++---------- .../Commands/Install-DevSetupEnv.Tests.ps1 | 16 +++++++-------- .../Commands/Show-DevSetupEnvList.Tests.ps1 | 16 +++++++-------- .../Export-InstalledChocolateyPackages.ps1 | 2 +- .../Get-ChocolateyPackageDependencies.ps1 | 2 +- .../Chocolatey/Install-ChocolateyPackages.ps1 | 2 +- .../Uninstall-ChocolateyPackages.ps1 | 2 +- .../Core/Install-CoreDependencies.ps1 | 2 +- .../Export-InstalledPowershellModules.ps1 | 2 +- .../Powershell/Install-PowershellModules.ps1 | 2 +- .../Uninstall-PowershellModules.ps1 | 2 +- .../Scoop/Export-InstalledScoopPackages.ps1 | 2 +- .../Scoop/Install-ScoopComponents.ps1 | 2 +- .../Scoop/Uninstall-ScoopComponents.ps1 | 2 +- .../Utils/Initialize-DevSetupEnvs.Tests.ps1 | 16 +++++++-------- .../Private/Utils/Initialize-DevSetupEnvs.ps1 | 2 +- .../Private/Utils/Optimize-DevSetupEnvs.ps1 | 2 +- 18 files changed, 53 insertions(+), 53 deletions(-) diff --git a/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1 b/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1 index 4d98a6b..80b8c04 100644 --- a/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1 +++ b/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1 @@ -1,10 +1,10 @@ BeforeAll { - . $PSScriptRoot\Export-DevSetupEnv.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupEnvPath.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupLocalEnvPath.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupCommunityEnvPath.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Write-NewConfig.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1 + . (Join-Path $PSScriptRoot "Export-DevSetupEnv.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Get-DevSetupEnvPath.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Get-DevSetupLocalEnvPath.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Get-DevSetupCommunityEnvPath.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Write-NewConfig.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") if ($PSVersionTable.PSVersion.Major -eq 5) { Mock Get-DevSetupEnvPath { "$TestDrive\DevSetup\DevSetupEnvs" } Mock Get-DevSetupLocalEnvPath { "$TestDrive\DevSetup\DevSetupEnvs\local" } diff --git a/DevSetup/Private/Commands/Initialize-DevSetup.Tests.ps1 b/DevSetup/Private/Commands/Initialize-DevSetup.Tests.ps1 index 1bc3cb6..e8d3038 100644 --- a/DevSetup/Private/Commands/Initialize-DevSetup.Tests.ps1 +++ b/DevSetup/Private/Commands/Initialize-DevSetup.Tests.ps1 @@ -1,14 +1,14 @@ BeforeAll { - . $PSScriptRoot\Initialize-DevSetup.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupPath.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupEnvPath.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupLocalEnvPath.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupCommunityEnvPath.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Providers\Core\Install-CoreDependencies.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Initialize-DevSetupEnvs.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupCachePath.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupLogPath.ps1 + . (Join-Path $PSScriptRoot "Initialize-DevSetup.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Get-DevSetupPath.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Get-DevSetupEnvPath.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Get-DevSetupLocalEnvPath.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Get-DevSetupCommunityEnvPath.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\Core\Install-CoreDependencies.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Initialize-DevSetupEnvs.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Get-DevSetupCachePath.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Get-DevSetupLogPath.ps1") Mock Write-Host { } Mock Write-Error { } Mock Write-Verbose { } diff --git a/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1 b/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1 index fa540f2..581b859 100644 --- a/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1 +++ b/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1 @@ -1,12 +1,12 @@ BeforeAll { - . $PSScriptRoot\Install-DevSetupEnv.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Providers\PowerShell\Install-PowershellModules.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Providers\Chocolatey\Install-ChocolateyPackages.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Providers\Scoop\Install-ScoopComponents.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Read-ConfigurationFile.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupEnvPath.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Test-OperatingSystem.ps1 + . (Join-Path $PSScriptRoot "Install-DevSetupEnv.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\PowerShell\Install-PowershellModules.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\Chocolatey\Install-ChocolateyPackages.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\Scoop\Install-ScoopComponents.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Read-ConfigurationFile.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Get-DevSetupEnvPath.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Test-OperatingSystem.ps1") if ($PSVersionTable.PSVersion.Major -eq 5) { Mock Get-DevSetupEnvPath { "C:\DevSetup" } } elseif ($PSVersionTable.PSVersion.Major -ge 6) { diff --git a/DevSetup/Private/Commands/Show-DevSetupEnvList.Tests.ps1 b/DevSetup/Private/Commands/Show-DevSetupEnvList.Tests.ps1 index 250549a..63eb0e9 100644 --- a/DevSetup/Private/Commands/Show-DevSetupEnvList.Tests.ps1 +++ b/DevSetup/Private/Commands/Show-DevSetupEnvList.Tests.ps1 @@ -1,12 +1,12 @@ BeforeAll { - . $PSScriptRoot\Show-DevSetupEnvList.ps1 - . $PSScriptRoot\Show-DevSetupEnvList.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupEnvPath.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Optimize-DevSetupEnvs.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Get-DevSetupPath.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Test-OperatingSystem.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Format-PrettyTable.ps1 - . $PSScriptRoot\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1 + . (Join-Path $PSScriptRoot "Show-DevSetupEnvList.ps1") + . (Join-Path $PSScriptRoot "Show-DevSetupEnvList.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Get-DevSetupEnvPath.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Optimize-DevSetupEnvs.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Get-DevSetupPath.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Test-OperatingSystem.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Format-PrettyTable.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") if ($PSVersionTable.PSVersion.Major -eq 5) { Mock Get-DevSetupPath { "C:\DevSetup" } } elseif ($PSVersionTable.PSVersion.Major -ge 6) { diff --git a/DevSetup/Private/Providers/Chocolatey/Export-InstalledChocolateyPackages.ps1 b/DevSetup/Private/Providers/Chocolatey/Export-InstalledChocolateyPackages.ps1 index 3950284..55dc2b4 100644 --- a/DevSetup/Private/Providers/Chocolatey/Export-InstalledChocolateyPackages.ps1 +++ b/DevSetup/Private/Providers/Chocolatey/Export-InstalledChocolateyPackages.ps1 @@ -64,7 +64,7 @@ Configuration Export, Package Discovery, YAML Generation #> -Function Export-InstalledChocolateyPackage { +Function Export-InstalledChocolateyPackages { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] diff --git a/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.ps1 b/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.ps1 index f057182..f0b8e03 100644 --- a/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.ps1 +++ b/DevSetup/Private/Providers/Chocolatey/Get-ChocolateyPackageDependencies.ps1 @@ -54,7 +54,7 @@ Dependency Analysis, Package Management, Metadata Extraction #> -Function Get-ChocolateyPackageDependency { +Function Get-ChocolateyPackageDependencies { [CmdletBinding()] [OutputType([array])] Param() diff --git a/DevSetup/Private/Providers/Chocolatey/Install-ChocolateyPackages.ps1 b/DevSetup/Private/Providers/Chocolatey/Install-ChocolateyPackages.ps1 index cf78829..d50e744 100644 --- a/DevSetup/Private/Providers/Chocolatey/Install-ChocolateyPackages.ps1 +++ b/DevSetup/Private/Providers/Chocolatey/Install-ChocolateyPackages.ps1 @@ -79,7 +79,7 @@ Bulk Installation, Configuration Processing, Package Management #> -Function Install-ChocolateyPackage { +Function Install-ChocolateyPackages { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] diff --git a/DevSetup/Private/Providers/Chocolatey/Uninstall-ChocolateyPackages.ps1 b/DevSetup/Private/Providers/Chocolatey/Uninstall-ChocolateyPackages.ps1 index 45a7e83..5939ac0 100644 --- a/DevSetup/Private/Providers/Chocolatey/Uninstall-ChocolateyPackages.ps1 +++ b/DevSetup/Private/Providers/Chocolatey/Uninstall-ChocolateyPackages.ps1 @@ -74,7 +74,7 @@ Package Management, Batch Uninstallation, Configuration Processing, System Cleanup #> -Function Uninstall-ChocolateyPackage { +Function Uninstall-ChocolateyPackages { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] diff --git a/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1 b/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1 index f58f617..5ef3ceb 100644 --- a/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1 +++ b/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1 @@ -67,7 +67,7 @@ System Setup, Dependency Management, Package Manager Installation #> -Function Install-CoreDependency { +Function Install-CoreDependencies { [CmdletBinding()] Param() diff --git a/DevSetup/Private/Providers/Powershell/Export-InstalledPowershellModules.ps1 b/DevSetup/Private/Providers/Powershell/Export-InstalledPowershellModules.ps1 index 5d307dd..bd1e7f7 100644 --- a/DevSetup/Private/Providers/Powershell/Export-InstalledPowershellModules.ps1 +++ b/DevSetup/Private/Providers/Powershell/Export-InstalledPowershellModules.ps1 @@ -65,7 +65,7 @@ Configuration Export, Module Discovery, YAML Generation #> -Function Export-InstalledPowershellModule { +Function Export-InstalledPowershellModules { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] diff --git a/DevSetup/Private/Providers/Powershell/Install-PowershellModules.ps1 b/DevSetup/Private/Providers/Powershell/Install-PowershellModules.ps1 index a05618b..bfb1c47 100644 --- a/DevSetup/Private/Providers/Powershell/Install-PowershellModules.ps1 +++ b/DevSetup/Private/Providers/Powershell/Install-PowershellModules.ps1 @@ -76,7 +76,7 @@ Bulk Installation, Configuration Processing, Module Management #> -Function Install-PowershellModule { +Function Install-PowershellModules { Param( [Parameter(Mandatory=$true, Position=0)] [ValidateNotNullOrEmpty()] diff --git a/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.ps1 b/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.ps1 index 700e95e..563910a 100644 --- a/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.ps1 +++ b/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.ps1 @@ -76,7 +76,7 @@ Package Management, Batch Uninstallation, Configuration Processing, Module Management #> -Function Uninstall-PowershellModule { +Function Uninstall-PowershellModules { Param( [Parameter(Mandatory=$true, Position=0)] [ValidateNotNullOrEmpty()] diff --git a/DevSetup/Private/Providers/Scoop/Export-InstalledScoopPackages.ps1 b/DevSetup/Private/Providers/Scoop/Export-InstalledScoopPackages.ps1 index 48b0619..9e97e9a 100644 --- a/DevSetup/Private/Providers/Scoop/Export-InstalledScoopPackages.ps1 +++ b/DevSetup/Private/Providers/Scoop/Export-InstalledScoopPackages.ps1 @@ -66,7 +66,7 @@ Configuration Export, Package Discovery, YAML Generation #> -Function Export-InstalledScoopPackage { +Function Export-InstalledScoopPackages { Param( [Parameter(Mandatory = $true)] [string]$Config, diff --git a/DevSetup/Private/Providers/Scoop/Install-ScoopComponents.ps1 b/DevSetup/Private/Providers/Scoop/Install-ScoopComponents.ps1 index eaf6f60..fb4b812 100644 --- a/DevSetup/Private/Providers/Scoop/Install-ScoopComponents.ps1 +++ b/DevSetup/Private/Providers/Scoop/Install-ScoopComponents.ps1 @@ -102,7 +102,7 @@ .FUNCTIONALITY Bulk Installation, Configuration Processing, Package Management #> -Function Install-ScoopComponent { +Function Install-ScoopComponents { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] diff --git a/DevSetup/Private/Providers/Scoop/Uninstall-ScoopComponents.ps1 b/DevSetup/Private/Providers/Scoop/Uninstall-ScoopComponents.ps1 index 7fd4b4f..4012b20 100644 --- a/DevSetup/Private/Providers/Scoop/Uninstall-ScoopComponents.ps1 +++ b/DevSetup/Private/Providers/Scoop/Uninstall-ScoopComponents.ps1 @@ -79,7 +79,7 @@ Package Management, Batch Uninstallation, Configuration Processing, Component Management #> -Function Uninstall-ScoopComponent { +Function Uninstall-ScoopComponents { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] diff --git a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1 b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1 index 0b5b19b..7101287 100644 --- a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1 +++ b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1 @@ -1,14 +1,14 @@ BeforeAll { Function Get-GitHubRepository { } Function Install-GitRepository { } - - . $PSScriptRoot\Initialize-DevSetupEnvs.ps1 - . $PSScriptRoot\Write-StatusMessage.ps1 - . $PSScriptRoot\Optimize-DevSetupEnvs.ps1 - . $PSScriptRoot\Get-DevSetupEnvPath.ps1 - . $PSScriptRoot\Get-DevSetupLocalEnvPath.ps1 - . $PSScriptRoot\Get-DevSetupCommunityEnvPath.ps1 - . $PSScriptRoot\Get-DevSetupManifest.ps1 + + . (Join-Path "$PSScriptRoot" "Initialize-DevSetupEnvs.ps1") + . (Join-Path "$PSScriptRoot" "Write-StatusMessage.ps1") + . (Join-Path "$PSScriptRoot" "Optimize-DevSetupEnvs.ps1") + . (Join-Path "$PSScriptRoot" "Get-DevSetupEnvPath.ps1") + . (Join-Path "$PSScriptRoot" "Get-DevSetupLocalEnvPath.ps1") + . (Join-Path "$PSScriptRoot" "Get-DevSetupCommunityEnvPath.ps1") + . (Join-Path "$PSScriptRoot" "Get-DevSetupManifest.ps1") Mock Get-DevSetupEnvPath { "TestDrive:\DevSetupEnvs" } Mock Get-DevSetupManifest { @{ diff --git a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1 b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1 index 5534210..5583d11 100644 --- a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1 +++ b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1 @@ -1,4 +1,4 @@ -Function Initialize-DevSetupEnv { +Function Initialize-DevSetupEnvs { try { # Define environments repository path $environmentsPath = Get-DevSetupEnvPath diff --git a/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1 b/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1 index a720888..8273bbe 100644 --- a/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1 +++ b/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1 @@ -1,4 +1,4 @@ -Function Optimize-DevSetupEnv { +Function Optimize-DevSetupEnvs { [CmdletBinding()] [OutputType([bool])] Param() From 507703db94cf2b52ba2ea0f93f96bed531971915 Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 15:14:54 -0500 Subject: [PATCH 06/18] Fixes for linux? --- .../Powershell/Install-PowershellModules.Tests.ps1 | 8 ++++---- .../Powershell/Uninstall-PowershellModules.Tests.ps1 | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/DevSetup/Private/Providers/Powershell/Install-PowershellModules.Tests.ps1 b/DevSetup/Private/Providers/Powershell/Install-PowershellModules.Tests.ps1 index 4ec0b6d..41cc75d 100644 --- a/DevSetup/Private/Providers/Powershell/Install-PowershellModules.Tests.ps1 +++ b/DevSetup/Private/Providers/Powershell/Install-PowershellModules.Tests.ps1 @@ -1,8 +1,8 @@ BeforeAll { - . $PSScriptRoot\Install-PowershellModules.ps1 - . $PSScriptRoot\Install-PowerShellModule.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Test-RunningAsAdmin.ps1 + . (Join-Path $PSScriptRoot "Install-PowershellModules.ps1") + . (Join-Path $PSScriptRoot "Install-PowerShellModule.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Test-RunningAsAdmin.ps1") Mock Write-StatusMessage { } Mock Test-RunningAsAdmin { return $true } Mock Write-Error {} diff --git a/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.Tests.ps1 b/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.Tests.ps1 index 17471fb..69d32c1 100644 --- a/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.Tests.ps1 +++ b/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.Tests.ps1 @@ -1,8 +1,8 @@ BeforeAll { - . $PSScriptRoot\Uninstall-PowershellModules.ps1 - . $PSScriptRoot\Uninstall-PowerShellModule.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Test-RunningAsAdmin.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1 + . (Join-Path $PSScriptRoot "Uninstall-PowershellModules.ps1") + . (Join-Path $PSScriptRoot "Uninstall-PowerShellModule.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Test-RunningAsAdmin.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") Mock Write-StatusMessage { } Mock Write-Warning { } Mock Write-Error { } From 7aee38781ae881284c875803de68fb198c808e99 Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 15:19:01 -0500 Subject: [PATCH 07/18] Fixing some capitalization issues mac doesnt like. --- .../Powershell/Install-PowershellModules.Tests.ps1 | 12 ++++++------ .../Powershell/Uninstall-PowershellModules.Tests.ps1 | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/DevSetup/Private/Providers/Powershell/Install-PowershellModules.Tests.ps1 b/DevSetup/Private/Providers/Powershell/Install-PowershellModules.Tests.ps1 index 41cc75d..f497f86 100644 --- a/DevSetup/Private/Providers/Powershell/Install-PowershellModules.Tests.ps1 +++ b/DevSetup/Private/Providers/Powershell/Install-PowershellModules.Tests.ps1 @@ -1,6 +1,6 @@ BeforeAll { . (Join-Path $PSScriptRoot "Install-PowershellModules.ps1") - . (Join-Path $PSScriptRoot "Install-PowerShellModule.ps1") + . (Join-Path $PSScriptRoot "Install-PowershellModule.ps1") . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Test-RunningAsAdmin.ps1") Mock Write-StatusMessage { } @@ -49,7 +49,7 @@ Describe "Install-PowershellModules" { Context "When modules are installed successfully (string format)" { It "Should install all modules and return true" { $script:installCalls = @() - Mock Install-PowerShellModule -MockWith { + Mock Install-PowershellModule -MockWith { param($ModuleName, $Force, $AllowClobber, $Scope, $Version) $script:installCalls += $ModuleName return $true @@ -73,7 +73,7 @@ Describe "Install-PowershellModules" { Context "When modules are installed successfully (object format)" { It "Should install all modules and return true" { $script:installCalls = @() - Mock Install-PowerShellModule -MockWith { + Mock Install-PowershellModule -MockWith { param($ModuleName, $Force, $AllowClobber, $Scope, $Version) $script:installCalls += $ModuleName return $true @@ -100,7 +100,7 @@ Describe "Install-PowershellModules" { Context "When some modules fail to install" { It "Should continue and return true" { $script:installCalls = @() - Mock Install-PowerShellModule -MockWith { + Mock Install-PowershellModule -MockWith { param($ModuleName, $Force, $AllowClobber, $Scope, $Version) $script:installCalls += $ModuleName if ($ModuleName -eq "PSReadLine") { return $false } @@ -126,7 +126,7 @@ Describe "Install-PowershellModules" { Context "When module entry is empty or missing name" { It "Should skip invalid entries and return true" { $script:installCalls = @() - Mock Install-PowerShellModule -MockWith { + Mock Install-PowershellModule -MockWith { param($ModuleName, $Force, $AllowClobber, $Scope, $Version) $script:installCalls += $ModuleName return $true @@ -153,7 +153,7 @@ Describe "Install-PowershellModules" { Context "When an exception occurs during installation" { It "Should catch and return false" { - Mock Install-PowerShellModule { throw "Unexpected error" } + Mock Install-PowershellModule { throw "Unexpected error" } $yamlData = @{ devsetup = @{ dependencies = @{ diff --git a/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.Tests.ps1 b/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.Tests.ps1 index 69d32c1..03f53c7 100644 --- a/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.Tests.ps1 +++ b/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.Tests.ps1 @@ -1,6 +1,6 @@ BeforeAll { . (Join-Path $PSScriptRoot "Uninstall-PowershellModules.ps1") - . (Join-Path $PSScriptRoot "Uninstall-PowerShellModule.ps1") + . (Join-Path $PSScriptRoot "Uninstall-PowershellModule.ps1") . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Test-RunningAsAdmin.ps1") . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") Mock Write-StatusMessage { } @@ -49,7 +49,7 @@ Describe "Uninstall-PowershellModules" { Context "When modules are uninstalled successfully (string format)" { It "Should uninstall all modules and return true" { $script:uninstallCalls = @() - Mock Uninstall-PowerShellModule -MockWith { + Mock Uninstall-PowershellModule -MockWith { param($ModuleName) $script:uninstallCalls += $ModuleName return $true @@ -73,7 +73,7 @@ Describe "Uninstall-PowershellModules" { Context "When modules are uninstalled successfully (object format)" { It "Should uninstall all modules and return true" { $script:uninstallCalls = @() - Mock Uninstall-PowerShellModule -MockWith { + Mock Uninstall-PowershellModule -MockWith { param($ModuleName) $script:uninstallCalls += $ModuleName return $true @@ -100,7 +100,7 @@ Describe "Uninstall-PowershellModules" { Context "When some modules fail to uninstall" { It "Should continue and return true" { $script:uninstallCalls = @() - Mock Uninstall-PowerShellModule -MockWith { + Mock Uninstall-PowershellModule -MockWith { param($ModuleName) $script:uninstallCalls += $ModuleName if ($ModuleName -eq "PSReadLine") { return $false } @@ -126,7 +126,7 @@ Describe "Uninstall-PowershellModules" { Context "When module entry is empty or missing name" { It "Should skip invalid entries and return true" { $script:uninstallCalls = @() - Mock Uninstall-PowerShellModule -MockWith { + Mock Uninstall-PowershellModule -MockWith { param($ModuleName) $script:uninstallCalls += $ModuleName return $true @@ -153,7 +153,7 @@ Describe "Uninstall-PowershellModules" { Context "When an exception occurs during uninstallation" { It "Should catch and return false" { - Mock Uninstall-PowerShellModule { throw "Unexpected error" } + Mock Uninstall-PowershellModule { throw "Unexpected error" } $yamlData = @{ devsetup = @{ dependencies = @{ From b84a1c89ac9d30f394c7ca2ded620c8ca308e11f Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 15:25:40 -0500 Subject: [PATCH 08/18] Fixing some more things that psscriptanalyzer 'fixed'. --- .../Commands/Install-DevSetupEnv.Tests.ps1 | 2 +- .../Commands/Uninstall-DevSetupEnv.Tests.ps1 | 2 +- .../Core/Install-CoreDependencies.Tests.ps1 | 36 +++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1 b/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1 index 581b859..a1fe22c 100644 --- a/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1 +++ b/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1 @@ -1,6 +1,6 @@ BeforeAll { . (Join-Path $PSScriptRoot "Install-DevSetupEnv.ps1") - . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\PowerShell\Install-PowershellModules.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\Powershell\Install-PowershellModules.ps1") . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\Chocolatey\Install-ChocolateyPackages.ps1") . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\Scoop\Install-ScoopComponents.ps1") . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Read-ConfigurationFile.ps1") diff --git a/DevSetup/Private/Commands/Uninstall-DevSetupEnv.Tests.ps1 b/DevSetup/Private/Commands/Uninstall-DevSetupEnv.Tests.ps1 index 82fb247..478e95c 100644 --- a/DevSetup/Private/Commands/Uninstall-DevSetupEnv.Tests.ps1 +++ b/DevSetup/Private/Commands/Uninstall-DevSetupEnv.Tests.ps1 @@ -7,7 +7,7 @@ BeforeAll { . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\Scoop\Uninstall-ScoopComponents.ps1") . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\Chocolatey\Uninstall-ChocolateyPackages.ps1") - . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\PowerShell\Uninstall-PowershellModules.ps1") + . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\Powershell\Uninstall-PowershellModules.ps1") . (Join-Path $PSScriptRoot "..\..\..\DevSetup\Private\Providers\Homebrew\Invoke-HomebrewComponentsUninstall.ps1") Mock Get-DevSetupEnvPath { "$TestDrive\DevSetup\DevSetupEnvs" } Mock Test-Path { $true } diff --git a/DevSetup/Private/Providers/Core/Install-CoreDependencies.Tests.ps1 b/DevSetup/Private/Providers/Core/Install-CoreDependencies.Tests.ps1 index 3631d34..51bb937 100644 --- a/DevSetup/Private/Providers/Core/Install-CoreDependencies.Tests.ps1 +++ b/DevSetup/Private/Providers/Core/Install-CoreDependencies.Tests.ps1 @@ -1,15 +1,15 @@ BeforeAll { - . $PSScriptRoot\Install-CoreDependencies.ps1 - . $PSScriptRoot\Install-Nuget.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Get-DevSetupManifest.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Providers\Powershell\Install-PowerShellModule.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Providers\Chocolatey\Install-Chocolatey.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Providers\Chocolatey\Install-ChocolateyPackage.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Providers\Scoop\Install-Scoop.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Providers\Homebrew\Install-Homebrew.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Test-RunningAsAdmin.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Test-OperatingSystem.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1 + . (Join-Path $PSScriptRoot "Install-CoreDependencies.ps1") + . (Join-Path $PSScriptRoot "Install-Nuget.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Get-DevSetupManifest.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Powershell\Install-PowershellModule.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Chocolatey\Install-Chocolatey.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Chocolatey\Install-ChocolateyPackage.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Scoop\Install-Scoop.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Providers\Homebrew\Install-Homebrew.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Test-RunningAsAdmin.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Test-OperatingSystem.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Write-StatusMessage.ps1") Mock Write-StatusMessage { } Mock Write-Host { } Mock Write-Warning { } @@ -49,7 +49,7 @@ Describe "Install-CoreDependencies" { Mock Get-DevSetupManifest { return @{ RequiredModules = @("posh-git", "PSReadLine") } } Mock Test-OperatingSystem { param($os) return $false } $script:callCount = 0 - Mock Install-PowerShellModule -MockWith { + Mock Install-PowershellModule -MockWith { param($ModuleName, $Force, $AllowClobber, $Scope) $script:callCount++ if ($script:callCount -eq 1) { return $true } @@ -64,7 +64,7 @@ Describe "Install-CoreDependencies" { It "Should skip empty module names and return true" { Mock Install-NuGet { return $true } Mock Get-DevSetupManifest { return @{ RequiredModules = @("posh-git", $null, "PSReadLine") } } - Mock Install-PowerShellModule { return $true } + Mock Install-PowershellModule { return $true } Mock Test-OperatingSystem { param($os) return $false } $result = Install-CoreDependencies $result | Should -Be $true @@ -75,7 +75,7 @@ Describe "Install-CoreDependencies" { It "Should install everything and return true" { Mock Install-NuGet { return $true } Mock Get-DevSetupManifest { return @{ RequiredModules = @("posh-git", "PSReadLine") } } - Mock Install-PowerShellModule { return $true } + Mock Install-PowershellModule { return $true } Mock Install-Chocolatey { return $true } Mock Install-ChocolateyPackage { return $true } Mock Install-Scoop { return $true } @@ -89,7 +89,7 @@ Describe "Install-CoreDependencies" { It "Should return false and write error" { Mock Install-NuGet { return $true } Mock Get-DevSetupManifest { return @{ RequiredModules = @("posh-git") } } - Mock Install-PowerShellModule { return $true } + Mock Install-PowershellModule { return $true } Mock Install-Chocolatey { return $false } Mock Test-OperatingSystem { param($Windows) if ($Windows) { return $true } else { return $false } } $result = Install-CoreDependencies @@ -101,7 +101,7 @@ Describe "Install-CoreDependencies" { It "Should return false and write error" { Mock Install-NuGet { return $true } Mock Get-DevSetupManifest { return @{ RequiredModules = @("posh-git") } } - Mock Install-PowerShellModule { return $true } + Mock Install-PowershellModule { return $true } Mock Install-Chocolatey { return $true } Mock Install-ChocolateyPackage { return $false } Mock Test-OperatingSystem { param($Windows) if ($Windows) { return $true } else { return $false } } @@ -114,7 +114,7 @@ Describe "Install-CoreDependencies" { It "Should return false and write error" { Mock Install-NuGet { return $true } Mock Get-DevSetupManifest { return @{ RequiredModules = @("posh-git") } } - Mock Install-PowerShellModule { return $true } + Mock Install-PowershellModule { return $true } Mock Install-Chocolatey { return $true } Mock Install-ChocolateyPackage { return $true } Mock Install-Scoop { return $false } @@ -128,7 +128,7 @@ Describe "Install-CoreDependencies" { It "Should skip Windows-only installs and return true" { Mock Install-NuGet { return $true } Mock Get-DevSetupManifest { return @{ RequiredModules = @("posh-git") } } - Mock Install-PowerShellModule { return $true } + Mock Install-PowershellModule { return $true } Mock Test-OperatingSystem { param($os) return $false } $result = Install-CoreDependencies $result | Should -Be $true From 05d0fba95a55e3151bfd4121b772547840f0dba7 Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 15:49:02 -0500 Subject: [PATCH 09/18] FIxing a couple more issues with the pester tests --- .../Install-ChocolateyPackage.Tests.ps1 | 49 ++++++++++--------- .../Utils/Initialize-DevSetupEnvs.Tests.ps1 | 19 ++++--- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/DevSetup/Private/Providers/Chocolatey/Install-ChocolateyPackage.Tests.ps1 b/DevSetup/Private/Providers/Chocolatey/Install-ChocolateyPackage.Tests.ps1 index 2299ecf..19c32ae 100644 --- a/DevSetup/Private/Providers/Chocolatey/Install-ChocolateyPackage.Tests.ps1 +++ b/DevSetup/Private/Providers/Chocolatey/Install-ChocolateyPackage.Tests.ps1 @@ -1,9 +1,10 @@ BeforeAll { - . $PSScriptRoot\Install-ChocolateyPackage.ps1 - . $PSScriptRoot\Test-ChocolateyPackageInstalled.ps1 - . $PSScriptRoot\Uninstall-ChocolateyPackage.ps1 - . $PSScriptRoot\Write-ChocolateyCache.ps1 - . $PSScriptRoot\..\..\..\..\DevSetup\Private\Utils\Test-RunningAsAdmin.ps1 + . (Join-Path $PSScriptRoot "Install-ChocolateyPackage.ps1") + . (Join-Path $PSScriptRoot "Test-ChocolateyPackageInstalled.ps1") + . (Join-Path $PSScriptRoot "Uninstall-ChocolateyPackage.ps1") + . (Join-Path $PSScriptRoot "Write-ChocolateyCache.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Utils\Test-RunningAsAdmin.ps1") + . (Join-Path $PSScriptRoot "..\..\..\..\DevSetup\Private\Enums\InstalledState.ps1") Mock Test-RunningAsAdmin { $true } Mock Test-ChocolateyPackageInstalled { } Mock Uninstall-ChocolateyPackage { $true } @@ -20,7 +21,7 @@ Describe "Install-ChocolateyPackage" { Context "When not running as administrator" { It "Should throw and return false" { Mock Test-RunningAsAdmin { $false } - $result = Install-ChocolateyPackage -PackageName "git" + $result = Install-ChocolateyPackage -PackageName "azshell" $result | Should -Be $false Assert-MockCalled Write-Error -Scope It -ParameterFilter { $Message -match "administrator privileges" } } @@ -29,25 +30,29 @@ Describe "Install-ChocolateyPackage" { Context "When package is already installed and version matches" { It "Should return true immediately" { Mock Test-ChocolateyPackageInstalled { - [InstalledState]::Pass -bor [InstalledState]::Installed + return ([InstalledState]::Pass) } - $result = Install-ChocolateyPackage -PackageName "git" + $result = Install-ChocolateyPackage -PackageName "azshell" $result | Should -Be $true } } Context "When package is installed but version does not match" { It "Should uninstall and reinstall the package" { - Mock Test-ChocolateyPackageInstalled { [InstalledState]::Installed } + Mock Test-ChocolateyPackageInstalled { + return ([InstalledState]::Installed + [InstalledState]::MinimumVersionMet + [InstalledState]::GlobalVersionMet) + } $script:uninstallCalled = $false Mock Uninstall-ChocolateyPackage -MockWith { param($PackageName) $script:uninstallCalled = $true $true } - $LASTEXITCODE = 0 - Mock Invoke-Command { } - $result = Install-ChocolateyPackage -PackageName "git" + $script:LASTEXITCODE = 0 + Mock Invoke-Command { + $script:LASTEXITCODE = 0 + } + $result = Install-ChocolateyPackage -PackageName "azshell" $result | Should -Be $true $script:uninstallCalled | Should -Be $true } @@ -55,14 +60,14 @@ Describe "Install-ChocolateyPackage" { Context "When installing with version and params" { It "Should build the correct choco command" { - $LASTEXITCODE = 0 + $script:LASTEXITCODE = 0 $script:paramsPassed = $null - Mock Test-ChocolateyPackageInstalled { [InstalledState]::NotInstalled } + Mock Test-ChocolateyPackageInstalled { return ([InstalledState]::NotInstalled) } Mock Invoke-Command -MockWith { param($ScriptBlock) $script:paramsPassed = $ScriptBlock.ToString() } - $result = Install-ChocolateyPackage -PackageName "git" -Version "2.42.0" -Param "/silent" + $result = Install-ChocolateyPackage -PackageName "azshell" -Version "0.2.2" -Param "/silent" $result | Should -Be $true # You can add more checks for $paramsPassed if needed } @@ -70,9 +75,9 @@ Describe "Install-ChocolateyPackage" { Context "When installation fails (non-zero exit code)" { It "Should write error and return false" { - $LASTEXITCODE = 1 - Mock Test-ChocolateyPackageInstalled { [InstalledState]::NotInstalled } - $result = Install-ChocolateyPackage -PackageName "git" + $script:LASTEXITCODE = 1 + Mock Test-ChocolateyPackageInstalled { return ([InstalledState]::NotInstalled) } + $result = Install-ChocolateyPackage -PackageName "azshell" $result | Should -Be $false Assert-MockCalled Write-Error -Scope It -ParameterFilter { $Message -match "Failed to install" } } @@ -80,10 +85,10 @@ Describe "Install-ChocolateyPackage" { Context "When Write-ChocolateyCache fails after install" { It "Should write warning and return false" { - $LASTEXITCODE = 0 - Mock Test-ChocolateyPackageInstalled { [InstalledState]::NotInstalled } + $script:LASTEXITCODE = 0 + Mock Test-ChocolateyPackageInstalled { return ([InstalledState]::NotInstalled) } Mock Write-ChocolateyCache { $false } - $result = Install-ChocolateyPackage -PackageName "git" + $result = Install-ChocolateyPackage -PackageName "azshell" $result | Should -Be $false Assert-MockCalled Write-Warning -Scope It -ParameterFilter { $Message -match "Failed to write Chocolatey cache" } } @@ -92,7 +97,7 @@ Describe "Install-ChocolateyPackage" { Context "When an exception occurs during install" { It "Should write error and return false" { Mock Test-ChocolateyPackageInstalled { throw "Unexpected error" } - $result = Install-ChocolateyPackage -PackageName "git" + $result = Install-ChocolateyPackage -PackageName "azshell" $result | Should -Be $false Assert-MockCalled Write-Error -Scope It -ParameterFilter { $Message -match "Error checking/installing package" } } diff --git a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1 b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1 index 7101287..1767098 100644 --- a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1 +++ b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1 @@ -2,13 +2,15 @@ BeforeAll { Function Get-GitHubRepository { } Function Install-GitRepository { } - . (Join-Path "$PSScriptRoot" "Initialize-DevSetupEnvs.ps1") - . (Join-Path "$PSScriptRoot" "Write-StatusMessage.ps1") - . (Join-Path "$PSScriptRoot" "Optimize-DevSetupEnvs.ps1") - . (Join-Path "$PSScriptRoot" "Get-DevSetupEnvPath.ps1") - . (Join-Path "$PSScriptRoot" "Get-DevSetupLocalEnvPath.ps1") - . (Join-Path "$PSScriptRoot" "Get-DevSetupCommunityEnvPath.ps1") - . (Join-Path "$PSScriptRoot" "Get-DevSetupManifest.ps1") + Function Set-GitHubConfiguration { } + + . (Join-Path $PSScriptRoot "Initialize-DevSetupEnvs.ps1") + . (Join-Path $PSScriptRoot "Write-StatusMessage.ps1") + . (Join-Path $PSScriptRoot "Optimize-DevSetupEnvs.ps1") + . (Join-Path $PSScriptRoot "Get-DevSetupEnvPath.ps1") + . (Join-Path $PSScriptRoot "Get-DevSetupLocalEnvPath.ps1") + . (Join-Path $PSScriptRoot "Get-DevSetupCommunityEnvPath.ps1") + . (Join-Path $PSScriptRoot "Get-DevSetupManifest.ps1") Mock Get-DevSetupEnvPath { "TestDrive:\DevSetupEnvs" } Mock Get-DevSetupManifest { @{ @@ -20,6 +22,7 @@ BeforeAll { } } Mock Get-GitHubRepository { @{ clone_url = "https://github.com/example/envrepo.git" } } + Mock Set-GitHubConfiguration { $true } Mock Test-Path { $false } Mock Install-GitRepository { $true } Mock Write-StatusMessage { } @@ -52,7 +55,7 @@ Describe "Initialize-DevSetupEnvs" { Context "When EnvironmentsProjectUri is not a .git URL and GitHub API fails" { It "Should write error and return null" { - Mock Get-GitHubRepository { $null } + Mock Get-GitHubRepository { return $null } $result = Initialize-DevSetupEnvs $result | Should -Be $null Assert-MockCalled Write-Error -Scope It -ParameterFilter { $Message -match "Failed to retrieve repository information or clone_url" } From bd12ae7ada86d3ec878ed5b2bd1d1899bd3b8ee8 Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 15:55:02 -0500 Subject: [PATCH 10/18] FIxing an errant output that was causing a problem --- DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1 | 6 +++--- DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1 | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1 b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1 index 1767098..bac7a36 100644 --- a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1 +++ b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1 @@ -111,7 +111,7 @@ Describe "Initialize-DevSetupEnvs" { Mock Test-Path { $false } Mock Install-GitRepository { $null } $global:LASTEXITCODE = 1 - $result = Initialize-DevSetupEnvs + Initialize-DevSetupEnvs Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -eq "[Failed]" } } } @@ -121,14 +121,14 @@ Describe "Initialize-DevSetupEnvs" { Mock Test-Path { $false } Mock Install-GitRepository { $null } $global:LASTEXITCODE = 0 - $result = Initialize-DevSetupEnvs + Initialize-DevSetupEnvs Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter { $Message -eq "[OK]" } } } Context "When Optimize-DevSetupEnvs is called" { It "Should call Optimize-DevSetupEnvs after cloning" { - $result = Initialize-DevSetupEnvs + Initialize-DevSetupEnvs Assert-MockCalled Optimize-DevSetupEnvs -Scope It } } diff --git a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1 b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1 index 5583d11..8d97788 100644 --- a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1 +++ b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1 @@ -21,7 +21,7 @@ # Check if the URI ends with .git, if not use Get-GitHubRepository to get clone_url if ($environmentsProjectUri -notlike "*.git") { try { - Set-GitHubConfiguration -DisableTelemetry + Set-GitHubConfiguration -DisableTelemetry | Out-Null #Write-Host "GitHub API access is required to retrieve repository information." -ForegroundColor Yellow #Write-Host "Please create a GitHub Personal Access Token with 'repo' scope at:" -ForegroundColor Yellow #Write-Host "https://github.com/settings/tokens" -ForegroundColor Cyan From a9129329ea0ca498fec27a517fe36c1e54ef32ef Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 15:58:40 -0500 Subject: [PATCH 11/18] Updating workflows to run on PR's to main and develop and also adding ability to trigger them manually. --- .github/workflows/run-lint.yml | 2 ++ .github/workflows/run-unit-tests.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/run-lint.yml b/.github/workflows/run-lint.yml index c96cfce..c8c2dd3 100644 --- a/.github/workflows/run-lint.yml +++ b/.github/workflows/run-lint.yml @@ -3,6 +3,8 @@ on: pull_request: branches: - develop + - main + workflow_dispatch: jobs: psscriptanalyzer: diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml index ef45772..732a8a0 100644 --- a/.github/workflows/run-unit-tests.yml +++ b/.github/workflows/run-unit-tests.yml @@ -3,6 +3,8 @@ on: pull_request: branches: - develop + - main + workflow_dispatch: jobs: pester-test-linux: From 7a1406abefa7cfb2a741b67607bf1c4fb12799e7 Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 16:14:47 -0500 Subject: [PATCH 12/18] Adding copilot suggested correction. --- .../Homebrew/Install-Homebrew.Tests.ps1 | 40 ++++++++++++++++--- .../Providers/Homebrew/Install-Homebrew.ps1 | 8 ++-- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/DevSetup/Private/Providers/Homebrew/Install-Homebrew.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.Tests.ps1 index 59f039a..095ed4c 100644 --- a/DevSetup/Private/Providers/Homebrew/Install-Homebrew.Tests.ps1 +++ b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.Tests.ps1 @@ -46,7 +46,13 @@ Describe "Install-Homebrew" { } Mock Invoke-ExternalCommand { $true } Mock Write-StatusMessage { } - Mock Get-EnvironmentVariable { "/bin/bash" } + Mock Get-EnvironmentVariable { + Param($Name) + switch($Name) { + "SHELL" { return "/bin/bash" } + "HOME" { return "/Users/TestUser" } + } + } Mock Add-Content { } $result = Install-Homebrew @@ -69,7 +75,13 @@ Describe "Install-Homebrew" { } Mock Invoke-ExternalCommand { "Installation successful" } Mock Write-StatusMessage { } - Mock Get-EnvironmentVariable { "/bin/zsh" } + Mock Get-EnvironmentVariable { + Param($Name) + switch($Name) { + "SHELL" { return "/bin/zsh" } + "HOME" { return "/home/testuser" } + } + } Mock Add-Content { } $result = Install-Homebrew @@ -92,7 +104,13 @@ Describe "Install-Homebrew" { } Mock Invoke-ExternalCommand { "Installation successful" } Mock Write-StatusMessage { } - Mock Get-EnvironmentVariable { "/bin/fish" } + Mock Get-EnvironmentVariable { + Param($Name) + switch($Name) { + "SHELL" { return "/bin/fish" } + "HOME" { return "/home/testuser" } + } + } Mock Add-Content { } $result = Install-Homebrew @@ -145,7 +163,13 @@ Describe "Install-Homebrew" { } Mock Invoke-ExternalCommand { $true } Mock Write-StatusMessage { } - Mock Get-EnvironmentVariable { "/bin/bash" } + Mock Get-EnvironmentVariable { + Param($Name) + switch($Name) { + "SHELL" { return "/bin/bash" } + "HOME" { return "/home/testuser" } + } + } Mock Add-Content { } $result = Install-Homebrew @@ -162,7 +186,13 @@ Describe "Install-Homebrew" { } Mock Invoke-ExternalCommand { $true } Mock Write-StatusMessage { } - Mock Get-EnvironmentVariable { "/bin/zsh" } + Mock Get-EnvironmentVariable { + Param($Name) + switch($Name) { + "SHELL" { return "/bin/zsh" } + "HOME" { return "/Users/TestUser" } + } + } Mock Add-Content { } $result = Install-Homebrew diff --git a/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1 b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1 index 133d083..11885bb 100644 --- a/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1 +++ b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1 @@ -33,14 +33,14 @@ Function Install-Homebrew { switch ((Get-EnvironmentVariable SHELL)) { { $_ -like "*zsh*" } { Write-StatusMessage "- Adding Homebrew path to $HOME/.zshrc" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline - Add-Content -Path "$HOME/.zshrc" -Value "" - Add-Content -Path "$HOME/.zshrc" -Value ([string]::Format('eval "$({0} shellenv)"', $HomebrewPath)) + Add-Content -Path ([string]::format("{0}/.zshrc", (Get-EnvironmentVariable HOME))) -Value "" + Add-Content -Path ([string]::format("{0}/.zshrc", (Get-EnvironmentVariable HOME))) -Value ([string]::Format('eval "$({0} shellenv)"', $HomebrewPath)) Write-StatusMessage "[OK]" -ForegroundColor Green } { $_ -like "*bash*" } { Write-StatusMessage "- Adding Homebrew path to $HOME/.bashrc" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline - Add-Content -Path "$HOME/.bashrc" -Value "" - Add-Content -Path "$HOME/.bashrc" -Value ([string]::Format('eval "$({0} shellenv)"', $HomebrewPath)) + Add-Content -Path ([string]::format("{0}/.bashrc", (Get-EnvironmentVariable HOME))) -Value "" + Add-Content -Path ([string]::format("{0}/.bashrc", (Get-EnvironmentVariable HOME))) -Value ([string]::Format('eval "$({0} shellenv)"', $HomebrewPath)) Write-StatusMessage "[OK]" -ForegroundColor Green } default { From 5f005e546fb32d5b559a620c465e26bb72c733ba Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 16:48:12 -0500 Subject: [PATCH 13/18] Fixing workflow to work with new branch permissions. --- .github/workflows/update-module-version.yaml | 15 +++++++++++---- .../Providers/Homebrew/Install-Homebrew.Tests.ps1 | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/update-module-version.yaml b/.github/workflows/update-module-version.yaml index 7e92541..901ec4b 100644 --- a/.github/workflows/update-module-version.yaml +++ b/.github/workflows/update-module-version.yaml @@ -29,8 +29,15 @@ jobs: run: | perl -pi -e 's/[0-9]\.[0-9]\.[0-9]/${{ steps.version_tracker.outputs.version }}/' DevSetup/DevSetup.psd1 - - name: Commit and push changes - uses: stefanzweifel/git-auto-commit-action@v5 +# - name: Commit and push changes +# uses: stefanzweifel/git-auto-commit-action@v5 +# with: +# commit_message: "Automated Release Tagging for ${{ steps.version_tracker.outputs.version }} in DevSetup.psd1" +# branch: + - name: Create Pull Request + uses: peter-evans/create-pull-request@v7 with: - commit_message: "Automated Release Tagging for ${{ steps.version_tracker.outputs.version }} in DevSetup.psd1" - branch: develop \ No newline at end of file + commit-message: Automated Release Tagging for ${{ steps.version_tracker.outputs.version }} in DevSetup.psd1 + title: Automated Release Tagging for ${{ steps.version_tracker.outputs.version }} in DevSetup.psd1 + body: Changing version to ${{ steps.version_tracker.outputs.version }} + branch: update-release-version-to-${{ steps.version_tracker.outputs.version }} \ No newline at end of file diff --git a/DevSetup/Private/Providers/Homebrew/Install-Homebrew.Tests.ps1 b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.Tests.ps1 index 095ed4c..85368e4 100644 --- a/DevSetup/Private/Providers/Homebrew/Install-Homebrew.Tests.ps1 +++ b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.Tests.ps1 @@ -50,7 +50,7 @@ Describe "Install-Homebrew" { Param($Name) switch($Name) { "SHELL" { return "/bin/bash" } - "HOME" { return "/Users/TestUser" } + "HOME" { return "/home/testuser" } } } Mock Add-Content { } From 7b5abc7efc443fd03c4be36a07bd37b04048c4d9 Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 16:49:46 -0500 Subject: [PATCH 14/18] Adding ability to trigger workflow manually. --- .github/workflows/update-module-version.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/update-module-version.yaml b/.github/workflows/update-module-version.yaml index 901ec4b..dc6c130 100644 --- a/.github/workflows/update-module-version.yaml +++ b/.github/workflows/update-module-version.yaml @@ -4,6 +4,7 @@ on: push: branches: - develop # Trigger on pushes to the main branch + workflow_dispatch: jobs: update_module_version: From 164dbd3edb3aae145fe2969c78c39c7a771f3a2b Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 16:55:06 -0500 Subject: [PATCH 15/18] Updating permissions on workflow. --- .github/workflows/update-module-version.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-module-version.yaml b/.github/workflows/update-module-version.yaml index dc6c130..a16d115 100644 --- a/.github/workflows/update-module-version.yaml +++ b/.github/workflows/update-module-version.yaml @@ -12,7 +12,10 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} permissions: - contents: write + contents: write + issues: read + checks: write + pull-requests: write steps: - name: checkout From 9a1888ece48098d439cabda28a3e2448ebad93c6 Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 16:56:02 -0500 Subject: [PATCH 16/18] adding name to workflow. --- .github/workflows/update-module-version.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/update-module-version.yaml b/.github/workflows/update-module-version.yaml index a16d115..909bc28 100644 --- a/.github/workflows/update-module-version.yaml +++ b/.github/workflows/update-module-version.yaml @@ -8,6 +8,7 @@ on: jobs: update_module_version: + name: Update Module Version runs-on: ubuntu-latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From b8d13d94aef5d003f04e4e0ee67631ab20bc6929 Mon Sep 17 00:00:00 2001 From: kormic911 Date: Sun, 7 Sep 2025 17:06:50 -0500 Subject: [PATCH 17/18] Adding custom token to allow for PR creation. --- .github/workflows/update-module-version.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-module-version.yaml b/.github/workflows/update-module-version.yaml index 909bc28..afb9148 100644 --- a/.github/workflows/update-module-version.yaml +++ b/.github/workflows/update-module-version.yaml @@ -45,4 +45,5 @@ jobs: commit-message: Automated Release Tagging for ${{ steps.version_tracker.outputs.version }} in DevSetup.psd1 title: Automated Release Tagging for ${{ steps.version_tracker.outputs.version }} in DevSetup.psd1 body: Changing version to ${{ steps.version_tracker.outputs.version }} - branch: update-release-version-to-${{ steps.version_tracker.outputs.version }} \ No newline at end of file + branch: update-release-version-to-${{ steps.version_tracker.outputs.version }} + token: ${{ secrets.WORKFLOW_TOKEN }} \ No newline at end of file From 6f05329ba7774abeee83af6fe74533f56ae33043 Mon Sep 17 00:00:00 2001 From: kormic911 <2898792+kormic911@users.noreply.github.com> Date: Sun, 7 Sep 2025 22:07:59 +0000 Subject: [PATCH 18/18] Automated Release Tagging for 1.0.9 in DevSetup.psd1 --- DevSetup/DevSetup.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DevSetup/DevSetup.psd1 b/DevSetup/DevSetup.psd1 index 5b547d5..6602e1f 100644 --- a/DevSetup/DevSetup.psd1 +++ b/DevSetup/DevSetup.psd1 @@ -12,7 +12,7 @@ RootModule = 'DevSetup.psm1' # Version number of this module. -ModuleVersion = '1.0.8' +ModuleVersion = '1.0.9' # Supported PSEditions # CompatiblePSEditions = @()