Fixers/Repair-ModuleManifest.ps1

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
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
            
        }
    }
}