Fixers/Repair-ModuleManifest.ps1
function Repair-ModuleManifest { param( # The Rule that flagged the problem [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] [ValidateScript({ if ($_ -isnot [Management.Automation.CommandInfo] -and $_ -isnot [Management.Automation.PSModuleInfo] ) { throw 'Must be a CommandInfo or a PSModuleInfo' } return $true })] [Object]$Rule, # The Problem [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] [Management.Automation.ErrorRecord] $Problem, # The Item with the Problem [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] [ValidateScript({ if ($_ -isnot [Management.Automation.CommandInfo] -and $_ -isnot [Management.Automation.PSModuleInfo] ) { throw 'Must be a CommandInfo or a PSModuleInfo' } return $true })] [Object]$ItemWithProblem, [Switch]$NotInteractive ) begin { function Write-PowerShellHashtable { <# .Synopsis Takes an existing Hashtable and creates the script you would need to embed to recreate the hashtable .Description Allows you to take a hashtable and create a hashtable you would embed into a script. Handles nested hashtables and automatically indents hashtables based off of how many times New-PowerShellHashtable is called .Parameter inputObject The hashtable to turn into a script .Parameter scriptBlock Determines if a string or a scriptblock is returned .Example # Corrects the presentation of a PowerShell hashtable @{Foo='Bar';Baz='Bing';Boo=@{Bam='Blang'}} | New-PowerShellHashtable .ReturnValue [string] .ReturnValue [ScriptBlock] .Link about_hash_tables #> param( [Parameter(Position=0,ValueFromPipelineByPropertyName=$true)] [PSObject] $InputObject, # Returns the content as a script block, rather than a string [switch]$scriptBlock ) process { $callstack = @(Get-PSCallStack | Where-Object { $_.Command -eq "Write-PowerShellHashtable"}) $depth = $callStack.Count if ($inputObject -is [Hashtable]) { $scriptString = "" $indent = $depth * 4 $scriptString+= "@{ " foreach ($kv in $inputObject.GetEnumerator()) { $indent = ($depth + 1) * 4 for($i=0;$i -lt $indent; $i++) { $scriptString+=" " } $keyString = $kv.Key if ($keyString -notlike "*.*" -and $keyString -notlike "*-*") { $scriptString+="$($kv.Key)=" } else { $scriptString+="'$($kv.Key)'=" } $value = $kv.Value Write-Verbose "$value" if ($value -is [string]) { $value = "'$value'" } elseif ($value -is [ScriptBlock]) { $value = "{$value}" } elseif ($value -is [Object[]]) { $oldOfs = $ofs $ofs = "', $(' ' * ($indent + 4))'" $value = "'$value'" $ofs = $oldOfs } elseif ($value -is [Hashtable]) { $value = "$(Write-PowerShellHashtable $value)" } else { $value = "'$value'" } $scriptString+="$value " } $indent = $depth * 4 for($i=0;$i -lt $indent; $i++) { $scriptString+=" " } $scriptString+= "}" if ($scriptBlock) { [ScriptBlock]::Create($scriptString) } else { $scriptString } } } } } process { if ($Problem.FullyQualifiedErrorId -notlike "TestModuleManifestQuality.*") { return } $ModuleRoot = $ItemWithProblem | Split-Path $modulePath = $ItemWithProblem | Split-Path -Leaf $manifestPath = Join-Path $moduleRoot "$($ItemWithProblem.Name).psd1" if (Test-Path $ManifestPath) { $manifestContent = ([PowerShell]::Create().AddScript(" `$executionContext.SessionState.LanguageMode = 'RestrictedLanguage' $([IO.File]::ReadAllText($ManifestPath)) ").Invoke())[0] $manifestMetaData = @{} + $manifestContent } $module = $ItemWithProblem if ($Problem.FullyQualifiedErrorId -like 'TestModuleManifestQuality.NoManifest*') { # Generate a Module Manifest with version 0.1, pointing to the path of the module $newManifest = @" @{ ModuleVersion='0.1' Guid='$([GUID]::NewGuid())' ModuleToProcess='$modulePath' } "@ [IO.File]::WriteAllText($ManifestPath, $newManifest) return TriedToFixProblem 'TestModuleManifestQuality.NoManifest' } if (-not $manifestMetaData) { return } if ($Problem.FullyQualifiedErrorId -like 'TestModuleManifestQuality.MissingFileList*') { # Take what's in the manifest, and add a file list if (-not $manifestMetaData) { return CouldNotFixProblem 'TestModuleManifestQuality.MissingFileList' } else { $manifestMetaData.FileList = $ModuleRoot | Get-ChildItem -Recurse | Where-Object { -not $_.PSIsContainer } | Select-Object -ExpandProperty FullName | ForEach-Object { $_.Replace("$ModuleRoot\", "") } Write-PowerShellHashtable -InputObject $manifestMetaData | Set-Content $ManifestPath return TriedToFixProblem 'TestModuleManifestQuality.MissingFileList' -FixRequiresRescan } } if ($Problem.FullyQualifiedErrorId -like 'TestModuleManifestQuality.MissingGuid*') { # Take what's in the manifest, and add a GUID $manifestMetaData.GUID = [GUID]::NewGuid() Write-PowerShellHashtable -InputObject $manifestMetaData | Set-Content $ManifestPath return TriedToFixProblem 'TestModuleManifestQuality.MissingGuid' -FixRequiresRescan } if ($Problem.FullyQualifiedErrorId -like 'TestModuleManifestQuality.MissingCopyrightNotice*') { # Take what's in the manifest, and add a GUID $manifestMetaData.Copyright = "Copyright $((Get-Date).Year)" Write-PowerShellHashtable -InputObject $manifestMetaData | Set-Content $ManifestPath return TriedToFixProblem 'TestModuleManifestQuality.MissingCopyrightNotice' -FixRequiresRescan } if ($problem.FullyQualifiedErrorId -like 'TestModuleManifestQuality.MissingDescription*') { if ($NonInteractive) { # Could Fix, but can't because I can't ask return CouldNotFixProblem 'TestModuleManifestQuality.MissingDescription' } else { $description = Read-Host -Prompt "What Does the Module $($ItemWithProblem) do?" $manifestMetaData.Description = $description Write-PowerShellHashtable -InputObject $manifestMetaData | Set-Content $ManifestPath return TriedToFixProblem 'TestModuleManifestQuality.MissingDescription' -FixRequiresRescan } } if ($problem.FullyQualifiedErrorId -like 'TestModuleManifestQuality.MissingAuthor*') { if ($NonInteractive) { # Assume current user $manifestMetaData.Author = $env:UserName } else { $author = Read-Host -Prompt "Who wrote the module $module ?" $manifestMetaData.Author = $author } Write-PowerShellHashtable -InputObject $manifestMetaData | Set-Content $ManifestPath return TriedToFixProblem 'TestModuleManifestQuality.MissingAuthor' -FixRequiresRescan } } } |