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
60 changes: 50 additions & 10 deletions scripts/build-cli-bundles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ At the time of writing (~2025-05-31), the bundles are created using Bun as the r
When node stabilizes SEA (https://nodejs.org/api/single-executable-applications.html) [and supports ESM -.-], this code can be adapted to build it using that instead (but cross-platform will be a CI experience)
*/

import { readFileSync } from 'node:fs';
import { readFile, rm, writeFile } from 'node:fs/promises';
import { basename } from 'node:path';

import { $, fileURLToPath } from 'bun';
import { $, type Build, build, fileURLToPath } from 'bun';

import { version } from '../package.json' with { type: 'json' };

Expand All @@ -30,13 +31,11 @@ const targets = (() => {
'bun-darwin-arm64-baseline',
'bun-linux-x64-musl',
'bun-linux-arm64-musl',
'bun-linux-x64-musl-baseline',
'bun-linux-arm64-musl-baseline',
];
] satisfies Build.CompileTarget[];
}

if (process.platform === 'win32') {
return ['bun-windows-x64', 'bun-windows-x64-baseline'];
return ['bun-windows-x64', 'bun-windows-x64-baseline'] satisfies Build.CompileTarget[];
}

return [
Expand All @@ -50,9 +49,7 @@ const targets = (() => {
'bun-darwin-arm64-baseline',
'bun-linux-x64-musl',
'bun-linux-arm64-musl',
'bun-linux-x64-musl-baseline',
'bun-linux-arm64-musl-baseline',
];
] satisfies Build.CompileTarget[];
})();

const entryPoints = [
Expand All @@ -77,6 +74,35 @@ await writeFile(metadataFile, newContent);
for (const entryPoint of entryPoints) {
const cliName = basename(entryPoint, '.ts');

const lines = readFileSync(entryPoint, 'utf-8').split('\n');
lines.splice(1, 0, 'import "proxy-agent";');

// Step 1: create one fat JS file with node resolver to ensure no imports point to non-node export conditions
const result = await build({
entrypoints: [entryPoint],
files: {
[entryPoint]: lines.join('\n'),
},
outdir: fileURLToPath(new URL(`../bundles/fat-clis`, import.meta.url)),
conditions: 'node',
target: 'bun',
sourcemap: 'none',
});

const entrypointResultFilePath = result.outputs[0]!.path;

// Fix apify client js (it now lazy loads proxy-agent, which makes bun skip it from the bundle)
{
const entrypointResultFileContent = await result.outputs[0]!.text();

const newEntrypointResultFileContent = entrypointResultFileContent.replace(
`(0, utils_1.dynamicNodeImport)("proxy-agent")`,
`Promise.resolve().then(() => import_proxy_agent)`,
);

await writeFile(entrypointResultFilePath, newEntrypointResultFileContent);
}

for (const target of targets) {
// eslint-disable-next-line prefer-const -- somehow it cannot tell that os and arch cannot be "const" while the rest are let
let [, os, arch, musl, baseline] = target.split('-');
Expand All @@ -88,6 +114,7 @@ for (const entryPoint of entryPoints) {

// If we are building on Windows ARM64, even though the target is x64, we mark it as "arm64" (there are some weird errors when compiling on x64
// and running on arm64). Hopefully bun will get arm64 native builds
// TODO: Vlad remove this in a subsequent PR as Bun now has native arm64 windows builds
if (os === 'windows' && process.platform === 'win32') {
const systemType = await $`pwsh -c "(Get-CimInstance Win32_ComputerSystem).SystemType"`.text();

Expand All @@ -108,8 +135,21 @@ for (const entryPoint of entryPoints) {
const outFile = fileURLToPath(new URL(`../bundles/${fileName}`, import.meta.url));

console.log(`Building ${cliName} for ${target} (result: ${fileName})...`);
// TODO: --sourcemap crashes for w/e reason and --bytecode doesn't support ESM (TLA to be exact)
await $`bun build --compile --minify --target=${target} --outfile=${outFile} ${entryPoint}`;

// Step 2: create the final executable bundle
await build({
entrypoints: [entrypointResultFilePath],
compile: {
outfile: outFile,
target,
},
format: 'esm',
minify: {
identifiers: true,
keepNames: true,
},
bytecode: true,
});

// Remove the arch override
await writeFile(metadataFile, newContent);
Expand Down
124 changes: 124 additions & 0 deletions scripts/install/dev-test-install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/usr/bin/env bash
set -euo pipefail

# This script should be used like so: `/bin/cat dev-test-install.sh | bash`

# Reset
Color_Off=''

# Regular Colors
Red=''
Dim='' # White

if [[ -t 1 ]]; then
# Reset
Color_Off='\033[0m' # Text Reset

# Regular Colors
Red='\033[0;31m' # Red
Dim='\033[0;2m' # White
fi

error() {
echo -e "${Red}error${Color_Off}:" "$@" >&2
exit 1
}

info() {
echo -e "${Dim}$@ ${Color_Off}"
}

platform=$(uname -ms)

case $platform in
'Darwin x86_64')
target=darwin-x64
;;
'Darwin arm64')
target=darwin-arm64
;;
'Linux aarch64' | 'Linux arm64')
target=linux-arm64
;;
'MINGW64'*)
target=windows-x64
;;
'Linux x86_64' | *)
target=linux-x64
;;
esac

case "$target" in
'linux'*)
if [ -f /etc/alpine-release ]; then
target="$target-musl"
fi
;;
esac

