Files
warp/script/linux/bundle
David Stern f61ef1dcd9 Fix schema generator binary recompilation during release bundling. (#9632)
## Description
The release bundling workflow runs `prepare_bundled_resources` after the
main `cargo build`, and that script in turn invokes `cargo run --bin
generate_settings_schema`. Until now we passed neither `--features` nor
`--target` to this run, so cargo's resolver-v2 saw a different
per-package feature unification than the main build and recompiled every
dependency whose enabled features differed (`rust-embed` with
`debug-embed`, `warp_core`'s `release_bundle`, sentry, jemalloc, etc.).
This added significant time to release builds and pulled in extra system
dependencies (e.g. protoc) for jobs that should otherwise just be
packaging the prebuilt binary.

There was a secondary correctness issue: the schema generator iterates
settings registered via `inventory::submit!`, and `#[cfg(feature =
\"...\")]`-gated settings were silently omitted from the schema in any
context whose feature set didn't match the main binary.

The fix threads the build's feature set (and target, where applicable)
through to the schema-generator invocation, and makes package selection
explicit with `-p warp`. With matching features and target, cargo reuses
the existing compilation artifacts; with the same feature set, the
generated schema is also faithful to what the bundled binary actually
exposes.

## Testing
Verified `bash -n` syntax for the modified bash scripts and parsed the
modified PowerShell files via `pwsh`. Bundling, schema generation, and
CI behavior will be exercised by this PR's release builds.

## Agent Mode
- [x] Warp Agent Mode - This PR was created via Warp's AI Agent Mode


[Conversation](https://staging.warp.dev/conversation/1bd5574f-df28-4c2e-867d-40d71ed7e827)
2026-04-30 15:57:17 -04:00

298 lines
8.7 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Builds a Warp binary and bundles it up for distribution.
set -e
WORKSPACE_ROOT_DIR="$(pwd)"
CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$WORKSPACE_ROOT_DIR/target}"
DIST_DIR="$CARGO_TARGET_DIR/dist"
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"
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
;;
--artifact)
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
if [[ "$2" != "app" && "$2" != "cli" ]]; then
echo "Error: --artifact must be either 'app' or 'cli', 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"
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" ]]; then
CARGO_PROFILE="release-cli-debug_assertions"
else
CARGO_PROFILE="release-lto-debug_assertions"
fi
else
if [[ "$ARTIFACT" == "cli" ]]; then
CARGO_PROFILE="release-cli"
else
CARGO_PROFILE="release-lto"
fi
fi
if [[ "$CARGO_PROFILE" == "dev" ]]; then
CARGO_TARGET_OUTPUT_DIR="$CARGO_TARGET_DIR/debug"
else
CARGO_TARGET_OUTPUT_DIR="$CARGO_TARGET_DIR/$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 profiling using jemalloc through pprof.
FEATURES="$FEATURES,jemalloc_pprof"
export HANDLE_MARKDOWN=1
elif [[ $RELEASE_CHANNEL = "preview" ]]; then
WARP_BIN="preview"
BINARY_NAME="warp-preview"
APP_NAME="WarpPreview"
FEATURES="$FEATURES,preview_channel"
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
fi
# Artifact-specific configuration
if [[ "$ARTIFACT" == "cli" ]]; then
FEATURES="$FEATURES,standalone"
elif [[ "$ARTIFACT" == "app" ]]; then
FEATURES="$FEATURES,gui,nld_improvements"
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
cargo check -p warp --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$FEATURES"
exit 0
fi
# Build the binary.
if [[ "$BUILD" == "true" ]]; then
echo "Building and bundling Warp for channel $RELEASE_CHANNEL and bundle id $BUNDLE_ID"
cargo build -p warp --profile "$CARGO_PROFILE" --bin "$WARP_BIN" --features "$FEATURES"
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.
strip --strip-debug "$EXECUTABLE_PATH"
else
# For production builds, strip all symbols, to keep the binary size
# smaller.
strip --strip-all "$EXECUTABLE_PATH"
fi
else
echo 'Skipping `cargo build` step due to --skip-build argument'
fi
# Prepare bundled resources for CLI builds.
if [[ "$ARTIFACT" == "cli" ]]; then
echo "Preparing CLI resources directory"
BUNDLED_RESOURCES_DIR="$OUT_DIR/resources"
"$WORKSPACE_ROOT_DIR/script/prepare_bundled_resources" "$BUNDLED_RESOURCES_DIR" "$RELEASE_CHANNEL" "$CARGO_PROFILE" "$FEATURES"
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=$EXECUTABLE_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 "::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 \
FEATURES
# 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