Skip to content

Commit fc4c522

Browse files
author
Justin Grote
committed
Add Bootstrap Logic
1 parent 214a3b7 commit fc4c522

4 files changed

Lines changed: 349 additions & 337 deletions

File tree

ModuleFast.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#requires -version 7.2
2-
# This is the bootstrap script for ModuleFast. It is used to load the module and do a simple invocation if arguments were specified.
2+
# This is the bootstrap script for ModuleFast. It is used to load the module ephemerally and do a simple invocation if arguments were specified.
33

44
$moduleUri = 'https://raw.githubusercontent.com/JustinGrote/ModuleFast/main/ModuleFast.psm1'
55
Write-Debug "Fetching Modulefast from $moduleUri"

ModuleFast.psm1

Lines changed: 33 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ using namespace System.Text
1313
using namespace System.Threading
1414
using namespace System.Threading.Tasks
1515

16+
#Because we are changing state, we want to be safe
17+
#TODO: Implement logic to only fail on module installs, such that one module failure doesn't prevent others from installing.
18+
#Probably need to take into account inconsistent state, such as if a dependent module fails then the depending modules should be removed.
19+
$ErrorActionPreference = 'Stop'
20+
1621
<#
1722
.SYNOPSIS
1823
High Performance Powershell Module Installation
@@ -32,7 +37,7 @@ function Install-ModuleFast {
3237
[PSCredential]$Credential,
3338
#By default will modify your PSModulePath to use the builtin destination if not present. Setting this implicitly skips profile update as well.
3439
[Switch]$NoPSModulePathUpdate,
35-
#Setting this won't add the default destination to your profile.
40+
#Setting this won't add the default destination to your powershell.config.json. This really only matters on Windows.
3641
[Switch]$NoProfileUpdate,
3742
#Setting this will check remote if the module spec has a higher bound than any currently installed local packages.
3843
[Switch]$Update
@@ -50,17 +55,14 @@ function Install-ModuleFast {
5055
New-Item -ItemType Directory -Path $Destination -Force | Out-Null
5156
}
5257
}
53-
54-
# Should error if not present
58+
# Should error if the specified destination is not present
5559
[string]$Destination = Resolve-Path $Destination
5660

57-
if ($defaultRepoPath -ne $Destination) {
58-
if (-not $NoProfileUpdate) {
59-
Write-Warning 'Parameter -Destination is set to a custom path. We assume you know what you are doing, so it will not automatically be added to your Profile but will be added to PSModulePath. Set -NoProfileUpdate to suppress this message in the future.'
60-
}
61-
$NoProfileUpdate = $true
62-
}
6361
if (-not $NoPSModulePathUpdate) {
62+
if ($defaultRepoPath -ne $Destination -and $Destination -notin $PSModulePaths) {
63+
Write-Warning 'Parameter -Destination is set to a custom path not in your current PSModulePath. We will add it to your PSModulePath for this session. You can suppress this behavior with the -NoPSModulePathUpdate switch.'
64+
$NoProfileUpdate = $true
65+
}
6466
Add-DestinationToPSModulePath -Destination $Destination -NoProfileUpdate:$NoProfileUpdate
6567
}
6668

@@ -76,7 +78,7 @@ function Install-ModuleFast {
7678
if ($WhatIfPreference) {
7779
Write-Host -fore DarkGreen '✅ No modules found to install or all modules are already installed.'
7880
}
79-
Write-Verbose 'No modules found to install or all modules are already installed. Exiting.'
81+
Write-Verbose '✅ All required modules installed! Exiting.'
8082
return
8183
}
8284

@@ -498,7 +500,6 @@ function Get-ModuleFastPlan {
498500
}
499501
}
500502

501-
502503
#endregion Main
503504

504505
function Install-ModuleFastHelper {
@@ -545,11 +546,20 @@ function Install-ModuleFastHelper {
545546
Write-Verbose "$($context.Module): Starting Extract Job to $installPath"
546547
# This is a sync process and we want to do it in parallel, hence the threadjob
547548
$installJob = Start-ThreadJob -ThrottleLimit 8 {
548-
$zip = [IO.Compression.ZipArchive]::new($USING:stream, 'Read')
549-
[IO.Compression.ZipFileExtensions]::ExtractToDirectory($zip, $USING:installPath)
549+
param(
550+
[ValidateNotNullOrEmpty()][string]$InstallPath = $USING:installPath,
551+
[ValidateNotNullOrEmpty()]$stream = $USING:stream,
552+
[ValidateNotNullOrEmpty()]$context = $USING:context
553+
)
554+
#TODO: Add a ".incomplete" marker file to the folder and remove it when done. This will allow us to detect failed installations
555+
$zip = [IO.Compression.ZipArchive]::new($stream, 'Read')
556+
[IO.Compression.ZipFileExtensions]::ExtractToDirectory($zip, $installPath)
557+
Write-Verbose "Cleanup Nuget Files in $installPath"
558+
if (-not $installPath) { throw 'ModuleDestination was not set. This is a bug, report it' }
559+
Remove-Item -Path $installPath -Include '_rels', 'package', '*.nuspec' -Recurse -Force
550560
($zip).Dispose()
551-
($USING:stream).Dispose()
552-
return ($USING:context).Module
561+
($stream).Dispose()
562+
return ($context).Module
553563
}
554564
$installJobs.Add($installJob)
555565
}
@@ -567,31 +577,7 @@ function Install-ModuleFastHelper {
567577
}
568578
}
569579

