#!/usr/bin/env bash # # Builds a Galaxy 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" 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" != "galaxyctrl" ]]; then echo "Error: --artifact must be 'app', 'cli', or 'galaxyctrl', 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" # Statically compile the CLI and galaxyctrl artifacts so they can run on older Linux distros. if [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "galaxyctrl" ]]; 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" == "galaxyctrl" ]]; then CARGO_PROFILE="release-cli-debug_assertions" else CARGO_PROFILE="release-lto-debug_assertions" fi else if [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "galaxyctrl" ]]; 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. # # GALAXY_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 GALAXY_BIN="galaxy-local" BINARY_NAME="galaxy-local" APP_NAME="GalaxyLocal" FEATURES="$FEATURES,agent_mode_debug" export HANDLE_MARKDOWN=1 elif [[ $RELEASE_CHANNEL = "dev" ]]; then GALAXY_BIN="galaxy-dev" BINARY_NAME="galaxy-dev" APP_NAME="GalaxyDev" 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 GALAXY_BIN="galaxy-preview" BINARY_NAME="galaxy-preview" APP_NAME="GalaxyPreview" 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 GALAXY_BIN="stable" BINARY_NAME="galaxy" APP_NAME="Galaxy" elif [[ $RELEASE_CHANNEL = "oss" ]]; then GALAXY_BIN="galaxy-oss" BINARY_NAME="galaxy-oss" APP_NAME="GalaxyOss" fi # Artifact-specific binary naming if [[ "$ARTIFACT" == "cli" ]]; then # Keep packaged CLI names aligned with `Channel::cli_command_name`. case "$RELEASE_CHANNEL" in stable) BINARY_NAME="galaxy-ai" ;; preview) BINARY_NAME="galaxy-ai-preview" ;; dev) BINARY_NAME="galaxy-ai-dev" ;; local) BINARY_NAME="galaxy-ai-local" ;; oss) BINARY_NAME="galaxy-ai-oss" ;; esac elif [[ "$ARTIFACT" == "galaxyctrl" ]]; then BINARY_NAME="galaxyctrl" PACKAGES=() fi # Artifact-specific configuration if [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "galaxyctrl" ]]; then FEATURES="$FEATURES,standalone" if [[ "$ARTIFACT" == "galaxyctrl" ]]; then FEATURES="$FEATURES,galaxy_control_cli" fi elif [[ "$ARTIFACT" == "app" ]]; then # All channels ship the v3 classifier and v2 heuristic. FEATURES="$FEATURES,gui,nld_classifier_v3,nld_heuristic_v2,galaxy_control_cli" fi if [[ -n "$EXTRA_FEATURES" ]]; then FEATURES="$FEATURES,$EXTRA_FEATURES" fi BUNDLE_ID="dev.galaxy.$APP_NAME" EXECUTABLE_PATH="$CARGO_TARGET_OUTPUT_DIR/$GALAXY_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 galaxy --profile "$CARGO_PROFILE" --bin "$GALAXY_BIN" --features "$FEATURES" ${TARGET_TRIPLE:+--target $TARGET_TRIPLE} exit 0 fi # Build the binary. if [[ "$BUILD" == "true" ]]; then echo "Building and bundling Galaxy for channel $RELEASE_CHANNEL and bundle id $BUNDLE_ID" cargo build -p galaxy --profile "$CARGO_PROFILE" --bin "$GALAXY_BIN" --features "$FEATURES" ${TARGET_TRIPLE:+--target $TARGET_TRIPLE} 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" == "galaxyctrl" ]]; then echo "Copying control-mode binary into $OUT_DIR/$GALAXY_BIN" cp "$EXECUTABLE_PATH" "$OUT_DIR/$GALAXY_BIN" GALAXYCTRL_SCRIPT_PATH="$OUT_DIR/galaxyctrl" echo "Creating galaxyctrl wrapper script at $GALAXYCTRL_SCRIPT_PATH" cat > "$GALAXYCTRL_SCRIPT_PATH" << EOF #!/usr/bin/env bash script_dir="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)" exec -a "\$0" "\$script_dir/$GALAXY_BIN" --galaxyctrl "\$@" EOF chmod +x "$GALAXYCTRL_SCRIPT_PATH" fi BINARY_PATH="$EXECUTABLE_PATH" if [[ "$ARTIFACT" == "galaxyctrl" ]]; then BINARY_PATH="$GALAXYCTRL_SCRIPT_PATH" fi # Prepare bundled resources for CLI builds. if [[ "$ARTIFACT" == "cli" || "$ARTIFACT" == "galaxyctrl" ]]; 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" 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 "::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