-
Notifications
You must be signed in to change notification settings - Fork 52
/
Install-VisualStudio.ps1
281 lines (253 loc) · 13.9 KB
/
Install-VisualStudio.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
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
function Install-VisualStudio {
<#
.SYNOPSIS
Installs Visual Studio
.DESCRIPTION
Installs Visual Studio with ability to specify additional features and supply product key.
.PARAMETER PackageName
The name of the VisualStudio package - this is arbitrary.
It's recommended you call it the same as your nuget package id.
.PARAMETER Url
This is the url to download the VS web installer.
.PARAMETER ChecksumSha1
The SHA-1 hash of the VS web installer file.
.EXAMPLE
Install-VisualStudio -PackageName VisualStudio2015Community -Url 'http://download.microsoft.com/download/zzz/vs_community.exe' -ChecksumSha1 'ABCDEF0123456789ABCDEF0123456789ABCDEF12'
.OUTPUTS
None
.NOTES
This helper reduces the number of lines one would have to write to download and install Visual Studio.
This method has no error handling built into it.
.LINK
Install-ChocolateyPackage
#>
[CmdletBinding()]
param(
[string] $PackageName,
[string] $ApplicationName,
[string] $Url,
[string] $Checksum,
[string] $ChecksumType,
[ValidateSet('MsiVS2015OrEarlier', 'WillowVS2017OrLater')] [string] $InstallerTechnology,
[string] $ProgramsAndFeaturesDisplayName = $ApplicationName,
[string] $VisualStudioYear,
[string] $Product,
[bool] $Preview,
[version] $DesiredProductVersion,
[hashtable] $DefaultParameterValues
)
if ($null -ne $Env:ChocolateyPackageDebug)
{
$VerbosePreference = 'Continue'
$DebugPreference = 'Continue'
Write-Warning "VerbosePreference and DebugPreference set to Continue due to the presence of ChocolateyPackageDebug environment variable"
}
Write-Debug "Running 'Install-VisualStudio' for $PackageName with ApplicationName:'$ApplicationName' Url:'$Url' Checksum:$Checksum ChecksumType:$ChecksumType InstallerTechnology:'$InstallerTechnology' ProgramsAndFeaturesDisplayName:'$ProgramsAndFeaturesDisplayName' VisualStudioYear:'$VisualStudioYear' Product:'$Product' Preview:'$Preview' DesiredProductVersion:'$DesiredProductVersion'";
$packageParameters = Parse-Parameters $env:chocolateyPackageParameters -DefaultValues $DefaultParameterValues
$creatingLayout = $packageParameters.ContainsKey('layout')
$assumeNewVS2017Installer = $InstallerTechnology -eq 'WillowVS2017OrLater'
$channelReference = $null
$productReference = $null
if ($VisualStudioYear -ne '')
{
$channelReference = Get-VSChannelReference -VisualStudioYear $VisualStudioYear -Preview $Preview -PackageParameters $packageParameters
}
elseif ($packageParameters.ContainsKey('channelId'))
{
# Fallback for old packages, which did not specify VisualStudioYear.
# The actual year value passed here does not matter, because the function will use the channelId from package parameters.
$channelReference = Get-VSChannelReference -VisualStudioYear '2017' -Preview $Preview -PackageParameters $packageParameters
}
if ($null -ne $channelReference -and $Product -ne '')
{
if ($Product -ne '')
{
$productReference = Get-VSProductReference -ChannelReference $channelReference -Product $Product -PackageParameters $packageParameters
}
elseif ($packageParameters.ContainsKey('productId'))
{
# Fallback for old packages, which did not specify VisualStudioYear.
# The actual product name passed here does not matter, because the function will use the productId from package parameters.
$productReference = Get-VSProductReference -ChannelReference $channelReference -Product 'Ignored' -PackageParameters $packageParameters
}
}
if (-not $creatingLayout)
{
if ($assumeNewVS2017Installer)
{
# there is a single Programs and Features entry for all products, so its presence is not enough
if ($null -ne $productReference)
{
$products = Resolve-VSProductInstance -ProductReference $productReference -PackageParameters $packageParameters
$productsCount = ($products | Measure-Object).Count
Write-Verbose ("Found {0} installed Visual Studio product(s) with ChannelId = {1} and ProductId = {2}" -f $productsCount, $productReference.ChannelId, $productReference.ProductId)
if ($productsCount -gt 0)
{
$allowUpdate = -not $packageParameters.ContainsKey('no-update')
if ($allowUpdate)
{
Write-Debug 'Updating existing VS instances is enabled (default)'
# The bootstrapper is used for updating (either from layout - indicated via bootstrapperPath, or downloaded from $Url).
# That way, users can expect that packages using Install-VisualStudio will always call the bootstrapper
# and workload packages will always call the installer, so the users will know which arguments will
# be supported in each case.
Start-VSModifyOperation `
-PackageName $PackageName `
-ArgumentList @() `
-ChannelReference $channelReference `
-ApplicableProducts @($Product) `
-OperationTexts @('update', 'updating', 'update') `
-Operation 'update' `
-DesiredProductVersion $DesiredProductVersion `
-PackageParameters $packageParameters `
-BootstrapperUrl $Url `
-BootstrapperChecksum $Checksum `
-BootstrapperChecksumType $ChecksumType `
-ProductReference $productReference `
-UseBootstrapper `
-ProductInstance $products
}
else
{
Write-Debug 'Updating existing VS instances is disabled because --no-update was passed in package parameters'
Write-Warning "$ApplicationName is already installed. Please use the Visual Studio Installer to modify or repair it."
}
return
}
}
}
else
{
$uninstallKey = Get-VSUninstallRegistryKey -ApplicationName $ProgramsAndFeaturesDisplayName
$count = ($uninstallKey | Measure-Object).Count
if ($count -gt 0)
{
Write-Warning "$ApplicationName is already installed. Please use Programs and Features in the Control Panel to modify or repair it."
return
}
}
}
$installSourceInfo = Open-VSInstallSource -PackageParameters $packageParameters -Url $Url
try
{
if ($assumeNewVS2017Installer)
{
$adminFile = $null
$logFilePath = $null
}
else
{
$defaultAdminFile = (Join-Path $Env:ChocolateyPackageFolder 'tools\AdminDeployment.xml')
Write-Debug "Default AdminFile: $defaultAdminFile"
$adminFile = Generate-AdminFile -Parameters $packageParameters -DefaultAdminFile $defaultAdminFile -PackageName $PackageName -InstallSourceInfo $installSourceInfo -Url $Url -Checksum $Checksum -ChecksumType $ChecksumType
Write-Debug "AdminFile: $adminFile"
Update-AdminFile $packageParameters $adminFile
$logFilePath = Join-Path $Env:TEMP ('{0}_{1:yyyyMMddHHmmss}.log' -f $PackageName, (Get-Date))
Write-Debug "Log file path: $logFilePath"
}
if ($creatingLayout)
{
$layoutPath = $packageParameters['layout']
Write-Warning "Creating an offline installation source for $PackageName in '$layoutPath'. $PackageName will not be actually installed."
}
if ($assumeNewVS2017Installer)
{
# Copy channel and product info back to package parameters. This helps packages which use the generic bootstrapper (vs_Setup.exe).
$packageParameters = $packageParameters.Clone()
if ($null -ne $channelReference)
{
if (-not $packageParameters.ContainsKey('channelId'))
{
$packageParameters['channelId'] = $channelReference.ChannelId
}
if (-not $packageParameters.ContainsKey('channelUri') -and -not [string]::IsNullOrEmpty($channelReference.ChannelUri))
{
$packageParameters['channelUri'] = $channelReference.ChannelUri
}
if (-not $packageParameters.ContainsKey('installChannelUri') -and -not [string]::IsNullOrEmpty($channelReference.InstallChannelUri))
{
$packageParameters['installChannelUri'] = $channelReference.InstallChannelUri
}
}
if ($null -ne $productReference)
{
if (-not $packageParameters.ContainsKey('productId'))
{
$packageParameters['productId'] = $productReference.ProductId
}
}
Assert-VSInstallerUpdated -PackageName $PackageName -PackageParameters $packageParameters -ChannelReference $channelReference -Url $Url -Checksum $Checksum -ChecksumType $ChecksumType -UseInstallChannelUri
if ($creatingLayout)
{
# vs_layout.exe (used internally by the VS Installer) does not support channel parameters on the command line (channelId, channelUri, installChannelUri), even though the docs suggest it should.
# Normally, those parameters are passed from a response file embedded inside the VS bootstrapper, so the values are hardwired to the origin of the bootstrapper.
# However, in some cases we want the flexibility to use a different channel, and in other cases we use a generic bootstrapper (vs_Setup.exe), which has a blank response file.
# Therefore, we need to remove those parameters from the command line and create our own response file (but only if the user did not pass their own!).
$channelParameters = @{}
foreach ($name in @('channelId', 'channelUri', 'installChannelUri'))
{
if ($packageParameters.ContainsKey($name))
{
Write-Debug "Layout: removing ${name} from command line parameters (value: '$($packageParameters[$name])')"
$channelParameters[$name] = $packageParameters[$name]
$packageParameters.Remove($name)
}
}
if ($channelParameters.Count -gt 0)
{
if (-not $packageParameters.ContainsKey('in'))
{
if ($channelParameters.ContainsKey('channelId') -and $channelParameters.ContainsKey('channelUri'))
{
$responseFilePath = Join-Path $Env:TEMP ('{0}_layout_{1:yyyyMMddHHmmss}.json' -f $PackageName, (Get-Date))
Write-Debug "Generating response file with path: $responseFilePath"
$responseFileContent = $channelParameters | ConvertTo-Json
Write-Debug "Response file content: $responseFileContent"
Set-Content -Path $responseFilePath -Value $responseFileContent -Encoding utf8
$packageParameters['in'] = $responseFilePath
}
else
{
# This should never happen with known VS packages, but someone somewhere may do something unexpected, so let's leave a hint in the logs.
Write-Verbose "When creating a layout, both channelId and channelUri should be provided if any of: channelId, channelUri, installChannelUri are."
}
}
else
{
Write-Debug "Some channel-related parameters were removed from the command line to the layout command, but a custom response file was passed via the --in parameter. In case of problems, please ensure the provided response file contains those parameters."
}
}
}
}
$silentArgs = Generate-InstallArgumentsString -parameters $packageParameters -adminFile $adminFile -logFilePath $logFilePath -assumeNewVS2017Installer:$assumeNewVS2017Installer
$arguments = @{
packageName = $PackageName
silentArgs = $silentArgs
url = $Url
checksum = $Checksum
checksumType = $ChecksumType
logFilePath = $logFilePath
assumeNewVS2017Installer = $assumeNewVS2017Installer
installerFilePath = $installSourceInfo.InstallerFilePath
}
$argumentsDump = ($arguments.GetEnumerator() | ForEach-Object { '-{0}:''{1}''' -f $_.Key,"$($_.Value)" }) -join ' '
Write-Debug "Install-VSChocolateyPackage $argumentsDump"
Install-VSChocolateyPackage @arguments
}
finally
{
Close-VSInstallSource -InstallSourceInfo $installSourceInfo
}
if ($creatingLayout)
{
Write-Warning "An offline installation source for $PackageName has been created in '$layoutPath'."
$bootstrapperExeName = $Url -split '[/\\]' | Select-Object -Last 1
if ($bootstrapperExeName -like '*.exe')
{
Write-Warning "To install $PackageName using this source, pass '--bootstrapperPath $layoutPath\$bootstrapperExeName' as package parameters."
}
Write-Warning 'Installation will now be terminated so that Chocolatey does not register this package as installed, do not be alarmed.'
Set-PowerShellExitCode -exitCode 814
throw 'An offline installation source has been created; the software has not been actually installed.'
}
}