diff --git a/.github/workflows/run-lint.yml b/.github/workflows/run-lint.yml
new file mode 100644
index 0000000..c8c2dd3
--- /dev/null
+++ b/.github/workflows/run-lint.yml
@@ -0,0 +1,30 @@
+name: Run PSScriptAnalyzer Tests
+on:
+ pull_request:
+ branches:
+ - develop
+ - main
+ workflow_dispatch:
+
+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..732a8a0
--- /dev/null
+++ b/.github/workflows/run-unit-tests.yml
@@ -0,0 +1,80 @@
+name: Run Pester Tests
+on:
+ pull_request:
+ branches:
+ - develop
+ - main
+ workflow_dispatch:
+
+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/.github/workflows/update-module-version.yaml b/.github/workflows/update-module-version.yaml
index 7e92541..afb9148 100644
--- a/.github/workflows/update-module-version.yaml
+++ b/.github/workflows/update-module-version.yaml
@@ -4,14 +4,19 @@ on:
push:
branches:
- develop # Trigger on pushes to the main branch
+ workflow_dispatch:
jobs:
update_module_version:
+ name: Update Module Version
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
permissions:
- contents: write
+ contents: write
+ issues: read
+ checks: write
+ pull-requests: write
steps:
- name: checkout
@@ -29,8 +34,16 @@ 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 }}
+ token: ${{ secrets.WORKFLOW_TOKEN }}
\ No newline at end of file
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 = @()
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..80b8c04 100644
--- a/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1
+++ b/DevSetup/Private/Commands/Export-DevSetupEnv.Tests.ps1
@@ -1,15 +1,35 @@
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
- Mock Get-DevSetupEnvPath { "TestDrive:\DevSetupEnvs" }
- Mock Get-DevSetupLocalEnvPath { "TestDrive:\DevSetupEnvs\local"}
- Mock Get-DevSetupCommunityEnvPath { "TestDrive:\DevSetupEnvs\community"}
+ . (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" }
+ 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 { }
+ Mock Write-StatusMessage { }
}
Describe "Export-DevSetupEnv" {
@@ -17,34 +37,66 @@ 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" }
- Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "exported to" -and $ForegroundColor -eq "Green" }
+ 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-StatusMessage -Scope It -ParameterFilter { $Message -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" }
- Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "exported to" -and $ForegroundColor -eq "Green" }
+ 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-StatusMessage -Scope It -ParameterFilter { $Message -match "exported to" -and $ForegroundColor -eq "Green" }
}
}
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"
- Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "sanitized" -and $ForegroundColor -eq "Yellow" }
+ 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-StatusMessage -Scope It -ParameterFilter { $Message -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"
- Assert-MockCalled Write-Host -Scope It -ParameterFilter { $Object -match "sanitized" -and $ForegroundColor -eq "Yellow" }
+ 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-StatusMessage -Scope It -ParameterFilter { $Message -match "sanitized" -and $ForegroundColor -eq "Yellow" }
}
}
@@ -53,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 b30fa3b..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.
@@ -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')) {
@@ -78,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
@@ -96,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
+ $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.Tests.ps1 b/DevSetup/Private/Commands/Initialize-DevSetup.Tests.ps1
index 2906d50..e8d3038 100644
--- a/DevSetup/Private/Commands/Initialize-DevSetup.Tests.ps1
+++ b/DevSetup/Private/Commands/Initialize-DevSetup.Tests.ps1
@@ -1,11 +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
+ . (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 { }
@@ -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..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,53 +62,55 @@
#>
Function Initialize-DevSetup {
- try {
+ 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
-
+
# Check if .devsetup folder exists
if (-not (Test-Path -Path $devSetupPath)) {
#Write-Host "Creating .devsetup directory at: $devSetupPath" -ForegroundColor Cyan
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 "`n- 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-Host ""
+ Write-StatusMessage "`nPath 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)`n" -ForegroundColor Gray
# 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..a1fe22c 100644
--- a/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1
+++ b/DevSetup/Private/Commands/Install-DevSetupEnv.Tests.ps1
@@ -1,11 +1,25 @@
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
- Mock Get-DevSetupEnvPath { "C:\DevSetupEnvs" }
+ . (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) {
+ 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..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
@@ -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,20 +81,20 @@ 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 {
+ 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]')) {
@@ -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..63eb0e9 100644
--- a/DevSetup/Private/Commands/Show-DevSetupEnvList.Tests.ps1
+++ b/DevSetup/Private/Commands/Show-DevSetupEnvList.Tests.ps1
@@ -1,13 +1,26 @@
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
- Mock Get-DevSetupPath { "C:\DevSetup" }
+ . (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) {
+ 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/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/Commands/Uninstall-DevSetupEnv.Tests.ps1 b/DevSetup/Private/Commands/Uninstall-DevSetupEnv.Tests.ps1
index 3dbcbbf..478e95c 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/Export-InstalledChocolateyPackages.ps1 b/DevSetup/Private/Providers/Chocolatey/Export-InstalledChocolateyPackages.ps1
index c259a60..55dc2b4 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.
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..f0b8e03 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.
@@ -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/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/Providers/Chocolatey/Install-ChocolateyPackages.ps1 b/DevSetup/Private/Providers/Chocolatey/Install-ChocolateyPackages.ps1
index 3583f32..d50e744 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.
diff --git a/DevSetup/Private/Providers/Chocolatey/Uninstall-ChocolateyPackages.ps1 b/DevSetup/Private/Providers/Chocolatey/Uninstall-ChocolateyPackages.ps1
index 35943c1..5939ac0 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.
diff --git a/DevSetup/Private/Providers/Core/Install-CoreDependencies.Tests.ps1 b/DevSetup/Private/Providers/Core/Install-CoreDependencies.Tests.ps1
index 8673921..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 { }
@@ -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
@@ -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
diff --git a/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1 b/DevSetup/Private/Providers/Core/Install-CoreDependencies.ps1
index 008c6bc..5ef3ceb 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.
@@ -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.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
new file mode 100644
index 0000000..8124e9f
--- /dev/null
+++ b/DevSetup/Private/Providers/Homebrew/Get-HomebrewCacheFile.ps1
@@ -0,0 +1,13 @@
+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
new file mode 100644
index 0000000..6596d9c
--- /dev/null
+++ b/DevSetup/Private/Providers/Homebrew/Get-HomebrewVersion.ps1
@@ -0,0 +1,37 @@
+Function Get-HomebrewVersion {
+ [CmdletBinding()]
+ [OutputType([string])]
+ Param(
+ )
+
+ 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
+ }
+
+ $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
+ }
+ } 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..85368e4
--- /dev/null
+++ b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.Tests.ps1
@@ -0,0 +1,202 @@
+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 {
+ Param($Name)
+ switch($Name) {
+ "SHELL" { return "/bin/bash" }
+ "HOME" { return "/home/testuser" }
+ }
+ }
+ 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 {
+ Param($Name)
+ switch($Name) {
+ "SHELL" { return "/bin/zsh" }
+ "HOME" { return "/home/testuser" }
+ }
+ }
+ 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 {
+ Param($Name)
+ switch($Name) {
+ "SHELL" { return "/bin/fish" }
+ "HOME" { return "/home/testuser" }
+ }
+ }
+ 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 {
+ Param($Name)
+ switch($Name) {
+ "SHELL" { return "/bin/bash" }
+ "HOME" { return "/home/testuser" }
+ }
+ }
+ 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 {
+ Param($Name)
+ switch($Name) {
+ "SHELL" { return "/bin/zsh" }
+ "HOME" { return "/Users/TestUser" }
+ }
+ }
+ 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 b11664a..11885bb 100644
--- a/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1
+++ b/DevSetup/Private/Providers/Homebrew/Install-Homebrew.ps1
@@ -1,23 +1,62 @@
Function Install-Homebrew {
[CmdletBinding()]
+ [OutputType([bool])]
Param(
)
- # Install Homebrew
- Write-StatusMessage "- Installing Homebrew package manager" -ForegroundColor Gray -Indent 2 -Width 77 -NoNewline
- if (-not (Get-Command "brew" -ErrorAction SilentlyContinue)) {
- # 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)) {
+ 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
+ }
+
+ # 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 ([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 ([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 {
+ 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
new file mode 100644
index 0000000..03f2f4f
--- /dev/null
+++ b/DevSetup/Private/Providers/Homebrew/Install-HomebrewPackage.ps1
@@ -0,0 +1,45 @@
+Function Install-HomebrewPackage {
+ [CmdletBinding(SupportsShouldProcess=$true)]
+ [OutputType([bool])]
+ Param(
+ [Parameter(Mandatory=$true, Position=0, ParameterSetName="Install")]
+ [Parameter(Mandatory=$true, Position=0, ParameterSetName="InstallMinimumVersion")]
+ [string]$PackageName,
+ [Parameter(Mandatory=$true, Position=1, ParameterSetName="InstallMinimumVersion")]
+ [string]$MinimumVersion
+ )
+
+ 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.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
new file mode 100644
index 0000000..b3579c4
--- /dev/null
+++ b/DevSetup/Private/Providers/Homebrew/Invoke-HomebrewComponentsExport.ps1
@@ -0,0 +1,80 @@
+Function Invoke-HomebrewComponentsExport {
+ [CmdletBinding(SupportsShouldProcess = $true)]
+ [OutputType([bool])]
+ Param(
+ [Parameter(Mandatory = $true)]
+ [string]$Config,
+ [Parameter(Mandatory = $false)]
+ [string]$OutFile
+ )
+
+ $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
+ if ($PSCmdlet.ShouldProcess($outputFile, "Out-File")) {
+ $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.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-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.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/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.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/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-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-HomebrewInstalled.ps1 b/DevSetup/Private/Providers/Homebrew/Test-HomebrewInstalled.ps1
new file mode 100644
index 0000000..14617a8
--- /dev/null
+++ b/DevSetup/Private/Providers/Homebrew/Test-HomebrewInstalled.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.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
new file mode 100644
index 0000000..abfafd5
--- /dev/null
+++ b/DevSetup/Private/Providers/Homebrew/Test-HomebrewPackageInstalled.ps1
@@ -0,0 +1,39 @@
+Function Test-HomebrewPackageInstalled {
+ [CmdletBinding()]
+ [OutputType([InstalledState])]
+ Param(
+ [Parameter(Mandatory=$true, Position=0)]
+ [string]$PackageName,
+ [Parameter(Mandatory=$false, Position=1)]
+ [Version]$MinimumVersion
+ )
+
+ [InstalledState]$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
+ $PackageStatus += [InstalledState]::RequiredVersionMet
+ $PackageStatus += [InstalledState]::GlobalVersionMet
+ }
+ } 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.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/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.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
new file mode 100644
index 0000000..9f1852f
--- /dev/null
+++ b/DevSetup/Private/Providers/Homebrew/Write-HomebrewCache.ps1
@@ -0,0 +1,30 @@
+Function Write-HomebrewCache {
+ [CmdletBinding()]
+ [OutputType([void])]
+ 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/Export-InstalledPowershellModules.ps1 b/DevSetup/Private/Providers/Powershell/Export-InstalledPowershellModules.ps1
index 65b1f91..bd1e7f7 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.
diff --git a/DevSetup/Private/Providers/Powershell/Install-PowershellModules.Tests.ps1 b/DevSetup/Private/Providers/Powershell/Install-PowershellModules.Tests.ps1
index 4ec0b6d..f497f86 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 {}
@@ -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/Install-PowershellModules.ps1 b/DevSetup/Private/Providers/Powershell/Install-PowershellModules.ps1
index 2b18c4e..bfb1c47 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.
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/Powershell/Uninstall-PowershellModules.Tests.ps1 b/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.Tests.ps1
index 17471fb..03f53c7 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 { }
@@ -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 = @{
diff --git a/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.ps1 b/DevSetup/Private/Providers/Powershell/Uninstall-PowershellModules.ps1
index 50b4563..563910a 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.
diff --git a/DevSetup/Private/Providers/Scoop/Export-InstalledScoopPackages.ps1 b/DevSetup/Private/Providers/Scoop/Export-InstalledScoopPackages.ps1
index db2107d..9e97e9a 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.
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/Install-ScoopComponents.ps1 b/DevSetup/Private/Providers/Scoop/Install-ScoopComponents.ps1
index cf5aa56..fb4b812 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.
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/Providers/Scoop/Uninstall-ScoopComponents.ps1 b/DevSetup/Private/Providers/Scoop/Uninstall-ScoopComponents.ps1
index d2b2f6e..4012b20 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.
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/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/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/Initialize-DevSetupEnvs.Tests.ps1 b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1
index 0b5b19b..bac7a36 100644
--- a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1
+++ b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.Tests.ps1
@@ -1,14 +1,16 @@
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
+
+ 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" }
@@ -108,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]" }
}
}
@@ -118,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 d8e29ee..8d97788 100644
--- a/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1
+++ b/DevSetup/Private/Utils/Initialize-DevSetupEnvs.ps1
@@ -1,4 +1,4 @@
-Function Initialize-DevSetupEnvs {
+Function Initialize-DevSetupEnvs {
try {
# Define environments repository path
$environmentsPath = Get-DevSetupEnvPath
@@ -21,7 +21,7 @@ Function Initialize-DevSetupEnvs {
# 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
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..8273bbe 100644
--- a/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1
+++ b/DevSetup/Private/Utils/Optimize-DevSetupEnvs.ps1
@@ -1,9 +1,12 @@
-Function Optimize-DevSetupEnvs {
+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/runSecurity.ps1 b/runSecurity.ps1
new file mode 100644
index 0000000..f40f775
--- /dev/null
+++ b/runSecurity.ps1
@@ -0,0 +1,11 @@
+#-IncludeRule @("PSAvoidUsingInvokeExpression", "PSAvoidUsingConvertToSecureStringWithPlainText") `
+Set-PSRepository PSGallery -InstallationPolicy Trusted
+Install-Module PSScriptAnalyzer -ErrorAction Stop
+Install-Module ConvertToSARIF -ErrorAction Stop
+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
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