functions/Get-PackageUpdateInfo.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
function Get-PackageUpdateInfo {
    <#
    .SYNOPSIS
        Get info about up-to-dateness for installed modules
 
    .DESCRIPTION
        Get-PackageUpdateInfo query locally installed modules and compare them against the online versions for up-to-dateness
 
    .PARAMETER Name
        The name of the module to check
 
    .PARAMETER Repository
        The repository to check
 
    .PARAMETER ShowOnlyNeededUpdate
        This switch suppresses up-to-date modules from the output.
 
    .PARAMETER ShowToastNotification
        This switch invokes nice Windows-Toast-Notifications with release note information on modules with update needed.
 
    .PARAMETER CurrentUser
        Only look for modules in the current user profile.
        This is helpfully if you're running without admin right, which you should always do as your default work preference.
 
    .PARAMETER AllUsers
        Only look for modules in the AllUsers/system directories.
        Keep in mind, that admin rights are required to update those modules.
 
    .PARAMETER Force
        Force to query info about up-to-dateness for installed modules, even if the UpdateCheckInterval
        from last check is not expired.
 
    .EXAMPLE
        PS C:\> Get-PackageUpdateInfo
 
        Outputs update information for all modules (currentUser and AllUsers).
        Output can look like:
 
        Name Repository VersionInstalled VersionOnline NeedUpdate Path
        ---- ---------- ---------------- ------------- ---------- ----
        PSReadline PSGallery 1.2 1.2 False C:\Program Files\WindowsPowerShell\Modules\PSReadline
        Pester PSGallery 4.4.0 4.4.2 True C:\Program Files\WindowsPowerShell\Modules\Pester
 
    .EXAMPLE
        PS C:\> Get-PackageUpdateInfo -ShowOnlyNeededUpdate
 
        This will filter output to show only modules where NeedUpdate is True
        Output can look like:
 
        Name Repository VersionInstalled VersionOnline NeedUpdate Path
        ---- ---------- ---------------- ------------- ---------- ----
        Pester PSGallery 4.4.0 4.4.2 True C:\Program Files\WindowsPowerShell\Modules\Pester
 
    .EXAMPLE
        PS C:\> "Pester", "PSReadline" | Get-PackageUpdateInfo
 
        Pipeline is supported. This returns the infos only for the two modules "Pester", "PSReadline"
 
        This also can be done with Get-Module cmdlet:
        Get-Module "Pester", "PSReadline" | Get-PackageUpdateInfo
 
    #>

    [CmdletBinding( DefaultParameterSetName = 'DefaultSet1',
        SupportsShouldProcess = $false,
        ConfirmImpact = 'Low')]
    [Alias('gpui')]
    [OutputType([PackageUpdate.Info])]
    Param (
        [Parameter(ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true)]
        [string[]]
        $Name,

        [string[]]
        $Repository,

        [switch]
        $ShowOnlyNeededUpdate,

        [switch]
        [Alias('ToastNotification', 'Notify')]
        $ShowToastNotification,

        [Parameter(ParameterSetName = 'CurrentUser')]
        [switch]
        $CurrentUser,

        [Parameter(ParameterSetName = 'AllUsers')]
        [switch]
        $AllUsers,

        [switch]
        $Force
    )

    begin {
        if ($ShowToastNotification -and (-not $script:EnableToastNotification)) {
            Write-Verbose -Message "System is not able to do Toast Notifications" -Verbose
        }

        # Doing checks if the update check against the modules is done
        $moduleSetting = Get-PackageUpdateSetting
        if (-not $Force) {
            if ($moduleSetting.LastCheck -gt $moduleSetting.LastSuccessfulCheck) {
                $effectiveCheckDate = $moduleSetting.LastCheck
            } else {
                $effectiveCheckDate = $moduleSetting.LastSuccessfulCheck
            }

            if ( ($effectiveCheckDate + $moduleSetting.UpdateCheckInterval) -ge (Get-Date) ) {
                Write-Warning -Message "Skip checking for updates on modules, due to last check happens at $($effectiveCheckDate.ToShortTimeString()) and minimum UpdateCheckInterval is set to $($moduleSetting.UpdateCheckInterval)"
                break
            }
        } else {
            Write-Verbose -Message "Force parameter specified. Bypassing checking on UpdateCheckInterval and enforcing up-to-dateness check on modules"
        }
        Set-PackageUpdateSetting -LastCheck (Get-Date)

        # Get the necessary repositories
        $getPSRepositoryParams = @{ }
        if ($Repository) { $getPSRepositoryParams.Add("Name", $Repository) }
        $psRepositories = Get-PSRepository @getPSRepositoryParams -ErrorAction Stop

    }

    process {
        if (-not $Name) { $Name = "*" }
        foreach ($nameItem in $Name) {
            # Get local module(s)
            Write-Verbose "Get local module(s): $($nameItem)"
            $getModuleParams = @{
                ListAvailable = $true
                Name          = $nameItem
                Verbose       = $false
            }
            $modulesLocal = Get-Module @getModuleParams | Where-Object RepositorySourceLocation | Sort-Object Name, Version -Descending | Group-Object Name | ForEach-Object { $_.group[0] }

            # Filtering out if switches are specified
            Write-Verbose "Do the filtering..."
            if ($CurrentUser) {
                $modulesLocal = foreach ($path in $CurrentUserModulePath) { $modulesLocal | Where-Object path -Like "$($path)*" }
            }
            if ($AllUsers) {
                $modulesLocal = foreach ($path in $AllUsersModulePath) { $modulesLocal | Where-Object path -Like "$($path)*" }
            }
            if ($Repository) {
                $modulesLocal = foreach ($psRepository in $psRepositories) { $modulesLocal | Where-Object RepositorySourceLocation -like "$($psRepository.SourceLocation)*" }
            }

            # Get available modules from online repositories
            Write-Verbose "Get available modules from online repositories"
            $modulesOnline = foreach ($moduleLocalName in $modulesLocal.Name) {
                $findModuleParams = @{
                    "Name" = $moduleLocalName
                    "Verbose" = $false
                }
                if ($Repository) { $findModuleParams["Repository"] = $Repository }
                Find-Module @findModuleParams
            }

            # Compare the version and create output
            Write-Verbose "Compare the version and create output"
            foreach ($moduleOnline in $modulesOnline) {
                $moduleLocal = $modulesLocal | Where-Object Name -like $moduleOnline.Name

                if ($moduleOnline.version -gt $moduleLocal.version) {
                    Write-Verbose "Update available for module '$($moduleOnline.Name)': local v$($moduleLocal.version) --> v$($moduleOnline.version) online"
                    $UpdateAvailable = Test-UpdateIsNeeded -ModuleLocal $moduleLocal -ModuleOnline $moduleOnline
                    #$UpdateAvailable = $true
                } elseif ($moduleOnline.version -lt $moduleLocal.version) {
                    Write-Warning "Local version for module '$($moduleOnline.Name)' is higher than online version: local v$($moduleLocal.version) <-- v$($moduleOnline.version) online"
                    $UpdateAvailable = $false
                } else {
                    Write-Verbose "The module '$($moduleOnline.Name)' is up to date (Version $($moduleLocal.version))"
                    $UpdateAvailable = $false
                }

                if ($ShowOnlyNeededUpdate -and (-not $UpdateAvailable)) { continue }

                $outputHash = [ordered]@{
                    Name             = $moduleLocal.Name
                    Repository       = ($psRepositories | Where-Object SourceLocation -like "$($moduleLocal.RepositorySourceLocation.ToString().Trim('/'))*").Name
                    VersionInstalled = $moduleLocal.version
                    VersionOnline    = $moduleOnline.version
                    NeedUpdate       = $UpdateAvailable
                    Path             = $moduleLocal.ModuleBase.Replace($moduleLocal.Version, '').trim('\')
                    ProjectUri       = $moduleOnline.ProjectUri
                    IconUri          = $moduleOnline.IconUri
                    ReleaseNotes     = $moduleOnline.ReleaseNotes
                    Author           = $moduleOnline.Author
                    PublishedDate    = $moduleOnline.PublishedDate
                    Description      = $moduleOnline.Description
                }
                $PackageUpdateInfo = New-Object -TypeName PackageUpdate.Info -Property $outputHash

                if ($script:EnableToastNotification -and $ShowToastNotification -and $PackageUpdateInfo.NeedUpdate) {
                    Show-ToastNotification -PackageUpdateInfo $PackageUpdateInfo
                }

                $PackageUpdateInfo
            }
        }
    }

    end {
        Set-PackageUpdateSetting -LastSuccessfulCheck (Get-Date)
    }
}