MyProfile.psm1
# References: # 1. Below are the list of predefined vars that can be used: # - $PSScriptRoot [System defined] The folder path for current scipt file, NOT the caller script to call this function . "$PSScriptRoot\Common.ps1" if (-not ("ddrr.PowerShell.MyProfileModule.MyProfile" -as [Type])) { Add-Type @" namespace ddrr.PowerShell.MyProfileModule { public class MyProfile { public string Name { get; set; } public int LoadOrder { get; set; } public string Path { get; set; } public bool Imported { get; set; } public bool ManualRegistered { get; set; } public bool AutoRegistered { get; set; } } } "@ } ############################################################################### # MyProfile private functions ############################################################################### <# .SYNOPSIS Check whether the specified path is a valid MyProfile. #> function Test-MyProfile { param( [Parameter(Position=0)] [string] $MyProfilePath ) # Check the path exists or not if ([string]::IsNullOrWhiteSpace($MyProfilePath)) { return $false } if (-NOT (Test-Path $MyProfilePath)) { return $false } # Check the manifest $manifestFilePath = Join-Path $MyProfilePath $MyProfileRelativeManifestPath if (-NOT(Test-Path $manifestFilePath)) { return $false } return $true } <# .SYNOPSIS Get the list of paths for my profile that has been imported. #> function Get-ImportededMyProfilePaths { return Get-EnvironmentListVariable -Name $MyProfileImportedListVarName } <# .SYNOPSIS Get the list of manual registered my profile paths. #> function Get-ManualRegisteredMyProfilePaths { return Get-EnvironmentListVariable -Name $MyProfileManualRegisteredListVarName } <# .SYNOPSIS Get the list of auto registered my profile paths. #> function Get-AutoRegisteredMyProfilePaths { $ret = @() # Default 1: SynologyDrive $ret += "$($env:USERPROFILE)\SynologyDrive\MyProfile" # Default 2: OneDrive - Personal $ret += "$($env:USERPROFILE)\OneDrive\MyProfile" # Default 3: OneDrive - <Business> Get-ChildItem -Path $env:USERPROFILE -Filter "OneDrive - *" | % { $ret += "$($env:USERPROFILE)\$($_.Name)\MyProfile" } return $ret } function Get-MyProfileManifest { param( [Parameter(Position=0)] [string] $MyProfilePath ) $manifestFilePath = Join-Path $MyProfilePath $MyProfileRelativeManifestPath return Import-PowerShellDataFile $manifestFilePath } ############################################################################### # MyProfile public functions ############################################################################### <# .SYNOPSIS Import all registered MyProfiles. #> function Import-MyProfiles { [CmdletBinding()] param() $profilePaths = @() $binPaths = @() $psModulesPaths = @() Get-MyProfile | ? { $_.ManualRegistered -OR $_.AutoRegistered } | % { # Import single MyProfile Write-ColorHost "Importing $($_.Name)" -Type Keynote # Register MyProfile path. This will support PowerShell Profile, System Profile, Cron $profilePaths += $_.Path # Register Bin path $binPaths += Join-Path $_.Path $MyProfileRelativeBinPath # Register PowerShell Modules $psModulesPaths += Join-Path $_.Path $MyProfileRelativePSMoudlesPath # Register Toolbox # Invoke PSProfile and PSModules for current MyProfile Invoke-MyProfile $_.Path -PSProfile -PSModules } # Save paths to environment variables Set-EnvironmentListVariable -Name $MyProfileImportedListVarName -ListValue $profilePaths -Target User Set-EnvironmentListVariable -Name $MyProfileBinPathVarName -ListValue $binPaths -Target User Set-EnvironmentListVariable -Name $MyProfilePSModulesPathVarName -ListValue $psModulesPaths -Target User # Invoke SysProfile for all MyProfiles together Invoke-MyProfile -SysProfile } function Invoke-MyProfile { [CmdletBinding()] param( [Parameter(Position=0)] [string] $Name, [switch] $SysProfile, [switch] $PSProfile, [switch] $PSModules ) if ((-NOT $SysProfile) -AND (-NOT $PSProfile) -AND (-NOT $PSModules)) { $SysProfile = $true $PSProfile = $true $PSModules = $true } $sysProfiles = @() # Note, only allow to invoke imported MyProfile. Get-MyProfile $Name | ? { $_.Imported } | % { if ($PSProfile) { $curPSProfilePath = Join-Path $_.Path $MyProfileRelativePSProfilePath # Note, use operator '.' here to invoke all MyPrfile's PowerShell profiles in the same scope of the MyProfile's PSProfile. # So, these PowerShell profiles can impact each other, and the MyProfile load order is important. . Invoke-ScriptInMyProfileModuleCallerScope $curPSProfilePath } if ($PSModules) { Import-MyModule -MyProfileName $_.Path -Force } # For SysProfile, it should not impact current PS scope, so they will not be invoked via operator '.' # But similar to PSProfiles, all SysProfiles should share the same scope. So they will be not invoked here. Instead, they will be invoked together later. # This is also consistent with the normal behavior when system startup: SysProfiles are invoked after all PSProfiles are invoked. if ($SysProfile) { $sysProfiles += Join-Path $_.Path $MyProfileRelativeSysProfilePath } } # NOT use operator '.' here to avoid all MyProfile's system profiles impact current PS scope. # But these SysProfiles will share the same scope, so the MyProfile load order is important. if ($sysProfiles.Count -gt 0){ Invoke-ScriptInMyProfileModuleCallerScope $sysProfiles } } function New-MyProfile { [CmdletBinding()] param( [Parameter(Position=0)] [string] $Path = '' ) if ([string]::IsNullOrWhiteSpace($Path)) { $Path = '.'} if (Test-Path $Path) { $Path = Resolve-Path $Path if ((Get-ChildItem $Path).Count -gt 0) { Write-ColorHost "The path for a new MyProfile must be a new path or an empty folder. Fail to create new MyProfile at '$Path'." -Type Error return } } else { New-Item -Path $Path -ItemType Directory | Out-Null } Copy-Item -Path "$MyProfileModuleTemplateRootPath\*" -Destination $Path Register-MyProfile -Path $Path Write-ColorHost "A new MyProfile has been created at '<Green>$Path</Green>'. You can update the <Green>$MyProfileRelativeManifestPath</Green> file to setup the details." -Type Highlight } function Get-MyProfile { [CmdletBinding()] param( [Parameter(Position=0)] [string] $Name ) [array]$importedPathList = Get-ImportededMyProfilePaths [array]$manualRegisteredPathList = Get-ManualRegisteredMyProfilePaths [array]$autoRegisteredPathList = Get-AutoRegisteredMyProfilePaths $isPathMode = $false if ((-NOT [string]::IsNullOrWhiteSpace($Name)) -AND (Test-Path $Name)) { [array]$profilePathList = @($Name) $isPathMode = $true } else { [array]$profilePathList = $manualRegisteredPathList + $autoRegisteredPathList + $importedPathList } [array]$profiles = $profilePathList | ? { Test-MyProfile $_ } | % { (Resolve-Path $_).Path.TrimEnd('\') } | Sort-Object -Unique | % { $curManifest = Get-MyProfileManifest $_ if ($isPathMode -OR ([string]::IsNullOrWhiteSpace($Name) -OR ($curManifest.Name -like $Name))) { $curProfile = New-Object ddrr.PowerShell.MyProfileModule.MyProfile $curProfile.Name = if ([string]::IsNullOrWhiteSpace($curManifest.Name)){ $_ } else { $curManifest.Name } $curProfile.LoadOrder = $curManifest.LoadOrder $curProfile.Path = $_ $curProfile.Imported = $importedPathList -contains $_ $curProfile.ManualRegistered = $manualRegisteredPathList -contains $_ $curProfile.AutoRegistered = $autoRegisteredPathList -contains $_ $curProfile } } return $profiles | Sort-Object LoadOrder,Path } function Register-MyProfile { [CmdletBinding()] param( [Parameter(Position=0)] [string] $Path ) if (Test-MyProfile $Path) { $Path = (Resolve-Path $Path).Path.TrimEnd('\') Add-EnvironmentListVariableValues -Name $MyProfileManualRegisteredListVarName -Target User -ValuesToAppend $Path } else { Write-ColorHost "'Failed to register $Path' as it is not a valid MyProfile." -Type Error } } function Unregister-MyProfile { [CmdletBinding()] param( [Parameter(Position=0)] [string] $Path ) if (Test-Path $Path) { $PathWithSlash = Join-Path $Path '\' -Resolve } else { $PathWithSlash = if ($Path[-1] -eq '\') { $Path } else { "$Path\" } } $PathWithoutSlash = $PathWithSlash.TrimEnd('\') Remove-EnvironmentListVariableValues -Name $MyProfileManualRegisteredListVarName -Target User -ValuesToRemove @($PathWithSlash, $PathWithoutSlash) } function Import-MyModule { [CmdletBinding()] param( [Parameter(Position=0)] [string] $Name, [string] $MyProfileName, [switch] $Force ) if ((-NOT [string]::IsNullOrWhiteSpace($Name)) -AND (Test-Path $Name)) { # Note: When $Name is a path to a module, will not check whether the corresponding MyProfile has been imported or not, as the MyProfile may not try { Import-Module $Name -Global -Force:$Force -ErrorAction Stop if (-NOT [string]::IsNullOrWhiteSpace($MyProfileName)) { Write-ColorHost "[$MyProfileName] " -NoNewLine -Type Highlight } Write-ColorHost "Imported my module '$Name'" -Type Highlight } catch { Write-ColorHost "Failed to import my module '$Name'" -Type Error Write-Host $_ } } else { Get-MyProfile | ? { $_.Imported } | ? { [string]::IsNullOrWhiteSpace($MyProfileName) -OR (($_.Name -like $MyProfileName) -OR ($_.Path -eq $MyProfileName)) } | % { $curMyProfile = $_ $psModulesPath = Join-Path $_.Path $MyProfileRelativePSMoudlesPath if (Test-Path $psModulesPath -PathType Container) { Get-ChildItem $psModulesPath -Directory | ? { -NOT($_.Name -like "Template*") } | ? { [string]::IsNullOrWhiteSpace($Name) -OR ($_.Name -like $Name) } | % { Import-MyModule -Name (Join-Path $psModulesPath $_.Name) -MyProfileName $curMyProfile.Name -Force:$Force } } } } } ############################################################################### # Alias ############################################################################### Set-Alias -Name Start-MySysProfile -Value $MyProfileModuleSysProfilePath |