Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 86 additions & 21 deletions install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ function Exit-Install {
[Int] $ErrorCode = 1
)

if ($IS_EXECUTED_FROM_IEX) {
# Don't abort with `exit` that would close the PS session if invoked
# with iex, yet set `LASTEXITCODE` for the caller to check
if ((-not $env:CI) -and $IS_EXECUTED_FROM_IEX) {
# Don't abort with `exit` that would close the interactive PS session
# if invoked with iex, yet set `LASTEXITCODE` for the caller to check
$Global:LASTEXITCODE = $ErrorCode
break
} else {
Expand Down Expand Up @@ -131,6 +131,38 @@ function Test-ValidateParameter {
if ($ProxyUseDefaultCredentials -and $null -ne $ProxyCredential) {
Deny-Install "ProxyUseDefaultCredentials is conflict with ProxyCredential. Don't use the -ProxyCredential and -ProxyUseDefaultCredentials together."
}

if (-not (Test-Path $SCOOP_DIR -IsValid)) {
Deny-Install "'$SCOOP_DIR' is not a valid path, please specify another path."
}

if (-not (Test-Path $SCOOP_GLOBAL_DIR -IsValid)) {
Deny-Install "'$SCOOP_GLOBAL_DIR' is not a valid path, please specify another path."
}

if (-not (Test-Path $SCOOP_CACHE_DIR -IsValid)) {
Deny-Install "'$SCOOP_CACHE_DIR' is not a valid path, please specify another path."
}

if (Test-Path $SCOOP_DIR -PathType Leaf) {
Deny-Install "'$SCOOP_DIR' is a file, please remove it or specify another path."
}

if ((Test-Path $SCOOP_DIR -PathType Container) -and (Test-Path "$SCOOP_DIR\*")) {
Deny-Install "'$SCOOP_DIR' exists and is not empty, please specify another path."
}

if (Test-Path $SCOOP_GLOBAL_DIR -PathType Leaf) {
Deny-Install "'$SCOOP_GLOBAL_DIR' is a file, please remove it or specify another path."
}

if ((Test-Path $SCOOP_GLOBAL_DIR -PathType Container) -and (Test-Path "$SCOOP_GLOBAL_DIR\*")) {
Deny-Install "'$SCOOP_GLOBAL_DIR' exists and is not empty, please specify another path."
}

if (Test-Path $SCOOP_CACHE_DIR -PathType Leaf) {
Deny-Install "'$SCOOP_CACHE_DIR' is a file, please remove it or specify another path."
}
}

function Test-IsAdministrator {
Expand All @@ -157,8 +189,8 @@ function Test-Prerequisite {

# Detect if RunAsAdministrator, there is no need to run as administrator when installing Scoop
if (!$RunAsAdmin -and (Test-IsAdministrator)) {
# Exception: Windows Sandbox, GitHub Actions CI
$exception = ($env:USERNAME -eq 'WDAGUtilityAccount') -or ($env:GITHUB_ACTIONS -eq 'true' -and $env:CI -eq 'true')
# Exception: CI, Windows Sandbox
$exception = $env:CI -or ($env:USERNAME -eq 'WDAGUtilityAccount')
if (!$exception) {
Deny-Install 'Running the installer as administrator is disabled by default, see https://github.com/ScoopInstaller/Install#for-admin for details.'
}
Expand Down Expand Up @@ -575,10 +607,10 @@ function Test-CommandAvailable {

function Install-Scoop {
Write-InstallInfo 'Initializing...'
# Validate install parameters
Test-ValidateParameter
# Check prerequisites
Test-Prerequisite
# Validate install parameters
Test-ValidateParameter
# Enable TLS 1.2
Optimize-SecurityProtocol

Expand Down Expand Up @@ -680,12 +712,36 @@ function Write-DebugInfo {
Write-Verbose "SCOOP_CONFIG_HOME: $SCOOP_CONFIG_HOME"
}

function Test-ShouldRunInstall {
param(
[String] $InvocationName
)

# not dot-sourced, run the install flow
if ($InvocationName -ne '.') {
return $true
}

# dot-sourced, but not in CI, don't run the install flow
if (-not $env:CI) {
return $false
}

# dot-sourced, in CI, determined by env SCOOP_NOINSTALL
$should = $true
if ($null -ne $env:SCOOP_NOINSTALL) {
$value = $env:SCOOP_NOINSTALL.ToString().Trim().ToLowerInvariant()
if ($value -in @('1', 'true', 'yes', 'on')) {
$should = $false
}
}

return $should
}

# Prepare variables
$IS_EXECUTED_FROM_IEX = ($null -eq $MyInvocation.MyCommand.Path)

# Abort when the language mode is restricted
Test-LanguageMode

# Scoop root directory
$SCOOP_DIR = $ScoopDir, $env:SCOOP, "$env:USERPROFILE\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
# Scoop global apps directory
Expand All @@ -709,14 +765,23 @@ $SCOOP_MAIN_BUCKET_REPO = 'https://github.com/ScoopInstaller/Main/archive/master
$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
$ErrorActionPreference = 'Stop'

# Logging debug info
Write-DebugInfo $PSBoundParameters
# Bootstrap function
Install-Scoop

# Reset $ErrorActionPreference to original value
$ErrorActionPreference = $oldErrorActionPreference
# The install flow triggers only when the script is executed directly, but
# not when dot-sourced. Dot-sourcing the installer will not trigger the
# installation, and only the functions will be loaded, e.g., for testing.
# Downstreams can call `Install-Scoop` explicitly to start the installation.
# In CI, dot-sourced execution is allowed by default as GitHub Actions executors
# run the job step in a dot-sourced session. To disable the install flow in CI,
# set `SCOOP_NOINSTALL=true` to force dot-sourced CI sessions to only
# load functions.
$shouldRunInstall = Test-ShouldRunInstall -InvocationName $MyInvocation.InvocationName
if ($shouldRunInstall) {
$oldErrorActionPreference = $ErrorActionPreference
try {
$ErrorActionPreference = 'Stop'
Test-LanguageMode
Write-DebugInfo $PSBoundParameters
Install-Scoop
} finally {
$ErrorActionPreference = $oldErrorActionPreference
}
}
56 changes: 28 additions & 28 deletions scoop.json
Original file line number Diff line number Diff line change
@@ -1,61 +1,61 @@
{
"buckets": [
{
"Name": "main",
"Source": "https://github.com/ScoopInstaller/Main.git",
"Updated": "2026-03-01T20:30:21+00:00",
"Manifests": 1449
}
],
"apps": [
{
"Updated": "2026-08-31T02:27:08.6767247+00:00",
"Info": "",
"Name": "7zip",
"Updated": "2026-03-02T00:11:42.5461877+00:00",
"Source": "main",
"Version": "26.00"
"Name": "7zip",
"Version": "26.02"
},
{
"Updated": "2026-08-31T02:27:21.6952543+00:00",
"Info": "",
"Name": "gcc",
"Updated": "2026-03-02T00:11:55.5410061+00:00",
"Source": "main",
"Name": "gcc",
"Version": "15.2.0"
},
{
"Updated": "2026-08-31T02:28:22.5891156+00:00",
"Info": "",
"Name": "go",
"Updated": "2026-03-02T00:12:48.7592051+00:00",
"Source": "main",
"Version": "1.26.0"
"Name": "go",
"Version": "1.27.0"
},
{
"Updated": "2026-08-31T02:28:22.8976333+00:00",
"Info": "",
"Name": "jq",
"Updated": "2026-03-02T00:12:49.0700649+00:00",
"Source": "main",
"Version": "1.8.1"
"Name": "jq",
"Version": "1.8.2"
},
{
"Updated": "2026-08-31T02:29:17.4145778+00:00",
"Info": "",
"Name": "mingw",
"Updated": "2026-03-02T00:13:39.1655567+00:00",
"Source": "main",
"Version": "15.2.0-rt_v13-rev1"
"Name": "mingw",
"Version": "16.2.0-rt_v14-rev1"
},
{
"Updated": "2026-08-31T02:29:29.1451328+00:00",
"Info": "",
"Name": "nodejs",
"Updated": "2026-03-02T00:13:47.8899354+00:00",
"Source": "main",
"Version": "25.7.0"
"Name": "nodejs",
"Version": "26.8.1"
},
{
"Updated": "2026-08-31T02:29:29.8866633+00:00",
"Info": "",
"Name": "task",
"Updated": "2026-03-02T00:13:49.2456734+00:00",
"Source": "main",
"Version": "3.48.0"
"Name": "task",
"Version": "3.53.1"
}
],
"buckets": [
{
"Name": "main",
"Source": "https://github.com/ScoopInstaller/Main.git",
"Updated": "2026-08-31T01:08:29+00:00",
"Manifests": 1640
}
]
}