Skip to content

Commit c0a7362

Browse files
committed
feat(install): single-bundle install/upgrade scripts
Update the public install.sh, install.ps1 and upgrade.ps1 to the single-bundle layout. They download the one `apify-cli` bundle and delegate wrapper-script creation to `apify-cli install [--shims-only]` (the bundle is the single source of truth for the apify/actor shims), instead of dropping three binaries or hand-rolling the shim text. - install.sh: detect Windows ARM64 (MINGW) target; download apify-cli; run `apify-cli install` for shims + shell integration - install.ps1: detect arch via PROCESSOR_ARCHITECTURE (no baseline for ARM64); download apify-cli.exe; run `apify-cli install --shims-only` - upgrade.ps1: download the single apify-cli bundle and run `apify-cli install --shims-only`; clean up legacy .exe leftovers Stacked on top of the single-apify-cli-bundle PR, which provides the bundle, the `--shims-only` flag, and the upgrade URL contract these scripts rely on.
1 parent 2b3be74 commit c0a7362

3 files changed

Lines changed: 107 additions & 94 deletions

File tree

scripts/install/install.ps1

Lines changed: 79 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,15 @@ param(
88
# The following script is adapted from the bun.sh install script
99
# Licensed under the MIT License (https://github.com/oven-sh/bun/blob/main/LICENSE.md)
1010

11-
$allowedSystemTypes = @("x64-based", "ARM64-based")
12-
$currentSystemType = (Get-CimInstance Win32_ComputerSystem).SystemType
11+
$Arch = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment').PROCESSOR_ARCHITECTURE
1312

1413
# filter out 32 bit
15-
if (-not ($allowedSystemTypes | Where-Object { $currentSystemType -match $_ })) {
14+
if (-not ($Arch -eq "AMD64" -or $Arch -eq "ARM64")) {
1615
Write-Output "Install Failed:"
1716
Write-Output "Apify CLI for Windows is currently only available for 64-bit Windows and ARM64 Windows.`n"
1817
return 1
1918
}
2019

21-
if ($currentSystemType -match "ARM64") {
22-
Write-Warning "Warning:"
23-
Write-Warning "ARM64-based systems are not natively supported yet.`nThe install will still continue but Apify CLI might not work as intended.`n"
24-
}
25-
2620
# This corresponds to .win10_rs5 in build.zig
2721
$MinBuild = 17763;
2822
$MinBuildName = "Windows 10 1809 / Windows Server 2019"
@@ -114,31 +108,45 @@ function Install-Apify {
114108
return 1
115109
}
116110

117-
$Arch = if ($currentSystemType -match "ARM64") { "arm64" } else { "x64" }
118-
$IsBaseline = $ForceBaseline
111+
$IsARM64 = $Arch -eq "ARM64"
112+
$Arch = if ($IsARM64) { "arm64" } else { "x64" }
113+
$IsBaseline = $false
119114

120-
if (-not $IsBaseline) {
121-
$IsBaseline = !(
122-
Add-Type -MemberDefinition '[DllImport("kernel32.dll")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);' -Name 'Kernel32' -Namespace 'Win32' -PassThru
123-
)::IsProcessorFeaturePresent(40)
115+
# Baseline (non-AVX2) builds only exist for x64; native ARM64 bundles never need them.
116+
if (-not $IsARM64) {
117+
$IsBaseline = $ForceBaseline
118+
119+
if (-not $IsBaseline) {
120+
$IsBaseline = !(
121+
Add-Type -MemberDefinition '[DllImport("kernel32.dll")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);' -Name 'Kernel32' -Namespace 'Win32' -PassThru
122+
)::IsProcessorFeaturePresent(40)
123+
}
124124
}
125125

126126
$ApifyRoot = if ($env:APIFY_CLI_INSTALL) { $env:APIFY_CLI_INSTALL } else { "${Home}\.apify" }
127127
$ApifyBin = mkdir -Force "${ApifyRoot}\bin"
128128

129129
try {
130+
# Remove any previously installed binaries and wrapper scripts (including legacy ones from the
131+
# old two-bundle layout, and `.old` leftovers from a self-migration).
130132
foreach ($ExecutableName in $ExecutableNames) {
131-
Remove-Item "${ApifyBin}\${ExecutableName}.exe" -Force
133+
Remove-Item "${ApifyBin}\${ExecutableName}.exe" -Force -ErrorAction Ignore
134+
Remove-Item "${ApifyBin}\${ExecutableName}.exe.old" -Force -ErrorAction Ignore
135+
Remove-Item "${ApifyBin}\${ExecutableName}.cmd" -Force -ErrorAction Ignore
132136
}
133137

134-
# Alias apify to apify-cli, as npm does (because otherwise npx apify-cli wouldn't work)
135-
Remove-Item "${ApifyBin}\apify-cli.exe" -Force
138+
# apify-cli.exe is the canonical binary. We guard the removal with Test-Path so a fresh install
139+
# doesn't error on a missing file, but deliberately let a lock error surface (no -ErrorAction
140+
# Ignore) so the UnauthorizedAccessException handler below can tell the user to close the running CLI.
141+
if (Test-Path "${ApifyBin}\apify-cli.exe") {
142+
Remove-Item "${ApifyBin}\apify-cli.exe" -Force
143+
}
136144
}
137145
catch [System.Management.Automation.ItemNotFoundException] {
138146
# ignore
139147
}
140148
catch [System.UnauthorizedAccessException] {
141-
$openProcesses = Get-Process -Name apify | Where-Object { $_.Path -eq "${ApifyBin}\apify.exe" }
149+
$openProcesses = Get-Process -Name apify, apify-cli -ErrorAction Ignore | Where-Object { $_.Path -like "${ApifyBin}\*" }
142150
if ($openProcesses.Count -gt 0) {
143151
Write-Output "Install Failed - An older installation exists and is open. Please close open Apify CLI processes and try again."
144152
return 1
@@ -167,55 +175,66 @@ function Install-Apify {
167175

168176
$null = mkdir -Force $ApifyBin
169177

170-
foreach ($ExecutableName in $ExecutableNames) {
171-
$FileName = "${ExecutableName}.exe"
172-
$Target = "${ExecutableName}-${Version}-windows-${Arch}${IsBaseline ? '-baseline' : ''}"
178+
# We now ship a single `apify-cli.exe` bundle. The `apify` and `actor` commands are `.cmd` wrapper
179+
# scripts that invoke it with APIFY_CLI_ENTRYPOINT set, instead of dropping the same binary three times.
180+
$FileName = "apify-cli.exe"
181+
$Target = "apify-cli-${Version}-windows-${Arch}${IsBaseline ? '-baseline' : ''}"
173182

174-
$DownloadURL = "${BaseURL}${Target}.exe"
175-
$DownloadPath = "${ApifyBin}\${FileName}"
183+
$DownloadURL = "${BaseURL}${Target}.exe"
184+
$DownloadPath = "${ApifyBin}\${FileName}"
176185

177-
curl.exe "-#SfLo" "$DownloadPath" "$DownloadURL"
186+
curl.exe "-#SfLo" "$DownloadPath" "$DownloadURL"
178187

179-
if ($LASTEXITCODE -ne 0) {
180-
Write-Warning "The command 'curl.exe $DownloadURL -o $DownloadPath' exited with code ${LASTEXITCODE}`nTrying an alternative download method..."
188+
if ($LASTEXITCODE -ne 0) {
189+
Write-Warning "The command 'curl.exe $DownloadURL -o $DownloadPath' exited with code ${LASTEXITCODE}`nTrying an alternative download method..."
181190

182-
try {
183-
# Use Invoke-RestMethod instead of Invoke-WebRequest because Invoke-WebRequest breaks on
184-
# some machines
185-
Invoke-RestMethod -Uri $DownloadURL -OutFile $DownloadPath
186-
}
187-
catch {
188-
Write-Output "Install Failed - could not download $DownloadURL"
189-
Write-Output "The command 'Invoke-RestMethod $DownloadURL -OutFile $DownloadPath' exited with code ${LASTEXITCODE}`n"
190-
return 1
191-
}
191+
try {
192+
# Use Invoke-RestMethod instead of Invoke-WebRequest because Invoke-WebRequest breaks on
193+
# some machines
194+
Invoke-RestMethod -Uri $DownloadURL -OutFile $DownloadPath
192195
}
193-
194-
$ApifyVersion = "$(& "${ApifyBin}\${FileName}" --version)"
195-
if ($LASTEXITCODE -eq 1073741795) {
196-
# STATUS_ILLEGAL_INSTRUCTION
197-
if ($IsBaseline) {
198-
Write-Output "Install Failed - apify.exe (baseline) is not compatible with your CPU.`n"
199-
return 1
200-
}
201-
202-
Write-Output "Install Failed - apify.exe is not compatible with your CPU. This should have been detected before downloading.`n"
203-
Write-Output "Attempting to download apify.exe (baseline) instead.`n"
204-
205-
Install-Apify -Version $Version -ForceBaseline $True
196+
catch {
197+
Write-Output "Install Failed - could not download $DownloadURL"
198+
Write-Output "The command 'Invoke-RestMethod $DownloadURL -OutFile $DownloadPath' exited with code ${LASTEXITCODE}`n"
206199
return 1
207200
}
201+
}
208202

209-
if ($LASTEXITCODE -ne 0) {
210-
Write-Output "Install Failed - could not verify apify.exe"
211-
Write-Output "The command '${ApifyBin}\apify.exe --version' exited with code ${LASTEXITCODE}`n"
203+
$ApifyVersion = "$(& "${ApifyBin}\${FileName}" --version)"
204+
if ($LASTEXITCODE -eq 1073741795) {
205+
# STATUS_ILLEGAL_INSTRUCTION
206+
if ($IsBaseline) {
207+
Write-Output "Install Failed - apify-cli.exe (baseline) is not compatible with your CPU.`n"
212208
return 1
213209
}
214210

215-
if ($ExecutableName -eq "apify") {
216-
# Alias apify to apify-cli, as npm does (because otherwise npx apify-cli wouldn't work)
217-
Copy-Item -Path "${ApifyBin}\${FileName}" -Destination "${ApifyBin}\apify-cli.exe" -Force
218-
}
211+
Write-Output "Install Failed - apify-cli.exe is not compatible with your CPU. This should have been detected before downloading.`n"
212+
Write-Output "Attempting to download apify-cli.exe (baseline) instead.`n"
213+
214+
Install-Apify -Version $Version -ForceBaseline $True
215+
return 1
216+
}
217+
218+
if ($LASTEXITCODE -ne 0) {
219+
Write-Output "Install Failed - could not verify apify-cli.exe"
220+
Write-Output "The command '${ApifyBin}\apify-cli.exe --version' exited with code ${LASTEXITCODE}`n"
221+
return 1
222+
}
223+
224+
# Let the bundle create the `apify`/`actor` wrapper scripts (.cmd, .ps1 and a POSIX shim for Git Bash).
225+
# Keeping the shim content in the bundle avoids duplicating it across the install/upgrade scripts.
226+
# Skip the bundle's automatic version check here - we just downloaded the requested version.
227+
$prevSkipCheck = $env:APIFY_CLI_SKIP_UPDATE_CHECK
228+
$env:APIFY_CLI_SKIP_UPDATE_CHECK = "1"
229+
try {
230+
& "${ApifyBin}\${FileName}" install --shims-only
231+
}
232+
finally {
233+
$env:APIFY_CLI_SKIP_UPDATE_CHECK = $prevSkipCheck
234+
}
235+
if ($LASTEXITCODE -ne 0) {
236+
Write-Output "Install Failed - could not create the apify/actor wrapper scripts (exit code ${LASTEXITCODE})`n"
237+
return 1
219238
}
220239

221240
$UpgradeScriptPath = "${ApifyBin}\upgrade.ps1"
@@ -241,20 +260,20 @@ function Install-Apify {
241260
$C_DIM = [char]27 + "[0;2m"
242261

243262
Write-Output "${C_GREEN}Apify and Actor CLI ${ApifyVersion} were installed successfully!${C_RESET}"
244-
Write-Output "${C_DIM}The binaries are located at ${ApifyBin}\apify.exe and ${ApifyBin}\actor.exe${C_RESET}`n"
263+
Write-Output "${C_DIM}The bundle is located at ${ApifyBin}\apify-cli.exe (invoked via the apify.cmd and actor.cmd wrappers)${C_RESET}`n"
245264

246265
$hasExistingOther = $false;
247266
try {
248267
$existing = Get-Command apify -ErrorAction
249-
if ($existing.Source -ne "${ApifyBin}\apify.exe") {
250-
Write-Warning "Note: Another apify.exe is already in %PATH% at $($existing.Source)`nTyping 'apify' in your terminal will not use what was just installed.`n"
268+
if ($existing.Source -ne "${ApifyBin}\apify.cmd") {
269+
Write-Warning "Note: Another apify is already in %PATH% at $($existing.Source)`nTyping 'apify' in your terminal will not use what was just installed.`n"
251270
$hasExistingOther = $true;
252271
}
253272
}
254273
catch {}
255274

256275
if (!$hasExistingOther) {
257-
# Only try adding to path if there isn't already a apify.exe in the path
276+
# Only try adding to path if there isn't already an apify in the path
258277
$Path = (Get-Env -Key "Path") -split ';'
259278
if ($Path -notcontains $ApifyBin) {
260279
$Path += $ApifyBin

scripts/install/install.sh

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ case $platform in
5858
'Linux aarch64' | 'Linux arm64')
5959
target=linux-arm64
6060
;;
61+
'MINGW64'*'ARM64'* | 'MINGW64'*'aarch64'*)
62+
target=windows-arm64
63+
;;
6164
'MINGW64'*)
6265
target=windows-x64
6366
;;
@@ -117,8 +120,6 @@ fetch_latest_version() {
117120
echo "$version"
118121
}
119122

120-
executable_names=("apify" "actor")
121-
122123
if [[ $# = 0 || $1 = "latest" ]]; then
123124
version=$(fetch_latest_version)
124125
else
@@ -133,14 +134,6 @@ else
133134
version=${version#v}
134135
fi
135136

136-
# Function to construct download URL
137-
construct_download_url() {
138-
local cli_name="$1"
139-
local edition="$2"
140-
141-
echo "https://github.com/apify/apify-cli/releases/download/v${version}/${cli_name}-${version}-${edition}"
142-
}
143-
144137
install_env=APIFY_CLI_INSTALL
145138
install_dir=${!install_env:-$HOME/.apify}
146139
bin_dir=$install_dir/bin
@@ -150,30 +143,24 @@ if [[ ! -d $bin_dir ]]; then
150143
error "Failed to create install directory \"$bin_dir\""
151144
fi
152145

153-
for executable_name in "${executable_names[@]}"; do
154-
download_url=$(construct_download_url "$executable_name" "$target")
155-
output_filename="${executable_name}"
156-
157-
info "Downloading $executable_name bundle for version $version and target $target"
146+
# We now ship a single `apify-cli` bundle. The `apify` and `actor` commands are tiny wrapper scripts
147+
# that invoke it with APIFY_CLI_ENTRYPOINT set, instead of dropping the same binary three times.
148+
download_url="https://github.com/apify/apify-cli/releases/download/v${version}/apify-cli-${version}-${target}"
158149

159-
curl --fail --location --progress-bar --output "$bin_dir/$output_filename" "$download_url" ||
160-
error "Failed to download $executable_name bundle for version $version and target $target (might not exist for this platform/arch combination)"
150+
info "Downloading apify-cli bundle for version $version and target $target"
161151

162-
chmod +x "$bin_dir/$output_filename" ||
163-
error "Failed to set permissions on $executable_name executable"
152+
curl --fail --location --progress-bar --output "$bin_dir/apify-cli" "$download_url" ||
153+
error "Failed to download apify-cli bundle for version $version and target $target (might not exist for this platform/arch combination)"
164154

165-
# Alias apify to apify-cli, as npm does (because otherwise npx apify-cli wouldn't work)
166-
if [[ $executable_name = "apify" ]]; then
167-
cp "$bin_dir/$output_filename" "$bin_dir/apify-cli"
168-
fi
169-
done
155+
chmod +x "$bin_dir/apify-cli" ||
156+
error "Failed to set permissions on apify-cli executable"
170157

171-
# Invoke the CLI to handle shell integrations nicely
158+
# Invoke the bundle to create the `apify`/`actor` wrapper scripts and handle shell integration.
172159
# When running the script via `curl xxx | bash`, stdin is the script that gets consumed by bash.
173160
# If stdin is not a tty and we have a readable /dev/tty, tell Node.js to open /dev/tty itself
174161
# (shell-level redirects don't support raw mode properly for Node.js/Inquirer).
175162
if ! [ -t 0 ] && [ -r /dev/tty ]; then
176-
PROVIDED_INSTALL_DIR="$install_dir" FINAL_BIN_DIR="$bin_dir" APIFY_OPEN_TTY=1 "$bin_dir/apify" install
163+
PROVIDED_INSTALL_DIR="$install_dir" FINAL_BIN_DIR="$bin_dir" APIFY_CLI_SKIP_UPDATE_CHECK=1 APIFY_OPEN_TTY=1 "$bin_dir/apify-cli" install
177164
else
178-
PROVIDED_INSTALL_DIR="$install_dir" FINAL_BIN_DIR="$bin_dir" "$bin_dir/apify" install
165+
PROVIDED_INSTALL_DIR="$install_dir" FINAL_BIN_DIR="$bin_dir" APIFY_CLI_SKIP_UPDATE_CHECK=1 "$bin_dir/apify-cli" install
179166
fi

scripts/install/upgrade.ps1

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ $UpgradeScriptURL = "https://raw.githubusercontent.com/apify/apify-cli/refs/head
1717

1818
$URLArray = $AllUrls -split ','
1919

20-
if ($URLArray.Count -ne 2) {
21-
Write-Error "URL parameter must contain exactly 2 comma-delimited URLs"
20+
if ($URLArray.Count -lt 1) {
21+
Write-Error "URL parameter must contain at least 1 URL"
2222
exit 1
2323
}
2424

@@ -116,13 +116,20 @@ function Download-File-To-Location {
116116

117117
}
118118

119-
foreach ($URL in $URLArray) {
120-
$URLSplit = $URL -split '/'
121-
$FullCLIName = $URLSplit[-1]
119+
# We now ship a single `apify-cli` bundle. Download it (the URL list may contain backwards-compatible
120+
# backup URLs too, but they are all copies of the same bundle, so the first one is enough).
121+
Download-File-To-Location -URL $URLArray[0] -FileName "apify-cli" -Location $InstallLocation -Type 0 -Version $Version
122122

123-
$CLIName = $FullCLIName.Split('-')[0]
123+
# Let the freshly downloaded bundle (re)create the `apify`/`actor` wrapper scripts (.cmd, .ps1 and a POSIX
124+
# shim), so the shim content lives in one place rather than being duplicated here.
125+
# Skip the bundle's automatic version check - we just downloaded the requested version.
126+
$env:APIFY_CLI_SKIP_UPDATE_CHECK = "1"
127+
& (Join-Path $InstallLocation "apify-cli.exe") install --shims-only
124128

125-
Download-File-To-Location -URL $URL -FileName $CLIName -Location $InstallLocation -Type 0 -Version $Version
129+
# Clean up any legacy full bundles the wrappers replace, so the wrappers (not the `.exe`) resolve on PATH.
130+
foreach ($Entrypoint in @("apify", "actor")) {
131+
Remove-Item -Path (Join-Path $InstallLocation "${Entrypoint}.exe") -Force -ErrorAction Ignore
132+
Remove-Item -Path (Join-Path $InstallLocation "${Entrypoint}.exe.old") -Force -ErrorAction Ignore
126133
}
127134

128135
# Download the updated upgrade script (should rarely change but just in case)

0 commit comments

Comments
 (0)