From 5a53f9c8e36b2ac951692064d4987ff722f47a83 Mon Sep 17 00:00:00 2001 From: Chawye Hsu Date: Wed, 10 Jan 2024 21:44:56 +0800 Subject: [PATCH 1/6] feat: add install dir check Signed-off-by: Chawye Hsu --- install.ps1 | 87 ++++++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/install.ps1 b/install.ps1 index 4e82228..1985bd8 100644 --- a/install.ps1 +++ b/install.ps1 @@ -95,7 +95,7 @@ function Deny-Install { ) Write-InstallInfo -String $message -ForegroundColor DarkRed - Write-InstallInfo "Abort." + Write-InstallInfo 'Abort.' # Don't abort if invoked with iex that would close the PS session if ($IS_EXECUTED_FROM_IEX) { @@ -107,12 +107,17 @@ function Deny-Install { function Test-ValidateParameter { if ($null -eq $Proxy -and ($null -ne $ProxyCredential -or $ProxyUseDefaultCredentials)) { - Deny-Install "Provide a valid proxy URI for the -Proxy parameter when using the -ProxyCredential or -ProxyUseDefaultCredentials." + Deny-Install 'Provide a valid proxy URI for the -Proxy parameter when using the -ProxyCredential or -ProxyUseDefaultCredentials.' } if ($ProxyUseDefaultCredentials -and $null -ne $ProxyCredential) { Deny-Install "ProxyUseDefaultCredentials is conflict with ProxyCredential. Don't use the -ProxyCredential and -ProxyUseDefaultCredentials together." } + + # Ensure the directory to install scoop is empty + if ((Test-Path $SCOOP_DIR -PathType Container) -and [bool](Get-ChildItem $SCOOP_DIR -Force)) { + Deny-Install "You are trying to install Scoop to a non-empty directory '$SCOOP_DIR', please choose another directory." + } } function Test-IsAdministrator { @@ -124,12 +129,12 @@ function Test-IsAdministrator { function Test-Prerequisite { # Scoop requires PowerShell 5 at least if (($PSVersionTable.PSVersion.Major) -lt 5) { - Deny-Install "PowerShell 5 or later is required to run Scoop. Go to https://microsoft.com/powershell to get the latest version of PowerShell." + Deny-Install 'PowerShell 5 or later is required to run Scoop. Go to https://microsoft.com/powershell to get the latest version of PowerShell.' } # Scoop requires TLS 1.2 SecurityProtocol, which exists in .NET Framework 4.5+ if ([System.Enum]::GetNames([System.Net.SecurityProtocolType]) -notcontains 'Tls12') { - 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." + 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.' } # Ensure Robocopy.exe is accessible @@ -139,13 +144,13 @@ function Test-Prerequisite { # Detect if RunAsAdministrator, there is no need to run as administrator when installing Scoop. if (!$RunAsAdmin -and (Test-IsAdministrator)) { - Deny-Install "Running the installer as administrator is disabled by default, see https://github.com/ScoopInstaller/Install#for-admin for details." + Deny-Install 'Running the installer as administrator is disabled by default, see https://github.com/ScoopInstaller/Install#for-admin for details.' } # Show notification to change execution policy $allowedExecutionPolicy = @('Unrestricted', 'RemoteSigned', 'ByPass') if ((Get-ExecutionPolicy).ToString() -notin $allowedExecutionPolicy) { - 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'." + 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'." } # Test if scoop is installed, by checking if scoop command exists. @@ -168,7 +173,7 @@ function Optimize-SecurityProtocol { # Set to TLS 1.2 (3072), then TLS 1.1 (768), and TLS 1.0 (192). Ssl3 has been superseded, # https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype?view=netframework-4.5 [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 - Write-Verbose "SecurityProtocol has been updated to support TLS 1.2" + Write-Verbose 'SecurityProtocol has been updated to support TLS 1.2' } } @@ -181,7 +186,7 @@ function Get-Downloader { } elseif ($Proxy) { # Prepend protocol if not provided if (!$Proxy.IsAbsoluteUri) { - $Proxy = New-Object System.Uri("http://" + $Proxy.OriginalString) + $Proxy = New-Object System.Uri('http://' + $Proxy.OriginalString) } $Proxy = New-Object System.Net.WebProxy($Proxy) @@ -268,7 +273,7 @@ function Expand-ZipArchive { function Out-UTF8File { param( [Parameter(Mandatory = $True, Position = 0)] - [Alias("Path")] + [Alias('Path')] [String] $FilePath, [Switch] $Append, [Switch] $NoNewLine, @@ -293,7 +298,7 @@ function Out-UTF8File { } function Import-ScoopShim { - Write-InstallInfo "Creating shim..." + Write-InstallInfo 'Creating shim...' # The scoop executable $path = "$SCOOP_APP_DIR\bin\scoop.ps1" @@ -331,31 +336,31 @@ function Import-ScoopShim { # make ps1 accessible from cmd.exe @( "@rem $absolutePath", - "@echo off", - "setlocal enabledelayedexpansion", - "set args=%*", - ":: replace problem characters in arguments", + '@echo off', + 'setlocal enabledelayedexpansion', + 'set args=%*', + ':: replace problem characters in arguments', "set args=%args:`"='%", "set args=%args:(=``(%", "set args=%args:)=``)%", "set invalid=`"='", - "if !args! == !invalid! ( set args= )", - "where /q pwsh.exe", - "if %errorlevel% equ 0 (", + 'if !args! == !invalid! ( set args= )', + 'where /q pwsh.exe', + 'if %errorlevel% equ 0 (', " pwsh -noprofile -ex unrestricted -file `"$absolutePath`" $arg %args%", - ") else (", + ') else (', " powershell -noprofile -ex unrestricted -file `"$absolutePath`" $arg %args%", - ")" + ')' ) -join "`r`n" | Out-UTF8File "$shim.cmd" @( - "#!/bin/sh", + '#!/bin/sh', "# $absolutePath", - "if command -v pwsh.exe > /dev/null 2>&1; then", + 'if command -v pwsh.exe > /dev/null 2>&1; then', " pwsh.exe -noprofile -ex unrestricted -file `"$absolutePath`" $arg `"$@`"", - "else", + 'else', " powershell.exe -noprofile -ex unrestricted -file `"$absolutePath`" $arg `"$@`"", - "fi" + 'fi' ) -join "`n" | Out-UTF8File $shim -NoNewLine } @@ -377,13 +382,13 @@ function Get-Env { } function Publish-Env { - if (-not ("Win32.NativeMethods" -as [Type])) { - Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @" + if (-not ('Win32.NativeMethods' -as [Type])) { + Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @' [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr SendMessageTimeout( IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam, uint fuFlags, uint uTimeout, out UIntPtr lpdwResult); -"@ +'@ } $HWND_BROADCAST = [IntPtr] 0xffff @@ -393,7 +398,7 @@ public static extern IntPtr SendMessageTimeout( [Win32.Nativemethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [UIntPtr]::Zero, - "Environment", + 'Environment', 2, 5000, [ref] $result @@ -440,7 +445,7 @@ function Add-ShimsDirToPath { } if (!($h -eq '\')) { - $friendlyPath = "$SCOOP_SHIMS_DIR" -Replace ([Regex]::Escape($h)), "~\" + $friendlyPath = "$SCOOP_SHIMS_DIR" -Replace ([Regex]::Escape($h)), '~\' Write-InstallInfo "Adding $friendlyPath to your path." } else { Write-InstallInfo "Adding $SCOOP_SHIMS_DIR to your path." @@ -552,7 +557,7 @@ function Test-CommandAvailable { } function Install-Scoop { - Write-InstallInfo "Initializing..." + Write-InstallInfo 'Initializing...' # Validate install parameters Test-ValidateParameter # Check prerequisites @@ -561,7 +566,7 @@ function Install-Scoop { Optimize-SecurityProtocol # Download scoop from GitHub - Write-InstallInfo "Downloading ..." + Write-InstallInfo 'Downloading ...' $downloader = Get-Downloader [bool]$downloadZipsRequired = $True @@ -577,12 +582,12 @@ function Install-Scoop { Write-Verbose "Cloning $SCOOP_PACKAGE_GIT_REPO to $SCOOP_APP_DIR" git clone -q $SCOOP_PACKAGE_GIT_REPO $SCOOP_APP_DIR if (-Not $?) { - throw "Cloning failed. Falling back to downloading zip files." + throw 'Cloning failed. Falling back to downloading zip files.' } Write-Verbose "Cloning $SCOOP_MAIN_BUCKET_GIT_REPO to $SCOOP_MAIN_BUCKET_DIR" git clone -q $SCOOP_MAIN_BUCKET_GIT_REPO $SCOOP_MAIN_BUCKET_DIR if (-Not $?) { - throw "Cloning failed. Falling back to downloading zip files." + throw 'Cloning failed. Falling back to downloading zip files.' } $downloadZipsRequired = $False } catch { @@ -611,7 +616,7 @@ function Install-Scoop { $downloader.downloadFile($SCOOP_MAIN_BUCKET_REPO, $scoopMainZipfile) # Extract files from downloaded zip - Write-InstallInfo "Extracting..." + Write-InstallInfo 'Extracting...' # 1. extract scoop $scoopUnzipTempDir = "$SCOOP_APP_DIR\_tmp" Write-Verbose "Extracting $scoopZipfile to $scoopUnzipTempDir" @@ -636,22 +641,22 @@ function Install-Scoop { # Setup initial configuration of Scoop Add-DefaultConfig - Write-InstallInfo "Scoop was installed successfully!" -ForegroundColor DarkGreen + Write-InstallInfo 'Scoop was installed successfully!' -ForegroundColor DarkGreen Write-InstallInfo "Type 'scoop help' for instructions." } function Write-DebugInfo { param($BoundArgs) - Write-Verbose "-------- PSBoundParameters --------" + Write-Verbose '-------- PSBoundParameters --------' $BoundArgs.GetEnumerator() | ForEach-Object { Write-Verbose $_ } - Write-Verbose "-------- Environment Variables --------" + Write-Verbose '-------- Environment Variables --------' Write-Verbose "`$env:USERPROFILE: $env:USERPROFILE" Write-Verbose "`$env:ProgramData: $env:ProgramData" Write-Verbose "`$env:SCOOP: $env:SCOOP" Write-Verbose "`$env:SCOOP_CACHE: $SCOOP_CACHE" Write-Verbose "`$env:SCOOP_GLOBAL: $env:SCOOP_GLOBAL" - Write-Verbose "-------- Selected Variables --------" + Write-Verbose '-------- Selected Variables --------' Write-Verbose "SCOOP_DIR: $SCOOP_DIR" Write-Verbose "SCOOP_CACHE_DIR: $SCOOP_CACHE_DIR" Write-Verbose "SCOOP_GLOBAL_DIR: $SCOOP_GLOBAL_DIR" @@ -678,11 +683,11 @@ $SCOOP_CONFIG_HOME = $env:XDG_CONFIG_HOME, "$env:USERPROFILE\.config" | Select-O $SCOOP_CONFIG_FILE = "$SCOOP_CONFIG_HOME\scoop\config.json" # TODO: Use a specific version of Scoop and the main bucket -$SCOOP_PACKAGE_REPO = "https://github.com/ScoopInstaller/Scoop/archive/master.zip" -$SCOOP_MAIN_BUCKET_REPO = "https://github.com/ScoopInstaller/Main/archive/master.zip" +$SCOOP_PACKAGE_REPO = 'https://github.com/ScoopInstaller/Scoop/archive/master.zip' +$SCOOP_MAIN_BUCKET_REPO = 'https://github.com/ScoopInstaller/Main/archive/master.zip' -$SCOOP_PACKAGE_GIT_REPO = "https://github.com/ScoopInstaller/Scoop.git" -$SCOOP_MAIN_BUCKET_GIT_REPO = "https://github.com/ScoopInstaller/Main.git" +$SCOOP_PACKAGE_GIT_REPO = 'https://github.com/ScoopInstaller/Scoop.git' +$SCOOP_MAIN_BUCKET_GIT_REPO = 'https://github.com/ScoopInstaller/Main.git' # Quit if anything goes wrong $oldErrorActionPreference = $ErrorActionPreference From e5575c6394fe75c3da2c306d3715cc18d9819d57 Mon Sep 17 00:00:00 2001 From: Chawye Hsu Date: Sat, 20 Jan 2024 13:33:30 +0800 Subject: [PATCH 2/6] check spaces Signed-off-by: Chawye Hsu --- install.ps1 | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/install.ps1 b/install.ps1 index 1985bd8..c7a5643 100644 --- a/install.ps1 +++ b/install.ps1 @@ -50,6 +50,8 @@ Use the credentials of the current user for the proxy server that is specified by the -Proxy parameter. .PARAMETER RunAsAdmin Force to run the installer as administrator. +.PARAMETER Force + Ignore validation and warnings and force to run the installer. .LINK https://scoop.sh .LINK @@ -63,7 +65,8 @@ param( [Uri] $Proxy, [System.Management.Automation.PSCredential] $ProxyCredential, [Switch] $ProxyUseDefaultCredentials, - [Switch] $RunAsAdmin + [Switch] $RunAsAdmin, + [Switch] $Force ) # Disable StrictMode in this script @@ -114,8 +117,13 @@ function Test-ValidateParameter { Deny-Install "ProxyUseDefaultCredentials is conflict with ProxyCredential. Don't use the -ProxyCredential and -ProxyUseDefaultCredentials together." } + # Check of installing scoop to a path containing spaces + if (!$Force -and $SCOOP_DIR.Contains(' ')) { + Deny-Install 'Installing Scoop to path '$SCOOP_DIR' containing spaces may cause unexpected behaviors, please choose another path.' + } + # Ensure the directory to install scoop is empty - if ((Test-Path $SCOOP_DIR -PathType Container) -and [bool](Get-ChildItem $SCOOP_DIR -Force)) { + if (!$Force -and (Test-Path $SCOOP_DIR -PathType Container) -and [bool](Get-ChildItem $SCOOP_DIR -Force)) { Deny-Install "You are trying to install Scoop to a non-empty directory '$SCOOP_DIR', please choose another directory." } } From fa6b556d5caba8898db7cf0b21affb64730f5620 Mon Sep 17 00:00:00 2001 From: Chawye Hsu Date: Sat, 20 Jan 2024 13:34:59 +0800 Subject: [PATCH 3/6] fix Signed-off-by: Chawye Hsu --- install.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.ps1 b/install.ps1 index c7a5643..1c48584 100644 --- a/install.ps1 +++ b/install.ps1 @@ -119,7 +119,7 @@ function Test-ValidateParameter { # Check of installing scoop to a path containing spaces if (!$Force -and $SCOOP_DIR.Contains(' ')) { - Deny-Install 'Installing Scoop to path '$SCOOP_DIR' containing spaces may cause unexpected behaviors, please choose another path.' + Deny-Install "Installing Scoop to path '$SCOOP_DIR' containing spaces may cause unexpected behaviors, please choose another path." } # Ensure the directory to install scoop is empty From 495e5faf933ee79ef4c04805921338a377b57a5b Mon Sep 17 00:00:00 2001 From: Chawye Hsu Date: Wed, 6 Mar 2024 00:20:05 +0800 Subject: [PATCH 4/6] chore: single quote string literals Signed-off-by: Chawye Hsu --- install.ps1 | 82 ++++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/install.ps1 b/install.ps1 index 23049f1..a4d1675 100644 --- a/install.ps1 +++ b/install.ps1 @@ -95,7 +95,7 @@ function Deny-Install { ) Write-InstallInfo -String $message -ForegroundColor DarkRed - Write-InstallInfo "Abort." + Write-InstallInfo 'Abort.' # Don't abort if invoked with iex that would close the PS session if ($IS_EXECUTED_FROM_IEX) { @@ -107,7 +107,7 @@ function Deny-Install { function Test-ValidateParameter { if ($null -eq $Proxy -and ($null -ne $ProxyCredential -or $ProxyUseDefaultCredentials)) { - Deny-Install "Provide a valid proxy URI for the -Proxy parameter when using the -ProxyCredential or -ProxyUseDefaultCredentials." + Deny-Install 'Provide a valid proxy URI for the -Proxy parameter when using the -ProxyCredential or -ProxyUseDefaultCredentials.' } if ($ProxyUseDefaultCredentials -and $null -ne $ProxyCredential) { @@ -124,12 +124,12 @@ function Test-IsAdministrator { function Test-Prerequisite { # Scoop requires PowerShell 5 at least if (($PSVersionTable.PSVersion.Major) -lt 5) { - Deny-Install "PowerShell 5 or later is required to run Scoop. Go to https://microsoft.com/powershell to get the latest version of PowerShell." + Deny-Install 'PowerShell 5 or later is required to run Scoop. Go to https://microsoft.com/powershell to get the latest version of PowerShell.' } # Scoop requires TLS 1.2 SecurityProtocol, which exists in .NET Framework 4.5+ if ([System.Enum]::GetNames([System.Net.SecurityProtocolType]) -notcontains 'Tls12') { - 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." + 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.' } # Ensure Robocopy.exe is accessible @@ -142,14 +142,14 @@ function Test-Prerequisite { # Exception: Windows Sandbox, GitHub Actions CI $exception = ($env:USERNAME -eq 'WDAGUtilityAccount') -or ($env:GITHUB_ACTIONS -eq 'true' -and $env:CI -eq 'true') if (!$exception) { - Deny-Install "Running the installer as administrator is disabled by default, see https://github.com/ScoopInstaller/Install#for-admin for details." + Deny-Install 'Running the installer as administrator is disabled by default, see https://github.com/ScoopInstaller/Install#for-admin for details.' } } # Show notification to change execution policy $allowedExecutionPolicy = @('Unrestricted', 'RemoteSigned', 'ByPass') if ((Get-ExecutionPolicy).ToString() -notin $allowedExecutionPolicy) { - 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'." + 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'." } # Test if scoop is installed, by checking if scoop command exists. @@ -172,7 +172,7 @@ function Optimize-SecurityProtocol { # Set to TLS 1.2 (3072), then TLS 1.1 (768), and TLS 1.0 (192). Ssl3 has been superseded, # https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype?view=netframework-4.5 [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 - Write-Verbose "SecurityProtocol has been updated to support TLS 1.2" + Write-Verbose 'SecurityProtocol has been updated to support TLS 1.2' } } @@ -185,7 +185,7 @@ function Get-Downloader { } elseif ($Proxy) { # Prepend protocol if not provided if (!$Proxy.IsAbsoluteUri) { - $Proxy = New-Object System.Uri("http://" + $Proxy.OriginalString) + $Proxy = New-Object System.Uri('http://' + $Proxy.OriginalString) } $Proxy = New-Object System.Net.WebProxy($Proxy) @@ -272,7 +272,7 @@ function Expand-ZipArchive { function Out-UTF8File { param( [Parameter(Mandatory = $True, Position = 0)] - [Alias("Path")] + [Alias('Path')] [String] $FilePath, [Switch] $Append, [Switch] $NoNewLine, @@ -297,7 +297,7 @@ function Out-UTF8File { } function Import-ScoopShim { - Write-InstallInfo "Creating shim..." + Write-InstallInfo 'Creating shim...' # The scoop executable $path = "$SCOOP_APP_DIR\bin\scoop.ps1" @@ -335,31 +335,31 @@ function Import-ScoopShim { # make ps1 accessible from cmd.exe @( "@rem $absolutePath", - "@echo off", - "setlocal enabledelayedexpansion", - "set args=%*", - ":: replace problem characters in arguments", + '@echo off', + 'setlocal enabledelayedexpansion', + 'set args=%*', + ':: replace problem characters in arguments', "set args=%args:`"='%", "set args=%args:(=``(%", "set args=%args:)=``)%", "set invalid=`"='", - "if !args! == !invalid! ( set args= )", - "where /q pwsh.exe", - "if %errorlevel% equ 0 (", + 'if !args! == !invalid! ( set args= )', + 'where /q pwsh.exe', + 'if %errorlevel% equ 0 (', " pwsh -noprofile -ex unrestricted -file `"$absolutePath`" $arg %args%", - ") else (", + ') else (', " powershell -noprofile -ex unrestricted -file `"$absolutePath`" $arg %args%", - ")" + ')' ) -join "`r`n" | Out-UTF8File "$shim.cmd" @( - "#!/bin/sh", + '#!/bin/sh', "# $absolutePath", - "if command -v pwsh.exe > /dev/null 2>&1; then", + 'if command -v pwsh.exe > /dev/null 2>&1; then', " pwsh.exe -noprofile -ex unrestricted -file `"$absolutePath`" $arg `"$@`"", - "else", + 'else', " powershell.exe -noprofile -ex unrestricted -file `"$absolutePath`" $arg `"$@`"", - "fi" + 'fi' ) -join "`n" | Out-UTF8File $shim -NoNewLine } @@ -381,13 +381,13 @@ function Get-Env { } function Publish-Env { - if (-not ("Win32.NativeMethods" -as [Type])) { - Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @" + if (-not ('Win32.NativeMethods' -as [Type])) { + Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @' [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr SendMessageTimeout( IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam, uint fuFlags, uint uTimeout, out UIntPtr lpdwResult); -"@ +'@ } $HWND_BROADCAST = [IntPtr] 0xffff @@ -397,7 +397,7 @@ public static extern IntPtr SendMessageTimeout( [Win32.Nativemethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [UIntPtr]::Zero, - "Environment", + 'Environment', 2, 5000, [ref] $result @@ -444,7 +444,7 @@ function Add-ShimsDirToPath { } if (!($h -eq '\')) { - $friendlyPath = "$SCOOP_SHIMS_DIR" -Replace ([Regex]::Escape($h)), "~\" + $friendlyPath = "$SCOOP_SHIMS_DIR" -Replace ([Regex]::Escape($h)), '~\' Write-InstallInfo "Adding $friendlyPath to your path." } else { Write-InstallInfo "Adding $SCOOP_SHIMS_DIR to your path." @@ -556,7 +556,7 @@ function Test-CommandAvailable { } function Install-Scoop { - Write-InstallInfo "Initializing..." + Write-InstallInfo 'Initializing...' # Validate install parameters Test-ValidateParameter # Check prerequisites @@ -565,7 +565,7 @@ function Install-Scoop { Optimize-SecurityProtocol # Download scoop from GitHub - Write-InstallInfo "Downloading ..." + Write-InstallInfo 'Downloading ...' $downloader = Get-Downloader [bool]$downloadZipsRequired = $True @@ -581,12 +581,12 @@ function Install-Scoop { Write-Verbose "Cloning $SCOOP_PACKAGE_GIT_REPO to $SCOOP_APP_DIR" git clone -q $SCOOP_PACKAGE_GIT_REPO $SCOOP_APP_DIR if (-Not $?) { - throw "Cloning failed. Falling back to downloading zip files." + throw 'Cloning failed. Falling back to downloading zip files.' } Write-Verbose "Cloning $SCOOP_MAIN_BUCKET_GIT_REPO to $SCOOP_MAIN_BUCKET_DIR" git clone -q $SCOOP_MAIN_BUCKET_GIT_REPO $SCOOP_MAIN_BUCKET_DIR if (-Not $?) { - throw "Cloning failed. Falling back to downloading zip files." + throw 'Cloning failed. Falling back to downloading zip files.' } $downloadZipsRequired = $False } catch { @@ -615,7 +615,7 @@ function Install-Scoop { $downloader.downloadFile($SCOOP_MAIN_BUCKET_REPO, $scoopMainZipfile) # Extract files from downloaded zip - Write-InstallInfo "Extracting..." + Write-InstallInfo 'Extracting...' # 1. extract scoop $scoopUnzipTempDir = "$SCOOP_APP_DIR\_tmp" Write-Verbose "Extracting $scoopZipfile to $scoopUnzipTempDir" @@ -640,22 +640,22 @@ function Install-Scoop { # Setup initial configuration of Scoop Add-DefaultConfig - Write-InstallInfo "Scoop was installed successfully!" -ForegroundColor DarkGreen + Write-InstallInfo 'Scoop was installed successfully!' -ForegroundColor DarkGreen Write-InstallInfo "Type 'scoop help' for instructions." } function Write-DebugInfo { param($BoundArgs) - Write-Verbose "-------- PSBoundParameters --------" + Write-Verbose '-------- PSBoundParameters --------' $BoundArgs.GetEnumerator() | ForEach-Object { Write-Verbose $_ } - Write-Verbose "-------- Environment Variables --------" + Write-Verbose '-------- Environment Variables --------' Write-Verbose "`$env:USERPROFILE: $env:USERPROFILE" Write-Verbose "`$env:ProgramData: $env:ProgramData" Write-Verbose "`$env:SCOOP: $env:SCOOP" Write-Verbose "`$env:SCOOP_CACHE: $SCOOP_CACHE" Write-Verbose "`$env:SCOOP_GLOBAL: $env:SCOOP_GLOBAL" - Write-Verbose "-------- Selected Variables --------" + Write-Verbose '-------- Selected Variables --------' Write-Verbose "SCOOP_DIR: $SCOOP_DIR" Write-Verbose "SCOOP_CACHE_DIR: $SCOOP_CACHE_DIR" Write-Verbose "SCOOP_GLOBAL_DIR: $SCOOP_GLOBAL_DIR" @@ -682,11 +682,11 @@ $SCOOP_CONFIG_HOME = $env:XDG_CONFIG_HOME, "$env:USERPROFILE\.config" | Select-O $SCOOP_CONFIG_FILE = "$SCOOP_CONFIG_HOME\scoop\config.json" # TODO: Use a specific version of Scoop and the main bucket -$SCOOP_PACKAGE_REPO = "https://github.com/ScoopInstaller/Scoop/archive/master.zip" -$SCOOP_MAIN_BUCKET_REPO = "https://github.com/ScoopInstaller/Main/archive/master.zip" +$SCOOP_PACKAGE_REPO = 'https://github.com/ScoopInstaller/Scoop/archive/master.zip' +$SCOOP_MAIN_BUCKET_REPO = 'https://github.com/ScoopInstaller/Main/archive/master.zip' -$SCOOP_PACKAGE_GIT_REPO = "https://github.com/ScoopInstaller/Scoop.git" -$SCOOP_MAIN_BUCKET_GIT_REPO = "https://github.com/ScoopInstaller/Main.git" +$SCOOP_PACKAGE_GIT_REPO = 'https://github.com/ScoopInstaller/Scoop.git' +$SCOOP_MAIN_BUCKET_GIT_REPO = 'https://github.com/ScoopInstaller/Main.git' # Quit if anything goes wrong $oldErrorActionPreference = $ErrorActionPreference From e3a10f28a8260aa85741cf3a5c428ba161d5e29f Mon Sep 17 00:00:00 2001 From: Chawye Hsu Date: Wed, 6 Mar 2024 00:52:19 +0800 Subject: [PATCH 5/6] feat: check file system Signed-off-by: Chawye Hsu --- install.ps1 | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/install.ps1 b/install.ps1 index 1bea486..db7becd 100644 --- a/install.ps1 +++ b/install.ps1 @@ -108,6 +108,65 @@ function Deny-Install { } } +function Test-PathFileSystem { + [CmdletBinding()] + [OutputType([bool])] + param ( + [string] $Path + ) + + $Path = Split-Path -Path $Path -Qualifier -ErrorAction SilentlyContinue + if ($null -eq $Path) { + return $false + } + + # Wrap the code in a job to avoid loading assemblies into the current session + $fs = Start-Job -ScriptBlock { + $DebugPreference = $using:DebugPreference + + Add-Type -Namespace Win32 -Name NativeMethods -UsingNamespace 'System.Text' -MemberDefinition @' +[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] +public static extern bool GetVolumeInformation( + string Volume, StringBuilder VolumeName, uint VolumeNameSize, + out uint SerialNumber, out uint SerialNumberLength, + out uint FileSystemFlags, StringBuilder FileSystem, uint FileSystemSize); +'@ + + $VolumeName = [System.Text.StringBuilder]::new(1024) + $FileSystem = [System.Text.StringBuilder]::new(1024) + $SerialNumber = 0 + $SerialNumberLength = 0 + $FileSystemFlags = 0 + # A trailing backslash is required + # ref: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationa#parameters + $Volume = "$using:Path\" + Write-Debug "Test-PathFileSystem: $Volume" + + $call = [Win32.NativeMethods]::GetVolumeInformation( + $Volume, + $VolumeName, + 1024, + [ref] $SerialNumber, + [ref] $SerialNumberLength, + [ref] $FileSystemFlags, + $FileSystem, + 1024 + ) + + if ($call) { + Write-Debug "Test-PathFileSystem: VolumeName '$($VolumeName.ToString())'" + return $FileSystem.ToString() + } else { + $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() + Write-Debug "Test-PathFileSystem: Error Code $ErrorCode" + return $null + } + } | Receive-Job -Wait + + Write-Debug "Test-PathFileSystem: FileSystem '$FileSystem'" + return $FileSystem -eq 'NTFS' +} + function Test-ValidateParameter { if ($null -eq $Proxy -and ($null -ne $ProxyCredential -or $ProxyUseDefaultCredentials)) { Deny-Install 'Provide a valid proxy URI for the -Proxy parameter when using the -ProxyCredential or -ProxyUseDefaultCredentials.' @@ -126,6 +185,12 @@ function Test-ValidateParameter { if (!$Force -and (Test-Path $SCOOP_DIR -PathType Container) -and [bool](Get-ChildItem $SCOOP_DIR -Force)) { Deny-Install "You are trying to install Scoop to a non-empty directory '$SCOOP_DIR', please choose another directory." } + + # Ensure the directory to install scoop is on an NTFS file system, which + # is required for NTFS features like hard links and NTFS junctions + if (!($Force -or (Test-PathFileSystem $SCOOP_DIR))) { + Deny-Install "The path '$SCOOP_DIR' is not on an NTFS file system, please choose another directory." + } } function Test-IsAdministrator { From 24c744663a16c8293af05bc296de8ee95a24104e Mon Sep 17 00:00:00 2001 From: Chawye Hsu Date: Wed, 6 Mar 2024 01:26:03 +0800 Subject: [PATCH 6/6] fix Signed-off-by: Chawye Hsu --- install.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.ps1 b/install.ps1 index db7becd..5be8cca 100644 --- a/install.ps1 +++ b/install.ps1 @@ -121,7 +121,7 @@ function Test-PathFileSystem { } # Wrap the code in a job to avoid loading assemblies into the current session - $fs = Start-Job -ScriptBlock { + $FileSystem = Start-Job -ScriptBlock { $DebugPreference = $using:DebugPreference Add-Type -Namespace Win32 -Name NativeMethods -UsingNamespace 'System.Text' -MemberDefinition @'