-
Notifications
You must be signed in to change notification settings - Fork 187
/
UpdateCheck.ps1
174 lines (146 loc) · 6.73 KB
/
UpdateCheck.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
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
# We'll cache our job name and result so that we don't check more than once per day.
$script:UpdateCheckJobName = $null
$script:HasLatestVersion = $null
function Invoke-UpdateCheck
{
<#
.SYNOPSIS
Checks PowerShellGallery to see if a newer version of this module has been published.
.DESCRIPTION
Checks PowerShellGallery to see if a newer version of this module has been published.
The check will only run once per day.
Runs asynchronously, so the user won't see any message until after they run their first
API request after the module has been imported.
Will always assume true in the event of an incomplete or failed check.
Reports the result to the user via a Warning message (if a newer version is available)
or a Verbose message (if they're running the latest version or the version check failed).
The Git repo for this module can be found here: http://aka.ms/PowerShellForGitHub
.PARAMETER Force
For debugging purposes, using this switch will allow the check to occur more than the limit
of once per day. This _will not_ bypass the DisableUpdateCheck configuration value however.
.EXAMPLE
Invoke-UpdateCheck
.NOTES
Internal-only helper method.
#>
[cmdletbinding()]
param([switch] $Force)
if (Get-GitHubConfiguration -Name DisableUpdateCheck)
{
return
}
$moduleName = $MyInvocation.MyCommand.Module.Name
$moduleVersion = $MyInvocation.MyCommand.Module.Version
$jobNameToday = "Invoke-UpdateCheck-" + (Get-Date -format 'yyyyMMdd')
if ($Force)
{
if ($null -ne $script:UpdateCheckJobName)
{
# We're going to clear out the existing job and try running it again.
$null = Receive-Job -Name $script:UpdateCheckJobName -AutoRemoveJob -Wait -ErrorAction SilentlyContinue -ErrorVariable errorInfo
}
$script:UpdateCheckJobName = $null
$script:HasLatestVersion = $null
}
# We only check once per day
if ($jobNameToday -eq $script:UpdateCheckJobName)
{
# Have we retrieved the status yet? $null means we haven't.
if ($null -ne $script:HasLatestVersion)
{
# We've already completed the check for today. No further action required.
return
}
$state = (Get-Job -Name $script:UpdateCheckJobName).state
if ($state -eq 'Failed')
{
# We'll just assume we're up-to-date if we failed to check today.
$message = '[$moduleName] update check failed for today (web request failed). Assuming up-to-date.'
Write-Log -Message $message -Level Verbose
$script:HasLatestVersion = $true
# Clear out the job info (even though we don't need the result)
$null = Receive-Job -Name $script:UpdateCheckJobName -AutoRemoveJob -Wait -ErrorAction SilentlyContinue -ErrorVariable errorInfo
return
}
elseif ($state -eq 'Completed')
{
$result = Receive-Job -Name $script:UpdateCheckJobName -AutoRemoveJob -Wait
try
{
# Occasionally the API puts two nearly identical XML responses in the body (each on a separate line).
# We'll try to avoid an unnecessary failure by always using the first line of the response.
$xml = [xml]($result.Content.Split([Environment]::NewLine) | Select-Object -First 1)
$latestVersion = $xml.feed.entry.properties.version |
ForEach-Object {[System.Version]$_} |
Sort-Object -Descending |
Select-Object -First 1
$script:HasLatestVersion = $latestVersion -eq $moduleVersion
if ($script:HasLatestVersion)
{
$message = "[$moduleName] update check complete. Running latest version: $latestVersion"
Write-Log -Message $message -Level Verbose
}
elseif ($moduleVersion -gt $latestVersion)
{
$message = "[$moduleName] update check complete. This version ($moduleVersion) is newer than the latest published version ($latestVersion)."
Write-Log -Message $message -Level Verbose
}
else
{
$message = "A newer version of $moduleName is available ($latestVersion). Your current version is $moduleVersion. Run 'Update-Module $moduleName' to get up-to-date."
Write-Log -Message $message -Level Warning
}
}
catch
{
# This could happen if the server returned back an invalid (non-XML) response for the request
# for some reason.
$message = "[$moduleName] update check failed for today (invalid XML response). Assuming up-to-date."
Write-Log -Message $message -Level Verbose
$script:HasLatestVersion = $true
}
return
}
else
{
# It's still running. Nothing further for us to do right now. We'll check back
# again next time.
return
}
}
else
{
# We either haven't checked yet, or it's a new day so we should check again.
$script:UpdateCheckJobName = $jobNameToday
$script:HasLatestVersion = $null
}
[scriptblock]$scriptBlock = {
param($ModuleName)
$params = @{}
$params.Add('Uri', "https://www.powershellgallery.com/api/v2/FindPackagesById()?id='$ModuleName'")
$params.Add('Method', 'Get')
$params.Add('UseDefaultCredentials', $true)
$params.Add('UseBasicParsing', $true)
try
{
# Disable Progress Bar in function scope during Invoke-WebRequest
$ProgressPreference = 'SilentlyContinue'
Invoke-WebRequest @params
}
catch
{
# We will silently ignore any errors that occurred, but we need to make sure that
# we are explicitly catching and throwing them so that our reported state is Failed.
throw
}
}
$null = Start-Job -Name $script:UpdateCheckJobName -ScriptBlock $scriptBlock -Arg @($moduleName)
# We're letting this run asynchronously so that users can immediately start using the module.
# We'll check back in on the result of this the next time they run any command.
}
# We will explicitly run this as soon as the module has been loaded,
# and then it will be called again during every major function call in the module
# so that the result can be reported.
Invoke-UpdateCheck