#!/usr/bin/env bash## Builds a Warp binary and bundles it up for distribution.set -eWORKSPACE_ROOT_DIR="$(pwd)"cleanup() { # Delete the directory that was used as a staging area during the bundling # process. if [ -d "$DIST_DIR" ]; then rm -rf "$DIST_DIR" fi}# Run cleanup() when the script terminates (whether it succeeded or failed).trap cleanup EXIT# By default we build dev bundles.RELEASE_CHANNEL="dev"FEATURES="release_bundle,crash_reporting"EXTRA_FEATURES=""PACKAGES=( appimage )BUILD="true"BUILD_ARCH="$(uname -m)"DEBUG=falseARTIFACT="app"# Cache all params so we can pass them to downstream scripts.ALL_PARAMS=$@PARAMS=""while (( "$#" )); do case "$1" in --debug) DEBUG=true shift ;; --check-only) echo 'Only running `cargo check` and not producing a bundle.' CHECK_ONLY="true" shift ;; --skip-build) BUILD="false" shift ;; --nouniversal) # Discard the --nouniversal argument, which is only used for macOS # bundles. shift ;; -c|--channel) if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then RELEASE_CHANNEL=$2 shift 2 else echo "Error: Argument for $1 is missing" >&2 exit 1 fi ;; --release-tag) if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then echo "Setting release tag to $2" export GIT_RELEASE_TAG=$2 shift 2 else echo "Error: Argument for $1 is missing" >&2 exit 1 fi ;; --packages) PACKAGES=( $(IFS=, ; echo $2) ) shift 2 ;; --features) if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then EXTRA_FEATURES="$2" shift 2 else echo "Error: Argument for $1 is missing" >&2 exit 1 fi ;; --artifact) if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then if [[ "$2" != "app" && "$2" != "cli" && "$2" != "warpctrl" && "$2" != "tui" ]]; then echo "Error: --artifact must be 'app', 'cli', 'warpctrl', or 'tui', got '$2'" >&2 exit 1 fi ARTIFACT="$2" shift 2 else echo "Error: Argument for $1 is missing" >&2 exit 1 fi ;; --arch) if [ -n "$2" ]; then if [ "$2" = "aarch64" -o "$2" = "x86_64" ]; then echo "Setting architecture to $2" export BUILD_ARCH=$2 shift 2 else echo "Error: Argument for $1 is invalid; got '$2' but expected 'aarch64' or 'x86_64'." >&2 exit 1 fi else echo "Error: Argument for $1 is missing" >&2 exit 1 fi ;; *) # preserve positional arguments PARAMS="$PARAMS $1" shift ;; esacdone# set positional arguments in their proper placeeval set -- "$PARAMS"# Linux has no transparent cross-arch execution (unlike macOS's Rosetta), so a# binary built for a different architecture than the host can never be run# here. Resolve the host architecture once and reuse it both for the TUI# native-build restriction below and for deciding whether the just-built# binary can stand in for a settings-schema executable further down.case "$(uname -m)" in arm64|aarch64) HOST_ARCH="aarch64" ;; x86_64|amd64) HOST_ARCH="x86_64" ;; *) echo "Error: Unsupported host architecture: $(uname -m)" >&2 exit 1 ;;esac# TUI Linux bundles use the native GNU target because their clipboard backends# link against host Wayland/X11 libraries. Refuse to compile on a different# architecture so an x86 binary can never be published under an ARM filename# (or vice versa). Packaging-only runs may still stage a prebuilt native binary.if [[ "$ARTIFACT" == "tui" && "$BUILD" == "true" && "$BUILD_ARCH" != "$HOST_ARCH" ]]; then echo "Error: TUI bundles must be built natively; requested '$BUILD_ARCH' on '$HOST_ARCH'." >&2 exit 1fi# Statically compile the CLI and warpctrl artifacts so they can run on older Linux distros.if [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "warpctrl" ]]; then TARGET_TRIPLE="${BUILD_ARCH}-unknown-linux-musl" # Only configure the musl toolchain when we are actually compiling. Packaging-only # runs (--skip-build) just need TARGET_TRIPLE to locate the prebuilt binary, and # configuring the toolchain there would force an unnecessary (and potentially # failing) musl-cross download on hosts that never compile. if [[ "$BUILD" == "true" ]]; then source "$WORKSPACE_ROOT_DIR/script/linux/configure_musl_toolchain" "$TARGET_TRIPLE" # Make sure we have the rust musl target available. rustup target add "$TARGET_TRIPLE" fifiCARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$WORKSPACE_ROOT_DIR/target}"CARGO_TARGET_OUTPUT_ROOT="$CARGO_TARGET_DIR${TARGET_TRIPLE:+/$TARGET_TRIPLE}"DIST_DIR="$CARGO_TARGET_OUTPUT_ROOT/dist"if [[ $DEBUG = true ]]; then CARGO_PROFILE="dev"elif [[ $RELEASE_CHANNEL = "local" || $RELEASE_CHANNEL = "dev" ]]; then # For dev bundles, we want to enable debug assertions to # catch violations that would otherwise silently pass in # a normal release build (e.g. in stable). if [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "warpctrl" || "$ARTIFACT" == "tui" ]]; then CARGO_PROFILE="release-cli-debug_assertions" else CARGO_PROFILE="release-lto-debug_assertions" fielse if [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "warpctrl" || "$ARTIFACT" == "tui" ]]; then CARGO_PROFILE="release-cli" else CARGO_PROFILE="release-lto" fifiif [[ "$CARGO_PROFILE" == "dev" ]]; then CARGO_TARGET_OUTPUT_DIR="$CARGO_TARGET_OUTPUT_ROOT/debug"else CARGO_TARGET_OUTPUT_DIR="$CARGO_TARGET_OUTPUT_ROOT/$CARGO_PROFILE"fi# NOTE: if you change this path, update the "Clean stale bundle output"# steps in .github/workflows/create_release.yml so they continue to wipe# stale packages left in the shared Namespace runner cache.OUT_DIR="$CARGO_TARGET_OUTPUT_DIR/bundle/linux"mkdir -p "$OUT_DIR"# Update parameters based on the target release channel.## APP_NAME here must match the value used in Rust as the# application name; see app/src/channel.rs.## WARP_BIN is the name of the binary produced by cargo;# BINARY_NAME is the desired name of the binary in the final package.if [[ $RELEASE_CHANNEL = "local" ]]; then WARP_BIN="warp" BINARY_NAME="warp-local" APP_NAME="WarpLocal" FEATURES="$FEATURES,agent_mode_debug" export HANDLE_MARKDOWN=1elif [[ $RELEASE_CHANNEL = "dev" ]]; then WARP_BIN="dev" BINARY_NAME="warp-dev" APP_NAME="WarpDev" FEATURES="$FEATURES,agent_mode_debug" # Enable heap usage tracking & profiling using jemalloc through pprof. FEATURES="$FEATURES,jemalloc_pprof,heap_usage_tracking" export HANDLE_MARKDOWN=1elif [[ $RELEASE_CHANNEL = "preview" ]]; then WARP_BIN="preview" BINARY_NAME="warp-preview" APP_NAME="WarpPreview" FEATURES="$FEATURES,preview_channel" # Enable heap usage tracking & profiling using jemalloc through pprof. FEATURES="$FEATURES,jemalloc_pprof,heap_usage_tracking"elif [[ $RELEASE_CHANNEL = "stable" ]]; then WARP_BIN="stable" BINARY_NAME="warp" APP_NAME="Warp"elif [[ $RELEASE_CHANNEL = "oss" ]]; then WARP_BIN="warp-oss" BINARY_NAME="warp-oss" APP_NAME="WarpOss" # The OSS channel does not ship Sentry, so drop the crash_reporting feature # (which would otherwise pull in the Sentry SDK as a dependency). FEATURES="release_bundle"fi# Artifact-specific binary namingif [[ "$ARTIFACT" == "cli" ]]; then # For CLI artifacts, use oz instead of warp as the binary name. The OSS # channel is an exception: its CLI command name is `warp-oss`, matching # `Channel::cli_command_name` in the Rust source. if [[ $RELEASE_CHANNEL != "oss" ]]; then BINARY_NAME="${BINARY_NAME/warp/oz}" fielif [[ "$ARTIFACT" == "warpctrl" ]]; then BINARY_NAME="warpctrl" PACKAGES=()elif [[ "$ARTIFACT" == "tui" ]]; then case "$RELEASE_CHANNEL" in local) WARP_BIN="warp-tui" ;; oss) WARP_BIN="warp-tui-oss" ;; *) WARP_BIN="warp-tui-$RELEASE_CHANNEL" ;; esac BINARY_NAME="$WARP_BIN" PACKAGES=()fi# Artifact-specific configurationif [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "warpctrl" ]]; then FEATURES="$FEATURES,standalone" if [[ "$ARTIFACT" == "warpctrl" ]]; then FEATURES="$FEATURES,warp_control_cli" fielif [[ "$ARTIFACT" == "tui" ]]; then # The TUI is a separate console crate with its own per-channel bins and a # minimal feature set. It uses the native GNU target so the Linux clipboard # backends can link against the host's Wayland/X11 libraries. Deliberately # omit `voice_input` so portable bundles do not require the host ALSA runtime. WARP_TUI_FEATURES="release_bundle,standalone" if [[ "$RELEASE_CHANNEL" != "oss" ]]; then WARP_TUI_FEATURES="$WARP_TUI_FEATURES,crash_reporting" fielif [[ "$ARTIFACT" == "app" ]]; then # All channels ship the v3 classifier and v2 heuristic. FEATURES="$FEATURES,gui,nld_classifier_v3,nld_heuristic_v2"fiif [[ -n "$EXTRA_FEATURES" ]]; then if [[ "$ARTIFACT" == "tui" ]]; then WARP_TUI_FEATURES="$WARP_TUI_FEATURES,$EXTRA_FEATURES" else FEATURES="$FEATURES,$EXTRA_FEATURES" fifiBUNDLE_ID="dev.warp.$APP_NAME"EXECUTABLE_PATH="$CARGO_TARGET_OUTPUT_DIR/$WARP_BIN"DEBUG_EXECUTABLE_PATH="$EXECUTABLE_PATH.debug"# Note that this variable must be set (and exported!) before we compile the# binary, as it is read at compile time by Linux autoupdate logic (to know the# expected name of the AppImage when downloading updates).export APPIMAGE_NAME="$APP_NAME-$BUILD_ARCH.AppImage"# If we only want to check that compilation will succeed, perform the checks# then exit. We use this script to invoke `cargo check` to ensure that we are# using the same feature flags and profile that we would be using in production.if [[ "$CHECK_ONLY" == "true" ]]; then if [[ "$ARTIFACT" == "tui" ]]; then cargo check -p warp_tui --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$WARP_TUI_FEATURES" else cargo check -p warp --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$FEATURES" ${TARGET_TRIPLE:+--target $TARGET_TRIPLE} fi exit 0fi# Build the binary.if [[ "$BUILD" == "true" ]]; then echo "Building and bundling Warp for channel $RELEASE_CHANNEL and bundle id $BUNDLE_ID" if [[ "$ARTIFACT" == "tui" ]]; then cargo build -p warp_tui --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$WARP_TUI_FEATURES" else cargo build -p warp --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$FEATURES" ${TARGET_TRIPLE:+--target $TARGET_TRIPLE} fi echo "Making debug copy of '$EXECUTABLE_PATH' at '$DEBUG_EXECUTABLE_PATH'" cp "$EXECUTABLE_PATH" "$DEBUG_EXECUTABLE_PATH" echo "Stripping debug symbols from '$EXECUTABLE_PATH'" if [[ $RELEASE_CHANNEL = "dev" ]]; then # For dev builds, only strip debug symbols, as we want to keep some # symbols for profiling purposes. "${WARP_MUSL_STRIP:-strip}" --strip-debug "$EXECUTABLE_PATH" else # For production builds, strip all symbols, to keep the binary size # smaller. "${WARP_MUSL_STRIP:-strip}" --strip-all "$EXECUTABLE_PATH" fielse echo 'Skipping `cargo build` step due to --skip-build argument'fiif [[ "$ARTIFACT" == "warpctrl" ]]; then echo "Copying control-mode binary into $OUT_DIR/$WARP_BIN" cp "$EXECUTABLE_PATH" "$OUT_DIR/$WARP_BIN" WARPCTRL_SCRIPT_PATH="$OUT_DIR/warpctrl" echo "Creating warpctrl wrapper script at $WARPCTRL_SCRIPT_PATH" cat > "$WARPCTRL_SCRIPT_PATH" << EOF#!/usr/bin/env bashscript_dir="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)"exec "\$script_dir/$WARP_BIN" --warpctrl "\$@"EOF chmod +x "$WARPCTRL_SCRIPT_PATH"fiBINARY_PATH="$EXECUTABLE_PATH"if [[ "$ARTIFACT" == "warpctrl" ]]; then BINARY_PATH="$WARPCTRL_SCRIPT_PATH"elif [[ "$ARTIFACT" == "tui" ]]; then echo "Copying TUI binary into $OUT_DIR/$WARP_BIN" cp "$EXECUTABLE_PATH" "$OUT_DIR/$WARP_BIN" BINARY_PATH="$OUT_DIR/$WARP_BIN"fiif [[ "${SKIP_SETTINGS_SCHEMA:-}" != "1" && -z "${SETTINGS_SCHEMA_EXECUTABLE:-}" && -z "${SETTINGS_SCHEMA_SOURCE:-}" ]]; then if [[ "$ARTIFACT" == "tui" ]]; then echo "Error: TUI bundles require SETTINGS_SCHEMA_SOURCE or SETTINGS_SCHEMA_EXECUTABLE." >&2 exit 1 elif [[ "$BUILD" != "true" ]]; then echo "Error: --skip-build requires SETTINGS_SCHEMA_SOURCE or SETTINGS_SCHEMA_EXECUTABLE." >&2 exit 1 elif [[ "$BUILD_ARCH" != "$HOST_ARCH" ]]; then # The CLI/warpctrl artifacts can be cross-compiled (see the musl toolchain # setup above); the resulting binary cannot then be executed on this host # to generate a schema, so require the caller to supply one explicitly # instead of failing later with an opaque exec format error. echo "Error: Cannot execute the cross-compiled '$BUILD_ARCH' binary on this '$HOST_ARCH' host to generate a settings schema; pass SETTINGS_SCHEMA_SOURCE or SETTINGS_SCHEMA_EXECUTABLE." >&2 exit 1 else export SETTINGS_SCHEMA_EXECUTABLE="$EXECUTABLE_PATH" fifiif [[ "${SKIP_SETTINGS_SCHEMA:-}" != "1" && -n "${SETTINGS_SCHEMA_EXECUTABLE:-}" && -n "${SETTINGS_SCHEMA_SOURCE:-}" ]]; then echo "Error: SETTINGS_SCHEMA_EXECUTABLE and SETTINGS_SCHEMA_SOURCE are mutually exclusive." >&2 exit 1fiif [[ "${SKIP_SETTINGS_SCHEMA:-}" != "1" && -n "${SETTINGS_SCHEMA_EXECUTABLE:-}" ]]; then GENERATED_SETTINGS_SCHEMA="$CARGO_TARGET_OUTPUT_DIR/settings_schema.json" echo "Generating reusable settings schema at $GENERATED_SETTINGS_SCHEMA" "$SETTINGS_SCHEMA_EXECUTABLE" dump-settings-schema "$GENERATED_SETTINGS_SCHEMA" unset SETTINGS_SCHEMA_EXECUTABLE export SETTINGS_SCHEMA_SOURCE="$GENERATED_SETTINGS_SCHEMA"fi# Prepare bundled resources for standalone builds.if [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "warpctrl" || "$ARTIFACT" == "tui" ]]; then echo "Preparing standalone resources directory" BUNDLED_RESOURCES_DIR="$OUT_DIR/resources" "$WORKSPACE_ROOT_DIR/script/prepare_bundled_resources" "$BUNDLED_RESOURCES_DIR" "$RELEASE_CHANNEL"fi# If this is being run within a GitHub action, set an output variable with the# location of the binary so it can be referenced by subsequent actions, as well# as the directory containing all built packages.if [ "${GITHUB_ACTIONS}" == "true" ]; then echo "::echo::on" echo "executable_path=$BINARY_PATH" >> "$GITHUB_OUTPUT" echo "debug_executable_path=$DEBUG_EXECUTABLE_PATH" >> "$GITHUB_OUTPUT" echo "packages_dir=$OUT_DIR" >> "$GITHUB_OUTPUT" echo "bundled_resources_dir=${BUNDLED_RESOURCES_DIR:-}" >> "$GITHUB_OUTPUT" echo "settings_schema_path=${SETTINGS_SCHEMA_SOURCE:-}" >> "$GITHUB_OUTPUT" echo "::echo::off"fi# Make sure a variety of environment variables are available in downstream scripts.export \ WORKSPACE_ROOT_DIR \ DIST_DIR \ CARGO_TARGET_OUTPUT_DIR \ OUT_DIR \ RELEASE_CHANNEL \ BUNDLE_ID \ EXECUTABLE_PATH \ BINARY_NAME \ APPIMAGE_NAME \ BUILD_ARCH \ ARTIFACT \ CARGO_PROFILE# Build the AppImage bundle.if [[ ${PACKAGES[@]} =~ "appimage" ]]; then echo "Building AppImage..." "$WORKSPACE_ROOT_DIR/script/linux/bundle_appimage"fi# Build the .deb package.if [[ ${PACKAGES[@]} =~ "deb" ]]; then echo "Building .deb package..." "$WORKSPACE_ROOT_DIR/script/linux/bundle_deb"fi# Build the .rpm package.if [[ ${PACKAGES[@]} =~ "rpm" ]]; then echo "Building .rpm package..." "$WORKSPACE_ROOT_DIR/script/linux/bundle_rpm"fi# Build the Arch Linux package.if [[ ${PACKAGES[@]} =~ "arch" ]]; then echo "Building Arch Linux package..." "$WORKSPACE_ROOT_DIR/script/linux/bundle_arch"fi
#!/usr/bin/env bash
#
# Builds a Warp binary and bundles it up for distribution.
set -e
WORKSPACE_ROOT_DIR="$(pwd)"
cleanup() {
# Delete the directory that was used as a staging area during the bundling
# process.
if [ -d "$DIST_DIR" ]; then
rm -rf "$DIST_DIR"
fi
}
# Run cleanup() when the script terminates (whether it succeeded or failed).
trap cleanup EXIT
# By default we build dev bundles.
RELEASE_CHANNEL="dev"
FEATURES="release_bundle,crash_reporting"
EXTRA_FEATURES=""
PACKAGES=( appimage )
BUILD="true"
BUILD_ARCH="$(uname -m)"
DEBUG=false
ARTIFACT="app"
# Cache all params so we can pass them to downstream scripts.
ALL_PARAMS=$@
PARAMS=""
while (( "$#" )); do
case "$1" in
--debug)
DEBUG=true
shift
;;
--check-only)
echo 'Only running `cargo check` and not producing a bundle.'
CHECK_ONLY="true"
shift
;;
--skip-build)
BUILD="false"
shift
;;
--nouniversal)
# Discard the --nouniversal argument, which is only used for macOS
# bundles.
shift
;;
-c|--channel)
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
RELEASE_CHANNEL=$2
shift 2
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
--release-tag)
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
echo "Setting release tag to $2"
export GIT_RELEASE_TAG=$2
shift 2
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
--packages)
PACKAGES=( $(IFS=, ; echo $2) )
shift 2
;;
--features)
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
EXTRA_FEATURES="$2"
shift 2
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
--artifact)
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
if [[ "$2" != "app" && "$2" != "cli" && "$2" != "warpctrl" && "$2" != "tui" ]]; then
echo "Error: --artifact must be 'app', 'cli', 'warpctrl', or 'tui', got '$2'" >&2
exit 1
fi
ARTIFACT="$2"
shift 2
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
--arch)
if [ -n "$2" ]; then
if [ "$2" = "aarch64" -o "$2" = "x86_64" ]; then
echo "Setting architecture to $2"
export BUILD_ARCH=$2
shift 2
else
echo "Error: Argument for $1 is invalid; got '$2' but expected 'aarch64' or 'x86_64'." >&2
exit 1
fi
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
*) # preserve positional arguments
PARAMS="$PARAMS $1"
shift
;;
esac
done
# set positional arguments in their proper place
eval set -- "$PARAMS"
# Linux has no transparent cross-arch execution (unlike macOS's Rosetta), so a
# binary built for a different architecture than the host can never be run
# here. Resolve the host architecture once and reuse it both for the TUI
# native-build restriction below and for deciding whether the just-built
# binary can stand in for a settings-schema executable further down.
case "$(uname -m)" in
arm64|aarch64) HOST_ARCH="aarch64" ;;
x86_64|amd64) HOST_ARCH="x86_64" ;;
*)
echo "Error: Unsupported host architecture: $(uname -m)" >&2
exit 1
;;
esac
# TUI Linux bundles use the native GNU target because their clipboard backends
# link against host Wayland/X11 libraries. Refuse to compile on a different
# architecture so an x86 binary can never be published under an ARM filename
# (or vice versa). Packaging-only runs may still stage a prebuilt native binary.
if [[ "$ARTIFACT" == "tui" && "$BUILD" == "true" && "$BUILD_ARCH" != "$HOST_ARCH" ]]; then
echo "Error: TUI bundles must be built natively; requested '$BUILD_ARCH' on '$HOST_ARCH'." >&2
exit 1
fi
# Statically compile the CLI and warpctrl artifacts so they can run on older Linux distros.
if [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "warpctrl" ]]; then
TARGET_TRIPLE="${BUILD_ARCH}-unknown-linux-musl"
# Only configure the musl toolchain when we are actually compiling. Packaging-only
# runs (--skip-build) just need TARGET_TRIPLE to locate the prebuilt binary, and
# configuring the toolchain there would force an unnecessary (and potentially
# failing) musl-cross download on hosts that never compile.
if [[ "$BUILD" == "true" ]]; then
source "$WORKSPACE_ROOT_DIR/script/linux/configure_musl_toolchain" "$TARGET_TRIPLE"
# Make sure we have the rust musl target available.
rustup target add "$TARGET_TRIPLE"
fi
fi
CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$WORKSPACE_ROOT_DIR/target}"
CARGO_TARGET_OUTPUT_ROOT="$CARGO_TARGET_DIR${TARGET_TRIPLE:+/$TARGET_TRIPLE}"
DIST_DIR="$CARGO_TARGET_OUTPUT_ROOT/dist"
if [[ $DEBUG = true ]]; then
CARGO_PROFILE="dev"
elif [[ $RELEASE_CHANNEL = "local" || $RELEASE_CHANNEL = "dev" ]]; then
# For dev bundles, we want to enable debug assertions to
# catch violations that would otherwise silently pass in
# a normal release build (e.g. in stable).
if [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "warpctrl" || "$ARTIFACT" == "tui" ]]; then
CARGO_PROFILE="release-cli-debug_assertions"
else
CARGO_PROFILE="release-lto-debug_assertions"
fi
else
if [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "warpctrl" || "$ARTIFACT" == "tui" ]]; then
CARGO_PROFILE="release-cli"
else
CARGO_PROFILE="release-lto"
fi
fi
if [[ "$CARGO_PROFILE" == "dev" ]]; then
CARGO_TARGET_OUTPUT_DIR="$CARGO_TARGET_OUTPUT_ROOT/debug"
else
CARGO_TARGET_OUTPUT_DIR="$CARGO_TARGET_OUTPUT_ROOT/$CARGO_PROFILE"
fi
# NOTE: if you change this path, update the "Clean stale bundle output"
# steps in .github/workflows/create_release.yml so they continue to wipe
# stale packages left in the shared Namespace runner cache.
OUT_DIR="$CARGO_TARGET_OUTPUT_DIR/bundle/linux"
mkdir -p "$OUT_DIR"
# Update parameters based on the target release channel.
#
# APP_NAME here must match the value used in Rust as the
# application name; see app/src/channel.rs.
#
# WARP_BIN is the name of the binary produced by cargo;
# BINARY_NAME is the desired name of the binary in the final package.
if [[ $RELEASE_CHANNEL = "local" ]]; then
WARP_BIN="warp"
BINARY_NAME="warp-local"
APP_NAME="WarpLocal"
FEATURES="$FEATURES,agent_mode_debug"
export HANDLE_MARKDOWN=1
elif [[ $RELEASE_CHANNEL = "dev" ]]; then
WARP_BIN="dev"
BINARY_NAME="warp-dev"
APP_NAME="WarpDev"
FEATURES="$FEATURES,agent_mode_debug"
# Enable heap usage tracking & profiling using jemalloc through pprof.
FEATURES="$FEATURES,jemalloc_pprof,heap_usage_tracking"
export HANDLE_MARKDOWN=1
elif [[ $RELEASE_CHANNEL = "preview" ]]; then
WARP_BIN="preview"
BINARY_NAME="warp-preview"
APP_NAME="WarpPreview"
FEATURES="$FEATURES,preview_channel"
# Enable heap usage tracking & profiling using jemalloc through pprof.
FEATURES="$FEATURES,jemalloc_pprof,heap_usage_tracking"
elif [[ $RELEASE_CHANNEL = "stable" ]]; then
WARP_BIN="stable"
BINARY_NAME="warp"
APP_NAME="Warp"
elif [[ $RELEASE_CHANNEL = "oss" ]]; then
WARP_BIN="warp-oss"
BINARY_NAME="warp-oss"
APP_NAME="WarpOss"
# The OSS channel does not ship Sentry, so drop the crash_reporting feature
# (which would otherwise pull in the Sentry SDK as a dependency).
FEATURES="release_bundle"
fi
# Artifact-specific binary naming
if [[ "$ARTIFACT" == "cli" ]]; then
# For CLI artifacts, use oz instead of warp as the binary name. The OSS
# channel is an exception: its CLI command name is `warp-oss`, matching
# `Channel::cli_command_name` in the Rust source.
if [[ $RELEASE_CHANNEL != "oss" ]]; then
BINARY_NAME="${BINARY_NAME/warp/oz}"
fi
elif [[ "$ARTIFACT" == "warpctrl" ]]; then
BINARY_NAME="warpctrl"
PACKAGES=()
elif [[ "$ARTIFACT" == "tui" ]]; then
case "$RELEASE_CHANNEL" in
local) WARP_BIN="warp-tui" ;;
oss) WARP_BIN="warp-tui-oss" ;;
*) WARP_BIN="warp-tui-$RELEASE_CHANNEL" ;;
esac
BINARY_NAME="$WARP_BIN"
PACKAGES=()
fi
# Artifact-specific configuration
if [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "warpctrl" ]]; then
FEATURES="$FEATURES,standalone"
if [[ "$ARTIFACT" == "warpctrl" ]]; then
FEATURES="$FEATURES,warp_control_cli"
fi
elif [[ "$ARTIFACT" == "tui" ]]; then
# The TUI is a separate console crate with its own per-channel bins and a
# minimal feature set. It uses the native GNU target so the Linux clipboard
# backends can link against the host's Wayland/X11 libraries. Deliberately
# omit `voice_input` so portable bundles do not require the host ALSA runtime.
WARP_TUI_FEATURES="release_bundle,standalone"
if [[ "$RELEASE_CHANNEL" != "oss" ]]; then
WARP_TUI_FEATURES="$WARP_TUI_FEATURES,crash_reporting"
fi
elif [[ "$ARTIFACT" == "app" ]]; then
# All channels ship the v3 classifier and v2 heuristic.
FEATURES="$FEATURES,gui,nld_classifier_v3,nld_heuristic_v2"
fi
if [[ -n "$EXTRA_FEATURES" ]]; then
if [[ "$ARTIFACT" == "tui" ]]; then
WARP_TUI_FEATURES="$WARP_TUI_FEATURES,$EXTRA_FEATURES"
else
FEATURES="$FEATURES,$EXTRA_FEATURES"
fi
fi
BUNDLE_ID="dev.warp.$APP_NAME"
EXECUTABLE_PATH="$CARGO_TARGET_OUTPUT_DIR/$WARP_BIN"
DEBUG_EXECUTABLE_PATH="$EXECUTABLE_PATH.debug"
# Note that this variable must be set (and exported!) before we compile the
# binary, as it is read at compile time by Linux autoupdate logic (to know the
# expected name of the AppImage when downloading updates).
export APPIMAGE_NAME="$APP_NAME-$BUILD_ARCH.AppImage"
# If we only want to check that compilation will succeed, perform the checks
# then exit. We use this script to invoke `cargo check` to ensure that we are
# using the same feature flags and profile that we would be using in production.
if [[ "$CHECK_ONLY" == "true" ]]; then
if [[ "$ARTIFACT" == "tui" ]]; then
cargo check -p warp_tui --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$WARP_TUI_FEATURES"
else
cargo check -p warp --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$FEATURES" ${TARGET_TRIPLE:+--target $TARGET_TRIPLE}
fi
exit 0
fi
# Build the binary.
if [[ "$BUILD" == "true" ]]; then
echo "Building and bundling Warp for channel $RELEASE_CHANNEL and bundle id $BUNDLE_ID"
if [[ "$ARTIFACT" == "tui" ]]; then
cargo build -p warp_tui --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$WARP_TUI_FEATURES"
else
cargo build -p warp --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$FEATURES" ${TARGET_TRIPLE:+--target $TARGET_TRIPLE}
fi
echo "Making debug copy of '$EXECUTABLE_PATH' at '$DEBUG_EXECUTABLE_PATH'"
cp "$EXECUTABLE_PATH" "$DEBUG_EXECUTABLE_PATH"
echo "Stripping debug symbols from '$EXECUTABLE_PATH'"
if [[ $RELEASE_CHANNEL = "dev" ]]; then
# For dev builds, only strip debug symbols, as we want to keep some
# symbols for profiling purposes.
"${WARP_MUSL_STRIP:-strip}" --strip-debug "$EXECUTABLE_PATH"
else
# For production builds, strip all symbols, to keep the binary size
# smaller.
"${WARP_MUSL_STRIP:-strip}" --strip-all "$EXECUTABLE_PATH"
fi
else
echo 'Skipping `cargo build` step due to --skip-build argument'
fi
if [[ "$ARTIFACT" == "warpctrl" ]]; then
echo "Copying control-mode binary into $OUT_DIR/$WARP_BIN"
cp "$EXECUTABLE_PATH" "$OUT_DIR/$WARP_BIN"
WARPCTRL_SCRIPT_PATH="$OUT_DIR/warpctrl"
echo "Creating warpctrl wrapper script at $WARPCTRL_SCRIPT_PATH"
cat > "$WARPCTRL_SCRIPT_PATH" << EOF
#!/usr/bin/env bash
script_dir="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
exec "\$script_dir/$WARP_BIN" --warpctrl "\$@"
EOF
chmod +x "$WARPCTRL_SCRIPT_PATH"
fi
BINARY_PATH="$EXECUTABLE_PATH"
if [[ "$ARTIFACT" == "warpctrl" ]]; then
BINARY_PATH="$WARPCTRL_SCRIPT_PATH"
elif [[ "$ARTIFACT" == "tui" ]]; then
echo "Copying TUI binary into $OUT_DIR/$WARP_BIN"
cp "$EXECUTABLE_PATH" "$OUT_DIR/$WARP_BIN"
BINARY_PATH="$OUT_DIR/$WARP_BIN"
fi
if [[ "${SKIP_SETTINGS_SCHEMA:-}" != "1" && -z "${SETTINGS_SCHEMA_EXECUTABLE:-}" && -z "${SETTINGS_SCHEMA_SOURCE:-}" ]]; then
if [[ "$ARTIFACT" == "tui" ]]; then
echo "Error: TUI bundles require SETTINGS_SCHEMA_SOURCE or SETTINGS_SCHEMA_EXECUTABLE." >&2
exit 1
elif [[ "$BUILD" != "true" ]]; then
echo "Error: --skip-build requires SETTINGS_SCHEMA_SOURCE or SETTINGS_SCHEMA_EXECUTABLE." >&2
exit 1
elif [[ "$BUILD_ARCH" != "$HOST_ARCH" ]]; then
# The CLI/warpctrl artifacts can be cross-compiled (see the musl toolchain
# setup above); the resulting binary cannot then be executed on this host
# to generate a schema, so require the caller to supply one explicitly
# instead of failing later with an opaque exec format error.
echo "Error: Cannot execute the cross-compiled '$BUILD_ARCH' binary on this '$HOST_ARCH' host to generate a settings schema; pass SETTINGS_SCHEMA_SOURCE or SETTINGS_SCHEMA_EXECUTABLE." >&2
exit 1
else
export SETTINGS_SCHEMA_EXECUTABLE="$EXECUTABLE_PATH"
fi
fi
if [[ "${SKIP_SETTINGS_SCHEMA:-}" != "1" && -n "${SETTINGS_SCHEMA_EXECUTABLE:-}" && -n "${SETTINGS_SCHEMA_SOURCE:-}" ]]; then
echo "Error: SETTINGS_SCHEMA_EXECUTABLE and SETTINGS_SCHEMA_SOURCE are mutually exclusive." >&2
exit 1
fi
if [[ "${SKIP_SETTINGS_SCHEMA:-}" != "1" && -n "${SETTINGS_SCHEMA_EXECUTABLE:-}" ]]; then
GENERATED_SETTINGS_SCHEMA="$CARGO_TARGET_OUTPUT_DIR/settings_schema.json"
echo "Generating reusable settings schema at $GENERATED_SETTINGS_SCHEMA"
"$SETTINGS_SCHEMA_EXECUTABLE" dump-settings-schema "$GENERATED_SETTINGS_SCHEMA"
unset SETTINGS_SCHEMA_EXECUTABLE
export SETTINGS_SCHEMA_SOURCE="$GENERATED_SETTINGS_SCHEMA"
fi
# Prepare bundled resources for standalone builds.
if [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "warpctrl" || "$ARTIFACT" == "tui" ]]; then
echo "Preparing standalone resources directory"
BUNDLED_RESOURCES_DIR="$OUT_DIR/resources"
"$WORKSPACE_ROOT_DIR/script/prepare_bundled_resources" "$BUNDLED_RESOURCES_DIR" "$RELEASE_CHANNEL"
fi
# If this is being run within a GitHub action, set an output variable with the
# location of the binary so it can be referenced by subsequent actions, as well
# as the directory containing all built packages.
if [ "${GITHUB_ACTIONS}" == "true" ]; then
echo "::echo::on"
echo "executable_path=$BINARY_PATH" >> "$GITHUB_OUTPUT"
echo "debug_executable_path=$DEBUG_EXECUTABLE_PATH" >> "$GITHUB_OUTPUT"
echo "packages_dir=$OUT_DIR" >> "$GITHUB_OUTPUT"
echo "bundled_resources_dir=${BUNDLED_RESOURCES_DIR:-}" >> "$GITHUB_OUTPUT"
echo "settings_schema_path=${SETTINGS_SCHEMA_SOURCE:-}" >> "$GITHUB_OUTPUT"
echo "::echo::off"
fi
# Make sure a variety of environment variables are available in downstream scripts.
export \
WORKSPACE_ROOT_DIR \
DIST_DIR \
CARGO_TARGET_OUTPUT_DIR \
OUT_DIR \
RELEASE_CHANNEL \
BUNDLE_ID \
EXECUTABLE_PATH \
BINARY_NAME \
APPIMAGE_NAME \
BUILD_ARCH \
ARTIFACT \
CARGO_PROFILE
# Build the AppImage bundle.
if [[ ${PACKAGES[@]} =~ "appimage" ]]; then
echo "Building AppImage..."
"$WORKSPACE_ROOT_DIR/script/linux/bundle_appimage"
fi
# Build the .deb package.
if [[ ${PACKAGES[@]} =~ "deb" ]]; then
echo "Building .deb package..."
"$WORKSPACE_ROOT_DIR/script/linux/bundle_deb"
fi
# Build the .rpm package.
if [[ ${PACKAGES[@]} =~ "rpm" ]]; then
echo "Building .rpm package..."
"$WORKSPACE_ROOT_DIR/script/linux/bundle_rpm"
fi
# Build the Arch Linux package.
if [[ ${PACKAGES[@]} =~ "arch" ]]; then
echo "Building Arch Linux package..."
"$WORKSPACE_ROOT_DIR/script/linux/bundle_arch"
fi