441 lines · 15.2 KB · Text
RawDownload
#!/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=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"  fifi 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=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"  fifi 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 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'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 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"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"  fifi 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 1fi 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