LocalModules.psm1
<#
.SYNOPSIS LocalModules simplifies the installation of modules under development. .DESCRIPTION LocalModules creates and manages a local repository named 'Developing' behind the scenes, streamlining the installation process for modules in develompment. .NOTES Inspired by ideas and tips from the following article: "Local PowerShell Module Repository – No Server Required" Author: Marc-André Moreau Published: March 2021 Source: https://blog.devolutions.net/2021/03/local-powershell-module-repository-no-server-required #> $moduleName = "LocalModules" # Create module folder into roaming directory if not exist New-Item -Path $env:APPDATA -Name "$ModuleName" -ItemType Directory -ErrorAction SilentlyContinue # Set configuration file # $configPath = Join-Path -Path $env:APPDATA -ChildPath $ModuleName 'config.json' if (-not (Test-Path -Path $configPath -PathType Leaf)) { # Create an empty config.json file if not exist [PSCustomObject]@{DevModulesPath = ''} | ConvertTo-Json | Set-Content -Path $configPath -Encoding UTF8 } # Import config.json $config = Get-Content -Path $configPath -Raw | ConvertFrom-Json function Test-Configuration { # Missing configuration value warning if (-not (Test-Path -Path $config.DevModulesPath -PathType Container)) { Write-Host "Missing configuration value for DevModulesPath`n" -ForegroundColor DarkYellow Write-Host "Edit the configuration file at $configPath and set the correct path to the folder containing your modules under development." -ForegroundColor DarkYellow Exit 67 } } # Create DevRepository folder if not exist $localRepoPath = Join-Path -Path $env:APPDATA -ChildPath $ModuleName 'DevelopingRepo' New-Item -Path $localRepoPath -ItemType Directory -ErrorAction SilentlyContinue | Out-Null # Register the local Developing repository $parameters = @{ Name = "Developing" SourceLocation = $localRepoPath InstallationPolicy = "Trusted" ErrorAction = "SilentlyContinue" } Register-PSRepository @parameters function Install-DevModule { <# .SYNOPSIS Installs the specified module under development. .DESCRIPTION This cmdlet ensure a smooth installation by creating a minimal module manifest if missing, removing any traces of a previously installed version, and managing the local repository behind the scenes to install the latest modified version of the module under development. #> [CmdletBinding( DefaultParameterSetName='NameParameterSet', SupportsShouldProcess=$true, ConfirmImpact='Medium')] param( [Parameter( ParameterSetName='NameParameterSet', Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)] [ValidateNotNullOrEmpty()] [string] ${Name}) begin { try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Install-Module', [System.Management.Automation.CommandTypes]::Function) # Dev-module code start # Test-Configuration $devModuleName = $Name # Test if dev-module exist $devModulePath = Join-Path -Path $config.DevModulesPath -ChildPath $devModuleName $devScriptPath = Join-Path -Path $devModulePath -ChildPath "$devModuleName.psm1" if (-not (Test-Path -Path $devScriptPath)) { Write-Host "Under development module not found" -ForegroundColor Red return } # Create minimal dev-module manifest if not exist $devManifestPath = Join-Path -Path $devModulePath -ChildPath "$devModuleName.psd1" if (-not (Test-Path -Path $devManifestPath)) { $parameters = @{ Path = $devManifestPath RootModule = ".\$devModuleName.psm1" ModuleVersion = '0.0.1' Author = "You Developer" Description = "Default Dev minimal manifest" } New-ModuleManifest @parameters Write-Host "Under development module manifest missing ..." -ForegroundColor DarkYellow Write-Host "Minimal module manifest created" -ForegroundColor DarkYellow } # Unpublish (remove) from local repository previous dev-module if exist $publishedDevModule = Find-Module -Name $devModuleName -Repository Developing -ErrorAction SilentlyContinue if ($publishedDevModule) { Remove-Item -Path (Join-Path $localRepoPath "$devModuleName.*.nupkg") -Force } # Publish to local repository current dev-module Publish-Module -Path $devModulePath -Repository Developing -WarningAction SilentlyContinue # Uninstall previous dev-module if installed Uninstall-Module -Name $devModuleName -ErrorAction SilentlyContinue # Set Developing as repository before calling Install-Module $PSBoundParameters += @{'Repository'= 'Developing'} $scriptCmd = {& $wrappedCmd @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline() $steppablePipeline.Begin($PSCmdlet) } catch { throw } } process { try { $steppablePipeline.Process($_) } catch { throw } } end { try { $steppablePipeline.End() exit 1 } catch { throw } } clean { if ($null -ne $steppablePipeline) { $steppablePipeline.Clean() } } } function Get-InstalledDevModule { <# .SYNOPSIS Gets a list of modules installed from the local 'Developing' repository on the computer. #> [CmdletBinding()] param( [Parameter(Position=0, ValueFromPipelineByPropertyName=$true)] [ValidateNotNullOrEmpty()] [string[]] ${Name}) begin { try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { $PSBoundParameters['OutBuffer'] = 1 } function command { Get-InstalledModule | Where-Object {$_.Repository -eq 'Developing'} } $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand("command", [System.Management.Automation.CommandTypes]::Function) $scriptCmd = {& $wrappedCmd @PSBoundParameters } $steppablePipeline = $scriptCmd.GetSteppablePipeline() $steppablePipeline.Begin($PSCmdlet) } catch { throw } } process { try { $steppablePipeline.Process($_) } catch { throw } } end { try { $steppablePipeline.End() } catch { throw } } clean { if ($null -ne $steppablePipeline) { $steppablePipeline.Clean() } } } |