-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodule-loader.ps1
More file actions
272 lines (230 loc) · 10.1 KB
/
module-loader.ps1
File metadata and controls
272 lines (230 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
param([switch]$VerboseSwitch = $false)
# $Verbose=$true -or $VerboseSwitch
$Verbose=$VerboseSwitch
$script = $MyInvocation.MyCommand
<#
.SYNOPSIS
Loads PowerShell modules defined in a YAML configuration file.
.DESCRIPTION
Reads modules.yml (or the path in $env:SnippetsModulesYaml), installs any
missing modules from PSGallery, and imports them into the current session.
Requires the powershell-yaml module for YAML parsing (auto-installed if missing).
#>
function Install-YamlModuleIfMissing {
param([switch]$Verbose = $false)
try {
$yamlMod = Get-Module -ListAvailable -Name 'powershell-yaml' -ErrorAction SilentlyContinue
if (-not $yamlMod) {
Write-Verbose "[$script] Installing powershell-yaml from PSGallery..." -Verbose:$Verbose
Install-Module -Name 'powershell-yaml' -Scope CurrentUser -Force -AllowClobber -AcceptLicense -ErrorAction Stop
}
Import-Module 'powershell-yaml' -ErrorAction Stop -Verbose:$false
return $true
}
catch {
Write-Warning "[$script] powershell-yaml unavailable: $_"
return $false
}
}
function Import-SnippetsModules {
param([switch]$Verbose = $false)
# Determine YAML config path
$yamlPath = $env:SnippetsModulesYaml
if (-not $yamlPath -or -not (Test-Path $yamlPath)) {
$yamlPath = Join-Path $env:Snippets -ChildPath 'modules.yml'
}
if (-not (Test-Path $yamlPath)) {
Write-Verbose "[$script] No modules.yml found at [$yamlPath]. Skipping module auto-load." -Verbose:$Verbose
return "No modules.yml found."
}
Write-Verbose "[$script] Loading module definitions from [$yamlPath]" -Verbose:$Verbose
# Ensure powershell-yaml is available
if (-not (Install-YamlModuleIfMissing -Verbose:$Verbose)) {
return "Module auto-loader disabled: powershell-yaml unavailable."
}
# Parse YAML
$yamlContent = Get-Content -Path $yamlPath -Raw -ErrorAction Stop
$config = ConvertFrom-Yaml $yamlContent -ErrorAction Stop
if (-not $config -or -not $config.modules) {
Write-Verbose "[$script] modules.yml is empty or has no 'modules' key." -Verbose:$Verbose
return "No modules defined."
}
$loaded = 0
$failed = 0
$moduleStatuses = [System.Collections.Generic.List[PSCustomObject]]::new()
foreach ($entry in $config.modules) {
$moduleName = $entry.name
$moduleVersion = $entry.version
$moduleSource = if ($entry.source) { $entry.source } else { 'PSGallery' }
$moduleRequired = if ($null -ne $entry.required) { $entry.required } else { $true }
$moduleParams = $entry.parameters
if (-not $moduleName) {
Write-Verbose "[$script] Skipping entry with no 'name' field." -Verbose:$Verbose
continue
}
# Skip powershell-yaml since we already loaded it above
if ($moduleName -ieq 'powershell-yaml') {
$loaded++
$moduleStatuses.Add([PSCustomObject]@{ Module = $moduleName; Status = 'Loaded' })
continue
}
try {
Write-Verbose "[$script] Processing module [$moduleName]..." -Verbose:$Verbose
# Check if already imported
$existing = Get-Module -Name $moduleName -ErrorAction SilentlyContinue
if ($existing) {
$parsedVer = $null
if (-not $moduleVersion -or (-not [Version]::TryParse(($moduleVersion -replace '-.*$',''), [ref]$parsedVer)) -or $existing.Version -ge $parsedVer) {
Write-Verbose "[$script] Module [$moduleName] already imported." -Verbose:$Verbose
$loaded++
$moduleStatuses.Add([PSCustomObject]@{ Module = $moduleName; Status = 'Already Loaded' })
continue
}
}
# Check if installed locally
$installed = Get-Module -ListAvailable -Name $moduleName -ErrorAction SilentlyContinue
$needsInstall = $false
if (-not $installed) {
$needsInstall = $true
}
elseif ($moduleVersion) {
$parsedMinVer = $null
if ([Version]::TryParse(($moduleVersion -replace '-.*$',''), [ref]$parsedMinVer)) {
$bestVersion = ($installed | Sort-Object Version -Descending | Select-Object -First 1).Version
if ($bestVersion -lt $parsedMinVer) {
$needsInstall = $true
}
}
}
# Install if needed
if ($needsInstall) {
if ($moduleSource -ine 'PSGallery' -and (Test-Path $moduleSource)) {
Write-Verbose "[$script] Importing [$moduleName] from path [$moduleSource]" -Verbose:$Verbose
}
else {
Write-Verbose "[$script] Installing [$moduleName] from PSGallery..." -Verbose:$Verbose
$installParams = @{
Name = $moduleName
Scope = 'CurrentUser'
Force = $true
AllowClobber = $true
AcceptLicense = $true
ErrorAction = 'Stop'
}
if ($moduleVersion) {
$installParams['MinimumVersion'] = $moduleVersion
}
Install-Module @installParams
}
}
# Import the module
$importParams = @{
Name = if ($moduleSource -ine 'PSGallery' -and (Test-Path $moduleSource)) { $moduleSource } else { $moduleName }
ErrorAction = 'Stop'
Verbose = $false
}
if ($moduleVersion -and $moduleSource -ieq 'PSGallery') {
$importParams['MinimumVersion'] = $moduleVersion
}
if ($moduleParams) {
$importParams['ArgumentList'] = $moduleParams
}
Import-Module @importParams
Write-Verbose "[$script] Loaded [$moduleName] successfully." -Verbose:$Verbose
$loaded++
$moduleStatuses.Add([PSCustomObject]@{ Module = $moduleName; Status = 'Loaded' })
}
catch {
$failed++
$statusLabel = if ($moduleRequired) { 'Failed (required)' } else { 'Failed (optional)' }
$moduleStatuses.Add([PSCustomObject]@{ Module = $moduleName; Status = $statusLabel })
if ($moduleRequired) {
Write-Error "[$script] Failed to load required module [$moduleName]: $_"
}
else {
Write-Verbose "[$script] Optional module [$moduleName] failed to load: $_" -Verbose:$Verbose
}
}
}
if ($moduleStatuses.Count -gt 0) {
Write-Host "`nLoaded modules from $([System.IO.Path]::GetFileName($yamlPath)):" -ForegroundColor Cyan
foreach ($entry in $moduleStatuses) {
$color = switch -Wildcard ($entry.Status) {
'Loaded' { 'Green' }
'Already Loaded' { 'DarkGreen' }
'Failed*' { 'Red' }
default { 'Gray' }
}
Write-Host (" {0,-40} {1}" -f $entry.Module, $entry.Status) -ForegroundColor $color
}
Write-Host ""
}
return "Module auto-load complete: $loaded loaded, $failed failed."
}
function Reload-SnippetsModule {
param(
[Parameter(Position = 0)][string]$ModuleName = '',
[switch]$VerboseSwitch = $false
)
if ($ModuleName) {
# Reload a single module by name
Remove-Module -Name $ModuleName -Force -ErrorAction SilentlyContinue
Import-Module -Name $ModuleName -Force -ErrorAction Stop -Verbose:$false
Write-Verbose "[$script] Reloaded [$ModuleName]." -Verbose:$VerboseSwitch
return "Reloaded module: $ModuleName"
}
else {
# Reload all modules from YAML
$yamlPath = $env:SnippetsModulesYaml
if (-not $yamlPath -or -not (Test-Path $yamlPath)) {
$yamlPath = Join-Path $env:Snippets -ChildPath 'modules.yml'
}
if (-not (Test-Path $yamlPath)) {
return "No modules.yml found."
}
$yamlContent = Get-Content -Path $yamlPath -Raw -ErrorAction Stop
$config = ConvertFrom-Yaml $yamlContent -ErrorAction Stop
if (-not $config -or -not $config.modules) {
return "No modules defined."
}
$reloaded = 0
foreach ($entry in $config.modules) {
$name = $entry.name
if (-not $name) { continue }
$source = if ($entry.source -and $entry.source -ine 'PSGallery' -and (Test-Path $entry.source)) { $entry.source } else { $name }
try {
Remove-Module -Name $name -Force -ErrorAction SilentlyContinue
Import-Module -Name $source -Force -ErrorAction Stop -Verbose:$false
Write-Verbose "[$script] Reloaded [$name]." -Verbose:$VerboseSwitch
$reloaded++
}
catch {
$isRequired = if ($null -ne $entry.required) { $entry.required } else { $true }
if ($isRequired) {
Write-Error "[$script] Failed to reload required module [$name]: $_"
}
else {
Write-Verbose "[$script] Optional module [$name] failed to reload: $_" -Verbose:$VerboseSwitch
}
}
}
return "Reloaded $reloaded modules from YAML."
}
}
try {
if ($env:SnippetsModulesLoaded) {
return "Modules already loaded."
}
$result = Import-SnippetsModules -Verbose:$Verbose
$env:SnippetsModulesLoaded = $true
Write-Verbose "[$script] $result" -Verbose:$Verbose
set-alias -Verbose:$Verbose -Scope Global -Description "Snippets: [modules] Reload a module or all YAML modules" -Name modrl -Value Reload-SnippetsModule
return $result
}
catch {
Write-Error "[$script] Module auto-loader error: $_"
}
finally {
Write-Verbose "[$script] Leaving..." -Verbose:$Verbose
$Verbose = $VerboseSwitch
}