Public/Install-ModuleFromLocalRepo.ps1

function Install-ModuleFromLocalRepo {
    <#
    .SYNOPSIS
        Install PowerShell modules from local PowerShell repositories.
 
    .DESCRIPTION
        This function installs modules from registered local PowerShell repositories.
        By default, it searches all registered repositories with local file paths.
        Can also install from a specific folder path containing NuPkg files.
 
    .PARAMETER RepositoryName
        Name of a specific registered PowerShell repository to search. If not specified,
        searches all registered repositories with local file system paths.
 
    .PARAMETER Path
        Path to a folder containing NuPkg files. Use this to install directly from
        a folder without requiring repository registration.
 
    .PARAMETER Force
        Force reinstallation even if the module is already installed.
 
    .PARAMETER PassThru
        Return information about the installed module.
 
    .PARAMETER PackageName
        Specify a specific package name to install without showing the menu.
 
    .EXAMPLE
        Install-ModuleFromLocalRepo
         
        Searches all registered local repositories and presents a selection menu.
 
    .EXAMPLE
        Install-ModuleFromLocalRepo -RepositoryName "LocalNuGetRepo"
         
        Installs from the specific registered repository.
 
    .EXAMPLE
        Install-ModuleFromLocalRepo -Path "C:\MyPackages" -Force
         
        Installs directly from a folder path, bypassing repository registration.
 
    .EXAMPLE
        Install-ModuleFromLocalRepo -PackageName "MyModule" -RepositoryName "LocalNuGetRepo"
         
        Directly installs MyModule from the specified repository.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Repository', SupportsShouldProcess)]
    param(
        [Parameter(ParameterSetName = 'Repository')]
        [string]$RepositoryName,
        
        [Parameter(ParameterSetName = 'Path', Position = 0)]
        [ValidateScript({
            if (-not (Test-Path -Path $_ -PathType Container)) {
                throw "Path '$_' does not exist or is not a directory."
            }
            $true
        })]
        [string]$Path,
        
        [Parameter()]
        [switch]$Force,
        
        [Parameter()]
        [switch]$PassThru,
        
        [Parameter()]
        [string]$PackageName
    )

    try {
        # Determine installation source
        if ($PSCmdlet.ParameterSetName -eq 'Repository') {
            # Use registered PowerShell repositories
            if ($RepositoryName) {
                # Install from specific repository
                $repositories = Get-PSRepository -Name $RepositoryName -ErrorAction SilentlyContinue
                if (-not $repositories) {
                    Write-Error "Repository '$RepositoryName' not found. Use Get-PSRepository to list available repositories."
                    return
                }
            } else {
                # Find all local file-based repositories
                $repositories = Get-PSRepository | Where-Object { 
                    $_.SourceLocation -match '^[A-Za-z]:\\' -or $_.SourceLocation -match '^\\\\' 
                }
                if (-not $repositories) {
                    Write-Warning "No local file-based repositories found. Use New-LocalRepository to create one, or specify -Path to install from a folder."
                    return
                }
            }
            
            Write-Host "Searching registered repositories..." -ForegroundColor Cyan
            foreach ($repo in $repositories) {
                Write-Host " - $($repo.Name): $($repo.SourceLocation)" -ForegroundColor Gray
            }
            
            # Install using PowerShell's native module installation
            if ($PackageName) {
                $repoNames = $repositories.Name -join ', '
                Write-Host "Installing $PackageName from repository: $repoNames" -ForegroundColor Yellow
                
                $installParams = @{
                    Name = $PackageName
                    Repository = $repositories.Name
                    Scope = 'CurrentUser'
                    Force = $Force.IsPresent
                }
                
                if ($PSCmdlet.ShouldProcess($PackageName, "Install module from repository")) {
                    try {
                        # Suppress the misleading "No match was found" error that occurs even when installation succeeds
                        $ErrorActionPreference = 'SilentlyContinue'
                        $WarningPreference = 'SilentlyContinue'
                        
                        Install-Module @installParams -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
                        
                        # Reset error preferences
                        $ErrorActionPreference = 'Continue'
                        $WarningPreference = 'Continue'
                        
                        # Verify installation success by checking if module is available
                        $installedModule = Get-Module -Name $PackageName -ListAvailable | Select-Object -First 1
                        if ($installedModule) {
                            Write-Host "Successfully installed $PackageName" -ForegroundColor Green
                            
                            if ($PassThru) {
                                return $installedModule
                            }
                        } else {
                            Write-Error "Failed to install $PackageName - module not found after installation attempt"
                        }
                    } catch {
                        # Only show actual critical errors
                        if ($_.Exception.Message -notmatch "No match was found") {
                            Write-Error "Failed to install $PackageName`: $($_.Exception.Message)"
                        } else {
                            # Check if module was installed despite the error
                            $installedModule = Get-Module -Name $PackageName -ListAvailable | Select-Object -First 1
                            if ($installedModule) {
                                Write-Host "Successfully installed $PackageName" -ForegroundColor Green
                                if ($PassThru) {
                                    return $installedModule
                                }
                            } else {
                                Write-Error "Failed to install $PackageName"
                            }
                        }
                    }
                }
            } else {
                # List available modules from repositories
                Write-Host "Finding available modules..." -ForegroundColor Cyan
                $availableModules = @()
                
                foreach ($repo in $repositories) {
                    try {
                        $modules = Find-Module -Repository $repo.Name -ErrorAction SilentlyContinue
                        foreach ($module in $modules) {
                            $availableModules += [PSCustomObject]@{
                                Name = $module.Name
                                Version = $module.Version
                                Repository = $repo.Name
                                Description = $module.Description
                            }
                        }
                    } catch {
                        Write-Verbose "Could not find modules in repository $($repo.Name): $($_.Exception.Message)"
                    }
                }
                
                if ($availableModules) {
                    Write-Host "Available modules:" -ForegroundColor Green
                    for ($i = 0; $i -lt $availableModules.Count; $i++) {
                        $module = $availableModules[$i]
                        Write-Host " [$($i + 1)] $($module.Name) v$($module.Version) [$($module.Repository)]" -ForegroundColor White
                    }
                    
                    $choice = Read-Host "`nEnter the number of the module to install (or 'q' to quit)"
                    if ($choice -eq 'q') {
                        Write-Host "Installation cancelled." -ForegroundColor Yellow
                        return
                    }
                    
                    if ($choice -match '^\d+$' -and [int]$choice -ge 1 -and [int]$choice -le $availableModules.Count) {
                        $selectedModule = $availableModules[[int]$choice - 1]
                        
                        if ($PSCmdlet.ShouldProcess($selectedModule.Name, "Install module from repository")) {
                            try {
                                # Suppress misleading errors
                                $ErrorActionPreference = 'SilentlyContinue'
                                $WarningPreference = 'SilentlyContinue'
                                
                                Install-Module -Name $selectedModule.Name -Repository $selectedModule.Repository -Scope CurrentUser -Force:$Force.IsPresent -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
                                
                                # Reset preferences
                                $ErrorActionPreference = 'Continue'
                                $WarningPreference = 'Continue'
                                
                                # Verify installation
                                $installedModule = Get-Module -Name $selectedModule.Name -ListAvailable | Select-Object -First 1
                                if ($installedModule) {
                                    Write-Host "Successfully installed $($selectedModule.Name) v$($selectedModule.Version)" -ForegroundColor Green
                                    
                                    if ($PassThru) {
                                        return $installedModule
                                    }
                                } else {
                                    Write-Error "Failed to install $($selectedModule.Name) - module not found after installation"
                                }
                            } catch {
                                if ($_.Exception.Message -notmatch "No match was found") {
                                    Write-Error "Failed to install $($selectedModule.Name): $($_.Exception.Message)"
                                } else {
                                    # Check if it installed despite the error
                                    $installedModule = Get-Module -Name $selectedModule.Name -ListAvailable | Select-Object -First 1
                                    if ($installedModule) {
                                        Write-Host "Successfully installed $($selectedModule.Name) v$($selectedModule.Version)" -ForegroundColor Green
                                        if ($PassThru) {
                                            return $installedModule
                                        }
                                    } else {
                                        Write-Error "Failed to install $($selectedModule.Name)"
                                    }
                                }
                            }
                        }
                    } else {
                        Write-Warning "Invalid selection. Installation cancelled."
                    }
                } else {
                    Write-Warning "No modules found in the specified repositories."
                }
            }
        } else {
            # Use legacy folder-based installation
            Write-Host "Scanning for NuPkg files in: $Path" -ForegroundColor Cyan
        
        # Find all .nupkg files
        $nupkgFiles = Get-ChildItem -Path $Path -Filter "*.nupkg" -File
        
        if (-not $nupkgFiles) {
            Write-Warning "No NuPkg files found in: $Path"
            return
        }
        
        Write-Host "Found $($nupkgFiles.Count) NuPkg file(s)" -ForegroundColor Gray
        
        # List the files found for debugging
        Write-Verbose "NuPkg files found:"
        foreach ($file in $nupkgFiles) {
            Write-Verbose " - $($file.Name) ($($file.FullName))"
        }
        
        # Parse package information
        $packages = @{}
        foreach ($file in $nupkgFiles) {
            Write-Verbose "Processing file: $($file.Name)"
            $pkgInfo = Get-NuPkgInfo -FilePath $file.FullName
            if ($pkgInfo) {
                $packageName = $pkgInfo.Name
                Write-Verbose "Parsed package: $packageName v$($pkgInfo.VersionString)"
                
                # Keep only the newest version of each package
                if (-not $packages.ContainsKey($packageName) -or $pkgInfo.Version -gt $packages[$packageName].Version) {
                    Write-Verbose "Adding/updating package: $packageName v$($pkgInfo.VersionString)"
                    $packages[$packageName] = $pkgInfo
                } else {
                    Write-Verbose "Skipping older version: $packageName v$($pkgInfo.VersionString) (current: v$($packages[$packageName].VersionString))"
                }
            } else {
                Write-Verbose "Failed to parse package info for: $($file.Name)"
            }
        }
        
        if ($packages.Count -eq 0) {
            Write-Warning "No valid NuPkg files found (could not parse package information)"
            return
        }
        
        Write-Verbose "Parsed $($packages.Count) unique packages:"
        foreach ($pkg in $packages.Values) {
            Write-Verbose " - $($pkg.Name) v$($pkg.VersionString) ($($pkg.Size) MB)"
        }
        
        # Select package
        $selectedPackage = $null
        
        if ($PackageName) {
            # Direct package selection
            if ($packages.ContainsKey($PackageName)) {
                $selectedPackage = $packages[$PackageName]
                Write-Host "Selected package: $($selectedPackage.Name) v$($selectedPackage.VersionString)" -ForegroundColor Green
            } else {
                Write-Error "Package '$PackageName' not found in repository. Available packages: $($packages.Keys -join ', ')"
                return
            }
        } else {
            # Interactive selection menu
            Write-Host "`nAvailable packages (newest versions):" -ForegroundColor Green
            Write-Host "=====================================" -ForegroundColor Green
            
            $packageList = @($packages.Values | Sort-Object Name)
            Write-Verbose "Package list contains $($packageList.Count) items"
            
            for ($i = 0; $i -lt $packageList.Count; $i++) {
                $pkg = $packageList[$i]
                Write-Verbose "Displaying package $($i+1): $($pkg.Name) v$($pkg.VersionString) ($($pkg.Size) MB)"
                Write-Host " [$($i + 1)] $($pkg.Name) v$($pkg.VersionString) ($($pkg.Size) MB)" -ForegroundColor White
            }
            
            Write-Host " [0] Exit" -ForegroundColor Gray
            Write-Host ""
            
            # Get user selection
            do {
                $selection = Read-Host "Select package to install (1-$($packageList.Count), 0 to exit)"
                
                if ($selection -eq "0") {
                    Write-Host "Installation cancelled." -ForegroundColor Yellow
                    return
                }
                
                $selectionInt = $null
                $isValidNumber = [int]::TryParse($selection, [ref]$selectionInt)
                
                if ($isValidNumber -and $selectionInt -ge 1 -and $selectionInt -le $packageList.Count) {
                    $selectedPackage = $packageList[$selectionInt - 1]
                    break
                } else {
                    Write-Host "Invalid selection. Please enter a number between 1 and $($packageList.Count), or 0 to exit." -ForegroundColor Red
                }
            } while ($true)
        }
        
        # Confirm installation
        $userModulesPath = Get-UserModulePath
        Write-Host "`nSelected package: $($selectedPackage.Name) v$($selectedPackage.VersionString)" -ForegroundColor Green
        Write-Host "Installation location: $userModulesPath" -ForegroundColor Gray
        
        if ($PSCmdlet.ShouldProcess($selectedPackage.Name, "Install PowerShell module to current user scope")) {
            $installResult = Install-NuPkgModule -PackageInfo $selectedPackage -DestinationPath $userModulesPath -Force:$Force
            
            if ($installResult) {
                Write-Host "`n[SUCCESS] Module installation completed!" -ForegroundColor Green
                
                # Show how to use the module
                Write-Host "`nTo use the module, run:" -ForegroundColor Cyan
                Write-Host " Import-Module $($selectedPackage.Name) -Force" -ForegroundColor White
                Write-Host " Get-Command -Module $($selectedPackage.Name)" -ForegroundColor White
                
                # Return module information if requested
                if ($PassThru) {
                    return Get-Module -Name $selectedPackage.Name -ListAvailable | Where-Object Version -eq $selectedPackage.VersionString
                }
            } else {
                Write-Error "Module installation failed."
            }
        }
        } # End of legacy folder-based installation
        
    } catch {
        Write-Error "Script execution failed: $($_.Exception.Message)"
        Write-Debug $_.ScriptStackTrace
    }
}