-
Notifications
You must be signed in to change notification settings - Fork 5
/
nuget-powershell.psm1
451 lines (381 loc) · 14.6 KB
/
nuget-powershell.psm1
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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
<#
.SYNOPSIS
This module aimes to help similify consuming NuGet packages from PowerShell. To find the commands
made available you can use.
Get-Command -module nuget-powershell
#>
function Get-ScriptDirectory
{
$Invocation = (Get-Variable MyInvocation -Scope 1).Value
Split-Path $Invocation.MyCommand.Path
}
$scriptDir = ((Get-ScriptDirectory) + "\")
$global:NuGetPowerShellSettings = New-Object PSObject -Property @{
cachePath = "$env:LOCALAPPDATA\LigerShark\nuget-ps\v1.1\"
nugetDownloadUrl = 'http://nuget.org/nuget.exe'
}
function InternalGet-CachePath{
[cmdletbinding()]
param(
$cachePath = $global:NuGetPowerShellSettings.cachePath
)
process{
if(-not (Test-Path $cachePath) ){
New-Item -ItemType Directory -Path $cachePath | Out-Null
}
Get-Item $cachePath
}
}
<#
.SYNOPSIS
This will return nuget from the $cachePath. If it is not there then it
will automatically be downloaded before the call completes.
#>
function Get-Nuget{
[cmdletbinding()]
param(
$toolsDir = (InternalGet-CachePath),
$nugetDownloadUrl = $global:NuGetPowerShellSettings.nugetDownloadUrl
)
process{
if(!(Test-Path $toolsDir)){
New-Item -Path $toolsDir -ItemType Directory | out-null
}
$nugetDestPath = Join-Path -Path $toolsDir -ChildPath nuget.exe
if(!(Test-Path $nugetDestPath)){
$nugetDir = ([System.IO.Path]::GetDirectoryName($nugetDestPath))
if(!(Test-Path $nugetDir)){
New-Item -Path $nugetDir -ItemType Directory | Out-Null
}
'Downloading nuget.exe' | Write-Verbose
(New-Object System.Net.WebClient).DownloadFile($nugetDownloadUrl, $nugetDestPath) | Out-Null
# double check that is was written to disk
if(!(Test-Path $nugetDestPath)){
throw 'unable to download nuget'
}
}
# return the path of the file
$nugetDestPath
}
}
<#
.SYNOPSIS
Updates nuget.exe to the latest and then returns the path to nuget.exe.
#>
function Update-NuGet{
[cmdletbinding()]
param()
process{
$cmdArgs = @('update','-self','-NonInteractive')
$command = '"{0}" {1}' -f (Get-NuGet),($cmdArgs -join ' ')
Execute-CommandString -command $command | Write-Verbose
# return the path to nuget.exe
Get-NuGet
}
}
<#
.SYNOPSIS
Used to execute a command line tool (i.e. nuget.exe) using cmd.exe. This is needed in
some cases due to hanlding of special characters.
.EXAMPLE
Execute-CommandString -command ('"{0}" {1}' -f (Get-NuGet),(@('install','psbuild') -join ' '))
Calls nuget.exe install psbuild using cmd.exe
.EXAMPLE
'"{0}" {1}' -f (Get-NuGet),(@('install','psbuild') -join ' ') | Execute-CommandString
Calls nuget.exe install psbuild using cmd.exe
.EXAMPLE
@('psbuild','packageweb') | % { """$(Get-NuGet)"" install $_ -prerelease"|Execute-CommandString}
Calls
nuget.exe install psbuild -prerelease
nuget.exe install packageweb -prerelease
#>
function Execute-CommandString{
[cmdletbinding()]
param(
[Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]
[string[]]$command,
[switch]
$ignoreExitCode
)
process{
foreach($cmdToExec in $command){
'Executing command [{0}]' -f $cmdToExec | Write-Verbose
cmd.exe /D /C $cmdToExec
if(-not $ignoreExitCode -and ($LASTEXITCODE -ne 0)){
$msg = ('The command [{0}] exited with code [{1}]' -f $cmdToExec, $LASTEXITCODE)
throw $msg
}
}
}
}
<#
.SYNOPSIS
This will return the path to where the given NuGet package is installed. If the package
is not in the local cache then it will automatically be downloaded. All interaction with
nuget servers go through nuget.exe.
.PARAMETER name
Name of the NuGet package to be installed. This is a mandatory argument.
.PARAMETER version
Version of NuGet package to get. If this is not passed the latst version will be
returned. That will result in a call to nuget.org (or other nugetUrl as specified).
.PARAMETER prerelease
Pass this to get the prerelease version of the NuGet package.
.PARAMETER cachePath
The directory where the package will be downloaded to. This is mostly an internal
parameter but it can be used to redirect the location of the tools directory.
To override this globally you can use $global:NuGetPowerShellSettings.cachePath.
.PARAMETER nugetUrl
You can use this to download the package from a different nuget feed.
.PARAMETER binpath
When this is passed the folder where all packages are expanded into will be returned
instead of the root directory.
.PARAMETER force
Used to re-download the package from the remote nuget feed.
.EXAMPLE
Get-NuGetPackage -name psbuild
Gets the latest version of the psbuild nuget package wich is not a prerelease package
.EXAMPLE
Get-NuGetPackage -name psbuild -prerelease
Gets the latest version (including prerelase) of the psbuild nuget package.
.EXAMPLE
Get-NuGetPackage psbuild -version 0.0.5
Gets psbuild version 0.0.5
.EXAMPLE
Get-NuGetPackage psbuild -version 0.0.6-beta5
Gets psbuild version 0.0.6-beta5. When passing a value for version you don't need
to pass -prerelease, it will be used by default on all calls when version is present.
.EXAMPLE
Get-NuGetPackage psbuild -version 0.0.6-beta5 -nugetUrl https://staging.nuget.org
Downloads psbuild version 0.0.6-beta5 fro staging.nuget.org
#>
function Get-NuGetPackage{
[cmdletbinding()]
param(
[Parameter(Mandatory=$true,Position=0)]
[string]$name,
[Parameter(Position=1)] # later we can make this optional
[string]$version,
[Parameter(Position=2)]
[switch]$prerelease,
[Parameter(Position=3)]
[System.IO.DirectoryInfo]$cachePath = (InternalGet-CachePath),
[Parameter(Position=4)]
[string]$nugetUrl = ('https://nuget.org/api/v2/'),
[Parameter(Position=6)]
[switch]$binpath,
[Parameter(Position=7)]
[switch]$force
)
process{
[string]$foldername = $name
if(-not ([string]::IsNullOrWhiteSpace($version))){
$foldername = ('{0}.{1}' -f $name,$version)
}
[System.IO.DirectoryInfo]$expectedPath = (Join-Path $cachePath $foldername)
if( -not (Test-Path $expectedPath) -or
( (Get-ChildItem $expectedPath -Recurse -File).Length -le 0 ) -or
$force ) {
# install to a temp dir
[System.IO.DirectoryInfo]$tempfolder = (InternalGet-NewTempFolder)
try{
Push-Location | Out-Null
Set-Location ($tempfolder.FullName) | Out-Null
# install the nuget package here
$cmdArgs = @('install',$name)
if($version){
$cmdArgs += '-Version'
$cmdArgs += "$version"
$prerelease = $true
}
if($prerelease){
$cmdArgs += '-prerelease'
}
$cmdArgs += '-NonInteractive'
if($nugetUrl -and !([string]::IsNullOrWhiteSpace($nugetUrl))){
$cmdArgs += "-source"
$cmdArgs += $nugetUrl
}
$nugetCommand = ('"{0}" {1}' -f (Get-Nuget), ($cmdArgs -join ' ' ))
'Calling nuget to install a package with the following args. [{0}]' -f $nugetCommand | Write-Verbose
[string[]]$nugetResult = (Execute-CommandString -command $nugetCommand)
$nugetResult | Write-Verbose
# combine results into a single __bin folder
$expbinpath = (Join-Path $tempfolder '__bin')
New-Item -Path $expbinpath -ItemType Directory | Write-Verbose
# copy lib folder to bin\
Get-ChildItem $tempfolder -Directory | InternalGet-LibFolderToUse | Get-ChildItem | Copy-Item -Destination $expbinpath -Recurse -ErrorAction SilentlyContinue | Write-Verbose
# copy tools folder to __bin\
Get-ChildItem $tempfolder 'tools' -Directory -Recurse | Get-ChildItem -Exclude *.ps*1 | Copy-Item -Destination $expbinpath -Recurse -ErrorAction SilentlyContinue | Write-Verbose
# copy files to the dest folder
if($force -and (Test-Path $expectedPath)){
# delete the folder and re-install
Remove-Item $expectedPath -Recurse | Write-Verbose
}
if(-not (test-path $expectedPath)){
New-Item -ItemType Directory -Path $expectedPath | Write-Verbose
}
foreach($pathtomove in (Get-ChildItem $tempfolder -Directory)){
[System.IO.DirectoryInfo]$dest = (Join-Path $expectedPath $pathtomove.BaseName)
if(-not (Test-Path $dest.FullName)){
Move-Item $pathtomove.FullName $expectedPath | Write-Verbose
}
}
}
finally{
Pop-Location | Out-Null
# delete the temp folder
if( -not ([string]::IsNullOrWhiteSpace($tempfolder) ) -and (Test-Path $tempfolder) ){
Remove-Item -Path $tempfolder -Recurse -ErrorAction SilentlyContinue | Write-Verbose
}
}
}
# return the full path
if($binpath){
(Get-Item (Join-Path $expectedPath '__bin')).FullName
}
else{
$expectedPath.FullName
}
}
}
function InternalGet-NewTempFolder{
[cmdletbinding()]
param(
[System.IO.DirectoryInfo]$tempFolder = ([System.IO.Path]::GetTempPath())
)
process{
$foldername = [Guid]::NewGuid()
New-Item -ItemType Directory -Path (Join-Path $tempFolder $foldername) | Write-Verbose
Get-Item -Path (Join-Path $tempFolder $foldername)
}
}
function InternalGet-LibFolderToUse{
[cmdletbinding()]
param(
[Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]
[System.IO.DirectoryInfo[]]$packageInstallPath
)
process{
$libtocheck = @('lib\net45','lib\45','lib\net40','lib\40','lib\net35','lib\35','lib\net30','lib\30','lib\net20','20','lib\11','11','lib')
foreach($pkgPath in $packageInstallPath){
try{
$pkgFullPath = $pkgPath.FullName
Push-Location | Out-Null
Set-Location $pkgFullPath | out-null
$libfolder = $null
foreach($lib in $libtocheck){
if(Test-Path (Join-Path $pkgFullPath $lib)){
$libfolder = (Get-Item (Join-Path $pkgFullPath $lib)).FullName
break
}
}
$libfolder
}
finally{
Pop-Location | Out-Null
}
}
}
}
<#
.SYNOPSIS
Returns the name (including version number) of the nuget package installed from the
nuget.exe results when calling nuget.exe install.
#>
function InternalGet-PackagePathFromNuGetOutput{
[cmdletbinding()]
param(
[Parameter(Mandatory=$true,Position=0)]
[string]$nugetOutput
)
process{
if(!([string]::IsNullOrWhiteSpace($nugetOutput))){
([regex]"'[^']*'").match((($nugetOutput -split "`n")[-1])).groups[0].value.TrimStart("'").TrimEnd("'").Replace(' ','.')
}
else{
throw 'nugetOutput parameter is null or empty'
}
}
}
<#
.SYNOPSIS
When this method is called all files in the given nuget package maching *.psm1 in the tools
folder will automatically be imported using Import-Module.
.EXAMPLE
Load-ModuleFromNuGetPackage -name psbuild
Loads the psbuild module from the latest psbuild nuget package (non-prerelease).
.EXAMPLE
Load-ModuleFromNuGetPackage -name psbuild -prerelease
Loads the psbuild module from the latest psbuild nuget package (including prerelease).
.EXAMPLE
Load-ModuleFromNuGetPackage -name psbuild -prerelease -force
Loads the psbuild module from the latest psbuild nuget package (including prerelease), and the package
will be re-dowloaded instead of the cached version.
#>
function Load-ModuleFromNuGetPackage{
[cmdletbinding()]
param(
[Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]
$name,
[Parameter(Position=1)]
$version,
[Parameter(Position=2)]
[switch]$prerelease,
[Parameter(Position=3)]
$cachePath = (InternalGet-CachePath),
[Parameter(Position=4)]
$nugetUrl = ('https://nuget.org/api/v2/'),
[Parameter(Position=5)]
[switch]$force
)
process{
$pkgDir = Get-NuGetPackage -name $name -version $version -prerelease:$prerelease -nugetUrl $nugetUrl -force:$force -binpath
$modules = (Get-ChildItem ("$pkgDir\tools") '*.psm1' -ErrorAction SilentlyContinue)
foreach($module in $modules){
$moduleFile = $module.FullName
$moduleName = $module.BaseName
if(Get-Module $moduleName){
Remove-Module $moduleName | out-null
}
'Loading module from [{0}]' -f $moduleFile | Write-Verbose
Import-Module $moduleFile -DisableNameChecking -Global -Force
}
}
}
function Get-NuGetPackageExpectedPath{
[cmdletbinding()]
param(
[Parameter(Mandatory=$true,Position=0)]
$name,
[Parameter(Mandatory=$true,Position=1)] # later we can make this optional
$version,
[Parameter(Position=2)]
$cachePath = (InternalGet-CachePath),
[Parameter(Position=3)]
[switch]$expandedPath
)
process{
$pathToFoundPkgFolder = $null
$cachePath=(get-item $cachePath).FullName
if(!$expandedPath){
(join-path $cachePath (('{0}.{1}' -f $name, $version)))
}
else{
(join-path $cachePath (('expanded\{0}{1}\{0}.{1}' -f $name, $version)))
}
}
}
function Get-NuGetPowerShellVersion{
param()
process{
New-Object -TypeName 'system.version' -ArgumentList '0.2.5.1'
}
}
if(!$env:IsDeveloperMachine){
Export-ModuleMember -function Get-*,Set-*,Invoke-*,Save-*,Test-*,Find-*,Add-*,Remove-*,Test-*,Open-*,New-*,Execute-*,Update-*,Load-*
}
else{
# you can set the env var to expose all functions to importer. easy for development.
# this is required for pester testing
Export-ModuleMember -function *
}