Repository navigation
Expand file tree
/
Copy pathPublish-PSMDScriptFile.ps1
More file actions
224 lines (197 loc) · 7.65 KB
/
Copy pathPublish-PSMDScriptFile.ps1
File metadata and controls
224 lines (197 loc) · 7.65 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
function Publish-PSMDScriptFile
{
<#
.SYNOPSIS
Packages a script with all dependencies and "publishes" it as a zip package.
.DESCRIPTION
Packages a script with all dependencies and "publishes" it as a zip package.
By default, it will be published to the user's desktop.
All modules it uses will be parsed from the script:
- Commands that cannot be resolved will trigger a warning.
- Modules that are installed in the Windows folder (such as the ActiveDirectory module or other modules associated with server roles) will be ignored.
- PSSnapins will be ignored
- All other modules determined by the commands used will be provided from a repository, packaged in a subfolder and included in the zip file.
If needed, the scriptfile will be modified to add the new modules folder to its list of known folders.
(The source file itself will never be modified)
Use Set-PSMDStagingRepository to create / use a local path for staging modules to provide that way.
This gives you better control over the versions used and better performance.
Also the ability to use this with non-public modules.
Use Publish-PSMDStagedModule to transfer modules from path or another repository into your registered staging repository.
.PARAMETER Path
Path to the scriptfile to publish.
The scriptfile is expected to be UTF8 encoded with BOM, otherwise some characters may end up broken.
.PARAMETER OutPath
The path to the folder where the output zip file will be created.
Defaults to the user's desktop.
.PARAMETER EnableException
This parameters disables user-friendly warnings and enables the throwing of exceptions.
This is less user friendly, but allows catching exceptions in calling scripts.
.EXAMPLE
PS C:\> Publish-PSMDScriptFile -Path 'C:\scripts\logrotate.ps1'
Creates a delivery package for the logrotate.ps1 scriptfile and places it on the desktop
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[PsfValidateScript('PSModuleDevelopment.Validate.File', ErrorString = 'PSModuleDevelopment.Validate.File')]
[string]
$Path,
[PsfValidateScript('PSModuleDevelopment.Validate.Path', ErrorString = 'PSModuleDevelopment.Validate.Path')]
[string]
$OutPath = (Get-PSFConfigValue -FullName 'PSModuleDevelopment.Script.OutPath'),
[switch]
$EnableException
)
begin
{
#region Utility Functions
function Get-Modifier
{
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]
$Path
)
$help = Get-Help $Path
$modifiers = $help.alertSet.alert.Text -split "`n" | Where-Object { $_ -like "PSMD: *" } | ForEach-Object { $_ -replace '^PSMD: ' }
foreach ($modifier in $modifiers)
{
$operation, $values = $modifier -split ":"
switch ($operation)
{
'Include'
{
foreach ($module in $values.Split(",").ForEach{ $_.Trim() })
{
[pscustomobject]@{
Type = 'Include'
Name = $module
}
}
}
'Exclude'
{
foreach ($module in $values.Split(",").ForEach{ $_.Trim() })
{
[pscustomobject]@{
Type = 'Exclude'
Name = $module
}
}
}
'IgnoreUnknownCommand'
{
foreach ($commandName in $values.Split(",").ForEach{ $_.Trim() })
{
[pscustomobject]@{
Type = 'IgnoreCommand'
Name = $commandName
}
}
}
}
}
}
function Add-PSModulePath
{
[CmdletBinding()]
param (
[string]
$Path
)
$psmodulePathCode = @'
# Ensure modules are available
$modulePath = "$PSScriptRoot\Modules"
if (-not $env:PSModulePath.Contains($modulePath)) { $env:PSModulePath = "$($env:PSModulePath);$($modulePath)" }
'@
$parsedFile = Read-PSMDScript -Path $Path
$assignment = $parsedFile.Ast.FindAll({
$args[0] -is [System.Management.Automation.Language.AssignmentStatementAst] -and
$args[0].Left.VariablePath.UserPath -eq 'env:PSModulePath'
}, $true)
if ($assignment) { return }
if ($parsedFile.Ast.ParamBlock.Extent)
{
$paramExtent = $parsedFile.Ast.ParamBlock.Extent
$text = [System.IO.File]::ReadAllText($Path)
$newText = $text.Substring(0, $paramExtent.EndOffset) + $psmodulePathCode + $text.Substring($paramExtent.EndOffset)
$encoding = [System.Text.UTF8Encoding]::new($true)
[System.IO.File]::WriteAllText($Path, $newText, $encoding)
}
else
{
$extent = $parsedFile.Ast.EndBlock.Statements[0].Extent
$text = [System.IO.File]::ReadAllText($Path)
$textBefore = ""
$textAfter = $text
if ($extent.StartOffset -gt 0)
{
$textBefore = $text.Substring(0, $extent.StartOffset)
$textAfter = $text.Substring($extent.StartOffset)
}
$newText = $textBefore + $psmodulePathCode + $textAfter
$encoding = [System.Text.UTF8Encoding]::new($true)
[System.IO.File]::WriteAllText($Path, $newText, $encoding)
}
}
#endregion Utility Functions
$modulesToProcess = @{
IgnoreCommand = @()
Include = @()
Exclude = @()
}
}
process
{
#region Prepare required Modules
# Scan help-notes for explicit directives
$modifiers = Get-Modifier -Path $Path
foreach ($modifier in $modifiers)
{
$modulesToProcess.$($modifier.Type) += $modifier.Name
}
# Detect modules needed and store them
try { $parsedCommands = Get-PSMDFileCommand -Path $Path -EnableException }
catch
{
Stop-PSFFunction -String 'Publish-PSMDScriptFile.Script.ParseError' -StringValues $Path -EnableException $EnableException -ErrorRecord $_
return
}
foreach ($command in $parsedCommands)
{
Write-PSFMessage -Level Verbose -String 'Publish-PSMDScriptFile.Script.Command' -StringValues $command.Name, $command.Count, $command.Module
if ($modulesToProcess.IgnoreCommand -contains $command.Name) { continue }
if (-not $command.Module -and -not $command.Internal)
{
Write-PSFMessage -Level Warning -String 'Publish-PSMDScriptFile.Script.Command.NotKnown' -StringValues $command.Name, $command.Count
continue
}
if ($modulesToProcess.Exclude -contains "$($command.Module)") { continue }
if ($modulesToProcess.Include -contains "$($command.Module)") { continue }
if ($command.Module -is [System.Management.Automation.PSSnapInInfo]) { continue }
if ($command.Module.ModuleBase -like 'C:\Windows\System32\WindowsPowerShell\v1.0*') { continue }
if ($command.Module.ModuleBase -like 'C:\Program Files\PowerShell\7*') { continue }
$modulesToProcess.Include += "$($command.Module)"
}
$tempPath = Get-PSFPath -Name Temp
$newPath = New-Item -Path $tempPath -Name "PSMD_$(Get-Random)" -ItemType Directory -Force
$modulesFolder = New-Item -Path $newPath.FullName -Name 'Modules' -ItemType Directory -Force
foreach ($moduleLabel in $modulesToProcess.Include | Select-Object -Unique)
{
if (-not $moduleLabel) { continue }
Invoke-PSFProtectedCommand -ActionString 'Publish-PSMDScriptFile.Module.Saving' -ActionStringValues $moduleLabel, (Get-PSFConfigValue -FullName 'PSModuleDevelopment.Script.StagingRepository') -Scriptblock {
Save-Module -Name $moduleLabel -Repository (Get-PSFConfigValue -FullName 'PSModuleDevelopment.Script.StagingRepository') -Path $modulesFolder.FullName -ErrorAction Stop
} -EnableException $EnableException -PSCmdlet $PSCmdlet -Target $moduleLabel
if (Test-PSFFunctionInterrupt) { return }
}
#endregion Prepare required Modules
# Copy script file
$newScript = Copy-Item -Path $Path -Destination $newPath.FullName -PassThru
# Update script to set PSModulePath
Add-PSModulePath -Path $newScript.FullName
# Zip result & move to destination
Compress-Archive -Path "$($newPath.FullName)\*" -DestinationPath ('{0}\{1}.zip' -f $OutPath, $newScript.BaseName) -Force
Remove-Item -Path $newPath.FullName -Recurse -Force -ErrorAction Ignore
}
}