570-
# This will be run inside a threadjob. We separate this so that we can test it independently
571-
# NOTE: This function is called in a threadjob and has context outside of what is defined here.
572-
function Install-ModuleFastOperation {
573-
param(
574-
#Name of the module to install
575-
[string]$Name,
576-
#Version of the module
577-
[string]$Version,
578-
#Path where the nuget package is stored
579-
[string]$DownloadPath,
580-
#Path where the module will be installed into a subfolder of its name and version
581-
[string]$Destination
582-
)
583-
$ErrorActionPreference = 'Stop'
584-
$ModuleDestination = Join-Path $Destination $Name $Version
585-
Write-Verbose "Installing $Name $Version from $DownloadPath to $ModuleDestination"
586-
$progressPreference = 'SilentlyContinue'
587-
Expand-Archive -Path $DownloadPath -DestinationPath $ModuleDestination -Force
588-
$progressPreference = 'Continue'
589-
Write-Verbose "Cleanup Nuget Files in $ModuleDestination"
590-
if (-not $ModuleDestination) { throw 'ModuleDestination was not set. This is a bug, report it' }
591-
Remove-Item -Path $ModuleDestination -Include '_rels', 'package', '*.nuspec' -Recurse -Force
592-
Remove-Item -LiteralPath (Join-Path $ModuleDestination '[Content_Types].xml')
593-
Write-Verbose "Installed $Name $Version from $DownloadPath to $ModuleDestination"
594-
}
580+
595581

596582
#region Classes
597583

@@ -1101,22 +1087,22 @@ Adds an existing PowerShell Modules path to the current session as well as the p
11011087
function Add-DestinationToPSModulePath {
11021088
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
11031089
param(
1104-
[string]$Destination,
1090+
[Parameter(Mandatory)][string]$Destination,
11051091
[switch]$NoProfileUpdate
11061092
)
11071093
$ErrorActionPreference = 'Stop'
11081094
$Destination = Resolve-Path $Destination #Will error if it doesn't exist
11091095

1110-
# Check if the destination is in the PSModulePath
1111-
[string[]]$modulePaths = $env:PSModulePath -split [Path]::PathSeparator
1096+
# Check if the destination is in the PSModulePath. For a default setup this should basically always be true for Mac/Linux
1097+
[string[]]$modulePaths = $env:PSModulePath.split([Path]::PathSeparator)
11121098
if ($Destination -in $modulePaths) {
11131099
Write-Debug "Destination '$Destination' is already in the PSModulePath, we will assume it is already configured correctly"
11141100
return
11151101
}
11161102

1103+
# Generally we only get this far on Windows where the default CurrentUser is in Documents
11171104
Write-Verbose "Updating PSModulePath to include $Destination"
1118-
$modulePaths += $Destination
1119-
$env:PSModulePath = $modulePaths -join [Path]::PathSeparator
1105+
$env:PSModulePath = $Destination, $env:PSModulePath -join [Path]::PathSeparator
11201106

11211107
if ($NoProfileUpdate) {
11221108
Write-Debug 'Skipping updating the profile because -NoProfileUpdate was specified'
@@ -1132,9 +1118,9 @@ function Add-DestinationToPSModulePath {
11321118
New-Item -ItemType File -Path $myProfile -Force | Out-Null
11331119
}
11341120

1135-
[string]$profileLine = "if ('$destination' -notin (`$env:PSModulePath -split [Path]::PathSeparator)) { `$env:PSModulePath += `$([IO.Path]::PathSeparator + '$Destination') } #Added by ModuleFast. DO NOT EDIT THIS LINE. If you do not want this, add -NoProfileUpdate to Install-ModuleFast."
1121+
[string]$profileLine = "if ('$Destination' -notin (`$env:PSModulePath.split([IO.Path]::PathSeparator))) { `$env:PSModulePath = '$Destination' + `$([IO.Path]::PathSeparator + '$Destination') } #Added by ModuleFast. DO NOT EDIT THIS LINE. If you do not want this, add -NoProfileUpdate to Install-ModuleFast or add the default destination to your powershell.config.json or to your PSModulePath another way."
11361122
if ((Get-Content -Raw $myProfile) -notmatch [Regex]::Escape($ProfileLine)) {
1137-
if (-not $PSCmdlet.ShouldProcess($myProfile, "Allow ModuleFast to work by adding $Destination to your PSModulePath on startup by appending to your CurrentUserAllHosts profile.")) { return }
1123+
if (-not $PSCmdlet.ShouldProcess($myProfile, "Allow ModuleFast to work by adding $Destination to your PSModulePath on startup by appending to your CurrentUserAllHosts profile. If you do not want this, add -NoProfileUpdate to Install-ModuleFast or add the specified destination to your powershell.config.json or to your PSModulePath another way.")) { return }
11381124
Write-Verbose "Adding $Destination to profile $myProfile"
11391125
Add-Content -Path $myProfile -Value "`n`n"
11401126
Add-Content -Path $myProfile -Value $ProfileLine
@@ -1338,7 +1324,6 @@ function ConvertTo-AuthenticationHeaderValue ([PSCredential]$Credential) {
13381324
function Get-StringHash ([string]$String, [string]$Algorithm = 'SHA256') {
13391325
(Get-FileHash -InputStream ([MemoryStream]::new([Encoding]::UTF8.GetBytes($String))) -Algorithm $algorithm).Hash
13401326
}
1341-
13421327
#endregion Helpers
13431328

13441329
### ISSUES

0 commit comments

Comments
 (0)