# If AVX2 isn't supported, use the -baseline build
case "$target" in
'darwin-x64'*)
if [[ $(sysctl -a | grep machdep.cpu | grep AVX2) == '' ]]; then
target="$target-baseline"
fi
;;
'linux-x64'*)
# If AVX2 isn't supported, use the -baseline build
if [[ $(cat /proc/cpuinfo | grep avx2) = '' ]]; then
target="$target-baseline"
fi
;;
esac

install_env=APIFY_CLI_INSTALL
install_dir=${!install_env:-$HOME/.apify}
bin_dir=$install_dir/bin

if [[ ! -d $bin_dir ]]; then
mkdir -p "$bin_dir" ||
error "Failed to create install directory \"$bin_dir\""
fi

# Ensure we are in the apify-cli root by checking for ./package.json
if [[ ! -f ./package.json ]]; then
error "Not in the apify-cli root"
fi

echo "Install directory: $install_dir"
echo "Bin directory: $bin_dir"

# Ensure we have bun installed
if ! command -v bun &> /dev/null; then
error "bun could not be found. Please install it from https://bun.sh/docs/installation"
exit 1
fi

# Check package.json for the version
version=$(jq -r '.version' package.json)
echo "Version: $version"

info "Installing dependencies"
yarn

info "Building bundles"
yarn insert-cli-metadata && yarn build-bundles && git checkout -- src/lib/hooks/useCLIMetadata.ts

info "Installing bundles"

executable_names=("apify" "actor")

for executable_name in "${executable_names[@]}"; do
output_filename="${executable_name}"

info "Installing $executable_name bundle for version $version and target $target"

cp "bundles/$executable_name-$version-$target" "$bin_dir/$output_filename"
chmod +x "$bin_dir/$output_filename"
done

if ! [ -t 0 ] && [ -r /dev/tty ]; then
PROVIDED_INSTALL_DIR="$install_dir" FINAL_BIN_DIR="$bin_dir" APIFY_OPEN_TTY=1 "$bin_dir/apify" install
else
PROVIDED_INSTALL_DIR="$install_dir" FINAL_BIN_DIR="$bin_dir" "$bin_dir/apify" install
fi
46 changes: 8 additions & 38 deletions scripts/install/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,15 @@ Color_Off=''

# Regular Colors
Red=''
Green=''
Dim='' # White

# Bold
Bold_White=''
Bold_Green=''

if [[ -t 1 ]]; then
# Reset
Color_Off='\033[0m' # Text Reset

# Regular Colors
Red='\033[0;31m' # Red
Green='\033[0;32m' # Green
Dim='\033[0;2m' # White

# Bold
Bold_Green='\033[1;32m' # Bold Green
Bold_White='\033[1m' # Bold White
fi

error() {
Expand All @@ -49,14 +39,6 @@ info() {
echo -e "${Dim}$@ ${Color_Off}"
}

info_bold() {
echo -e "${Bold_White}$@ ${Color_Off}"
}

success() {
echo -e "${Green}$@ ${Color_Off}"
}

if [[ $# -gt 1 ]]; then
error 'Too many arguments, only 1 is allowed. The first can be a specific tag of Apify CLI to install. (e.g. "0.28.0")'
fi
Expand Down Expand Up @@ -101,10 +83,6 @@ if [[ $target = darwin-x64 ]]; then
fi
fi

GITHUB=${GITHUB-"https://github.com"}

github_repo="$GITHUB/apify/apify-cli"

# If AVX2 isn't supported, use the -baseline build
case "$target" in
'darwin-x64'*)
Expand Down Expand Up @@ -190,20 +168,12 @@ for executable_name in "${executable_names[@]}"; do
fi
done

tildify() {
if [[ $1 = $HOME/* ]]; then
local replacement=\~/

echo "${1/$HOME\//$replacement}"
else
echo "$1"
fi
}

echo ''
echo ''
success "Apify and Actor CLI $version were installed successfully!"
info "The binaries are located at $Bold_Green$(tildify "$bin_dir/apify") ${Dim}and $Bold_Green$(tildify "$bin_dir/actor")"

# Invoke the CLI to handle shell integrations nicely
PROVIDED_INSTALL_DIR="$install_dir" FINAL_BIN_DIR="$bin_dir" "$bin_dir/apify" install
# When running the script via `curl xxx | bash`, stdin is the script that gets consumed by bash.
# If stdin is not a tty and we have a readable /dev/tty, tell Node.js to open /dev/tty itself
# (shell-level redirects don't support raw mode properly for Node.js/Inquirer).
if ! [ -t 0 ] && [ -r /dev/tty ]; then
PROVIDED_INSTALL_DIR="$install_dir" FINAL_BIN_DIR="$bin_dir" APIFY_OPEN_TTY=1 "$bin_dir/apify" install
else
PROVIDED_INSTALL_DIR="$install_dir" FINAL_BIN_DIR="$bin_dir" "$bin_dir/apify" install
fi
Loading