Nessuna descrizione

install.ps1 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718
  1. # Issue Tracker: https://github.com/ScoopInstaller/Install/issues
  2. # Unlicense License:
  3. #
  4. # This is free and unencumbered software released into the public domain.
  5. #
  6. # Anyone is free to copy, modify, publish, use, compile, sell, or
  7. # distribute this software, either in source code form or as a compiled
  8. # binary, for any purpose, commercial or non-commercial, and by any
  9. # means.
  10. #
  11. # In jurisdictions that recognize copyright laws, the author or authors
  12. # of this software dedicate any and all copyright interest in the
  13. # software to the public domain. We make this dedication for the benefit
  14. # of the public at large and to the detriment of our heirs and
  15. # successors. We intend this dedication to be an overt act of
  16. # relinquishment in perpetuity of all present and future rights to this
  17. # software under copyright law.
  18. #
  19. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  21. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  22. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  23. # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  24. # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  25. # OTHER DEALINGS IN THE SOFTWARE.
  26. #
  27. # For more information, please refer to <http://unlicense.org/>
  28. <#
  29. .SYNOPSIS
  30. Scoop installer.
  31. .DESCRIPTION
  32. The installer of Scoop. For details please check the website and wiki.
  33. .PARAMETER ScoopDir
  34. Specifies Scoop root path.
  35. If not specified, Scoop will be installed to '$env:USERPROFILE\scoop'.
  36. .PARAMETER ScoopGlobalDir
  37. Specifies directory to store global apps.
  38. If not specified, global apps will be installed to '$env:ProgramData\scoop'.
  39. .PARAMETER ScoopCacheDir
  40. Specifies cache directory.
  41. If not specified, caches will be downloaded to '$ScoopDir\cache'.
  42. .PARAMETER NoProxy
  43. Bypass system proxy during the installation.
  44. .PARAMETER Proxy
  45. Specifies proxy to use during the installation.
  46. .PARAMETER ProxyCredential
  47. Specifies credential for the given proxy.
  48. .PARAMETER ProxyUseDefaultCredentials
  49. Use the credentials of the current user for the proxy server that is specified by the -Proxy parameter.
  50. .PARAMETER RunAsAdmin
  51. Force to run the installer as administrator.
  52. .LINK
  53. https://scoop.sh
  54. .LINK
  55. https://github.com/ScoopInstaller/Scoop/wiki
  56. #>
  57. param(
  58. [String] $ScoopDir,
  59. [String] $ScoopGlobalDir,
  60. [String] $ScoopCacheDir,
  61. [Switch] $NoProxy,
  62. [Uri] $Proxy,
  63. [System.Management.Automation.PSCredential] $ProxyCredential,
  64. [Switch] $ProxyUseDefaultCredentials,
  65. [Switch] $RunAsAdmin
  66. )
  67. # Disable StrictMode in this script
  68. Set-StrictMode -Off
  69. function Write-InstallInfo {
  70. param(
  71. [Parameter(Mandatory = $True, Position = 0)]
  72. [String] $String,
  73. [Parameter(Mandatory = $False, Position = 1)]
  74. [System.ConsoleColor] $ForegroundColor = $host.UI.RawUI.ForegroundColor
  75. )
  76. $backup = $host.UI.RawUI.ForegroundColor
  77. if ($ForegroundColor -ne $host.UI.RawUI.ForegroundColor) {
  78. $host.UI.RawUI.ForegroundColor = $ForegroundColor
  79. }
  80. Write-Output "$String"
  81. $host.UI.RawUI.ForegroundColor = $backup
  82. }
  83. function Deny-Install {
  84. param(
  85. [String] $message,
  86. [Int] $errorCode = 1
  87. )
  88. Write-InstallInfo -String $message -ForegroundColor DarkRed
  89. Write-InstallInfo 'Abort.'
  90. # Don't abort if invoked with iex that would close the PS session
  91. if ($IS_EXECUTED_FROM_IEX) {
  92. break
  93. } else {
  94. exit $errorCode
  95. }
  96. }
  97. function Test-LanguageMode {
  98. if ($ExecutionContext.SessionState.LanguageMode -ne 'FullLanguage') {
  99. Write-Output 'Scoop requires PowerShell FullLanguage mode to run, current PowerShell environment is restricted.'
  100. Write-Output 'Abort.'
  101. if ($IS_EXECUTED_FROM_IEX) {
  102. break
  103. } else {
  104. exit $errorCode
  105. }
  106. }
  107. }
  108. function Test-ValidateParameter {
  109. if ($null -eq $Proxy -and ($null -ne $ProxyCredential -or $ProxyUseDefaultCredentials)) {
  110. Deny-Install 'Provide a valid proxy URI for the -Proxy parameter when using the -ProxyCredential or -ProxyUseDefaultCredentials.'
  111. }
  112. if ($ProxyUseDefaultCredentials -and $null -ne $ProxyCredential) {
  113. Deny-Install "ProxyUseDefaultCredentials is conflict with ProxyCredential. Don't use the -ProxyCredential and -ProxyUseDefaultCredentials together."
  114. }
  115. }
  116. function Test-IsAdministrator {
  117. return ([Security.Principal.WindowsPrincipal]`
  118. [Security.Principal.WindowsIdentity]::GetCurrent()`
  119. ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
  120. }
  121. function Test-Prerequisite {
  122. # Scoop requires PowerShell 5 at least
  123. if (($PSVersionTable.PSVersion.Major) -lt 5) {
  124. Deny-Install 'PowerShell 5 or later is required to run Scoop. Go to https://microsoft.com/powershell to get the latest version of PowerShell.'
  125. }
  126. # Scoop requires TLS 1.2 SecurityProtocol, which exists in .NET Framework 4.5+
  127. if ([System.Enum]::GetNames([System.Net.SecurityProtocolType]) -notcontains 'Tls12') {
  128. Deny-Install 'Scoop requires .NET Framework 4.5+ to work. Go to https://microsoft.com/net/download to get the latest version of .NET Framework.'
  129. }
  130. # Ensure Robocopy.exe is accessible
  131. if (!(Test-CommandAvailable('robocopy'))) {
  132. Deny-Install "Scoop requires 'C:\Windows\System32\Robocopy.exe' to work. Please make sure 'C:\Windows\System32' is in your PATH."
  133. }
  134. # Detect if RunAsAdministrator, there is no need to run as administrator when installing Scoop
  135. if (!$RunAsAdmin -and (Test-IsAdministrator)) {
  136. # Exception: Windows Sandbox, GitHub Actions CI
  137. $exception = ($env:USERNAME -eq 'WDAGUtilityAccount') -or ($env:GITHUB_ACTIONS -eq 'true' -and $env:CI -eq 'true')
  138. if (!$exception) {
  139. Deny-Install 'Running the installer as administrator is disabled by default, see https://github.com/ScoopInstaller/Install#for-admin for details.'
  140. }
  141. }
  142. # Show notification to change execution policy
  143. $allowedExecutionPolicy = @('Unrestricted', 'RemoteSigned', 'ByPass')
  144. if ((Get-ExecutionPolicy).ToString() -notin $allowedExecutionPolicy) {
  145. Deny-Install "PowerShell requires an execution policy in [$($allowedExecutionPolicy -join ', ')] to run Scoop. For example, to set the execution policy to 'RemoteSigned' please run 'Set-ExecutionPolicy RemoteSigned -Scope CurrentUser'."
  146. }
  147. # Test if scoop is installed, by checking if scoop command exists.
  148. if (Test-CommandAvailable('scoop')) {
  149. Deny-Install "Scoop is already installed. Run 'scoop update' to get the latest version."
  150. }
  151. }
  152. function Optimize-SecurityProtocol {
  153. # .NET Framework 4.7+ has a default security protocol called 'SystemDefault',
  154. # which allows the operating system to choose the best protocol to use.
  155. # If SecurityProtocolType contains 'SystemDefault' (means .NET4.7+ detected)
  156. # and the value of SecurityProtocol is 'SystemDefault', just do nothing on SecurityProtocol,
  157. # 'SystemDefault' will use TLS 1.2 if the webrequest requires.
  158. $isNewerNetFramework = ([System.Enum]::GetNames([System.Net.SecurityProtocolType]) -contains 'SystemDefault')
  159. $isSystemDefault = ([System.Net.ServicePointManager]::SecurityProtocol.Equals([System.Net.SecurityProtocolType]::SystemDefault))
  160. # If not, change it to support TLS 1.2
  161. if (!($isNewerNetFramework -and $isSystemDefault)) {
  162. # Set to TLS 1.2 (3072), then TLS 1.1 (768), and TLS 1.0 (192). Ssl3 has been superseded,
  163. # https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype?view=netframework-4.5
  164. [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192
  165. Write-Verbose 'SecurityProtocol has been updated to support TLS 1.2'
  166. }
  167. }
  168. function Get-Downloader {
  169. $downloadSession = New-Object System.Net.WebClient
  170. # Set proxy to null if NoProxy is specificed
  171. if ($NoProxy) {
  172. $downloadSession.Proxy = $null
  173. } elseif ($Proxy) {
  174. # Prepend protocol if not provided
  175. if (!$Proxy.IsAbsoluteUri) {
  176. $Proxy = New-Object System.Uri('http://' + $Proxy.OriginalString)
  177. }
  178. $Proxy = New-Object System.Net.WebProxy($Proxy)
  179. if ($null -ne $ProxyCredential) {
  180. $Proxy.Credentials = $ProxyCredential.GetNetworkCredential()
  181. } elseif ($ProxyUseDefaultCredentials) {
  182. $Proxy.UseDefaultCredentials = $true
  183. }
  184. $downloadSession.Proxy = $Proxy
  185. }
  186. return $downloadSession
  187. }
  188. function Test-isFileLocked {
  189. param(
  190. [String] $path
  191. )
  192. $file = New-Object System.IO.FileInfo $path
  193. if (!(Test-Path $path)) {
  194. return $false
  195. }
  196. try {
  197. $stream = $file.Open(
  198. [System.IO.FileMode]::Open,
  199. [System.IO.FileAccess]::ReadWrite,
  200. [System.IO.FileShare]::None
  201. )
  202. if ($stream) {
  203. $stream.Close()
  204. }
  205. return $false
  206. } catch {
  207. # The file is locked by a process.
  208. return $true
  209. }
  210. }
  211. function Expand-ZipArchive {
  212. param(
  213. [String] $path,
  214. [String] $to
  215. )
  216. if (!(Test-Path $path)) {
  217. Deny-Install "Unzip failed: can't find $path to unzip."
  218. }
  219. # Check if the zip file is locked, by antivirus software for example
  220. $retries = 0
  221. while ($retries -le 10) {
  222. if ($retries -eq 10) {
  223. Deny-Install "Unzip failed: can't unzip because a process is locking the file."
  224. }
  225. if (Test-isFileLocked $path) {
  226. Write-InstallInfo "Waiting for $path to be unlocked by another process... ($retries/10)"
  227. $retries++
  228. Start-Sleep -Seconds 2
  229. } else {
  230. break
  231. }
  232. }
  233. # Workaround to suspend Expand-Archive verbose output,
  234. # upstream issue: https://github.com/PowerShell/Microsoft.PowerShell.Archive/issues/98
  235. $oldVerbosePreference = $VerbosePreference
  236. $global:VerbosePreference = 'SilentlyContinue'
  237. # Disable progress bar to gain performance
  238. $oldProgressPreference = $ProgressPreference
  239. $global:ProgressPreference = 'SilentlyContinue'
  240. # PowerShell 5+: use Expand-Archive to extract zip files
  241. Microsoft.PowerShell.Archive\Expand-Archive -Path $path -DestinationPath $to -Force
  242. $global:VerbosePreference = $oldVerbosePreference
  243. $global:ProgressPreference = $oldProgressPreference
  244. }
  245. function Out-UTF8File {
  246. param(
  247. [Parameter(Mandatory = $True, Position = 0)]
  248. [Alias('Path')]
  249. [String] $FilePath,
  250. [Switch] $Append,
  251. [Switch] $NoNewLine,
  252. [Parameter(ValueFromPipeline = $True)]
  253. [PSObject] $InputObject
  254. )
  255. process {
  256. if ($Append) {
  257. [System.IO.File]::AppendAllText($FilePath, $InputObject)
  258. } else {
  259. if (!$NoNewLine) {
  260. # Ref: https://stackoverflow.com/questions/5596982
  261. # Performance Note: `WriteAllLines` throttles memory usage while
  262. # `WriteAllText` needs to keep the complete string in memory.
  263. [System.IO.File]::WriteAllLines($FilePath, $InputObject)
  264. } else {
  265. # However `WriteAllText` does not add ending newline.
  266. [System.IO.File]::WriteAllText($FilePath, $InputObject)
  267. }
  268. }
  269. }
  270. }
  271. function Import-ScoopShim {
  272. Write-InstallInfo 'Creating shim...'
  273. # The scoop executable
  274. $path = "$SCOOP_APP_DIR\bin\scoop.ps1"
  275. if (!(Test-Path $SCOOP_SHIMS_DIR)) {
  276. New-Item -Type Directory $SCOOP_SHIMS_DIR | Out-Null
  277. }
  278. # The scoop shim
  279. $shim = "$SCOOP_SHIMS_DIR\scoop"
  280. # Convert to relative path
  281. Push-Location $SCOOP_SHIMS_DIR
  282. $relativePath = Resolve-Path -Relative $path
  283. Pop-Location
  284. $absolutePath = Resolve-Path $path
  285. # if $path points to another drive resolve-path prepends .\ which could break shims
  286. $ps1text = if ($relativePath -match '^(\.\\)?\w:.*$') {
  287. @(
  288. "# $absolutePath",
  289. "`$path = `"$path`"",
  290. "if (`$MyInvocation.ExpectingInput) { `$input | & `$path $arg @args } else { & `$path $arg @args }",
  291. "exit `$LASTEXITCODE"
  292. )
  293. } else {
  294. @(
  295. "# $absolutePath",
  296. "`$path = Join-Path `$PSScriptRoot `"$relativePath`"",
  297. "if (`$MyInvocation.ExpectingInput) { `$input | & `$path $arg @args } else { & `$path $arg @args }",
  298. "exit `$LASTEXITCODE"
  299. )
  300. }
  301. $ps1text -join "`r`n" | Out-UTF8File "$shim.ps1"
  302. # make ps1 accessible from cmd.exe
  303. @(
  304. "@rem $absolutePath",
  305. '@echo off',
  306. 'setlocal enabledelayedexpansion',
  307. 'set args=%*',
  308. ':: replace problem characters in arguments',
  309. "set args=%args:`"='%",
  310. "set args=%args:(=``(%",
  311. "set args=%args:)=``)%",
  312. "set invalid=`"='",
  313. 'if !args! == !invalid! ( set args= )',
  314. 'where /q pwsh.exe',
  315. 'if %errorlevel% equ 0 (',
  316. " pwsh -noprofile -ex unrestricted -file `"$absolutePath`" $arg %args%",
  317. ') else (',
  318. " powershell -noprofile -ex unrestricted -file `"$absolutePath`" $arg %args%",
  319. ')'
  320. ) -join "`r`n" | Out-UTF8File "$shim.cmd"
  321. @(
  322. '#!/bin/sh',
  323. "# $absolutePath",
  324. 'if command -v pwsh.exe > /dev/null 2>&1; then',
  325. " pwsh.exe -noprofile -ex unrestricted -file `"$absolutePath`" $arg `"$@`"",
  326. 'else',
  327. " powershell.exe -noprofile -ex unrestricted -file `"$absolutePath`" $arg `"$@`"",
  328. 'fi'
  329. ) -join "`n" | Out-UTF8File $shim -NoNewLine
  330. }
  331. function Get-Env {
  332. param(
  333. [String] $name,
  334. [Switch] $global
  335. )
  336. $RegisterKey = if ($global) {
  337. Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
  338. } else {
  339. Get-Item -Path 'HKCU:'
  340. }
  341. $EnvRegisterKey = $RegisterKey.OpenSubKey('Environment')
  342. $RegistryValueOption = [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames
  343. $EnvRegisterKey.GetValue($name, $null, $RegistryValueOption)
  344. }
  345. function Publish-Env {
  346. if (-not ('Win32.NativeMethods' -as [Type])) {
  347. Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @'
  348. [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  349. public static extern IntPtr SendMessageTimeout(
  350. IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
  351. uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
  352. '@
  353. }
  354. $HWND_BROADCAST = [IntPtr] 0xffff
  355. $WM_SETTINGCHANGE = 0x1a
  356. $result = [UIntPtr]::Zero
  357. [Win32.Nativemethods]::SendMessageTimeout($HWND_BROADCAST,
  358. $WM_SETTINGCHANGE,
  359. [UIntPtr]::Zero,
  360. 'Environment',
  361. 2,
  362. 5000,
  363. [ref] $result
  364. ) | Out-Null
  365. }
  366. function Write-Env {
  367. param(
  368. [String] $name,
  369. [String] $val,
  370. [Switch] $global
  371. )
  372. $RegisterKey = if ($global) {
  373. Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
  374. } else {
  375. Get-Item -Path 'HKCU:'
  376. }
  377. $EnvRegisterKey = $RegisterKey.OpenSubKey('Environment', $true)
  378. if ($val -eq $null) {
  379. $EnvRegisterKey.DeleteValue($name)
  380. } else {
  381. $RegistryValueKind = if ($val.Contains('%')) {
  382. [Microsoft.Win32.RegistryValueKind]::ExpandString
  383. } elseif ($EnvRegisterKey.GetValue($name)) {
  384. $EnvRegisterKey.GetValueKind($name)
  385. } else {
  386. [Microsoft.Win32.RegistryValueKind]::String
  387. }
  388. $EnvRegisterKey.SetValue($name, $val, $RegistryValueKind)
  389. }
  390. Publish-Env
  391. }
  392. function Add-ShimsDirToPath {
  393. # Get $env:PATH of current user
  394. $userEnvPath = Get-Env 'PATH'
  395. if ($userEnvPath -notmatch [Regex]::Escape($SCOOP_SHIMS_DIR)) {
  396. $h = (Get-PSProvider 'FileSystem').Home
  397. if (!$h.EndsWith('\')) {
  398. $h += '\'
  399. }
  400. if (!($h -eq '\')) {
  401. $friendlyPath = "$SCOOP_SHIMS_DIR" -Replace ([Regex]::Escape($h)), '~\'
  402. Write-InstallInfo "Adding $friendlyPath to your path."
  403. } else {
  404. Write-InstallInfo "Adding $SCOOP_SHIMS_DIR to your path."
  405. }
  406. # For future sessions
  407. Write-Env 'PATH' "$SCOOP_SHIMS_DIR;$userEnvPath"
  408. # For current session
  409. $env:PATH = "$SCOOP_SHIMS_DIR;$env:PATH"
  410. }
  411. }
  412. function Use-Config {
  413. if (!(Test-Path $SCOOP_CONFIG_FILE)) {
  414. return $null
  415. }
  416. try {
  417. return (Get-Content $SCOOP_CONFIG_FILE -Raw | ConvertFrom-Json -ErrorAction Stop)
  418. } catch {
  419. Deny-Install "ERROR loading $SCOOP_CONFIG_FILE`: $($_.Exception.Message)"
  420. }
  421. }
  422. function Add-Config {
  423. param (
  424. [Parameter(Mandatory = $True, Position = 0)]
  425. [String] $Name,
  426. [Parameter(Mandatory = $True, Position = 1)]
  427. [String] $Value
  428. )
  429. $scoopConfig = Use-Config
  430. if ($scoopConfig -is [System.Management.Automation.PSObject]) {
  431. if ($Value -eq [bool]::TrueString -or $Value -eq [bool]::FalseString) {
  432. $Value = [System.Convert]::ToBoolean($Value)
  433. }
  434. if ($null -eq $scoopConfig.$Name) {
  435. $scoopConfig | Add-Member -MemberType NoteProperty -Name $Name -Value $Value
  436. } else {
  437. $scoopConfig.$Name = $Value
  438. }
  439. } else {
  440. $baseDir = Split-Path -Path $SCOOP_CONFIG_FILE
  441. if (!(Test-Path $baseDir)) {
  442. New-Item -Type Directory $baseDir | Out-Null
  443. }
  444. $scoopConfig = New-Object PSObject
  445. $scoopConfig | Add-Member -MemberType NoteProperty -Name $Name -Value $Value
  446. }
  447. if ($null -eq $Value) {
  448. $scoopConfig.PSObject.Properties.Remove($Name)
  449. }
  450. ConvertTo-Json $scoopConfig | Set-Content $SCOOP_CONFIG_FILE -Encoding ASCII
  451. return $scoopConfig
  452. }
  453. function Add-DefaultConfig {
  454. # If user-level SCOOP env not defined, save to root_path
  455. if (!(Get-Env 'SCOOP')) {
  456. if ($SCOOP_DIR -ne "$env:USERPROFILE\scoop") {
  457. Write-Verbose "Adding config root_path: $SCOOP_DIR"
  458. Add-Config -Name 'root_path' -Value $SCOOP_DIR | Out-Null
  459. }
  460. }
  461. # Use system SCOOP_GLOBAL, or set system SCOOP_GLOBAL
  462. # with $env:SCOOP_GLOBAL if RunAsAdmin, otherwise save to global_path
  463. if (!(Get-Env 'SCOOP_GLOBAL' -global)) {
  464. if ((Test-IsAdministrator) -and $env:SCOOP_GLOBAL) {
  465. Write-Verbose "Setting System Environment Variable SCOOP_GLOBAL: $env:SCOOP_GLOBAL"
  466. [Environment]::SetEnvironmentVariable('SCOOP_GLOBAL', $env:SCOOP_GLOBAL, 'Machine')
  467. } else {
  468. if ($SCOOP_GLOBAL_DIR -ne "$env:ProgramData\scoop") {
  469. Write-Verbose "Adding config global_path: $SCOOP_GLOBAL_DIR"
  470. Add-Config -Name 'global_path' -Value $SCOOP_GLOBAL_DIR | Out-Null
  471. }
  472. }
  473. }
  474. # Use system SCOOP_CACHE, or set system SCOOP_CACHE
  475. # with $env:SCOOP_CACHE if RunAsAdmin, otherwise save to cache_path
  476. if (!(Get-Env 'SCOOP_CACHE' -global)) {
  477. if ((Test-IsAdministrator) -and $env:SCOOP_CACHE) {
  478. Write-Verbose "Setting System Environment Variable SCOOP_CACHE: $env:SCOOP_CACHE"
  479. [Environment]::SetEnvironmentVariable('SCOOP_CACHE', $env:SCOOP_CACHE, 'Machine')
  480. } else {
  481. if ($SCOOP_CACHE_DIR -ne "$SCOOP_DIR\cache") {
  482. Write-Verbose "Adding config cache_path: $SCOOP_CACHE_DIR"
  483. Add-Config -Name 'cache_path' -Value $SCOOP_CACHE_DIR | Out-Null
  484. }
  485. }
  486. }
  487. # save current datatime to last_update
  488. Add-Config -Name 'last_update' -Value ([System.DateTime]::Now.ToString('o')) | Out-Null
  489. }
  490. function Test-CommandAvailable {
  491. param (
  492. [Parameter(Mandatory = $True, Position = 0)]
  493. [String] $Command
  494. )
  495. return [Boolean](Get-Command $Command -ErrorAction SilentlyContinue)
  496. }
  497. function Install-Scoop {
  498. Write-InstallInfo 'Initializing...'
  499. # Validate install parameters
  500. Test-ValidateParameter
  501. # Check prerequisites
  502. Test-Prerequisite
  503. # Enable TLS 1.2
  504. Optimize-SecurityProtocol
  505. # Download scoop from GitHub
  506. Write-InstallInfo 'Downloading...'
  507. $downloader = Get-Downloader
  508. [bool]$downloadZipsRequired = $True
  509. if (Test-CommandAvailable('git')) {
  510. $old_https = $env:HTTPS_PROXY
  511. $old_http = $env:HTTP_PROXY
  512. try {
  513. if ($downloader.Proxy) {
  514. #define env vars for git when behind a proxy
  515. $Env:HTTP_PROXY = $downloader.Proxy.Address
  516. $Env:HTTPS_PROXY = $downloader.Proxy.Address
  517. }
  518. Write-Verbose "Cloning $SCOOP_PACKAGE_GIT_REPO to $SCOOP_APP_DIR"
  519. git clone -q $SCOOP_PACKAGE_GIT_REPO $SCOOP_APP_DIR
  520. if (-Not $?) {
  521. throw 'Cloning failed. Falling back to downloading zip files.'
  522. }
  523. Write-Verbose "Cloning $SCOOP_MAIN_BUCKET_GIT_REPO to $SCOOP_MAIN_BUCKET_DIR"
  524. git clone -q $SCOOP_MAIN_BUCKET_GIT_REPO $SCOOP_MAIN_BUCKET_DIR
  525. if (-Not $?) {
  526. throw 'Cloning failed. Falling back to downloading zip files.'
  527. }
  528. $downloadZipsRequired = $False
  529. } catch {
  530. Write-Warning "$($_.Exception.Message)"
  531. $Global:LastExitCode = 0
  532. } finally {
  533. $env:HTTPS_PROXY = $old_https
  534. $env:HTTP_PROXY = $old_http
  535. }
  536. }
  537. if ($downloadZipsRequired) {
  538. # 1. download scoop
  539. $scoopZipfile = "$SCOOP_APP_DIR\scoop.zip"
  540. if (!(Test-Path $SCOOP_APP_DIR)) {
  541. New-Item -Type Directory $SCOOP_APP_DIR | Out-Null
  542. }
  543. Write-Verbose "Downloading $SCOOP_PACKAGE_REPO to $scoopZipfile"
  544. $downloader.downloadFile($SCOOP_PACKAGE_REPO, $scoopZipfile)
  545. # 2. download scoop main bucket
  546. $scoopMainZipfile = "$SCOOP_MAIN_BUCKET_DIR\scoop-main.zip"
  547. if (!(Test-Path $SCOOP_MAIN_BUCKET_DIR)) {
  548. New-Item -Type Directory $SCOOP_MAIN_BUCKET_DIR | Out-Null
  549. }
  550. Write-Verbose "Downloading $SCOOP_MAIN_BUCKET_REPO to $scoopMainZipfile"
  551. $downloader.downloadFile($SCOOP_MAIN_BUCKET_REPO, $scoopMainZipfile)
  552. # Extract files from downloaded zip
  553. Write-InstallInfo 'Extracting...'
  554. # 1. extract scoop
  555. $scoopUnzipTempDir = "$SCOOP_APP_DIR\_tmp"
  556. Write-Verbose "Extracting $scoopZipfile to $scoopUnzipTempDir"
  557. Expand-ZipArchive $scoopZipfile $scoopUnzipTempDir
  558. Copy-Item "$scoopUnzipTempDir\scoop-*\*" $SCOOP_APP_DIR -Recurse -Force
  559. # 2. extract scoop main bucket
  560. $scoopMainUnzipTempDir = "$SCOOP_MAIN_BUCKET_DIR\_tmp"
  561. Write-Verbose "Extracting $scoopMainZipfile to $scoopMainUnzipTempDir"
  562. Expand-ZipArchive $scoopMainZipfile $scoopMainUnzipTempDir
  563. Copy-Item "$scoopMainUnzipTempDir\Main-*\*" $SCOOP_MAIN_BUCKET_DIR -Recurse -Force
  564. # Cleanup
  565. Remove-Item $scoopUnzipTempDir -Recurse -Force
  566. Remove-Item $scoopZipfile
  567. Remove-Item $scoopMainUnzipTempDir -Recurse -Force
  568. Remove-Item $scoopMainZipfile
  569. }
  570. # Create the scoop shim
  571. Import-ScoopShim
  572. # Finially ensure scoop shims is in the PATH
  573. Add-ShimsDirToPath
  574. # Setup initial configuration of Scoop
  575. Add-DefaultConfig
  576. Write-InstallInfo 'Scoop was installed successfully!' -ForegroundColor DarkGreen
  577. Write-InstallInfo "Type 'scoop help' for instructions."
  578. }
  579. function Write-DebugInfo {
  580. param($BoundArgs)
  581. Write-Verbose '-------- PSBoundParameters --------'
  582. $BoundArgs.GetEnumerator() | ForEach-Object { Write-Verbose $_ }
  583. Write-Verbose '-------- Environment Variables --------'
  584. Write-Verbose "`$env:USERPROFILE: $env:USERPROFILE"
  585. Write-Verbose "`$env:ProgramData: $env:ProgramData"
  586. Write-Verbose "`$env:SCOOP: $env:SCOOP"
  587. Write-Verbose "`$env:SCOOP_CACHE: $SCOOP_CACHE"
  588. Write-Verbose "`$env:SCOOP_GLOBAL: $env:SCOOP_GLOBAL"
  589. Write-Verbose '-------- Selected Variables --------'
  590. Write-Verbose "SCOOP_DIR: $SCOOP_DIR"
  591. Write-Verbose "SCOOP_CACHE_DIR: $SCOOP_CACHE_DIR"
  592. Write-Verbose "SCOOP_GLOBAL_DIR: $SCOOP_GLOBAL_DIR"
  593. Write-Verbose "SCOOP_CONFIG_HOME: $SCOOP_CONFIG_HOME"
  594. }
  595. # Prepare variables
  596. $IS_EXECUTED_FROM_IEX = ($null -eq $MyInvocation.MyCommand.Path)
  597. # Abort when the language mode is restricted
  598. Test-LanguageMode
  599. # Scoop root directory
  600. $SCOOP_DIR = $ScoopDir, $env:SCOOP, "$env:USERPROFILE\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
  601. # Scoop global apps directory
  602. $SCOOP_GLOBAL_DIR = $ScoopGlobalDir, $env:SCOOP_GLOBAL, "$env:ProgramData\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
  603. # Scoop cache directory
  604. $SCOOP_CACHE_DIR = $ScoopCacheDir, $env:SCOOP_CACHE, "$SCOOP_DIR\cache" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
  605. # Scoop shims directory
  606. $SCOOP_SHIMS_DIR = "$SCOOP_DIR\shims"
  607. # Scoop itself directory
  608. $SCOOP_APP_DIR = "$SCOOP_DIR\apps\scoop\current"
  609. # Scoop main bucket directory
  610. $SCOOP_MAIN_BUCKET_DIR = "$SCOOP_DIR\buckets\main"
  611. # Scoop config file location
  612. $SCOOP_CONFIG_HOME = $env:XDG_CONFIG_HOME, "$env:USERPROFILE\.config" | Select-Object -First 1
  613. $SCOOP_CONFIG_FILE = "$SCOOP_CONFIG_HOME\scoop\config.json"
  614. # TODO: Use a specific version of Scoop and the main bucket
  615. $SCOOP_PACKAGE_REPO = 'https://github.com/ScoopInstaller/Scoop/archive/master.zip'
  616. $SCOOP_MAIN_BUCKET_REPO = 'https://github.com/ScoopInstaller/Main/archive/master.zip'
  617. $SCOOP_PACKAGE_GIT_REPO = 'https://github.com/ScoopInstaller/Scoop.git'
  618. $SCOOP_MAIN_BUCKET_GIT_REPO = 'https://github.com/ScoopInstaller/Main.git'
  619. # Quit if anything goes wrong
  620. $oldErrorActionPreference = $ErrorActionPreference
  621. $ErrorActionPreference = 'Stop'
  622. # Logging debug info
  623. Write-DebugInfo $PSBoundParameters
  624. # Bootstrap function
  625. Install-Scoop
  626. # Reset $ErrorActionPreference to original value
  627. $ErrorActionPreference = $oldErrorActionPreference